Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions apps/evm/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ module github.com/evstack/ev-node/apps/evm

go 1.25.0

replace (
github.com/evstack/ev-node => ../../
github.com/evstack/ev-node/execution/evm => ../../execution/evm
)

require (
github.com/celestiaorg/go-header v0.8.1
github.com/ethereum/go-ethereum v1.16.8
Expand Down
4 changes: 0 additions & 4 deletions apps/evm/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -409,12 +409,8 @@ github.com/ethereum/go-ethereum v1.16.8 h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9i
github.com/ethereum/go-ethereum v1.16.8/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
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 v1.0.0-rc.1 h1:MO7DT3y1X4WK7pTgl/867NroqhXJ/oe2NbmvMr3jqq8=
github.com/evstack/ev-node v1.0.0-rc.1/go.mod h1:JtbvY2r6k6ZhGYMeDNZk7cx6ALj3d0f6dVyyJmJHBd4=
github.com/evstack/ev-node/core v1.0.0-rc.1 h1:Dic2PMUMAYUl5JW6DkDj6HXDEWYzorVJQuuUJOV0FjE=
github.com/evstack/ev-node/core v1.0.0-rc.1/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY=
github.com/evstack/ev-node/execution/evm v1.0.0-rc.1 h1:CrjlRI6hufue3KozvDuKP14gLwFvnOmXfGEJIszGEcQ=
github.com/evstack/ev-node/execution/evm v1.0.0-rc.1/go.mod h1:GUxGZgS9F4w6DOcS5gEdW1h71IdAGdaY8C1urSOkpUQ=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
Expand Down
5 changes: 5 additions & 0 deletions apps/grpc/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ module github.com/evstack/ev-node/apps/grpc

go 1.25.0

replace (
github.com/evstack/ev-node => ../../
github.com/evstack/ev-node/execution/grpc => ../../execution/grpc
)

require (
github.com/evstack/ev-node v1.0.0-rc.1
github.com/evstack/ev-node/core v1.0.0-rc.1
Expand Down
4 changes: 0 additions & 4 deletions apps/grpc/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6Ni
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/evstack/ev-node v1.0.0-rc.1 h1:MO7DT3y1X4WK7pTgl/867NroqhXJ/oe2NbmvMr3jqq8=
github.com/evstack/ev-node v1.0.0-rc.1/go.mod h1:JtbvY2r6k6ZhGYMeDNZk7cx6ALj3d0f6dVyyJmJHBd4=
github.com/evstack/ev-node/core v1.0.0-rc.1 h1:Dic2PMUMAYUl5JW6DkDj6HXDEWYzorVJQuuUJOV0FjE=
github.com/evstack/ev-node/core v1.0.0-rc.1/go.mod h1:n2w/LhYQTPsi48m6lMj16YiIqsaQw6gxwjyJvR+B3sY=
github.com/evstack/ev-node/execution/grpc v1.0.0-rc.1 h1:OzrWLDDY6/9+LWx0XmUqPzxs/CHZRJICOwQ0Me/i6dY=
github.com/evstack/ev-node/execution/grpc v1.0.0-rc.1/go.mod h1:Pr/sF6Zx8am9ZeWFcoz1jYPs0kXmf+OmL8Tz2Gyq7E4=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
Expand Down
9 changes: 0 additions & 9 deletions block/internal/executing/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -809,15 +809,6 @@ func (e *Executor) recordBlockMetrics(newState types.State, data *types.Data) {
e.metrics.CommittedHeight.Set(float64(data.Metadata.Height))
}

// IsSynced checks if the last block height in the stored state matches the expected height and returns true if they are equal.
func (e *Executor) IsSynced(expHeight uint64) bool {
state, err := e.store.GetState(e.ctx)
if err != nil {
return false
}
return state.LastBlockHeight == expHeight
}

// IsSyncedWithRaft checks if the local state is synced with the given raft state, including hash check.
func (e *Executor) IsSyncedWithRaft(raftState *raft.RaftBlockState) (int, error) {
state, err := e.store.GetState(e.ctx)
Expand Down
297 changes: 297 additions & 0 deletions execution/evm/filter_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
package evm

import (
"context"
"crypto/rand"
"fmt"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
ds "github.com/ipfs/go-datastore"
dssync "github.com/ipfs/go-datastore/sync"
)

const (
benchPrivateKey = "cece4f25ac74deb1468965160c7185e07dff413f23fcadb611b05ca37ab0a52e"
benchToAddress = "0x944fDcD1c868E3cC566C78023CcB38A32cDA836E"
benchChainID = "1234"
)

// createBenchClient creates a minimal EngineClient for benchmarking FilterTxs.
// It only needs the logger to be set for the FilterTxs method.
func createBenchClient(b *testing.B) *EngineClient {
b.Helper()
baseStore := dssync.MutexWrap(ds.NewMapDatastore())
store := NewEVMStore(baseStore)
client := &EngineClient{
store: store,
}
return client
}

// generateSignedTransaction creates a valid signed Ethereum transaction for benchmarking.
func generateSignedTransaction(b *testing.B, nonce uint64, gasLimit uint64) []byte {
b.Helper()

privateKey, err := crypto.HexToECDSA(benchPrivateKey)
if err != nil {
b.Fatalf("failed to parse private key: %v", err)
}

chainID, ok := new(big.Int).SetString(benchChainID, 10)
if !ok {
b.Fatalf("failed to parse chain ID")
}

toAddress := common.HexToAddress(benchToAddress)
txValue := big.NewInt(1000000000000000000)
gasPrice := big.NewInt(30000000000)

data := make([]byte, 16)
if _, err := rand.Read(data); err != nil {
b.Fatalf("failed to generate random data: %v", err)
}

tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &toAddress,
Value: txValue,
Gas: gasLimit,
GasPrice: gasPrice,
Data: data,
})

signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
b.Fatalf("failed to sign transaction: %v", err)
}

txBytes, err := signedTx.MarshalBinary()
if err != nil {
b.Fatalf("failed to marshal transaction: %v", err)
}

return txBytes
}

