ethertest

package module
v0.0.0-...-c4964f1 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 85 Imported by: 0

README

ethertest

ethertest is an embeddable Go local Ethereum test node with execution JSON-RPC over HTTP, WebSocket, in-process, or IPC transports and a synthetic Beacon REST/SSE surface. It targets DApps, wallets, explorers, indexers, and other off-chain applications that need realistic cross-layer behavior without P2P or a validator client.

The current version is 0.1.0-alpha.1. State format compatibility is not promised until v0.1.0. This tree uses metadata schema v3 and ethertest-state-v3 archives. Schema-v1/v2 databases and v1/v2 archives are rejected with an explicit rebuild error; there is no migration command. An unspecified genesis_time (0) is resolved once for a new generated chain and then read from the persisted timeline on later Pebble starts. An explicitly supplied value that differs from the stored value is rejected.

Quick start

go build -trimpath -o bin/ethertest ./cmd/ethertest
./bin/ethertest

Defaults:

  • EL HTTP+WS and Beacon REST+SSE: http://127.0.0.1:8545
  • IPC: disabled; when enabled its default name is ethertest.ipc
  • Beacon API paths start at http://127.0.0.1:8545/eth/
  • chain/network ID: 1337 (matching geth --dev; network_id = 0 inherits the chain ID)
  • 10 Anvil-compatible accounts with 10,000 ETH each in the generated default genesis
  • 6-second slots, 8 slots/epoch, 64 deterministic validators
  • Amsterdam/Gloas active at genesis
  • synthetic safe/finalized checkpoints lagging one/two epochs
  • consensusMode: synthetic, Beacon API gloas-subset, fullConsensus: false

In human mode, a separate stderr banner prints development private keys. Keys are never included in structured logs, errors, metrics, or state archives.

Docker Compose

The included Compose example builds the cgo-enabled, nonroot image, publishes the shared API listener to host loopback only, and persists Pebble plus the graceful-shutdown state archive:

docker compose up --build
docker compose down

Execution RPC is available at http://127.0.0.1:8545 and Beacon REST/SSE uses the same listener, for example http://127.0.0.1:8545/eth/v1/beacon/headers/head. Fork-dependent blocks use /eth/v2/beacon/blocks/{block_id} and Fulu/Gloas data columns use /eth/v1/debug/beacon/data_column_sidecars/{block_id}. docker compose down sends SIGTERM and allows the node to write /state/ethertest-state.tar.zst before exit. Use docker compose down -v only when intentionally deleting both named volumes.

Implemented alpha surface

The library exposes Config, Node, in-process RPC clients, endpoint discovery, transaction submission, manual mining, missed slots, snapshots, persistent repeatable checkpoints, persistent explicit branches, canonical switching, bounded persistent event replay, synthetic finality pause/resume controls, next-block withdrawal injection, native Electra execution-request projection, persistent execution-request controls, safety queries, and atomic state archives. Clean histories can be replayed by geth's execution state processor. State control methods deliberately create unsafe fixtures; the control block, every descendant, its branches, and the containing session/archive remain tainted.

The network surface currently includes:

  • Core eth, net, web3, txpool, miner, personal, and debug methods.
  • An in-memory wallet for configured and runtime-imported signers, including eth_signTypedData_v4, standalone EIP-7702 authorization signing, and ethertest_importAccount/ethertest_removeAccount.
  • EIP-1186 proofs, state overrides, polling filters, newHeads/logs/newPendingTransactions subscriptions, HTTP/WS/IPC batch, struct logging, debug_traceBlockByHash, debug_traceBlockByNumber, and native Go tracers. JavaScript tracers are rejected.
  • One immutable pending candidate view shared by pending block/state/call/proof queries, with deterministic executable/queued classification.
  • Node.AddWithdrawal and ethertest_addWithdrawal for adding up to 16 automatically indexed EIP-4895 withdrawals to the next canonical block.
  • Native EIP-6110/7002/7251 execution requests from geth plus persistent Node.Add*Request and ethertest_add*Request control FIFOs. The same typed bytes back EL requestsHash, Beacon JSON/SSZ, restart, reorg, and archive behavior.
  • Type-3 raw submission with mandatory KZG validation, Deneb JSON/SSZ sidecars, Osaka cell proofs, packed-bytes-v1, stable blob retrieval, and Fulu data columns.
  • Beacon API Gloas subset (revision a3f0654) for genesis/config/health, signed headers and blocks, validators/balances, Deneb→Electra/Fulu→Gloas container transitions, synthetic finality, JSON/SSZ negotiation, required-topic standard SSE replay, and structured errors.
  • Node.PauseFinality, Node.ResumeFinality, Node.FinalityStatus, and matching ethertest_* RPC controls for persistent synthetic finality fixtures.
  • Memory and Pebble databases; recovery-journaled execution/auxiliary commits; streaming, checksum-verified, zstd-compressed state archives with atomic staging for both dump and load.
  • Node.SafetyStatus, Node.BlockSafety, ethertest_safetyStatus, and ethertest_blockSafety for permanent fixture taint discovery.
  • Offline locked EIP-4788 wraparound, KZG proof, and SSZ container regression vectors; their source revisions and digests are recorded in spec.lock.
Anvil development workflows

The selected Anvil v1.8.3 subset includes dedicated anvil_* / evm_* adapters, runtime mining and pool controls, read-only block overrides, and common CLI flags. This is partial compatibility: state setters still mine permanently tainted control blocks, timestamps remain slot-based, and finality is synthetic. The default chain ID remains 1337.

anvil_setBalance/setCode/setNonce now return null, anvil_setStorageAt returns true, anvil_mine returns null, and evm_mine returns "0x0". Use the corresponding ethertest_* methods to retain block-hash results; quantity-style evm_mine(count) callers must migrate to ethertest_mine(count). See the compatibility contracts, migration table, CLI flags, and differential checks. make test-anvil-compat uses the pinned reference and fails if it is missing; ordinary Go tests remain offline.

