### SHA256 Hashing with AssemblyScript Source: https://github.com/chainsafe/ssz/blob/master/packages/as-sha256/README.md Demonstrates how to use the as-sha256 library in AssemblyScript for SHA256 hashing. It shows both the one-pass `digest` and `digest64` functions, as well as the multi-pass `SHA256` class with `init`, `update`, and `final` methods. Requires installation via `pnpm add @chainsafe/as-sha256`. ```typescript import {digest, digest64, SHA256} from "@chainsafe/as-sha256"; let hash: Uint8Array; // create a new sha256 context const sha256 = new SHA256(); // with init(), update(data), and final() hash = sha256.init().update(Buffer.from("Hello world")).final(); // or use a one-pass interface hash = digest(Buffer.from("Hello world")); // or use a faster one-pass interface for hashing (only) 64 bytes hash = digest64(Buffer.from("abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh")); ``` -------------------------------- ### Tree View: Subview Behavior Example Source: https://github.com/chainsafe/ssz/blob/master/packages/ssz/README.md Illustrates the subview behavior of the Tree View. Assigning one subview to another does not link the views; mutations to a subview only affect the tree it's directly associated with. This differs from ViewDU. ```typescript const C = new ContainerType({ a: new VectorBasicType(new UintNumberType(1), 2), }); const c1 = C.toView({a: [0, 0]}); const c2 = C.toView({a: [1, 1]}); c1.a = c2.a; c1.a.set(0, 2); // This statement mutates ONLY c2 data c2.a.set(0, 3); ``` -------------------------------- ### Define and Use Integer Types (UintNumberType, UintBigintType) in SSZ (TypeScript) Source: https://context7.com/chainsafe/ssz/llms.txt Illustrates the usage of UintNumberType and UintBigintType for defining unsigned integer types in Chainsafe SSZ. It explains the difference between Number-based and BigInt-based types, their bit sizes, and provides an example of creating a BeaconBlockHeader container with mixed integer types. Includes examples for serialization, deserialization, and merkle root calculation of integers. ```typescript import { UintNumberType, UintBigintType, ContainerType, ByteVectorType } from "@chainsafe/ssz"; // Number-based types (faster, for values < 2^53) const uint8 = new UintNumberType(1); // 8-bit const uint16 = new UintNumberType(2); // 16-bit const uint32 = new UintNumberType(4); // 32-bit const uint64Num = new UintNumberType(8, { clipInfinity: true }); // 64-bit with Infinity support // BigInt-based types (for large values) const uint64 = new UintBigintType(8); // 64-bit const uint128 = new UintBigintType(16); // 128-bit const uint256 = new UintBigintType(32); // 256-bit (for hashes, etc.) // Example: Block header with mixed uint types const BeaconBlockHeader = new ContainerType({ slot: new UintBigintType(8), proposerIndex: new UintBigintType(8), parentRoot: new ByteVectorType(32), stateRoot: new ByteVectorType(32), bodyRoot: new ByteVectorType(32), }); // Serialize/deserialize integers const slotBytes = uint64.serialize(BigInt(12345678)); const slotValue = uint64.deserialize(slotBytes); console.log(slotValue); // 12345678n // Merkle root of a single uint const slotRoot = uint64.hashTreeRoot(BigInt(12345678)); ``` -------------------------------- ### Tree ViewDU: Subview Behavior Example Source: https://github.com/chainsafe/ssz/blob/master/packages/ssz/README.md Demonstrates the subview behavior of Tree ViewDU. Assigning a subview from one ViewDU to another links them, meaning mutations to the shared subview affect both original views. This is a key difference from the standard Tree View. ```typescript const C = new ContainerType({ a: new VectorBasicType(new UintNumberType(1), 2), }); const c1 = C.toViewDU({a: [0, 0]}); const c2 = C.toViewDU({a: [1, 1]}); c1.a = c2.a; c1.a.set(0, 2); // This statement mutates c1 AND c2 data c2.a.set(0, 3); ``` -------------------------------- ### ListBasicType and ListCompositeType - Variable-Length Lists in TypeScript Source: https://context7.com/chainsafe/ssz/llms.txt Demonstrates the usage of ListBasicType for basic data types and ListCompositeType for composite data types in TypeScript. These types represent ordered, variable-length homogeneous collections with a maximum limit. Includes examples of serialization, deserialization, and root hashing. ```typescript import { ListBasicType, ListCompositeType, UintBigintType, ContainerType, ByteVectorType, BooleanType } from "@chainsafe/ssz"; // List of uint64 balances with max 2^40 validators const Balances = new ListBasicType(new UintBigintType(8), 1099511627776); // Create and manipulate list values const balances = [BigInt(32000000000), BigInt(31500000000), BigInt(32100000000)]; const serializedBalances = Balances.serialize(balances); const deserializedBalances = Balances.deserialize(serializedBalances); const balancesRoot = Balances.hashTreeRoot(balances); // List of composite types (e.g., validators) const Validator = new ContainerType({ pubkey: new ByteVectorType(48), withdrawalCredentials: new ByteVectorType(32), effectiveBalance: new UintBigintType(8), slashed: new BooleanType(), activationEligibilityEpoch: new UintBigintType(8), activationEpoch: new UintBigintType(8), exitEpoch: new UintBigintType(8), withdrawableEpoch: new UintBigintType(8), }); const Validators = new ListCompositeType(Validator, 1099511627776); // Working with validator lists const validators = [ Validator.defaultValue(), Validator.defaultValue(), ]; validators[0].effectiveBalance = BigInt(32000000000); validators[1].effectiveBalance = BigInt(31000000000); const validatorsRoot = Validators.hashTreeRoot(validators); const validatorsSerialized = Validators.serialize(validators); ``` -------------------------------- ### ByteVectorType and ByteListType - Binary Data Handling in TypeScript Source: https://context7.com/chainsafe/ssz/llms.txt Explains the ByteVectorType for fixed-length byte arrays and ByteListType for variable-length byte arrays with a maximum limit in TypeScript. Includes examples for common types like hashes, public keys, and signatures, along with serialization, deserialization, and root hashing. ```typescript import { ByteVectorType, ByteListType, ContainerType } from "@chainsafe/ssz"; // Fixed 32-byte hash const Root = new ByteVectorType(32); // Fixed 48-byte BLS public key const BLSPubkey = new ByteVectorType(48); // Fixed 96-byte BLS signature const BLSSignature = new ByteVectorType(96); // Variable-length bytes (e.g., transaction data) const Transaction = new ByteListType(1073741824); // Max 1GB // Example: Deposit data const DepositData = new ContainerType({ pubkey: new ByteVectorType(48), withdrawalCredentials: new ByteVectorType(32), amount: new UintBigintType(8), signature: new ByteVectorType(96), }); // Serialize/deserialize const pubkey = new Uint8Array(48).fill(0xab); const serialized = BLSPubkey.serialize(pubkey); const deserialized = BLSPubkey.deserialize(serialized); const root = BLSPubkey.hashTreeRoot(pubkey); ``` -------------------------------- ### VectorBasicType and VectorCompositeType - Fixed-Length Arrays in TypeScript Source: https://context7.com/chainsafe/ssz/llms.txt Illustrates the use of VectorBasicType and VectorCompositeType for fixed-length arrays in TypeScript. Vectors are homogeneous collections with a known size at compile time. Examples cover basic and composite types, including their serialization and Merkle root calculation. ```typescript import { VectorBasicType, VectorCompositeType, UintBigintType, ByteVectorType, ContainerType } from "@chainsafe/ssz"; // Fixed-size vector of 4 uint64 values const HistoricalRoots = new VectorBasicType(new UintBigintType(8), 4); // Vector of 32-byte roots (common in Ethereum) const StateRoots = new VectorCompositeType(new ByteVectorType(32), 8192); // Usage const roots = new Array(8192).fill(new Uint8Array(32)); const serialized = StateRoots.serialize(roots); const root = StateRoots.hashTreeRoot(roots); // Vector inside a container const HistoricalBatch = new ContainerType({ blockRoots: new VectorCompositeType(new ByteVectorType(32), 8192), stateRoots: new VectorCompositeType(new ByteVectorType(32), 8192), }); ``` -------------------------------- ### Tree View: SSZ Operations, Getters, and Setters Source: https://github.com/chainsafe/ssz/blob/master/packages/ssz/README.md Demonstrates the creation and usage of a Tree View for SSZ operations, including serialization, hash root calculation, property access via getters, and immediate updates via setters. Changes are propagated upwards to the root parent tree. ```typescript const C = new ContainerType({ a: new VectorBasicType(new UintNumberType(1), 2), }); const c = C.defaultView(); c.serialize() === C.hashTreeRoot(C.defaultValue()); const root = c.hashTreeRoot(); c.a.get(0) === 0; c.a.set(0, 1); assert(root.toString() !== c.hashTreeRoot().toString()); ``` -------------------------------- ### Generate and Verify Merkle Proofs with Chainsafe Persistent Merkle Tree Source: https://context7.com/chainsafe/ssz/llms.txt Demonstrates how to create, generate, and verify various types of Merkle proofs (single, multi, tree offset, compact multi) for tree nodes using the @chainsafe/persistent-merkle-tree library. It covers creating a tree, adding nodes, generating proofs, and reconstructing partial trees from proofs. ```typescript import { Tree, zeroNode, LeafNode, ProofType, createProof, createNodeFromProof, Proof } from "@chainsafe/persistent-merkle-tree"; // Create a tree with some data const tree = new Tree(zeroNode(4)); tree.setNode(BigInt(16), LeafNode.fromRoot(new Uint8Array(32).fill(0xaa))); tree.setNode(BigInt(17), LeafNode.fromRoot(new Uint8Array(32).fill(0xbb))); tree.setNode(BigInt(18), LeafNode.fromRoot(new Uint8Array(32).fill(0xcc))); // Generate single proof for one gindex const singleProofWitnesses: Uint8Array[] = tree.getSingleProof(BigInt(16)); // Generate multi-proof for multiple gindices const gindices = [BigInt(16), BigInt(17), BigInt(18)]; const multiProof: Proof = tree.getProof({ type: ProofType.treeOffset, gindices, }); // Recreate partial tree from proof const partialTree: Tree = Tree.createFromProof(multiProof); // Verified nodes can be accessed const provenNode = partialTree.getNode(BigInt(16)); console.log(provenNode.root); // Uint8Array [0xaa, 0xaa, ...] // Accessing unproven nodes throws error try { partialTree.getNode(BigInt(20)); // throws - not in proof } catch (e) { console.log("Node not in proof"); } // Tree offset proof (efficient multi-proof format) const treeOffsetProof = tree.getProof({ type: ProofType.treeOffset, gindices: [BigInt(16), BigInt(18)], }); // Compact multi-proof const compactProof = tree.getProof({ type: ProofType.compactMulti, descriptor: new Uint8Array([/* descriptor bytes */]), }); ``` -------------------------------- ### Tree ViewDU: SSZ Operations, Getters, and Setters Source: https://github.com/chainsafe/ssz/blob/master/packages/ssz/README.md Shows how to use Tree ViewDU for deferred updates. SSZ operations and getters work similarly to Tree View, but setters do not immediately update the tree. Changes are only reflected after calling the `commit` method. ```typescript const C = new ContainerType({ a: new VectorBasicType(new UintNumberType(1), 2), }); const c = C.defaultViewDU(); c.serialize() === C.hashTreeRoot(C.defaultValue()); const root = c.hashTreeRoot(); c.a.get(0) === 0; c.a.set(0, 1); assert(root.toString() === c.hashTreeRoot().toString()); c.commit(); assert(root.toString() !== c.hashTreeRoot().toString()); ``` -------------------------------- ### Define and Use SSZ Keypair Type in TypeScript Source: https://github.com/chainsafe/ssz/blob/master/packages/ssz/README.md Demonstrates how to define a custom SSZ data type 'Keypair' using ContainerType and ByteVectorType. It shows how to derive the TypeScript type, create default values, serialize, deserialize, generate merkle roots, clone, convert to/from JSON, and create tree-backed views. ```typescript import {ContainerType, ByteVectorType, ValueOf, TreeBacked} from "@chainsafe/ssz"; // Creates a "Keypair" SSZ data type (a private key of 32 bytes, a public key of 48 bytes) const Keypair = new ContainerType({ privateKey: new ByteVectorType(32), publicKey: new ByteVectorType(48), }, ); // The type value of any SSZ types are derived with `ValueOf` type KeypairType = ValueOf // Now you can perform different operations on Keypair objects const keypair = Keypair.defaultValue(); // Create a default Keypair console.log(keypair.privateKey); // => Uint8Array [0,0,0,...], length 32 console.log(keypair.publicKey); // => Uint8Array [0,0,0, ...], length 48 // serialize the object to a byte array const serialized: Uint8Array = Keypair.serialize(keypair); console.log("Serialized:", serialized); // get the merkle root of the object const root: Uint8Array = Keypair.hashTreeRoot(keypair); console.log("Merkle Root:", root); // create a copy of the object const keypair2: KeypairType = Keypair.clone(keypair); console.log("Cloned Keypair:", keypair2); // deserialize a serialized object const keypair3: KeypairType = Keypair.deserialize(serialized); console.log("Deserialized Keypair:", keypair3); // Convert to JSON-serializable representation // (binary data is converted to hex strings) const keypairJSON = Keypair.toJson(keypair); console.log("Keypair JSON:", keypairJSON); const keypairJSONStr = JSON.stringify(keypairJSON); console.log("Keypair JSON String:", keypairJSONStr); // convert the json-serializable representation to the object const keypair4: KeypairType = Keypair.fromJson(keypairJSON); console.log("Keypair from JSON:", keypair4); // The merkle-tree-backed representation of a Keypair may be created / operated on const keypairView: TreeBacked = Keypair.toView(keypair) // All of the same operations can be performed on tree-backed values console.log("Serialized from View:", keypairView.serialize()); console.log("Merkle Root from View:", keypairView.hashTreeRoot()); ``` -------------------------------- ### Generate and Recreate Merkle Proofs Source: https://github.com/chainsafe/ssz/blob/master/packages/persistent-merkle-tree/README.md Shows how to generate single and multiple proofs for a Merkle Tree using `getSingleProof` and `getProof`. It also demonstrates recreating a `Tree` object from a proof using `Tree.createFromProof` and the implications of navigating unproven nodes. ```typescript import {Tree, ProofType} from "@chainsafe/persistent-merkle-tree"; const gindex = BigInt(1); const tree = new Tree(zeroNode(10)); // Assuming tree is initialized // A merkle proof for a gindex can be generated const proof: Uint8Array[] = tree.getSingleProof(gindex); // Multiple types of proofs are supported through the `getProof` interface // For example, a multiproof for multiple gindices can be generated like so const gindices: BigInt[] = [BigInt(2), BigInt(3)]; const proofMulti: Proof = tree.getProof({ type: ProofType.treeOffset, gindices, }); // `Proof` objects can be used to recreate `Tree` objects // These `Tree` objects can be navigated as usual for all nodes contained in the proof // Navigating to unknown/unproven nodes results in an error const partialTree: Tree = Tree.createFromProof(proofMulti); const unknownGindex: BigInt = BigInt(4); // gindices.includes(unknownGindex) // false // partialTree.getRoot(unknownGindex) // throws ``` -------------------------------- ### Create and Navigate Persistent Merkle Tree Nodes Source: https://github.com/chainsafe/ssz/blob/master/packages/persistent-merkle-tree/README.md Demonstrates the creation of LeafNode and BranchNode instances, accessing their root hashes, and checking node types. It also shows how to utilize pre-defined zero nodes for initializing tree structures. ```typescript import {LeafNode, BranchNode, zeroNode} from "@chainsafe/persistent-merkle-tree"; const leaf = LeafNode.fromRoot(Buffer.alloc(32, 0xaa)); const otherLeaf = LeafNode.fromRoot(Buffer.alloc(32, 0xbb)); const branch = new BranchNode(leaf, otherLeaf); // The `root` property returns the merkle root of a Node // this is equal to `hash(leaf.root, otherLeaf.root));` const r: Uint8Array = branch.root; // The `isLeaf` method returns true if the Node is a LeafNode branch.isLeaf() === false; leaf.isLeaf() === true; // Well-known zero nodes are provided // 0x0 const zero0 = zeroNode(0); // hash(0, 0) const zero1 = zeroNode(1); // hash(hash(0, 0), hash(0, 0)) const zero2 = zeroNode(2); ``` -------------------------------- ### Deferred Updates with TreeViewDU Source: https://context7.com/chainsafe/ssz/llms.txt Explains TreeViewDU (Deferred Update View) for efficient batch updates on large Merkle trees. Mutations are cached and applied only upon calling `commit()`, optimizing performance for frequent changes. ```typescript import { ContainerType, ListBasicType, UintBigintType } from "@chainsafe/ssz"; const BeaconState = new ContainerType({ slot: new UintBigintType(8), balances: new ListBasicType(new UintBigintType(8), 1099511627776), // ... other fields }); // Create ViewDU from default const state = BeaconState.defaultViewDU(); // Make multiple mutations (NOT applied to tree yet) state.slot = BigInt(12345); state.balances.push(BigInt(32000000000)); state.balances.push(BigInt(31500000000)); state.balances.set(0, BigInt(32100000000)); // Root still reflects OLD state until commit const oldRoot = state.hashTreeRoot(); // Commit all changes to tree at once state.commit(); // Now root reflects all changes const newRoot = state.hashTreeRoot(); // Clone ViewDU (shares tree structure, changes are independent) const stateCopy = state.clone(); stateCopy.slot = BigInt(12346); stateCopy.commit(); // Original state unchanged console.log(state.slot); // 12345n console.log(stateCopy.slot); // 12346n ``` -------------------------------- ### Manage Tree with Mutable Wrapper and Gindex Navigation Source: https://github.com/chainsafe/ssz/blob/master/packages/persistent-merkle-tree/README.md Illustrates the usage of the `Tree` wrapper for managing the persistent Merkle Tree. It covers accessing the root node and merkle root, navigating the tree using `Gindex`, retrieving nodes and their roots at specific indices, and creating subtrees. ```typescript import {Tree, Node, zeroNode} from "@chainsafe/persistent-merkle-tree"; const tree = new Tree(zeroNode(10)); // `rootNode` property returns the root Node of a Tree const rootNode: Node = tree.rootNode; // `root` property returns the merkle root of a Tree const rr: Uint8Array = tree.root; // A Tree is navigated by Gindex const gindex = BigInt(1); const n: Node = tree.getNode(gindex); // the Node at gindex const rrr: Uint8Array = tree.getRoot(gindex); // the Uint8Array root at gindex const subtree: Tree = tree.getSubtree(gindex); // the Tree wrapping the Node at gindex. Updates to `subtree` will be propagated to `tree` ``` -------------------------------- ### Navigate Subtrees and Maintain Parent-Child Updates with Chainsafe Persistent Merkle Tree Source: https://context7.com/chainsafe/ssz/llms.txt Illustrates how to navigate to subtrees within a Merkle tree and maintain parent-child update relationships using the @chainsafe/persistent-merkle-tree library. It shows how updates to a subtree automatically propagate to its parent and how to efficiently retrieve nodes at a specific depth. ```typescript import { Tree, zeroNode, LeafNode } from "@chainsafe/persistent-merkle-tree"; // Create parent tree const parentTree = new Tree(zeroNode(8)); // Get subtree at gindex (maintains hook to parent) const subtree = parentTree.getSubtree(BigInt(4)); // Updates to subtree propagate to parent const newLeaf = LeafNode.fromRoot(new Uint8Array(32).fill(0xdd)); subtree.setNode(BigInt(2), newLeaf); // Parent tree is automatically updated console.log(parentTree.root); // Reflects subtree change // Batch operations at depth const nodes = parentTree.getNodesAtDepth(3, 0, 8); // Get 8 nodes at depth 3 // Iterate nodes efficiently for (const node of parentTree.iterateNodesAtDepth(3, 0, 8)) { console.log(node.root); } ``` -------------------------------- ### Optimized SHA256 Hashing with Chainsafe AS-SHA256 Source: https://context7.com/chainsafe/ssz/llms.txt Showcases the use of the @chainsafe/as-sha256 package for WebAssembly-accelerated SHA256 hashing, optimized for Merkle tree operations. It covers general-purpose hashing, optimized 64-byte hashing, direct hashing of two 32-byte values, batch hashing, hashing into a preallocated buffer, and streaming hashing. ```typescript import { digest, digest64, digest2Bytes32, batchHash4UintArray64s, hashInto, SHA256 } from "@chainsafe/as-sha256"; // General-purpose hash const data = Buffer.from("Hello, Ethereum!"); const hash: Uint8Array = digest(data); // Optimized 64-byte hash (for merkle tree internal nodes) const left = new Uint8Array(32).fill(0xaa); const right = new Uint8Array(32).fill(0xbb); const combined = new Uint8Array(64); combined.set(left, 0); combined.set(right, 32); const parentHash = digest64(combined); // Hash two 32-byte values directly const parentHash2 = digest2Bytes32(left, right); // Batch hash 4 pairs in parallel (SIMD optimized) const inputs = [ new Uint8Array(64).fill(0x11), new Uint8Array(64).fill(0x22), new Uint8Array(64).fill(0x33), new Uint8Array(64).fill(0x44), ]; const outputs = batchHash4UintArray64s(inputs); // outputs[0] = hash(inputs[0]) // outputs[1] = hash(inputs[1]) // ... // Hash into preallocated output buffer const input = new Uint8Array(256); // Must be multiple of 64 const output = new Uint8Array(128); // Half the size hashInto(input, output); // Streaming hash with context const sha256 = new SHA256(); const streamHash = sha256 .init() .update(Buffer.from("Part 1")) .update(Buffer.from("Part 2")) .final(); ``` -------------------------------- ### Immutable Singly Linked List with Chainsafe Persistent TS Source: https://context7.com/chainsafe/ssz/llms.txt Demonstrates the usage of an immutable singly linked list implementation from the @chainsafe/persistent-ts library. It covers creating lists, prepending elements (an O(1) operation that creates a new list), accessing the head and tail, taking and dropping elements, converting to an array, and checking for emptiness. ```typescript import { List } from "@chainsafe/persistent-ts"; // Create empty list const empty = List.empty(); // Create from values const list = List.of(1, 2, 3, 4); // Prepend (O(1) - creates new node referencing old list) const withZero = list.prepend(0); // (0, 1, 2, 3, 4) // Head and tail const first = list.head(); // 1 const rest = list.tail(); // (2, 3, 4) // Take and drop const firstThree = list.take(3); // (1, 2, 3) const lastTwo = list.drop(2); // (3, 4) // Convert to array const arr = [...list]; // [1, 2, 3, 4] // Check if empty console.log(empty.isEmpty()); // true console.log(list.isEmpty()); // false ``` -------------------------------- ### Mutable Merkle Tree Access with TreeView Source: https://context7.com/chainsafe/ssz/llms.txt Illustrates using TreeView for immediate, mutable access to tree-backed data structures. Changes made via TreeView are instantly committed to the underlying Merkle tree. ```typescript import { ContainerType, UintNumberType, VectorBasicType } from "@chainsafe/ssz"; const MyContainer = new ContainerType({ a: new VectorBasicType(new UintNumberType(1), 2), b: new UintNumberType(4), }); // Create tree view from default value const view = MyContainer.defaultView(); // Read values console.log(view.a.get(0)); // 0 console.log(view.b); // 0 // Write values (immediately committed) view.a.set(0, 100); view.b = 42; // Get merkle root (reflects changes immediately) const root1 = view.hashTreeRoot(); // Serialize const serialized = view.serialize(); // Create view from existing value const view2 = MyContainer.toView({ a: [1, 2], b: 99 }); ``` -------------------------------- ### Ethereum Beacon State Definition and Usage with @chainsafe/ssz Source: https://context7.com/chainsafe/ssz/llms.txt Illustrates the definition of Ethereum's BeaconState structure using the Chainsafe SSZ type system. It demonstrates how to create, modify, serialize, and compute the Merkle root of the state, showcasing the library's capabilities for complex data structures. ```typescript import { ContainerType, ListBasicType, ListCompositeType, VectorCompositeType, ByteVectorType, UintBigintType, UintNumberType, BooleanType, BitVectorType, ValueOf, toHexString } from "@chainsafe/ssz"; // Define Ethereum consensus types const BLSPubkey = new ByteVectorType(48); const Root = new ByteVectorType(32); const Epoch = new UintBigintType(8); const Slot = new UintBigintType(8); const Gwei = new UintBigintType(8); const ValidatorIndex = new UintBigintType(8); const Validator = new ContainerType({ pubkey: BLSPubkey, withdrawalCredentials: Root, effectiveBalance: Gwei, slashed: new BooleanType(), activationEligibilityEpoch: Epoch, activationEpoch: Epoch, exitEpoch: Epoch, withdrawableEpoch: Epoch, }, { typeName: "Validator" }); const VALIDATOR_REGISTRY_LIMIT = 1099511627776; const EPOCHS_PER_HISTORICAL_VECTOR = 65536; const SLOTS_PER_HISTORICAL_ROOT = 8192; const BeaconState = new ContainerType({ genesisTime: new UintBigintType(8), genesisValidatorsRoot: Root, slot: Slot, // Fork data, etc... blockRoots: new VectorCompositeType(Root, SLOTS_PER_HISTORICAL_ROOT), stateRoots: new VectorCompositeType(Root, SLOTS_PER_HISTORICAL_ROOT), validators: new ListCompositeType(Validator, VALIDATOR_REGISTRY_LIMIT), balances: new ListBasicType(Gwei, VALIDATOR_REGISTRY_LIMIT), }, { typeName: "BeaconState" }); // Usage with ViewDU for optimal performance const state = BeaconState.defaultViewDU(); // Efficiently modify state state.slot = BigInt(1000000); state.genesisTime = BigInt(1606824023); // Add validators for (let i = 0; i < 100; i++) { const validator = Validator.defaultValue(); validator.effectiveBalance = BigInt(32000000000); validator.activationEpoch = BigInt(0); state.validators.push(validator); state.balances.push(BigInt(32000000000)); } // Commit all changes state.commit(); // Get state root const stateRoot = state.hashTreeRoot(); console.log("State root:", toHexString(stateRoot)); // Serialize entire state const serialized = state.serialize(); console.log("Serialized size:", serialized.length, "bytes"); // Create proof for specific validator const validatorGindex = state.type.getPropertyGindex("validators"); console.log("Validators gindex:", validatorGindex); ``` -------------------------------- ### Define and Use ContainerType for Structured Data in SSZ (TypeScript) Source: https://context7.com/chainsafe/ssz/llms.txt Demonstrates how to define complex structured data types like Keypair and Validator using ContainerType in Chainsafe SSZ. It covers defining nested types, specifying data formats (ByteVector, UintBigint, Boolean), configuring JSON casing, and performing common operations like defaultValue, serialize, deserialize, hashTreeRoot, clone, toJson, and fromJson. ```typescript import { ContainerType, ByteVectorType, UintNumberType, UintBigintType, ListBasicType, BooleanType, ValueOf } from "@chainsafe/ssz"; // Define a Keypair type with 32-byte private key and 48-byte public key const Keypair = new ContainerType({ privateKey: new ByteVectorType(32), publicKey: new ByteVectorType(48), }); // Define a Validator type matching Ethereum consensus spec const Validator = new ContainerType({ pubkey: new ByteVectorType(48), withdrawalCredentials: new ByteVectorType(32), effectiveBalance: new UintBigintType(8), slashed: new BooleanType(), activationEligibilityEpoch: new UintBigintType(8), activationEpoch: new UintBigintType(8), exitEpoch: new UintBigintType(8), withdrawableEpoch: new UintBigintType(8), }, { typeName: "Validator", jsonCase: "snake", // JSON keys use snake_case }); // TypeScript type inference type ValidatorType = ValueOf; // Create default value const validator: ValidatorType = Validator.defaultValue(); // validator.pubkey => Uint8Array [0,0,0,...], length 48 // validator.effectiveBalance => 0n // Serialize to bytes const serialized: Uint8Array = Validator.serialize(validator); // Deserialize from bytes const deserialized: ValidatorType = Validator.deserialize(serialized); // Get merkle root (hash tree root) const root: Uint8Array = Validator.hashTreeRoot(validator); // Clone value const validatorCopy: ValidatorType = Validator.clone(validator); // JSON conversion const jsonObj = Validator.toJson(validator); const fromJson: ValidatorType = Validator.fromJson(jsonObj); ``` -------------------------------- ### List Operations in TypeScript Source: https://github.com/chainsafe/ssz/blob/master/packages/persistent-ts/README.md Demonstrates common operations for the immutable List data structure, such as creating empty lists, creating lists from values, prepending elements, accessing head/tail, taking/dropping elements, and converting to an array. Uses TypeScript generics for type safety. ```typescript import { List } from "@chainsafe/persistent-ts"; // () List.empty(); // (1, 2, 3, 4) List.of(1, 2, 3, 4); // (1, 2) List.of(1).prepend(2); // 1 List.of(1, 2, 3).head(); // (2, 3) List.of(1, 2, 3).tail(); // (1, 2, 3) List.of(1, 2, 3, 4).take(3); // (4) List.of(1, 2, 3, 4).drop(3); //[1, 2, 3, 4] [...List.of(1, 2, 3, 4)]; ``` -------------------------------- ### Immutable Vector Operations with @chainsafe/persistent-ts Source: https://context7.com/chainsafe/ssz/llms.txt Demonstrates the creation and manipulation of immutable vectors using the Vector class from the @chainsafe/persistent-ts library. Supports operations like append, pop, random access, and updates, all while maintaining immutability and efficient structural sharing. ```typescript import { Vector } from "@chainsafe/persistent-ts"; // Create empty vector const empty = Vector.empty(); // Create from array const vec = Vector.from([1, 2, 3, 4, 5]); // Append (creates new vector sharing structure) const vec2 = vec.append(6); // [1, 2, 3, 4, 5, 6] // Pop last element const vec3 = vec.pop(); // [1, 2, 3, 4] // Random access (O(log n)) const third = vec.get(2); // 3 // Update at index (O(log n), returns new vector) const updated = vec.set(2, 100); // [1, 2, 100, 4, 5] // Original unchanged (immutable) console.log(vec.get(2)); // 3 // Iterate for (const value of vec) { console.log(value); } ``` -------------------------------- ### Define Optional and Union Types with SSZ Source: https://context7.com/chainsafe/ssz/llms.txt Demonstrates how to define OptionalType for nullable fields and UnionType for sum types using Chainsafe SSZ. These types are crucial for representing data with varying structures or optional presence. ```typescript import { UnionType, OptionalType, NoneType, ContainerType, UintBigintType, ByteVectorType } from "@chainsafe/ssz"; // Optional field (null or value) const OptionalExecutionPayload = new OptionalType( new ContainerType({ blockHash: new ByteVectorType(32), gasLimit: new UintBigintType(8), }) ); // Usage const withPayload = { blockHash: new Uint8Array(32), gasLimit: BigInt(30000000) }; const withoutPayload = null; const serialized1 = OptionalExecutionPayload.serialize(withPayload); const serialized2 = OptionalExecutionPayload.serialize(withoutPayload); // Union type (one of multiple types) const ExecutionPayload = new ContainerType({ blockHash: new ByteVectorType(32), }); const BlindedPayload = new ContainerType({ blockRoot: new ByteVectorType(32), }); const PayloadUnion = new UnionType([ new NoneType(), ExecutionPayload, BlindedPayload, ]); // Value is { selector: number, value: ... } const unionValue = { selector: 1, value: { blockHash: new Uint8Array(32) } }; const serialized = PayloadUnion.serialize(unionValue); ``` -------------------------------- ### Hex String and Byte Array Utilities with @chainsafe/ssz Source: https://context7.com/chainsafe/ssz/llms.txt Provides utility functions for converting between byte arrays and hexadecimal strings, as well as comparing byte arrays for equality. These functions are essential for handling data in Ethereum consensus applications. ```typescript import { toHexString, fromHexString, byteArrayEquals } from "@chainsafe/ssz"; // Convert bytes to hex const bytes = new Uint8Array([0xde, 0xad, 0xbe, 0xef]); const hex = toHexString(bytes); // "0xdeadbeef" // Convert hex to bytes const bytes2 = fromHexString("0xdeadbeef"); const bytes3 = fromHexString("deadbeef"); // Also valid without 0x // Compare byte arrays const equal = byteArrayEquals(bytes, bytes2); // true ``` -------------------------------- ### Immutable Merkle Tree Operations Source: https://context7.com/chainsafe/ssz/llms.txt Details operations on the persistent Merkle tree, emphasizing its immutable nature and structural sharing. Updates result in new trees, preserving previous states and optimizing memory usage. ```typescript import { LeafNode, BranchNode, Tree, zeroNode, getNode, setNode } from "@chainsafe/persistent-merkle-tree"; // Create leaf nodes const leaf1 = LeafNode.fromRoot(new Uint8Array(32).fill(0xaa)); const leaf2 = LeafNode.fromRoot(new Uint8Array(32).fill(0xbb)); // Create branch node (parent of two leaves) const branch = new BranchNode(leaf1, leaf2); // Get merkle root (computed lazily and cached) const root: Uint8Array = branch.root; // Check node type console.log(branch.isLeaf()); // false console.log(leaf1.isLeaf()); // true // Well-known zero nodes (cached) const zero0 = zeroNode(0); // 32 zero bytes const zero1 = zeroNode(1); // hash(zero0, zero0) const zero2 = zeroNode(2); // hash(zero1, zero1) // Create mutable Tree wrapper const tree = new Tree(zeroNode(10)); // Tree of depth 10 // Navigate by gindex (generalized index) const gindex = BigInt(1024); // Navigate to specific leaf const node = tree.getNode(gindex); const nodeRoot = tree.getRoot(gindex); // Set node (creates new tree with structural sharing) const newLeaf = LeafNode.fromRoot(new Uint8Array(32).fill(0xff)); tree.setNode(gindex, newLeaf); // Get/set by depth and index const nodeAtDepth = tree.getNodeAtDepth(5, 16); tree.setNodeAtDepth(5, 16, newLeaf); ``` -------------------------------- ### Vector Operations in TypeScript Source: https://github.com/chainsafe/ssz/blob/master/packages/persistent-ts/README.md Illustrates fundamental operations for the immutable Vector data structure, including creating empty vectors, appending elements, removing the last element (pop), accessing elements by index, and updating elements at a specific index. Leverages TypeScript generics. ```typescript import { Vector } from "@chainsafe/persistent-ts"; // [] Vector.empty(); // [1] Vector.empty().append(1); // [] Vector.from([1]).pop(); // 3 Vector.from([1, 2, 3]).get(2); // [1, 2, 100] Vector.from([1, 2, 3]).set(2, 100); ``` -------------------------------- ### BitListType and BitVectorType - Bit Array Operations in TypeScript Source: https://context7.com/chainsafe/ssz/llms.txt Details the BitListType for variable-length bit lists and BitVectorType for fixed-length bit vectors in TypeScript. These are commonly used for attestation aggregation bits. Demonstrates creating BitArrays, serialization, deserialization, bit manipulation, and Merkle root calculation. ```typescript import { BitListType, BitVectorType, BitArray } from "@chainsafe/ssz"; // Variable-length bit list (e.g., aggregation bits) const MAX_VALIDATORS_PER_COMMITTEE = 2048; const AggregationBits = new BitListType(MAX_VALIDATORS_PER_COMMITTEE); // Fixed-length bit vector const SyncCommitteeBits = new BitVectorType(512); // Create BitArray from boolean array const bits = BitArray.fromBoolArray([true, false, true, true, false]); // Or from single bits const aggregationBits = BitArray.fromSingleBit(2048, 5); // Bit 5 is set // Serialize/deserialize const serialized = AggregationBits.serialize(bits); const deserialized = AggregationBits.deserialize(serialized); // Get/set individual bits console.log(bits.get(0)); // true console.log(bits.get(1)); // false // Count set bits console.log(bits.getTrueBitIndexes()); // [0, 2, 3] // Merkle root const root = AggregationBits.hashTreeRoot(bits); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.