// generateTransactionBatch creates a batch of signed transactions for benchmarking.
func generateTransactionBatch(b *testing.B, count int, gasLimit uint64) [][]byte {
b.Helper()
txs := make([][]byte, count)
for i := 0; i < count; i++ {
txs[i] = generateSignedTransaction(b, uint64(i), gasLimit)
}
return txs
}

// generateMixedTransactionBatch creates a batch with some valid and some invalid transactions.
// forcedRatio is the percentage of transactions that are "forced" (could include invalid ones).
func generateMixedTransactionBatch(b *testing.B, count int, gasLimit uint64, includeInvalid bool) [][]byte {
b.Helper()
txs := make([][]byte, count)
for i := 0; i < count; i++ {
if includeInvalid && i%10 == 0 {
// Every 10th transaction is invalid (random garbage)
txs[i] = make([]byte, 100)
if _, err := rand.Read(txs[i]); err != nil {
b.Fatalf("failed to generate random data: %v", err)
}
} else {
txs[i] = generateSignedTransaction(b, uint64(i), gasLimit)
}
}
return txs
}

func benchName(n int) string {
if n >= 1000 {
return fmt.Sprintf("%dk", n/1000)
}
return fmt.Sprintf("%d", n)
}

// BenchmarkFilterTxs_OnlyNormalTxs benchmarks FilterTxs when hasForceIncludedTransaction is false.
// In this case, UnmarshalBinary is NOT called - mempool transactions are already validated.
func BenchmarkFilterTxs_OnlyNormalTxs(b *testing.B) {
client := createBenchClient(b)
ctx := context.Background()

txCounts := []int{100, 1000, 10000}

for _, count := range txCounts {
b.Run(benchName(count), func(b *testing.B) {
txs := generateTransactionBatch(b, count, 21000)

b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
// hasForceIncludedTransaction=false means UnmarshalBinary is skipped
_, err := client.FilterTxs(ctx, txs, 0, 0, false)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})
}
}

// BenchmarkFilterTxs_WithForcedTxs benchmarks FilterTxs when hasForceIncludedTransaction is true.
// In this case, UnmarshalBinary IS called for every transaction to validate and extract gas.
func BenchmarkFilterTxs_WithForcedTxs(b *testing.B) {
client := createBenchClient(b)
ctx := context.Background()

txCounts := []int{100, 1000, 10000}

for _, count := range txCounts {
b.Run(benchName(count), func(b *testing.B) {
txs := generateTransactionBatch(b, count, 21000)

b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
// hasForceIncludedTransaction=true triggers UnmarshalBinary path
_, err := client.FilterTxs(ctx, txs, 0, 0, true)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})
}
}

