cevm

package
v1.7.38 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

README

cevm — C++ EVM execution backend

The CGO bridge to the C++ EVM in ~/work/luxcpp/cevm. Selected at link time via build tags from chains/evm/backend_{cgo,nocgo}.go.

This is not a standalone VM. It is the GPU-accelerated execution backend for C-Chain when built with LUX_CGO=1. The pure-Go fallback (luxfi/geth) is used when LUX_CGO=0.

Layout

chains/evm/
├── main.go                 # luxd plugin entry
├── backend_cgo.go          # cgo backend selector (LUX_CGO=1)
├── backend_nocgo.go        # pure-Go selector (LUX_CGO=0)
└── cevm/                   # this package — C++ FFI shim
    ├── cevm.go             # Go API
    ├── cevm_cgo.go         # cgo bindings (linked against luxcpp libs)
    ├── cevm_nocgo.go       # no-op stubs for !cgo builds
    └── plugin.go           # plugin registration glue

Linking

cevm_cgo.go references ${SRCDIR}/../../../../luxcpp/... for headers and libs. From ~/work/lux/chains/evm/cevm/ that resolves to ~/work/luxcpp/. For module-cache builds, see accel's fetch-luxcpp.sh pattern (TODO: port the same approach here).

Performance

When CGO is enabled and luxcpp libs are available:

  • ~3–5× the throughput of pure-Go EVM
  • SIMD opcode dispatch (AVX2/NEON)
  • GPU batch operations via Block-STM (CUDA/Metal)

Provenance

Folded into chains/evm from the previously-standalone luxfi/cevm repo on 2026-04-30. cevm is internal-only — no operator daemon. C-Chain runs in luxd. See ~/work/lux/chains/PLUGGABLE.md for the canonicalization pattern that governs the rest of the chain VMs.

Documentation

Overview

Package cevm provides Go bindings to the C++ EVM (cevm) with GPU acceleration. Import this package to use the C++ EVM as a drop-in replacement for go-ethereum's EVM.

The C++ EVM supports:

  • Block-STM parallel execution
  • GPU Keccak-256 state hashing (Metal/CUDA)
  • GPU batch ecrecover (Metal/CUDA)
  • GPU EVM opcode interpreter (Metal/CUDA)
  • ZAP VM plugin protocol (native)

Build against the native library: CGO_ENABLED=1 go build -tags lux_cevm_native Build without it: go build (types only, no execution) — the default, and the only thing that works on a host lacking the lux-cevm bundle. Binary: the `cevm` binary in luxcpp/evm/build/bin/ is the Lux VM plugin.

Concurrency model

ExecuteBlock and ExecuteBlock are safe to call concurrently from multiple goroutines. The implementation guarantees:

  1. No shared mutable state on the Go side. Every call allocates a fresh []C.CGpuTx for its inputs and a fresh runtime.Pinner for its lifetime. The pinner pins the base address of every Go-owned []byte (tx.Data, tx.Code) that the C side dereferences, and is unpinned via defer after the C call returns — including on the error path.

  2. The C result is freed via defer (gpu_free_result / gpu_free_result_v2) on every code path including failure. Gas/status arrays are copied into Go-owned slices before the result is freed.

  3. The C++ engine uses a thread_local engine cache (one per OS thread reached by goroutines via cgo) for the Keccak hasher; per-instance MTLBuffer / CUDA context caches are mutex-protected on the C++ side. Two goroutines on different OS threads use independent kernel state.

  4. The CPU path is fully reentrant: each call constructs a fresh cevm state and tears it down before returning.

What is NOT safe:

  • Mutating the Transaction.Data or Transaction.Code slices while a concurrent ExecuteBlock call is reading them. The pinner only prevents GC moves; it does not provide read/write synchronization.
  • Sharing a *BlockResult between goroutines without external sync.

ABI version

The Go module's ABIVersion constant is checked against the loaded library's gpu_abi_version() in init(). A mismatch panics at process start — that is intentional. A silent ABI mismatch produces wrong gas/state results and would corrupt consensus, so fail-fast is the only safe behaviour.

Use Health() at startup to additionally verify each backend executes the canonical health-check battery (arithmetic, storage, hashing, memory, and the call bridge) without error.

Index

Constants

View Source
const ABIVersion uint32 = 0

No library linked, so there is no ABI to report.

Variables

View Source
var ErrNotLinked = errors.New("cevm: native EVM not linked (rebuild with CGO_ENABLED=1 -tags=lux_cevm_native)")

ErrNotLinked is what every entry point that needs the native luxcpp library returns when this binary was built without it — the default build, and any build on a host lacking the lux-cevm bundle.

It is a value rather than a message because a caller has to tell it apart from a bad input, and the two lead to opposite decisions: a build that cannot execute declines the block to whatever else can, while a block that cannot be executed is a hard failure. A caller that could only read the message either treated a missing library as a corrupt block, or — worse — fell back on a signature the library had rejected.

Functions

func BackendName

func BackendName(b Backend) string

BackendName uses the local Go-side string; there is no library to ask.

func BatchRecoverSenders

func BatchRecoverSenders(txs types.Transactions, signer types.Signer) ([]common.Address, error)

BatchRecoverSenders returns an error under !cgo: the GPU sig-batch primitive lives in the C++ luxcpp/crypto library and there is no Go equivalent in this package. Callers should fall back to per-tx types.Sender when this returns an error.

func LibraryABIVersion

func LibraryABIVersion() uint32

LibraryABIVersion returns the Go-side constant when there's no library.

Types

type Backend

