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:
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.
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.
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.
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
- func BackendName(b Backend) string
- func BatchRecoverSenders(txs types.Transactions, signer types.Signer) ([]common.Address, error)
- func LibraryABIVersion() uint32
- func PluginExists() bool
- func PluginPath() string
- func VMID() string
- type Backend
- type BlockContext
- type BlockResult
- type HealthProbeResult
- type HealthReport
- type StateAccount
- type Transaction
- type TxStatus
Constants ¶
const ABIVersion uint32 = 0
No library linked, so there is no ABI to report.
Variables ¶
This section is empty.
Functions ¶
func BackendName ¶
BackendName uses the local Go-side string when CGo is off.
func BatchRecoverSenders ¶
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.
func PluginExists ¶
func PluginExists() bool
PluginExists reports whether the cevm plugin binary is present on disk.
func PluginPath ¶
func PluginPath() string
PluginPath returns the absolute path to the cevm VM plugin binary. Used by lux CLI and universe Makefiles to locate the built plugin.
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 when built without CGo.
func AvailableBackends ¶
func AvailableBackends() []Backend
AvailableBackends returns CPUSequential only when built without CGo.
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 ¶
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 indicating CGo is disabled.
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.