// BenchmarkFilterTxs_MixedWithInvalidTxs benchmarks FilterTxs with a mix of valid and invalid transactions.
// This tests the UnmarshalBinary error handling path.
func BenchmarkFilterTxs_MixedWithInvalidTxs(b *testing.B) {
client := createBenchClient(b)
ctx := context.Background()

txCounts := []int{100, 1000, 10000}

for _, count := range txCounts {
b.Run(benchName(count), func(b *testing.B) {
// Generate batch with 10% invalid transactions
txs := generateMixedTransactionBatch(b, count, 21000, true)

b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
// hasForceIncludedTransaction=true triggers UnmarshalBinary path
_, err := client.FilterTxs(ctx, txs, 0, 0, true)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})
}
}

// BenchmarkFilterTxs_WithGasLimit benchmarks FilterTxs with gas limit enforcement.
// This tests the full path including gas accumulation logic.
func BenchmarkFilterTxs_WithGasLimit(b *testing.B) {
client := createBenchClient(b)
ctx := context.Background()

txCounts := []int{100, 1000}
// Gas limit that allows roughly half the transactions
maxGas := uint64(21000 * 500)

for _, count := range txCounts {
b.Run(benchName(count), func(b *testing.B) {
txs := generateTransactionBatch(b, count, 21000)

b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
// With gas limit and forced transactions
_, err := client.FilterTxs(ctx, txs, 0, maxGas, true)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})
}
}

// BenchmarkFilterTxs_WithSizeLimit benchmarks FilterTxs with byte size limit enforcement.
func BenchmarkFilterTxs_WithSizeLimit(b *testing.B) {
client := createBenchClient(b)
ctx := context.Background()

txCounts := []int{100, 1000}
// Size limit that allows roughly half the transactions (~110 bytes per tx)
maxBytes := uint64(110 * 500)

for _, count := range txCounts {
b.Run(benchName(count)+"_noForced", func(b *testing.B) {
txs := generateTransactionBatch(b, count, 21000)

b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
// Without forced transactions - UnmarshalBinary skipped
_, err := client.FilterTxs(ctx, txs, maxBytes, 0, false)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})

b.Run(benchName(count)+"_withForced", func(b *testing.B) {
txs := generateTransactionBatch(b, count, 21000)

b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
// With forced transactions - UnmarshalBinary called
_, err := client.FilterTxs(ctx, txs, maxBytes, 0, true)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})
}
}

// BenchmarkFilterTxs_CompareUnmarshalOverhead directly compares the overhead of UnmarshalBinary.
// Runs the same transaction set with and without the UnmarshalBinary path.
func BenchmarkFilterTxs_CompareUnmarshalOverhead(b *testing.B) {
client := createBenchClient(b)
ctx := context.Background()

count := 1000
txs := generateTransactionBatch(b, count, 21000)

b.Run("without_unmarshal", func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
_, err := client.FilterTxs(ctx, txs, 0, 0, false)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})

b.Run("with_unmarshal", func(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()

for b.Loop() {
_, err := client.FilterTxs(ctx, txs, 0, 0, true)
if err != nil {
b.Fatalf("FilterTxs failed: %v", err)
}
}
})
}
8 changes: 3 additions & 5 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,11 +363,9 @@ func (c *Config) Validate() error {
}

if len(c.DA.GetForcedInclusionNamespace()) > 0 {
// if err := validateNamespace(c.DA.GetForcedInclusionNamespace()); err != nil {
// return fmt.Errorf("could not validate forced inclusion namespace (%s): %w", c.DA.GetForcedInclusionNamespace(), err)
// }
return fmt.Errorf("forced inclusion is not yet live")

if err := validateNamespace(c.DA.GetForcedInclusionNamespace()); err != nil {
return fmt.Errorf("could not validate forced inclusion namespace (%s): %w", c.DA.GetForcedInclusionNamespace(), err)
}
}

// Validate lazy mode configuration
Expand Down
Loading
Loading