commit
d4c13b093c
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
130
x/auth/ante.go
130
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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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() })
|
||||
}
|
15
x/bank/tx.go
15
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"
|
||||
)
|
||||
|
||||
|
@ -144,9 +142,6 @@ func (msg IssueMsg) GetSigners() []sdk.Address {
|
|||
type Input struct {
|
||||
Address sdk.Address `json:"address"`
|
||||
Coins sdk.Coins `json:"coins"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
|
||||
signature crypto.Signature
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
|
|
|
@ -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,12 +45,10 @@ 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
|
||||
|
@ -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}},
|
||||
},
|
||||
},
|
||||
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}},
|
||||
},
|
||||
},
|
||||
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}},
|
||||
},
|
||||
},
|
||||
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}},
|
||||
},
|
||||
},
|
||||
Outputs: []Output{NewOutput(addr, coins)},
|
||||
}
|
||||
res := msg.GetSignBytes()
|
||||
// TODO bad results
|
||||
|
|
Loading…
Reference in New Issue