test(server/v2/cometbft): Add abci unit tests (#21020)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
Hieu Vu 2024-09-09 17:34:29 +07:00 committed by GitHub
parent 9672879d21
commit ac53e337a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 957 additions and 14 deletions

View File

@ -0,0 +1,714 @@
package cometbft
import (
"context"
"crypto/sha256"
"io"
"strings"
"testing"
"time"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
"cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
"cosmossdk.io/log"
am "cosmossdk.io/server/v2/appmanager"
"cosmossdk.io/server/v2/cometbft/handlers"
cometmock "cosmossdk.io/server/v2/cometbft/internal/mock"
"cosmossdk.io/server/v2/cometbft/mempool"
"cosmossdk.io/server/v2/cometbft/types"
"cosmossdk.io/server/v2/stf"
"cosmossdk.io/server/v2/stf/branch"
"cosmossdk.io/server/v2/stf/mock"
abciproto "github.com/cometbft/cometbft/api/cometbft/abci/v1"
v1 "github.com/cometbft/cometbft/api/cometbft/types/v1"
"github.com/cosmos/gogoproto/proto"
"encoding/json"
consensustypes "cosmossdk.io/x/consensus/types"
gogotypes "github.com/cosmos/gogoproto/types"
"github.com/stretchr/testify/require"
)
var (
sum = sha256.Sum256([]byte("test-hash"))
DefaulConsensusParams = &v1.ConsensusParams{
Block: &v1.BlockParams{
MaxGas: 5000000,
},
}
mockTx = mock.Tx{
Sender: []byte("sender"),
Msg: &gogotypes.BoolValue{Value: true},
GasLimit: 100_000,
}
invalidMockTx = mock.Tx{
Sender: []byte("sender"),
Msg: &gogotypes.BoolValue{Value: true},
GasLimit: 0,
}
actorName = []byte("cookies")
)
func getQueryRouterBuilder[T any, PT interface {
*T
proto.Message
},
U any, UT interface {
*U
proto.Message
}](
t *testing.T,
handler func(ctx context.Context, msg PT) (UT, error),
) *stf.MsgRouterBuilder {
t.Helper()
queryRouterBuilder := stf.NewMsgRouterBuilder()
err := queryRouterBuilder.RegisterHandler(
proto.MessageName(PT(new(T))),
func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) {
typedReq := msg.(PT)
typedResp, err := handler(ctx, typedReq)
if err != nil {
return nil, err
}
return typedResp, nil
},
)
require.NoError(t, err)
return queryRouterBuilder
}
func getMsgRouterBuilder[T any, PT interface {
*T
transaction.Msg
},
U any, UT interface {
*U
transaction.Msg
}](
t *testing.T,
handler func(ctx context.Context, msg PT) (UT, error),
) *stf.MsgRouterBuilder {
t.Helper()
msgRouterBuilder := stf.NewMsgRouterBuilder()
err := msgRouterBuilder.RegisterHandler(
proto.MessageName(PT(new(T))),
func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) {
typedReq := msg.(PT)
typedResp, err := handler(ctx, typedReq)
if err != nil {
return nil, err
}
return typedResp, nil
},
)
require.NoError(t, err)
return msgRouterBuilder
}
func TestConsensus_InitChain_Without_UpdateParam(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
mockStore := c.store
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 0)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 1)
}
func TestConsensus_InitChain_With_UpdateParam(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
mockStore := c.store
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
ConsensusParams: DefaulConsensusParams,
InitialHeight: 1,
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 0)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 1)
}
func TestConsensus_InitChain_Invalid_Height(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
mockStore := c.store
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 2,
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, 0)
// Shouldn't be able to commit genesis block 2
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 2,
})
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "unable to commit the changeset"))
}
func TestConsensus_FinalizeBlock_Invalid_Height(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 3,
})
require.Error(t, err)
}
func TestConsensus_FinalizeBlock_NoTxs(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
mockStore := c.store
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
endBlock := 10
for i := 2; i <= endBlock; i++ {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: int64(i),
Hash: sum[:],
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, uint64(i))
}
require.Equal(t, int64(endBlock), c.lastCommittedHeight.Load())
}
func TestConsensus_FinalizeBlock_MultiTxs_OutOfGas(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
endBlock := 10
for i := 2; i <= endBlock; i++ {
res, err := c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: int64(i),
Hash: sum[:],
Txs: [][]byte{invalidMockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
require.NotEqual(t, res.TxResults[0].Code, 0)
}
require.Equal(t, int64(endBlock), c.lastCommittedHeight.Load())
}
func TestConsensus_FinalizeBlock_MultiTxs(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
mockStore := c.store
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
endBlock := 10
for i := 2; i <= endBlock; i++ {
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: int64(i),
Hash: sum[:],
Txs: [][]byte{mockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
assertStoreLatestVersion(t, mockStore, uint64(i))
}
require.Equal(t, int64(endBlock), c.lastCommittedHeight.Load())
}
func TestConsensus_CheckTx(t *testing.T) {
c := setUpConsensus(t, 0, mempool.NoOpMempool[mock.Tx]{})
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
// empty byte
_, err = c.CheckTx(context.Background(), &abciproto.CheckTxRequest{
Tx: []byte{},
})
require.Error(t, err)
// out of gas
res, err := c.CheckTx(context.Background(), &abciproto.CheckTxRequest{
Tx: mock.Tx{
Sender: []byte("sender"),
Msg: &gogotypes.BoolValue{Value: true},
GasLimit: 100_000,
}.Bytes(),
})
require.NoError(t, err)
require.NotEqual(t, res.Code, 0)
c = setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
res, err = c.CheckTx(context.Background(), &abciproto.CheckTxRequest{
Tx: mock.Tx{
Sender: []byte("sender"),
Msg: &gogotypes.BoolValue{Value: true},
GasLimit: 100_000,
}.Bytes(),
})
require.NoError(t, err)
require.NotEqual(t, res.GasUsed, 0)
}
func TestConsensus_ExtendVote(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
ConsensusParams: &v1.ConsensusParams{
Block: &v1.BlockParams{
MaxGas: 5000000,
},
Abci: &v1.ABCIParams{
VoteExtensionsEnableHeight: 2,
},
Feature: &v1.FeatureParams{
VoteExtensionsEnableHeight: &gogotypes.Int64Value{Value: 2},
},
},
})
require.NoError(t, err)
// Votes not enabled yet
_, err = c.ExtendVote(context.Background(), &abciproto.ExtendVoteRequest{
Height: 1,
})
require.ErrorContains(t, err, "vote extensions are not enabled")
// Empty extendVote handler
_, err = c.ExtendVote(context.Background(), &abciproto.ExtendVoteRequest{
Height: 2,
})
require.ErrorContains(t, err, "no extend function was set")
// Use NoOp handler
c.extendVote = DefaultServerOptions[mock.Tx]().ExtendVoteHandler
res, err := c.ExtendVote(context.Background(), &abciproto.ExtendVoteRequest{
Height: 2,
})
require.NoError(t, err)
require.Equal(t, len(res.VoteExtension), 0)
}
func TestConsensus_VerifyVoteExtension(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
ConsensusParams: &v1.ConsensusParams{
Block: &v1.BlockParams{
MaxGas: 5000000,
},
Abci: &v1.ABCIParams{
VoteExtensionsEnableHeight: 2,
},
Feature: &v1.FeatureParams{
VoteExtensionsEnableHeight: &gogotypes.Int64Value{Value: 2},
},
},
})
require.NoError(t, err)
// Votes not enabled yet
_, err = c.VerifyVoteExtension(context.Background(), &abciproto.VerifyVoteExtensionRequest{
Height: 1,
})
require.ErrorContains(t, err, "vote extensions are not enabled")
// Empty verifyVote handler
_, err = c.VerifyVoteExtension(context.Background(), &abciproto.VerifyVoteExtensionRequest{
Height: 2,
})
require.ErrorContains(t, err, "no verify function was set")
// Use NoOp handler
c.verifyVoteExt = DefaultServerOptions[mock.Tx]().VerifyVoteExtensionHandler
res, err := c.VerifyVoteExtension(context.Background(), &abciproto.VerifyVoteExtensionRequest{
Height: 2,
Hash: []byte("test"),
})
require.NoError(t, err)
require.Equal(t, res.Status, abciproto.VERIFY_VOTE_EXTENSION_STATUS_ACCEPT)
}
func TestConsensus_PrepareProposal(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
// Invalid height
_, err := c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 0,
})
require.Error(t, err)
// empty handler
_, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
})
require.Error(t, err)
// NoOp handler
c.prepareProposalHandler = DefaultServerOptions[mock.Tx]().PrepareProposalHandler
_, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
Txs: [][]byte{mockTx.Bytes()},
})
require.NoError(t, err)
}
func TestConsensus_PrepareProposal_With_Handler_NoOpMempool(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
c.prepareProposalHandler = handlers.NewDefaultProposalHandler(c.mempool).PrepareHandler()
// zero MaxTxBytes
res, err := c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
MaxTxBytes: 0,
Txs: [][]byte{mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, len(res.Txs), 0)
// have tx exeed MaxTxBytes
// each mock tx has 128 bytes, should select 2 txs
res, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
MaxTxBytes: 300,
Txs: [][]byte{mockTx.Bytes(), mockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, len(res.Txs), 2)
// reach MaxTxBytes
res, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
MaxTxBytes: 256,
Txs: [][]byte{mockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, len(res.Txs), 2)
// Over gas, under MaxTxBytes
// 300_000 gas limit, should only take 3 txs
res, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
MaxTxBytes: 1000,
Txs: [][]byte{mockTx.Bytes(), mockTx.Bytes(), mockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, len(res.Txs), 3)
// Reach max gas
res, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
MaxTxBytes: 1000,
Txs: [][]byte{mockTx.Bytes(), mockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, len(res.Txs), 3)
// have a bad encoding tx
res, err = c.PrepareProposal(context.Background(), &abciproto.PrepareProposalRequest{
Height: 1,
MaxTxBytes: 1000,
Txs: [][]byte{mockTx.Bytes(), append(mockTx.Bytes(), []byte("bad")...), mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, len(res.Txs), 2)
}
func TestConsensus_ProcessProposal(t *testing.T) {
c := setUpConsensus(t, 100_000, mempool.NoOpMempool[mock.Tx]{})
// Invalid height
_, err := c.ProcessProposal(context.Background(), &abciproto.ProcessProposalRequest{
Height: 0,
})
require.Error(t, err)
// empty handler
_, err = c.ProcessProposal(context.Background(), &abciproto.ProcessProposalRequest{
Height: 1,
})
require.Error(t, err)
// NoOp handler
c.processProposalHandler = DefaultServerOptions[mock.Tx]().ProcessProposalHandler
_, err = c.ProcessProposal(context.Background(), &abciproto.ProcessProposalRequest{
Height: 1,
Txs: [][]byte{mockTx.Bytes()},
})
require.NoError(t, err)
}
func TestConsensus_ProcessProposal_With_Handler(t *testing.T) {
c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{})
c.processProposalHandler = handlers.NewDefaultProposalHandler(c.mempool).ProcessHandler()
// exeed max gas
res, err := c.ProcessProposal(context.Background(), &abciproto.ProcessProposalRequest{
Height: 1,
Txs: [][]byte{mockTx.Bytes(), mockTx.Bytes(), mockTx.Bytes(), mockTx.Bytes()},
})
require.NoError(t, err)
require.Equal(t, res.Status, abciproto.PROCESS_PROPOSAL_STATUS_REJECT)
// have bad encode tx
// should reject
res, err = c.ProcessProposal(context.Background(), &abciproto.ProcessProposalRequest{
Height: 1,
Txs: [][]byte{mockTx.Bytes(), append(mockTx.Bytes(), []byte("bad")...), mockTx.Bytes(), mockTx.Bytes()},
})
require.Equal(t, res.Status, abciproto.PROCESS_PROPOSAL_STATUS_REJECT)
}
func TestConsensus_Info(t *testing.T) {
c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{})
// Version 0
res, err := c.Info(context.Background(), &abciproto.InfoRequest{})
require.NoError(t, err)
require.Equal(t, res.LastBlockHeight, int64(0))
// Commit store to version 1
_, err = c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
})
require.NoError(t, err)
res, err = c.Info(context.Background(), &abciproto.InfoRequest{})
require.NoError(t, err)
require.Equal(t, res.LastBlockHeight, int64(1))
}
// TODO:
// - GRPC request
// - app request
// - p2p request
func TestConsensus_Query(t *testing.T) {
c := setUpConsensus(t, 100_000, cometmock.MockMempool[mock.Tx]{})
// Write data to state storage
c.store.GetStateStorage().ApplyChangeset(1, &store.Changeset{
Changes: []store.StateChanges{
{
Actor: actorName,
StateChanges: []store.KVPair{
{
Key: []byte("key"),
Value: []byte("value"),
Remove: false,
},
},
},
},
})
_, err := c.InitChain(context.Background(), &abciproto.InitChainRequest{
Time: time.Now(),
ChainId: "test",
InitialHeight: 1,
})
require.NoError(t, err)
_, err = c.FinalizeBlock(context.Background(), &abciproto.FinalizeBlockRequest{
Time: time.Now(),
Height: 1,
Txs: [][]byte{mockTx.Bytes()},
})
require.NoError(t, err)
// empty request
res, err := c.Query(context.Background(), &abciproto.QueryRequest{})
require.NoError(t, err)
require.Equal(t, res.Code, uint32(1))
require.Contains(t, res.Log, "no query path provided")
// Query store
res, err = c.Query(context.Background(), &abciproto.QueryRequest{
Path: "store/cookies/",
Data: []byte("key"),
Height: 1,
})
require.NoError(t, err)
require.Equal(t, string(res.Value), "value")
// Query store with no value
res, err = c.Query(context.Background(), &abciproto.QueryRequest{
Path: "store/cookies/",
Data: []byte("exec"),
Height: 1,
})
require.NoError(t, err)
require.Equal(t, res.Value, []byte(nil))
}
func setUpConsensus(t *testing.T, gasLimit uint64, mempool mempool.Mempool[mock.Tx]) *Consensus[mock.Tx] {
msgRouterBuilder := getMsgRouterBuilder(t, func(ctx context.Context, msg *gogotypes.BoolValue) (*gogotypes.BoolValue, error) {
return nil, nil
})
queryRouterBuilder := getQueryRouterBuilder(t, func(ctx context.Context, q *consensustypes.QueryParamsRequest) (*consensustypes.QueryParamsResponse, error) {
cParams := &v1.ConsensusParams{
Block: &v1.BlockParams{
MaxGas: 300000,
},
Abci: &v1.ABCIParams{
VoteExtensionsEnableHeight: 2,
},
Feature: &v1.FeatureParams{
VoteExtensionsEnableHeight: &gogotypes.Int64Value{Value: 2},
},
}
return &consensustypes.QueryParamsResponse{
Params: cParams,
}, nil
})
s, err := stf.NewSTF(
log.NewNopLogger().With("module", "stf"),
msgRouterBuilder,
queryRouterBuilder,
func(ctx context.Context, txs []mock.Tx) error { return nil },
func(ctx context.Context) error {
return nil
},
func(ctx context.Context) error {
return nil
},
func(ctx context.Context, tx mock.Tx) error {
return nil
},
func(ctx context.Context) ([]appmodulev2.ValidatorUpdate, error) { return nil, nil },
func(ctx context.Context, tx mock.Tx, success bool) error {
return nil
},
branch.DefaultNewWriterMap,
)
require.NoError(t, err)
ss := cometmock.NewMockStorage(log.NewNopLogger(), t.TempDir())
sc := cometmock.NewMockCommiter(log.NewNopLogger(), string(actorName), "stf")
mockStore := cometmock.NewMockStore(ss, sc)
b := am.Builder[mock.Tx]{
STF: s,
DB: mockStore,
ValidateTxGasLimit: gasLimit,
QueryGasLimit: gasLimit,
SimulationGasLimit: gasLimit,
InitGenesis: func(ctx context.Context, src io.Reader, txHandler func(json.RawMessage) error) error {
return nil
},
}
am, err := b.Build()
require.NoError(t, err)
return NewConsensus[mock.Tx](log.NewNopLogger(), "testing-app", "authority", am, mempool, map[string]struct{}{}, nil, mockStore, Config{AppTomlConfig: DefaultAppTomlConfig()}, mock.TxCodec{}, "test")
}
// Check target version same with store's latest version
// And should have commit info of target version
// If block 0, commitInfo returned should be nil
func assertStoreLatestVersion(t *testing.T, store types.Store, target uint64) {
t.Helper()
version, err := store.GetLatestVersion()
require.NoError(t, err)
require.Equal(t, version, target)
commitInfo, err := store.GetStateCommitment().GetCommitInfo(version)
require.NoError(t, err)
if target != 0 {
require.Equal(t, commitInfo.Version, target)
} else {
require.Nil(t, commitInfo)
}
}

