Skip to content

Commit

Permalink
all: type correction leafs with leaves
Browse files Browse the repository at this point in the history
  • Loading branch information
kaxxa123 committed Sep 21, 2024
1 parent 861cb5b commit cf9a9f8
Show file tree
Hide file tree
Showing 12 changed files with 38 additions and 27 deletions.
6 changes: 3 additions & 3 deletions merkle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ Multiple Implemenations are included, introducing different optimizations:
/
[ e ]
/ \
[ d ] [ 0 ] <-- No child leafs
[ d ] [ 0 ] <-- No child leaves
/ \
[ c ] [ 0 ] <-- No child leafs
[ c ] [ 0 ] <-- No child leaves
/ \
[ a ] [ b ]
```
* [merkle_h0.ts](./src/trees/merkle_h0.ts) - On top of `merkle_naive`, eliminates the computation of zero hashes by setting empty leafs to `H(0 | 0)` and defining this as: <BR />
* [merkle_h0.ts](./src/trees/merkle_h0.ts) - On top of `merkle_naive`, eliminates the computation of zero hashes by setting empty leaves to `H(0 | 0)` and defining this as: <BR />
`H(0 | 0) = 0`
Expand Down
8 changes: 4 additions & 4 deletions merkle/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type TreeConfig = {
type: string,
level: number,
sort_hash: boolean,
leafs: LeafConfig[]
leaves: LeafConfig[]
};

function isLeafConfig(obj: any): obj is LeafConfig {
Expand All @@ -42,8 +42,8 @@ function isTreeConfig(obj: any): obj is TreeConfig {
isValidType(obj.type) &&
typeof obj.level === 'number' &&
typeof obj.sort_hash === 'boolean' &&
Array.isArray(obj.leafs) &&
obj.leafs.every(isLeafConfig);
Array.isArray(obj.leaves) &&
obj.leaves.every(isLeafConfig);
}

function normalizeIndex(config: LeafConfig): bigint {
Expand All @@ -64,7 +64,7 @@ export async function loadConfig(path: string): Promise<TreeConfig> {

// Check if leaf indexes are in range
let MAX = 2n ** BigInt(json.level);
json.leafs.forEach(leaf => {
json.leaves.forEach(leaf => {
let idx = normalizeIndex(leaf);
if ((idx < 0n) || (idx >= MAX))
throw `Configuration error: Leaf index out of range ${leaf.index}`;
Expand Down
4 changes: 2 additions & 2 deletions merkle/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const DEFAULT_CONFIG: TreeConfig = {
type: TREE_TYPE_DEFAULT,
level: 5,
sort_hash: true,
leafs: []
leaves: []
};

let g_horiz_offset: number;
Expand All @@ -42,7 +42,7 @@ async function main() {
g_tree_type = json_config.type;
g_sortHashes = json_config.sort_hash;
g_tree = new TreeDisplay(initTreeType(g_tree_type, json_config.level, g_sortHashes), PRETTY);
json_config.leafs.forEach(leaf => {
json_config.leaves.forEach(leaf => {
g_tree.addLeaf(BigInt(leaf.index), leaf.value);
})

Expand Down
4 changes: 2 additions & 2 deletions merkle/src/index2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ async function main() {
console.log(tree.NAME());
console.log();

config.leafs.forEach((leaf) => {
config.leaves.forEach((leaf) => {
let hash = tree.addLeaf(BigInt(leaf.index), leaf.value)
console.log(`Added leaf #${leaf.index}`)
console.log(` Hash: ${hash}`)
console.log(` Value: ${leaf.value}`)
console.log()
});

