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:
committed by
Christopher Goes
parent
1b20adcd22
commit
161cb474d4
+19
-4
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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}})
|
||||
|
||||
@@ -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
@@ -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
@@ -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}})
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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++
|
||||
|
||||
|
||||
Reference in New Issue
Block a user