diff --git a/cmd/basecli/commands/cmds.go b/cmd/basecli/commands/cmds.go index c451d35283..3bd6e26a6c 100644 --- a/cmd/basecli/commands/cmds.go +++ b/cmd/basecli/commands/cmds.go @@ -90,7 +90,7 @@ func doSendTx(cmd *cobra.Command, args []string) error { if seq < 0 { return fmt.Errorf("sequence must be greater than 0") } - tx = nonce.NewTx(tx, uint32(seq), nonceAccount) // XXX - what is the nonceAccount here!!! + tx = nonce.NewTx(uint32(seq), nonceAccount, tx) // Note: this is single sig (no multi sig yet) stx := auth.NewSig(tx) diff --git a/errors/common.go b/errors/common.go index 13aaf3d488..bdce81d944 100644 --- a/errors/common.go +++ b/errors/common.go @@ -12,7 +12,6 @@ import ( var ( errDecoding = fmt.Errorf("Error decoding input") - errNoAccount = fmt.Errorf("No such account") errUnauthorized = fmt.Errorf("Unauthorized") errInvalidSignature = fmt.Errorf("Invalid Signature") errTooLarge = fmt.Errorf("Input size too large") @@ -139,17 +138,13 @@ func IsNoChainErr(err error) bool { func ErrNoNonce() TMError { return WithCode(errNoNonce, unauthorized) } -func ErrBadNonce() TMError { - return WithCode(errBadNonce, unauthorized) +func ErrBadNonce(got, expected uint32) TMError { + return WithCode(fmt.Errorf("Bad nonce seqence, got %d, expected %d", got, expected), unauthorized) } func ErrNotMember() TMError { return WithCode(errBadNonce, unauthorized) } -func ErrNoAccount() TMError { - return WithCode(errNoAccount, unknownAddress) -} - func ErrWrongChain(chain string) TMError { msg := errors.Wrap(errWrongChain, chain) return WithCode(msg, unauthorized) diff --git a/modules/nonce/store.go b/modules/nonce/store.go index 51f6cdb1dd..ae10ccaf66 100644 --- a/modules/nonce/store.go +++ b/modules/nonce/store.go @@ -10,10 +10,10 @@ import ( ) func getSeq(store state.KVStore, key []byte) (seq uint32, err error) { - // fmt.Printf("load: %X\n", key) data := store.Get(key) if len(data) == 0 { - return seq, errors.ErrNoAccount() + //if the key is not stored, its a new key with a sequence of zero! + return 0, nil } err = wire.ReadBinaryBytes(data, &seq) if err != nil { diff --git a/modules/nonce/tx.go b/modules/nonce/tx.go index 95773a0895..09d5be6c9c 100644 --- a/modules/nonce/tx.go +++ b/modules/nonce/tx.go @@ -20,31 +20,24 @@ const ( ) func init() { - basecoin.TxMapper.RegisterImplementation(&Tx{}, TypeNonce, ByteNonce) + basecoin.TxMapper.RegisterImplementation(Tx{}, TypeNonce, ByteNonce) } // Tx - XXX fill in type Tx struct { - Tx basecoin.Tx `json:p"tx"` - Sequence uint32 - Signers []basecoin.Actor // or simple []data.Bytes (they are only pubkeys...) - seqKey []byte //key to store the sequence number + Sequence uint32 `json:"sequence"` + Signers []basecoin.Actor `json:"signers"` + Tx basecoin.Tx `json:"tx"` } var _ basecoin.TxInner = &Tx{} // NewTx wraps the tx with a signable nonce -func NewTx(tx basecoin.Tx, sequence uint32, signers []basecoin.Actor) basecoin.Tx { - - //Generate the sequence key as the hash of the list of signers, sorted by address - sort.Sort(basecoin.ByAddress(signers)) - seqKey := merkle.SimpleHashFromBinary(signers) - +func NewTx(sequence uint32, signers []basecoin.Actor, tx basecoin.Tx) basecoin.Tx { return (Tx{ - Tx: tx, Sequence: sequence, Signers: signers, - seqKey: seqKey, + Tx: tx, }).Wrap() } @@ -59,13 +52,17 @@ func (n Tx) ValidateBasic() error { // CheckIncrementSeq - XXX fill in func (n Tx) CheckIncrementSeq(ctx basecoin.Context, store state.KVStore) error { + //Generate the sequence key as the hash of the list of signers, sorted by address + sort.Sort(basecoin.ByAddress(n.Signers)) + seqKey := merkle.SimpleHashFromBinary(n.Signers) + // check the current state - cur, err := getSeq(store, n.seqKey) + cur, err := getSeq(store, seqKey) if err != nil { return err } if n.Sequence != cur+1 { - return errors.ErrBadNonce() + return errors.ErrBadNonce(n.Sequence, cur+1) } // make sure they all signed @@ -76,7 +73,7 @@ func (n Tx) CheckIncrementSeq(ctx basecoin.Context, store state.KVStore) error { } //finally increment the sequence by 1 - err = setSeq(store, n.seqKey, cur+1) + err = setSeq(store, seqKey, cur+1) if err != nil { return err } diff --git a/modules/nonce/tx_test.go b/modules/nonce/tx_test.go new file mode 100644 index 0000000000..faafba216c --- /dev/null +++ b/modules/nonce/tx_test.go @@ -0,0 +1,60 @@ +package nonce + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/stack" + "github.com/tendermint/basecoin/state" + + "github.com/tendermint/tmlibs/log" +) + +func TestNonce(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + // generic args here... + chainID := "my-chain" + height := uint64(100) + ctx := stack.NewContext(chainID, height, log.NewNopLogger()) + store := state.NewMemKVStore() + + act1 := basecoin.Actor{ChainID: chainID, App: "fooz", Address: []byte{1, 2, 3, 4}} + act2 := basecoin.Actor{ChainID: chainID, App: "fooz", Address: []byte{1, 1, 1, 1}} + act3 := basecoin.Actor{ChainID: chainID, App: "fooz", Address: []byte{3, 3, 3, 3}} + + testList := []struct { + valid bool + seq uint32 + actors []basecoin.Actor + }{ + {false, 0, []basecoin.Actor{act1}}, + {true, 1, []basecoin.Actor{act1}}, + {false, 777, []basecoin.Actor{act1}}, + {true, 2, []basecoin.Actor{act1}}, + {false, 0, []basecoin.Actor{act1, act2}}, + {true, 1, []basecoin.Actor{act1, act2}}, + {true, 2, []basecoin.Actor{act1, act2}}, + {true, 3, []basecoin.Actor{act1, act2}}, + {false, 2, []basecoin.Actor{act1, act2}}, + {false, 2, []basecoin.Actor{act1, act2, act3}}, + {true, 1, []basecoin.Actor{act1, act2, act3}}, + } + + for _, test := range testList { + + tx := NewTx(test.seq, test.actors, basecoin.Tx{}) + nonceTx, ok := tx.Unwrap().(Tx) + require.True(ok) + err := nonceTx.CheckIncrementSeq(ctx, store) + if test.valid { + assert.Nil(err, "%v,%v: %+v", test.seq, test.actors, err) + } else { + assert.NotNil(err, "%v,%v", test.seq, test.actors) + } + } +}