config.leafs.forEach((leaf) => {
config.leaves.forEach((leaf) => {
let proof = tree.getProof(BigInt(leaf.index))

console.log()
Expand Down
6 changes: 3 additions & 3 deletions merkle/src/tests/test_single_leaf.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SMTSingleLeaf and SMTHashZero should always produce the same root.
// The two implementations only differ in how they store the tree.
//
// Randomly add/remove tree leafs and check that the roots always match.
// Randomly add/remove tree leaves and check that the roots always match.

import { SMTHashZero } from '../trees/merkle_h0'
import { SMTSingleLeaf } from '../trees/merkle_single_leaf'
Expand Down Expand Up @@ -30,7 +30,7 @@ function reproduce() {
console.log(`${pos}. Root after ${(addr[1] ? "adding" : "removing")} leaf ${addr[0]} : ${tree1.ROOT()}`)

if (leaf0 !== leaf1)
throw "Leafs added/removed did not match!";
throw "Leaves added/removed did not match!";

if (tree0.ROOT() !== tree1.ROOT())
throw "Roots did not match!";
Expand All @@ -53,7 +53,7 @@ function main() {
console.log();

if (leaf0 !== leaf1)
throw "Leafs added/removed did not match!";
throw "Leaves added/removed did not match!";

if (tree0.ROOT() !== tree1.ROOT())
throw "Roots did not match!";
Expand Down
19 changes: 15 additions & 4 deletions merkle/src/trees/merkle_single_leaf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ export class SMTSingleLeaf implements IMerkle {
}

// Get the root hash for a single non-zero leaf subtree
//
// Inputs
// address - address of non-zero leaf
//
// hashLeaf - hash of non-zero leaf
//
// lvl - subtree root level, where zero is the tree
// root and LEVELS_TOTAL() is the leaf level.
//
// Returns
// subtree hash
private _singleLeafSubtree(address: bigint, hashLeaf: string, lvl: bigint): string {
let bitmask = 1n;
let hashLevel = hashLeaf;
Expand Down Expand Up @@ -195,7 +206,7 @@ export class SMTSingleLeaf implements IMerkle {
node = subtree[1];
}
else {
// As long as the paths for the two leafs overlap,
// As long as the paths for the two leaves overlap,
// the sibling hash will be zero.
for (; pos < this.LEVELS_TOTAL(); ++pos) {
if ((address & bitmask) != (leaf_address & bitmask))
Expand All @@ -208,7 +219,7 @@ export class SMTSingleLeaf implements IMerkle {
// We now have two sibling subtrees, both with a single
// non-zero leaf. Get the hash of the sibiling subtree.
// Here we need to handle a limit case where the two
// leafs are siblings.
// leaves are siblings.
//
// [ a ] [ z ]
// | |
Expand Down Expand Up @@ -337,7 +348,7 @@ export class SMTSingleLeaf implements IMerkle {

private _tree_set(log: string, parent: string, children: string[]) {
// Filter out entries whose parent is zero.
// This happens because of the way we handle removal of leafs
// This happens because of the way we handle removal of leaves
// which involves setting the leaf to a zero hash.
if (parent === this.HASH_ZERO())
return;
Expand Down Expand Up @@ -375,7 +386,7 @@ export class SMTSingleLeaf implements IMerkle {
throw `Unexpected siblings array length: ${siblings.length}!`;

// When an auxiliary node IS included, the sibling list will always have missing
// leafs. We thus expect the nodes array to be terminated with two extra hashes:
// entries. We expect the nodes array to be terminated with two extra hashes:
// [..., single_leaf_subtree_hash, leaf_hash]
if ((aux.length !== 0) && (nodes.length !== siblings.length + 2))
throw `Unexpected nodes array length, with auxiliary node! Nodes: ${nodes.length}, Siblings: ${siblings.length}`;
Expand Down
2 changes: 1 addition & 1 deletion merkle/tree_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"type": "h0",
"level": 160,
"sort_hash": true,
"leafs": [
"leaves": [
{
"index": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"value": "000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb922660000000000000000000000000000000000000000000000004563918244f400000000000000000000000000000000000000000000000000000000000000000001"
Expand Down
2 changes: 1 addition & 1 deletion pom_check/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This test was excuted against a Reth node. Some harcoded address values may chan
{
"level": 3,
"sort_hash": true,
"leafs": [
"leaves": [
{
"index": 0,
"value": "000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb922660000000000000000000000000000000000000000000000004563918244f40000"
Expand Down
2 changes: 1 addition & 1 deletion pom_check/ignition/modules/TokenDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const TokenDropModule = buildModule("TokenDropModule", (m) => {

const drop = m.contract(
"TokenDrop",
// Root for Level=3, Sorted, Leafs Set: 0,1,2,3
// Root for Level=3, Sorted, Leaf Set: 0,1,2,3
[usd, "0xef0f86bd9a12acd5285d712174c8f8035f503428154c4b4a40c2494f32a77b3b"],
{ after: [usd] });

Expand Down
8 changes: 4 additions & 4 deletions solarity_tree/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ Some conclusions that standout:

1. The library makes extensive use of recursion. This is not a good practice in Solidity programming, where the maximum stack size is limited.

For large SMTs, one should analyze the potential of an attack where leaf operations fails due to stack overflow. Can an attacker add leafs such that to block proof-of-ownership for other leafs?
For large SMTs, one should analyze the potential of an attack where leaf operations fails due to stack overflow. Can an attacker add leaves such that to block proof-of-ownership for other leaves?

1. Gas consumption is highly dependent on the current tree size. As new leafs are added, the traversal depth and storage access operations required to add a new leaf increases. Thus tree access becomes more expensive over time.
1. Gas consumption is highly dependent on the current tree size. As new leaves are added, the traversal depth and storage access operations required to add a new leaf increases. Thus tree access becomes more expensive over time.

1. The design is wasteful in storage. This is especially true for the `Node` struct which encapsulates different variables for `MIDDLE` and `LEAF` nodes.

Expand Down Expand Up @@ -120,7 +120,7 @@ struct Node {

`nodeHash` - Node hash where for... <BR />
`MIDDLE` nodes `nodeHash = Hash(Left_Hash | Right_Hash)` <BR />
`LEAF` nodes `nodeHash = Hash(key | value | 1)`. The doc says _1 acts as a domain separator_ i.e. it separates leafs from non-leafs.
`LEAF` nodes `nodeHash = Hash(key | value | 1)`. The doc says _1 acts as a domain separator_ i.e. it separates leaves from intermediate nodes.

`key`, `value` - Node address and value in case of a `LEAF` node.

Expand Down Expand Up @@ -405,7 +405,7 @@ function _setNode(
let treeFactory = await ethers.getContractFactory("TokenSnapshot")
let tree = await treeFactory.attach(contract_addrs.DeployModule_TokenSnapshot)
// Create tree leafs...
// Create tree leaves...
let accounts = await ethers.getSigners()
await tree.getLeafValue(accounts[0])
await tree.recordBalance(accounts[0], 5n*10n**18n)
Expand Down
2 changes: 1 addition & 1 deletion vitalik_merkle_optimizations/new_bintrie_hex.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def make_single_key_hash(path, depth, value):
# vals is reallocated 4 times in each round halfing its size.
#
# In this manner we are hashing adjecent siblings moving from
# leafs to root
# leaf to root
def hash_16_els(vals):
assert len(vals) == 16
for _ in range(4):
Expand Down
2 changes: 1 addition & 1 deletion vitalik_merkle_optimizations/new_bintrie_optimized.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def make_single_key_hash(path, depth, value):

# Make a root hash of a (sub)tree with two key/value pairs, and save intermediate nodes in the DB
#
# Adds DB nodes in cases where the subtree includes 2 non-zero leafs.
# Adds DB nodes in cases where the subtree includes 2 non-zero leaves.
#
# Function shows how the value of a single leaf subtree is encoded
# with an extra byte.
Expand Down

0 comments on commit cf9a9f8

Please sign in to comment.