A Rootstock (RSK) light client implementation in Rust. Rustock syncs and validates block headers from the RSK network using Bitcoin merged mining proofs, follows the chain tip in real time, and exposes an rskj-compatible JSON-RPC interface.
Alpha software. This project has been primarily coded by AI and has not yet undergone human code review or any security audit. Use at your own risk and do not rely on it for production workloads or anything involving real funds.
- Header sync: Skeleton-based bulk sync with parallel chunk downloads across multiple peers, followed by real-time tip following via
NewBlockHashes. - Consensus validation: Full verification of Bitcoin merged mining (AuxPow) proofs, difficulty adjustment, gas limits, timestamps, and all RSK consensus rules including activation-height-gated RSKIPs.
- Chain reorganization: Detects competing forks, compares total difficulty, and rewrites canonical chain pointers when a heavier fork is found.
- P2P networking: Full RLPx encryption (inbound and outbound), Kademlia-based UDP discovery, peer exchange, and persistent node tables.
- Transaction relay: Receives transaction messages from peers, deduplicates via an LRU cache, and rebroadcasts to all other connected peers.
- Serving peers: Responds to
BlockHeadersRequest,BlockHashRequest, andSkeletonRequestmessages from other nodes. - JSON-RPC server: rskj-compatible HTTP API with
eth,net,web3,rpc, andrskmodules. - Storage: RocksDB-backed persistence for headers, total difficulty, and canonical chain mappings.
- Rust (edition 2021)
- RocksDB system libraries (usually handled automatically by
rust-rocksdb)
cargo build --workspace --releaseStart the light client on RSK Mainnet (default):
cargo run -p rustock-cli --release -- --port 30303 --data-dir ./data --log-to-stdoutLogs are written to <data-dir>/rustock.log by default (with daily rotation). Use --log-to-stdout for console output.
| Flag | Default | Description |
|---|---|---|
--port |
30303 |
P2P listen port |
--data-dir |
./data |
Data directory for RocksDB and logs |
--network-id |
30 |
Network ID (30 = mainnet, 31 = testnet) |
--secret-key |
auto-generated | Hex-encoded secp256k1 private key |
--log-level |
info |
trace, debug, info, warn, error, or RUST_LOG-style directives |
--log-to-stdout |
false |
Log to console instead of file |
--rpc-port |
4444 |
JSON-RPC HTTP port |
--rpc-host |
127.0.0.1 |
JSON-RPC bind address |
--no-rpc |
false |
Disable the JSON-RPC server |
--external-ip |
none | External IP to advertise in discovery (e.g. 203.0.113.42) |
cargo test --workspace195 tests covering consensus validation, RLP encoding, P2P handshakes, sync state machines, storage, RPC methods, transaction relay, and chain reorganizations.
The RPC server is compatible with rskj's JSON-RPC 2.0 interface. Supported methods:
Fully implemented (using local header data):
eth_blockNumber,eth_chainId,eth_syncing,eth_protocolVersioneth_gasPrice,eth_mining,eth_hashrate,eth_accounts,eth_coinbaseeth_getBlockByHash,eth_getBlockByNumbereth_getBlockTransactionCountByHash,eth_getBlockTransactionCountByNumbereth_getUncleCountByBlockHash,eth_getUncleCountByBlockNumbereth_sendRawTransaction(broadcasts to connected peers)net_version,net_peerCount,net_listening,net_peerListweb3_clientVersion,web3_sha3rpc_modulesrsk_protocolVersion,rsk_getRawBlockHeaderByHash,rsk_getRawBlockHeaderByNumber
Unsupported (returns error -- requires state, bodies, or receipts not available in a header-only client):
eth_getBalance,eth_getStorageAt,eth_getCode,eth_getTransactionCounteth_call,eth_estimateGaseth_getTransactionByHash,eth_getTransactionReceipt,eth_getLogseth_newFilter,eth_getFilterChanges,eth_getFilterLogsdebug_*,trace_*,personal_*,txpool_*
crates/
cli/ Main entry point, CLI argument parsing
core/ Base types (Header, Block, Transaction), consensus rules, chain config
networking/ P2P protocol (RLPx, discovery, sessions, peer management)
rpc/ JSON-RPC HTTP server (axum-based)
storage/ RocksDB persistence for headers and chain state
sync/ Sync state machine, header validation pipeline, transaction relay
Rustock is a header-only light client. It validates and stores block headers but does not download block bodies, execute transactions, or maintain world state. This means:
- No state queries: Methods like
eth_getBalance,eth_call, andeth_getStorageAtcannot be answered locally. These require Merkle state proofs verified against the header'sstate_root. - No receipt/log queries:
eth_getTransactionReceiptandeth_getLogsrequire receipt proofs verified against the header'sreceipts_root. - No transaction validation: The node relays transactions without validating them (no nonce checking, gas estimation, or balance verification).
Enabling state and receipt queries in a trustless way requires changes in rskj (the RSK full node):
-
eth_getProofsupport: rskj does not currently implement EIP-1186 (eth_getProof), which provides Merkle Patricia trie proofs for account state and storage. RSK's Unitrie architecture would need an adapted proof format. -
LES-style P2P protocol: There is no Light Ethereum Subprotocol (LES) equivalent in the RSK P2P protocol. A light client proof-serving protocol would allow requesting proofs over P2P instead of relying on trusted RPC endpoints.
-
Receipt proofs via RPC (partially available): rskj already exposes
rsk_getTransactionReceiptNodesByHashwhich provides receipt trie nodes. This could be used to build receipt inclusion proofs, but requires an RPC connection to a trusted full node.
These limitations are inherent to RSK's current protocol design, not to this implementation. If rskj adds proof-serving capabilities in the future, Rustock can be extended to support trustless state and receipt verification.
MIT