![]() |
VOOZH | about |
dotnet add package Nethereum.ChainStateVerification --version 6.1.0
NuGet\Install-Package Nethereum.ChainStateVerification -Version 6.1.0
<PackageReference Include="Nethereum.ChainStateVerification" Version="6.1.0" />
<PackageVersion Include="Nethereum.ChainStateVerification" Version="6.1.0" />Directory.Packages.props
<PackageReference Include="Nethereum.ChainStateVerification" />Project file
paket add Nethereum.ChainStateVerification --version 6.1.0
#r "nuget: Nethereum.ChainStateVerification, 6.1.0"
#:package Nethereum.ChainStateVerification@6.1.0
#addin nuget:?package=Nethereum.ChainStateVerification&version=6.1.0Install as a Cake Addin
#tool nuget:?package=Nethereum.ChainStateVerification&version=6.1.0Install as a Cake Tool
Nethereum.ChainStateVerification provides verified execution-state primitives using cryptographic proofs rooted in consensus-layer light client state. It enables trustless querying of account balances, storage, code, and nonces with merkle proof verification against finalized or optimistic headers.
This package allows applications to query Ethereum state (accounts, storage, code) from untrusted RPC nodes while cryptographically verifying responses against a trusted state root provided by a light client. This enables:
eth_getProof)dotnet add package Nethereum.ChainStateVerification
Traditional Ethereum clients must trust RPC nodes to provide correct state. This package enables verification:
eth_getProof)InvalidChainDataExceptionFinalized Mode (default):
Optimistic Mode:
Ethereum stores state in a Merkle Patricia Trie:
Located in TrieProofVerifier.cs:14-47
Main interface for verified state queries. Located in IVerifiedStateService.cs:8-25.
Methods:
GetAccountAsync(string address) - Get full account (balance, nonce, codeHash, storageRoot)GetBalanceAsync(string address) - Get account balanceGetNonceAsync(string address) - Get account nonceGetCodeAsync(string address) - Get contract bytecode with hash verificationGetCodeHashAsync(string address) - Get contract code hashGetStorageAtAsync(string address, BigInteger position) - Get storage value at slotGetStorageAtAsync(string address, string slotHex) - Get storage value at hex slotGetBlockHash(ulong blockNumber) - Get block hash from light clientGetCurrentHeader() - Get current trusted header (finalized or optimistic)Properties:
Mode - VerificationMode.Finalized or VerificationMode.OptimisticImplementation of IVerifiedStateService. Located in VerifiedStateService.cs:15-265.
Constructor:
public VerifiedStateService(
ITrustedHeaderProvider headerProvider, // Light client header provider
IEthGetProof getProof, // RPC eth_getProof
IEthGetCode getCode, // RPC eth_getCode
ITrieProofVerifier proofVerifier // Merkle proof verifier
)
Properties:
Mode - Verification mode (default: VerificationMode.Finalized)EnableCaching - Enable caching (default: true)VerifyCodeHash - Verify code hash matches account (default: true)Methods:
ClearCache() - Clear verified state cacheDispose() - Dispose resourcesVerifies Merkle Patricia Trie proofs. Located in TrieProofVerifier.cs:12-48.
Methods:
VerifyAccountProof(byte[] stateRoot, AccountProof accountProof) - Verifies account proof against state rootVerifyStorageProof(Account account, StorageProof storageProof) - Verifies storage proof against account storage rootThrows InvalidChainDataException if proof is invalid.
Thread-safe cache for verified state. Located in Caching/VerifiedStateCache.cs:9-233.
Properties:
BlockNumber - Current cached blockStateRoot - Current cached state rootMethods:
SetBlock(ulong blockNumber, byte[] stateRoot) - Set block (clears cache if changed)TryGetAccount(string address, out VerifiedAccountState state) - Get cached accountSetAccount(string address, Account account) - Cache accountTryGetCode(string address, out byte[] code) - Get cached codeSetCode(string address, byte[] code) - Cache codeTryGetStorage(string address, string slotHex, out byte[] value) - Get cached storageSetStorage(string address, string slotHex, byte[] value) - Cache storageClear() - Clear all cacheCache is automatically cleared when block changes.
Adapter that implements INodeDataService (from Nethereum.EVM.BlockchainState) backed by verified state. This allows the EVM simulator to use proof-verified data for balance, code, storage, nonce, and block hash lookups. Located in NodeData/VerifiedNodeDataService.cs:9-81.
Constructor:
public VerifiedNodeDataService(IVerifiedStateService verifiedState)
Implements:
GetBalanceAsync(string address) - Verified balance via merkle proofGetCodeAsync(string address) - Verified contract code with hash checkGetStorageAtAsync(string address, BigInteger position) - Verified storage slotGetTransactionCount(string address) - Verified nonceGetBlockHashAsync(BigInteger blockNumber) - Block hash from light client (within 256-block window)Enum for verification mode. Located in VerificationMode.cs:3-8.
public enum VerificationMode
{
Finalized, // Use finalized header (~12-15 min behind)
Optimistic // Use optimistic header (~12 sec behind)
}
Exception thrown when proof verification fails. Located in InvalidChainDataException.cs:5-19.
Indicates RPC node returned tampered or invalid data.
using Nethereum.ChainStateVerification;
using Nethereum.Consensus.LightClient;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
// Setup light client (provides trusted state root)
var lightClient = await CreateLightClientAsync(); // See Nethereum.Consensus.LightClient
var trustedProvider = new TrustedHeaderProvider(lightClient);
// Setup RPC client (untrusted)
var rpcClient = new RpcClient(new Uri("https://mainnet.infura.io/v3/YOUR_KEY"));
var ethGetProof = new EthGetProof(rpcClient);
var ethGetCode = new EthGetCode(rpcClient);
// Setup verifier
var trieVerifier = new TrieProofVerifier();
// Create verified state service
var verifiedState = new VerifiedStateService(
trustedProvider,
ethGetProof,
ethGetCode,
trieVerifier
);
// Query balance with proof verification
var address = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
var balance = await verifiedState.GetBalanceAsync(address);
Console.WriteLine($"Verified balance: {balance} wei");
// Balance is cryptographically verified against light client state root
From: VerifiedStateService.cs:98-102
using Nethereum.ChainStateVerification;
using System.Numerics;
// Using verifiedState from Example 1
// Query storage slot 0 of WETH contract
var wethContract = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
var slot = BigInteger.Zero;
var storageValue = await verifiedState.GetStorageAtAsync(wethContract, slot);
Console.WriteLine($"Storage slot 0: {storageValue.ToHex(true)}");
// Storage value is verified via merkle proof
From test: VerifiedStorageProofLiveTests.cs:25-49
For Solidity mappings, calculate storage slot using keccak256:
using Nethereum.ChainStateVerification;
using Nethereum.Hex.HexConvertors.Extensions;
using Nethereum.Util;
using System.Linq;
var verifiedState = CreateVerifiedStateService();
// For mapping(address => uint256) balances at slot 3:
// storage_slot = keccak256(abi.encodePacked(address, uint256(3)))
var addressBytes = "0xVitalikAddress".HexToByteArray();
var paddedAddress = new byte[32];
Buffer.BlockCopy(addressBytes, 0, paddedAddress, 12, 20); // Left pad to 32 bytes
var slotIndex = new byte[32];
slotIndex[31] = 3; // Slot 3
var combined = paddedAddress.Concat(slotIndex).ToArray();
var mappingSlot = new Sha3Keccack().CalculateHash(combined);
var wethContract = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
var balanceValue = await verifiedState.GetStorageAtAsync(
wethContract,
mappingSlot.ToHex(true)
);
var balance = new BigInteger(balanceValue.Reverse().Concat(new byte[] { 0 }).ToArray());
Console.WriteLine($"WETH balance: {balance}");
From test: VerifiedStorageProofLiveTests.cs:52-85
using Nethereum.ChainStateVerification;
var verifiedState = CreateVerifiedStateService();
// Get contract code with hash verification
var usdcContract = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
var code = await verifiedState.GetCodeAsync(usdcContract);
Console.WriteLine($"Contract code size: {code.Length} bytes");
// Code hash is verified against account codeHash from proof
From: VerifiedStateService.cs:116-166
Code Verification Process:
GetAccountAsync (includes codeHash in proof)eth_getCodekeccak256(code)InvalidChainDataException if mismatchLocated in VerifiedStateService.cs:150-158
using Nethereum.ChainStateVerification;
var verifiedState = CreateVerifiedStateService();
// Use finalized mode (maximum security, ~12-15 min old)
verifiedState.Mode = VerificationMode.Finalized;
var finalizedBalance = await verifiedState.GetBalanceAsync(address);
var finalizedHeader = verifiedState.GetCurrentHeader();
Console.WriteLine($"Finalized block: {finalizedHeader.BlockNumber}");
Console.WriteLine($"Finalized balance: {finalizedBalance}");
// Switch to optimistic mode (fresher data, ~12 sec old)
verifiedState.Mode = VerificationMode.Optimistic;
var optimisticBalance = await verifiedState.GetBalanceAsync(address);
var optimisticHeader = verifiedState.GetCurrentHeader();
Console.WriteLine($"Optimistic block: {optimisticHeader.BlockNumber}");
Console.WriteLine($"Optimistic balance: {optimisticBalance}");
// Optimistic is typically 60-80 blocks ahead of finalized
From test: VerifiedStorageProofLiveTests.cs:146-180
using Nethereum.ChainStateVerification;
using System.Numerics;
var verifiedState = CreateVerifiedStateService();
var wethContract = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
// Query multiple slots sequentially
var slots = new[] { BigInteger.Zero, BigInteger.One, new BigInteger(2) };
foreach (var slot in slots)
{
var storageValue = await verifiedState.GetStorageAtAsync(wethContract, slot);
Console.WriteLine($"Slot {slot}: {storageValue.ToHex(true)}");
}
// Each query is individually verified via merkle proof
From test: VerifiedStorageProofLiveTests.cs:116-144
using Nethereum.ChainStateVerification;
var verifiedState = CreateVerifiedStateService();
var address = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb";
// Get complete account state
var account = await verifiedState.GetAccountAsync(address);
Console.WriteLine($"Balance: {account.Balance} wei");
Console.WriteLine($"Nonce: {account.Nonce}");
Console.WriteLine($"Code Hash: {account.CodeHash.ToHex(true)}");
Console.WriteLine($"Storage Root: {account.StateRoot.ToHex(true)}");
// All fields are cryptographically verified via account proof
From: VerifiedStateService.cs:61-96
using Nethereum.ChainStateVerification;
var verifiedState = CreateVerifiedStateService();
// First query: fetches proof from RPC and verifies
var balance1 = await verifiedState.GetBalanceAsync(address);
// Second query: uses cached verified account (no RPC call)
var balance2 = await verifiedState.GetBalanceAsync(address);
// Both balances are identical and verified
Assert.Equal(balance1, balance2);
// Query storage (uses cached account if available)
var storage = await verifiedState.GetStorageAtAsync(address, BigInteger.Zero);
// Cache persists across queries for the same block
var nonce = await verifiedState.GetNonceAsync(address); // Uses cache
// Clear cache if needed
verifiedState.ClearCache();
// Disable caching if desired
verifiedState.EnableCaching = false;
From: VerifiedStateService.cs:70-78, 90-93, 192-200, 242-245
using Nethereum.ChainStateVerification;
using System;
var verifiedState = CreateVerifiedStateService();
try
{
var balance = await verifiedState.GetBalanceAsync(address);
Console.WriteLine($"Verified balance: {balance}");
}
catch (InvalidChainDataException ex)
{
// RPC node returned invalid proof or tampered data
Console.WriteLine($"Proof verification failed: {ex.Message}");
// Should try different RPC node or report malicious behavior
}
catch (InvalidOperationException ex) when (ex.Message.Contains("proof"))
{
// RPC node didn't return expected proof data
Console.WriteLine($"Incomplete proof: {ex.Message}");
}
From: VerifiedStateService.cs:84-86, TrieProofVerifier.cs:22-25, 40-43
Complete setup with light client:
using Nethereum.ChainStateVerification;
using Nethereum.Consensus.LightClient;
using Nethereum.Consensus.LightClient.BeaconApiClient;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.Web3;
// Step 1: Setup light client
var beaconClient = new BeaconApiHttpClient("https://lodestar-mainnet.chainsafe.io");
var config = new LightClientConfig
{
GenesisValidatorsRoot = "0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95".HexToByteArray(),
CurrentForkVersion = "0x06000000".HexToByteArray(), // Electra
SlotsPerEpoch = 32,
SecondsPerSlot = 12,
WeakSubjectivityRoot = await beaconClient.GetFinalizedBlockRootAsync()
};
var store = new InMemoryLightClientStore();
var bls = new HerumiNativeBindings();
bls.InitializeLibrary();
var lightClient = new LightClientService(config, beaconClient, store, bls);
await lightClient.InitializeAsync();
// Sync light client (run periodically)
await lightClient.SyncAsync();
// Step 2: Create verified state service
var trustedProvider = new TrustedHeaderProvider(lightClient);
var executionRpcUrl = "https://mainnet.infura.io/v3/YOUR_KEY";
var rpcClient = new RpcClient(new Uri(executionRpcUrl));
var ethGetProof = new EthGetProof(rpcClient);
var ethGetCode = new EthGetCode(rpcClient);
var trieVerifier = new TrieProofVerifier();
var verifiedState = new VerifiedStateService(
trustedProvider,
ethGetProof,
ethGetCode,
trieVerifier
);
// Step 3: Use verified state
verifiedState.Mode = VerificationMode.Finalized;
var balance = await verifiedState.GetBalanceAsync(address);
var nonce = await verifiedState.GetNonceAsync(address);
Console.WriteLine($"Verified balance: {balance}");
Console.WriteLine($"Verified nonce: {nonce}");
Consensus Layer Light Client must be trustworthy:
RPC Node is untrusted:
Proof Verification is cryptographic:
Scenario 1: Malicious RPC Node
TrieProofVerifier detects invalid proofInvalidChainDataException thrownScenario 2: Stale Data
Scenario 3: Code Tampering
VerifyCodeHash = true)InvalidChainDataException when hash mismatchesScenario 4: Eclipse Attack
VerifyCodeHash = true)InvalidChainDataExceptionlightClient.SyncAsync()DateTime.UtcNow - header.TimestampEach verified query requires:
eth_getProof + optional eth_getCode)Default behavior (EnableCaching = true):
Memory usage:
VerifyCodeHash = false)The interceptor subsystem lets you transparently verify RPC responses by plugging into the standard Nethereum Web3 pipeline. Instead of calling IVerifiedStateService directly, the VerifiedStateInterceptor hooks into the RPC client and replaces supported method responses with proof-verified data.
The simplest way to enable verified state is the UseVerifiedState() extension method, available on both IWeb3 and IClient:
using Nethereum.ChainStateVerification.Interceptor;
using Nethereum.Web3;
var verifiedState = CreateVerifiedStateService(); // See earlier examples
// Option 1: On Web3 (returns IWeb3 for fluent chaining)
var web3 = new Web3("https://mainnet.infura.io/v3/YOUR_KEY");
web3.UseVerifiedState(verifiedState, config =>
{
config.Mode = VerificationMode.Finalized;
config.FallbackOnError = true;
});
var blockNumber = await web3.Eth.Blocks.GetBlockNumber.SendRequestAsync();
var balance = await web3.Eth.GetBalance.SendRequestAsync("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
// Option 2: On IClient directly
var rpcClient = new RpcClient(new Uri("https://mainnet.infura.io/v3/YOUR_KEY"));
rpcClient.UseVerifiedState(verifiedState, config =>
{
config.Mode = VerificationMode.Finalized;
});
var web3FromClient = new Web3(rpcClient);
var nonce = await web3FromClient.Eth.Transactions.GetTransactionCount.SendRequestAsync("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
UseVerifiedState() on IWeb3 returns the IWeb3 instance, enabling one-liner queries:
using Nethereum.ChainStateVerification.Interceptor;
using Nethereum.Web3;
var verifiedState = CreateVerifiedStateService();
var balance = await new Web3("https://mainnet.infura.io/v3/YOUR_KEY")
.UseVerifiedState(verifiedState, config =>
{
config.Mode = VerificationMode.Finalized;
config.FallbackOnError = true;
})
.Eth.GetBalance.SendRequestAsync("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
Console.WriteLine($"Verified balance: {UnitConversion.Convert.FromWei(balance.Value)} ETH");
The configuration object controls interceptor behavior:
| Property | Type | Default | Description |
|---|---|---|---|
Mode |
VerificationMode |
Finalized |
Which consensus header to verify against (Finalized or Optimistic) |
FallbackOnError |
bool |
true |
When true, falls back to normal RPC if verification fails; when false, exceptions propagate |
EnabledMethods |
HashSet<string> |
See below | Set of RPC methods the interceptor handles |
Default enabled methods:
eth_getBalanceeth_getTransactionCounteth_getCodeeth_getStorageAteth_blockNumberAny RPC method not in EnabledMethods passes through to the node unmodified. For example, eth_gasPrice and eth_call always go directly to the RPC node.
For advanced scenarios where you need direct access to the interceptor (e.g., to subscribe to events), use the CreateVerifiedStateInterceptor() factory method:
using Nethereum.ChainStateVerification.Interceptor;
using Nethereum.JsonRpc.Client;
using Nethereum.Web3;
var verifiedState = CreateVerifiedStateService();
var interceptor = verifiedState.CreateVerifiedStateInterceptor(config =>
{
config.Mode = VerificationMode.Finalized;
config.FallbackOnError = true;
});
var rpcClient = new RpcClient(new Uri("https://mainnet.infura.io/v3/YOUR_KEY"));
rpcClient.OverridingRequestInterceptor = interceptor;
var web3 = new Web3(rpcClient);
var balance = await web3.Eth.GetBalance.SendRequestAsync("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045");
When FallbackOnError = true and verification fails (e.g., the RPC node cannot provide a valid proof for the finalized state root, often due to state pruning), the interceptor falls back to the unverified RPC response and raises the FallbackTriggered event:
using Nethereum.ChainStateVerification.Interceptor;
var verifiedState = CreateVerifiedStateService();
var interceptor = verifiedState.CreateVerifiedStateInterceptor(config =>
{
config.Mode = VerificationMode.Finalized;
config.FallbackOnError = true;
});
interceptor.FallbackTriggered += (sender, args) =>
{
Console.WriteLine($"Fallback triggered for method: {args.Method}");
Console.WriteLine($"Reason: {args.Exception?.Message}");
};
rpcClient.OverridingRequestInterceptor = interceptor;
var web3 = new Web3(rpcClient);
// If proof verification fails, FallbackTriggered fires and the
// balance is returned from the unverified RPC response instead
var balance = await web3.Eth.GetBalance.SendRequestAsync(address);
The VerificationFallbackEventArgs provides:
Method - The RPC method that failed verification (e.g., "eth_getBalance")Exception - The exception that caused the fallbackFallbackTriggeredeth_gasPrice, eth_call) → Passes through to RPC node directly"earliest") → Passes through to RPC (interceptor only handles latest, pending, finalized, safe)Requires Archive Node (or recent state):
eth_getProof at historical blocksLight Client Dependency:
Finalized Lag:
No Write Operations:
Core dependencies:
eth_getProofCore Services:
IVerifiedStateService.cs - Main service interfaceVerifiedStateService.cs - Verified state implementationITrieProofVerifier.cs - Proof verifier interfaceTrieProofVerifier.cs - Merkle proof verificationCaching:
Caching/VerifiedStateCache.cs - Thread-safe state cacheCaching/VerifiedAccountState.cs - Cached account stateSupporting Types:
VerificationMode.cs - Finalized vs Optimistic enumInvalidChainDataException.cs - Proof verification exceptionInterceptor:
Interceptor/VerifiedStateInterceptor.cs - RPC request interceptor for transparent verificationInterceptor/VerifiedStateInterceptorConfiguration.cs - Interceptor configuration optionsInterceptor/Web3VerifiedStateExtensions.cs - Extension methods for IWeb3 and IClientNode Data:
NodeData/VerifiedNodeDataService.cs - INodeDataService adapter for EVM integrationTest Files:
tests/Nethereum.Consensus.LightClient.Tests/Live/VerifiedStorageProofLiveTests.cs - Storage proof teststests/Nethereum.Consensus.LightClient.Tests/Live/VerifiedEvmCallLiveTests.cs - EVM call teststests/Nethereum.ChainStateVerification.Tests/Caching/VerifiedStateCacheTests.cs - Cache teststests/Nethereum.Consensus.LightClient.Tests/Live/VerifiedStateInterceptorLiveTests.cs - Interceptor live testsNethereum is licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 1 NuGet packages that depend on Nethereum.ChainStateVerification:
| Package | Downloads |
|---|---|
|
Nethereum.Wallet
Core wallet services for managing accounts, vaults, and configuration across the Nethereum stack. |
This package is not used by any popular GitHub repositories.