326 lines
9.9 KiB
Go
326 lines
9.9 KiB
Go
package auth
|
|
|
|
import (
|
|
"testing"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
abci "github.com/tendermint/abci/types"
|
|
crypto "github.com/tendermint/go-crypto"
|
|
)
|
|
|
|
func newTestMsg(addrs ...sdk.Address) *sdk.TestMsg {
|
|
return sdk.NewTestMsg(addrs...)
|
|
}
|
|
|
|
func newStdFee() sdk.StdFee {
|
|
return sdk.NewStdFee(100,
|
|
sdk.Coin{"atom", 150},
|
|
)
|
|
}
|
|
|
|
// coins to more than cover the fee
|
|
func newCoins() sdk.Coins {
|
|
return sdk.Coins{
|
|
{"atom", 10000000},
|
|
}
|
|
}
|
|
|
|
// 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, fee sdk.StdFee) sdk.Tx {
|
|
signBytes := sdk.StdSignBytes(ctx.ChainID(), seqs, fee, msg)
|
|
return newTestTxWithSignBytes(msg, privs, seqs, fee, signBytes)
|
|
}
|
|
|
|
func newTestTxWithSignBytes(msg sdk.Msg, privs []crypto.PrivKey, seqs []int64, fee sdk.StdFee, signBytes []byte) sdk.Tx {
|
|
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]}
|
|
}
|
|
tx := sdk.NewStdTx(msg, fee, sigs)
|
|
return tx
|
|
}
|
|
|
|
// 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)
|
|
fee := newStdFee()
|
|
|
|
// test no signatures
|
|
privs, seqs := []crypto.PrivKey{}, []int64{}
|
|
tx = newTestTx(ctx, msg, privs, 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)
|
|
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)
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeUnrecognizedAddress)
|
|
|
|
// save the first account, but second is still unrecognized
|
|
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
|
|
acc1.SetCoins(fee.Amount)
|
|
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)
|
|
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, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
|
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
|
checkValidTx(t, anteHandler, ctx, tx)
|
|
|
|
// test sending it again fails (replay protection)
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidSequence)
|
|
|
|
// fix sequence, should pass
|
|
seqs = []int64{1}
|
|
tx = newTestTx(ctx, msg, privs, 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)
|
|
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)
|
|
privs, seqs = []crypto.PrivKey{priv2}, []int64{0}
|
|
tx = newTestTx(ctx, msg, privs, 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)
|
|
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)
|
|
checkValidTx(t, anteHandler, ctx, tx)
|
|
}
|
|
|
|
// Test logic around fee deduction.
|
|
func TestAnteHandlerFees(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()
|
|
|
|
// set the accounts
|
|
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
|
|
mapper.SetAccount(ctx, acc1)
|
|
|
|
// msg and signatures
|
|
var tx sdk.Tx
|
|
msg := newTestMsg(addr1)
|
|
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
|
fee := sdk.NewStdFee(100,
|
|
sdk.Coin{"atom", 150},
|
|
)
|
|
|
|
// signer does not have enough funds to pay the fee
|
|
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds)
|
|
|
|
acc1.SetCoins(sdk.Coins{{"atom", 149}})
|
|
mapper.SetAccount(ctx, acc1)
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInsufficientFunds)
|
|
|
|
acc1.SetCoins(sdk.Coins{{"atom", 150}})
|
|
mapper.SetAccount(ctx, acc1)
|
|
checkValidTx(t, anteHandler, ctx, tx)
|
|
}
|
|
|
|
func TestAnteHandlerBadSignBytes(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)
|
|
acc1.SetCoins(newCoins())
|
|
mapper.SetAccount(ctx, acc1)
|
|
acc2 := mapper.NewAccountWithAddress(ctx, addr2)
|
|
acc2.SetCoins(newCoins())
|
|
mapper.SetAccount(ctx, acc2)
|
|
|
|
var tx sdk.Tx
|
|
msg := newTestMsg(addr1)
|
|
fee := newStdFee()
|
|
fee2 := newStdFee()
|
|
fee2.Gas += 100
|
|
fee3 := newStdFee()
|
|
fee3.Amount[0].Amount += 100
|
|
|
|
// test good tx and signBytes
|
|
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
|
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
|
checkValidTx(t, anteHandler, ctx, tx)
|
|
|
|
chainID := ctx.ChainID()
|
|
chainID2 := chainID + "somemorestuff"
|
|
codeUnauth := sdk.CodeUnauthorized
|
|
|
|
cases := []struct {
|
|
chainID string
|
|
seqs []int64
|
|
fee sdk.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
|
|
}
|
|
|
|
privs, seqs = []crypto.PrivKey{priv1}, []int64{1}
|
|
for _, cs := range cases {
|
|
tx := newTestTxWithSignBytes(
|
|
msg, privs, seqs, fee,
|
|
sdk.StdSignBytes(cs.chainID, 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)
|
|
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)
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
|
|
|
|
}
|
|
|
|
func TestAnteHandlerSetPubKey(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()
|
|
_, 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)
|
|
|
|
var tx sdk.Tx
|
|
|
|
// test good tx and set public key
|
|
msg := newTestMsg(addr1)
|
|
privs, seqs := []crypto.PrivKey{priv1}, []int64{0}
|
|
fee := newStdFee()
|
|
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
|
checkValidTx(t, anteHandler, ctx, tx)
|
|
|
|
acc1 = mapper.GetAccount(ctx, addr1)
|
|
require.Equal(t, acc1.GetPubKey(), priv1.PubKey())
|
|
|
|
// test public key not found
|
|
msg = newTestMsg(addr2)
|
|
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
|
sigs := tx.GetSignatures()
|
|
sigs[0].PubKey = crypto.PubKey{}
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
|
|
|
|
acc2 = mapper.GetAccount(ctx, addr2)
|
|
assert.True(t, acc2.GetPubKey().Empty())
|
|
|
|
// test invalid signature and public key
|
|
tx = newTestTx(ctx, msg, privs, seqs, fee)
|
|
checkInvalidTx(t, anteHandler, ctx, tx, sdk.CodeInvalidPubKey)
|
|
|
|
acc2 = mapper.GetAccount(ctx, addr2)
|
|
assert.True(t, acc2.GetPubKey().Empty())
|
|
}
|