Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/evm/single/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ replace github.com/celestiaorg/go-header => github.com/julienrbrt/go-header v0.0

replace (
github.com/evstack/ev-node => ../../../
github.com/evstack/ev-node/core => ../../../core
github.com/evstack/ev-node/da => ../../../da
github.com/evstack/ev-node/execution/evm => ../../../execution/evm
github.com/evstack/ev-node/sequencers/single => ../../../sequencers/single
Expand Down
2 changes: 0 additions & 2 deletions apps/evm/single/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ github.com/ethereum/go-ethereum v1.16.4 h1:H6dU0r2p/amA7cYg6zyG9Nt2JrKKH6oX2utfc
github.com/ethereum/go-ethereum v1.16.4/go.mod h1:P7551slMFbjn2zOQaKrJShZVN/d8bGxp4/I6yZVlb5w=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/evstack/ev-node/core v1.0.0-beta.3 h1:01K2Ygm3puX4m2OBxvg/HDxu+he54jeNv+KDmpgujFc=
github.com/evstack/ev-node/core v1.0.0-beta.3/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU=
Expand Down
1 change: 1 addition & 0 deletions apps/grpc/single/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ require (

replace (
github.com/evstack/ev-node => ../../../
github.com/evstack/ev-node/core => ../../../core
github.com/evstack/ev-node/da => ../../../da
github.com/evstack/ev-node/execution/grpc => ../../../execution/grpc
github.com/evstack/ev-node/sequencers/single => ../../../sequencers/single
Expand Down
2 changes: 0 additions & 2 deletions apps/grpc/single/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evstack/ev-node/core v1.0.0-beta.3 h1:01K2Ygm3puX4m2OBxvg/HDxu+he54jeNv+KDmpgujFc=
github.com/evstack/ev-node/core v1.0.0-beta.3/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY=
github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU=
github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs=
github.com/filecoin-project/go-jsonrpc v0.8.0 h1:2yqlN3Vd8Gx5UtA3fib7tQu2aW1cSOJt253LEBWExo4=
Expand Down
1 change: 1 addition & 0 deletions apps/testapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ replace github.com/celestiaorg/go-header => github.com/julienrbrt/go-header v0.0

replace (
github.com/evstack/ev-node => ../../.
github.com/evstack/ev-node/core => ../../core
github.com/evstack/ev-node/da => ../../da
github.com/evstack/ev-node/sequencers/single => ../../sequencers/single
)
Expand Down
2 changes: 0 additions & 2 deletions apps/testapp/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evstack/ev-node/core v1.0.0-beta.3 h1:01K2Ygm3puX4m2OBxvg/HDxu+he54jeNv+KDmpgujFc=
github.com/evstack/ev-node/core v1.0.0-beta.3/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY=
github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU=
github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs=
github.com/filecoin-project/go-jsonrpc v0.8.0 h1:2yqlN3Vd8Gx5UtA3fib7tQu2aW1cSOJt253LEBWExo4=
Expand Down
173 changes: 173 additions & 0 deletions block/internal/common/execution_syncer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package common

import (
"bytes"
"context"
"encoding/hex"
"fmt"

"github.com/rs/zerolog"

coreexecutor "github.com/evstack/ev-node/core/execution"
"github.com/evstack/ev-node/pkg/genesis"
"github.com/evstack/ev-node/pkg/store"
"github.com/evstack/ev-node/types"
)

// ExecutionLayerSyncer handles synchronization of the execution layer with ev-node's state.
// It replays blocks from the store to bring the execution layer up to date.
type ExecutionLayerSyncer struct {
Comment thread
randygrok marked this conversation as resolved.
Outdated
store store.Store
exec coreexecutor.Executor
genesis genesis.Genesis
logger zerolog.Logger
}

// NewExecutionLayerSyncer creates a new execution layer syncer.
func NewExecutionLayerSyncer(
store store.Store,
exec coreexecutor.Executor,
genesis genesis.Genesis,
logger zerolog.Logger,
) *ExecutionLayerSyncer {
return &ExecutionLayerSyncer{
store: store,
exec: exec,
genesis: genesis,
logger: logger.With().Str("component", "execution_syncer").Logger(),
}
}

// SyncToHeight checks if the execution layer is behind ev-node and syncs it to the target height.
// This is useful for crash recovery scenarios where ev-node is ahead of the execution layer.
//
// Returns:
// - error if sync fails or if execution layer is ahead of ev-node (unexpected state)
func (s *ExecutionLayerSyncer) SyncToHeight(ctx context.Context, targetHeight uint64) error {
// Check if the executor implements HeightProvider
execHeightProvider, ok := s.exec.(coreexecutor.HeightProvider)
if !ok {
s.logger.Debug().Msg("executor does not implement HeightProvider, skipping sync")
return nil
}

// Skip sync check if we're at genesis
if targetHeight < s.genesis.InitialHeight {
s.logger.Debug().Msg("at genesis height, skipping execution layer sync check")
return nil
}

execHeight, err := execHeightProvider.GetLatestHeight(ctx)
if err != nil {
return fmt.Errorf("failed to get execution layer height: %w", err)
}

s.logger.Info().
Uint64("target_height", targetHeight).
Uint64("exec_layer_height", execHeight).
Msg("execution layer height check")

// If execution layer is ahead, this is unexpected, fail hard
if execHeight > targetHeight {
s.logger.Error().
Uint64("target_height", targetHeight).
Uint64("exec_layer_height", execHeight).
Msg("execution layer is ahead of target height - this should not happen")
return fmt.Errorf("execution layer height (%d) is ahead of target height (%d)", execHeight, targetHeight)
}

// If execution layer is behind, sync the missing blocks
if execHeight < targetHeight {
s.logger.Info().
Uint64("target_height", targetHeight).
Uint64("exec_layer_height", execHeight).
Uint64("blocks_to_sync", targetHeight-execHeight).
Msg("execution layer is behind, syncing blocks")

// Sync blocks from execHeight+1 to targetHeight
for height := execHeight + 1; height <= targetHeight; height++ {
if err := s.replayBlock(ctx, height); err != nil {
return fmt.Errorf("failed to replay block %d to execution layer: %w", height, err)
}
}

s.logger.Info().
Uint64("synced_blocks", targetHeight-execHeight).
Msg("successfully synced execution layer")
} else {
s.logger.Info().Msg("execution layer is in sync")
}

return nil
}

// replayBlock replays a specific block from the store to the execution layer.
func (s *ExecutionLayerSyncer) replayBlock(ctx context.Context, height uint64) error {
s.logger.Info().Uint64("height", height).Msg("replaying block to execution layer")

// Get the block from store
header, data, err := s.store.GetBlockData(ctx, height)
if err != nil {
return fmt.Errorf("failed to get block data from store: %w", err)
}

// Get the previous state
var prevState types.State
if height == s.genesis.InitialHeight {
// For the first block, use genesis state
prevState = types.State{
ChainID: s.genesis.ChainID,
InitialHeight: s.genesis.InitialHeight,
LastBlockHeight: s.genesis.InitialHeight - 1,
LastBlockTime: s.genesis.StartTime,
AppHash: header.AppHash, // This will be updated by InitChain
}
} else {
// Get previous state from store
prevState, err = s.store.GetState(ctx)
if err != nil {
return fmt.Errorf("failed to get previous state: %w", err)
}
// We need the state at height-1, so load that block's app hash
prevHeader, _, err := s.store.GetBlockData(ctx, height-1)
if err != nil {
return fmt.Errorf("failed to get previous block header: %w", err)
}
prevState.AppHash = prevHeader.AppHash
prevState.LastBlockHeight = height - 1
}

// Prepare transactions
rawTxs := make([][]byte, len(data.Txs))
for i, tx := range data.Txs {
rawTxs[i] = []byte(tx)
}

// Execute transactions on the execution layer
s.logger.Debug().
Uint64("height", height).
Int("tx_count", len(rawTxs)).
Msg("executing transactions on execution layer")

newAppHash, _, err := s.exec.ExecuteTxs(ctx, rawTxs, height, header.Time(), prevState.AppHash)
if err != nil {
return fmt.Errorf("failed to execute transactions: %w", err)
}

// Verify the app hash matches
if !bytes.Equal(newAppHash, header.AppHash) {
s.logger.Warn().
Str("expected", hex.EncodeToString(header.AppHash)).
Str("got", hex.EncodeToString(newAppHash)).
Uint64("height", height).
Msg("app hash mismatch during replay")
// Don't fail here - the execution layer may compute slightly different
// state roots in some cases, but the state should still be valid
}

s.logger.Info().
Uint64("height", height).
Msg("successfully replayed block to execution layer")

return nil
}
Loading
Loading