mukan-consensus/state/compatibility_test.go
Mukan Erkin Törük ef24c0b67e
Some checks are pending
docker-build-cometbft / vars (push) Waiting to run
docker-build-cometbft / build-images (amd64, ubuntu-24.04) (push) Blocked by required conditions
docker-build-cometbft / build-images (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
docker-build-cometbft / merge-images (push) Blocked by required conditions
docker-build-e2e-node / vars (push) Waiting to run
docker-build-e2e-node / build-images (amd64, ubuntu-24.04) (push) Blocked by required conditions
docker-build-e2e-node / build-images (arm64, ubuntu-24.04-arm) (push) Blocked by required conditions
docker-build-e2e-node / merge-images (push) Blocked by required conditions
initial: sovereign Mukan Network fork
2026-05-11 03:18:27 +03:00

281 lines
11 KiB
Go

package state_test
import (
"fmt"
"testing"
"time"
dbm "github.com/cometbft/cometbft-db"
cmtcrypto "github.com/cometbft/cometbft/proto/tendermint/crypto"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
sm "github.com/cometbft/cometbft/state"
abci "github.com/cometbft/cometbft/abci/types"
cmtstate "github.com/cometbft/cometbft/proto/tendermint/state"
"github.com/stretchr/testify/require"
)
// Compatibility test across different state proto versions
func calcABCIResponsesKey(height int64) []byte {
return []byte(fmt.Sprintf("abciResponsesKey:%v", height))
}
var lastABCIResponseKey = []byte("lastABCIResponseKey")
var (
_ sm.Store = (*MultiStore)(nil)
_ LegacyStore = (*MultiStore)(nil)
)
// MultiStore represents a state store that implements the Store interface
// and contains additional store and database options.
type MultiStore struct {
sm.Store
db dbm.DB
sm.StoreOptions
}
// NewMultiStore returns a new MultiStore.
// It sets the store, db, and StoreOptions fields of the MultiStore struct.
func NewMultiStore(db dbm.DB, options sm.StoreOptions, store sm.Store) *MultiStore {
return &MultiStore{
Store: store,
db: db,
StoreOptions: options,
}
}
// LegacyStore represents a legacy data store.
type LegacyStore interface {
SaveABCIResponses(height int64, abciResponses *cmtstate.LegacyABCIResponses) error
}
// SaveABCIResponses saves the ABCIResponses for a given height in the MultiStore.
// It strips out any nil values from the DeliverTxs field, and saves the ABCIResponses to
// disk if the DiscardABCIResponses flag is set to false. It also saves the last ABCI response
// for crash recovery, overwriting the previously saved response.
func (multi MultiStore) SaveABCIResponses(height int64, abciResponses *cmtstate.LegacyABCIResponses) error {
var dtxs []*abci.ExecTxResult
// strip nil values,
for _, tx := range abciResponses.DeliverTxs {
if tx != nil {
dtxs = append(dtxs, tx)
}
}
abciResponses.DeliverTxs = dtxs
// If the flag is false then we save the ABCIResponse. This can be used for the /block_results
// query or to reindex an event using the command line.
if !multi.DiscardABCIResponses {
bz, err := abciResponses.Marshal()
if err != nil {
return err
}
if err := multi.db.Set(calcABCIResponsesKey(height), bz); err != nil {
return err
}
}
// We always save the last ABCI response for crash recovery.
// This overwrites the previous saved ABCI Response.
response := &cmtstate.ABCIResponsesInfo{
LegacyAbciResponses: abciResponses,
Height: height,
}
bz, err := response.Marshal()
if err != nil {
return err
}
return multi.db.SetSync(lastABCIResponseKey, bz)
}
// TestLegacySaveAndLoadFinalizeBlock tests saving and loading of ABCIResponses
// using the multiStore. It verifies that the loaded ABCIResponses match the
// original ones and that missing fields are correctly handled.
// This test is important for the LoadFinalizeBlockResponse method in the state store.
func TestLegacySaveAndLoadFinalizeBlock(t *testing.T) {
tearDown, stateDB, _, store := setupTestCaseWithStore(t)
defer tearDown(t)
options := sm.StoreOptions{
DiscardABCIResponses: false,
}
height := int64(1)
multiStore := NewMultiStore(stateDB, options, store)
// try with a complete ABCI Response
legacyABCIResponses := newLegacyABCIResponses()
err := multiStore.SaveABCIResponses(height, &legacyABCIResponses)
require.NoError(t, err)
require.Equal(t, 1, len(legacyABCIResponses.DeliverTxs))
require.Equal(t, 1, len(legacyABCIResponses.BeginBlock.Events))
require.Equal(t, 1, len(legacyABCIResponses.EndBlock.Events))
responseFinalizeBlock, err := multiStore.LoadFinalizeBlockResponse(height)
require.NoError(t, err)
// Test for not nil
require.NotNil(t, responseFinalizeBlock.TxResults)
require.NotNil(t, responseFinalizeBlock.Events)
require.NotNil(t, responseFinalizeBlock.ValidatorUpdates)
require.NotNil(t, responseFinalizeBlock.ConsensusParamUpdates)
require.Nil(t, responseFinalizeBlock.AppHash)
// Test for equality
require.Equal(t, 1, len(responseFinalizeBlock.TxResults))
require.Equal(t, len(legacyABCIResponses.DeliverTxs), len(responseFinalizeBlock.TxResults))
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Code, responseFinalizeBlock.TxResults[0].Code)
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Data, responseFinalizeBlock.TxResults[0].Data)
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Log, responseFinalizeBlock.TxResults[0].Log)
require.Equal(t, legacyABCIResponses.DeliverTxs[0].GasWanted, responseFinalizeBlock.TxResults[0].GasWanted)
require.Equal(t, legacyABCIResponses.DeliverTxs[0].GasUsed, responseFinalizeBlock.TxResults[0].GasUsed)
require.Equal(t, len(legacyABCIResponses.DeliverTxs[0].Events), len(responseFinalizeBlock.TxResults[0].Events))
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Events[0].Type, responseFinalizeBlock.TxResults[0].Events[0].Type)
require.Equal(t, len(legacyABCIResponses.DeliverTxs[0].Events[0].Attributes), len(responseFinalizeBlock.TxResults[0].Events[0].Attributes))
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Events[0].Attributes[0].Key, responseFinalizeBlock.TxResults[0].Events[0].Attributes[0].Key)
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Events[0].Attributes[0].Value, responseFinalizeBlock.TxResults[0].Events[0].Attributes[0].Value)
require.Equal(t, legacyABCIResponses.DeliverTxs[0].Codespace, responseFinalizeBlock.TxResults[0].Codespace)
require.Equal(t, 2, len(responseFinalizeBlock.Events))
require.Equal(t, len(legacyABCIResponses.BeginBlock.Events)+len(legacyABCIResponses.EndBlock.Events), len(responseFinalizeBlock.Events))
require.Equal(t, legacyABCIResponses.BeginBlock.Events[0].Type, responseFinalizeBlock.Events[0].Type)
require.Equal(t, len(legacyABCIResponses.BeginBlock.Events[0].Attributes)+1, len(responseFinalizeBlock.Events[0].Attributes)) // +1 for inject 'mode' attribute
require.Equal(t, legacyABCIResponses.BeginBlock.Events[0].Attributes[0].Key, responseFinalizeBlock.Events[0].Attributes[0].Key)
require.Equal(t, legacyABCIResponses.BeginBlock.Events[0].Attributes[0].Value, responseFinalizeBlock.Events[0].Attributes[0].Value)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Block.MaxBytes, responseFinalizeBlock.ConsensusParamUpdates.Block.MaxBytes)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Block.MaxGas, responseFinalizeBlock.ConsensusParamUpdates.Block.MaxGas)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Evidence.MaxAgeNumBlocks, responseFinalizeBlock.ConsensusParamUpdates.Evidence.MaxAgeNumBlocks)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Evidence.MaxAgeDuration, responseFinalizeBlock.ConsensusParamUpdates.Evidence.MaxAgeDuration)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Evidence.MaxBytes, responseFinalizeBlock.ConsensusParamUpdates.Evidence.MaxBytes)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Validator.PubKeyTypes, responseFinalizeBlock.ConsensusParamUpdates.Validator.PubKeyTypes)
require.Equal(t, legacyABCIResponses.EndBlock.ConsensusParamUpdates.Version.App, responseFinalizeBlock.ConsensusParamUpdates.Version.App)
require.Nil(t, responseFinalizeBlock.ConsensusParamUpdates.Abci)
require.Nil(t, responseFinalizeBlock.AppHash)
require.Equal(t, len(legacyABCIResponses.EndBlock.ValidatorUpdates), len(responseFinalizeBlock.ValidatorUpdates))
require.Equal(t, legacyABCIResponses.EndBlock.ValidatorUpdates[0].Power, responseFinalizeBlock.ValidatorUpdates[0].Power)
// skip until an equivalency test is possible
require.Equal(t, legacyABCIResponses.EndBlock.ValidatorUpdates[0].PubKey.GetEd25519(), responseFinalizeBlock.ValidatorUpdates[0].PubKey.GetEd25519())
// try with an ABCI Response missing fields
height = int64(2)
legacyABCIResponses = newLegacyABCIResponsesWithNullFields()
require.Equal(t, 1, len(legacyABCIResponses.DeliverTxs))
require.Equal(t, 1, len(legacyABCIResponses.BeginBlock.Events))
require.Nil(t, legacyABCIResponses.EndBlock)
err = multiStore.SaveABCIResponses(height, &legacyABCIResponses)
require.NoError(t, err)
responseFinalizeBlock, err = multiStore.LoadFinalizeBlockResponse(height)
require.NoError(t, err)
require.Equal(t, len(legacyABCIResponses.DeliverTxs), len(responseFinalizeBlock.TxResults))
require.Equal(t, legacyABCIResponses.DeliverTxs[0].String(), responseFinalizeBlock.TxResults[0].String())
require.Equal(t, len(legacyABCIResponses.BeginBlock.Events), len(responseFinalizeBlock.Events))
}
// Generate a Legacy ABCIResponses with data for all fields.
func newLegacyABCIResponses() cmtstate.LegacyABCIResponses {
eventAttr := abci.EventAttribute{
Key: "key",
Value: "value",
}
deliverTxEvent := abci.Event{
Type: "deliver_tx_event",
Attributes: []abci.EventAttribute{eventAttr},
}
endBlockEvent := abci.Event{
Type: "end_block_event",
Attributes: []abci.EventAttribute{eventAttr},
}
beginBlockEvent := abci.Event{
Type: "begin_block_event",
Attributes: []abci.EventAttribute{eventAttr},
}
responseDeliverTx := abci.ExecTxResult{
Code: abci.CodeTypeOK,
Events: []abci.Event{deliverTxEvent},
}
validatorUpdates := []abci.ValidatorUpdate{{
PubKey: cmtcrypto.PublicKey{Sum: &cmtcrypto.PublicKey_Ed25519{Ed25519: make([]byte, 1)}},
Power: int64(10),
}}
consensusParams := &cmtproto.ConsensusParams{
Block: &cmtproto.BlockParams{
MaxBytes: int64(100000),
MaxGas: int64(10000),
},
Evidence: &cmtproto.EvidenceParams{
MaxAgeNumBlocks: int64(10),
MaxAgeDuration: time.Duration(1000),
MaxBytes: int64(10000),
},
Validator: &cmtproto.ValidatorParams{
PubKeyTypes: []string{"ed25519"},
},
Version: &cmtproto.VersionParams{
App: uint64(10),
},
}
// Legacy ABCI Responses
legacyABCIResponses := cmtstate.LegacyABCIResponses{
DeliverTxs: []*abci.ExecTxResult{
&responseDeliverTx,
},
EndBlock: &cmtstate.ResponseEndBlock{
Events: []abci.Event{endBlockEvent},
ConsensusParamUpdates: consensusParams,
ValidatorUpdates: validatorUpdates,
},
BeginBlock: &cmtstate.ResponseBeginBlock{
Events: []abci.Event{beginBlockEvent},
},
}
return legacyABCIResponses
}
// Generate a Legacy ABCIResponses with null data for some fields.
func newLegacyABCIResponsesWithNullFields() cmtstate.LegacyABCIResponses {
eventAttr := abci.EventAttribute{
Key: "key",
Value: "value",
}
deliverTxEvent := abci.Event{
Type: "deliver_tx_event",
Attributes: []abci.EventAttribute{eventAttr},
}
beginBlockEvent := abci.Event{
Type: "begin_block_event",
Attributes: []abci.EventAttribute{eventAttr},
}
responseDeliverTx := abci.ExecTxResult{
Code: abci.CodeTypeOK,
Events: []abci.Event{deliverTxEvent},
}
// Legacy ABCI Responses
legacyABCIResponses := cmtstate.LegacyABCIResponses{
DeliverTxs: []*abci.ExecTxResult{
&responseDeliverTx,
},
BeginBlock: &cmtstate.ResponseBeginBlock{
Events: []abci.Event{beginBlockEvent},
},
}
return legacyABCIResponses
}