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 with CGo: CGO_ENABLED=1 go build -tags cgo Build without CGo: CGO_ENABLED=0 go build (types only, no execution) 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(C.EVM_GPU_ABI_VERSION)
init validates the ABI version of the loaded shared library against the Go module's expected ABIVersion. A mismatch means the binary and the cevm module were built against incompatible C++ headers and any execution would produce silently wrong results — fail fast at process start instead. ABIVersion is the ABI this build was compiled against, taken from the library's own header. There is no second copy to keep in sync.
Variables ¶
This section is empty.
Functions ¶
func BackendName ¶
BackendName returns the human-readable name of a backend as reported by the C++ library (which is authoritative).
func BatchRecoverSenders ¶
BatchRecoverSenders recovers the sender address for every transaction in the input slice in a single cgo dispatch into the luxcpp/crypto first-party secp256k1 ecrecover pipeline.
The output slice is the same length as txs and indexed positionally — i.e. out[i] is the sender of txs[i]. Per-tx behaviour matches types.Sender byte-for-byte; that contract is enforced by the parity test in cevm_secp256k1_parity_test.go.
On any per-tx recovery failure the call returns an error naming the offending tx index. This matches the existing parallel.Executor behaviour (Stage 1 in parallel/parallel.go) which errors on the first failed sender rather than mixing successful and failed senders mid-block.
The recovered addresses are written into each tx's sigCache via types.CacheSender, so subsequent calls to types.Sender(signer, tx) return the cached value and skip recomputation.
func LibraryABIVersion ¶
func LibraryABIVersion() uint32
LibraryABIVersion returns the ABI version reported by the loaded library. Useful for diagnostics when binaries and shared libs may drift.
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 the best available backend for this machine.
func AvailableBackends ¶
func AvailableBackends() []Backend
AvailableBackends returns the list of backends compiled and detected at runtime by the loaded library.
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 runs txs against backend and returns the block result. A nil ctx leaves the C side on its own block defaults; nil state means no prestate is seeded. numThreads is passed through to the C executor.
type HealthProbeResult ¶
HealthProbeResult is the outcome of a single probe on a single backend.
type HealthReport ¶
type HealthReport struct {
Backend Backend
Name string
OK bool
Err error
Probe string // first failing probe name, empty when OK
ProbesRun int
ProbeResults []HealthProbeResult
// Aggregate stats — sum of gas across probes, time of the last probe.
GasUsed uint64
Status TxStatus
ExecTime float64
}
HealthReport is the per-backend result of Health(). It aggregates the per-probe results into a single OK / not-OK signal: a backend is healthy iff every probe ran to its expected status with non-zero gas.
func Health ¶
func Health() []HealthReport
Health runs a battery of canonical bytecode programs through every backend the loaded library exposes and returns a per-backend report. Use at process start to fail-fast on misconfigured GPUs (driver missing, library mismatch, device permissions, kernel coverage gaps). Returns nil only if the runtime cannot enumerate backends at all.
The battery covers:
- arithmetic (ADD/POP) — strict gas parity required across backends
- storage (SSTORE / SLOAD) — strict gas parity required
- hashing (KECCAK256) — non-zero gas required, parity not strict
- memory ops (MSTORE / MLOAD / MCOPY) — non-zero gas required, parity not strict
- the CALL bridge (CALL with a constant target) — must complete cleanly
A backend is reported OK iff every probe executed to its expected status with non-zero gas AND its gas matches every other backend on the strict- parity probes. A failure sets Err and Probe to identify the offending case.
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.