diff --git a/client/app/upgrades/v1_3_0/constants.go b/client/app/upgrades/v1_3_0/constants.go new file mode 100644 index 00000000..4ba1bd13 --- /dev/null +++ b/client/app/upgrades/v1_3_0/constants.go @@ -0,0 +1,18 @@ +//nolint:revive,stylecheck // versioning +package v_1_3_0 + +import ( + storetypes "cosmossdk.io/store/types" + + "github.com/piplabs/story/client/app/upgrades" +) + +const ( + UpgradeName = "v1.3.0" +) + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + CreateUpgradeHandler: CreateUpgradeHandler, + StoreUpgrades: storetypes.StoreUpgrades{}, +} diff --git a/client/app/upgrades/v1_3_0/upgrades.go b/client/app/upgrades/v1_3_0/upgrades.go new file mode 100644 index 00000000..9903452d --- /dev/null +++ b/client/app/upgrades/v1_3_0/upgrades.go @@ -0,0 +1,61 @@ +//nolint:revive,stylecheck // versioning +package v_1_3_0 + +import ( + "context" + + upgradetypes "cosmossdk.io/x/upgrade/types" + + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/ethereum/go-ethereum/common" + + "github.com/piplabs/story/client/app/keepers" + "github.com/piplabs/story/lib/errors" + "github.com/piplabs/story/lib/log" +) + +func CreateUpgradeHandler( + _ *module.Manager, + _ module.Configurator, + keepers *keepers.Keepers, +) upgradetypes.UpgradeHandler { + return func(ctx context.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + log.Info(ctx, "Start upgrade v1.3.0") + + // When this handler is triggered at the beginning of block X, we must process the events from block X-1. + // Otherwise, events from block X-1 will be discarded as the upgrade changes the logic to process events of the + // current block once finalized. + + // Get the block hash and events of the previous block. + head, err := keepers.EVMEngKeeper.GetExecutionHead(ctx) + if err != nil { + return nil, errors.Wrap(err, "get execution head") + } + lastBlockHeight := head.GetBlockHeight() + lastBlockHash := common.BytesToHash(head.GetBlockHash()) + + events, err := keepers.EVMEngKeeper.EvmEvents(ctx, lastBlockHash) + if err != nil { + return nil, errors.Wrap(err, "fetch evm event logs") + } + + // Deliver all the payload log events of the block X-1 + if err := keepers.EvmStakingKeeper.ProcessStakingEvents(ctx, lastBlockHeight, events); err != nil { + return nil, errors.Wrap(err, "deliver staking-related event logs") + } + if err := keepers.EVMEngKeeper.ProcessUpgradeEvents(ctx, lastBlockHeight, events); err != nil { + return nil, errors.Wrap(err, "deliver upgrade-related event logs") + } + if err := keepers.EVMEngKeeper.ProcessUbiEvents(ctx, lastBlockHeight, events); err != nil { + return nil, errors.Wrap(err, "deliver ubi-related event logs") + } + + if err := keepers.EVMEngKeeper.UpdateExecutionHeadWithBlock(ctx, lastBlockHeight, lastBlockHash, head.GetBlockTime()); err != nil { + return nil, errors.Wrap(err, "update execution head") + } + + log.Info(ctx, "Upgrade v1.3.0 complete") + + return vm, nil + } +} diff --git a/client/proto/story/evmengine/v1/types/tx.proto b/client/proto/story/evmengine/v1/types/tx.proto index 07e563db..a2f3692b 100644 --- a/client/proto/story/evmengine/v1/types/tx.proto +++ b/client/proto/story/evmengine/v1/types/tx.proto @@ -19,7 +19,7 @@ message MsgExecutionPayload { option (cosmos.msg.v1.signer) = "authority"; string authority = 1; bytes execution_payload = 2; - repeated EVMEvent prev_payload_events = 3; + repeated EVMEvent prev_payload_events = 3 [deprecated = true]; } message ExecutionPayloadResponse {} diff --git a/client/x/evmengine/keeper/abci.go b/client/x/evmengine/keeper/abci.go index 23d7ddfc..b071c166 100644 --- a/client/x/evmengine/keeper/abci.go +++ b/client/x/evmengine/keeper/abci.go @@ -137,23 +137,10 @@ func (k *Keeper) PrepareProposal(ctx sdk.Context, req *abci.RequestPreparePropos return nil, errors.Wrap(err, "encode") } - // First, collect all vote extension msgs from the vote provider. - // voteMsgs, err := k.voteProvider.PrepareVotes(ctx, req.LocalLastCommit) - // if err != nil { - // return nil, errors.Wrap(err, "prepare votes") - //} - - // Next, collect all prev payload evm event logs. - evmEvents, err := k.evmEvents(ctx, payloadResp.ExecutionPayload.ParentHash) - if err != nil { - return nil, errors.Wrap(err, "prepare evm event logs") - } - // Then construct the execution payload message. payloadMsg := &types.MsgExecutionPayload{ - Authority: authtypes.NewModuleAddress(types.ModuleName).String(), - ExecutionPayload: payloadData, - PrevPayloadEvents: evmEvents, + Authority: authtypes.NewModuleAddress(types.ModuleName).String(), + ExecutionPayload: payloadData, } // Combine all the votes messages and the payload message into a single transaction. @@ -171,8 +158,6 @@ func (k *Keeper) PrepareProposal(ctx sdk.Context, req *abci.RequestPreparePropos log.Info(ctx, "Proposing new block", "height", req.Height, log.Hex7("execution_block_hash", payloadResp.ExecutionPayload.BlockHash[:]), - // "vote_msgs", len(voteMsgs), - "evm_events", len(evmEvents), ) return &abci.ResponsePrepareProposal{Txs: [][]byte{tx}}, nil @@ -253,7 +238,7 @@ func (k *Keeper) PostFinalize(ctx sdk.Context) error { // startBuild triggers the building of a new execution payload on top of the current execution head. // It returns the EngineAPI response which contains a status and payload ID. func (k *Keeper) startBuild(ctx context.Context, feeRecipient common.Address, withdrawals []*etypes.Withdrawal, appHash common.Hash, timestamp time.Time) (engine.ForkChoiceResponse, error) { - head, err := k.getExecutionHead(ctx) + head, err := k.GetExecutionHead(ctx) if err != nil { return engine.ForkChoiceResponse{}, errors.Wrap(err, "latest execution block") } diff --git a/client/x/evmengine/keeper/abci_internal_test.go b/client/x/evmengine/keeper/abci_internal_test.go index 6256a888..0c26fd9a 100644 --- a/client/x/evmengine/keeper/abci_internal_test.go +++ b/client/x/evmengine/keeper/abci_internal_test.go @@ -284,7 +284,7 @@ func TestKeeper_PrepareProposal(t *testing.T) { // get the genesis block to build on top of // Get the parent block we will build on top of - head, err := keeper.getExecutionHead(ctx) + head, err := keeper.GetExecutionHead(ctx) require.NoError(t, err) req := &abci.RequestPrepareProposal{ @@ -536,10 +536,6 @@ func assertExecutablePayload(t *testing.T, msg sdk.Msg, ts int64, blockHash comm require.Equal(t, payload.FeeRecipient, validatorAddr) require.Empty(t, payload.Withdrawals) require.Equal(t, payload.Number, height) - - // require.Len(t, executionPayload.PrevPayloadEvents, 1) - // evmLog := executionPayload.PrevPayloadEvents[0] - // require.Equal(t, evmLog.Address, zeroAddr.Bytes()) } func ctxWithAppHash(t *testing.T, appHash common.Hash) (context.Context, *storetypes.KVStoreKey) { @@ -599,6 +595,7 @@ type mockEngineAPI struct { forkchoiceUpdatedV3Func func(context.Context, eengine.ForkchoiceStateV1, *eengine.PayloadAttributes) (eengine.ForkChoiceResponse, error) getPayloadV3Func func(context.Context, eengine.PayloadID) (*eengine.ExecutionPayloadEnvelope, error) newPayloadV3Func func(context.Context, eengine.ExecutableData, []common.Hash, *common.Hash) (eengine.PayloadStatusV1, error) + filterLogsFunc func(context.Context, ethereum.FilterQuery) ([]types.Log, error) // forceInvalidNewPayloadV3 forces the NewPayloadV3 returns an invalid status. forceInvalidNewPayloadV3 bool // forceInvalidForkchoiceUpdatedV3 forces the ForkchoiceUpdatedV3 returns an invalid status. @@ -683,7 +680,11 @@ func (m mockEngineAPI) maybeSync() (eengine.PayloadStatusV1, bool) { } } -func (mockEngineAPI) FilterLogs(context.Context, ethereum.FilterQuery) ([]types.Log, error) { +func (m mockEngineAPI) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) { + if m.filterLogsFunc != nil { + return m.filterLogsFunc(ctx, q) + } + return nil, nil } diff --git a/client/x/evmengine/keeper/db.go b/client/x/evmengine/keeper/db.go index eee9c505..af1bb99f 100644 --- a/client/x/evmengine/keeper/db.go +++ b/client/x/evmengine/keeper/db.go @@ -34,8 +34,8 @@ func (k *Keeper) InsertGenesisHead(ctx context.Context, executionBlockHash []byt return nil } -// getExecutionHead returns the current execution head. -func (k *Keeper) getExecutionHead(ctx context.Context) (*ExecutionHead, error) { +// GetExecutionHead returns the current execution head. +func (k *Keeper) GetExecutionHead(ctx context.Context) (*ExecutionHead, error) { head, err := k.headTable.Get(ctx, executionHeadID) if err != nil { return nil, errors.Wrap(err, "get execution head") @@ -44,14 +44,24 @@ func (k *Keeper) getExecutionHead(ctx context.Context) (*ExecutionHead, error) { return head, nil } -// updateExecutionHead updates the execution head with the given payload. -func (k *Keeper) updateExecutionHead(ctx context.Context, payload engine.ExecutableData) error { +// UpdateExecutionHead updates the execution head with the given payload. +func (k *Keeper) UpdateExecutionHead(ctx context.Context, payload engine.ExecutableData) error { + return k.updateExecutionHead(ctx, payload.Number, payload.BlockHash, payload.Timestamp) +} + +func (k *Keeper) UpdateExecutionHeadWithBlock(ctx context.Context, blockHeight uint64, blockHash common.Hash, blockTime uint64) error { + return k.updateExecutionHead(ctx, blockHeight, blockHash, blockTime) +} + +func (k *Keeper) updateExecutionHead( + ctx context.Context, blockHeight uint64, blockHash common.Hash, blockTime uint64, +) error { head := &ExecutionHead{ Id: executionHeadID, CreatedHeight: uint64(sdk.UnwrapSDKContext(ctx).BlockHeight()), - BlockHeight: payload.Number, - BlockHash: payload.BlockHash.Bytes(), - BlockTime: payload.Timestamp, + BlockHeight: blockHeight, + BlockHash: blockHash.Bytes(), + BlockTime: blockTime, } err := k.headTable.Update(ctx, head) diff --git a/client/x/evmengine/keeper/db_internal_test.go b/client/x/evmengine/keeper/db_internal_test.go index 32d0eeda..a322aa08 100644 --- a/client/x/evmengine/keeper/db_internal_test.go +++ b/client/x/evmengine/keeper/db_internal_test.go @@ -14,7 +14,7 @@ func TestKeeper_InsertGenesisHead(t *testing.T) { ctx, keeper := createTestKeeper(t) // make sure the execution head does not exist - _, err := keeper.getExecutionHead(ctx) + _, err := keeper.GetExecutionHead(ctx) require.Error(t, err, "execution head should not exist") // insert genesis head @@ -23,7 +23,7 @@ func TestKeeper_InsertGenesisHead(t *testing.T) { require.NoError(t, err) // make sure the execution head is set correctly - head, err := keeper.getExecutionHead(ctx) + head, err := keeper.GetExecutionHead(ctx) require.NoError(t, err) require.NotNil(t, head, "execution head should exist") require.Equal(t, dummyBlockHash, head.GetBlockHash(), "block hash should match") @@ -39,7 +39,7 @@ func TestKeeper_updateExecutionHead(t *testing.T) { ctx, keeper := createTestKeeper(t) // make sure the execution head does not exist - _, err := keeper.getExecutionHead(ctx) + _, err := keeper.GetExecutionHead(ctx) require.Error(t, err, "execution head should not exist") // insert genesis head @@ -48,13 +48,13 @@ func TestKeeper_updateExecutionHead(t *testing.T) { require.NoError(t, err) // make sure the execution head is set correctly - head, err := keeper.getExecutionHead(ctx) + head, err := keeper.GetExecutionHead(ctx) require.NoError(t, err) require.NotNil(t, head, "execution head should exist") // update the execution head newBlockHash := common.BytesToHash([]byte("new hash")) - err = keeper.updateExecutionHead(ctx, engine.ExecutableData{ + err = keeper.UpdateExecutionHead(ctx, engine.ExecutableData{ Number: 100, BlockHash: newBlockHash, Timestamp: 0, @@ -62,7 +62,7 @@ func TestKeeper_updateExecutionHead(t *testing.T) { require.NoError(t, err) // make sure the execution head is updated correctly - head, err = keeper.getExecutionHead(ctx) + head, err = keeper.GetExecutionHead(ctx) require.NoError(t, err) require.NotNil(t, head, "execution head should exist") require.Equal(t, newBlockHash.Bytes(), head.GetBlockHash(), "block hash should match") diff --git a/client/x/evmengine/keeper/evmmsgs.go b/client/x/evmengine/keeper/evmmsgs.go index c567f78b..de9a34cd 100644 --- a/client/x/evmengine/keeper/evmmsgs.go +++ b/client/x/evmengine/keeper/evmmsgs.go @@ -13,8 +13,8 @@ import ( clog "github.com/piplabs/story/lib/log" ) -// evmEvents returns selected EVM log events from the provided block hash. -func (k *Keeper) evmEvents(ctx context.Context, blockHash common.Hash) ([]*types.EVMEvent, error) { +// EvmEvents returns selected EVM log events from the provided block hash. +func (k *Keeper) EvmEvents(ctx context.Context, blockHash common.Hash) ([]*types.EVMEvent, error) { var logs []ethtypes.Log err := retryForever(ctx, func(ctx context.Context) (fetched bool, err error) { logs, err = k.engineCl.FilterLogs(ctx, ethereum.FilterQuery{ @@ -43,18 +43,16 @@ func (k *Keeper) evmEvents(ctx context.Context, blockHash common.Hash) ([]*types for _, t := range l.Topics { topics = append(topics, t.Bytes()) } - events = append(events, &types.EVMEvent{ + event := &types.EVMEvent{ Address: l.Address.Bytes(), Topics: topics, Data: l.Data, TxHash: l.TxHash.Bytes(), - }) - } - - for _, event := range events { + } if err := event.Verify(); err != nil { return nil, errors.Wrap(err, "verify event") } + events = append(events, event) } return events, nil diff --git a/client/x/evmengine/keeper/keeper.go b/client/x/evmengine/keeper/keeper.go index 5e971d09..07173c2e 100644 --- a/client/x/evmengine/keeper/keeper.go +++ b/client/x/evmengine/keeper/keeper.go @@ -156,7 +156,7 @@ func (k *Keeper) parseAndVerifyProposedPayload(ctx context.Context, msg *types.M } // Fetch the latest execution head from the local keeper DB. - head, err := k.getExecutionHead(ctx) + head, err := k.GetExecutionHead(ctx) if err != nil { return engine.ExecutableData{}, errors.Wrap(err, "latest execution block") } diff --git a/client/x/evmengine/keeper/keeper_internal_test.go b/client/x/evmengine/keeper/keeper_internal_test.go index 058cb62d..5b292338 100644 --- a/client/x/evmengine/keeper/keeper_internal_test.go +++ b/client/x/evmengine/keeper/keeper_internal_test.go @@ -165,7 +165,7 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { { name: "fail: payload parent hash is not equal to head hash", msg: func(c context.Context) *types.MsgExecutionPayload { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) payload, err := ethclient.MakePayload(fuzzer, execHead.GetBlockHeight()+1, uint64(now.Unix()), common.Hash{}, common.Address{}, common.Hash{}, &common.Hash{}) @@ -184,7 +184,7 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { { name: "fail: invalid payload timestamp", msg: func(c context.Context) *types.MsgExecutionPayload { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) weekAgo := execHead.GetBlockTime() - 604800 @@ -204,7 +204,7 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { { name: "fail: invalid payload random", msg: func(c context.Context) *types.MsgExecutionPayload { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) payload, err := ethclient.MakePayload(fuzzer, execHead.GetBlockHeight()+1, uint64(now.Unix()), execHead.Hash(), common.Address{}, common.Hash{}, &common.Hash{}) @@ -223,7 +223,7 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { { name: "fail: invalid authority", msg: func(c context.Context) *types.MsgExecutionPayload { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) payload, err := ethclient.MakePayload(fuzzer, execHead.GetBlockHeight()+1, uint64(now.Unix()), execHead.Hash(), common.Address{}, execHead.Hash(), &common.Hash{}) @@ -242,7 +242,7 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { { name: "pass: valid payload", msg: func(c context.Context) *types.MsgExecutionPayload { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) payload, err := ethclient.MakePayload(fuzzer, execHead.GetBlockHeight()+1, uint64(now.Unix()), execHead.Hash(), common.Address{}, execHead.Hash(), &common.Hash{}) @@ -260,10 +260,10 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { { name: "pass: valid payload when consensus block time is less than execution block time", setup: func(c context.Context) sdk.Context { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) // update execution head with current block time - err = keeper.updateExecutionHead(c, engine.ExecutableData{ + err = keeper.UpdateExecutionHead(c, engine.ExecutableData{ Number: execHead.GetBlockHeight(), BlockHash: common.BytesToHash(execHead.GetBlockHash()), Timestamp: uint64(now.Unix()), @@ -277,7 +277,7 @@ func TestKeeper_parseAndVerifyProposedPayload(t *testing.T) { return sdkCtx }, msg: func(c context.Context) *types.MsgExecutionPayload { - execHead, err := keeper.getExecutionHead(c) + execHead, err := keeper.GetExecutionHead(c) require.NoError(t, err) payload, err := ethclient.MakePayload(fuzzer, execHead.GetBlockHeight()+1, execHead.GetBlockTime()+1, execHead.Hash(), common.Address{}, execHead.Hash(), &common.Hash{}) diff --git a/client/x/evmengine/keeper/msg_server.go b/client/x/evmengine/keeper/msg_server.go index 62c387e4..c50ae5da 100644 --- a/client/x/evmengine/keeper/msg_server.go +++ b/client/x/evmengine/keeper/msg_server.go @@ -141,18 +141,24 @@ func (s msgServer) ExecutionPayload(ctx context.Context, msg *types.MsgExecution return nil, err } - // Deliver all the previous payload log events - if err := s.evmstakingKeeper.ProcessStakingEvents(ctx, payload.Number-1, msg.PrevPayloadEvents); err != nil { + // get events of the newly finalized block + events, err := s.EvmEvents(ctx, payload.BlockHash) + if err != nil { + return nil, errors.Wrap(err, "fetch evm event logs") + } + + // Deliver all the payload log events of the newly finalized block + if err := s.evmstakingKeeper.ProcessStakingEvents(ctx, payload.Number-1, events); err != nil { return nil, errors.Wrap(err, "deliver staking-related event logs") } - if err := s.ProcessUpgradeEvents(ctx, payload.Number-1, msg.PrevPayloadEvents); err != nil { + if err := s.ProcessUpgradeEvents(ctx, payload.Number-1, events); err != nil { return nil, errors.Wrap(err, "deliver upgrade-related event logs") } - if err := s.ProcessUbiEvents(ctx, payload.Number-1, msg.PrevPayloadEvents); err != nil { + if err := s.ProcessUbiEvents(ctx, payload.Number-1, events); err != nil { return nil, errors.Wrap(err, "deliver ubi-related event logs") } - if err := s.updateExecutionHead(ctx, payload); err != nil { + if err := s.UpdateExecutionHead(ctx, payload); err != nil { return nil, errors.Wrap(err, "update execution head") } diff --git a/client/x/evmengine/keeper/msg_server_internal_test.go b/client/x/evmengine/keeper/msg_server_internal_test.go index baa46868..e148d9dc 100644 --- a/client/x/evmengine/keeper/msg_server_internal_test.go +++ b/client/x/evmengine/keeper/msg_server_internal_test.go @@ -11,9 +11,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/beacon/engine" "github.com/ethereum/go-ethereum/common" etypes "github.com/ethereum/go-ethereum/core/types" + fuzz "github.com/google/gofuzz" "github.com/stretchr/testify/require" moduletestutil "github.com/piplabs/story/client/x/evmengine/testutil" @@ -51,7 +53,6 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { ctx, storeKey, storeService := setupCtxStore(t, &header) ctx = ctx.WithExecMode(sdk.ExecModeFinalize) - evmLogProc := mockLogProvider{deliverErr: errors.New("test error")} mockEngine, err := newMockEngineAPI(storeKey, 2) require.NoError(t, err) keeper, err := NewKeeper(cdc, storeService, &mockEngine, mockClient, txConfig, ak, esk, uk, dk) @@ -80,20 +81,32 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { return block, payloadID, payloadData } - createRandomEvents := func(c context.Context, blkHash common.Hash) []*types.EVMEvent { - events, err := evmLogProc.Prepare(c, blkHash) - require.NoError(t, err) - - return events + createRandomEvmEvents := func(c context.Context, blockHash common.Hash) func(ctx context.Context, q ethereum.FilterQuery) ([]etypes.Log, error) { + f := fuzz.NewWithSeed(int64(blockHash[0])) + + var topic common.Hash + f.Fuzz(&topic) + + var txHash common.Hash + f.Fuzz(&txHash) + + return func(ctx context.Context, q ethereum.FilterQuery) ([]etypes.Log, error) { + return []etypes.Log{{ + Address: zeroAddr, + Topics: []common.Hash{topic}, + Data: []byte("data"), + TxHash: txHash, + }}, nil + } } tcs := []struct { - name string - setup func(c context.Context) sdk.Context - createPayload func(c context.Context) (*etypes.Block, engine.PayloadID, []byte) - createPrevPayloadEvents func(c context.Context, blkHash common.Hash) []*types.EVMEvent - expectedError string - postCheck func(c context.Context, block *etypes.Block, payloadID engine.PayloadID) + name string + setup func(c context.Context) sdk.Context + createPayload func(c context.Context) (*etypes.Block, engine.PayloadID, []byte) + createEvmLogs func(ctx context.Context, blockHash common.Hash) func(ctx context.Context, q ethereum.FilterQuery) ([]etypes.Log, error) + expectedError string + postCheck func(c context.Context, block *etypes.Block, payloadID engine.PayloadID) }{ { name: "pass: valid payload", @@ -105,8 +118,8 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { return sdk.UnwrapSDKContext(c) }, - createPayload: createValidPayload, - createPrevPayloadEvents: createRandomEvents, + createPayload: createValidPayload, + createEvmLogs: createRandomEvmEvents, postCheck: func(c context.Context, block *etypes.Block, payloadID engine.PayloadID) { gotPayload, err := mockEngine.GetPayloadV3(c, payloadID) require.NoError(t, err) @@ -156,8 +169,8 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { return block, payloadID, payloadData }, - createPrevPayloadEvents: createRandomEvents, - expectedError: "invalid proposed payload number", + createEvmLogs: createRandomEvmEvents, + expectedError: "invalid proposed payload number", }, { name: "fail: DequeueEligibleWithdrawals error", @@ -180,9 +193,9 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { return sdk.UnwrapSDKContext(ctx) }, - createPayload: createValidPayload, - createPrevPayloadEvents: createRandomEvents, - expectedError: "payload invalid", + createPayload: createValidPayload, + createEvmLogs: createRandomEvmEvents, + expectedError: "payload invalid", }, { name: "fail: ForkchoiceUpdatedV3 returns status invalid", @@ -194,9 +207,9 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { return sdk.UnwrapSDKContext(ctx) }, - createPayload: createValidPayload, - createPrevPayloadEvents: createRandomEvents, - expectedError: "payload invalid", + createPayload: createValidPayload, + createEvmLogs: createRandomEvmEvents, + expectedError: "payload invalid", }, { name: "fail: ProcessStakingEvents error", @@ -208,36 +221,38 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { return sdk.UnwrapSDKContext(ctx) }, - createPayload: createValidPayload, - createPrevPayloadEvents: createRandomEvents, - expectedError: "deliver staking-related event logs", + createPayload: createValidPayload, + createEvmLogs: createRandomEvmEvents, + expectedError: "deliver staking-related event logs", }, { - name: "fail: ProcessUpgradeEvents error", + name: "fail: parsing event error", setup: func(ctx context.Context) sdk.Context { esk.EXPECT().MaxWithdrawalPerBlock(ctx).Return(uint32(0), nil) esk.EXPECT().DequeueEligibleWithdrawals(ctx, gomock.Any()).Return(nil, nil) esk.EXPECT().DequeueEligibleRewardWithdrawals(ctx, gomock.Any()).Return(nil, nil) - esk.EXPECT().ProcessStakingEvents(ctx, gomock.Any(), gomock.Any()).Return(nil) return sdk.UnwrapSDKContext(ctx) }, createPayload: createValidPayload, - createPrevPayloadEvents: func(_ context.Context, _ common.Hash) []*types.EVMEvent { + createEvmLogs: func(ctx context.Context, blockHash common.Hash) func(ctx context.Context, q ethereum.FilterQuery) ([]etypes.Log, error) { // crate invalid upgrade event to trigger ProcessUpgradeEvents failure upgradeAbi, err := bindings.UpgradeEntrypointMetaData.GetAbi() require.NoError(t, err, "failed to load ABI") data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade", int64(0), "test-info") require.NoError(t, err) - return []*types.EVMEvent{{ - Address: nil, // nil address - Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, - Data: data, - TxHash: dummyHash.Bytes(), - }} + return func(ctx context.Context, q ethereum.FilterQuery) ([]etypes.Log, error) { + return []etypes.Log{{ + Address: zeroAddr, + // use empty bytes (not any of the expected event IDs) to trigger error + Topics: []common.Hash{}, + Data: data, + TxHash: dummyHash, + }}, nil + } }, - expectedError: "deliver upgrade-related event logs", + expectedError: "fetch evm event logs: verify event: empty topics", }, } @@ -248,7 +263,6 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { var payloadData []byte var payloadID engine.PayloadID var block *etypes.Block - var events []*types.EVMEvent cachedCtx, _ := ctx.CacheContext() if tc.setup != nil { @@ -257,14 +271,13 @@ func Test_msgServer_ExecutionPayload(t *testing.T) { if tc.createPayload != nil { block, payloadID, payloadData = tc.createPayload(cachedCtx) } - if tc.createPrevPayloadEvents != nil { - events = tc.createPrevPayloadEvents(cachedCtx, block.Hash()) + if tc.createEvmLogs != nil { + mockEngine.filterLogsFunc = tc.createEvmLogs(cachedCtx, block.Hash()) } resp, err := msgSrv.ExecutionPayload(cachedCtx, &types.MsgExecutionPayload{ - Authority: authtypes.NewModuleAddress(types.ModuleName).String(), - ExecutionPayload: payloadData, - PrevPayloadEvents: events, + Authority: authtypes.NewModuleAddress(types.ModuleName).String(), + ExecutionPayload: payloadData, }) if tc.expectedError != "" { require.ErrorContains(t, err, tc.expectedError) diff --git a/client/x/evmengine/keeper/proposal_server.go b/client/x/evmengine/keeper/proposal_server.go index 88fc7a07..fb29c2f7 100644 --- a/client/x/evmengine/keeper/proposal_server.go +++ b/client/x/evmengine/keeper/proposal_server.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/cosmos/gogoproto/proto" etypes "github.com/ethereum/go-ethereum/core/types" "github.com/piplabs/story/client/x/evmengine/types" @@ -51,17 +50,6 @@ func (s proposalServer) ExecutionPayload(ctx context.Context, msg *types.MsgExec return nil, err } - // Collect local view of the evm logs from the previous payload. - evmEvents, err := s.evmEvents(ctx, payload.ParentHash) - if err != nil { - return nil, errors.Wrap(err, "prepare evm event logs") - } - - // Ensure the proposed evm event logs are equal to the local view. - if err := evmEventsEqual(evmEvents, msg.PrevPayloadEvents); err != nil { - return nil, errors.Wrap(err, "verify prev payload events") - } - return &types.ExecutionPayloadResponse{}, nil } @@ -73,20 +61,6 @@ func NewProposalServer(keeper *Keeper) types.MsgServiceServer { var _ types.MsgServiceServer = proposalServer{} -func evmEventsEqual(a, b []*types.EVMEvent) error { - if len(a) != len(b) { - return errors.New("count mismatch") - } - - for i := range a { - if !proto.Equal(a[i], b[i]) { - return errors.New("log mismatch", "index", i) - } - } - - return nil -} - // compareWithdrawals compares the local peek and received withdrawals. func (s proposalServer) compareWithdrawals(ctx context.Context, actualWithdrawals etypes.Withdrawals) error { maxWithdrawals, err := s.evmstakingKeeper.MaxWithdrawalPerBlock(ctx) diff --git a/client/x/evmengine/types/tx.pb.go b/client/x/evmengine/types/tx.pb.go index 46acbd23..70bc1424 100644 --- a/client/x/evmengine/types/tx.pb.go +++ b/client/x/evmengine/types/tx.pb.go @@ -33,7 +33,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type MsgExecutionPayload struct { Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` ExecutionPayload []byte `protobuf:"bytes,2,opt,name=execution_payload,json=executionPayload,proto3" json:"execution_payload,omitempty"` - PrevPayloadEvents []*EVMEvent `protobuf:"bytes,3,rep,name=prev_payload_events,json=prevPayloadEvents,proto3" json:"prev_payload_events,omitempty"` + PrevPayloadEvents []*EVMEvent `protobuf:"bytes,3,rep,name=prev_payload_events,json=prevPayloadEvents,proto3" json:"prev_payload_events,omitempty"` // Deprecated: Do not use. } func (m *MsgExecutionPayload) Reset() { *m = MsgExecutionPayload{} } @@ -83,6 +83,7 @@ func (m *MsgExecutionPayload) GetExecutionPayload() []byte { return nil } +// Deprecated: Do not use. func (m *MsgExecutionPayload) GetPrevPayloadEvents() []*EVMEvent { if m != nil { return m.PrevPayloadEvents @@ -205,33 +206,33 @@ func init() { func init() { proto.RegisterFile("story/evmengine/v1/types/tx.proto", fileDescriptor_21bf9c7ed4e0e2c4) } var fileDescriptor_21bf9c7ed4e0e2c4 = []byte{ - // 403 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x41, 0x8b, 0xd3, 0x40, - 0x14, 0xc7, 0x3b, 0xdb, 0xb5, 0xeb, 0x8e, 0x45, 0x76, 0x67, 0xc1, 0x1d, 0x8a, 0x84, 0x9a, 0x53, - 0x58, 0x31, 0x43, 0xeb, 0xcd, 0xa3, 0x50, 0x10, 0xa4, 0x20, 0x11, 0x3c, 0x78, 0x29, 0xd3, 0xe4, - 0x91, 0x0c, 0x34, 0x99, 0x21, 0x6f, 0x12, 0x92, 0x9b, 0x78, 0xf4, 0xe4, 0x47, 0xd9, 0x0f, 0xe1, - 0xc1, 0xe3, 0x1e, 0x3d, 0x4a, 0x7b, 0xd8, 0xaf, 0x21, 0x9d, 0x6e, 0x5c, 0xa9, 0xd6, 0x5b, 0xde, - 0xe3, 0xf7, 0x7f, 0x79, 0xff, 0x37, 0x7f, 0xfa, 0x0c, 0xad, 0x2e, 0x5b, 0x01, 0x75, 0x0e, 0x45, - 0xaa, 0x0a, 0x10, 0xf5, 0x44, 0xd8, 0xd6, 0x00, 0x0a, 0xdb, 0x84, 0xa6, 0xd4, 0x56, 0x33, 0xee, - 0x90, 0xf0, 0x37, 0x12, 0xd6, 0x93, 0xd0, 0x21, 0xa3, 0xcb, 0x58, 0x63, 0xae, 0x51, 0xe4, 0x98, - 0x6e, 0x75, 0x39, 0xa6, 0x3b, 0x89, 0xff, 0x8d, 0xd0, 0x8b, 0x39, 0xa6, 0xb3, 0x06, 0xe2, 0xca, - 0x2a, 0x5d, 0xbc, 0x93, 0xed, 0x4a, 0xcb, 0x84, 0x3d, 0xa5, 0xa7, 0xb2, 0xb2, 0x99, 0x2e, 0x95, - 0x6d, 0x39, 0x19, 0x93, 0xe0, 0x34, 0xba, 0x6f, 0xb0, 0xe7, 0xf4, 0x1c, 0x3a, 0xc5, 0xc2, 0xec, - 0x24, 0xfc, 0x68, 0x4c, 0x82, 0x61, 0x74, 0x06, 0xfb, 0xa3, 0x22, 0x7a, 0x61, 0x4a, 0xa8, 0x3b, - 0x6e, 0x01, 0x35, 0x14, 0x16, 0x79, 0x7f, 0xdc, 0x0f, 0x1e, 0x4d, 0xfd, 0xf0, 0xd0, 0xce, 0xe1, - 0xec, 0xc3, 0x7c, 0xb6, 0x45, 0xa3, 0xf3, 0xad, 0xfc, 0x6e, 0x9a, 0xeb, 0xe0, 0xab, 0xc7, 0x9f, - 0x6f, 0xaf, 0xaf, 0xee, 0x17, 0xf2, 0x47, 0x94, 0xef, 0x5b, 0x88, 0x00, 0x8d, 0x2e, 0x10, 0x7c, - 0x45, 0x1f, 0x76, 0xa3, 0x18, 0xa7, 0x27, 0x32, 0x49, 0x4a, 0x40, 0x74, 0xa6, 0x86, 0x51, 0x57, - 0xb2, 0x27, 0x74, 0x60, 0xb5, 0x51, 0x31, 0xf2, 0xa3, 0x71, 0x3f, 0x18, 0x46, 0x77, 0x15, 0x63, - 0xf4, 0x38, 0x91, 0x56, 0xf2, 0xbe, 0xc3, 0xdd, 0x37, 0xbb, 0xa4, 0x27, 0xb6, 0x59, 0x64, 0x12, - 0x33, 0x7e, 0xec, 0xda, 0x03, 0xdb, 0xbc, 0x91, 0x98, 0x4d, 0xbf, 0x10, 0x4a, 0xe7, 0x98, 0xbe, - 0x87, 0xb2, 0x56, 0x31, 0xb0, 0x8a, 0x9e, 0xfd, 0x75, 0xd8, 0x17, 0x87, 0x0d, 0xff, 0xe3, 0x1d, - 0x46, 0xd3, 0xff, 0xdc, 0xe7, 0x80, 0xe1, 0xd1, 0x83, 0x4f, 0xb7, 0xd7, 0x57, 0xe4, 0xf5, 0xdb, - 0xef, 0x6b, 0x8f, 0xdc, 0xac, 0x3d, 0xf2, 0x73, 0xed, 0x91, 0xaf, 0x1b, 0xaf, 0x77, 0xb3, 0xf1, - 0x7a, 0x3f, 0x36, 0x5e, 0xef, 0xe3, 0x24, 0x55, 0x36, 0xab, 0x96, 0x61, 0xac, 0x73, 0x61, 0x94, - 0x59, 0xc9, 0x25, 0x8a, 0x5d, 0xba, 0xe2, 0x95, 0x82, 0xc2, 0x8a, 0xe6, 0x8f, 0x98, 0xb9, 0x9f, - 0x2d, 0x07, 0x2e, 0x2e, 0x2f, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x94, 0x42, 0x36, 0xc4, 0x86, - 0x02, 0x00, 0x00, + // 406 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xc1, 0x6a, 0xdb, 0x40, + 0x10, 0x86, 0xbd, 0x76, 0xea, 0x34, 0x5b, 0x53, 0x92, 0x0d, 0x34, 0x8b, 0x29, 0xc2, 0xf5, 0x49, + 0xa4, 0x54, 0x8b, 0xdd, 0x5b, 0x8f, 0x01, 0x43, 0xa1, 0x18, 0x8a, 0x0a, 0x39, 0xf4, 0x62, 0xd6, + 0xd2, 0x20, 0x2d, 0x58, 0xda, 0x45, 0xb3, 0x12, 0xd2, 0xad, 0xf4, 0xd8, 0x53, 0x1f, 0x25, 0x8f, + 0x91, 0x63, 0x8e, 0x3d, 0x16, 0xfb, 0x90, 0xd7, 0x28, 0x5e, 0x47, 0x4d, 0x71, 0xeb, 0xde, 0x34, + 0xc3, 0xf7, 0xff, 0x9a, 0x7f, 0x76, 0xe8, 0x2b, 0xb4, 0xba, 0x68, 0x04, 0x54, 0x19, 0xe4, 0x89, + 0xca, 0x41, 0x54, 0x13, 0x61, 0x1b, 0x03, 0x28, 0x6c, 0x1d, 0x98, 0x42, 0x5b, 0xcd, 0xb8, 0x43, + 0x82, 0xdf, 0x48, 0x50, 0x4d, 0x02, 0x87, 0x0c, 0x2f, 0x22, 0x8d, 0x99, 0x46, 0x91, 0x61, 0xb2, + 0xd5, 0x65, 0x98, 0xec, 0x24, 0xe3, 0x5b, 0x42, 0xcf, 0xe7, 0x98, 0xcc, 0x6a, 0x88, 0x4a, 0xab, + 0x74, 0xfe, 0x51, 0x36, 0x2b, 0x2d, 0x63, 0xf6, 0x92, 0x9e, 0xc8, 0xd2, 0xa6, 0xba, 0x50, 0xb6, + 0xe1, 0x64, 0x44, 0xfc, 0x93, 0xf0, 0xb1, 0xc1, 0x5e, 0xd3, 0x33, 0x68, 0x15, 0x0b, 0xb3, 0x93, + 0xf0, 0xee, 0x88, 0xf8, 0x83, 0xf0, 0x14, 0xf6, 0xad, 0xae, 0xe9, 0xb9, 0x29, 0xa0, 0x6a, 0xb9, + 0x05, 0x54, 0x90, 0x5b, 0xe4, 0xbd, 0x51, 0xcf, 0x7f, 0x36, 0x1d, 0x07, 0x87, 0x66, 0x0e, 0x66, + 0xd7, 0xf3, 0xd9, 0x16, 0xbd, 0xea, 0x72, 0x12, 0x9e, 0x6d, 0x2d, 0x1e, 0x1c, 0x5d, 0x17, 0xdf, + 0x3d, 0xff, 0x7a, 0x7f, 0x73, 0xf9, 0x38, 0xd4, 0x78, 0x48, 0xf9, 0x7e, 0x8c, 0x10, 0xd0, 0xe8, + 0x1c, 0x61, 0xac, 0xe8, 0xd3, 0xd6, 0x8e, 0x71, 0x7a, 0x2c, 0xe3, 0xb8, 0x00, 0x44, 0x17, 0x6c, + 0x10, 0xb6, 0x25, 0x7b, 0x41, 0xfb, 0x56, 0x1b, 0x15, 0x21, 0xef, 0x8e, 0x7a, 0xfe, 0x20, 0x7c, + 0xa8, 0x18, 0xa3, 0x47, 0xb1, 0xb4, 0x92, 0xf7, 0x1c, 0xee, 0xbe, 0xd9, 0x05, 0x3d, 0xb6, 0xf5, + 0x22, 0x95, 0x98, 0xf2, 0x23, 0xd7, 0xee, 0xdb, 0xfa, 0xbd, 0xc4, 0x74, 0xfa, 0x8d, 0x50, 0x3a, + 0xc7, 0xe4, 0x13, 0x14, 0x95, 0x8a, 0x80, 0x95, 0xf4, 0xf4, 0xaf, 0xe5, 0xbe, 0x39, 0x1c, 0xfa, + 0x1f, 0x6f, 0x31, 0x9c, 0xfe, 0x67, 0x47, 0x07, 0x02, 0x0f, 0x9f, 0x7c, 0xb9, 0xbf, 0xb9, 0x24, + 0x57, 0x1f, 0x6e, 0xd7, 0x1e, 0xb9, 0x5b, 0x7b, 0xe4, 0xe7, 0xda, 0x23, 0xdf, 0x37, 0x5e, 0xe7, + 0x6e, 0xe3, 0x75, 0x7e, 0x6c, 0xbc, 0xce, 0xe7, 0x49, 0xa2, 0x6c, 0x5a, 0x2e, 0x83, 0x48, 0x67, + 0xc2, 0x28, 0xb3, 0x92, 0x4b, 0x14, 0xbb, 0x0b, 0x8b, 0x56, 0x0a, 0x72, 0x2b, 0xea, 0x3f, 0x4e, + 0xcd, 0xfd, 0x6c, 0xd9, 0x77, 0x27, 0xf3, 0xf6, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3b, 0xee, + 0x21, 0x5e, 0x8a, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used.