Execution APIs v1.0.0-beta.7

The standard RPC registration baseline is ethereum/execution-apis@v1.0.0-beta.7, commit 5aebdfdd45cadeb723be4bd45b4611b71c8b1c85. The locked offline classification in specs/upstream/execution-rpc-subset.json contains all 78 methods: 51 are implemented and 27 are deliberately unregistered. Existing web3, personal, miner, subscription, and ethertest/anvil/evm extensions remain available but are not counted in that baseline.

Surface Status
eth block, transaction, receipt, state, fee, filter, signing, config, capabilities, and eth_simulateV1 methods 41 implemented
debug_getRawHeader, debug_getRawBlock, debug_getRawReceipts, debug_getRawTransaction 4 implemented
net_version, txpool_status, txpool_content, txpool_contentFrom 4 implemented
All 25 engine_* methods Excluded until ethertest has a real authenticated EL/CL Engine API boundary
debug_getBadBlocks, testing_buildBlockV1 Excluded because v0.1 has no truthful sync bad-block pipeline or public upstream testing service
eth_getBlockAccessList, debug_getRawBlockAccessList Implemented for Amsterdam; earlier blocks return null

safe and finalized continue to be synthetic slot-derived tags. Configured development accounts and runtime-imported accounts are accepted by eth_sign, eth_signTransaction, eth_sendTransaction, and eth_signTypedData_v4; signatures and errors never expose their keys or mnemonic. These wallet methods are extensions and do not change the locked beta.7 method counts. Pending blocks and transactions encode unconfirmed inclusion fields as null. Polling filters and pending-transaction history are bounded in-memory state and are not restored after restart. eth_capabilities reports the actual archive/state window while block, transaction, log, and receipt history starts at genesis.

eth_simulateV1 is read-only and uses sequential state across simulated blocks. It supports block/state overrides, moved precompiles, gap-filled empty blocks, optional validation, full transaction results, and synthetic ETH transfer logs; omitted timestamps advance by the configured network slot duration. Its fixed v0.1 limits are 256 output blocks, 5,000 calls per block, 10,000 calls per request, 50,000,000 cumulative gas, and a five-second EVM timeout. Limit exhaustion returns -38026; timeout returns -32016.

Amsterdam and synthetic Gloas

geth v1.17.6 supplies Amsterdam execution rules, including SLOTNUM, the new gas schedule, block access lists (BAL), and native builder deposit/exit requests. Generated genesis includes geth's canonical system contracts and deterministic factory. Configure [chain.forks] amsterdam_epoch, ETHERTEST_AMSTERDAM_EPOCH, or --amsterdam-epoch: 0 activates at genesis (default), a positive epoch activates at its exact first slot, and -1 disables Amsterdam/Gloas. Activation cannot precede Osaka. Blob parameters remain fixed at this repository's preset.

eth_getBlockAccessList and debug_getRawBlockAccessList expose the same validated BAL committed by the execution header, including pending candidates. Gloas blocks use deterministic self-build bids; the current payload, all five request types, and BAL live in a separately signed envelope. Parent requests appear in the next Gloas block. Builder requests have no injection control API. GET /eth/v1/beacon/execution_payload_envelopes/{block_id} supports JSON and SSZ with Eth-Consensus-Version: gloas. Gloas data columns use slot/root and bid commitments; Fulu retains its earlier container. KZG verification is mandatory. SSE adds versioned head_v2 and execution_payload_available; legacy head keeps its existing response shape.

These objects are synthetic projections, not a full Gloas state transition or builder network. No Engine API, P2P, validator client, or external builder is provided. Consensus types follow consensus-specs v1.7.0-beta.2 and the pinned Beacon API revision in spec.lock. Independent Python SSZ reference vectors cover progressive containers/lists; full upstream consensus/execution suites remain release gates. Schema v3 stores block and envelope together in the recovery journal and validates their references, signatures, and BAL on startup. Existing v1/v2 state requires a fresh chain; no migration is provided.

Next-block withdrawals

ethertest_addWithdrawal accepts one object with validatorIndex, address, and nonzero amount fields. Integer fields are JSON-RPC quantities and amount is denominated in Gwei. The node assigns the globally monotonic withdrawal index, refreshes the pending block and state immediately, and returns true without triggering mining. The accepted withdrawals are consumed by the next canonical block, including an explicitly empty or control block. The in-memory queue is limited to 16 entries and is not restored by snapshots, checkpoints, archives, or process restart.

Electra execution requests

Every Prague-or-later block captures geth's native EIP-6110 deposit logs and EIP-7002/EIP-7251 system-contract queue output after its final transaction. Native typed bytes are validated strictly against the block requestsHash, then persisted with the Electra/Fulu projection or Gloas payload envelope. Amsterdam also captures native builder deposit and exit queues (types 3 and 4). Native-only blocks keep normal geth insertion and do not taint their history. The deposit log parser uses geth's configured DepositContractAddress; contract code at any other address is not treated as EIP-6110 output. With an imported execution genesis, capture uses that file's exact state and ChainConfig after the cross-layer compatibility checks described below.

The Go controls Node.AddDepositRequest, Node.AddWithdrawalRequest, and Node.AddConsolidationRequest have matching RPCs:

  • ethertest_addDepositRequest({pubkey, withdrawalCredentials, amount, signature, index})
  • ethertest_addWithdrawalRequest({sourceAddress, validatorPubkey, amount})
  • ethertest_addConsolidationRequest({sourceAddress, sourcePubkey, targetPubkey})

RPC integer fields are hex quantities. Fixed byte lengths are checked, while validator membership and BLS/BeaconState semantics are intentionally not claimed. The persistent FIFO limits are 8192 deposits, 16 withdrawals, and 2 consolidations. Enqueueing refreshes pending without mining; requests may wait before Prague, and interval mining advances until the first eligible block.

For each type, native entries precede control entries. Native output consumes the protocol capacity first, so controls remain queued when a block is full. Only canonical mining consumes controls; canonical rewinds restore consumed IDs in FIFO order and retain later pending additions. Native queue state follows geth's branch StateDB instead. Queue changes, native and complete typed block records, Beacon projection, slots, safety, and events share the prepared-operation recovery journal and survive Pebble restart and archive round trips.

Including any control entry changes requestsHash without applying the corresponding execution contract/log transition. Such a block and all of its descendants are therefore permanently unsafe with reason execution-request-control; Beacon optimistic flags, safety RPCs, and archive manifests expose that taint. No anvil_* or evm_* aliases are registered, and the existing EIP-4895 ethertest_addWithdrawal queue is independent.

This is a synthetic Beacon projection, not a consensus client: it does not implement BeaconState transitions, Casper FFG, fork choice, P2P, the Engine API, or standard CL block import.

Synthetic finality controls

Node.PauseFinality and ethertest_pauseFinality freeze the slot used to derive synthetic safe and finalized checkpoints. Blocks and missed slots continue advancing while paused. The frozen projection is shared by EL block tags, Beacon finality checkpoints and finalized response flags, branch protection, and finalized-checkpoint events.

Node.ResumeFinality and ethertest_resumeFinality immediately catch the projection up to the current slot. If the finalized checkpoint changed, one event for the latest checkpoint is published. Both operations are idempotent; repeated calls do not create events or revisions. Node.FinalityStatus and ethertest_finalityStatus report the current and projection slots plus the resolved safe/finalized slots, hashes, and block numbers.

The paused state survives Pebble restart and state archive dump/load. A snapshot revert, checkpoint restore, or branch switch does not resume finality; rewinding before the frozen slot lowers that slot to the new head. These methods are registered only in the ethertest namespace, not as anvil_* or evm_* aliases.

Runtime wallet accounts

Node.ImportAccount and ethertest_importAccount add an unlocked signer to the node's in-memory wallet. The RPC accepts a 0x-prefixed 32-byte secp256k1 private key and an optional absolute uint256 balance. Without a balance, import does not change the chain. With a balance, import creates the same permanently tainted unsafe control block as ethertest_setBalance and returns its hash as controlBlockHash.

Configured mnemonic accounts cannot be removed. Runtime accounts can be removed with Node.RemoveAccount or ethertest_removeAccount; removal only withdraws signing authority and leaves balance, nonce, code, and history untouched. Runtime membership is not restored by snapshots, checkpoints, branches, state archives, or process restart. A persisted balance control block survives normally even though its runtime signer does not.

eth_signTypedData_v4 accepts either an EIP-712 object or its JSON string. If domain.chainId is present it must match the node; an omitted chain ID is allowed. The RPC returns r || s || v with the legacy 27/28 recovery byte. There are no v3, legacy typed-data, password, lock/unlock, or encrypted-keystore variants in this wallet surface.

Node.SignAuthorization and ethertest_signAuthorization sign an exact EIP-7702 authorization tuple with a configured or runtime-imported account. The RPC takes the authority address followed by {chainId,address,nonce}, where address is the delegation target, and returns the standard chainId, address, nonce, yParity, r, and s object. The target may be zero to clear a delegation. The chain ID must match the node or be explicitly zero; zero is the replayable cross-chain value defined by EIP-7702.

The signer never derives a nonce or submits a transaction. With viem, use prepareAuthorization to resolve the chain ID, pending nonce, and executor: "self" adjustment, then pass that complete tuple to the custom RPC. Viem's standard signAuthorization action still rejects JSON-RPC accounts, so this method is an explicit ethertest extension rather than transparent wallet compatibility. Signing does not change execution state, the canonical head, events, revisions, snapshots, or archives. No eth_*, anvil_*, or evm_* alias is registered. ethertest_capabilities advertises authorizationSigning: true.

Not yet release-complete: unsafe header mutation sessions, complete RPC compatibility, generated full upstream API contracts, all official vector suites, encrypted secret packages, resource pruning modes, and release provenance/signing. These remain gates for v0.1.0; the current tree must not be tagged as that release.

CLI

ethertest [flags]
ethertest config print|validate
ethertest network --json
ethertest blob encode|decode|send
ethertest state inspect|dump|load
ethertest accounts export --unsafe-plain
ethertest capabilities
ethertest completion bash|zsh|fish

ethertest state dump requires an effective Pebble storage.path (or --data-dir); it never silently creates and exports a fresh in-memory chain. state load verifies and stages the complete v3 archive before replacing an empty destination.

Configuration precedence is defaults, strict TOML, ETHERTEST_*, then CLI. Unknown TOML keys and conflicting settings are rejected. Non-loopback listeners require explicit --allow-unsafe-external. TLS only uses user-provided certificate/key pairs and never modifies a trust store. Beacon exposes only an enabled setting and inherits the shared HTTP address, CORS, TLS, request limits, and unsafe-external policy. Disabling HTTP disables HTTP, WebSocket, Beacon REST, and Beacon SSE; IPC remains independently available. Library configurations cannot enable Beacon without HTTP.

Execution genesis import

An optional geth-compatible execution genesis.json can be selected with Config.Chain.GenesisFile, [chain] genesis, ETHERTEST_GENESIS, or --genesis. Relative paths are resolved from the process working directory. The selected file is authoritative for its execution ChainConfig, header fields, gas limit, timestamp, and complete alloc; ethertest does not merge the default account balances into it. Configured mnemonic accounts remain unlocked signers, but an address omitted from alloc starts unfunded.

Imported chains must be proof-of-stake, start at London/Shanghai/Cancun, schedule Prague, Osaka, and optional Amsterdam on exact ethertest epoch boundaries, retain the repository's pinned Cancun/Prague blob parameters, and leave later forks disabled. Missing amsterdamTime disables Amsterdam on imported chains. The fixed EIP-4788, EIP-2935, EIP-7002, and EIP-7251 system contract accounts must match geth's canonical initial code and state; Amsterdam chains must also include the canonical builder deposit/exit contracts and deterministic factory. Invalid files fail before the node starts and are never rewritten.