type Backend int
const (
	// CPUSequential runs transactions one at a time on a single core.
	CPUSequential Backend = 0
	// CPUParallel uses Block-STM to run transactions across all cores.
	CPUParallel Backend = 1
	// GPUMetal offloads Keccak, ecrecover, and the EVM interpreter to Metal.
	GPUMetal Backend = 2
	// GPUCUDA offloads Keccak, ecrecover, and the EVM interpreter to CUDA.
	GPUCUDA Backend = 3
)

func AutoDetect

func AutoDetect() Backend

AutoDetect returns CPUSequential: with no library there is no lane to detect.

func AvailableBackends

func AvailableBackends() []Backend

AvailableBackends returns CPUSequential alone — the only lane a build with no library has, and it cannot execute either.

func (Backend) String

func (b Backend) String() string

String returns the human-readable name of the backend.

type BlockContext

type BlockContext struct {
	Origin        [20]byte
	GasPrice      uint64
	Timestamp     uint64
	Number        uint64
	Prevrandao    [32]byte
	GasLimit      uint64
	ChainID       uint64
	BaseFee       uint64
	BlobBaseFee   uint64
	Coinbase      [20]byte
	BlobHashes    [8][32]byte
	NumBlobHashes uint32
}

BlockContext is the block-level execution context shared by every transaction in a block. It feeds the EVM opcodes that report block-level state: TIMESTAMP, NUMBER, CHAINID, BASEFEE, COINBASE, GASLIMIT, PREVRANDAO, BLOBHASH, BLOBBASEFEE.

Pass a non-nil *BlockContext to ExecuteBlock when the call must mirror real chain semantics (consensus, replay, fork-aware execution). The zero-value is the documented "no context" default — chain id resolves to 0, timestamp to 0, etc., which matches the dispatcher's pre-v0.26 behaviour.

Field layout matches the C-side CBlockContext byte-for-byte: this struct is passed to the C ABI via direct memcpy, no field-by-field translation. Field order MUST match go_bridge.h CBlockContext exactly. Adding new fields requires bumping ABIVersion and the C-side EVM_GPU_ABI_VERSION in lockstep.

type BlockResult

type BlockResult struct {
	StateRoot    [32]byte
	GasUsed      []uint64
	Status       []TxStatus
	TotalGas     uint64
	ExecTimeMs   float64
	Conflicts    uint32
	ReExecutions uint32
	ABIVersion   uint32
}

Backend selects the C++ EVM execution mode. BlockResult extends BlockResult with the V2 ABI fields: per-tx status and the post-execution state root.

func ExecuteBlock

func ExecuteBlock(backend Backend, numThreads uint32, txs []Transaction, ctx *BlockContext, state []StateAccount) (*BlockResult, error)

ExecuteBlock returns an error when built without CGo. Mirrors the V4 cgo signature so consumers can call it unconditionally.

type HealthProbeResult

type HealthProbeResult struct {
	Name    string
	OK      bool
	GasUsed uint64
	Status  TxStatus
	Err     error
}

HealthProbeResult mirrors the cgo build's struct so consumers see the same API surface either way. Under nocgo the slice is always empty.

type HealthReport

type HealthReport struct {
	Backend      Backend
	Name         string
	OK           bool
	Err          error
	Probe        string
	ProbesRun    int
	ProbeResults []HealthProbeResult
	GasUsed      uint64
	Status       TxStatus
	ExecTime     float64
}

HealthReport is the per-backend result of Health(). The nocgo build only reports CPUSequential and never executes — it returns OK=false with an explanatory error and an empty ProbeResults slice.

func Health

func Health() []HealthReport

Health returns a single non-OK report naming the reason nothing can execute.

The reason used to read "built without CGo", which is wrong on the default build: cgo is on, and what is missing is the lux_cevm_native tag and the luxcpp bundle behind it. An operator reading it went and checked CGO_ENABLED, which was already 1.

type StateAccount

type StateAccount struct {
	Address  [20]byte
	Nonce    uint64
	Balance  [4]uint64
	Code     []byte
	CodeHash [32]byte
}

StateAccount is one entry in the snapshot of touched accounts handed to ExecuteBlock. Fields mirror the C-side CGpuStateAccount byte-for-byte (modulo the inline `Code` slice which the binding flattens into a single blob before crossing the cgo boundary).

Address is canonical 20-byte big-endian. Balance is little-endian limbs (Balance[0] = low 64 bits). Code may be nil for EOAs — empty code is the EOA marker. CodeHash should be keccak256(code); the dispatcher does not recompute it because callers usually have it cached on the StateDB side.

type Transaction

type Transaction struct {
	From     [20]byte
	To       [20]byte
	HasTo    bool
	Data     []byte // Calldata
	Code     []byte // EVM bytecode (optional — required for real GPU execution)
	GasLimit uint64
	Value    uint64
	Nonce    uint64
	GasPrice uint64
}

Transaction is a single EVM transaction to execute.

When Code is non-empty AND a GPU backend is selected, the C++ EVM dispatches each tx through the parallel opcode interpreter (Metal: kernel::EvmKernelHost, CUDA: cuda::EvmKernel). When Code is empty, GPU backends use the scheduler-only Block-STM kernel.

type TxStatus

type TxStatus uint8

TxStatus is a per-transaction execution outcome from the V2 ABI.

const (
	TxOK               TxStatus = 0 // STOP / clean exit
	TxReturn           TxStatus = 1
	TxRevert           TxStatus = 2
	TxOOG              TxStatus = 3
	TxError            TxStatus = 4
	TxCallNotSupported TxStatus = 5
)

func (TxStatus) String

func (s TxStatus) String() string

String returns a short label for the tx status.

Directories

Path Synopsis
Package parallel — cevm BlockExecutor for github.com/luxfi/evm.
Package parallel — cevm BlockExecutor for github.com/luxfi/evm.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL