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

test: rust-reference-impls: merkle: Add command to compute Merkle root #46

Merged
merged 3 commits into from
Mar 4, 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
4 changes: 4 additions & 0 deletions codegen/merkle-zeros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ version = "0.1.0"
edition = "2021"

[dependencies]
# === Renegade === #
renegade-crypto = { git = "https://github.com/renegade-fi/renegade.git" }
renegade-constants = { package = "constants", git = "https://github.com/renegade-fi/renegade.git", default-features = false }
common = { path = "../../test/rust-reference-impls/common" }

# === Misc === #
clap = { version = "4.5.1", features = ["derive"] }
anyhow = "1.0"
tiny-keccak = { version = "2.0", features = ["keccak"] }
44 changes: 7 additions & 37 deletions codegen/merkle-zeros/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@

use anyhow::{anyhow, Result};
use clap::Parser;
use renegade_constants::{Scalar, MERKLE_HEIGHT};
use renegade_crypto::hash::compute_poseidon_hash;
use common::merkle_helpers::{generate_zero_values, LEAF_KECCAK_PREIMAGE};
use renegade_constants::MERKLE_HEIGHT;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use tiny_keccak::{Hasher, Keccak};

/// Name of the Solidity contract to generate
const CONTRACT_NAME: &str = "MerkleZeros";
/// The string that is used to create leaf zero values
const LEAF_KECCAK_PREIMAGE: &str = "renegade";

