Skip to content

Commit

Permalink
librarires: merkle: MerkleTree: Use zero values in tree init + insert
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykraut committed Mar 1, 2025
1 parent 31de6e6 commit 11da5c7
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 9 deletions.
1 change: 0 additions & 1 deletion codegen/merkle-zeros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@ renegade-constants = { package = "constants", git = "https://github.com/renegade
clap = { version = "4.5.1", features = ["derive"] }
anyhow = "1.0"
tiny-keccak = { version = "2.0", features = ["keccak"] }
hex = "0.4"
28 changes: 25 additions & 3 deletions codegen/merkle-zeros/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
//!
//! This tool generates a Solidity file with predefined Merkle tree zero values.
// ⚠ ️WARNING: This file is auto-generated by `codegen/merkle-zeros`. Do not edit directly.

use anyhow::{anyhow, Result};
use clap::Parser;
use hex;
use renegade_constants::{Scalar, MERKLE_HEIGHT};
use renegade_crypto::hash::compute_poseidon_hash;
use std::fs::File;
Expand Down Expand Up @@ -58,6 +55,31 @@ fn generate_solidity_contract() -> Result<String> {
root
));

// Add an assembly-based getter function for gas-efficient constant-time access
contract.push_str("\n\t/// @notice Get zero value for a given height\n");
contract.push_str("\t/// @param height The height in the Merkle tree\n");
contract.push_str("\t/// @return The zero value for the given height\n");
contract
.push_str("\tfunction getZeroValue(uint256 height) internal pure returns (uint256) {\n");
contract.push_str("\t\t// Require height to be within valid range\n");
contract.push_str("\t\trequire(height <= 31, \"MerkleZeros: height must be <= 31\");\n\n");

contract.push_str("\t\tuint256 result;\n");
contract.push_str("\t\tassembly {\n");
contract.push_str("\t\t\tswitch height\n");

// Generate all the assembly cases with direct constant values
for i in 0..MERKLE_HEIGHT {
contract.push_str(&format!(
"\t\t\tcase {} {{ result := ZERO_VALUE_{} }}\n",
i, i
));
}

// Remove the default case for assembly since require will handle invalid values
contract.push_str("\t\t}\n");
contract.push_str("\t}\n");

// Close contract
contract.push_str("}\n");
Ok(contract)
Expand Down
17 changes: 12 additions & 5 deletions src/libraries/merkle/MerkleTree.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.0;
import { BN254 } from "solidity-bn254/BN254.sol";
import { IHasher } from "../poseidon2/IHasher.sol";
import { DarkpoolConstants } from "../darkpool/Constants.sol";
import { MerkleZeros } from "./MerkleZeros.sol";

/// @title MerkleTreeLib
/// @notice Library for Merkle tree operations
Expand All @@ -21,17 +22,24 @@ library MerkleTreeLib {
mapping(BN254.ScalarField => bool) rootHistory;
}

/// @notice Get the zero value for a given height in the Merkle tree
/// @param height The height in the Merkle tree
/// @return The zero value for the given height
function zeroValue(uint256 height) internal pure returns (BN254.ScalarField) {
// Use the assembly-based getter for maximum gas efficiency
return BN254.ScalarField.wrap(MerkleZeros.getZeroValue(height));
}

/// @notice Initialize the Merkle tree
/// @param tree The tree to initialize
function initialize(MerkleTree storage tree) internal {
tree.nextIndex = 0;
tree.root = BN254.ScalarField.wrap(0);

// Initialize the sibling path
BN254.ScalarField zero = BN254.ScalarField.wrap(0);
// Initialize the sibling path array
tree.siblingPath = new BN254.ScalarField[](DarkpoolConstants.MERKLE_DEPTH);
for (uint256 i = 0; i < DarkpoolConstants.MERKLE_DEPTH; i++) {
tree.siblingPath[i] = zero;
tree.siblingPath[i] = zeroValue(i);
}
}

Expand Down Expand Up @@ -78,8 +86,7 @@ library MerkleTreeLib {
} else {
// Right node, the new sibling is in a new sub-tree, and is the zero value
// for this depth in the tree
// TODO: Use depth-dependent zero values
tree.siblingPath[i] = BN254.ScalarField.wrap(0);
tree.siblingPath[i] = zeroValue(i);
}
}
}
Expand Down
45 changes: 45 additions & 0 deletions src/libraries/merkle/MerkleZeros.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,49 @@ library MerkleZeros {
uint256 constant public ZERO_VALUE_30 = 7035835480239620343712770214636030506415861196323445446427955599547555378646;
uint256 constant public ZERO_VALUE_31 = 3570982782379586050211724779746612745305269241448247085265205218748662232570;
uint256 constant public ZERO_VALUE_ROOT = 21822647340628839684360703580761466999432252031123851327655447000077200870350;

/// @notice Get zero value for a given height
/// @param height The height in the Merkle tree
/// @return The zero value for the given height
function getZeroValue(uint256 height) internal pure returns (uint256) {
// Require height to be within valid range
require(height <= 31, "MerkleZeros: height must be <= 31");

uint256 result;
assembly {
switch height
case 0 { result := ZERO_VALUE_0 }
case 1 { result := ZERO_VALUE_1 }
case 2 { result := ZERO_VALUE_2 }
case 3 { result := ZERO_VALUE_3 }
case 4 { result := ZERO_VALUE_4 }
case 5 { result := ZERO_VALUE_5 }
case 6 { result := ZERO_VALUE_6 }
case 7 { result := ZERO_VALUE_7 }
case 8 { result := ZERO_VALUE_8 }
case 9 { result := ZERO_VALUE_9 }
case 10 { result := ZERO_VALUE_10 }
case 11 { result := ZERO_VALUE_11 }
case 12 { result := ZERO_VALUE_12 }
case 13 { result := ZERO_VALUE_13 }
case 14 { result := ZERO_VALUE_14 }
case 15 { result := ZERO_VALUE_15 }
case 16 { result := ZERO_VALUE_16 }
case 17 { result := ZERO_VALUE_17 }
case 18 { result := ZERO_VALUE_18 }
case 19 { result := ZERO_VALUE_19 }
case 20 { result := ZERO_VALUE_20 }
case 21 { result := ZERO_VALUE_21 }
case 22 { result := ZERO_VALUE_22 }
case 23 { result := ZERO_VALUE_23 }
case 24 { result := ZERO_VALUE_24 }
case 25 { result := ZERO_VALUE_25 }
case 26 { result := ZERO_VALUE_26 }
case 27 { result := ZERO_VALUE_27 }
case 28 { result := ZERO_VALUE_28 }
case 29 { result := ZERO_VALUE_29 }
case 30 { result := ZERO_VALUE_30 }
case 31 { result := ZERO_VALUE_31 }
}
}
}

0 comments on commit 11da5c7

Please sign in to comment.