Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

librarires: merkle: MerkleTree: Use zero values in tree init + insert #45

Merged
merged 1 commit into from
Mar 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }
}
}
}
Loading