Get Block Witness

mega_getBlockWitness — fetch the SALT + MPT witness needed to stateless-verify a MegaETH block.

MegaETH defines a mega_getBlockWitness RPC method to return the cryptographic witness for a single MegaETH block. The witness contains the subset of state the block reads or writes, packaged with proofs against the previous block's state root, so that a stateless verifier can re-execute the block without holding any chain state locally.

The RPC method is served at the public MegaETH RPC endpoint:

https://mainnet.megaeth.com/rpc

The witness JSON-RPC method is the same logical service that powers the stateless validator's --witness-endpoint. Any client — an operator running stateless-validatorarrow-up-right or a custom verifier — can call it directly.

Request

Value

Method

mega_getBlockWitness

Params

[<keys>] — single-element array

<keys> is a JSON object that identifies the block. blockNumber is always required; pair it with blockHash to pin the witness to a specific block.

Field
Type
Required
Description

blockNumber

Quantity (hex)

Yes

Block number, 0x-prefixed lowercase hex (e.g. "0x7fd").

blockHash

Data (32 bytes)

No

0x-prefixed lowercase hash of the block to fetch the witness for. Pins the result, so it is reorg-safe.

Lookup modes

The combination of fields chosen determines the lookup mode. Always pass blockHash when one is available. The blockNumber-only mode does not pin the result to a specific block and can return a witness for the wrong fork.

Mode
Recommendation
When to use

blockNumber + blockHash

Preferred

The caller already knows the canonical block hash (e.g. fetched from eth_getBlockByNumber first). The witness is pinned to that exact block, so the result is reorg-safe.

blockNumber

Avoid

Last-resort convenience. The backend returns the first stored witness it finds at that height — there is no guarantee the returned witness is for the block you expect. Only use when you cannot obtain a hash and can independently verify the response.

circle-exclamation

Examples

Response

The response result is a single string of the form <version>:<base64-payload>.

Field
Description

version

Encoding version. Currently v0. Bumped if the witness payload format ever changes — clients must check the prefix.

payload

Base64-encoded, Zstd-compressed bincodearrow-up-right tuple (SaltWitness, MptWitness).

Errors

Code
Cause

-32602

Invalid params — malformed JSON, missing blockNumber, or unparseable hex.

-32000

Witness not found — no witness stored for the requested keys.

-32001

Decompression failed — stored payload is corrupted (server-side issue).

The server returns -32000 (a 404 equivalent) when no witness exists for the requested keys. The RPC layer in front of the witness service may also return standard JSON-RPC transport codes: -32700 (parse error), -32600 (invalid request), -32603 (internal error).

Decoding pipeline

To turn the response string back into a witness, apply these steps in order:

  1. Verify the string starts with the literal prefix v0: and strip it.

  2. Base64-decode the payload (standard alphabet, padded).

  3. Zstd-decompress the result.

  4. Bincode-deserialize using the legacy config (fixed-int encoding, little-endian) into (SaltWitness, MptWitness).

A reference Rust implementation lives in the upstream stateless validator at fetch_witness_rawarrow-up-right:

circle-info

SaltWitness is defined in the saltarrow-up-right crate; MptWitness is defined in the stateless-core crate of the stateless-validatorarrow-up-right repository. Add both as Cargo dependencies (via a git or path source) before compiling this snippet.

Witness data structure

The decoded payload is a 2-tuple — one component per state surface the validator must check. Every type below is given as the upstream Rust definition with its source location, so a third-party decoder in another language can reproduce the byte layout exactly.

SaltWitness — main state trie

Carries the subset of SALTarrow-up-right key-value pairs the block touches, plus a single multi-point IPA proof binding them to the previous block's state root.

Defined at salt/src/proof/salt_witness.rs:46arrow-up-right:

Field
Description

kvs

Witnessed slots. Some(v) means the slot is occupied with value v; None means the slot is proven empty; an absent key is unknown (verifier must error).

proof

IPA multi-point proof + the path commitments needed to authenticate every entry in kvs against the state root.

A verifier uses the witness as a StateReader / TrieReader: every state read during block re-execution falls through to kvs (existence-proven) or errors (unknown). The three-state distinction (existing / proven-empty / unknown) is what blocks a malicious witness server from hiding state by omission — see the security model in the upstream SaltWitness doc-comment. For the trie design this witness proves against, see the SALT READMEarrow-up-right.

