@aztec/foundation 0.69.1 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/abi/note_selector.d.ts +5 -2
- package/dest/abi/note_selector.d.ts.map +1 -1
- package/dest/abi/note_selector.js +12 -4
- package/dest/collection/array.d.ts +8 -0
- package/dest/collection/array.d.ts.map +1 -1
- package/dest/collection/array.js +28 -1
- package/dest/config/env_var.d.ts +1 -1
- package/dest/config/env_var.d.ts.map +1 -1
- package/dest/json-rpc/server/safe_json_rpc_server.d.ts +7 -2
- package/dest/json-rpc/server/safe_json_rpc_server.d.ts.map +1 -1
- package/dest/json-rpc/server/safe_json_rpc_server.js +18 -16
- package/dest/log/pino-logger.d.ts +3 -0
- package/dest/log/pino-logger.d.ts.map +1 -1
- package/dest/log/pino-logger.js +14 -2
- package/dest/promise/running-promise.d.ts.map +1 -1
- package/dest/promise/running-promise.js +9 -1
- package/dest/queue/serial_queue.d.ts +1 -0
- package/dest/queue/serial_queue.d.ts.map +1 -1
- package/dest/queue/serial_queue.js +6 -1
- package/dest/testing/files/index.d.ts +2 -1
- package/dest/testing/files/index.d.ts.map +1 -1
- package/dest/testing/files/index.js +6 -2
- package/dest/trees/index.d.ts +2 -1
- package/dest/trees/index.d.ts.map +1 -1
- package/dest/trees/index.js +3 -2
- package/dest/trees/{unbalanced_merkle_root.d.ts → unbalanced_merkle_tree.d.ts} +6 -2
- package/dest/trees/unbalanced_merkle_tree.d.ts.map +1 -0
- package/dest/trees/{unbalanced_merkle_root.js → unbalanced_merkle_tree.js} +40 -2
- package/dest/trees/unbalanced_tree_store.d.ts +19 -0
- package/dest/trees/unbalanced_tree_store.d.ts.map +1 -0
- package/dest/trees/unbalanced_tree_store.js +80 -0
- package/package.json +2 -2
- package/src/abi/note_selector.ts +11 -4
- package/src/collection/array.ts +31 -0
- package/src/config/env_var.ts +5 -2
- package/src/json-rpc/server/safe_json_rpc_server.ts +22 -15
- package/src/log/pino-logger.ts +19 -2
- package/src/promise/running-promise.ts +8 -0
- package/src/queue/serial_queue.ts +5 -0
- package/src/testing/files/index.ts +6 -1
- package/src/trees/index.ts +2 -1
- package/src/trees/unbalanced_merkle_tree.ts +103 -0
- package/src/trees/unbalanced_tree_store.ts +102 -0
- package/dest/trees/unbalanced_merkle_root.d.ts.map +0 -1
- package/src/trees/unbalanced_merkle_root.ts +0 -52
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
2
|
+
import { sha256Trunc } from '@aztec/foundation/crypto';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Computes the merkle root for an unbalanced tree.
|
|
6
|
+
*
|
|
7
|
+
* @dev Adapted from unbalanced_tree.ts.
|
|
8
|
+
* Calculates the tree upwards layer by layer until we reach the root.
|
|
9
|
+
* The L1 calculation instead computes the tree from right to left (slightly cheaper gas).
|
|
10
|
+
* TODO: A more thorough investigation of which method is cheaper, then use that method everywhere.
|
|
11
|
+
*/
|
|
12
|
+
export function computeUnbalancedMerkleRoot(leaves: Buffer[], emptyLeaf?: Buffer, hasher = sha256Trunc): Buffer {
|
|
13
|
+
// Pad leaves to 2
|
|
14
|
+
if (leaves.length < 2) {
|
|
15
|
+
if (emptyLeaf === undefined) {
|
|
16
|
+
throw new Error('Cannot compute a Merkle root with less than 2 leaves');
|
|
17
|
+
} else {
|
|
18
|
+
leaves = padArrayEnd(leaves, emptyLeaf, 2);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const depth = Math.ceil(Math.log2(leaves.length));
|
|
23
|
+
let [layerWidth, nodeToShift] =
|
|
24
|
+
leaves.length & 1 ? [leaves.length - 1, leaves[leaves.length - 1]] : [leaves.length, Buffer.alloc(0)];
|
|
25
|
+
// Allocate this layer's leaves and init the next layer up
|
|
26
|
+
let thisLayer = leaves.slice(0, layerWidth);
|
|
27
|
+
let nextLayer = [];
|
|
28
|
+
for (let i = 0; i < depth; i++) {
|
|
29
|
+
for (let j = 0; j < layerWidth; j += 2) {
|
|
30
|
+
// Store the hash of each pair one layer up
|
|
31
|
+
nextLayer[j / 2] = hasher(Buffer.concat([thisLayer[j], thisLayer[j + 1]]));
|
|
32
|
+
}
|
|
33
|
+
layerWidth /= 2;
|
|
34
|
+
if (layerWidth & 1) {
|
|
35
|
+
if (nodeToShift.length) {
|
|
36
|
+
// If the next layer has odd length, and we have a node that needs to be shifted up, add it here
|
|
37
|
+
nextLayer.push(nodeToShift);
|
|
38
|
+
layerWidth += 1;
|
|
39
|
+
nodeToShift = Buffer.alloc(0);
|
|
40
|
+
} else {
|
|
41
|
+
// If we don't have a node waiting to be shifted, store the next layer's final node to be shifted
|
|
42
|
+
layerWidth -= 1;
|
|
43
|
+
nodeToShift = nextLayer[layerWidth];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// reset the layers
|
|
47
|
+
thisLayer = nextLayer;
|
|
48
|
+
nextLayer = [];
|
|
49
|
+
}
|
|
50
|
+
// return the root
|
|
51
|
+
return thisLayer[0];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getMaxBalancedTreeDepth(numLeaves: number) {
|
|
55
|
+
return Math.floor(Math.log2(numLeaves));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getMaxUnbalancedTreeDepth(numLeaves: number) {
|
|
59
|
+
return Math.ceil(Math.log2(numLeaves));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function findPosition(
|
|
63
|
+
rootLevel: number,
|
|
64
|
+
leafLevel: number,
|
|
65
|
+
numLeaves: number,
|
|
66
|
+
indexOffset: number,
|
|
67
|
+
targetIndex: number,
|
|
68
|
+
): { level: number; indexAtLevel: number } {
|
|
69
|
+
if (numLeaves <= 1) {
|
|
70
|
+
// Single leaf.
|
|
71
|
+
return { level: rootLevel, indexAtLevel: indexOffset };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// The largest balanced tree that can be created with the given number of leaves.
|
|
75
|
+
const maxBalancedTreeDepth = getMaxBalancedTreeDepth(numLeaves);
|
|
76
|
+
const numBalancedLeaves = 2 ** maxBalancedTreeDepth;
|
|
77
|
+
const numRemainingLeaves = numLeaves - numBalancedLeaves;
|
|
78
|
+
|
|
79
|
+
if (targetIndex < numBalancedLeaves) {
|
|
80
|
+
// Target is in the balanced tree.
|
|
81
|
+
|
|
82
|
+
// - If numRemainingLeaves is 0: this balanced tree is grown from the current root.
|
|
83
|
+
// - If numRemainingLeaves is not 0: the remaining leaves will form another tree, which will become the right child of the root.
|
|
84
|
+
// And the balanced tree will be the left child of the root.
|
|
85
|
+
// There will be an extra level between the root of the balanced tree and the current root.
|
|
86
|
+
const extraLevel = numRemainingLeaves ? 1 : 0;
|
|
87
|
+
|
|
88
|
+
return { level: rootLevel + maxBalancedTreeDepth + extraLevel, indexAtLevel: indexOffset + targetIndex };
|
|
89
|
+
} else {
|
|
90
|
+
// Target is in the right branch.
|
|
91
|
+
const rightBranchMaxLevel = getMaxUnbalancedTreeDepth(numRemainingLeaves);
|
|
92
|
+
const shiftedUp = leafLevel - rootLevel - rightBranchMaxLevel - 1;
|
|
93
|
+
const nextLeafLevel = leafLevel - shiftedUp;
|
|
94
|
+
const newIndexOffset = (indexOffset + numBalancedLeaves) >> shiftedUp;
|
|
95
|
+
const shiftedTargetIndex = targetIndex - numBalancedLeaves;
|
|
96
|
+
return findPosition(rootLevel + 1, nextLeafLevel, numRemainingLeaves, newIndexOffset, shiftedTargetIndex);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function findLeafLevelAndIndex(numLeaves: number, leafIndex: number) {
|
|
101
|
+
const maxLevel = getMaxUnbalancedTreeDepth(numLeaves);
|
|
102
|
+
return findPosition(0, maxLevel, numLeaves, 0, leafIndex);
|
|
103
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { findLeafLevelAndIndex } from './unbalanced_merkle_tree.js';
|
|
2
|
+
|
|
3
|
+
export interface TreeNodeLocation {
|
|
4
|
+
level: number;
|
|
5
|
+
index: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface TreeNode<T> {
|
|
9
|
+
value: T;
|
|
10
|
+
location: TreeNodeLocation;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class UnbalancedTreeStore<T> {
|
|
14
|
+
#nodeMapping: Map<string, TreeNode<T>> = new Map();
|
|
15
|
+
readonly #numLeaves: number;
|
|
16
|
+
|
|
17
|
+
constructor(numLeaves: number) {
|
|
18
|
+
this.#numLeaves = numLeaves;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setLeaf(leafIndex: number, value: T): TreeNodeLocation {
|
|
22
|
+
if (leafIndex >= this.#numLeaves) {
|
|
23
|
+
throw new Error(`Expected at most ${this.#numLeaves} leaves. Received a leaf at index ${leafIndex}.`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { level, indexAtLevel } = findLeafLevelAndIndex(this.#numLeaves, leafIndex);
|
|
27
|
+
const location = {
|
|
28
|
+
level,
|
|
29
|
+
index: indexAtLevel,
|
|
30
|
+
};
|
|
31
|
+
this.#nodeMapping.set(this.#getKey(location), {
|
|
32
|
+
location,
|
|
33
|
+
value,
|
|
34
|
+
});
|
|
35
|
+
return location;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
setNode({ level, index }: TreeNodeLocation, value: T) {
|
|
39
|
+
const location = {
|
|
40
|
+
level,
|
|
41
|
+
index,
|
|
42
|
+
};
|
|
43
|
+
this.#nodeMapping.set(this.#getKey(location), {
|
|
44
|
+
location,
|
|
45
|
+
value,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getParentLocation({ level, index }: TreeNodeLocation): TreeNodeLocation {
|
|
50
|
+
if (level === 0) {
|
|
51
|
+
throw new Error('Tree root does not have a parent.');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { level: level - 1, index: Math.floor(index / 2) };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getSiblingLocation({ level, index }: TreeNodeLocation): TreeNodeLocation {
|
|
58
|
+
if (level === 0) {
|
|
59
|
+
throw new Error('Tree root does not have a sibling.');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { level, index: index % 2 ? index - 1 : index + 1 };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getChildLocations({ level, index }: TreeNodeLocation): [TreeNodeLocation, TreeNodeLocation] {
|
|
66
|
+
const left = { level: level + 1, index: index * 2 };
|
|
67
|
+
const right = { level: level + 1, index: index * 2 + 1 };
|
|
68
|
+
return [left, right];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getLeaf(leafIndex: number) {
|
|
72
|
+
const { level, indexAtLevel } = findLeafLevelAndIndex(this.#numLeaves, leafIndex);
|
|
73
|
+
const location = {
|
|
74
|
+
level,
|
|
75
|
+
index: indexAtLevel,
|
|
76
|
+
};
|
|
77
|
+
return this.getNode(location);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getNode(location: TreeNodeLocation): T | undefined {
|
|
81
|
+
return this.#nodeMapping.get(this.#getKey(location))?.value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getParent(location: TreeNodeLocation): T | undefined {
|
|
85
|
+
const parentLocation = this.getParentLocation(location);
|
|
86
|
+
return this.getNode(parentLocation);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
getSibling(location: TreeNodeLocation): T | undefined {
|
|
90
|
+
const siblingLocation = this.getSiblingLocation(location);
|
|
91
|
+
return this.getNode(siblingLocation);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getChildren(location: TreeNodeLocation): [T | undefined, T | undefined] {
|
|
95
|
+
const [left, right] = this.getChildLocations(location);
|
|
96
|
+
return [this.getNode(left), this.getNode(right)];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
#getKey(location: TreeNodeLocation) {
|
|
100
|
+
return `${location.level}-${location.index}`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unbalanced_merkle_root.d.ts","sourceRoot":"","sources":["../../src/trees/unbalanced_merkle_root.ts"],"names":[],"mappings":";;AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,qBAAc,GAAG,MAAM,CAwC9G"}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { padArrayEnd } from '@aztec/foundation/collection';
|
|
2
|
-
import { sha256Trunc } from '@aztec/foundation/crypto';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Computes the merkle root for an unbalanced tree.
|
|
6
|
-
*
|
|
7
|
-
* @dev Adapted from proving-state.ts -> findMergeLevel and unbalanced_tree.ts.
|
|
8
|
-
* Calculates the tree upwards layer by layer until we reach the root.
|
|
9
|
-
* The L1 calculation instead computes the tree from right to left (slightly cheaper gas).
|
|
10
|
-
* TODO: A more thorough investigation of which method is cheaper, then use that method everywhere.
|
|
11
|
-
*/
|
|
12
|
-
export function computeUnbalancedMerkleRoot(leaves: Buffer[], emptyLeaf?: Buffer, hasher = sha256Trunc): Buffer {
|
|
13
|
-
// Pad leaves to 2
|
|
14
|
-
if (leaves.length < 2) {
|
|
15
|
-
if (emptyLeaf === undefined) {
|
|
16
|
-
throw new Error('Cannot compute a Merkle root with less than 2 leaves');
|
|
17
|
-
} else {
|
|
18
|
-
leaves = padArrayEnd(leaves, emptyLeaf, 2);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const depth = Math.ceil(Math.log2(leaves.length));
|
|
23
|
-
let [layerWidth, nodeToShift] =
|
|
24
|
-
leaves.length & 1 ? [leaves.length - 1, leaves[leaves.length - 1]] : [leaves.length, Buffer.alloc(0)];
|
|
25
|
-
// Allocate this layer's leaves and init the next layer up
|
|
26
|
-
let thisLayer = leaves.slice(0, layerWidth);
|
|
27
|
-
let nextLayer = [];
|
|
28
|
-
for (let i = 0; i < depth; i++) {
|
|
29
|
-
for (let j = 0; j < layerWidth; j += 2) {
|
|
30
|
-
// Store the hash of each pair one layer up
|
|
31
|
-
nextLayer[j / 2] = hasher(Buffer.concat([thisLayer[j], thisLayer[j + 1]]));
|
|
32
|
-
}
|
|
33
|
-
layerWidth /= 2;
|
|
34
|
-
if (layerWidth & 1) {
|
|
35
|
-
if (nodeToShift.length) {
|
|
36
|
-
// If the next layer has odd length, and we have a node that needs to be shifted up, add it here
|
|
37
|
-
nextLayer.push(nodeToShift);
|
|
38
|
-
layerWidth += 1;
|
|
39
|
-
nodeToShift = Buffer.alloc(0);
|
|
40
|
-
} else {
|
|
41
|
-
// If we don't have a node waiting to be shifted, store the next layer's final node to be shifted
|
|
42
|
-
layerWidth -= 1;
|
|
43
|
-
nodeToShift = nextLayer[layerWidth];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
// reset the layers
|
|
47
|
-
thisLayer = nextLayer;
|
|
48
|
-
nextLayer = [];
|
|
49
|
-
}
|
|
50
|
-
// return the root
|
|
51
|
-
return thisLayer[0];
|
|
52
|
-
}
|