network_id = 0 inherits the effective execution chain ID; a nonzero network_id, ETHERTEST_NETWORK_ID, or --network-id explicitly overrides net_version. ethertest config validate parses and validates the genesis, while config print and network --json report the imported effective chain fields, gas limit, and fork epochs.

For Pebble, the imported marker and genesis hash are stored alongside the timeline. Later starts may omit the source file and recover the genesis header, alloc, and ChainConfig from the database. If a file is supplied again, both its genesis hash and decoded ChainConfig must exactly match the stored values. A configured but unreadable path is an error. State archives retain the same metadata, so archive loads also restart without the source file.

IPC is opt-in. Enable it with [ipc] enabled = true, ETHERTEST_IPC_ENABLED=true, or --ipc PATH; use --no-ipc to override an earlier configuration layer. ipc.path defaults to ethertest.ipc. On Unix, a simple name is placed under storage.path for Pebble nodes or the system temporary directory for in-memory nodes, while a path containing a directory is used exactly as supplied. Windows maps the configured name to a named pipe. Unix sockets are created with mode 0600. ethertest network --json reports the resolved endpoint in its ipc field. Startup removes only an unreachable stale Unix socket; regular files, symlinks, and live sockets are never replaced.

Resource limits

[limits] bounds transport bytes and expensive local-node work. Defaults are 16 MiB requests, 64 MiB responses, 1,000 batch items, 256 control operations, 10,000 log blocks/results, 50,000,000 RPC gas, a 5-second trace timeout, 1,024 polling filters, 1,024 active subscriptions, and a 5-minute filter lifetime. Every field has a matching ETHERTEST_* variable; over-limit RPC work returns -38026 and execution timeout returns -32016. HTTP, WebSocket, and IPC all apply the configured byte bounds. Embedded Go callers can classify the same limit failures with errors.Is(err, ethertest.ErrResourceLimit).

An IPC-only CLI node can be started with:

ethertest --no-http --ipc /tmp/ethertest.ipc

For containers, set --ipc to a path in a bind-mounted writable directory. Because the image runs as nonroot, create that host directory for UID/GID 65532 and mount the directory rather than an individual socket file, for example --ipc /run/ethertest/ethertest.ipc with a bind mount at /run/ethertest.

Logging

The CLI writes structured runtime logs to stdout at info level. Human-only development account output is kept on stderr so it cannot corrupt the log stream. Lifecycle and control operations are logged immediately, while transaction and interval automining activity is combined into one chain_progress event every 10 seconds. Empty intervals and per-request access logs are suppressed. debug replaces the aggregate with per-transaction and per-block events.

Configure logging with [log], --log-level, --log-json, and --log-progress-interval, or with ETHERTEST_LOG_LEVEL, ETHERTEST_LOG_JSON, and ETHERTEST_LOG_PROGRESS_INTERVAL. Supported levels are debug, info, warn, error, and off; the minimum progress interval is one second. JSON mode emits one object per line with stable time, level, msg, and event fields and suppresses the human private-key banner.

Embedded nodes do not write logs unless the caller supplies ethertest.WithLogger(logger). Runtime logs never contain private keys, mnemonics, raw transaction data, or control-state values.

Library

cfg := ethertest.DefaultConfig()
cfg.HTTP.Enabled = false
cfg.Beacon.Enabled = false
cfg.IPC.Enabled = true
cfg.IPC.Path = "/tmp/ethertest.ipc"

node, err := ethertest.New(cfg)
if err != nil { /* handle */ }
if err := node.Start(); err != nil { /* handle */ }
defer node.Close()

// Optional: wait for listener/controller failure or host cancellation.
err = node.Wait(ctx)

client := node.RPCClient()
defer client.Close()

RPCClient remains an in-process client. External clients can connect to node.Endpoints().IPC with geth's rpc.DialIPC after Start returns.

Applications that want node logs can pass a host-owned *slog.Logger with ethertest.New(cfg, ethertest.WithLogger(logger)).

Runtime signers can be managed through the library without exposing keys over JSON-RPC:

result, err := node.ImportAccount(ctx, privateKey, optionalBalance)
removed, err := node.RemoveAccount(ctx, result.Address)

result.ControlBlockHash is nil when no balance was requested.

All public writes pass through one controller and a common write-disabled gate. Pending blocks are assembled once against a read-through in-memory state layer; only canonical/branch/control commits reach the authoritative database. Queries use immutable committed headers/state roots and may run concurrently.

Development

Use Go 1.27.0 as declared in go.mod and used by CI and Docker.

Install the CI-pinned golangci-lint version before running the lint target:

go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1
make test
make test-race
make test-rpc-e2e
make lint
make build

make lint checks production and test code with the standard golangci-lint set, the modernize analyzer, and gofmt. It uses read-only module mode so linting cannot rewrite go.mod or go.sum; CI runs the same pinned tool and configuration in a dedicated job.

make test-rpc-e2e builds and starts the real CLI on random loopback ports, then runs the representative-client JSON-RPC suite against it. The suite requires Node.js 24 or newer and Foundry/cast v1.8.3; it installs the lockfile's exact viem v2.55.13 dependency with lifecycle scripts disabled. viem typed actions and cast typed/raw calls collectively exercise every method marked implemented in the locked execution API subset, plus HTTP batching, WebSocket newHeads, EIP-712, EIP-7702, common local-node controls, and canonical RPC errors. CI runs this as a separate blocking Ubuntu job.

Upstream protocol inputs are pinned in spec.lock; the consumed minimal/Gloas constants and API surface contracts are vendored under specs/upstream.

Licensing

Original code is MIT licensed. go-ethereum is LGPL-3.0; see THIRD_PARTY_NOTICES.md. A static binary release remains gated on a formal LGPL distribution review and reproducible corresponding-source materials.

Documentation

Index

Constants

View Source
const (
	PackedBytesV1HeaderSize = 48
	PackedBytesV1Capacity   = 4096*31 - PackedBytesV1HeaderSize
)
View Source
const (
	DefaultMnemonic = "test test test test test test test test test test test junk"
	DefaultChainID  = uint64(1337)
)
View Source
const StateArchiveFormat = "ethertest-state-v3"
View Source
const Version = "0.1.0-alpha.1"

Variables

View Source
var (
	ErrDepositRequestQueueFull       = errors.New("deposit execution request queue already has 8192 entries")
	ErrWithdrawalRequestQueueFull    = errors.New("withdrawal execution request queue already has 16 entries")
	ErrConsolidationRequestQueueFull = errors.New("consolidation execution request queue already has 2 entries")
	ErrExecutionRequestIDOverflow    = errors.New("execution request ID overflow")
)
View Source
var (
	ErrWithdrawalAmountZero    = errors.New("withdrawal amount must be greater than zero")
	ErrWithdrawalQueueFull     = errors.New("next block already has 16 withdrawals")
	ErrWithdrawalIndexOverflow = errors.New("withdrawal index overflow")
)
View Source
var ErrEventGap = errors.New("requested revision is no longer available")
View Source
var ErrNodeStopped = errors.New("ethertest node is stopped")
View Source
var ErrResourceLimit = errors.New("resource limit exceeded")

ErrResourceLimit is returned when a request exceeds a configured local-node work or response bound.

Functions

func DecodePackedBytesV1

func DecodePackedBytesV1(blob kzg4844.Blob) ([]byte, error)

func EncodePackedBytesV1

func EncodePackedBytesV1(payload []byte) (kzg4844.Blob, error)

EncodePackedBytesV1 encodes one payload into one canonical EIP-4844 blob. Byte zero in every field element remains zero, so every element is canonical.

func LoadState

func LoadState(path, destination string) error

LoadState replaces an empty Pebble destination with a verified archive.

func SignBlobTransaction

func SignBlobTransaction(request BlobTransactionRequest, key *ecdsa.PrivateKey) (*types.Transaction, error)

SignBlobTransaction builds an Osaka sidecar with real KZG commitment and 128 cell proofs, then signs the EIP-4844 transaction.

Types

type Account

type Account struct {
	Address    common.Address
	PrivateKey *ecdsa.PrivateKey
	Path       string
}

func DeriveAccounts

func DeriveAccounts(mnemonic string, count int) ([]Account, error)

type AccountChanges

type AccountChanges struct {
	Balance     *big.Int                     `json:"balance,omitempty"`
	Nonce       *uint64                      `json:"nonce,omitempty"`
	Code        *[]byte                      `json:"code,omitempty"`
	Storage     *map[common.Hash]common.Hash `json:"storage,omitempty"`
	StorageDiff *map[common.Hash]common.Hash `json:"storage_diff,omitempty"`
}

type AccountsConfig

type AccountsConfig struct {
	Mnemonic string `toml:"mnemonic"`
	Count    int    `toml:"count"`
	Balance  string `toml:"balance"`
}

type AuthorizationRequest

type AuthorizationRequest struct {
	ChainID *big.Int
	Address common.Address
	Nonce   uint64
}

AuthorizationRequest is the exact EIP-7702 tuple to sign. Address is the delegation target, including the zero address used to clear a delegation. Callers are responsible for selecting the nonce; signing does not read or mutate execution state.

type BeaconBlockRequest

type BeaconBlockRequest struct {
	BlockID string `path:"block_id"`
}

BeaconBlockRequest is generated from the locked path and query parameters.

type BeaconConfig

type BeaconConfig struct {
	Enabled bool `toml:"enabled"`
}

type BeaconDataColumnSidecarsRequest

type BeaconDataColumnSidecarsRequest struct {
	BlockID string   `path:"block_id"`
	Indices []string `query:"indices"`
}

BeaconDataColumnSidecarsRequest is generated from the locked path and query parameters.

type BeaconDataEnvelope

type BeaconDataEnvelope[T any] struct {
	Data T `json:"data"`
}

BeaconDataEnvelope is generated from components.schemas.DataEnvelope.

type BeaconErrorMessage

type BeaconErrorMessage struct {
	Code        int      `json:"code"`
	Message     string   `json:"message"`
	Stacktraces []string `json:"stacktraces,omitempty"`
}

BeaconErrorMessage is generated from components.schemas.ErrorMessage.

type BeaconEventsRequest

type BeaconEventsRequest struct {
	Topics []string `query:"topics"`
}

BeaconEventsRequest is generated from the locked path and query parameters.

type BeaconExecutionPayloadEnvelopeRequest

type BeaconExecutionPayloadEnvelopeRequest struct {
	BlockID string `path:"block_id"`
}

BeaconExecutionPayloadEnvelopeRequest is generated from the locked path and query parameters.

type BeaconResponse

type BeaconResponse[T any] struct {
	ExecutionOptimistic bool `json:"execution_optimistic"`
	Finalized           bool `json:"finalized"`
	Data                T    `json:"data"`
	EthertestTainted    bool `json:"ethertest_tainted,omitempty"`
}

BeaconResponse is generated from components.schemas.Response.

type BeaconValidatorBalancesRequest

type BeaconValidatorBalancesRequest struct {
	StateID string   `path:"state_id"`
	IDs     []string `query:"id"`
}

BeaconValidatorBalancesRequest is generated from the locked path and query parameters.

type BeaconValidatorsRequest

type BeaconValidatorsRequest struct {
	StateID  string   `path:"state_id"`
	IDs      []string `query:"id"`
	Statuses []string `query:"status"`
}

BeaconValidatorsRequest is generated from the locked path and query parameters.

type BeaconVersionedResponse

type BeaconVersionedResponse[T any] struct {
	Version             string `json:"version"`
	ExecutionOptimistic bool   `json:"execution_optimistic"`
	Finalized           bool   `json:"finalized"`
	Data                T      `json:"data"`
	EthertestTainted    bool   `json:"ethertest_tainted,omitempty"`
}

BeaconVersionedResponse is generated from components.schemas.VersionedResponse.

