diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index affa014155..148f0411fc 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -21,28 +21,16 @@ import ( "github.com/tendermint/tmlibs/log" ) -// helper variables and functions - +// Construct some global addrs and txs for tests. var ( - // Construct genesis key/accounts priv1 = crypto.GenPrivKeyEd25519() addr1 = priv1.PubKey().Address() addr2 = crypto.GenPrivKeyEd25519().PubKey().Address() + coins = sdk.Coins{{"foocoin", 10}} sendMsg = bank.SendMsg{ - Inputs: []bank.Input{ - { - Address: addr1, - Coins: sdk.Coins{{"foocoin", 10}}, - Sequence: 1, - }, - }, - Outputs: []bank.Output{ - { - Address: addr2, - Coins: sdk.Coins{{"foocoin", 10}}, - }, - }, + Inputs: []bank.Input{bank.NewInput(addr1, coins)}, + Outputs: []bank.Output{bank.NewOutput(addr2, coins)}, } whatCoolMsg1 = cool.WhatCoolMsg{ @@ -80,8 +68,10 @@ func TestMsgs(t *testing.T) { {setWhatCoolMsg}, } + chainID := "" + sequences := []int64{0} for i, m := range msgs { - sig := priv1.Sign(m.msg.GetSignBytes()) + sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, m.msg)) tx := sdk.NewStdTx(m.msg, []sdk.StdSignature{{ PubKey: priv1.PubKey(), Signature: sig, @@ -178,9 +168,12 @@ func TestSendMsgWithAccounts(t *testing.T) { assert.Equal(t, acc1, res1) // Sign the tx + chainID := "" // TODO: InitChain should get the ChainID + sequences := []int64{0} + sig := priv1.Sign(sdk.StdSignBytes(chainID, sequences, sendMsg)) tx := sdk.NewStdTx(sendMsg, []sdk.StdSignature{{ PubKey: priv1.PubKey(), - Signature: priv1.Sign(sendMsg.GetSignBytes()), + Signature: sig, }}) // Run a Check @@ -198,6 +191,22 @@ func TestSendMsgWithAccounts(t *testing.T) { res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2) assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin") assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin") + + // Delivering again should cause replay error + res = bapp.Deliver(tx) + assert.Equal(t, sdk.CodeInvalidSequence, res.Code, res.Log) + + // bumping the txnonce number without resigning should be an auth error + tx.Signatures[0].Sequence = 1 + res = bapp.Deliver(tx) + assert.Equal(t, sdk.CodeUnauthorized, res.Code, res.Log) + + // resigning the tx with the bumped sequence should work + sequences = []int64{1} + sig = priv1.Sign(sdk.StdSignBytes(chainID, sequences, tx.Msg)) + tx.Signatures[0].Signature = sig + res = bapp.Deliver(tx) + assert.Equal(t, sdk.CodeOK, res.Code, res.Log) } //func TestWhatCoolMsg(t *testing.T) { diff --git a/types/errors.go b/types/errors.go index e52fe625f1..9d8175e309 100644 --- a/types/errors.go +++ b/types/errors.go @@ -22,12 +22,11 @@ const ( CodeOK CodeType = 0 CodeInternal CodeType = 1 CodeTxParse CodeType = 2 - CodeBadNonce CodeType = 3 + CodeInvalidSequence CodeType = 3 CodeUnauthorized CodeType = 4 CodeInsufficientFunds CodeType = 5 CodeUnknownRequest CodeType = 6 CodeUnrecognizedAddress CodeType = 7 - CodeInvalidSequence CodeType = 8 CodeGenesisParse CodeType = 0xdead // TODO: remove ? ) @@ -41,8 +40,8 @@ func CodeToDefaultMsg(code CodeType) string { return "Tx parse error" case CodeGenesisParse: return "Genesis parse error" - case CodeBadNonce: - return "Bad nonce" + case CodeInvalidSequence: + return "Invalid sequence" case CodeUnauthorized: return "Unauthorized" case CodeInsufficientFunds: @@ -51,8 +50,6 @@ func CodeToDefaultMsg(code CodeType) string { return "Unknown request" case CodeUnrecognizedAddress: return "Unrecognized address" - case CodeInvalidSequence: - return "Invalid sequence" default: return fmt.Sprintf("Unknown code %d", code) } @@ -72,8 +69,8 @@ func ErrTxParse(msg string) Error { func ErrGenesisParse(msg string) Error { return newError(CodeGenesisParse, msg) } -func ErrBadNonce(msg string) Error { - return newError(CodeBadNonce, msg) +func ErrInvalidSequence(msg string) Error { + return newError(CodeInvalidSequence, msg) } func ErrUnauthorized(msg string) Error { return newError(CodeUnauthorized, msg) @@ -87,9 +84,6 @@ func ErrUnknownRequest(msg string) Error { func ErrUnrecognizedAddress(addr Address) Error { return newError(CodeUnrecognizedAddress, addr.String()) } -func ErrInvalidSequence(msg string) Error { - return newError(CodeInvalidSequence, msg) -} //---------------------------------------- // Error & sdkError diff --git a/types/tx_msg.go b/types/tx_msg.go index c6c96c364c..63f9347265 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -1,5 +1,7 @@ package types +import "encoding/json" + // Transactions messages must fulfill the Msg type Msg interface { @@ -66,7 +68,31 @@ func (tx StdTx) GetMsg() Msg { return tx.Msg } func (tx StdTx) GetFeePayer() Address { return tx.Signatures[0].PubKey.Address() } // XXX but PubKey is optional! func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures } -//__________________________________________________________ +// StdSignDoc is replay-prevention structure. +// It includes the result of msg.GetSignBytes(), +// as well as the ChainID (prevent cross chain replay) +// and the Sequence numbers for each signature (prevent +// inchain replay and enforce tx ordering per account). +type StdSignDoc struct { + ChainID string `json:"chain_id"` + Sequences []int64 `json:"sequences"` + MsgBytes []byte `json:"msg_bytes"` + AltBytes []byte `json:"alt_bytes"` // TODO: do we really want this ? +} + +func StdSignBytes(chainID string, sequences []int64, msg Msg) []byte { + bz, err := json.Marshal(StdSignDoc{ + ChainID: chainID, + Sequences: sequences, + MsgBytes: msg.GetSignBytes(), + }) + if err != nil { + panic(err) + } + return bz +} + +//------------------------------------- // Application function variable used to unmarshal transaction bytes type TxDecoder func(txBytes []byte) (Tx, Error) diff --git a/x/auth/ante.go b/x/auth/ante.go index 11aa03c0d7..48e2344c6b 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -1,6 +1,8 @@ package auth import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -9,82 +11,98 @@ func NewAnteHandler(accountMapper sdk.AccountMapper) sdk.AnteHandler { ctx sdk.Context, tx sdk.Tx, ) (_ sdk.Context, _ sdk.Result, abort bool) { - // Deduct the fee from the fee payer. - // This is done first because it only - // requires fetching 1 account. - payerAddr := tx.GetFeePayer() - if payerAddr != nil { - payerAcc := accountMapper.GetAccount(ctx, payerAddr) - if payerAcc == nil { - return ctx, - sdk.ErrUnrecognizedAddress(payerAddr).Result(), - true - } - // TODO: Charge fee from payerAcc. - // TODO: accountMapper.SetAccount(ctx, payerAddr) - } else { - // TODO: Ensure that some other spam prevention is used. - } - - var sigs = tx.GetSignatures() - // Assert that there are signatures. + var sigs = tx.GetSignatures() if len(sigs) == 0 { return ctx, sdk.ErrUnauthorized("no signers").Result(), true } - // Ensure that sigs are correct. - var msg = tx.GetMsg() - var signerAddrs = msg.GetSigners() - var signerAccs = make([]sdk.Account, len(signerAddrs)) + // TODO: can tx just implement message? + msg := tx.GetMsg() // Assert that number of signatures is correct. + var signerAddrs = msg.GetSigners() if len(sigs) != len(signerAddrs) { return ctx, sdk.ErrUnauthorized("wrong number of signers").Result(), true } - // Check each nonce and sig. - // TODO Refactor out. - for i, sig := range sigs { + // Collect accounts to set in the context + var signerAccs = make([]sdk.Account, len(signerAddrs)) - var signerAcc = accountMapper.GetAccount(ctx, signerAddrs[i]) + // Get the sign bytes by collecting all sequence numbers + sequences := make([]int64, len(signerAddrs)) + for i := 0; i < len(signerAddrs); i++ { + sequences[i] = sigs[i].Sequence + } + signBytes := sdk.StdSignBytes(ctx.ChainID(), sequences, msg) + + // Check fee payer sig and nonce, and deduct fee. + // This is done first because it only + // requires fetching 1 account. + payerAddr, payerSig := signerAddrs[0], sigs[0] + payerAcc, res := processSig(ctx, accountMapper, payerAddr, payerSig, signBytes) + if !res.IsOK() { + return ctx, res, true + } + signerAccs[0] = payerAcc + // TODO: Charge fee from payerAcc. + // TODO: accountMapper.SetAccount(ctx, payerAddr) + + // Check sig and nonce for the rest. + for i := 1; i < len(sigs); i++ { + signerAddr, sig := signerAddrs[i], sigs[i] + signerAcc, res := processSig(ctx, accountMapper, signerAddr, sig, signBytes) + if !res.IsOK() { + return ctx, res, true + } signerAccs[i] = signerAcc - - // If no pubkey, set pubkey. - if signerAcc.GetPubKey().Empty() { - err := signerAcc.SetPubKey(sig.PubKey) - if err != nil { - return ctx, - sdk.ErrInternal("setting PubKey on signer").Result(), - true - } - } - - // Check and increment sequence number. - seq := signerAcc.GetSequence() - if seq != sig.Sequence { - return ctx, - sdk.ErrInvalidSequence("").Result(), - true - } - signerAcc.SetSequence(seq + 1) - - // Check sig. - if !sig.PubKey.VerifyBytes(msg.GetSignBytes(), sig.Signature) { - return ctx, - sdk.ErrUnauthorized("").Result(), - true - } - - // Save the account. - accountMapper.SetAccount(ctx, signerAcc) } ctx = WithSigners(ctx, signerAccs) return ctx, sdk.Result{}, false // continue... } } + +// verify the signature and increment the sequence. +// if the account doesn't have a pubkey, set it as well. +func processSig(ctx sdk.Context, am sdk.AccountMapper, addr sdk.Address, sig sdk.StdSignature, signBytes []byte) (acc sdk.Account, res sdk.Result) { + + // Get the account + acc = am.GetAccount(ctx, addr) + if acc == nil { + return nil, sdk.ErrUnrecognizedAddress(addr).Result() + } + + // Check and increment sequence number. + seq := acc.GetSequence() + if seq != sig.Sequence { + return nil, sdk.ErrInvalidSequence( + fmt.Sprintf("Invalid sequence. Got %d, expected %d", sig.Sequence, seq)).Result() + } + acc.SetSequence(seq + 1) + + // Check and possibly set pubkey. + pubKey := acc.GetPubKey() + if pubKey.Empty() { + pubKey = sig.PubKey + err := acc.SetPubKey(pubKey) + if err != nil { + return nil, sdk.ErrInternal("setting PubKey on signer").Result() + } + } + // TODO: should we enforce pubKey == sig.PubKey ? + // If not, ppl can send useless PubKeys after first tx + + // Check sig. + if !sig.PubKey.VerifyBytes(signBytes, sig.Signature) { + return nil, sdk.ErrUnauthorized("signature verification failed").Result() + } + + // Save the account. + am.SetAccount(ctx, acc) + return +} diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go new file mode 100644 index 0000000000..17ea204d31 --- /dev/null +++ b/x/auth/ante_test.go @@ -0,0 +1,163 @@ +package auth + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + abci "github.com/tendermint/abci/types" + crypto "github.com/tendermint/go-crypto" +) + +// msg type for testing +type testMsg struct { + signBytes []byte + signers []sdk.Address +} + +func newTestMsg(addrs ...sdk.Address) *testMsg { + return &testMsg{ + signBytes: []byte("some sign bytes"), + signers: addrs, + } +} + +func (msg *testMsg) Type() string { return "testMsg" } +func (msg *testMsg) Get(key interface{}) (value interface{}) { return nil } +func (msg *testMsg) GetSignBytes() []byte { + return msg.signBytes +} +func (msg *testMsg) ValidateBasic() sdk.Error { return nil } +func (msg *testMsg) GetSigners() []sdk.Address { + return msg.signers +} + +// generate a priv key and return it with its address +func privAndAddr() (crypto.PrivKey, sdk.Address) { + priv := crypto.GenPrivKeyEd25519() + addr := priv.PubKey().Address() + return priv.Wrap(), addr +} + +// run the tx through the anteHandler and ensure its valid +func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx) { + _, result, abort := anteHandler(ctx, tx) + assert.False(t, abort) + assert.Equal(t, sdk.CodeOK, result.Code) + assert.True(t, result.IsOK()) +} + +// run the tx through the anteHandler and ensure it fails with the given code +func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, code sdk.CodeType) { + _, result, abort := anteHandler(ctx, tx) + assert.True(t, abort) + assert.Equal(t, code, result.Code) +} + +func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, seqs []int64) sdk.Tx { + signBytes := sdk.StdSignBytes(ctx.ChainID(), seqs, msg) + sigs := make([]sdk.StdSignature, len(privs)) + for i, priv := range privs { + sigs[i] = sdk.StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), Sequence: seqs[i]} + } + return sdk.NewStdTx(msg, sigs) +} + +// Test various error cases in the AnteHandler control flow. +func TestAnteHandlerSigErrors(t *testing.T) { + // setup + ms, capKey := setupMultiStore() + mapper := NewAccountMapper(capKey, &BaseAccount{}) + anteHandler := NewAnteHandler(mapper) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil) + + // keys and addresses + priv1, addr1 := privAndAddr() + priv2, addr2 := privAndAddr() + + // msg and signatures + var tx sdk.Tx + msg := newTestMsg(addr1, addr2) + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1, priv2}, []int64{0, 0}) + + // test no signatures + tx = newTestTx(ctx, msg, []crypto.PrivKey{}, []int64{}) + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized) + + // test num sigs dont match GetSigners + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1}, []int64{0}) + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized) + + // test an unrecognized account + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1, priv2}, []int64{0, 0}) + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnrecognizedAddress) + + // save the first account, but second is still unrecognized + acc1 := mapper.NewAccountWithAddress(ctx, addr1) + mapper.SetAccount(ctx, acc1) + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnrecognizedAddress) +} + +// Test logic around sequence checking with one signer and many signers. +func TestAnteHandlerSequences(t *testing.T) { + // setup + ms, capKey := setupMultiStore() + mapper := NewAccountMapper(capKey, &BaseAccount{}) + anteHandler := NewAnteHandler(mapper) + ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil) + + // keys and addresses + priv1, addr1 := privAndAddr() + priv2, addr2 := privAndAddr() + + // set the accounts + acc1 := mapper.NewAccountWithAddress(ctx, addr1) + mapper.SetAccount(ctx, acc1) + acc2 := mapper.NewAccountWithAddress(ctx, addr2) + mapper.SetAccount(ctx, acc2) + + // msg and signatures + var tx sdk.Tx + msg := newTestMsg(addr1) + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1}, []int64{0}) + + // test good tx from one signer + checkValidTx(t, anteHandler, ctx, tx) + + // test sending it again fails (replay protection) + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + + // fix sequence, should pass + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1}, []int64{1}) + checkValidTx(t, anteHandler, ctx, tx) + + // new tx with another signer and correct sequences + msg = newTestMsg(addr1, addr2) + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1, priv2}, []int64{2, 0}) + checkValidTx(t, anteHandler, ctx, tx) + + // replay fails + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + + // tx from just second signer with incorrect sequence fails + msg = newTestMsg(addr2) + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv2}, []int64{0}) + checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence) + + // fix the sequence and it passes + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv2}, []int64{1}) + checkValidTx(t, anteHandler, ctx, tx) + + // another tx from both of them that passes + msg = newTestMsg(addr1, addr2) + tx = newTestTx(ctx, msg, []crypto.PrivKey{priv1, priv2}, []int64{3, 2}) + checkValidTx(t, anteHandler, ctx, tx) +} + +func TestAnteHandlerBadSignBytes(t *testing.T) { + // TODO: test various cases of bad sign bytes +} + +func TestAnteHandlerSetPubKey(t *testing.T) { + // TODO: test cases where pubkey is already set on the account +} diff --git a/x/auth/mapper.go b/x/auth/mapper.go index b50e4405fd..1176db3cbe 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -11,6 +11,9 @@ import ( wire "github.com/cosmos/cosmos-sdk/wire" ) +var _ sdk.AccountMapper = (*accountMapper)(nil) +var _ sdk.AccountMapper = (*sealedAccountMapper)(nil) + // Implements sdk.AccountMapper. // This AccountMapper encodes/decodes accounts using the // go-wire (binary) encoding/decoding library. @@ -108,6 +111,7 @@ func (sam sealedAccountMapper) WireCodec() *wire.Codec { //---------------------------------------- // misc. +// NOTE: currently unused func (am accountMapper) clonePrototypePtr() interface{} { protoRt := reflect.TypeOf(am.proto) if protoRt.Kind() == reflect.Ptr { diff --git a/x/auth/mapper_test.go b/x/auth/mapper_test.go new file mode 100644 index 0000000000..4ac96c3810 --- /dev/null +++ b/x/auth/mapper_test.go @@ -0,0 +1,81 @@ +package auth + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + abci "github.com/tendermint/abci/types" + crypto "github.com/tendermint/go-crypto" + oldwire "github.com/tendermint/go-wire" + dbm "github.com/tendermint/tmlibs/db" + + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func setupMultiStore() (sdk.MultiStore, *sdk.KVStoreKey) { + db := dbm.NewMemDB() + capKey := sdk.NewKVStoreKey("capkey") + ms := store.NewCommitMultiStore(db) + ms.MountStoreWithDB(capKey, sdk.StoreTypeIAVL, db) + ms.LoadLatestVersion() + + // wire registration while we're at it ... TODO + var _ = oldwire.RegisterInterface( + struct{ sdk.Account }{}, + oldwire.ConcreteType{&BaseAccount{}, 0x1}, + ) + + return ms, capKey +} + +func TestAccountMapperGetSet(t *testing.T) { + ms, capKey := setupMultiStore() + + // make context and mapper + ctx := sdk.NewContext(ms, abci.Header{}, false, nil) + mapper := NewAccountMapper(capKey, &BaseAccount{}) + + addr := sdk.Address([]byte("some-address")) + + // no account before its created + acc := mapper.GetAccount(ctx, addr) + assert.Nil(t, acc) + + // create account and check default values + acc = mapper.NewAccountWithAddress(ctx, addr) + assert.NotNil(t, acc) + assert.Equal(t, addr, acc.GetAddress()) + assert.EqualValues(t, crypto.PubKey{}, acc.GetPubKey()) + assert.EqualValues(t, 0, acc.GetSequence()) + + // NewAccount doesn't call Set, so it's still nil + assert.Nil(t, mapper.GetAccount(ctx, addr)) + + // set some values on the account and save it + newSequence := int64(20) + acc.SetSequence(newSequence) + mapper.SetAccount(ctx, acc) + + // check the new values + acc = mapper.GetAccount(ctx, addr) + assert.NotNil(t, acc) + assert.Equal(t, newSequence, acc.GetSequence()) +} + +func TestAccountMapperSealed(t *testing.T) { + _, capKey := setupMultiStore() + + // normal mapper exposes the wire codec + mapper := NewAccountMapper(capKey, &BaseAccount{}) + assert.NotNil(t, mapper.WireCodec()) + + // seal mapper, should panic when we try to get the codec + mapperSealed := mapper.Seal() + assert.Panics(t, func() { mapperSealed.WireCodec() }) + + // another way to get a sealed mapper + mapperSealed = NewAccountMapperSealed(capKey, &BaseAccount{}) + assert.Panics(t, func() { mapperSealed.WireCodec() }) +} diff --git a/x/bank/tx.go b/x/bank/tx.go index eef2a087d0..58af937210 100644 --- a/x/bank/tx.go +++ b/x/bank/tx.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" - crypto "github.com/tendermint/go-crypto" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -142,11 +140,8 @@ func (msg IssueMsg) GetSigners() []sdk.Address { // Transaction Output type Input struct { - Address sdk.Address `json:"address"` - Coins sdk.Coins `json:"coins"` - Sequence int64 `json:"sequence"` - - signature crypto.Signature + Address sdk.Address `json:"address"` + Coins sdk.Coins `json:"coins"` } // ValidateBasic - validate transaction input @@ -154,9 +149,6 @@ func (in Input) ValidateBasic() sdk.Error { if len(in.Address) == 0 { return ErrInvalidAddress(in.Address.String()) } - if in.Sequence < 0 { - return ErrInvalidSequence("negative sequence") - } if !in.Coins.IsValid() { return ErrInvalidCoins(in.Coins.String()) } @@ -179,13 +171,6 @@ func NewInput(addr sdk.Address, coins sdk.Coins) Input { return input } -// NewInputWithSequence - create a transaction input, used with SendMsg -func NewInputWithSequence(addr sdk.Address, coins sdk.Coins, seq int64) Input { - input := NewInput(addr, coins) - input.Sequence = seq - return input -} - //---------------------------------------- // Output diff --git a/x/bank/tx_test.go b/x/bank/tx_test.go index 099e81dbf4..bbfdc62ffd 100644 --- a/x/bank/tx_test.go +++ b/x/bank/tx_test.go @@ -13,20 +13,12 @@ func TestNewSendMsg(t *testing.T) {} func TestSendMsgType(t *testing.T) { // Construct a SendMsg + addr1 := sdk.Address([]byte("input")) + addr2 := sdk.Address([]byte("output")) + coins := sdk.Coins{{"atom", 10}} var msg = SendMsg{ - Inputs: []Input{ - { - Address: sdk.Address([]byte("input")), - Coins: sdk.Coins{{"atom", 10}}, - Sequence: 1, - }, - }, - Outputs: []Output{ - { - Address: sdk.Address([]byte("output")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Inputs: []Input{NewInput(addr1, coins)}, + Outputs: []Output{NewOutput(addr2, coins)}, } // TODO some failures for bad result @@ -53,18 +45,16 @@ func TestInputValidation(t *testing.T) { }{ // auth works with different apps {true, NewInput(addr1, someCoins)}, - {true, NewInputWithSequence(addr1, someCoins, 100)}, - {true, NewInputWithSequence(addr2, someCoins, 100)}, - {true, NewInputWithSequence(addr2, multiCoins, 100)}, + {true, NewInput(addr2, someCoins)}, + {true, NewInput(addr2, multiCoins)}, - {false, NewInput(emptyAddr, someCoins)}, // empty address - {false, NewInputWithSequence(addr1, someCoins, -1)}, // negative sequence - {false, NewInput(addr1, emptyCoins)}, // invalid coins - {false, NewInput(addr1, emptyCoins2)}, // invalid coins - {false, NewInput(addr1, someEmptyCoins)}, // invalid coins - {false, NewInput(addr1, minusCoins)}, // negative coins - {false, NewInput(addr1, someMinusCoins)}, // negative coins - {false, NewInput(addr1, unsortedCoins)}, // unsorted coins + {false, NewInput(emptyAddr, someCoins)}, // empty address + {false, NewInput(addr1, emptyCoins)}, // invalid coins + {false, NewInput(addr1, emptyCoins2)}, // invalid coins + {false, NewInput(addr1, someEmptyCoins)}, // invalid coins + {false, NewInput(addr1, minusCoins)}, // negative coins + {false, NewInput(addr1, someMinusCoins)}, // negative coins + {false, NewInput(addr1, unsortedCoins)}, // unsorted coins } for i, tc := range cases { @@ -144,7 +134,7 @@ func TestSendMsgValidation(t *testing.T) { {false, SendMsg{Inputs: []Input{input1}}}, // just input {false, SendMsg{Outputs: []Output{output1}}}, // just ouput {false, SendMsg{ - Inputs: []Input{NewInputWithSequence(emptyAddr, atom123, 1)}, // invalid input + Inputs: []Input{NewInput(emptyAddr, atom123)}, // invalid input Outputs: []Output{output1}}}, {false, SendMsg{ Inputs: []Input{input1}, @@ -189,82 +179,54 @@ func TestSendMsgValidation(t *testing.T) { func TestSendMsgString(t *testing.T) { // Construct a SendMsg + addr1 := sdk.Address([]byte("input")) + addr2 := sdk.Address([]byte("output")) + coins := sdk.Coins{{"atom", 10}} var msg = SendMsg{ - Inputs: []Input{ - { - Address: sdk.Address([]byte("input")), - Coins: sdk.Coins{{"atom", 10}}, - Sequence: 1, - }, - }, - Outputs: []Output{ - { - Address: sdk.Address([]byte("output")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Inputs: []Input{NewInput(addr1, coins)}, + Outputs: []Output{NewOutput(addr2, coins)}, } + res := msg.String() // TODO some failures for bad results assert.Equal(t, res, "SendMsg{[Input{696E707574,10atom}]->[Output{364637353734373037353734,10atom}]}") } func TestSendMsgGet(t *testing.T) { + addr1 := sdk.Address([]byte("input")) + addr2 := sdk.Address([]byte("output")) + coins := sdk.Coins{{"atom", 10}} var msg = SendMsg{ - Inputs: []Input{ - { - Address: sdk.Address([]byte("input")), - Coins: sdk.Coins{{"atom", 10}}, - Sequence: 1, - }, - }, - Outputs: []Output{ - { - Address: sdk.Address([]byte("output")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Inputs: []Input{NewInput(addr1, coins)}, + Outputs: []Output{NewOutput(addr2, coins)}, } res := msg.Get(nil) assert.Nil(t, res) } func TestSendMsgGetSignBytes(t *testing.T) { + addr1 := sdk.Address([]byte("input")) + addr2 := sdk.Address([]byte("output")) + coins := sdk.Coins{{"atom", 10}} var msg = SendMsg{ - Inputs: []Input{ - { - Address: sdk.Address([]byte("input")), - Coins: sdk.Coins{{"atom", 10}}, - Sequence: 1, - }, - }, - Outputs: []Output{ - { - Address: sdk.Address([]byte("output")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Inputs: []Input{NewInput(addr1, coins)}, + Outputs: []Output{NewOutput(addr2, coins)}, } res := msg.GetSignBytes() // TODO bad results - assert.Equal(t, string(res), `{"inputs":[{"address":"696E707574","coins":[{"denom":"atom","amount":10}],"sequence":1}],"outputs":[{"address":"6F7574707574","coins":[{"denom":"atom","amount":10}]}]}`) + assert.Equal(t, string(res), `{"inputs":[{"address":"696E707574","coins":[{"denom":"atom","amount":10}]}],"outputs":[{"address":"6F7574707574","coins":[{"denom":"atom","amount":10}]}]}`) } func TestSendMsgGetSigners(t *testing.T) { var msg = SendMsg{ Inputs: []Input{ - { - Address: sdk.Address([]byte("input1")), - }, - { - Address: sdk.Address([]byte("input2")), - }, - { - Address: sdk.Address([]byte("input3")), - }, + NewInput(sdk.Address([]byte("input1")), nil), + NewInput(sdk.Address([]byte("input2")), nil), + NewInput(sdk.Address([]byte("input3")), nil), }, } res := msg.GetSigners() + // TODO: fix this ! assert.Equal(t, fmt.Sprintf("%v", res), "[696E70757431 696E70757432 696E70757433]") } @@ -297,14 +259,11 @@ func TestNewIssueMsg(t *testing.T) { func TestIssueMsgType(t *testing.T) { // Construct an IssueMsg + addr := sdk.Address([]byte("loan-from-bank")) + coins := sdk.Coins{{"atom", 10}} var msg = IssueMsg{ - Banker: sdk.Address([]byte("input")), - Outputs: []Output{ - { - Address: sdk.Address([]byte("loan-from-bank")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Banker: sdk.Address([]byte("input")), + Outputs: []Output{NewOutput(addr, coins)}, } // TODO some failures for bad result @@ -317,42 +276,34 @@ func TestIssueMsgValidation(t *testing.T) { func TestIssueMsgString(t *testing.T) { // Construct a IssueMsg + addr := sdk.Address([]byte("loan-from-bank")) + coins := sdk.Coins{{"atom", 10}} var msg = IssueMsg{ - Banker: sdk.Address([]byte("input")), - Outputs: []Output{ - { - Address: sdk.Address([]byte("loan-from-bank")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Banker: sdk.Address([]byte("input")), + Outputs: []Output{NewOutput(addr, coins)}, } res := msg.String() + // TODO: FIX THIS OUTPUT! assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}") } func TestIssueMsgGet(t *testing.T) { + addr := sdk.Address([]byte("loan-from-bank")) + coins := sdk.Coins{{"atom", 10}} var msg = IssueMsg{ - Banker: sdk.Address([]byte("input")), - Outputs: []Output{ - { - Address: sdk.Address([]byte("loan-from-bank")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Banker: sdk.Address([]byte("input")), + Outputs: []Output{NewOutput(addr, coins)}, } res := msg.Get(nil) assert.Nil(t, res) } func TestIssueMsgGetSignBytes(t *testing.T) { + addr := sdk.Address([]byte("loan-from-bank")) + coins := sdk.Coins{{"atom", 10}} var msg = IssueMsg{ - Banker: sdk.Address([]byte("input")), - Outputs: []Output{ - { - Address: sdk.Address([]byte("loan-from-bank")), - Coins: sdk.Coins{{"atom", 10}}, - }, - }, + Banker: sdk.Address([]byte("input")), + Outputs: []Output{NewOutput(addr, coins)}, } res := msg.GetSignBytes() // TODO bad results