Skip to content

Commit

Permalink
Remove merkleizeSingleBuff
Browse files Browse the repository at this point in the history
Add more documentation

Add typeName to all containers

Add eth2 JSON casing

Rename merge to bellatrix

Fix missing renames of merge to bellatrix

Update ssz readme

Export byteArrayEquals

Add more BitArray functionality

Various fixes

Test type UX of allForks types

Abstract logic into BitArray and ByteArray

Add .equals functionality

Don't run allForks code

Rename receiptRoot

Update ssz docstrings

Multiple fixes

Remove non-existent variable

Add proof api

Fix int conversion in node.getUintBigint

Add Proofs functionality

Run Proof tests on ssz_static minimal

Add length node for proofs of lists

Require rootNode only when necessary in Array proofs

Fix rebase issues

Move Node navigation to functions

Add treeZeroAfterIndex fn

Add sliceTo method in CompositeList ViewDU

Use Uint64NumInf only where necessary

Add Eth1Block type

Use defaultView and defaultViewDU methods

Support BranchNodeStruct in proofs

Run proof tests

Post process proof nodes

Fix proof generation for all types

Fix ContainerNodeStructType proof unit test

Stop tracking complimentary benchmarks

Fix typo in Union maxSize

Add test for maxSize and minSize for all data structures

Export helper functions

Fix length param in Vector

Prevent setting values in ViewDU beyond length

Add more test cases

Improve ListBasicTreeViewDU.push logic

Fix Composite ViewDU commit nodes logic

Fix cache logic in ViewDU

Use type guards and simply set composite

Fix allForks test

Fix recursive call in BitArray

Ensure data consistency in ArrayCompositeViewDU

Better error in BitArray.set

FIx typo in populateAllNodes

Add more BitArray unit tests

TreeViewDU.commit should not return node

Better error messages in fromHexString

Auto-commit on ViewDU

Fix perf types

Fix proof generation for Validator type

Benchmark more eth2 ssz objects

Track raw cost of hashing vc list

Add more coverage WIP

More coverage WIP

Prevent pushing over length

More coverage

Ensure consistent mutability behaviour

Ensure correct mutability in ContainerNodeStruct

Move isViewMutable to CompositeType

Prevent keeping references for immutable views

Fix ContainerNodeStruct valid tests

Fix types in Uint constructor

Increase bitArray coverage

Guard against new BitList checks

Fix readVariableOffsetsArrayComposite
  • Loading branch information
dapplion committed Feb 12, 2022
1 parent fc3df93 commit 94f8749
Show file tree
Hide file tree
Showing 120 changed files with 5,927 additions and 1,795 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ jobs:
- name: Spec tests altair-mainnet
run: LODESTAR_PRESET=mainnet LODESTAR_FORK=altair ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests merge-minimal
run: LODESTAR_PRESET=minimal LODESTAR_FORK=merge ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
- name: Spec tests bellatrix-minimal
run: LODESTAR_PRESET=minimal LODESTAR_FORK=bellatrix ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
- name: Spec tests merge-mainnet
run: LODESTAR_PRESET=mainnet LODESTAR_FORK=merge ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
- name: Spec tests bellatrix-mainnet
run: LODESTAR_PRESET=mainnet LODESTAR_FORK=bellatrix ../../node_modules/.bin/mocha test/spec/ssz_static.test.ts
working-directory: packages/ssz
42 changes: 41 additions & 1 deletion packages/persistent-merkle-tree/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const gindex = BigInt(...);

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
const subtree: Tree = tree.getSubtree(gindex); // the Tree wrapping the Node at gindex. Updates to `subtree` will be propagated to `tree`

// A merkle proof for a gindex can be generated

Expand Down Expand Up @@ -115,6 +115,46 @@ Any update to a tree (either to a leaf or intermediate node) is performed as a r

A `Tree` object wraps `Node` and provides an API for tree navigation and transparent rebinding on updates.

#### Navigation by gindex or (depth, index)

Many tree methods allow navigation with a gindex. A gindex (or generalized index) describes a path through the tree, starting from the root and nagivating downwards.

```
1
/ \
2 3
/ \ / \
4 5 6 7
```

It can also be interpreted as a bitstring, starting with "1", then appending "0" for each navigation left, or "1" for each navigation right.