type BlobTransactionRequest

type BlobTransactionRequest struct {
	ChainID    *big.Int
	Nonce      uint64
	To         common.Address
	Gas        uint64
	GasTipCap  *big.Int
	GasFeeCap  *big.Int
	BlobFeeCap *big.Int
	Value      *big.Int
	Data       []byte
	Blob       kzg4844.Blob
}

type BlockSafety

type BlockSafety struct {
	BlockHash        common.Hash  `json:"block_hash"`
	Tainted          bool         `json:"tainted"`
	FirstUnsafeBlock *common.Hash `json:"first_unsafe_block,omitempty"`
	Reasons          []string     `json:"reasons,omitempty"`
}

BlockSafety describes whether a block belongs to Ethereum-replayable history or to an unsafe fixture lineage.

type ChainConfig

type ChainConfig struct {
	ChainID       uint64        `toml:"chain_id"`
	NetworkID     uint64        `toml:"network_id"`
	GenesisFile   string        `toml:"genesis"`
	GenesisTime   int64         `toml:"genesis_time"`
	SlotDuration  time.Duration `toml:"slot_duration"`
	SlotsPerEpoch uint64        `toml:"slots_per_epoch"`
	Validators    uint64        `toml:"validators"`
	GasLimit      uint64        `toml:"gas_limit"`
	Forks         ForkConfig    `toml:"forks"`
}

type Config

type Config struct {
	Chain     ChainConfig    `toml:"chain"`
	Accounts  AccountsConfig `toml:"accounts"`
	Mining    MiningConfig   `toml:"mining"`
	HTTP      ListenerConfig `toml:"http"`
	IPC       IPCConfig      `toml:"ipc"`
	Beacon    BeaconConfig   `toml:"beacon"`
	Storage   StorageConfig  `toml:"storage"`
	Events    EventsConfig   `toml:"events"`
	Limits    ResourceLimits `toml:"limits"`
	Log       LogConfig      `toml:"log"`
	DumpState string         `toml:"dump_state"`
}

func DefaultConfig

func DefaultConfig() Config

func LoadConfig

func LoadConfig(path string) (Config, error)

func ReadConfig

func ReadConfig(path string) (Config, error)

ReadConfig applies defaults, a strict TOML file, then ETHERTEST_* variables. It intentionally leaves validation to the caller so CLI overrides can resolve a conflict introduced by an earlier layer.

func ResolveConfig

func ResolveConfig(cfg Config) (Config, error)

ResolveConfig loads and validates an explicitly configured execution genesis and returns the effective chain fields. network_id = 0 remains an inheritance marker until a Node has selected either a fresh or persisted genesis.

func (Config) EffectiveNetworkID

func (c Config) EffectiveNetworkID() uint64

EffectiveNetworkID resolves network_id = 0 to the effective execution chain ID. Nodes store the resolved non-zero value after genesis initialization.

func (Config) IPCEndpoint

func (c Config) IPCEndpoint() string

IPCEndpoint resolves the configured socket or named-pipe endpoint. A simple name follows geth semantics: it is placed in persistent storage when one is configured, otherwise in the system temporary directory.

func (Config) Validate

func (c Config) Validate() error

type ControlChanges

type ControlChanges map[common.Address]AccountChanges

type Endpoints

type Endpoints struct {
	Execution string `json:"execution"`
	Beacon    string `json:"beacon"`
	IPC       string `json:"ipc"`
}

type Event

type Event struct {
	Revision    Revision    `json:"revision"`
	Type        string      `json:"type"`
	Slot        uint64      `json:"slot"`
	BlockHash   common.Hash `json:"block_hash,omitempty"`
	BlockNumber uint64      `json:"block_number,omitempty"`
	Removed     bool        `json:"removed,omitempty"`
	OldHead     common.Hash `json:"old_head,omitempty"`
	NewHead     common.Hash `json:"new_head,omitempty"`
	Depth       uint64      `json:"depth,omitempty"`
}

type EventsConfig

type EventsConfig struct {
	Capacity uint64 `toml:"capacity"`
}

type ExecutionConsolidationRequest

type ExecutionConsolidationRequest struct {
	SourceAddress common.Address
	SourcePubkey  [48]byte
	TargetPubkey  [48]byte
}

ExecutionConsolidationRequest is an EIP-7251 consolidation request queued for an Electra execution_requests container.

type ExecutionDepositRequest

type ExecutionDepositRequest struct {
	Pubkey                [48]byte
	WithdrawalCredentials [32]byte
	Amount                uint64
	Signature             [96]byte
	Index                 uint64
}

ExecutionDepositRequest is an EIP-6110 deposit request queued for an Electra execution_requests container. It is a synthetic control object and does not run a BeaconState deposit transition.

type ExecutionWithdrawalRequest

type ExecutionWithdrawalRequest struct {
	SourceAddress   common.Address
	ValidatorPubkey [48]byte
	Amount          uint64
}

ExecutionWithdrawalRequest is an EIP-7002 withdrawal request queued for an Electra execution_requests container. Amount is denominated in Gwei; zero retains the protocol's full-exit meaning.

type FinalityStatus

type FinalityStatus struct {
	Paused               bool        `json:"paused"`
	CurrentSlot          uint64      `json:"current_slot"`
	FinalitySlot         uint64      `json:"finality_slot"`
	SafeSlot             uint64      `json:"safe_slot"`
	SafeBlockHash        common.Hash `json:"safe_block_hash"`
	SafeBlockNumber      uint64      `json:"safe_block_number"`
	FinalizedSlot        uint64      `json:"finalized_slot"`
	FinalizedBlockHash   common.Hash `json:"finalized_block_hash"`
	FinalizedBlockNumber uint64      `json:"finalized_block_number"`
	ConsensusMode        string      `json:"consensus_mode"`
}

FinalityStatus describes ethertest's current synthetic finality projection. FinalitySlot is the observer slot from which the one/two-epoch lags are calculated. It equals CurrentSlot unless finality is paused.

