Skip to content

Commit

Permalink
libraries: IVerifier: Refactor verifier into interface (#41)
Browse files Browse the repository at this point in the history
* libraries: darkpool: PublicInputs: Add scalar (de)serialization methods

* libraries: IVerifier: Refactor verifier into interface
  • Loading branch information
joeykraut authored Mar 1, 2025
1 parent b77615b commit f1a1a65
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 57 deletions.
26 changes: 21 additions & 5 deletions src/Darkpool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,35 @@ pragma solidity ^0.8.0;

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/Verifier.sol";
import { VerificationKeys } from "./VerificationKeys.sol";
import { VerifierCore } from "./libraries/verifier/VerifierCore.sol";
import { VerificationKeys } from "./libraries/darkpool/VerificationKeys.sol";
import { console2 } from "forge-std/console2.sol";
import { IHasher } from "./libraries/merkle/IHasher.sol";
import { ValidWalletCreateStatement, StatementSerializer } from "./libraries/darkpool/PublicInputs.sol";

// Use the StatementSerializer for all statements
using StatementSerializer for ValidWalletCreateStatement;

contract Darkpool {
/// @notice The hasher for the darkpool
IHasher public hasher;

/// @notice The constructor for the darkpool
/// @param hasher_ The hasher for the darkpool
constructor(IHasher hasher_) {
hasher = hasher_;
}

/// @notice Create a wallet in the darkpool
/// @param publicInputs The public inputs to the proof
/// @param statement The statement to verify
/// @param proof The proof of `VALID WALLET CREATE`
function createWallet(BN254.ScalarField[] memory publicInputs, PlonkProof memory proof) public {
function createWallet(ValidWalletCreateStatement memory statement, PlonkProof memory proof) public view {
// Load the verification key from the constant
VerificationKey memory vk = abi.decode(VerificationKeys.VALID_WALLET_CREATE_VKEY, (VerificationKey));

// 1. Verify the proof
// VerifierCore.verify(proof, publicInputs, vk);
// Serialize the public inputs
BN254.ScalarField[] memory publicInputs = statement.scalarSerialize();
VerifierCore.verify(proof, publicInputs, vk);
}
}
32 changes: 32 additions & 0 deletions src/Verifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { PlonkProof, VerificationKey } from "./libraries/verifier/Types.sol";
import { ValidWalletCreateStatement, StatementSerializer } from "./libraries/darkpool/PublicInputs.sol";
import { VerificationKeys } from "./libraries/darkpool/VerificationKeys.sol";
import { IVerifier } from "./libraries/verifier/IVerifier.sol";
import { VerifierCore } from "./libraries/verifier/VerifierCore.sol";
import { BN254 } from "solidity-bn254/BN254.sol";

using StatementSerializer for ValidWalletCreateStatement;

/// @title PlonK Verifier with the Jellyfish-style arithmetization
/// @notice The methods on this contract are darkpool-specific
contract Verifier is IVerifier {
/// @notice Verify a proof of `VALID WALLET CREATE`
/// @param statement The public inputs to the proof
/// @param proof The proof to verify
/// @return True if the proof is valid, false otherwise
function verifyValidWalletCreate(
ValidWalletCreateStatement memory statement,
PlonkProof memory proof
)
external
view
returns (bool)
{
VerificationKey memory vk = abi.decode(VerificationKeys.VALID_WALLET_CREATE_VKEY, (VerificationKey));
BN254.ScalarField[] memory publicInputs = statement.scalarSerialize();
return VerifierCore.verify(proof, publicInputs, vk);
}
}
74 changes: 74 additions & 0 deletions src/libraries/darkpool/PublicInputs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { BN254 } from "solidity-bn254/BN254.sol";

// This file represents the public inputs (statements) for various proofs used by the darkpool

// -------------------
// | Statement Types |
// -------------------

/// @title ValidWalletCreateStatement the statement type for the `VALID WALLET CREATE` proof
struct ValidWalletCreateStatement {
/// @dev The commitment to the wallet's private shares
BN254.ScalarField walletCommitment;
/// @dev The public wallet shares of the wallet
BN254.ScalarField[] publicShares;
}

// ------------------------
// | Scalar Serialization |
// ------------------------

/// @title StatementSerializer Library for serializing statement types to scalar arrays
library StatementSerializer {
// --- Valid Wallet Create --- //

/// @notice Serializes a ValidWalletCreateStatement into an array of scalar field elements
/// @param self The statement to serialize
/// @return serialized The serialized statement as an array of scalar field elements
function scalarSerialize(ValidWalletCreateStatement memory self)
internal
pure
returns (BN254.ScalarField[] memory)
{
// Create array with size = 1 (for walletCommitment) + publicShares.length
BN254.ScalarField[] memory serialized = new BN254.ScalarField[](1 + self.publicShares.length);

// Add the wallet commitment
serialized[0] = self.walletCommitment;

// Add all public shares
for (uint256 i = 0; i < self.publicShares.length; i++) {
serialized[i + 1] = self.publicShares[i];
}

return serialized;
}

/// @notice Deserializes an array of scalar field elements into a ValidWalletCreateStatement
/// @param serialized The serialized statement as an array of scalar field elements
/// @return statement The deserialized ValidWalletCreateStatement
function scalarDeserialize(BN254.ScalarField[] memory serialized)
internal
pure
returns (ValidWalletCreateStatement memory statement)
{
require(serialized.length >= 1, "Invalid serialized statement length");

// Extract the wallet commitment
statement.walletCommitment = serialized[0];

// Extract the public shares
statement.publicShares = new BN254.ScalarField[](serialized.length - 1);
for (uint256 i = 0; i < serialized.length - 1; i++) {
statement.publicShares[i] = serialized[i + 1];
}

return statement;
}
}

// Enable the library for the statement type
using StatementSerializer for ValidWalletCreateStatement;
File renamed without changes.
24 changes: 24 additions & 0 deletions src/libraries/merkle/IHasher.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/// @title Hasher, a simple hashing interface for Merkle and sponge hashing using Poseidon2
interface IHasher {
/// @notice Hash a leaf into the merkle tree
/// @param idx The index of the leaf in the tree
/// @param input The input to the leaf
/// @param sisterLeaves The sister leaves of the current node
/// @return The incremental hashes up the tree, including the root, the root is the last element of the array
function merkleHash(
uint256 idx,
uint256 input,
uint256[] memory sisterLeaves
)
external
view
returns (uint256[] memory);

/// @notice Hash a series of inputs into a sponge and squeeze
/// @param inputs The inputs to the sponge
/// @return The hash of the inputs
function spongeHash(uint256[] memory inputs) external view returns (uint256);
}
1 change: 0 additions & 1 deletion src/libraries/poseidon2/poseidonSponge.huff
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
dup4 0x1 eq lastInput jumpi
// Absorb the first two inputs
ABSORB_NEXT_TWO() // [state[0], state[1], state[2], len(inputs), *nextElem]

loopStart:
// If we have one or zero elements left, the loop is done
dup4 0x00 eq allAbsorbed jumpi // [state[0], state[1], state[2], len(inputs), *nextElem]
Expand Down
19 changes: 19 additions & 0 deletions src/libraries/verifier/IVerifier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { PlonkProof } from "./Types.sol";
import { ValidWalletCreateStatement } from "../darkpool/PublicInputs.sol";

interface IVerifier {
/// @notice Verify a proof of `VALID WALLET CREATE`
/// @param proof The proof to verify
/// @param statement The public inputs to the proof
/// @return True if the proof is valid, false otherwise
function verifyValidWalletCreate(
ValidWalletCreateStatement memory statement,
PlonkProof memory proof
)
external
view
returns (bool);
}
File renamed without changes.
10 changes: 6 additions & 4 deletions test/Darkpool.t.sol
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { BN254 } from "solidity-bn254/BN254.sol";
import { Test } from "forge-std/Test.sol";
import { TestUtils } from "./utils/TestUtils.sol";
import { HuffDeployer } from "foundry-huff/HuffDeployer.sol";

import { PlonkProof } from "../src/libraries/verifier/Types.sol";
import { Darkpool } from "../src/Darkpool.sol";
import { BN254 } from "solidity-bn254/BN254.sol";
import { IHasher } from "../src/libraries/merkle/IHasher.sol";

contract DarkpoolTest is TestUtils {
Darkpool public darkpool;

function setUp() public {
darkpool = new Darkpool();
IHasher hasher = IHasher(HuffDeployer.deploy("libraries/merkle/main"));
darkpool = new Darkpool(hasher);
}

function test_createWallet() public {
Expand All @@ -30,7 +34,5 @@ contract DarkpoolTest is TestUtils {
sigma_evals: [dummyScalar, dummyScalar, dummyScalar, dummyScalar],
z_bar: dummyScalar
});

darkpool.createWallet(publicInputs, proof);
}
}
23 changes: 5 additions & 18 deletions test/Merkle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import { console } from "forge-std/console.sol";
import { HuffDeployer } from "foundry-huff/HuffDeployer.sol";
import { TestUtils } from "./utils/TestUtils.sol";
import { console2 } from "forge-std/console2.sol";
import { IHasher } from "../src/libraries/merkle/IHasher.sol";

contract MerkleTest is TestUtils {
/// @dev The Merkle depth
uint256 constant MERKLE_DEPTH = 32;

/// @dev The MerklePoseidon contract
MerklePoseidon public merklePoseidon;
IHasher public hasher;

/// @dev Deploy the MerklePoseidon contract
function setUp() public {
merklePoseidon = MerklePoseidon(HuffDeployer.deploy("libraries/merkle/main"));
hasher = IHasher(HuffDeployer.deploy("libraries/merkle/main"));
}

/// @dev Test the hashMerkle function with sequential inserts
Expand All @@ -27,7 +28,7 @@ contract MerkleTest is TestUtils {
for (uint256 i = 0; i < MERKLE_DEPTH; i++) {
sisterLeaves[i] = randomFelt();
}
uint256[] memory results = merklePoseidon.merkleHash(idx, input, sisterLeaves);
uint256[] memory results = hasher.merkleHash(idx, input, sisterLeaves);
uint256[] memory expected = runMerkleReferenceImpl(idx, input, sisterLeaves);
assertEq(results.length, MERKLE_DEPTH, "Expected 32 results");

Expand All @@ -45,7 +46,7 @@ contract MerkleTest is TestUtils {
}

uint256 expected = runSpongeHashReferenceImpl(inputs);
uint256 result = merklePoseidon.spongeHash(inputs);
uint256 result = hasher.spongeHash(inputs);
assertEq(result, expected, "Sponge hash result does not match reference implementation");
}

Expand Down Expand Up @@ -105,17 +106,3 @@ contract MerkleTest is TestUtils {
return result;
}
}

interface MerklePoseidon {
/// @dev Hash a merkle leaf into the tree, return the intermediate hashes including the root
function merkleHash(
uint256 idx,
uint256 input,
uint256[] calldata sisterLeaves
)
external
returns (uint256[] memory);

/// @dev Hash an array of inputs using the Poseidon sponge
function spongeHash(uint256[] calldata inputs) external returns (uint256);
}
Loading

0 comments on commit f1a1a65

Please sign in to comment.