![]() |
VOOZH | about |
dotnet add package Nethereum.Consensus.Ssz --version 6.1.0
NuGet\Install-Package Nethereum.Consensus.Ssz -Version 6.1.0
<PackageReference Include="Nethereum.Consensus.Ssz" Version="6.1.0" />
<PackageVersion Include="Nethereum.Consensus.Ssz" Version="6.1.0" />Directory.Packages.props
<PackageReference Include="Nethereum.Consensus.Ssz" />Project file
paket add Nethereum.Consensus.Ssz --version 6.1.0
#r "nuget: Nethereum.Consensus.Ssz, 6.1.0"
#:package Nethereum.Consensus.Ssz@6.1.0
#addin nuget:?package=Nethereum.Consensus.Ssz&version=6.1.0Install as a Cake Addin
#tool nuget:?package=Nethereum.Consensus.Ssz&version=6.1.0Install as a Cake Tool
SSZ container implementations for Ethereum consensus layer light client types, including beacon block headers, sync committees, and light client data structures.
Nethereum.Consensus.Ssz provides strongly-typed SSZ containers for Ethereum's consensus layer (beacon chain), specifically designed for light client synchronization. This package builds on top of Nethereum.Ssz primitives to implement complete consensus data structures with encoding, decoding, and hash tree root computation.
Key Features:
dotnet add package Nethereum.Consensus.Ssz
Or via Package Manager Console:
Install-Package Nethereum.Consensus.Ssz
Ethereum uses two different serialization formats:
| Aspect | Execution Layer | Consensus Layer |
|---|---|---|
| Format | RLP (Recursive Length Prefix) | SSZ (Simple Serialize) |
| Hash Function | Keccak-256 | SHA-256 |
| Byte Order | Big-endian | Little-endian |
| Use Cases | Transactions, blocks, accounts | Beacon blocks, attestations, validators |
This package implements consensus layer types using SSZ.
BeaconBlockHeader (112 bytes fixed): Core beacon chain block header
SyncCommittee (24,624 bytes): Committee of 512 validators
LightClientHeader: Beacon header with execution layer payload
LightClientBootstrap: Initial sync data for light clients
All containers follow a consistent interface:
public class Container
{
// Serialize to SSZ bytes
public byte[] Encode() { }
// Deserialize from SSZ bytes
public static Container Decode(ReadOnlySpan<byte> data) { }
// Compute SHA-256 merkle root
public byte[] HashTreeRoot() { }
}
using Nethereum.Consensus.Ssz;
using Nethereum.Hex.HexConvertors.Extensions;
// Create and encode a beacon block header
var header = new BeaconBlockHeader
{
Slot = 1234567,
ProposerIndex = 42,
ParentRoot = new byte[32],
StateRoot = new byte[32],
BodyRoot = new byte[32]
};
// Serialize to SSZ
byte[] encoded = header.Encode();
// Deserialize from SSZ
var decoded = BeaconBlockHeader.Decode(encoded);
// Compute hash tree root for verification
byte[] root = header.HashTreeRoot();
Console.WriteLine($"Block root: {root.ToHex(true)}");
using Nethereum.Consensus.Ssz;
// Create a beacon block header
var header = new BeaconBlockHeader
{
Slot = 5000000,
ProposerIndex = 123456,
ParentRoot = new byte[32] { 0x01, 0x02, /* ... */ },
StateRoot = new byte[32] { 0x03, 0x04, /* ... */ },
BodyRoot = new byte[32] { 0x05, 0x06, /* ... */ }
};
// Encode to SSZ bytes (always 112 bytes)
byte[] sszBytes = header.Encode();
Console.WriteLine($"Encoded length: {sszBytes.Length}"); // 112
// Decode back to object
var decoded = BeaconBlockHeader.Decode(sszBytes);
// Verify round-trip
Assert.Equal(header.Slot, decoded.Slot);
Assert.Equal(header.ProposerIndex, decoded.ProposerIndex);
using Nethereum.Consensus.Ssz;
// Create sync committee with 512 public keys
var syncCommittee = new SyncCommittee();
// Each public key is 48 bytes (BLS12-381)
syncCommittee.PubKeys = new List<byte[]>();
for (int i = 0; i < SszBasicTypes.SyncCommitteeSize; i++)
{
var pubkey = new byte[SszBasicTypes.PubKeyLength];
pubkey[0] = (byte)(i % 256);
pubkey[1] = (byte)(i / 256);
syncCommittee.PubKeys.Add(pubkey);
}
// Aggregate public key
syncCommittee.AggregatePubKey = new byte[SszBasicTypes.PubKeyLength];
// Encode (24,624 bytes total)
byte[] encoded = syncCommittee.Encode();
Console.WriteLine($"Size: {encoded.Length}"); // 24,624
// Decode
var decoded = SyncCommittee.Decode(encoded);
Assert.Equal(512, decoded.PubKeys.Count);
using Nethereum.Consensus.Ssz;
using Nethereum.Hex.HexConvertors.Extensions;
var header = new BeaconBlockHeader
{
Slot = 1000,
ProposerIndex = 50,
ParentRoot = new byte[32],
StateRoot = new byte[32],
BodyRoot = new byte[32]
};
// Compute SHA-256 merkle root
byte[] root = header.HashTreeRoot();
// This root can be used for:
// 1. Block identification
// 2. Merkle proof verification
// 3. Light client sync
Console.WriteLine($"Block root: {root.ToHex(true)}");
// The root is deterministic - same input = same root
byte[] root2 = header.HashTreeRoot();
Assert.True(root.SequenceEqual(root2));
using Nethereum.Consensus.Ssz;
// Bootstrap data for light client initialization
var bootstrap = new LightClientBootstrap();
// Current header
bootstrap.Header = new LightClientHeader
{
Beacon = new BeaconBlockHeader
{
Slot = 5000000,
ProposerIndex = 1000,
ParentRoot = new byte[32],
StateRoot = new byte[32],
BodyRoot = new byte[32]
}
};
// Current sync committee
bootstrap.CurrentSyncCommittee = new SyncCommittee
{
PubKeys = new List<byte[]>(),
AggregatePubKey = new byte[48]
};
// Add 512 validator pubkeys
for (int i = 0; i < 512; i++)
{
bootstrap.CurrentSyncCommittee.PubKeys.Add(new byte[48]);
}
// Merkle branch for verification (depth varies by spec)
bootstrap.CurrentSyncCommitteeBranch = new List<byte[]>
{
new byte[32], new byte[32], new byte[32], new byte[32]
};
// Encode for transmission
byte[] encoded = bootstrap.Encode();
// Light client can decode and verify
var decoded = LightClientBootstrap.Decode(encoded);
using Nethereum.Consensus.Ssz;
// Post-merge blocks include execution layer data
var executionHeader = new ExecutionPayloadHeader
{
ParentHash = new byte[32],
FeeRecipient = new byte[20], // Ethereum address
StateRoot = new byte[32],
ReceiptsRoot = new byte[32],
LogsBloom = new byte[256],
PrevRandao = new byte[32],
BlockNumber = 15537394, // Example merge block
GasLimit = 30000000,
GasUsed = 12000000,
Timestamp = 1663224162,
ExtraData = new byte[0],
BaseFeePerGas = System.Numerics.BigInteger.Parse("15000000000"),
BlockHash = new byte[32],
TransactionsRoot = new byte[32]
};
// Encode execution header
byte[] encoded = executionHeader.Encode();
// Decode
var decoded = ExecutionPayloadHeader.Decode(encoded);
Console.WriteLine($"Block number: {decoded.BlockNumber}");
Console.WriteLine($"Gas used: {decoded.GasUsed}/{decoded.GasLimit}");
using Nethereum.Consensus.Ssz;
var lightClientHeader = new LightClientHeader();
// Beacon layer header
lightClientHeader.Beacon = new BeaconBlockHeader
{
Slot = 5000000,
ProposerIndex = 42,
ParentRoot = new byte[32],
StateRoot = new byte[32],
BodyRoot = new byte[32]
};
// Execution layer header (post-merge)
lightClientHeader.Execution = new ExecutionPayloadHeader
{
BlockNumber = 15537394,
BlockHash = new byte[32],
ParentHash = new byte[32],
// ... other fields
};
// Merkle branch proving execution payload inclusion
lightClientHeader.ExecutionBranch = new List<byte[]>
{
new byte[32], new byte[32], new byte[32], new byte[32]
};
// Encode complete header
byte[] encoded = lightClientHeader.Encode();
// Light client uses this to verify execution layer blocks
var decoded = LightClientHeader.Decode(encoded);
using Nethereum.Consensus.Ssz;
// SSZ containers have two sections:
// 1. Fixed: Known size at compile time
// 2. Dynamic: Variable size (lists, variable bytes)
var header = new BeaconBlockHeader
{
Slot = 100, // Fixed: 8 bytes
ProposerIndex = 200, // Fixed: 8 bytes
ParentRoot = new byte[32], // Fixed: 32 bytes
StateRoot = new byte[32], // Fixed: 32 bytes
BodyRoot = new byte[32] // Fixed: 32 bytes
};
// BeaconBlockHeader is entirely fixed: 112 bytes
byte[] encoded = header.Encode();
Assert.Equal(112, encoded.Length);
// Containers with dynamic fields use offsets
var bootstrap = new LightClientBootstrap
{
Header = new LightClientHeader(),
CurrentSyncCommittee = new SyncCommittee(),
CurrentSyncCommitteeBranch = new List<byte[]> { new byte[32] }
};
// Dynamic section comes after fixed section
// Fixed section contains 4-byte offsets to dynamic data
byte[] dynamicEncoded = bootstrap.Encode();
using Nethereum.Consensus.Ssz;
using Nethereum.Hex.HexConvertors.Extensions;
// Light client verification workflow
public bool VerifyLightClientUpdate(
byte[] trustedRoot,
LightClientHeader receivedHeader)
{
// 1. Compute hash tree root of received header
byte[] computedRoot = receivedHeader.HashTreeRoot();
// 2. Compare with trusted root
if (!computedRoot.SequenceEqual(trustedRoot))
{
Console.WriteLine("Header root mismatch!");
return false;
}
// 3. Verify merkle branch for execution payload
if (receivedHeader.ExecutionBranch != null)
{
// Merkle proof verification would go here
// using the execution branch
}
Console.WriteLine($"Header verified: slot {receivedHeader.Beacon.Slot}");
return true;
}
using Nethereum.Consensus.Ssz;
using Nethereum.Hex.HexConvertors.Extensions;
// Step 1: Bootstrap light client
var bootstrap = new LightClientBootstrap();
bootstrap.Header = new LightClientHeader
{
Beacon = new BeaconBlockHeader
{
Slot = 5000000,
ProposerIndex = 100,
ParentRoot = new byte[32],
StateRoot = new byte[32],
BodyRoot = new byte[32]
}
};
bootstrap.CurrentSyncCommittee = new SyncCommittee
{
PubKeys = new List<byte[]>(),
AggregatePubKey = new byte[48]
};
for (int i = 0; i < 512; i++)
{
bootstrap.CurrentSyncCommittee.PubKeys.Add(new byte[48]);
}
bootstrap.CurrentSyncCommitteeBranch = new List<byte[]>
{
new byte[32], new byte[32], new byte[32], new byte[32]
};
// Step 2: Encode and transmit
byte[] bootstrapData = bootstrap.Encode();
Console.WriteLine($"Bootstrap size: {bootstrapData.Length} bytes");
// Step 3: Light client receives and decodes
var decoded = LightClientBootstrap.Decode(bootstrapData);
// Step 4: Verify sync committee
byte[] syncCommitteeRoot = decoded.CurrentSyncCommittee.HashTreeRoot();
Console.WriteLine($"Sync committee root: {syncCommitteeRoot.ToHex(true)}");
// Step 5: Now light client can verify future updates using this committee
Console.WriteLine($"Light client synced to slot: {decoded.Header.Beacon.Slot}");
Console.WriteLine($"Sync committee size: {decoded.CurrentSyncCommittee.PubKeys.Count}");
Core beacon chain block header (112 bytes fixed size).
public class BeaconBlockHeader
{
public ulong Slot { get; set; } // 8 bytes
public ulong ProposerIndex { get; set; } // 8 bytes
public byte[] ParentRoot { get; set; } // 32 bytes
public byte[] StateRoot { get; set; } // 32 bytes
public byte[] BodyRoot { get; set; } // 32 bytes
public byte[] Encode();
public static BeaconBlockHeader Decode(ReadOnlySpan<byte> data);
public byte[] HashTreeRoot();
}
Sync committee of 512 validators (24,624 bytes).
public class SyncCommittee
{
public List<byte[]> PubKeys { get; set; } // 512 x 48 bytes
public byte[] AggregatePubKey { get; set; } // 48 bytes
public byte[] Encode();
public static SyncCommittee Decode(ReadOnlySpan<byte> data);
public byte[] HashTreeRoot();
}
Beacon header with execution payload.
public class LightClientHeader
{
public BeaconBlockHeader Beacon { get; set; }
public ExecutionPayloadHeader Execution { get; set; }
public List<byte[]> ExecutionBranch { get; set; }
public byte[] Encode();
public static LightClientHeader Decode(byte[] data);
public byte[] HashTreeRoot();
}
Initial sync data for light clients.
public class LightClientBootstrap
{
public LightClientHeader Header { get; set; }
public SyncCommittee CurrentSyncCommittee { get; set; }
public List<byte[]> CurrentSyncCommitteeBranch { get; set; }
public byte[] Encode();
public static LightClientBootstrap Decode(byte[] data);
public byte[] HashTreeRoot();
}
Full light client update with next sync committee and finality proof.
public class LightClientUpdate
{
public LightClientHeader AttestedHeader { get; set; }
public SyncCommittee NextSyncCommittee { get; set; }
public IList<byte[]> NextSyncCommitteeBranch { get; set; }
public LightClientHeader FinalizedHeader { get; set; }
public IList<byte[]> FinalityBranch { get; set; }
public SyncAggregate SyncAggregate { get; set; }
public ulong SignatureSlot { get; set; }
public byte[] Encode();
public static LightClientUpdate Decode(byte[] data);
public byte[] HashTreeRoot();
}
Light client update proving finality without sync committee rotation.
public class LightClientFinalityUpdate
{
public LightClientHeader AttestedHeader { get; set; }
public LightClientHeader FinalizedHeader { get; set; }
public IList<byte[]> FinalityBranch { get; set; }
public SyncAggregate SyncAggregate { get; set; }
public ulong SignatureSlot { get; set; }
public byte[] Encode();
public static LightClientFinalityUpdate Decode(byte[] data);
public byte[] HashTreeRoot();
}
Lightweight update for optimistic header tracking.
public class LightClientOptimisticUpdate
{
public LightClientHeader AttestedHeader { get; set; }
public SyncAggregate SyncAggregate { get; set; }
public ulong SignatureSlot { get; set; }
public byte[] Encode();
public static LightClientOptimisticUpdate Decode(byte[] data);
public byte[] HashTreeRoot();
}
Aggregated sync committee participation bits and BLS signature (160 bytes fixed).
public class SyncAggregate
{
public byte[] SyncCommitteeBits { get; set; } // 64 bytes (512 bits)
public byte[] SyncCommitteeSignature { get; set; } // 96 bytes
public byte[] Encode();
public static SyncAggregate Decode(ReadOnlySpan<byte> data);
public byte[] HashTreeRoot();
}
Post-merge execution layer header.
public class ExecutionPayloadHeader
{
public byte[] ParentHash { get; set; } // 32 bytes
public byte[] FeeRecipient { get; set; } // 20 bytes
public byte[] StateRoot { get; set; } // 32 bytes
public byte[] ReceiptsRoot { get; set; } // 32 bytes
public byte[] LogsBloom { get; set; } // 256 bytes
public byte[] PrevRandao { get; set; } // 32 bytes
public ulong BlockNumber { get; set; } // 8 bytes
public ulong GasLimit { get; set; } // 8 bytes
public ulong GasUsed { get; set; } // 8 bytes
public ulong Timestamp { get; set; } // 8 bytes
public byte[] ExtraData { get; set; } // Variable
public BigInteger BaseFeePerGas { get; set; } // 32 bytes
public byte[] BlockHash { get; set; } // 32 bytes
public byte[] TransactionsRoot { get; set; } // 32 bytes
public byte[] Encode();
public static ExecutionPayloadHeader Decode(ReadOnlySpan<byte> data);
public byte[] HashTreeRoot();
}
Constants for consensus layer types.
public static class SszBasicTypes
{
public const int RootLength = 32;
public const int PubKeyLength = 48;
public const int SignatureLength = 96;
public const int SyncCommitteeSize = 512;
public const int BeaconBlockHeaderLength = 112;
}
Internal helper for combining fixed and dynamic SSZ sections. Used internally by container Encode() methods.
internal static class SszContainerEncoding
{
public static byte[] Combine(
byte[] fixedSection,
params byte[][] dynamicSections);
}
DO NOT mix consensus and execution serialization formats:
// WRONG - using RLP on consensus types
var header = new BeaconBlockHeader();
byte[] rlpEncoded = header.EncodeRLP(); // Does not exist!
// CORRECT - using SSZ on consensus types
byte[] sszEncoded = header.Encode();
Many fields have strict size requirements:
// WRONG - incorrect sizes
header.ParentRoot = new byte[16]; // Must be 32!
syncCommittee.PubKeys.Add(new byte[64]); // Must be 48!
// CORRECT
header.ParentRoot = new byte[32];
syncCommittee.PubKeys.Add(new byte[48]);
Consensus layer uses SHA-256, not Keccak-256:
// WRONG - Keccak is for execution layer
byte[] hash = Keccak256.Compute(header.Encode());
// CORRECT - SHA-256 for consensus
byte[] root = header.HashTreeRoot();
Sync committees must have exactly 512 validators:
// WRONG
var syncCommittee = new SyncCommittee
{
PubKeys = new List<byte[]>(256) // Wrong size!
};
// CORRECT
var syncCommittee = new SyncCommittee
{
PubKeys = new List<byte[]>(512)
};
for (int i = 0; i < SszBasicTypes.SyncCommitteeSize; i++)
{
syncCommittee.PubKeys.Add(new byte[48]);
}
Merkle branch depth varies by consensus spec version. Always verify against the current Ethereum specification.
This package is part of the Nethereum project and follows the same 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 3 NuGet packages that depend on Nethereum.Consensus.Ssz:
| Package | Downloads |
|---|---|
|
Nethereum.Beaconchain
Nethereum.Beaconchain - Ethereum Beacon Chain REST API client library for light client, state, and validator endpoints. |
|
|
Nethereum.Consensus.LightClient
Beacon light client orchestration (bootstrap, updates, trusted execution headers). |
|
|
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.