```
1
/ \
10 11
/ \ / \
100 101 110 111
```

Alternatively, several tree methods, with names ending with `AtDepth`, allow navigation by (depth, index). Depth and index navigation works by first navigating down levels into the tree from the top, starting at 0 (depth), and indexing nodes from the left, starting at 0 (index).

```
0 <- depth 0
/ \
0 1 <- depth 1
/ \ / \
0 1 2 3 <- depth 2
```

#### Memory efficiency

As an implementation detail, nodes hold their values as a `HashObject` (a javascript object with `h0`, ... `h7` uint32 `number` values) internally, rather than as a 32 byte `Uint8Array`. Memory benchmarking shows that this results in a ~3x reduction in memory over `Uint8Array`. This optimization allows this library to practically scale to trees with millions of nodes.

#### Navigation efficiency

In performance-critical applications performing many reads and writes to trees, being smart with tree navigation is crucial. This library correctly provides tree navigation methods that handle several important optimized cases: multi-node get and set, and get-then-set operations.

## See also:

https://github.com/protolambda/remerkleable
Expand Down
20 changes: 18 additions & 2 deletions packages/persistent-merkle-tree/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import {hashObjectToUint8Array, hashTwoObjects, uint8ArrayToHashObject} from "./

const TWO_POWER_32 = 2 ** 32;

/**
* An immutable binary merkle tree node
*/
export abstract class Node implements HashObject {
/**
* May be null. This is to save an extra variable to check if a node has a root or not
Expand All @@ -16,9 +19,13 @@ export abstract class Node implements HashObject {
h6: number;
h7: number;

/** The root hash of the node */
abstract root: Uint8Array;
/** The root hash of the node as a `HashObject` */
abstract rootHashObject: HashObject;
/** The left child node */
abstract left: Node;
/** The right child node */
abstract right: Node;

constructor(h0: number, h1: number, h2: number, h3: number, h4: number, h5: number, h6: number, h7: number) {
Expand All @@ -43,11 +50,17 @@ export abstract class Node implements HashObject {
this.h7 = root.h7;
}

/** Returns true if the node is a `LeafNode` */
abstract isLeaf(): boolean;
/** Returns a new node with a new left child and the existing right child */
abstract rebindLeft(left: Node): Node;
/** Returns a new node with a new right child and the existing left child */
abstract rebindRight(right: Node): Node;
}

/**
* An immutable binary merkle tree node that has a `left` and `right` child
*/
export class BranchNode extends Node {
constructor(private _left: Node, private _right: Node) {
// First null value is to save an extra variable to check if a node has a root or not
Expand Down Expand Up @@ -93,6 +106,9 @@ export class BranchNode extends Node {
}
}

/**
* An immutable binary merkle tree node that has no children
*/
export class LeafNode extends Node {
static fromRoot(root: Uint8Array): LeafNode {
return this.fromHashObject(uint8ArrayToHashObject(root));
Expand Down Expand Up @@ -214,15 +230,15 @@ export class LeafNode extends Node {

// number equals the h value
else if (uintBytes === 4) {
return BigInt(getNodeH(this, hIndex));
return BigInt(getNodeH(this, hIndex) >>> 0);
}

// number spans multiple h values
else {
const hRange = Math.ceil(uintBytes / 4);
let v = BigInt(0);
for (let i = 0; i < hRange; i++) {
v += BigInt(getNodeH(this, hIndex + i)) << BigInt(32 * i);
v += BigInt(getNodeH(this, hIndex + i) >>> 0) << BigInt(32 * i);
}
return v;
}
Expand Down
9 changes: 9 additions & 0 deletions packages/persistent-merkle-tree/src/proof/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@ export const ProofTypeSerialized = [
ProofType.treeOffset, // 1
];

/**
* A common merkle proof.
* A proof for a single leaf in a tree.
*/
export interface SingleProof {
type: ProofType.single;
gindex: Gindex;
leaf: Uint8Array;
witnesses: Uint8Array[];
}
/**
* A proof for possibly multiple leaves in a tree.
*
* See https://github.com/protolambda/eth-merkle-trees/blob/master/tree_offsets.md
*/
export interface TreeOffsetProof {
type: ProofType.treeOffset;
offsets: number[];
Expand Down
Loading

0 comments on commit 94f8749

Please sign in to comment.