cosmos-sdk/baseapp/baseapp_test.go

585 lines
16 KiB
Go
Raw Normal View History

package baseapp
2017-12-20 17:34:51 -08:00
import (
"bytes"
"encoding/json"
"fmt"
"os"
2017-12-20 17:34:51 -08:00
"testing"
"github.com/stretchr/testify/assert"
2018-05-11 11:02:05 -07:00
"github.com/stretchr/testify/require"
2017-12-20 17:34:51 -08:00
abci "github.com/tendermint/abci/types"
2018-05-23 22:09:01 -07:00
crypto "github.com/tendermint/go-crypto"
2017-12-20 17:34:51 -08:00
cmn "github.com/tendermint/tmlibs/common"
2017-12-26 17:04:48 -08:00
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/log"
2018-01-15 17:38:56 -08:00
sdk "github.com/cosmos/cosmos-sdk/types"
2018-05-11 11:02:05 -07:00
"github.com/cosmos/cosmos-sdk/wire"
2018-05-23 22:09:01 -07:00
"github.com/cosmos/cosmos-sdk/x/auth"
2017-12-20 17:34:51 -08:00
)
func defaultLogger() log.Logger {
return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
}
func newBaseApp(name string) *BaseApp {
logger := defaultLogger()
db := dbm.NewMemDB()
2018-05-11 11:02:05 -07:00
codec := wire.NewCodec()
wire.RegisterCrypto(codec)
2018-05-15 17:31:52 -07:00
return NewBaseApp(name, codec, logger, db)
}
2018-02-16 16:52:07 -08:00
func TestMountStores(t *testing.T) {
2018-02-16 17:58:51 -08:00
name := t.Name()
app := newBaseApp(name)
assert.Equal(t, name, app.Name())
2018-02-16 16:52:07 -08:00
// make some cap keys
capKey1 := sdk.NewKVStoreKey("key1")
capKey2 := sdk.NewKVStoreKey("key2")
// no stores are mounted
assert.Panics(t, func() { app.LoadLatestVersion(capKey1) })
app.MountStoresIAVL(capKey1, capKey2)
2018-02-16 16:52:07 -08:00
// stores are mounted
2018-02-16 16:52:07 -08:00
err := app.LoadLatestVersion(capKey1)
assert.Nil(t, err)
// check both stores
store1 := app.cms.GetCommitKVStore(capKey1)
assert.NotNil(t, store1)
store2 := app.cms.GetCommitKVStore(capKey2)
assert.NotNil(t, store2)
2018-02-16 16:52:07 -08:00
}
2018-02-26 02:59:48 -08:00
2018-02-19 10:02:04 -08:00
// Test that we can make commits and then reload old versions.
// Test that LoadLatestVersion actually does.
2018-02-16 16:52:07 -08:00
func TestLoadVersion(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
name := t.Name()
2018-05-15 17:31:52 -07:00
app := NewBaseApp(name, nil, logger, db)
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
emptyCommitID := sdk.CommitID{}
lastHeight := app.LastBlockHeight()
lastID := app.LastCommitID()
assert.Equal(t, int64(0), lastHeight)
assert.Equal(t, emptyCommitID, lastID)
// execute some blocks
header := abci.Header{Height: 1}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
res := app.Commit()
commitID := sdk.CommitID{1, res.Data}
// reload
2018-05-15 17:31:52 -07:00
app = NewBaseApp(name, nil, logger, db)
app.MountStoresIAVL(capKey)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
lastHeight = app.LastBlockHeight()
lastID = app.LastCommitID()
assert.Equal(t, int64(1), lastHeight)
assert.Equal(t, commitID, lastID)
2018-02-17 16:13:22 -08:00
}
// Test that the app hash is static
// TODO: https://github.com/cosmos/cosmos-sdk/issues/520
/*func TestStaticAppHash(t *testing.T) {
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
// execute some blocks
header := abci.Header{Height: 1}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
res := app.Commit()
commitID1 := sdk.CommitID{1, res.Data}
header = abci.Header{Height: 2}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
res = app.Commit()
commitID2 := sdk.CommitID{2, res.Data}
assert.Equal(t, commitID1.Hash, commitID2.Hash)
}
*/
2018-02-19 10:02:04 -08:00
// Test that txs can be unmarshalled and read and that
// correct error codes are returned when not
2018-02-17 16:13:22 -08:00
func TestTxDecoder(t *testing.T) {
// TODO
}
2018-02-19 10:02:04 -08:00
// Test that Info returns the latest committed state.
2018-02-17 16:13:22 -08:00
func TestInfo(t *testing.T) {
2018-02-19 10:02:04 -08:00
app := newBaseApp(t.Name())
// ----- test an empty response -------
reqInfo := abci.RequestInfo{}
res := app.Info(reqInfo)
// should be empty
assert.Equal(t, "", res.Version)
2018-02-26 02:59:48 -08:00
assert.Equal(t, t.Name(), res.GetData())
2018-02-19 10:02:04 -08:00
assert.Equal(t, int64(0), res.LastBlockHeight)
assert.Equal(t, []uint8(nil), res.LastBlockAppHash)
// ----- test a proper response -------
2018-02-17 16:13:22 -08:00
// TODO
2018-02-19 10:02:04 -08:00
2018-02-16 16:52:07 -08:00
}
2018-02-16 17:58:51 -08:00
func TestInitChainer(t *testing.T) {
name := t.Name()
db := dbm.NewMemDB()
logger := defaultLogger()
2018-05-15 17:31:52 -07:00
app := NewBaseApp(name, nil, logger, db)
// make cap keys and mount the stores
// NOTE/TODO: mounting multiple stores is broken
// see https://github.com/cosmos/cosmos-sdk/issues/532
2018-02-16 17:58:51 -08:00
capKey := sdk.NewKVStoreKey("main")
capKey2 := sdk.NewKVStoreKey("key2")
app.MountStoresIAVL(capKey, capKey2)
2018-02-16 17:58:51 -08:00
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
key, value := []byte("hello"), []byte("goodbye")
// initChainer sets a value in the store
var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
2018-02-16 17:58:51 -08:00
store := ctx.KVStore(capKey)
store.Set(key, value)
return abci.ResponseInitChain{}
2018-02-16 17:58:51 -08:00
}
query := abci.RequestQuery{
2018-05-10 11:20:34 -07:00
Path: "/store/main/key",
2018-02-16 17:58:51 -08:00
Data: key,
}
// initChainer is nil - nothing happens
app.InitChain(abci.RequestInitChain{})
res := app.Query(query)
assert.Equal(t, 0, len(res.Value))
// set initChainer and try again - should see the value
app.SetInitChainer(initChainer)
2018-05-23 13:25:56 -07:00
app.InitChain(abci.RequestInitChain{GenesisBytes: []byte("{}")}) // must have valid JSON genesis file, even if empty
app.Commit()
2018-02-16 17:58:51 -08:00
res = app.Query(query)
assert.Equal(t, value, res.Value)
// reload app
2018-05-15 17:31:52 -07:00
app = NewBaseApp(name, nil, logger, db)
app.MountStoresIAVL(capKey, capKey2)
err = app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
app.SetInitChainer(initChainer)
// ensure we can still query after reloading
res = app.Query(query)
assert.Equal(t, value, res.Value)
// commit and ensure we can still query
app.BeginBlock(abci.RequestBeginBlock{})
app.Commit()
res = app.Query(query)
assert.Equal(t, value, res.Value)
2018-02-16 16:52:07 -08:00
}
2018-02-19 10:02:04 -08:00
// Test that successive CheckTx can see each others' effects
2018-02-17 16:13:22 -08:00
// on the store within a block, and that the CheckTx state
// gets reset to the latest Committed state during Commit
func TestCheckTx(t *testing.T) {
// TODO
}
2018-02-19 10:02:04 -08:00
// Test that successive DeliverTx can see each others' effects
2018-02-17 16:13:22 -08:00
// on the store, both within and across blocks.
func TestDeliverTx(t *testing.T) {
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
counter := 0
txPerHeight := 2
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(capKey)
if counter > 0 {
// check previous value in store
counterBytes := []byte{byte(counter - 1)}
prevBytes := store.Get(counterBytes)
assert.Equal(t, prevBytes, counterBytes)
}
// set the current counter in the store
counterBytes := []byte{byte(counter)}
store.Set(counterBytes, counterBytes)
// check we can see the current header
thisHeader := ctx.BlockHeader()
height := int64((counter / txPerHeight) + 1)
assert.Equal(t, height, thisHeader.Height)
2018-04-18 12:39:32 -07:00
counter++
2018-02-17 16:13:22 -08:00
return sdk.Result{}
2018-04-02 08:30:30 -07:00
})
2018-02-17 16:13:22 -08:00
tx := testUpdatePowerTx{} // doesn't matter
header := abci.Header{AppHash: []byte("apphash")}
nBlocks := 3
for blockN := 0; blockN < nBlocks; blockN++ {
// block1
header.Height = int64(blockN + 1)
app.BeginBlock(abci.RequestBeginBlock{Header: header})
for i := 0; i < txPerHeight; i++ {
app.Deliver(tx)
}
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
}
}
2018-05-11 11:02:05 -07:00
func TestSimulateTx(t *testing.T) {
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
counter := 0
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx.GasMeter().ConsumeGas(10, "test")
store := ctx.KVStore(capKey)
// ensure store is never written
require.Nil(t, store.Get([]byte("key")))
store.Set([]byte("key"), []byte("value"))
// check we can see the current header
thisHeader := ctx.BlockHeader()
height := int64(counter)
assert.Equal(t, height, thisHeader.Height)
counter++
return sdk.Result{}
})
tx := testUpdatePowerTx{} // doesn't matter
header := abci.Header{AppHash: []byte("apphash")}
app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) {
var ttx testUpdatePowerTx
fromJSON(txBytes, &ttx)
return ttx, nil
})
nBlocks := 3
for blockN := 0; blockN < nBlocks; blockN++ {
// block1
header.Height = int64(blockN + 1)
app.BeginBlock(abci.RequestBeginBlock{Header: header})
result := app.Simulate(tx)
require.Equal(t, result.Code, sdk.ABCICodeOK)
2018-05-15 17:06:17 -07:00
require.Equal(t, int64(80), result.GasUsed)
2018-05-11 11:02:05 -07:00
counter--
encoded, err := json.Marshal(tx)
require.Nil(t, err)
query := abci.RequestQuery{
Path: "/app/simulate",
Data: encoded,
}
queryResult := app.Query(query)
require.Equal(t, queryResult.Code, uint32(sdk.ABCICodeOK))
var res sdk.Result
2018-05-11 13:06:53 -07:00
app.cdc.MustUnmarshalBinary(queryResult.Value, &res)
2018-05-15 17:06:17 -07:00
require.Equal(t, sdk.ABCICodeOK, res.Code)
require.Equal(t, int64(160), res.GasUsed)
2018-05-11 11:02:05 -07:00
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
}
}
// Test that transactions exceeding gas limits fail
func TestTxGasLimits(t *testing.T) {
logger := defaultLogger()
db := dbm.NewMemDB()
2018-05-15 17:31:52 -07:00
app := NewBaseApp(t.Name(), nil, logger, db)
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
2018-05-15 17:31:52 -07:00
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) {
newCtx = ctx.WithGasMeter(sdk.NewGasMeter(0))
return
})
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
ctx.GasMeter().ConsumeGas(10, "counter")
return sdk.Result{}
})
tx := testUpdatePowerTx{} // doesn't matter
header := abci.Header{AppHash: []byte("apphash")}
app.BeginBlock(abci.RequestBeginBlock{Header: header})
res := app.Deliver(tx)
assert.Equal(t, res.Code, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeOutOfGas), "Expected transaction to run out of gas")
app.EndBlock(abci.RequestEndBlock{})
app.Commit()
}
2018-02-17 16:13:22 -08:00
// Test that we can only query from the latest committed state.
func TestQuery(t *testing.T) {
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
key, value := []byte("hello"), []byte("goodbye")
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
store := ctx.KVStore(capKey)
store.Set(key, value)
return sdk.Result{}
2018-04-02 08:30:30 -07:00
})
2018-02-17 16:13:22 -08:00
query := abci.RequestQuery{
2018-05-10 11:20:34 -07:00
Path: "/store/main/key",
2018-02-17 16:13:22 -08:00
Data: key,
}
// query is empty before we do anything
res := app.Query(query)
assert.Equal(t, 0, len(res.Value))
tx := testUpdatePowerTx{} // doesn't matter
// query is still empty after a CheckTx
app.Check(tx)
res = app.Query(query)
assert.Equal(t, 0, len(res.Value))
// query is still empty after a DeliverTx before we commit
app.BeginBlock(abci.RequestBeginBlock{})
app.Deliver(tx)
res = app.Query(query)
assert.Equal(t, 0, len(res.Value))
// query returns correct value after Commit
app.Commit()
res = app.Query(query)
assert.Equal(t, value, res.Value)
}
2018-05-15 07:00:17 -07:00
// Test p2p filter queries
func TestP2PQuery(t *testing.T) {
app := newBaseApp(t.Name())
// make a cap key and mount the store
capKey := sdk.NewKVStoreKey("main")
app.MountStoresIAVL(capKey)
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
assert.Nil(t, err)
app.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery {
require.Equal(t, "1.1.1.1:8000", addrport)
return abci.ResponseQuery{Code: uint32(3)}
})
app.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery {
require.Equal(t, "testpubkey", pubkey)
return abci.ResponseQuery{Code: uint32(4)}
})
addrQuery := abci.RequestQuery{
Path: "/p2p/filter/addr/1.1.1.1:8000",
}
res := app.Query(addrQuery)
require.Equal(t, uint32(3), res.Code)
pubkeyQuery := abci.RequestQuery{
Path: "/p2p/filter/pubkey/testpubkey",
}
res = app.Query(pubkeyQuery)
require.Equal(t, uint32(4), res.Code)
}
2018-02-16 16:52:07 -08:00
//----------------------
2018-02-17 16:13:22 -08:00
// TODO: clean this up
2018-02-16 16:52:07 -08:00
2018-01-06 12:53:31 -08:00
// A mock transaction to update a validator's voting power.
2018-01-26 04:19:33 -08:00
type testUpdatePowerTx struct {
2018-01-06 12:53:31 -08:00
Addr []byte
NewPower int64
}
2017-12-20 17:34:51 -08:00
2018-01-26 06:22:56 -08:00
const msgType = "testUpdatePowerTx"
2018-01-15 17:38:56 -08:00
2018-05-23 22:09:01 -07:00
func (tx testUpdatePowerTx) Type() string { return msgType }
func (tx testUpdatePowerTx) GetMsg() sdk.Msg { return tx }
func (tx testUpdatePowerTx) GetSignBytes() []byte { return nil }
func (tx testUpdatePowerTx) ValidateBasic() sdk.Error { return nil }
func (tx testUpdatePowerTx) GetSigners() []sdk.Address { return nil }
func (tx testUpdatePowerTx) GetSignatures() []auth.StdSignature { return nil }
2018-01-06 12:53:31 -08:00
2018-02-17 16:13:22 -08:00
func TestValidatorChange(t *testing.T) {
2017-12-20 17:34:51 -08:00
// Create app.
app := newBaseApp(t.Name())
capKey := sdk.NewKVStoreKey("key")
app.MountStoresIAVL(capKey)
2018-01-26 04:19:33 -08:00
app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) {
var ttx testUpdatePowerTx
2018-01-06 12:53:31 -08:00
fromJSON(txBytes, &ttx)
return ttx, nil
})
2018-01-15 17:38:56 -08:00
2018-02-14 17:09:00 -08:00
app.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx) (newCtx sdk.Context, res sdk.Result, abort bool) { return })
2018-01-26 06:22:56 -08:00
app.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
2018-01-06 12:53:31 -08:00
// TODO
2018-01-15 17:38:56 -08:00
return sdk.Result{}
2018-04-02 08:30:30 -07:00
})
2017-12-20 17:34:51 -08:00
// Load latest state, which should be empty.
err := app.LoadLatestVersion(capKey)
2017-12-20 17:34:51 -08:00
assert.Nil(t, err)
2017-12-26 17:04:48 -08:00
assert.Equal(t, app.LastBlockHeight(), int64(0))
2017-12-20 17:34:51 -08:00
// Create the validators
var numVals = 3
2017-12-26 17:04:48 -08:00
var valSet = make([]abci.Validator, numVals)
2017-12-20 17:34:51 -08:00
for i := 0; i < numVals; i++ {
valSet[i] = makeVal(secret(i))
}
// Initialize the chain
app.InitChain(abci.RequestInitChain{
2017-12-26 17:04:48 -08:00
Validators: valSet,
2017-12-20 17:34:51 -08:00
})
// Simulate the start of a block.
app.BeginBlock(abci.RequestBeginBlock{})
// Add 1 to each validator's voting power.
for i, val := range valSet {
2018-01-26 04:19:33 -08:00
tx := testUpdatePowerTx{
2017-12-20 17:34:51 -08:00
Addr: makePubKey(secret(i)).Address(),
NewPower: val.Power + 1,
}
txBytes := toJSON(tx)
res := app.DeliverTx(txBytes)
2018-01-26 05:11:01 -08:00
assert.True(t, res.IsOK(), "%#v\nABCI log: %s", res, res.Log)
2017-12-20 17:34:51 -08:00
}
// Simulate the end of a block.
// Get the summary of validator updates.
2017-12-26 17:04:48 -08:00
res := app.EndBlock(abci.RequestEndBlock{})
2017-12-20 17:34:51 -08:00
valUpdates := res.ValidatorUpdates
// Assert that validator updates are correct.
for _, val := range valSet {
2017-12-26 17:04:48 -08:00
// Sanity
assert.NotEqual(t, len(val.PubKey), 0)
2017-12-20 17:34:51 -08:00
// Find matching update and splice it out.
for j := 0; j < len(valUpdates); {
2017-12-26 17:04:48 -08:00
valUpdate := valUpdates[j]
2017-12-20 17:34:51 -08:00
// Matched.
if bytes.Equal(valUpdate.PubKey, val.PubKey) {
2017-12-26 17:04:48 -08:00
assert.Equal(t, valUpdate.Power, val.Power+1)
2017-12-20 17:34:51 -08:00
if j < len(valUpdates)-1 {
// Splice it out.
valUpdates = append(valUpdates[:j], valUpdates[j+1:]...)
}
break
}
// Not matched.
j++
2017-12-20 17:34:51 -08:00
}
}
assert.Equal(t, len(valUpdates), 0, "Some validator updates were unexpected")
}
//----------------------------------------
func randPower() int64 {
return cmn.RandInt64()
}
2017-12-26 17:04:48 -08:00
func makeVal(secret string) abci.Validator {
return abci.Validator{
PubKey: makePubKey(secret).Bytes(),
2017-12-20 17:34:51 -08:00
Power: randPower(),
}
}
func makePubKey(secret string) crypto.PubKey {
return makePrivKey(secret).PubKey()
}
func makePrivKey(secret string) crypto.PrivKey {
2017-12-26 17:04:48 -08:00
privKey := crypto.GenPrivKeyEd25519FromSecret([]byte(secret))
2018-04-06 17:25:08 -07:00
return privKey
2017-12-20 17:34:51 -08:00
}
2017-12-26 17:04:48 -08:00
func secret(index int) string {
return fmt.Sprintf("secret%d", index)
2017-12-20 17:34:51 -08:00
}
2017-12-26 17:04:48 -08:00
func copyVal(val abci.Validator) abci.Validator {
// val2 := *val
// return &val2
return val
2017-12-20 17:34:51 -08:00
}
func toJSON(o interface{}) []byte {
2017-12-26 17:04:48 -08:00
bz, err := json.Marshal(o)
2017-12-20 17:34:51 -08:00
if err != nil {
panic(err)
}
2017-12-26 17:04:48 -08:00
// fmt.Println(">> toJSON:", string(bz))
return bz
2017-12-20 17:34:51 -08:00
}
2017-12-26 17:04:48 -08:00
func fromJSON(bz []byte, ptr interface{}) {
// fmt.Println(">> fromJSON:", string(bz))
err := json.Unmarshal(bz, ptr)
2017-12-20 17:34:51 -08:00
if err != nil {
panic(err)
}
}