type ForkConfig

type ForkConfig struct {
	AmsterdamEpoch int64  `toml:"amsterdam_epoch"`
	CancunEpoch    uint64 `toml:"cancun_epoch"`
	PragueEpoch    uint64 `toml:"prague_epoch"`
	OsakaEpoch     uint64 `toml:"osaka_epoch"`
}

type IPCConfig

type IPCConfig struct {
	Enabled bool   `toml:"enabled"`
	Path    string `toml:"path"`
}

type ImportAccountResult

type ImportAccountResult struct {
	Address          common.Address `json:"address"`
	ControlBlockHash *common.Hash   `json:"controlBlockHash"`
}

ImportAccountResult describes both the new signer and the optional unsafe control block created to set its initial balance.

type ListenerConfig

type ListenerConfig struct {
	Enabled             bool      `toml:"enabled"`
	Address             string    `toml:"address"`
	CORS                []string  `toml:"cors"`
	AllowUnsafeExternal bool      `toml:"allow_unsafe_external"`
	TLS                 TLSConfig `toml:"tls"`
}

type LogConfig

type LogConfig struct {
	Level            string        `toml:"level"`
	JSON             bool          `toml:"json"`
	ProgressInterval time.Duration `toml:"progress_interval"`
}

type MiningConfig

type MiningConfig struct {
	Mode          string        `toml:"mode"`
	Interval      time.Duration `toml:"interval"`
	Order         string        `toml:"order"`
	AutoMineEmpty bool          `toml:"auto_mine_empty"`
	FeeRecipient  string        `toml:"fee_recipient"`
}

type Node

type Node struct {
	// contains filtered or unexported fields
}

func New

func New(cfg Config, suppliedOptions ...Option) (*Node, error)

func (*Node) Accounts

func (n *Node) Accounts() []common.Address

func (*Node) AddConsolidationRequest

func (n *Node) AddConsolidationRequest(ctx context.Context, request ExecutionConsolidationRequest) error

AddConsolidationRequest persistently queues an EIP-7251-shaped synthetic request for the next canonical Prague-or-later block with remaining capacity.

func (*Node) AddDepositRequest

func (n *Node) AddDepositRequest(ctx context.Context, request ExecutionDepositRequest) error

AddDepositRequest persistently queues an EIP-6110-shaped synthetic request for the next canonical Prague-or-later block with remaining deposit capacity.

func (*Node) AddWithdrawal

func (n *Node) AddWithdrawal(ctx context.Context, request WithdrawalRequest) error

AddWithdrawal queues a withdrawal for the next canonical block. It updates the pending candidate without triggering block production.

func (*Node) AddWithdrawalRequest

func (n *Node) AddWithdrawalRequest(ctx context.Context, request ExecutionWithdrawalRequest) error

AddWithdrawalRequest persistently queues an EIP-7002-shaped synthetic request for the next canonical Prague-or-later block with remaining capacity.

func (*Node) ApplyControl

func (n *Node) ApplyControl(ctx context.Context, changes ControlChanges) (common.Hash, error)

func (*Node) Automine

func (n *Node) Automine() bool

Automine reports whether accepted transactions trigger immediate mining.

func (*Node) BlockSafety

func (n *Node) BlockSafety(hash common.Hash) (BlockSafety, error)

func (*Node) Checkpoint

func (n *Node) Checkpoint(ctx context.Context, name string) error

func (*Node) Close

func (n *Node) Close() error

func (*Node) ControlChanges

func (n *Node) ControlChanges(hash common.Hash) (ControlChanges, bool)

func (*Node) CreateBranch

func (n *Node) CreateBranch(ctx context.Context, name string, blockNumber uint64) error

func (*Node) DropAllTransactions

func (n *Node) DropAllTransactions(ctx context.Context) error

DropAllTransactions clears the pending pool, retaining canonical blob data.

func (*Node) DropTransaction

func (n *Node) DropTransaction(ctx context.Context, hash common.Hash) (bool, error)

DropTransaction removes only a currently pooled transaction.

func (*Node) DumpState

func (n *Node) DumpState(path string) error

func (*Node) Endpoints

func (n *Node) Endpoints() Endpoints

func (*Node) EventsSince

func (n *Node) EventsSince(revision Revision) ([]Event, error)

func (*Node) FinalityStatus

func (n *Node) FinalityStatus() FinalityStatus

FinalityStatus returns the active synthetic safe/finalized projection.

func (*Node) ImportAccount

func (n *Node) ImportAccount(ctx context.Context, privateKey *ecdsa.PrivateKey, balance *big.Int) (ImportAccountResult, error)

ImportAccount adds a runtime-only signing key. When balance is non-nil, its absolute value is applied through the normal unsafe control-block path.

func (*Node) IntervalMining

func (n *Node) IntervalMining() time.Duration

IntervalMining returns the active runtime interval, or zero when disabled.

func (*Node) Mine

func (n *Node) Mine(ctx context.Context, count uint64, empty bool) ([]common.Hash, error)

func (*Node) MineBranch

func (n *Node) MineBranch(ctx context.Context, name string, count uint64) ([]common.Hash, error)

func (*Node) MissSlots

func (n *Node) MissSlots(ctx context.Context, count uint64) ([]uint64, error)

func (*Node) PauseFinality

func (n *Node) PauseFinality(ctx context.Context) error

PauseFinality freezes synthetic safe/finalized resolution at the current slot while block production and missed-slot processing continue.

func (*Node) RPCClient

func (n *Node) RPCClient() *rpc.Client

func (*Node) RemoveAccount

func (n *Node) RemoveAccount(ctx context.Context, address common.Address) (bool, error)

RemoveAccount removes only a runtime-imported signer. It does not modify the corresponding execution account state.

func (*Node) RemovePoolTransactions

func (n *Node) RemovePoolTransactions(ctx context.Context, address common.Address) error

RemovePoolTransactions removes all pending transactions of an account.

func (*Node) Restore

