Merge pull request #563 from cosmos/bucky/sequence

Replay Protection
This commit is contained in:
Ethan Buchman 2018-03-13 01:52:45 +01:00 committed by GitHub
commit d4c13b093c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 436 additions and 205 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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)

View File

@ -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
}

163
x/auth/ante_test.go Normal file
View File

@ -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
}

View File

@ -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 {

81
x/auth/mapper_test.go Normal file
View File

@ -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() })
}

View File

@ -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

View File

@ -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