View File

@ -8,6 +8,7 @@ replace (
cosmossdk.io/core/testing => ../../../core/testing
cosmossdk.io/server/v2 => ../
cosmossdk.io/server/v2/appmanager => ../appmanager
cosmossdk.io/server/v2/stf => ../stf
cosmossdk.io/store => ../../../store
cosmossdk.io/store/v2 => ../../../store/v2
cosmossdk.io/x/bank => ../../../x/bank
@ -25,6 +26,7 @@ require (
cosmossdk.io/log v1.4.1
cosmossdk.io/server/v2 v2.0.0-00010101000000-000000000000
cosmossdk.io/server/v2/appmanager v0.0.0-20240802110823-cffeedff643d
cosmossdk.io/server/v2/stf v0.0.0-20240708142107-25e99c54bac1
cosmossdk.io/store/v2 v2.0.0-00010101000000-000000000000
cosmossdk.io/x/consensus v0.0.0-00010101000000-000000000000
github.com/cometbft/cometbft v1.0.0-rc1.0.20240908111210-ab0be101882f
@ -37,6 +39,7 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117
google.golang.org/grpc v1.66.0
google.golang.org/protobuf v1.34.2
@ -46,6 +49,7 @@ require (
require (
buf.build/gen/go/cosmos/gogo-proto/protocolbuffers/go v1.34.2-20240130113600-88ef6483f90f.2 // indirect
cosmossdk.io/collections v0.4.0 // indirect
cosmossdk.io/core/testing v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/depinject v1.0.0 // indirect
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 // indirect
cosmossdk.io/math v1.3.0 // indirect
@ -121,6 +125,7 @@ require (
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/minio/highwayhash v1.0.3 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@ -146,7 +151,6 @@ require (
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/supranational/blst v0.3.13 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect

View File

@ -0,0 +1,19 @@
package mock
import (
"context"
"cosmossdk.io/core/transaction"
"cosmossdk.io/server/v2/cometbft/mempool"
)
var _ mempool.Mempool[transaction.Tx] = (*MockMempool[transaction.Tx])(nil)
// MockMempool implements Mempool
// Used for testing instead of NoOpMempool
type MockMempool[T transaction.Tx] struct{}
func (MockMempool[T]) Insert(context.Context, T) error { return nil }
func (MockMempool[T]) Select(context.Context, []T) mempool.Iterator[T] { return nil }
func (MockMempool[T]) CountTx() int { return 0 }
func (MockMempool[T]) Remove([]T) error { return nil }

View File

@ -0,0 +1,65 @@
package mock
import (
corestore "cosmossdk.io/core/store"
)
// ReaderMap defines an adapter around a RootStore that only exposes read-only
// operations. This is useful for exposing a read-only view of the RootStore at
// a specific version in history, which could also be the latest state.
type ReaderMap struct {
store *MockStore
version uint64
}
func NewMockReaderMap(v uint64, rs *MockStore) *ReaderMap {
return &ReaderMap{
store: rs,
version: v,
}
}
func (roa *ReaderMap) GetReader(actor []byte) (corestore.Reader, error) {
return NewMockReader(roa.version, roa.store, actor), nil
}
// Reader represents a read-only adapter for accessing data from the root store.
type MockReader struct {
version uint64 // The version of the data.
store *MockStore // The root store to read data from.
actor []byte // The actor associated with the data.
}
func NewMockReader(v uint64, rs *MockStore, actor []byte) *MockReader {
return &MockReader{
version: v,
store: rs,
actor: actor,
}
}
func (roa *MockReader) Has(key []byte) (bool, error) {
val, err := roa.store.GetStateStorage().Has(roa.actor, roa.version, key)
if err != nil {
return false, err
}
return val, nil
}
func (roa *MockReader) Get(key []byte) ([]byte, error) {
result, err := roa.store.GetStateStorage().Get(roa.actor, roa.version, key)
if err != nil {
return nil, err
}
return result, nil
}
func (roa *MockReader) Iterator(start, end []byte) (corestore.Iterator, error) {
return roa.store.GetStateStorage().Iterator(roa.actor, roa.version, start, end)
}
func (roa *MockReader) ReverseIterator(start, end []byte) (corestore.Iterator, error) {
return roa.store.GetStateStorage().ReverseIterator(roa.actor, roa.version, start, end)
}

View File

@ -0,0 +1,139 @@
package mock
import (
"crypto/sha256"
"fmt"
"cosmossdk.io/core/log"
corestore "cosmossdk.io/core/store"
storev2 "cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/commitment"
"cosmossdk.io/store/v2/commitment/iavl"
dbm "cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/storage"
"cosmossdk.io/store/v2/storage/sqlite"
)
type MockStore struct {
Storage storev2.VersionedDatabase
Commiter storev2.Committer
}
func NewMockStorage(logger log.Logger, dir string) storev2.VersionedDatabase {
storageDB, _ := sqlite.New(dir)
ss := storage.NewStorageStore(storageDB, logger)
return ss
}
func NewMockCommiter(logger log.Logger, actors ...string) storev2.Committer {
treeMap := make(map[string]commitment.Tree)
for _, actor := range actors {
tree := iavl.NewIavlTree(dbm.NewMemDB(), logger, iavl.DefaultConfig())
treeMap[actor] = tree
}
sc, _ := commitment.NewCommitStore(treeMap, treeMap, dbm.NewMemDB(), logger)
return sc
}
func NewMockStore(ss storev2.VersionedDatabase, sc storev2.Committer) *MockStore {
return &MockStore{Storage: ss, Commiter: sc}
}
func (s *MockStore) GetLatestVersion() (uint64, error) {
lastCommitID, err := s.LastCommitID()
if err != nil {
return 0, err
}
return lastCommitID.Version, nil
}
func (s *MockStore) StateLatest() (uint64, corestore.ReaderMap, error) {
v, err := s.GetLatestVersion()
if err != nil {
return 0, nil, err
}
return v, NewMockReaderMap(v, s), nil
}
func (s *MockStore) Commit(changeset *corestore.Changeset) (corestore.Hash, error) {
v, _, _ := s.StateLatest()
err := s.Storage.ApplyChangeset(v, changeset)
if err != nil {
return []byte{}, err
}
err = s.Commiter.WriteChangeset(changeset)
if err != nil {
return []byte{}, err
}
commitInfo, err := s.Commiter.Commit(v + 1)
fmt.Println("commitInfo", commitInfo, err)
return []byte{}, err
}
func (s *MockStore) StateAt(version uint64) (corestore.ReaderMap, error) {
info, err := s.Commiter.GetCommitInfo(version)
if err != nil || info == nil {
return nil, fmt.Errorf("failed to get commit info for version %d: %w", version, err)
}
return NewMockReaderMap(version, s), nil
}
func (s *MockStore) GetStateStorage() storev2.VersionedDatabase {
return s.Storage
}
func (s *MockStore) GetStateCommitment() storev2.Committer {
return s.Commiter
}
type Result struct {
key []byte
value []byte
version uint64
proofOps []proof.CommitmentOp
}
func (s *MockStore) Query(storeKey []byte, version uint64, key []byte, prove bool) (storev2.QueryResult, error) {
state, err := s.StateAt(version)
reader, err := state.GetReader(storeKey)
value, err := reader.Get(key)
res := storev2.QueryResult{
Key: key,
Value: value,
Version: version,
}
return res, err
}
func (s *MockStore) LastCommitID() (proof.CommitID, error) {
v, err := s.GetStateCommitment().GetLatestVersion()
bz := sha256.Sum256([]byte{})
return proof.CommitID{
Version: v,
Hash: bz[:],
}, err
}
func (s *MockStore) SetInitialVersion(v uint64) error {
return s.Commiter.SetInitialVersion(v)
}
func (s *MockStore) WorkingHash(changeset *corestore.Changeset) (corestore.Hash, error) {
v, _, _ := s.StateLatest()
err := s.Storage.ApplyChangeset(v, changeset)
if err != nil {
return []byte{}, err
}
err = s.Commiter.WriteChangeset(changeset)
if err != nil {
return []byte{}, err
}
return []byte{}, nil
}

View File

@ -65,56 +65,58 @@ func (t Tx) Bytes() []byte {
return tx
}
func (t *Tx) Decode(b []byte) {
func (t *Tx) Decode(b []byte) error {
rawTx := new(encodedTx)
err := json.Unmarshal(b, rawTx)
if err != nil {
panic(err)
return err
}
msgName, err := gogoproto.AnyMessageName(rawTx.Msg)
msgType := proto.MessageType(msgName).Elem()
if err != nil {
panic(err)
return err
}
msg := reflect.New(msgType).Interface().(proto.Message)
if err := gogoproto.UnmarshalAny(rawTx.Msg, msg); err != nil {
panic(err)
return err
}
t.Msg = msg
t.Sender = rawTx.Sender
t.GasLimit = rawTx.GasLimit
return nil
}
func (t *Tx) DecodeJSON(b []byte) {
func (t *Tx) DecodeJSON(b []byte) error {
rawTx := new(encodedTx)
err := json.Unmarshal(b, rawTx)
if err != nil {
panic(err)
return err
}
msgName, err := gogoproto.AnyMessageName(rawTx.Msg)
msgType := proto.MessageType(msgName).Elem()
if err != nil {
panic(err)
return err
}
msg := reflect.New(msgType).Interface().(transaction.Msg)
if err := gogoproto.UnmarshalAny(rawTx.Msg, msg); err != nil {
panic(err)
return err
}
t.Msg = msg
t.Sender = rawTx.Sender
t.GasLimit = rawTx.GasLimit
return nil
}
type TxCodec struct{}
func (TxCodec) Decode(bytes []byte) (Tx, error) {
t := new(Tx)
t.Decode(bytes)
return *t, nil
err := t.Decode(bytes)
return *t, err
}
func (TxCodec) DecodeJSON(bytes []byte) (Tx, error) {
t := new(Tx)
t.DecodeJSON(bytes)
return *t, nil
err := t.DecodeJSON(bytes)
return *t, err
}

View File

@ -57,7 +57,7 @@ require (
cosmossdk.io/errors/v2 v2.0.0-20240731132947-df72853b3ca5 // indirect
cosmossdk.io/schema v0.2.0 // indirect
cosmossdk.io/server/v2/appmanager v0.0.0-20240802110823-cffeedff643d // indirect
cosmossdk.io/server/v2/stf v0.0.0-00010101000000-000000000000 // indirect
cosmossdk.io/server/v2/stf v0.0.0-20240708142107-25e99c54bac1 // indirect
cosmossdk.io/store v1.1.1-0.20240418092142-896cdf1971bc // indirect
cosmossdk.io/x/accounts/defaults/lockup v0.0.0-20240417181816-5e7aae0db1f5 // indirect
cosmossdk.io/x/accounts/defaults/multisig v0.0.0-00010101000000-000000000000 // indirect