SaltKey

Defined at salt/src/types.rs:198arrow-up-right:

The u64 packs the SALT trie address into two fields:

Bits
Field
Range
Meaning

63..=40

bucket_id

24 bits (~16M)

Index into the static main trie. The first NUM_META_BUCKETS = 65_536 buckets hold metadata.

39..=0

slot_id

40 bits (~1T)

Slot offset inside the bucket's SHI hash table.

Use bucket_id = key >> 40 and slot_id = key & ((1<<40) - 1) to unpack. Bincode-legacy serializes SaltKey as a fixed 8-byte little-endian u64.

SaltValue

Defined at salt/src/types.rs:274arrow-up-right:

data holds a length-prefixed key-value blob:

Offset
Size
Field

0

1 byte

key_len

1

1 byte

value_len

2..2+key_len

key_len

key

2+key_len..2+key_len+value_len

value_len

value

2+key_len+value_len..94

remainder

zero-padded

Three SaltValue flavors share this encoding:

Kind

key_len

value_len

Used bytes

Notes

Account

20

40 (EOA) / 72 (contract)

62 / 94

Key is the 20-byte address; value is the encoded account body.

Storage

52

32

86

Key is address(20) ++ storage_slot(32); value is the 32-byte slot value.

BucketMeta

12

0

14

Reserved for the metadata buckets — BucketMeta is fully encoded into the 12-byte key, value is empty.

SaltProof

Defined at salt/src/proof/prover.rs:103arrow-up-right:

Field
Type
Description

parents_commitments

BTreeMap<NodeId, SerdeCommitment>

Commitment for every trie node on the path from a witnessed bucket up to the root. Lookups by NodeId = u64 (a flat trie-node index) — the verifier walks these to the root.

proof

SerdeMultiPointProof

The IPA multi-point opening proof over the Banderwagon scalar field. Serialized via MultiPointProof::to_bytes; deserialize with MultiPointProof::from_bytes(&buf, DOMAIN_SIZE) where DOMAIN_SIZE = 256 (the IPA polynomial degree, matching SALT's 256-ary trie fan-out).

levels

FxHashMap<BucketId, u8>

Number of subtree levels for each bucket present in the proof. Required because the verifier doesn't always know a bucket's capacity from the witness alone.

SerdeCommitment wraps a Banderwagon group Element; SerdeMultiPointProof wraps an ipa_multipoint::MultiPointProof. Both serialize to opaque byte vectors via the IPA crate's encoding.

circle-info

Map encoding order. kvs and parents_commitments are BTreeMap, so bincode emits their entries in canonical (sorted) key order — wire output is byte-stable for re-implementors that want to round-trip-compare. levels is an FxHashMap, whose iteration order depends on the hasher; do not assume a fixed order when re-encoding.

MptWitness — withdrawals storage trie

A small Merkle Patricia Trie witness covering the storage trie of the L2-to-L1 message-passer contract (0x4200000000000000000000000000000000000016), so the validator can recompute withdrawals_root after applying the block's withdrawal-message writes.

Defined at stateless-core/src/withdrawals.rs:49arrow-up-right:

Field
Type
Description

storage_root

B256

Pre-state storage root of the L2ToL1MessagePasser contract. Serialized as 32 raw bytes.

state

Vec<Bytes>

RLP-encoded MPT trie nodes that authenticate the storage slots the block's withdrawal writes will touch. Each Bytes is a length-prefixed byte string.

This is intentionally an MPT (not SALT) witness: withdrawals are committed to the standard Ethereum withdrawals MPT root for L1 compatibility, so the slice of state needed to maintain it is proved separately from the SALT-backed account/storage state.

Example

Fetch the witness for a known block and pipe it through the decode pipeline. Replace <BLOCK_NUMBER> (0x-prefixed lowercase hex) and <BLOCK_HASH> with values from eth_getBlockByNumber. The pipeline below assumes jq and zstd are on PATH — install them via brew install jq zstd on macOS or apt install jq zstd on Debian/Ubuntu.

witness.bincode is a Zstd-decompressed bincode tuple — feed it into a Rust deserializer (using the snippet under Decoding pipeline) to obtain (SaltWitness, MptWitness).

Last updated