diff --git a/blocks/signature.go b/blocks/signature.go index 80b6c91d8..33f6f9a47 100644 --- a/blocks/signature.go +++ b/blocks/signature.go @@ -6,7 +6,7 @@ import ( ) type Signable interface { - GenDocument() []byte + Binary GetSignature() Signature SetSignature(Signature) } diff --git a/blocks/tx.go b/blocks/tx.go index c4f311ecc..e43f3b8b9 100644 --- a/blocks/tx.go +++ b/blocks/tx.go @@ -19,59 +19,56 @@ Validation Txs: */ type Tx interface { - Type() byte - //IsValidation() bool - Binary + Signable + GetSequence() uint64 } const ( // Account transactions - txTypeSend = byte(0x01) - txTypeName = byte(0x02) + TxTypeSend = byte(0x01) + TxTypeName = byte(0x02) // Validation transactions - txTypeBond = byte(0x11) - txTypeUnbond = byte(0x12) - txTypeTimeout = byte(0x13) - txTypeDupeout = byte(0x14) + TxTypeBond = byte(0x11) + TxTypeUnbond = byte(0x12) + TxTypeTimeout = byte(0x13) + TxTypeDupeout = byte(0x14) ) func ReadTx(r io.Reader, n *int64, err *error) Tx { switch t := ReadByte(r, n, err); t { - case txTypeSend: + case TxTypeSend: return &SendTx{ BaseTx: ReadBaseTx(r, n, err), Fee: ReadUInt64(r, n, err), To: ReadUInt64(r, n, err), Amount: ReadUInt64(r, n, err), } - case txTypeName: + case TxTypeName: return &NameTx{ BaseTx: ReadBaseTx(r, n, err), Fee: ReadUInt64(r, n, err), Name: ReadString(r, n, err), PubKey: ReadByteSlice(r, n, err), } - case txTypeBond: + case TxTypeBond: return &BondTx{ BaseTx: ReadBaseTx(r, n, err), Fee: ReadUInt64(r, n, err), UnbondTo: ReadUInt64(r, n, err), - Amount: ReadUInt64(r, n, err), } - case txTypeUnbond: + case TxTypeUnbond: return &UnbondTx{ BaseTx: ReadBaseTx(r, n, err), Fee: ReadUInt64(r, n, err), - Amount: ReadUInt64(r, n, err), } - case txTypeTimeout: + case TxTypeTimeout: return &TimeoutTx{ BaseTx: ReadBaseTx(r, n, err), AccountId: ReadUInt64(r, n, err), Penalty: ReadUInt64(r, n, err), } - case txTypeDupeout: + case TxTypeDupeout: return &DupeoutTx{ BaseTx: ReadBaseTx(r, n, err), VoteA: *ReadVote(r, n, err), @@ -103,6 +100,10 @@ func (tx BaseTx) WriteTo(w io.Writer) (n int64, err error) { return } +func (tx *BaseTx) GetSequence() uint64 { + return tx.Sequence +} + func (tx *BaseTx) GetSignature() Signature { return tx.Signature } @@ -120,12 +121,8 @@ type SendTx struct { Amount uint64 } -func (tx *SendTx) Type() byte { - return txTypeSend -} - func (tx *SendTx) WriteTo(w io.Writer) (n int64, err error) { - WriteByte(w, tx.Type(), &n, &err) + WriteByte(w, TxTypeSend, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err) WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(w, tx.To, &n, &err) @@ -142,12 +139,8 @@ type NameTx struct { PubKey []byte } -func (tx *NameTx) Type() byte { - return txTypeName -} - func (tx *NameTx) WriteTo(w io.Writer) (n int64, err error) { - WriteByte(w, tx.Type(), &n, &err) + WriteByte(w, TxTypeName, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err) WriteUInt64(w, tx.Fee, &n, &err) WriteString(w, tx.Name, &n, &err) @@ -161,19 +154,13 @@ type BondTx struct { BaseTx Fee uint64 UnbondTo uint64 - Amount uint64 -} - -func (tx *BondTx) Type() byte { - return txTypeBond } func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) { - WriteByte(w, tx.Type(), &n, &err) + WriteByte(w, TxTypeBond, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err) WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(w, tx.UnbondTo, &n, &err) - WriteUInt64(w, tx.Amount, &n, &err) return } @@ -181,19 +168,13 @@ func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) { type UnbondTx struct { BaseTx - Fee uint64 - Amount uint64 -} - -func (tx *UnbondTx) Type() byte { - return txTypeUnbond + Fee uint64 } func (tx *UnbondTx) WriteTo(w io.Writer) (n int64, err error) { - WriteByte(w, tx.Type(), &n, &err) + WriteByte(w, TxTypeUnbond, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err) WriteUInt64(w, tx.Fee, &n, &err) - WriteUInt64(w, tx.Amount, &n, &err) return } @@ -205,12 +186,8 @@ type TimeoutTx struct { Penalty uint64 } -func (tx *TimeoutTx) Type() byte { - return txTypeTimeout -} - func (tx *TimeoutTx) WriteTo(w io.Writer) (n int64, err error) { - WriteByte(w, tx.Type(), &n, &err) + WriteByte(w, TxTypeTimeout, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err) WriteUInt64(w, tx.AccountId, &n, &err) WriteUInt64(w, tx.Penalty, &n, &err) @@ -225,22 +202,10 @@ type DupeoutTx struct { VoteB Vote } -func (tx *DupeoutTx) Type() byte { - return txTypeDupeout -} - func (tx *DupeoutTx) WriteTo(w io.Writer) (n int64, err error) { - WriteByte(w, tx.Type(), &n, &err) + WriteByte(w, TxTypeDupeout, &n, &err) WriteBinary(w, tx.BaseTx, &n, &err) WriteBinary(w, &tx.VoteA, &n, &err) WriteBinary(w, &tx.VoteB, &n, &err) return } - -func (tx *DupeoutTx) GenDocument() []byte { - oldSig := tx.Signature - tx.Signature = Signature{} - doc := BinaryBytes(tx) - tx.Signature = oldSig - return doc -} diff --git a/blocks/vote.go b/blocks/vote.go index babd58229..041657096 100644 --- a/blocks/vote.go +++ b/blocks/vote.go @@ -49,14 +49,6 @@ func (v *Vote) WriteTo(w io.Writer) (n int64, err error) { return } -func (v *Vote) GenDocument() []byte { - oldSig := v.Signature - v.Signature = Signature{} - doc := BinaryBytes(v) - v.Signature = oldSig - return doc -} - func (v *Vote) GetSignature() Signature { return v.Signature } diff --git a/consensus/consensus.go b/consensus/consensus.go index 0d3ffdf3b..4e616ad5c 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -402,7 +402,7 @@ OUTER_LOOP: // Signs a vote document and broadcasts it. func (conR *ConsensusReactor) signAndBroadcastVote(rs *RoundState, vote *Vote) { if rs.PrivValidator != nil { - rs.PrivValidator.SignVote(vote) + rs.PrivValidator.Sign(vote) conR.conS.AddVote(vote) msg := p2p.TypedMessage{msgTypeVote, vote} conR.sw.Broadcast(VoteCh, msg) diff --git a/consensus/pol.go b/consensus/pol.go index ae34c48c9..1308b3744 100644 --- a/consensus/pol.go +++ b/consensus/pol.go @@ -45,8 +45,8 @@ func (pol *POL) WriteTo(w io.Writer) (n int64, err error) { func (pol *POL) Verify(vset *ValidatorSet) error { talliedVotingPower := uint64(0) - voteDoc := (&Vote{Height: pol.Height, Round: pol.Round, - Type: VoteTypeBare, BlockHash: pol.BlockHash}).GenDocument() + voteDoc := BinaryBytes(&Vote{Height: pol.Height, Round: pol.Round, + Type: VoteTypeBare, BlockHash: pol.BlockHash}) seenValidators := map[uint64]struct{}{} for _, sig := range pol.Votes { @@ -59,7 +59,7 @@ func (pol *POL) Verify(vset *ValidatorSet) error { if validator == nil { return Errorf("Invalid validator for vote %v for POL %v", sig, pol) } - if !validator.Verify(voteDoc, sig) { + if !validator.VerifyBytes(voteDoc, sig) { return Errorf("Invalid signature for vote %v for POL %v", sig, pol) } @@ -80,9 +80,9 @@ func (pol *POL) Verify(vset *ValidatorSet) error { return Errorf("Invalid validator for commit %v for POL %v", sig, pol) } - commitDoc := (&Vote{Height: pol.Height, Round: round, - Type: VoteTypeCommit, BlockHash: pol.BlockHash}).GenDocument() // TODO cache - if !validator.Verify(commitDoc, sig) { + commitDoc := BinaryBytes(&Vote{Height: pol.Height, Round: round, + Type: VoteTypeCommit, BlockHash: pol.BlockHash}) // TODO cache + if !validator.VerifyBytes(commitDoc, sig) { return Errorf("Invalid signature for commit %v for POL %v", sig, pol) } diff --git a/consensus/priv_validator.go b/consensus/priv_validator.go index cb6c53538..c5152e228 100644 --- a/consensus/priv_validator.go +++ b/consensus/priv_validator.go @@ -13,14 +13,14 @@ type PrivValidator struct { db *db_.LevelDB } -// Double signing results in an error. -func (pv *PrivValidator) SignProposal(proposal *Proposal) { - //TODO: prevent double signing. - pv.SignSignable(proposal) -} - -// Double signing results in an error. -func (pv *PrivValidator) SignVote(vote *Vote) { - //TODO: prevent double signing. - pv.SignSignable(vote) +// Double signing results in a panic. +func (pv *PrivValidator) Sign(o Signable) { + switch o.(type) { + case *Proposal: + //TODO: prevent double signing. + pv.PrivAccount.Sign(o.(*Proposal)) + case *Vote: + //TODO: prevent double signing. + pv.PrivAccount.Sign(o.(*Vote)) + } } diff --git a/consensus/proposal.go b/consensus/proposal.go index 2fb695042..ab87281a3 100644 --- a/consensus/proposal.go +++ b/consensus/proposal.go @@ -58,14 +58,6 @@ func (p *Proposal) WriteTo(w io.Writer) (n int64, err error) { return } -func (p *Proposal) GenDocument() []byte { - oldSig := p.Signature - p.Signature = Signature{} - doc := BinaryBytes(p) - p.Signature = oldSig - return doc -} - func (p *Proposal) GetSignature() Signature { return p.Signature } diff --git a/consensus/state.go b/consensus/state.go index 3fd38e9d9..74b25218b 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -178,7 +178,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error { } // Verify signature - if !cs.Proposer.Verify(proposal.GenDocument(), proposal.Signature) { + if !cs.Proposer.Verify(proposal) { return ErrInvalidProposalSignature } @@ -220,7 +220,7 @@ func (cs *ConsensusState) MakeProposal() { // Make proposal proposal := NewProposal(cs.Height, cs.Round, blockPartSet.Total(), blockPartSet.RootHash(), polPartSet.Total(), polPartSet.RootHash()) - cs.PrivValidator.SignProposal(proposal) + cs.PrivValidator.Sign(proposal) // Set fields cs.Proposal = proposal diff --git a/consensus/vote_set.go b/consensus/vote_set.go index 39f49c00c..851b15ec8 100644 --- a/consensus/vote_set.go +++ b/consensus/vote_set.go @@ -69,7 +69,7 @@ func (vs *VoteSet) AddVote(vote *Vote) (bool, error) { } // Check signature. - if !val.Verify(vote.GenDocument(), vote.Signature) { + if !val.Verify(vote) { // Bad signature. return false, ErrVoteInvalidSignature } diff --git a/merkle/iavl_tree.go b/merkle/iavl_tree.go index b2962a968..5fed69677 100644 --- a/merkle/iavl_tree.go +++ b/merkle/iavl_tree.go @@ -3,8 +3,6 @@ package merkle import ( "bytes" "container/list" - . "github.com/tendermint/tendermint/binary" - . "github.com/tendermint/tendermint/common" ) const defaultCacheCapacity = 1000 // TODO make configurable. @@ -117,83 +115,6 @@ func (t *IAVLTree) Copy() Tree { //----------------------------------------------------------------------------- -// TODO: make TypedTree work with the underlying tree to cache the decoded value. -type TypedTree struct { - Tree Tree - keyCodec Codec - valueCodec Codec -} - -func NewTypedTree(tree Tree, keyCodec, valueCodec Codec) *TypedTree { - return &TypedTree{ - Tree: tree, - keyCodec: keyCodec, - valueCodec: valueCodec, - } -} - -func (t *TypedTree) Has(key interface{}) bool { - bytes, err := t.keyCodec.Write(key) - if err != nil { - Panicf("Error from keyCodec: %v", err) - } - return t.Tree.Has(bytes) -} - -func (t *TypedTree) Get(key interface{}) interface{} { - keyBytes, err := t.keyCodec.Write(key) - if err != nil { - Panicf("Error from keyCodec: %v", err) - } - valueBytes := t.Tree.Get(keyBytes) - if valueBytes == nil { - return nil - } - value, err := t.valueCodec.Read(valueBytes) - if err != nil { - Panicf("Error from valueCodec: %v", err) - } - return value -} - -func (t *TypedTree) Set(key interface{}, value interface{}) bool { - keyBytes, err := t.keyCodec.Write(key) - if err != nil { - Panicf("Error from keyCodec: %v", err) - } - valueBytes, err := t.valueCodec.Write(value) - if err != nil { - Panicf("Error from valueCodec: %v", err) - } - return t.Tree.Set(keyBytes, valueBytes) -} - -func (t *TypedTree) Remove(key interface{}) (interface{}, error) { - keyBytes, err := t.keyCodec.Write(key) - if err != nil { - Panicf("Error from keyCodec: %v", err) - } - valueBytes, err := t.Tree.Remove(keyBytes) - if valueBytes == nil { - return nil, err - } - value, err_ := t.valueCodec.Read(valueBytes) - if err_ != nil { - Panicf("Error from valueCodec: %v", err) - } - return value, err -} - -func (t *TypedTree) Copy() *TypedTree { - return &TypedTree{ - Tree: t.Tree.Copy(), - keyCodec: t.keyCodec, - valueCodec: t.valueCodec, - } -} - -//----------------------------------------------------------------------------- - type nodeElement struct { node *IAVLNode elem *list.Element diff --git a/merkle/typed_tree.go b/merkle/typed_tree.go new file mode 100644 index 000000000..efafc6b88 --- /dev/null +++ b/merkle/typed_tree.go @@ -0,0 +1,81 @@ +package merkle + +import ( + . "github.com/tendermint/tendermint/binary" + . "github.com/tendermint/tendermint/common" +) + +// TODO: make TypedTree work with the underlying tree to cache the decoded value. +type TypedTree struct { + Tree Tree + keyCodec Codec + valueCodec Codec +} + +func NewTypedTree(tree Tree, keyCodec, valueCodec Codec) *TypedTree { + return &TypedTree{ + Tree: tree, + keyCodec: keyCodec, + valueCodec: valueCodec, + } +} + +func (t *TypedTree) Has(key interface{}) bool { + bytes, err := t.keyCodec.Write(key) + if err != nil { + Panicf("Error from keyCodec: %v", err) + } + return t.Tree.Has(bytes) +} + +func (t *TypedTree) Get(key interface{}) interface{} { + keyBytes, err := t.keyCodec.Write(key) + if err != nil { + Panicf("Error from keyCodec: %v", err) + } + valueBytes := t.Tree.Get(keyBytes) + if valueBytes == nil { + return nil + } + value, err := t.valueCodec.Read(valueBytes) + if err != nil { + Panicf("Error from valueCodec: %v", err) + } + return value +} + +func (t *TypedTree) Set(key interface{}, value interface{}) bool { + keyBytes, err := t.keyCodec.Write(key) + if err != nil { + Panicf("Error from keyCodec: %v", err) + } + valueBytes, err := t.valueCodec.Write(value) + if err != nil { + Panicf("Error from valueCodec: %v", err) + } + return t.Tree.Set(keyBytes, valueBytes) +} + +func (t *TypedTree) Remove(key interface{}) (interface{}, error) { + keyBytes, err := t.keyCodec.Write(key) + if err != nil { + Panicf("Error from keyCodec: %v", err) + } + valueBytes, err := t.Tree.Remove(keyBytes) + if valueBytes == nil { + return nil, err + } + value, err_ := t.valueCodec.Read(valueBytes) + if err_ != nil { + Panicf("Error from valueCodec: %v", err) + } + return value, err +} + +func (t *TypedTree) Copy() *TypedTree { + return &TypedTree{ + Tree: t.Tree.Copy(), + keyCodec: t.keyCodec, + valueCodec: t.valueCodec, + } +} diff --git a/state/account.go b/state/account.go index 45aada2cf..91405209f 100644 --- a/state/account.go +++ b/state/account.go @@ -9,8 +9,9 @@ import ( ) const ( - AccountBalanceStatusNominal = byte(0x00) - AccountBalanceStatusBonded = byte(0x01) + AccountDetailStatusNominal = byte(0x00) + AccountDetailStatusBonded = byte(0x01) + AccountDetailStatusUnbonding = byte(0x02) ) type Account struct { @@ -31,7 +32,7 @@ func (account Account) WriteTo(w io.Writer) (n int64, err error) { return } -func (account Account) Verify(msg []byte, sig Signature) bool { +func (account Account) VerifyBytes(msg []byte, sig Signature) bool { if sig.SignerId != account.Id { panic("account.id doesn't match sig.signerid") } @@ -44,32 +45,37 @@ func (account Account) Verify(msg []byte, sig Signature) bool { return ok } -func (account Account) VerifySignable(o Signable) bool { - msg := o.GenDocument() +func (account Account) Verify(o Signable) bool { sig := o.GetSignature() - return account.Verify(msg, sig) + o.SetSignature(Signature{}) // clear + msg := BinaryBytes(o) + o.SetSignature(sig) // restore + return account.VerifyBytes(msg, sig) } //----------------------------------------------------------------------------- -type AccountBalance struct { +type AccountDetail struct { Account - Balance uint64 - Status byte + Sequence uint64 + Balance uint64 + Status byte } -func ReadAccountBalance(r io.Reader, n *int64, err *error) *AccountBalance { - return &AccountBalance{ - Account: ReadAccount(r, n, err), - Balance: ReadUInt64(r, n, err), - Status: ReadByte(r, n, err), +func ReadAccountDetail(r io.Reader, n *int64, err *error) *AccountDetail { + return &AccountDetail{ + Account: ReadAccount(r, n, err), + Sequence: ReadUInt64(r, n, err), + Balance: ReadUInt64(r, n, err), + Status: ReadByte(r, n, err), } } -func (accBal AccountBalance) WriteTo(w io.Writer) (n int64, err error) { - WriteBinary(w, accBal.Account, &n, &err) - WriteUInt64(w, accBal.Balance, &n, &err) - WriteByte(w, accBal.Status, &n, &err) +func (accDet AccountDetail) WriteTo(w io.Writer) (n int64, err error) { + WriteBinary(w, accDet.Account, &n, &err) + WriteUInt64(w, accDet.Sequence, &n, &err) + WriteUInt64(w, accDet.Balance, &n, &err) + WriteByte(w, accDet.Status, &n, &err) return } @@ -94,7 +100,7 @@ func GenPrivAccount() *PrivAccount { } } -func (pa *PrivAccount) Sign(msg []byte) Signature { +func (pa *PrivAccount) SignBytes(msg []byte) Signature { signature := crypto.SignMessage(msg, pa.PrivKey, pa.PubKey) sig := Signature{ SignerId: pa.Id, @@ -103,8 +109,9 @@ func (pa *PrivAccount) Sign(msg []byte) Signature { return sig } -func (pa *PrivAccount) SignSignable(o Signable) { - msg := o.GenDocument() - sig := pa.Sign(msg) +func (pa *PrivAccount) Sign(o Signable) { + o.SetSignature(Signature{}) // clear + msg := BinaryBytes(o) + sig := pa.SignBytes(msg) o.SetSignature(sig) } diff --git a/state/account_test.go b/state/account_test.go index 528097052..a9af1761e 100644 --- a/state/account_test.go +++ b/state/account_test.go @@ -11,7 +11,7 @@ func TestSignAndValidate(t *testing.T) { account := &privAccount.Account msg := CRandBytes(128) - sig := privAccount.Sign(msg) + sig := privAccount.SignBytes(msg) t.Logf("msg: %X, sig: %X", msg, sig) // Test the signature diff --git a/state/state.go b/state/state.go index 395dde505..b5d26d613 100644 --- a/state/state.go +++ b/state/state.go @@ -12,62 +12,67 @@ import ( ) var ( + ErrStateInvalidAccountId = errors.New("Error State invalid account id") + ErrStateInvalidSignature = errors.New("Error State invalid signature") ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") + ErrStateInvalidAccountState = errors.New("Error State invalid account state") ErrStateInvalidValidationStateHash = errors.New("Error State invalid ValidationStateHash") ErrStateInvalidAccountStateHash = errors.New("Error State invalid AccountStateHash") + ErrStateInsufficientFunds = errors.New("Error State insufficient funds") - stateKey = []byte("stateKey") + stateKey = []byte("stateKey") + minBondAmount = uint64(1) // TODO adjust ) -type accountBalanceCodec struct{} +type accountDetailCodec struct{} -func (abc accountBalanceCodec) Write(accBal interface{}) (accBalBytes []byte, err error) { +func (abc accountDetailCodec) Write(accDet interface{}) (accDetBytes []byte, err error) { w := new(bytes.Buffer) - _, err = accBal.(*AccountBalance).WriteTo(w) + _, err = accDet.(*AccountDetail).WriteTo(w) return w.Bytes(), err } -func (abc accountBalanceCodec) Read(accBalBytes []byte) (interface{}, error) { - n, err, r := new(int64), new(error), bytes.NewBuffer(accBalBytes) - return ReadAccountBalance(r, n, err), *err +func (abc accountDetailCodec) Read(accDetBytes []byte) (interface{}, error) { + n, err, r := new(int64), new(error), bytes.NewBuffer(accDetBytes) + return ReadAccountDetail(r, n, err), *err } //----------------------------------------------------------------------------- // NOTE: not goroutine-safe. type State struct { - DB DB - Height uint32 // Last known block height - BlockHash []byte // Last known block hash - CommitTime time.Time - AccountBalances *merkle.TypedTree - Validators *ValidatorSet + DB DB + Height uint32 // Last known block height + BlockHash []byte // Last known block hash + CommitTime time.Time + AccountDetails *merkle.TypedTree + Validators *ValidatorSet } -func GenesisState(db DB, genesisTime time.Time, accBals []*AccountBalance) *State { +func GenesisState(db DB, genesisTime time.Time, accDets []*AccountDetail) *State { // TODO: Use "uint64Codec" instead of BasicCodec - accountBalances := merkle.NewTypedTree(merkle.NewIAVLTree(db), BasicCodec, accountBalanceCodec{}) + accountDetails := merkle.NewTypedTree(merkle.NewIAVLTree(db), BasicCodec, accountDetailCodec{}) validators := map[uint64]*Validator{} - for _, accBal := range accBals { - accountBalances.Set(accBal.Id, accBal) - validators[accBal.Id] = &Validator{ - Account: accBal.Account, + for _, accDet := range accDets { + accountDetails.Set(accDet.Id, accDet) + validators[accDet.Id] = &Validator{ + Account: accDet.Account, BondHeight: 0, - VotingPower: accBal.Balance, + VotingPower: accDet.Balance, Accum: 0, } } validatorSet := NewValidatorSet(validators) return &State{ - DB: db, - Height: 0, - BlockHash: nil, - CommitTime: genesisTime, - AccountBalances: accountBalances, - Validators: validatorSet, + DB: db, + Height: 0, + BlockHash: nil, + CommitTime: genesisTime, + AccountDetails: accountDetails, + Validators: validatorSet, } } @@ -83,8 +88,8 @@ func LoadState(db DB) *State { s.Height = ReadUInt32(reader, &n, &err) s.CommitTime = ReadTime(reader, &n, &err) s.BlockHash = ReadByteSlice(reader, &n, &err) - accountBalancesHash := ReadByteSlice(reader, &n, &err) - s.AccountBalances = merkle.NewTypedTree(merkle.LoadIAVLTreeFromHash(db, accountBalancesHash), BasicCodec, accountBalanceCodec{}) + accountDetailsHash := ReadByteSlice(reader, &n, &err) + s.AccountDetails = merkle.NewTypedTree(merkle.LoadIAVLTreeFromHash(db, accountDetailsHash), BasicCodec, accountDetailCodec{}) var validators = map[uint64]*Validator{} for reader.Len() > 0 { validator := ReadValidator(reader, &n, &err) @@ -103,14 +108,14 @@ func LoadState(db DB) *State { // is saved here. func (s *State) Save(commitTime time.Time) { s.CommitTime = commitTime - s.AccountBalances.Tree.Save() + s.AccountDetails.Tree.Save() var buf bytes.Buffer var n int64 var err error WriteUInt32(&buf, s.Height, &n, &err) WriteTime(&buf, commitTime, &n, &err) WriteByteSlice(&buf, s.BlockHash, &n, &err) - WriteByteSlice(&buf, s.AccountBalances.Tree.Hash(), &n, &err) + WriteByteSlice(&buf, s.AccountDetails.Tree.Hash(), &n, &err) for _, validator := range s.Validators.Map() { WriteBinary(&buf, validator, &n, &err) } @@ -122,26 +127,76 @@ func (s *State) Save(commitTime time.Time) { func (s *State) Copy() *State { return &State{ - DB: s.DB, - Height: s.Height, - CommitTime: s.CommitTime, - BlockHash: s.BlockHash, - AccountBalances: s.AccountBalances.Copy(), - Validators: s.Validators.Copy(), + DB: s.DB, + Height: s.Height, + CommitTime: s.CommitTime, + BlockHash: s.BlockHash, + AccountDetails: s.AccountDetails.Copy(), + Validators: s.Validators.Copy(), } } // If the tx is invalid, an error will be returned. // Unlike AppendBlock(), state will not be altered. func (s *State) ExecTx(tx Tx) error { - /* - // Get the signer's incr - signerId := tx.Signature().SignerId - if mem.state.AccountSequence(signerId) != tx.Sequence() { - return ErrStateInvalidSequenceNumber + accDet := s.GetAccountDetail(tx.GetSignature().SignerId) + if accDet == nil { + return ErrStateInvalidAccountId + } + // Check signature + if !accDet.Verify(tx) { + return ErrStateInvalidSignature + } + // Check sequence + if tx.GetSequence() <= accDet.Sequence { + return ErrStateInvalidSequenceNumber + } + // Exec tx + switch tx.(type) { + case *SendTx: + stx := tx.(*SendTx) + toAccDet := s.GetAccountDetail(stx.To) + // Accounts must be nominal + if accDet.Status != AccountDetailStatusNominal { + return ErrStateInvalidAccountState + } + if toAccDet.Status != AccountDetailStatusNominal { + return ErrStateInvalidAccountState + } + // Check account balance + if accDet.Balance < stx.Fee+stx.Amount { + return ErrStateInsufficientFunds + } + // Check existence of destination account + if toAccDet == nil { + return ErrStateInvalidAccountId } - */ - // XXX commit the tx + // Good! + accDet.Balance -= (stx.Fee + stx.Amount) + toAccDet.Balance += (stx.Amount) + s.SetAccountDetail(accDet) + s.SetAccountDetail(toAccDet) + //case *NameTx + case *BondTx: + btx := tx.(*BondTx) + // Account must be nominal + if accDet.Status != AccountDetailStatusNominal { + return ErrStateInvalidAccountState + } + // Check account balance + if accDet.Balance < minBondAmount { + return ErrStateInsufficientFunds + } + // TODO: max number of validators? + // Good! + accDet.Balance -= btx.Fee // remaining balance are bonded coins. + accDet.Status = AccountDetailStatusBonded + s.SetAccountDetail(accDet) + // XXX add validator + case *UnbondTx: + case *TimeoutTx: + case *DupeoutTx: + } panic("Implement ExecTx()") return nil } @@ -170,7 +225,7 @@ func (s *State) AppendBlock(b *Block) error { if !bytes.Equal(s.Validators.Hash(), b.ValidationStateHash) { return ErrStateInvalidValidationStateHash } - if !bytes.Equal(s.AccountBalances.Tree.Hash(), b.AccountStateHash) { + if !bytes.Equal(s.AccountDetails.Tree.Hash(), b.AccountStateHash) { return ErrStateInvalidAccountStateHash } @@ -179,10 +234,15 @@ func (s *State) AppendBlock(b *Block) error { return nil } -func (s *State) AccountBalance(accountId uint64) *AccountBalance { - accBal := s.AccountBalances.Get(accountId) - if accBal == nil { +func (s *State) GetAccountDetail(accountId uint64) *AccountDetail { + accDet := s.AccountDetails.Get(accountId) + if accDet == nil { return nil } - return accBal.(*AccountBalance) + return accDet.(*AccountDetail) +} + +// Returns false if new, true if updated. +func (s *State) SetAccountDetail(accDet *AccountDetail) (updated bool) { + return s.AccountDetails.Set(accDet.Id, accDet) }