func (n *Node) Restore(ctx context.Context, name string) error

func (*Node) ResumeFinality

func (n *Node) ResumeFinality(ctx context.Context) error

ResumeFinality resumes slot-derived finality and immediately catches the projection up to the current slot.

func (*Node) Revert

func (n *Node) Revert(ctx context.Context, id uint64) (bool, error)

func (*Node) Revision

func (n *Node) Revision() Revision

func (*Node) SafetyStatus

func (n *Node) SafetyStatus() SafetyStatus

func (*Node) SendTransaction

func (n *Node) SendTransaction(ctx context.Context, tx *types.Transaction) (common.Hash, error)

func (*Node) SetAutomine

func (n *Node) SetAutomine(ctx context.Context, enabled bool) error

SetAutomine changes runtime mining without changing the slot timeline. Already executable transactions are mined before enabling automine. If a later block fails, earlier committed blocks remain, as with Mine.

func (*Node) SetFeeRecipient

func (n *Node) SetFeeRecipient(ctx context.Context, address common.Address) error

SetFeeRecipient changes the runtime beneficiary and the pending candidate.

func (*Node) SetIntervalMining

func (n *Node) SetIntervalMining(ctx context.Context, interval time.Duration) error

SetIntervalMining selects runtime interval mining, including empty blocks. Zero selects manual mode. Block timestamps still advance by slot duration.

func (*Node) SignAuthorization

func (n *Node) SignAuthorization(authority common.Address, request AuthorizationRequest) (types.SetCodeAuthorization, error)

SignAuthorization signs an EIP-7702 authorization with a configured or runtime-imported account. ChainID must be the node chain ID or zero, the replayable cross-chain value allowed by EIP-7702.

func (*Node) Snapshot

func (n *Node) Snapshot(ctx context.Context) (uint64, error)

func (*Node) Start

func (n *Node) Start() error

Start starts the single-writer controller. Network listeners are started by Serve; embedded users may use the in-process methods without opening ports.

func (*Node) SwitchBranch

func (n *Node) SwitchBranch(ctx context.Context, name string) error

func (*Node) VerifyControlBlock

func (n *Node) VerifyControlBlock(ctx context.Context, hash common.Hash) (bool, error)

VerifyControlBlock is retained as a deprecated compatibility wrapper. Deprecated: use VerifyControlRecord.

func (*Node) VerifyControlRecord

func (n *Node) VerifyControlRecord(ctx context.Context, hash common.Hash) (bool, error)

VerifyControlRecord verifies ethertest's unsafe fixture record. It does not assert that the block is valid under the Ethereum state transition.

func (*Node) Wait

func (n *Node) Wait(ctx context.Context) error

Wait blocks until the controller stops, a background server fails, or ctx is canceled. It does not close storage; callers should always call Close.

type Option

type Option func(*nodeOptions)

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger configures structured runtime logging. New is silent when this option is omitted, allowing an embedded node to follow its host's policy.

type ResourceLimits

type ResourceLimits struct {
	MaxRequestBytes      int64         `toml:"max_request_bytes"`
	MaxBatchItems        int           `toml:"max_batch_items"`
	MaxResponseBytes     int64         `toml:"max_response_bytes"`
	MaxControlOperations uint64        `toml:"max_control_operations"`
	MaxLogBlocks         uint64        `toml:"max_log_blocks"`
	MaxLogResults        int           `toml:"max_log_results"`
	RPCGasCap            uint64        `toml:"rpc_gas_cap"`
	TraceTimeout         time.Duration `toml:"trace_timeout"`
	MaxFilters           int           `toml:"max_filters"`
	MaxSubscriptions     int           `toml:"max_subscriptions"`
	FilterTimeout        time.Duration `toml:"filter_timeout"`
}

type Revision

type Revision uint64

type SafetyStatus

type SafetyStatus struct {
	SessionTainted   bool         `json:"session_tainted"`
	HeadTainted      bool         `json:"head_tainted"`
	FirstUnsafeBlock *common.Hash `json:"first_unsafe_block,omitempty"`
	Reasons          []string     `json:"reasons,omitempty"`
	TimelineComplete bool         `json:"timeline_complete"`
	ConsensusMode    string       `json:"consensus_mode"`
}

SafetyStatus describes the safety of both the active head and the database session. Session taint is permanent because archives retain non-canonical history even after the active head returns to a clean branch.

type StateManifest

type StateManifest struct {
	Format           string   `json:"format"`
	Version          string   `json:"version"`
	CreatedAt        string   `json:"created_at"`
	ChainID          uint64   `json:"chain_id"`
	GenesisHash      string   `json:"genesis_hash"`
	HeadHash         string   `json:"head_hash"`
	HeadNumber       uint64   `json:"head_number"`
	Revision         uint64   `json:"revision"`
	DatabaseSHA256   string   `json:"database_sha256"`
	Secrets          bool     `json:"secrets"`
	Tainted          bool     `json:"tainted"`
	HeadTainted      bool     `json:"head_tainted"`
	TaintReasons     []string `json:"taint_reasons,omitempty"`
	TimelineComplete bool     `json:"timeline_complete"`
	ConsensusMode    string   `json:"consensus_mode"`
}

func InspectState

func InspectState(path string) (StateManifest, error)

type StorageConfig

type StorageConfig struct {
	Engine  string `toml:"engine"`
	Path    string `toml:"path"`
	Archive bool   `toml:"archive"`
}

type TLSConfig

type TLSConfig struct {
	CertFile string `toml:"cert_file"`
	KeyFile  string `toml:"key_file"`
}

type WithdrawalRequest

type WithdrawalRequest struct {
	ValidatorIndex uint64
	Address        common.Address
	Amount         uint64
}

WithdrawalRequest describes a withdrawal to include in the next canonical block. Amount is denominated in Gwei. The globally monotonic withdrawal index is assigned by the node when it builds the pending candidate.

Directories

Path Synopsis
cmd
ethertest command
internal

Jump to

Keyboard shortcuts

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