The executor asks cevm nothing on a CPU lane, and Health tells an estimate apart

cevm's Go entry runs no block on a CPU lane: it passes no host, and a CPU
lane runs no plain transfer without one. A library that answers there
anyway answers a gas estimate, the tx's limit, which a transfer limited to
21000 passes off as a result; the executor now declines the block on a CPU
lane without asking. The Health probe's limit is 30000, so a lane that
answers the limit instead of running the transfer is not reported healthy.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-09-23 09:52:43 -07:00
co-authored by Hanzo Dev
parent 10522343a0
commit 596e18dfe1
3 changed files with 35 additions and 2 deletions
+3 -2
View File
@@ -330,7 +330,8 @@ type healthProbe struct {
// code, and the CPU lanes run nothing without a host. So the battery is one
// such block — a funded sender's transfer to a fresh address — which a lane
// that can run anything runs to TxOK at its 21000 intrinsic gas, and which
// every other lane declines.
// every other lane declines. Its limit is above that, so a lane that answers
// a gas estimate (the limit) instead of running it is not taken for healthy.
func healthBattery() []healthProbe {
from, to, coinbase := [20]byte{0x01}, [20]byte{0x02}, [20]byte{0xC0}
row := func(addr [20]byte, balance uint64) StateAccount {
@@ -343,7 +344,7 @@ func healthBattery() []healthProbe {
}
return []healthProbe{{
name: "transfer",
txs: []Transaction{{From: from, To: to, HasTo: true, GasLimit: 21000, Value: 1, GasPrice: 1}},
txs: []Transaction{{From: from, To: to, HasTo: true, GasLimit: 30000, Value: 1, GasPrice: 1}},
ctx: BlockContext{GasLimit: 30_000_000, ChainID: 1, BaseFee: 1, Coinbase: coinbase},
state: []StateAccount{row(from, 1_000_000), row(to, 0), row(coinbase, 0)},
wantGas: 21000,
+25
View File
@@ -83,6 +83,7 @@ func TestABlockCevmDeclinesRunsOnTheGoEVM(t *testing.T) {
func TestACevmFailureThatIsNotADeclineIsAnError(t *testing.T) {
broken := errors.New("cevm: ABI version mismatch in result")
e := &Executor{
CevmBackend: cevm.GPUMetal,
execute: func(cevm.Backend, uint32, []cevm.Transaction, *cevm.BlockContext, []cevm.StateAccount) (*cevm.BlockResult, error) {
return nil, broken
},
@@ -101,6 +102,7 @@ func TestACevmFailureThatIsNotADeclineIsAnError(t *testing.T) {
func TestABlockCevmRanIsReceipted(t *testing.T) {
txs := types.Transactions{transfer(t, 0, big.NewInt(1)), transfer(t, 1, big.NewInt(1))}
e := &Executor{
CevmBackend: cevm.GPUMetal,
execute: func(cevm.Backend, uint32, []cevm.Transaction, *cevm.BlockContext, []cevm.StateAccount) (*cevm.BlockResult, error) {
return &cevm.BlockResult{
GasUsed: []uint64{21000, 21000},
@@ -177,6 +179,28 @@ func TestWhatDoesNotFitTheWireNeverReachesCevm(t *testing.T) {
}
}
// A CPU lane runs nothing through cevm's Go entry, so the block goes to the Go
// EVM without asking cevm — a library that answered there would answer a gas
// estimate.
func TestACPULaneDeclinesWithoutAskingCevm(t *testing.T) {
for _, b := range []cevm.Backend{cevm.CPUSequential, cevm.CPUParallel} {
t.Run(b.String(), func(t *testing.T) {
e := &Executor{
CevmBackend: b,
execute: func(cevm.Backend, uint32, []cevm.Transaction, *cevm.BlockContext, []cevm.StateAccount) (*cevm.BlockResult, error) {
t.Fatal("cevm was asked to run a block on a CPU lane")
return nil, nil
},
}
txs := types.Transactions{transfer(t, 0, big.NewInt(1))}
receipts, err := e.run(chainConfig(), newHeader(), txs, senders(len(txs)), newState(t))
if err != nil || receipts != nil {
t.Fatalf("run = (%v, %v), want (nil, nil)", receipts, err)
}
})
}
}
// -----------------------------------------------------------------------------
// A build with no C++ EVM
// -----------------------------------------------------------------------------
@@ -602,6 +626,7 @@ func TestABlockBeyondValueTransferIsDeclinedBeforeCevm(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
e := &Executor{
CevmBackend: cevm.GPUMetal,
execute: func(cevm.Backend, uint32, []cevm.Transaction, *cevm.BlockContext, []cevm.StateAccount) (*cevm.BlockResult, error) {
t.Fatal("cevm was asked to run a block it could not receipt")
return nil, nil
+7
View File
@@ -162,6 +162,13 @@ func (e *Executor) run(
senders []common.Address,
statedb *state.StateDB,
) ([]*types.Receipt, error) {
// cevm's Go entry runs nothing on a CPU lane: it passes no host, and a
// CPU lane runs no plain transfer without one (go_bridge.h, ABI 6). A
// library that answers there anyway answers something else — a gas
// estimate — so it is not asked.
if e.CevmBackend != cevm.GPUMetal && e.CevmBackend != cevm.GPUCUDA {
return declineBlock("cpu_lane", header.Number.Uint64(), 0)
}
cevmTxs, i := shape(txs, senders, statedb)
if i < len(txs) {
return declineBlock("tx_not_representable", header.Number.Uint64(), uint64(i))