/// Command line arguments for the merkle-zeros-codegen binary
#[derive(Parser, Debug)]
Expand Down Expand Up @@ -44,7 +41,7 @@ fn generate_solidity_contract() -> Result<String> {
let root = zero_values[MERKLE_HEIGHT];

// Add the constant values to the contract
for (i, value) in zero_values[..MERKLE_HEIGHT].iter().rev().enumerate() {
for (i, value) in zero_values[..MERKLE_HEIGHT].iter().enumerate() {
contract.push_str(&format!(
"\tuint256 constant public ZERO_VALUE_{} = {};\n",
i, value
Expand All @@ -58,13 +55,13 @@ fn generate_solidity_contract() -> Result<String> {
// 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/// @return result the zero value for the given height\n");
contract.push_str(
"\tfunction getZeroValue(uint256 height) internal pure returns (uint256 result) {\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");

Expand All @@ -85,33 +82,6 @@ fn generate_solidity_contract() -> Result<String> {
Ok(contract)
}

/// Generate the zero values for each height in the Merkle tree
fn generate_zero_values() -> Vec<Scalar> {
let mut result = vec![generate_leaf_zero_value()];
for height in 1..=MERKLE_HEIGHT {
let last_zero = result[height - 1];
let next_zero = compute_poseidon_hash(&[last_zero, last_zero]);
result.push(next_zero);
}
result
}

/// Generate the zero value for a leaf in the Merkle tree
fn generate_leaf_zero_value() -> Scalar {
// Create a Keccak-256 hasher
let mut hasher = Keccak::v256();

// Prepare input and output buffers
let input = LEAF_KECCAK_PREIMAGE.as_bytes();
let mut output = [0u8; 32]; // 256 bits = 32 bytes

// Compute the hash
hasher.update(input);
hasher.finalize(&mut output);

Scalar::from_be_bytes_mod_order(&output)
}

/// Entrypoint
fn main() -> Result<()> {
// Parse command line arguments
Expand Down
1 change: 1 addition & 0 deletions src/Darkpool.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { console2 } from "forge-std/console2.sol";
import { PlonkProof, VerificationKey, NUM_SELECTORS, NUM_WIRE_TYPES } from "./libraries/verifier/Types.sol";
import { BN254 } from "solidity-bn254/BN254.sol";
import { VerifierCore } from "./libraries/verifier/VerifierCore.sol";
Expand Down
11 changes: 6 additions & 5 deletions src/libraries/merkle/MerkleTree.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ library MerkleTreeLib {
/// @param tree The tree to initialize
function initialize(MerkleTree storage tree) internal {
tree.nextIndex = 0;
tree.root = BN254.ScalarField.wrap(0);
tree.root = BN254.ScalarField.wrap(MerkleZeros.ZERO_VALUE_ROOT);
tree.rootHistory[tree.root] = true;

// Initialize the sibling path array
tree.siblingPath = new BN254.ScalarField[](DarkpoolConstants.MERKLE_DEPTH);
Expand All @@ -46,16 +47,16 @@ library MerkleTreeLib {
/// @notice Returns the root of the tree
/// @param tree The tree to get the root of
/// @return The root of the tree
function root(MerkleTree storage tree) internal view returns (BN254.ScalarField) {
function getRoot(MerkleTree storage tree) internal view returns (BN254.ScalarField) {
return tree.root;
}

/// @notice Returns whether the given root is in the history of the tree
/// @param tree The tree to check the root history of
/// @param root The root to check
/// @param historicalRoot The root to check
/// @return Whether the root is in the history of the tree
function rootInHistory(MerkleTree storage tree, BN254.ScalarField root) internal view returns (bool) {
return tree.rootHistory[root];
function rootInHistory(MerkleTree storage tree, BN254.ScalarField historicalRoot) internal view returns (bool) {
return tree.rootHistory[historicalRoot];
}

/// @notice Insert a leaf into the tree
Expand Down
69 changes: 34 additions & 35 deletions src/libraries/merkle/MerkleZeros.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,47 @@ pragma solidity ^0.8.0;
library MerkleZeros {
// LEAF_ZERO_VALUE is the keccak256 hash of the string "renegade"

uint256 constant public ZERO_VALUE_0 = 15962612577647058420645476952537206750366608626029610482408928804774318734059;
uint256 constant public ZERO_VALUE_1 = 19133436467657663288002792317295361471584527575409806386877579220142957274311;
uint256 constant public ZERO_VALUE_2 = 9687651560707488562669993660671667535167069078962303879140600742064277138783;
uint256 constant public ZERO_VALUE_3 = 7756681173448585507407836521077252702714499259676930386277118558421472733695;
uint256 constant public ZERO_VALUE_4 = 19675392939688276802339156281601404320980406070882905227039824358807516124555;
uint256 constant public ZERO_VALUE_5 = 2125779998549108384849449352238557248823106982222780494941361329497674962055;
uint256 constant public ZERO_VALUE_6 = 21045373409225237393227367297008943911912705632505943546436228216465187679794;
uint256 constant public ZERO_VALUE_7 = 15240140699176831850148543788268509701353515782125796441374715127049044490345;
uint256 constant public ZERO_VALUE_8 = 4869202566029698064271465793060035209512941191405170347160900659782492599420;
uint256 constant public ZERO_VALUE_9 = 21499774241007975435158701841671012387268617016438138010529957752176281043982;
uint256 constant public ZERO_VALUE_10 = 12005465458277291918585874267570875140312745411055133999597826802122331383829;
uint256 constant public ZERO_VALUE_11 = 5954813481663260920526782055140925931240652453878241256547343648547886256115;
uint256 constant public ZERO_VALUE_12 = 11215346573587842331133140809886378146723590715517066956754015818399718028211;
uint256 constant public ZERO_VALUE_13 = 7411979333183272993586192846445207497153951530258254274653315223417956289516;
uint256 constant public ZERO_VALUE_14 = 1021314982886605788655750223042027732834175048831600032925052368337071312920;
uint256 constant public ZERO_VALUE_15 = 12626775664493191592868103463442370443572107219205819234160503479267552095144;
uint256 constant public ZERO_VALUE_16 = 20123836996685171851147985684125431013529635771352218757607199589107940123097;
uint256 constant public ZERO_VALUE_17 = 3382712961983301882764582068036696973957406957867468693626457706392093541451;
uint256 constant public ZERO_VALUE_18 = 2902297038939909195370997474544705413513565002202017991304595081134299351859;
uint256 constant public ZERO_VALUE_19 = 18195474162697958019742127733181574648348403460399143282804115462140090973272;
uint256 constant public ZERO_VALUE_20 = 7917393958708656447966347763175495240817592276812457488958366778499968534404;
uint256 constant public ZERO_VALUE_21 = 7093370145395736599029247649221138464255596728001346862162636717742248456822;
uint256 constant public ZERO_VALUE_22 = 7143033676627539261504534687959441406366684773196748433880878745292610721342;
uint256 constant public ZERO_VALUE_23 = 19481598376776831435239791620729985799885972897300809781485986746773456392129;
uint256 constant public ZERO_VALUE_24 = 19076218734736948577879157617365119126235873506901406237134838330454326767382;
uint256 constant public ZERO_VALUE_25 = 11202409622115594108632295319026050428586618859253344007474069726617383330201;
uint256 constant public ZERO_VALUE_26 = 4691736141445911110922368938732204433610503339213348004083266455528299545086;
uint256 constant public ZERO_VALUE_27 = 7492191656949889326191300377435382192231120613403472660772864155136961721921;
uint256 constant public ZERO_VALUE_28 = 17974638503328037533292433722585764042021737271514033680858878722945293807513;
uint256 constant public ZERO_VALUE_29 = 16152469242921808488194486632224509727076872200432979464611802545119788097844;
uint256 constant public ZERO_VALUE_30 = 7035835480239620343712770214636030506415861196323445446427955599547555378646;
uint256 constant public ZERO_VALUE_31 = 3570982782379586050211724779746612745305269241448247085265205218748662232570;
uint256 constant public ZERO_VALUE_0 = 3570982782379586050211724779746612745305269241448247085265205218748662232570;
uint256 constant public ZERO_VALUE_1 = 7035835480239620343712770214636030506415861196323445446427955599547555378646;
uint256 constant public ZERO_VALUE_2 = 16152469242921808488194486632224509727076872200432979464611802545119788097844;
uint256 constant public ZERO_VALUE_3 = 17974638503328037533292433722585764042021737271514033680858878722945293807513;
uint256 constant public ZERO_VALUE_4 = 7492191656949889326191300377435382192231120613403472660772864155136961721921;
uint256 constant public ZERO_VALUE_5 = 4691736141445911110922368938732204433610503339213348004083266455528299545086;
uint256 constant public ZERO_VALUE_6 = 11202409622115594108632295319026050428586618859253344007474069726617383330201;
uint256 constant public ZERO_VALUE_7 = 19076218734736948577879157617365119126235873506901406237134838330454326767382;
uint256 constant public ZERO_VALUE_8 = 19481598376776831435239791620729985799885972897300809781485986746773456392129;
uint256 constant public ZERO_VALUE_9 = 7143033676627539261504534687959441406366684773196748433880878745292610721342;
uint256 constant public ZERO_VALUE_10 = 7093370145395736599029247649221138464255596728001346862162636717742248456822;
uint256 constant public ZERO_VALUE_11 = 7917393958708656447966347763175495240817592276812457488958366778499968534404;
uint256 constant public ZERO_VALUE_12 = 18195474162697958019742127733181574648348403460399143282804115462140090973272;
uint256 constant public ZERO_VALUE_13 = 2902297038939909195370997474544705413513565002202017991304595081134299351859;
uint256 constant public ZERO_VALUE_14 = 3382712961983301882764582068036696973957406957867468693626457706392093541451;
uint256 constant public ZERO_VALUE_15 = 20123836996685171851147985684125431013529635771352218757607199589107940123097;
uint256 constant public ZERO_VALUE_16 = 12626775664493191592868103463442370443572107219205819234160503479267552095144;
uint256 constant public ZERO_VALUE_17 = 1021314982886605788655750223042027732834175048831600032925052368337071312920;
uint256 constant public ZERO_VALUE_18 = 7411979333183272993586192846445207497153951530258254274653315223417956289516;
uint256 constant public ZERO_VALUE_19 = 11215346573587842331133140809886378146723590715517066956754015818399718028211;
uint256 constant public ZERO_VALUE_20 = 5954813481663260920526782055140925931240652453878241256547343648547886256115;
uint256 constant public ZERO_VALUE_21 = 12005465458277291918585874267570875140312745411055133999597826802122331383829;
uint256 constant public ZERO_VALUE_22 = 21499774241007975435158701841671012387268617016438138010529957752176281043982;
uint256 constant public ZERO_VALUE_23 = 4869202566029698064271465793060035209512941191405170347160900659782492599420;
uint256 constant public ZERO_VALUE_24 = 15240140699176831850148543788268509701353515782125796441374715127049044490345;
uint256 constant public ZERO_VALUE_25 = 21045373409225237393227367297008943911912705632505943546436228216465187679794;
uint256 constant public ZERO_VALUE_26 = 2125779998549108384849449352238557248823106982222780494941361329497674962055;
uint256 constant public ZERO_VALUE_27 = 19675392939688276802339156281601404320980406070882905227039824358807516124555;
uint256 constant public ZERO_VALUE_28 = 7756681173448585507407836521077252702714499259676930386277118558421472733695;
uint256 constant public ZERO_VALUE_29 = 9687651560707488562669993660671667535167069078962303879140600742064277138783;
uint256 constant public ZERO_VALUE_30 = 19133436467657663288002792317295361471584527575409806386877579220142957274311;
uint256 constant public ZERO_VALUE_31 = 15962612577647058420645476952537206750366608626029610482408928804774318734059;
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) {
/// @return result the zero value for the given height
function getZeroValue(uint256 height) internal pure returns (uint256 result) {
// 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 }
Expand Down
50 changes: 50 additions & 0 deletions test/MerkleTree.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { DarkpoolConstants } from "../src/libraries/darkpool/Constants.sol";
import { MerkleTreeLib } from "../src/libraries/merkle/MerkleTree.sol";
import { MerkleZeros } from "../src/libraries/merkle/MerkleZeros.sol";
import { IHasher } from "../src/libraries/poseidon2/IHasher.sol";
import { TestUtils } from "./utils/TestUtils.sol";
import { HuffDeployer } from "foundry-huff/HuffDeployer.sol";
import { BN254 } from "solidity-bn254/BN254.sol";

contract MerkleTreeTest is TestUtils {
using MerkleTreeLib for MerkleTreeLib.MerkleTree;

MerkleTreeLib.MerkleTree private tree;
IHasher private hasher;

function setUp() public {
tree.initialize();
hasher = IHasher(HuffDeployer.deploy("libraries/poseidon2/poseidonHasher"));
}

/// @notice Test that the root and root history are initialized correctly
function test_rootAfterInitialization() public view {
// Test that the root is the default zero valued root
uint256 expectedRoot = MerkleZeros.ZERO_VALUE_ROOT;
uint256 actualRoot = BN254.ScalarField.unwrap(tree.getRoot());
assertEq(actualRoot, expectedRoot);

// Test that the zero valued root is in the history
BN254.ScalarField rootScalar = BN254.ScalarField.wrap(expectedRoot);
bool expectedInHistory = tree.rootInHistory(rootScalar);
assertEq(expectedInHistory, true);
}

/// @notice Test that the zero valued leaf hashes to the zero valued root
function test_zeroValueLeafMerkleHash() public view {
uint256 root = BN254.ScalarField.unwrap(tree.getRoot());

uint256 currLeaf = MerkleZeros.getZeroValue(0);
for (uint256 i = 0; i < DarkpoolConstants.MERKLE_DEPTH; i++) {
uint256[] memory inputs = new uint256[](2);
inputs[0] = currLeaf;
inputs[1] = currLeaf;
currLeaf = hasher.spongeHash(inputs);
}

assertEq(currLeaf, root);
}
}
4 changes: 4 additions & 0 deletions test/rust-reference-impls/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ ark-ff = "0.4.0"
ark-ec = "0.4.0"
ark-serialize = "0.4.0"

# === Cryptography === #
tiny-keccak = { version = "2.0", features = ["keccak"] }

# === EVM === #
alloy = "0.11"
alloy-sol-types = "0.8.21"
Expand All @@ -19,6 +22,7 @@ mpc-plonk = { workspace = true }
mpc-relation = { workspace = true }
renegade-circuit-types = { workspace = true }
renegade-constants = { workspace = true }
renegade-crypto = { workspace = true }

# === Misc === #
itertools = "0.14"
Expand Down
1 change: 1 addition & 0 deletions test/rust-reference-impls/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Common utilities for the reference implementations

pub mod abi_types;
pub mod merkle_helpers;
35 changes: 35 additions & 0 deletions test/rust-reference-impls/common/src/merkle_helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Helper functions for merkle tree operations

use renegade_constants::{Scalar, MERKLE_HEIGHT};
use renegade_crypto::hash::compute_poseidon_hash;
use tiny_keccak::{Hasher, Keccak};

/// The string that is used to create leaf zero values
pub const LEAF_KECCAK_PREIMAGE: &str = "renegade";

/// Generate the zero values for each height in the Merkle tree
pub fn generate_zero_values() -> Vec<Scalar> {
let mut result = vec![generate_leaf_zero_value()];
for height in 1..=MERKLE_HEIGHT {
let last_zero = result[height - 1];
let next_zero = compute_poseidon_hash(&[last_zero, last_zero]);
result.push(next_zero);
}
result
}

/// Generate the zero value for a leaf in the Merkle tree
pub fn generate_leaf_zero_value() -> Scalar {
// Create a Keccak-256 hasher
let mut hasher = Keccak::v256();

// Prepare input and output buffers
let input = LEAF_KECCAK_PREIMAGE.as_bytes();
let mut output = [0u8; 32]; // 256 bits = 32 bytes

// Compute the hash
hasher.update(input);
hasher.finalize(&mut output);

Scalar::from_be_bytes_mod_order(&output)
}
1 change: 1 addition & 0 deletions test/rust-reference-impls/merkle/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ rand = "0.8"

renegade-constants = { workspace = true }
renegade-crypto = { workspace = true }
common = { workspace = true }
Loading
Loading