Account Numbers for Replay Protection with Account Pruning (#1077)

* in progress
* passes current tests
* added tests and got working
* changelog and docs
* removed prints
* oops
* works!
* issue in test_cli
* number 9 number 9 number 9
* at least it does only the 9 issue now
* hallelujah it works!
* removed print statement
* rebased
* gah
This commit is contained in:
Sunny Aggarwal
2018-06-12 04:30:54 +02:00
committed by Christopher Goes
parent 1b20adcd22
commit 161cb474d4
28 changed files with 337 additions and 173 deletions
+19 -4
View File
@@ -17,6 +17,9 @@ type Account interface {
GetPubKey() crypto.PubKey // can return nil.
SetPubKey(crypto.PubKey) error
GetAccountNumber() int64
SetAccountNumber(int64) error
GetSequence() int64
SetSequence(int64) error
@@ -36,10 +39,11 @@ var _ Account = (*BaseAccount)(nil)
// Extend this by embedding this in your AppAccount.
// See the examples/basecoin/types/account.go for an example.
type BaseAccount struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"`
Sequence int64 `json:"sequence"`
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
PubKey crypto.PubKey `json:"public_key"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
func NewBaseAccountWithAddress(addr sdk.Address) BaseAccount {
@@ -84,6 +88,17 @@ func (acc *BaseAccount) SetCoins(coins sdk.Coins) error {
return nil
}
// Implements Account
func (acc *BaseAccount) GetAccountNumber() int64 {
return acc.AccountNumber
}
// Implements Account
func (acc *BaseAccount) SetAccountNumber(accNumber int64) error {
acc.AccountNumber = accNumber
return nil
}
// Implements sdk.Account.
func (acc *BaseAccount) GetSequence() int64 {
return acc.Sequence
+14 -3
View File
@@ -14,7 +14,7 @@ const (
)
// NewAnteHandler returns an AnteHandler that checks
// and increments sequence numbers, checks signatures,
// and increments sequence numbers, checks signatures & account numbers,
// and deducts fees from the first signer.
func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
@@ -46,11 +46,15 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
true
}
// Get the sign bytes (requires all sequence numbers and the fee)
// Get the sign bytes (requires all account & sequence numbers and the fee)
sequences := make([]int64, len(signerAddrs))
for i := 0; i < len(signerAddrs); i++ {
sequences[i] = sigs[i].Sequence
}
accNums := make([]int64, len(signerAddrs))
for i := 0; i < len(signerAddrs); i++ {
accNums[i] = sigs[i].AccountNumber
}
fee := stdTx.Fee
chainID := ctx.ChainID()
// XXX: major hack; need to get ChainID
@@ -58,7 +62,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
if chainID == "" {
chainID = viper.GetString("chain-id")
}
signBytes := StdSignBytes(ctx.ChainID(), sequences, fee, msg)
signBytes := StdSignBytes(ctx.ChainID(), accNums, sequences, fee, msg)
// Check sig and nonce and collect signer accounts.
var signerAccs = make([]Account, len(signerAddrs))
@@ -117,6 +121,13 @@ func processSig(
return nil, sdk.ErrUnknownAddress(addr.String()).Result()
}
// Check account number.
accnum := acc.GetAccountNumber()
if accnum != sig.AccountNumber {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
}
// Check and increment sequence number.
seq := acc.GetSequence()
if seq != sig.Sequence {
+98 -41
View File
@@ -52,15 +52,15 @@ func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context,
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, code), result.Code)
}
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee StdFee) sdk.Tx {
signBytes := StdSignBytes(ctx.ChainID(), seqs, fee, msg)
return newTestTxWithSignBytes(msg, privs, seqs, fee, signBytes)
func newTestTx(ctx sdk.Context, msg sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee) sdk.Tx {
signBytes := StdSignBytes(ctx.ChainID(), accNums, seqs, fee, msg)
return newTestTxWithSignBytes(msg, privs, accNums, seqs, fee, signBytes)
}
func newTestTxWithSignBytes(msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee StdFee, signBytes []byte) sdk.Tx {
func newTestTxWithSignBytes(msg sdk.Msg, privs []crypto.PrivKey, accNums []int64, seqs []int64, fee StdFee, signBytes []byte) sdk.Tx {
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), Sequence: seqs[i]}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: priv.Sign(signBytes), AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msg, fee, sigs)
return tx
@@ -87,18 +87,18 @@ func TestAnteHandlerSigErrors(t *testing.T) {
fee := newStdFee()
// test no signatures
privs, seqs := []crypto.PrivKey{}, []int64{}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accNums, seqs := []crypto.PrivKey{}, []int64{}, []int64{}
tx = newTestTx(ctx, msg, privs, accNums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized)
// test num sigs dont match GetSigners
privs, seqs = []crypto.PrivKey{priv1}, []int64{0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accNums, seqs = []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
tx = newTestTx(ctx, msg, privs, accNums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized)
// test an unrecognized account
privs, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accNums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{0, 0}
tx = newTestTx(ctx, msg, privs, accNums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnknownAddress)
// save the first account, but second is still unrecognized
@@ -108,6 +108,61 @@ func TestAnteHandlerSigErrors(t *testing.T) {
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnknownAddress)
}
// Test logic around account number checking with one signer and many signers.
func TestAnteHandlerAccountNumbers(t *testing.T) {
// setup
ms, capKey, capKey2 := setupMultiStore()
cdc := wire.NewCodec()
RegisterBaseAccount(cdc)
mapper := NewAccountMapper(cdc, capKey, &BaseAccount{})
feeCollector := NewFeeCollectionKeeper(cdc, capKey2)
anteHandler := NewAnteHandler(mapper, feeCollector)
ctx := sdk.NewContext(ms, abci.Header{ChainID: "mychainid"}, false, nil, log.NewNopLogger())
// keys and addresses
priv1, addr1 := privAndAddr()
priv2, addr2 := privAndAddr()
// set the accounts
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
acc1.SetCoins(newCoins())
mapper.SetAccount(ctx, acc1)
acc2 := mapper.NewAccountWithAddress(ctx, addr2)
acc2.SetCoins(newCoins())
mapper.SetAccount(ctx, acc2)
// msg and signatures
var tx sdk.Tx
msg := newTestMsg(addr1)
fee := newStdFee()
// test good tx from one signer
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
// new tx from wrong account number
seqs = []int64{1}
tx = newTestTx(ctx, msg, privs, []int64{1}, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
// from correct account number
seqs = []int64{1}
tx = newTestTx(ctx, msg, privs, []int64{0}, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
// new tx with another signer and incorrect account numbers
msg = newTestMsg(addr1, addr2)
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{1, 0}, []int64{2, 0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
// correct account numbers
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{2, 0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
}
// Test logic around sequence checking with one signer and many signers.
func TestAnteHandlerSequences(t *testing.T) {
// setup
@@ -137,8 +192,8 @@ func TestAnteHandlerSequences(t *testing.T) {
fee := newStdFee()
// test good tx from one signer
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
// test sending it again fails (replay protection)
@@ -146,13 +201,13 @@ func TestAnteHandlerSequences(t *testing.T) {
// fix sequence, should pass
seqs = []int64{1}
tx = newTestTx(ctx, msg, privs, seqs, fee)
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
// new tx with another signer and correct sequences
msg = newTestMsg(addr1, addr2)
privs, seqs = []crypto.PrivKey{priv1, priv2}, []int64{2, 0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{2, 0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
// replay fails
@@ -160,18 +215,18 @@ func TestAnteHandlerSequences(t *testing.T) {
// tx from just second signer with incorrect sequence fails
msg = newTestMsg(addr2)
privs, seqs = []crypto.PrivKey{priv2}, []int64{0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{1}, []int64{0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
// fix the sequence and it passes
tx = newTestTx(ctx, msg, []crypto.PrivKey{priv2}, []int64{1}, fee)
tx = newTestTx(ctx, msg, []crypto.PrivKey{priv2}, []int64{1}, []int64{1}, fee)
checkValidTx(t, anteHandler, ctx, tx)
// another tx from both of them that passes
msg = newTestMsg(addr1, addr2)
privs, seqs = []crypto.PrivKey{priv1, priv2}, []int64{3, 2}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs = []crypto.PrivKey{priv1, priv2}, []int64{0, 1}, []int64{3, 2}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
}
@@ -196,13 +251,13 @@ func TestAnteHandlerFees(t *testing.T) {
// msg and signatures
var tx sdk.Tx
msg := newTestMsg(addr1)
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
fee := NewStdFee(100,
sdk.Coin{"atom", 150},
)
// signer does not have enough funds to pay the fee
tx = newTestTx(ctx, msg, privs, seqs, fee)
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds)
acc1.SetCoins(sdk.Coins{{"atom", 149}})
@@ -249,8 +304,8 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
fee3.Amount[0].Amount += 100
// test good tx and signBytes
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
chainID := ctx.ChainID()
@@ -259,37 +314,39 @@ func TestAnteHandlerBadSignBytes(t *testing.T) {
cases := []struct {
chainID string
accnums []int64
seqs []int64
fee StdFee
msg sdk.Msg
code sdk.CodeType
}{
{chainID2, []int64{1}, fee, msg, codeUnauth}, // test wrong chain_id
{chainID, []int64{2}, fee, msg, codeUnauth}, // test wrong seqs
{chainID, []int64{1, 2}, fee, msg, codeUnauth}, // test wrong seqs
{chainID, []int64{1}, fee, newTestMsg(addr2), codeUnauth}, // test wrong msg
{chainID, []int64{1}, fee2, newTestMsg(addr2), codeUnauth}, // test wrong fee
{chainID, []int64{1}, fee3, newTestMsg(addr2), codeUnauth}, // test wrong fee
{chainID2, []int64{0}, []int64{1}, fee, msg, codeUnauth}, // test wrong chain_id
{chainID, []int64{0}, []int64{2}, fee, msg, codeUnauth}, // test wrong seqs
{chainID, []int64{0}, []int64{1, 2}, fee, msg, codeUnauth}, // test wrong seqs
{chainID, []int64{1}, []int64{1}, fee, msg, codeUnauth}, // test wrong accnum
{chainID, []int64{0}, []int64{1}, fee, newTestMsg(addr2), codeUnauth}, // test wrong msg
{chainID, []int64{0}, []int64{1}, fee2, msg, codeUnauth}, // test wrong fee
{chainID, []int64{0}, []int64{1}, fee3, msg, codeUnauth}, // test wrong fee
}
privs, seqs = []crypto.PrivKey{priv1}, []int64{1}
for _, cs := range cases {
tx := newTestTxWithSignBytes(
msg, privs, seqs, fee,
StdSignBytes(cs.chainID, cs.seqs, cs.fee, cs.msg),
msg, privs, accnums, seqs, fee,
StdSignBytes(cs.chainID, cs.accnums, cs.seqs, cs.fee, cs.msg),
)
checkInvalidTx(t, anteHandler, ctx, tx, cs.code)
}
// test wrong signer if public key exist
privs, seqs = []crypto.PrivKey{priv2}, []int64{1}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs = []crypto.PrivKey{priv2}, []int64{0}, []int64{1}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnauthorized)
// test wrong signer if public doesn't exist
msg = newTestMsg(addr2)
privs, seqs = []crypto.PrivKey{priv1}, []int64{0}
tx = newTestTx(ctx, msg, privs, seqs, fee)
privs, accnums, seqs = []crypto.PrivKey{priv1}, []int64{1}, []int64{0}
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
}
@@ -320,9 +377,9 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
// test good tx and set public key
msg := newTestMsg(addr1)
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
privs, accnums, seqs := []crypto.PrivKey{priv1}, []int64{0}, []int64{0}
fee := newStdFee()
tx = newTestTx(ctx, msg, privs, seqs, fee)
tx = newTestTx(ctx, msg, privs, accnums, seqs, fee)
checkValidTx(t, anteHandler, ctx, tx)
acc1 = mapper.GetAccount(ctx, addr1)
@@ -330,7 +387,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
// test public key not found
msg = newTestMsg(addr2)
tx = newTestTx(ctx, msg, privs, seqs, fee)
tx = newTestTx(ctx, msg, privs, []int64{1}, seqs, fee)
sigs := tx.(StdTx).GetSignatures()
sigs[0].PubKey = nil
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
@@ -339,7 +396,7 @@ func TestAnteHandlerSetPubKey(t *testing.T) {
assert.Nil(t, acc2.GetPubKey())
// test invalid signature and public key
tx = newTestTx(ctx, msg, privs, seqs, fee)
tx = newTestTx(ctx, msg, privs, []int64{1}, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
acc2 = mapper.GetAccount(ctx, addr2)
+1 -1
View File
@@ -47,7 +47,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode
// perform query
ctx := context.NewCoreContextFromViper()
res, err := ctx.Query(key, storeName)
res, err := ctx.Query(auth.AddressStoreKey(key), storeName)
if err != nil {
return err
}
+1 -1
View File
@@ -34,7 +34,7 @@ func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder aut
return
}
res, err := ctx.Query(addr, storeName)
res, err := ctx.Query(auth.AddressStoreKey(addr), storeName)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(fmt.Sprintf("Could't query account. Error: %s", err.Error())))
+37 -3
View File
@@ -9,6 +9,8 @@ import (
crypto "github.com/tendermint/go-crypto"
)
var globalAccountNumberKey = []byte("globalAccountNumber")
// This AccountMapper encodes/decodes accounts using the
// go-amino (binary) encoding/decoding library.
type AccountMapper struct {
@@ -38,13 +40,25 @@ func NewAccountMapper(cdc *wire.Codec, key sdk.StoreKey, proto Account) AccountM
func (am AccountMapper) NewAccountWithAddress(ctx sdk.Context, addr sdk.Address) Account {
acc := am.clonePrototype()
acc.SetAddress(addr)
acc.SetAccountNumber(am.GetNextAccountNumber(ctx))
return acc
}
// New Account
func (am AccountMapper) NewAccount(ctx sdk.Context, acc Account) Account {
acc.SetAccountNumber(am.GetNextAccountNumber(ctx))
return acc
}
// Turn an address to key used to get it from the account store
func AddressStoreKey(addr sdk.Address) []byte {
return append([]byte("account:"), addr.Bytes()...)
}
// Implements sdk.AccountMapper.
func (am AccountMapper) GetAccount(ctx sdk.Context, addr sdk.Address) Account {
store := ctx.KVStore(am.key)
bz := store.Get(addr)
bz := store.Get(AddressStoreKey(addr))
if bz == nil {
return nil
}
@@ -57,13 +71,13 @@ func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) {
addr := acc.GetAddress()
store := ctx.KVStore(am.key)
bz := am.encodeAccount(acc)
store.Set(addr, bz)
store.Set(AddressStoreKey(addr), bz)
}
// Implements sdk.AccountMapper.
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) {
store := ctx.KVStore(am.key)
iter := store.Iterator(nil, nil)
iter := sdk.KVStorePrefixIterator(store, []byte("account:"))
for {
if !iter.Valid() {
return
@@ -116,6 +130,26 @@ func (am AccountMapper) setSequence(ctx sdk.Context, addr sdk.Address, newSequen
return nil
}
// Returns and increments the global account number counter
func (am AccountMapper) GetNextAccountNumber(ctx sdk.Context) int64 {
var accNumber int64
store := ctx.KVStore(am.key)
bz := store.Get(globalAccountNumberKey)
if bz == nil {
accNumber = 0
} else {
err := am.cdc.UnmarshalBinary(bz, &accNumber)
if err != nil {
panic(err)
}
}
bz = am.cdc.MustMarshalBinary(accNumber + 1)
store.Set(globalAccountNumberKey, bz)
return accNumber
}
//----------------------------------------
// misc.
+3 -1
View File
@@ -78,7 +78,9 @@ func (app *App) CompleteSetup(t *testing.T, newKeys []*sdk.KVStoreKey) {
func (app *App) InitChainer(ctx sdk.Context, _ abci.RequestInitChain) abci.ResponseInitChain {
// load the accounts
for _, acc := range app.GenesisAccounts {
for _, genacc := range app.GenesisAccounts {
acc := app.AccountMapper.NewAccountWithAddress(ctx, genacc.GetAddress())
acc.SetCoins(genacc.GetCoins())
app.AccountMapper.SetAccount(ctx, acc)
}
+4 -4
View File
@@ -61,7 +61,7 @@ func TestMsgChangePubKey(t *testing.T) {
assert.Equal(t, acc1, res1.(*auth.BaseAccount))
// Run a CheckDeliver
SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, true, priv1)
SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, true, priv1)
// Check balances
CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 67}})
@@ -77,19 +77,19 @@ func TestMsgChangePubKey(t *testing.T) {
acc2 := mapp.AccountMapper.GetAccount(ctxDeliver, addr1)
// send a MsgChangePubKey
SignCheckDeliver(t, mapp.BaseApp, changePubKeyMsg, []int64{1}, true, priv1)
SignCheckDeliver(t, mapp.BaseApp, changePubKeyMsg, []int64{0}, []int64{1}, true, priv1)
acc2 = mapp.AccountMapper.GetAccount(ctxDeliver, addr1)
assert.True(t, priv2.PubKey().Equals(acc2.GetPubKey()))
// signing a SendMsg with the old privKey should be an auth error
mapp.BeginBlock(abci.RequestBeginBlock{})
tx := GenTx(sendMsg1, []int64{2}, priv1)
tx := GenTx(sendMsg1, []int64{0}, []int64{2}, priv1)
res := mapp.Deliver(tx)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the new correct priv key should work
SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{2}, true, priv2)
SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{2}, true, priv2)
// Check balances
CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 57}})
+7 -6
View File
@@ -33,7 +33,7 @@ func CheckBalance(t *testing.T, app *App, addr sdk.Address, exp sdk.Coins) {
}
// generate a signed transaction
func GenTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
func GenTx(msg sdk.Msg, accnums []int64, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
// make the transaction free
fee := auth.StdFee{
@@ -44,19 +44,20 @@ func GenTx(msg sdk.Msg, seq []int64, priv ...crypto.PrivKeyEd25519) auth.StdTx {
sigs := make([]auth.StdSignature, len(priv))
for i, p := range priv {
sigs[i] = auth.StdSignature{
PubKey: p.PubKey(),
Signature: p.Sign(auth.StdSignBytes(chainID, seq, fee, msg)),
Sequence: seq[i],
PubKey: p.PubKey(),
Signature: p.Sign(auth.StdSignBytes(chainID, accnums, seq, fee, msg)),
AccountNumber: accnums[i],
Sequence: seq[i],
}
}
return auth.NewStdTx(msg, fee, sigs)
}
// simulate a block
func SignCheckDeliver(t *testing.T, app *baseapp.BaseApp, msg sdk.Msg, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
func SignCheckDeliver(t *testing.T, app *baseapp.BaseApp, msg sdk.Msg, accnums []int64, seq []int64, expPass bool, priv ...crypto.PrivKeyEd25519) {
// Sign the tx
tx := GenTx(msg, seq, priv...)
tx := GenTx(msg, accnums, seq, priv...)
// Run a Check
res := app.Check(tx)
+20 -16
View File
@@ -85,21 +85,23 @@ func (fee StdFee) Bytes() []byte {
// 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"`
FeeBytes json.RawMessage `json:"fee_bytes"`
MsgBytes json.RawMessage `json:"msg_bytes"`
AltBytes json.RawMessage `json:"alt_bytes"`
ChainID string `json:"chain_id"`
AccountNumbers []int64 `json:"account_numbers"`
Sequences []int64 `json:"sequences"`
FeeBytes []byte `json:"fee_bytes"`
MsgBytes []byte `json:"msg_bytes"`
AltBytes []byte `json:"alt_bytes"`
}
// StdSignBytes returns the bytes to sign for a transaction.
// TODO: change the API to just take a chainID and StdTx ?
func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg sdk.Msg) []byte {
bz, err := msgCdc.MarshalJSON(StdSignDoc{
ChainID: chainID,
Sequences: sequences,
FeeBytes: json.RawMessage(fee.Bytes()),
MsgBytes: json.RawMessage(msg.GetSignBytes()),
func StdSignBytes(chainID string, accnums []int64, sequences []int64, fee StdFee, msg sdk.Msg) []byte {
bz, err := json.Marshal(StdSignDoc{
ChainID: chainID,
AccountNumbers: accnums,
Sequences: sequences,
FeeBytes: fee.Bytes(),
MsgBytes: msg.GetSignBytes(),
})
if err != nil {
panic(err)
@@ -111,21 +113,23 @@ func StdSignBytes(chainID string, sequences []int64, fee StdFee, msg sdk.Msg) []
// a Msg with the other requirements for a StdSignDoc before
// it is signed. For use in the CLI.
type StdSignMsg struct {
ChainID string
Sequences []int64
Fee StdFee
Msg sdk.Msg
ChainID string
AccountNumbers []int64
Sequences []int64
Fee StdFee
Msg sdk.Msg
// XXX: Alt
}
// get message bytes
func (msg StdSignMsg) Bytes() []byte {
return StdSignBytes(msg.ChainID, msg.Sequences, msg.Fee, msg.Msg)
return StdSignBytes(msg.ChainID, msg.AccountNumbers, msg.Sequences, msg.Fee, msg.Msg)
}
// Standard Signature
type StdSignature struct {
crypto.PubKey `json:"pub_key"` // optional
crypto.Signature `json:"signature"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
}
+8 -8
View File
@@ -107,25 +107,25 @@ func TestMsgSendWithAccounts(t *testing.T) {
assert.Equal(t, acc, res1.(*auth.BaseAccount))
// Run a CheckDeliver
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, true, priv1)
// Check balances
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 57}})
mock.CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 10}})
// Delivering again should cause replay error
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, false, priv1)
// bumping the txnonce number without resigning should be an auth error
mapp.BeginBlock(abci.RequestBeginBlock{})
tx := mock.GenTx(sendMsg1, []int64{0}, priv1)
tx := mock.GenTx(sendMsg1, []int64{0}, []int64{0}, priv1)
tx.Signatures[0].Sequence = 1
res := mapp.Deliver(tx)
assert.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)
// resigning the tx with the bumped sequence should work
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{1}, true, priv1)
}
func TestMsgSendMultipleOut(t *testing.T) {
@@ -145,7 +145,7 @@ func TestMsgSendMultipleOut(t *testing.T) {
mock.SetGenesis(mapp, accs)
// Simulate a Block
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg2, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg2, []int64{0}, []int64{0}, true, priv1)
// Check balances
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 32}})
@@ -173,7 +173,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
mock.SetGenesis(mapp, accs)
// CheckDeliver
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg3, []int64{0, 0}, true, priv1, priv4)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg3, []int64{0, 2}, []int64{0, 0}, true, priv1, priv4)
// Check balances
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 32}})
@@ -194,14 +194,14 @@ func TestMsgSendDependent(t *testing.T) {
mock.SetGenesis(mapp, accs)
// CheckDeliver
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg1, []int64{0}, []int64{0}, true, priv1)
// Check balances
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 32}})
mock.CheckBalance(t, mapp, addr2, sdk.Coins{{"foocoin", 10}})
// Simulate a Block
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg4, []int64{0}, true, priv2)
mock.SignCheckDeliver(t, mapp.BaseApp, sendMsg4, []int64{1}, []int64{0}, true, priv2)
// Check balances
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"foocoin", 42}})
+2
View File
@@ -27,6 +27,7 @@ type sendBody struct {
LocalAccountName string `json:"name"`
Password string `json:"password"`
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
}
@@ -91,6 +92,7 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.CoreCont
ctx = ctx.WithGas(m.Gas)
// sign
ctx = ctx.WithAccountNumber(m.AccountNumber)
ctx = ctx.WithSequence(m.Sequence)
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
if err != nil {
+5 -5
View File
@@ -5,9 +5,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/cosmos/cosmos-sdk/x/auth/mock"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/mock"
"github.com/cosmos/cosmos-sdk/x/bank"
abci "github.com/tendermint/abci/types"
@@ -70,10 +70,10 @@ func TestIBCMsgs(t *testing.T) {
Sequence: 0,
}
mock.SignCheckDeliver(t, mapp.BaseApp, transferMsg, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, transferMsg, []int64{0},[]int64{0}, true, priv1)
mock.CheckBalance(t, mapp, addr1, emptyCoins)
mock.SignCheckDeliver(t, mapp.BaseApp, transferMsg, []int64{1}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, receiveMsg, []int64{2}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, transferMsg, []int64{0}, []int64{1}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, receiveMsg, []int64{0}, []int64{2}, true, priv1)
mock.CheckBalance(t, mapp, addr1, coins)
mock.SignCheckDeliver(t, mapp.BaseApp, receiveMsg, []int64{3}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, receiveMsg, []int64{0}, []int64{3}, false, priv1)
}
+2
View File
@@ -25,6 +25,7 @@ type transferBody struct {
LocalAccountName string `json:"name"`
Password string `json:"password"`
SrcChainID string `json:"src_chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
}
@@ -82,6 +83,7 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx context.Core
ctx = ctx.WithGas(m.Gas)
// sign
ctx = ctx.WithAccountNumber(m.AccountNumber)
ctx = ctx.WithSequence(m.Sequence)
txBytes, err := ctx.SignAndBuild(m.LocalAccountName, m.Password, msg, cdc)
if err != nil {
+4 -4
View File
@@ -117,7 +117,7 @@ func TestStakeMsgs(t *testing.T) {
createValidatorMsg := NewMsgCreateValidator(
addr1, priv1.PubKey(), bondCoin, description,
)
mock.SignCheckDeliver(t, mapp.BaseApp, createValidatorMsg, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, createValidatorMsg, []int64{0}, []int64{0}, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
mapp.BeginBlock(abci.RequestBeginBlock{})
@@ -134,7 +134,7 @@ func TestStakeMsgs(t *testing.T) {
description = NewDescription("bar_moniker", "", "", "")
editValidatorMsg := NewMsgEditValidator(addr1, description)
mock.SignCheckDeliver(t, mapp.BaseApp, editValidatorMsg, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, editValidatorMsg, []int64{0}, []int64{1}, true, priv1)
validator = checkValidator(t, mapp, keeper, addr1, true)
require.Equal(t, description, validator.Description)
@@ -143,7 +143,7 @@ func TestStakeMsgs(t *testing.T) {
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
delegateMsg := NewMsgDelegate(addr2, addr1, bondCoin)
mock.SignCheckDeliver(t, mapp.BaseApp, delegateMsg, []int64{0}, true, priv2)
mock.SignCheckDeliver(t, mapp.BaseApp, delegateMsg, []int64{1}, []int64{0}, true, priv2)
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin.Minus(bondCoin)})
checkDelegation(t, mapp, keeper, addr2, addr1, true, sdk.NewRat(10))
@@ -151,7 +151,7 @@ func TestStakeMsgs(t *testing.T) {
// Unbond
unbondMsg := NewMsgUnbond(addr2, addr1, "MAX")
mock.SignCheckDeliver(t, mapp.BaseApp, unbondMsg, []int64{1}, true, priv2)
mock.SignCheckDeliver(t, mapp.BaseApp, unbondMsg, []int64{1}, []int64{1}, true, priv2)
mock.CheckBalance(t, mapp, addr2, sdk.Coins{genCoin})
checkDelegation(t, mapp, keeper, addr2, addr1, false, sdk.Rat{})
}
+2
View File
@@ -39,6 +39,7 @@ type editDelegationsBody struct {
LocalAccountName string `json:"name"`
Password string `json:"password"`
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Delegate []msgDelegateInput `json:"delegate"`
@@ -129,6 +130,7 @@ func editDelegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, ctx conte
signedTxs := make([][]byte, len(messages[:]))
for i, msg := range messages {
// increment sequence for each message
ctx = ctx.WithAccountNumber(m.AccountNumber)
ctx = ctx.WithSequence(m.Sequence)
m.Sequence++