diff --git a/examples/CRISP/crates/evm_helpers/src/lib.rs b/examples/CRISP/crates/evm_helpers/src/lib.rs index 724071dc03..4e53009f8a 100644 --- a/examples/CRISP/crates/evm_helpers/src/lib.rs +++ b/examples/CRISP/crates/evm_helpers/src/lib.rs @@ -25,7 +25,7 @@ sol! { #[derive(Debug)] #[sol(rpc)] contract CRISPProgram { - function setRoundData(uint256 e3_id, uint256 _root, address _token, uint256 _balanceThreshold) external; + function setMerkleRoot(uint256 e3_id, uint256 _root) external; } } @@ -33,7 +33,6 @@ sol! { event InputPublished(uint256 indexed e3Id, bytes vote, uint256 index); } - /// Type alias for write provider (same as EnclaveWriteProvider) pub type CRISPWriteProvider = FillProvider< JoinFill< @@ -80,17 +79,15 @@ impl CRISPContract { }) } - /// Set round data on the CRISPProgram contract - pub async fn set_round_data( + /// Set Merkle root on the CRISPProgram contract + pub async fn set_merkle_root( &self, e3_id: U256, merkle_root: U256, - token_address: Address, - balance_threshold: U256, ) -> Result { let contract = CRISPProgram::new(self.contract_address, self.provider.as_ref()); let receipt = contract - .setRoundData(e3_id, merkle_root, token_address, balance_threshold) + .setMerkleRoot(e3_id, merkle_root) .send() .await? .get_receipt() diff --git a/examples/CRISP/package.json b/examples/CRISP/package.json index 37d5c723b3..b614f9ad4f 100644 --- a/examples/CRISP/package.json +++ b/examples/CRISP/package.json @@ -19,8 +19,6 @@ "ciphernode:add": "pnpm -C packages/crisp-contracts ciphernode:add", "ciphernode:mint:tokens": "pnpm -C packages/crisp-contracts ciphernode:mint:tokens", "ciphernode:add:self": "pnpm -C packages/crisp-contracts ciphernode:add:self", - "deploy:contracts:full:mock": "pnpm -C packages/crisp-contracts deploy:contracts:full:mock", - "deploy:contracts:mock": "pnpm -C packages/crisp-contracts deploy:contracts:mock", "deploy:contracts": "pnpm -C packages/crisp-contracts deploy:contracts", "test": "pnpm test:e2e", "test:circuits:inputs": "node --test test/governanceCircuit.test.ts", diff --git a/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol b/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol index 10e37d9b10..786f7ccf5e 100644 --- a/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol +++ b/examples/CRISP/packages/crisp-contracts/contracts/CRISPProgram.sol @@ -11,100 +11,77 @@ import { IE3Program } from "@enclave-e3/contracts/contracts/interfaces/IE3Progra import { IEnclave } from "@enclave-e3/contracts/contracts/interfaces/IEnclave.sol"; import { E3 } from "@enclave-e3/contracts/contracts/interfaces/IE3.sol"; import { LazyIMTData, InternalLazyIMT, PoseidonT3 } from "@zk-kit/lazy-imt.sol/InternalLazyIMT.sol"; - import { HonkVerifier } from "./CRISPVerifier.sol"; contract CRISPProgram is IE3Program, Ownable { using InternalLazyIMT for LazyIMTData; - /// @notice a structure that holds the round data + + /// @notice Struct to store all data related to a voting round struct RoundData { - /// @notice The governance token address. - address token; - /// @notice The minimum balance required to pass the validation. - uint256 balanceThreshold; - /// @notice The Merkle root of the census. - uint256 censusMerkleRoot; + uint256 merkleRoot; + bytes32 paramsHash; + mapping(address slot => uint40 index) voteSlots; + LazyIMTData votes; } // Constants + /// @notice Encryption scheme ID used for the CRISP program. bytes32 public constant ENCRYPTION_SCHEME_ID = keccak256("fhe.rs:BFV"); - - // The depth of the input merkle tree + /// @notice The depth of the input Merkle tree. uint8 public constant TREE_DEPTH = 20; + /// @notice Half of the largest minimum degree used to fit votes inside the plaintext polynomial. + uint256 public constant HALF_LARGEST_MINIMUM_DEGREE = 28; // static, hardcoded in the circuit. // State variables IEnclave public enclave; - IRiscZeroVerifier public verifier; - HonkVerifier private immutable HONK_VERIFIER; + IRiscZeroVerifier public risc0Verifier; bytes32 public imageId; - - /// @notice the round data - mapping(uint256 e3Id => RoundData roundData) public roundsData; - /// @notice whether the round data has been set - mapping(uint256 e3Id => bool isDataSet) public isRoundsDataSet; - - /// @notice Half of the largest minimum degree used to fit votes - /// inside the plaintext polynomial - uint256 public constant HALF_LARGEST_MINIMUM_DEGREE = 28; + HonkVerifier private immutable honkVerifier; // Mappings mapping(address => bool) public authorizedContracts; - mapping(uint256 e3Id => bytes32 paramsHash) public paramsHashes; - /// @notice Mapping to store votes slot indices. Each eligible voter has their own slot - /// to store their vote inside the merkle tree. - mapping(uint256 e3Id => mapping(address slot => uint40 index)) public voteSlots; - mapping(uint256 e3Id => LazyIMTData) public votes; + mapping(uint256 e3Id => RoundData) e3Data; // Errors error CallerNotAuthorized(); error E3AlreadyInitialized(); error E3DoesNotExist(); error EnclaveAddressZero(); - error VerifierAddressZero(); - - /// @notice The error emitted when the honk verifier address is invalid. + error Risc0VerifierAddressZero(); error InvalidHonkVerifier(); - /// @notice The error emitted when the input data is empty. error EmptyInputData(); - /// @notice The error emitted when the input data is invalid. - error InvalidInputData(bytes reason); - /// @notice The error emitted when the Noir proof is invalid. error InvalidNoirProof(); - /// @notice The error emitted when the round data is not set. - error RoundDataNotSet(); - /// @notice The error emitted when trying to set the round data more than once. - error RoundDataAlreadySet(); + error InvalidMerkleRoot(); + error MerkleRootAlreadySet(); - /// @notice The event emitted when an input is published. + // Events event InputPublished(uint256 indexed e3Id, bytes vote, uint256 index); /// @notice Initialize the contract, binding it to a specified RISC Zero verifier. /// @param _enclave The enclave address - /// @param _verifier The RISC Zero verifier address + /// @param _risc0Verifier The RISC Zero verifier address /// @param _honkVerifier The honk verifier address /// @param _imageId The image ID for the guest program - constructor(IEnclave _enclave, IRiscZeroVerifier _verifier, HonkVerifier _honkVerifier, bytes32 _imageId) Ownable(msg.sender) { - require(address(_enclave) != address(0), EnclaveAddressZero()); - require(address(_verifier) != address(0), VerifierAddressZero()); - require(address(_honkVerifier) != address(0), InvalidHonkVerifier()); + constructor(IEnclave _enclave, IRiscZeroVerifier _risc0Verifier, HonkVerifier _honkVerifier, bytes32 _imageId) Ownable(msg.sender) { + if (address(_enclave) == address(0)) revert EnclaveAddressZero(); + if (address(_risc0Verifier) == address(0)) revert Risc0VerifierAddressZero(); + if (address(_honkVerifier) == address(0)) revert InvalidHonkVerifier(); enclave = _enclave; - verifier = _verifier; - HONK_VERIFIER = _honkVerifier; + risc0Verifier = _risc0Verifier; + honkVerifier = _honkVerifier; authorizedContracts[address(_enclave)] = true; imageId = _imageId; } - /// @notice Sets the Round data. Can only be set once. + /// @notice Sets the Merkle root for an E3 program. Can only be set once. + /// @param _e3Id The E3 program ID /// @param _root The Merkle root to set. - /// @param _token The governance token address. - /// @param _balanceThreshold The minimum balance required. - function setRoundData(uint256 _e3Id, uint256 _root, address _token, uint256 _balanceThreshold) external onlyOwner { - if (isRoundsDataSet[_e3Id]) revert RoundDataAlreadySet(); - - isRoundsDataSet[_e3Id] = true; + function setMerkleRoot(uint256 _e3Id, uint256 _root) external onlyOwner { + if (_root == 0) revert InvalidMerkleRoot(); + if (e3Data[_e3Id].merkleRoot != 0) revert MerkleRootAlreadySet(); - roundsData[_e3Id] = RoundData({ token: _token, balanceThreshold: _balanceThreshold, censusMerkleRoot: _root }); + e3Data[_e3Id].merkleRoot = _root; } /// @notice Set the Image ID for the guest program @@ -113,30 +90,29 @@ contract CRISPProgram is IE3Program, Ownable { imageId = _imageId; } - /// @notice Set the RISC Zero verifier address - /// @param _verifier The new RISC Zero verifier address - function setVerifier(IRiscZeroVerifier _verifier) external onlyOwner { - if (address(_verifier) == address(0)) revert VerifierAddressZero(); - verifier = _verifier; + /// @notice Set the RISC Zero verifier. + /// @param _risc0Verifier The new RISC Zero verifier address + function setRisc0Verifier(IRiscZeroVerifier _risc0Verifier) external onlyOwner { + if (address(_risc0Verifier) == address(0)) revert Risc0VerifierAddressZero(); + risc0Verifier = _risc0Verifier; } /// @notice Get the params hash for an E3 program /// @param e3Id The E3 program ID /// @return The params hash function getParamsHash(uint256 e3Id) public view returns (bytes32) { - return paramsHashes[e3Id]; + return e3Data[e3Id].paramsHash; } - /// @notice Validate the E3 program parameters - /// @param e3Id The E3 program ID - /// @param e3ProgramParams The E3 program parameters + /// @inheritdoc IE3Program function validate(uint256 e3Id, uint256, bytes calldata e3ProgramParams, bytes calldata) external returns (bytes32) { - require(authorizedContracts[msg.sender] || msg.sender == owner(), CallerNotAuthorized()); - require(paramsHashes[e3Id] == bytes32(0), E3AlreadyInitialized()); - paramsHashes[e3Id] = keccak256(e3ProgramParams); + if (!authorizedContracts[msg.sender] && msg.sender != owner()) revert CallerNotAuthorized(); + if (e3Data[e3Id].paramsHash != bytes32(0)) revert E3AlreadyInitialized(); + + e3Data[e3Id].paramsHash = keccak256(e3ProgramParams); - // we need to init the inputs merkle tree for this e3Id - votes[e3Id]._init(TREE_DEPTH); + // Initialize the votes Merkle tree for this E3 ID. + e3Data[e3Id].votes._init(TREE_DEPTH); return ENCRYPTION_SCHEME_ID; } @@ -144,9 +120,11 @@ contract CRISPProgram is IE3Program, Ownable { /// @inheritdoc IE3Program function validateInput(uint256 e3Id, address, bytes memory data) external { // it should only be called via Enclave for now - require(authorizedContracts[msg.sender] || msg.sender == owner(), CallerNotAuthorized()); + if (!authorizedContracts[msg.sender] && msg.sender != owner()) revert CallerNotAuthorized(); + // We need to ensure that the CRISP admin set the merkle root of the census. - if (!isRoundsDataSet[e3Id]) revert RoundDataNotSet(); + // TODO: Uncomment this when we make the merkle root a public input of the circuit. + // if (e3Data[e3Id].merkleRoot == 0) revert MerkleRootNotSet(); if (data.length == 0) revert EmptyInputData(); @@ -156,47 +134,23 @@ contract CRISPProgram is IE3Program, Ownable { (uint40 voteIndex, bool isFirstVote) = _processVote(e3Id, slotAddress, voteBytes); + // Set public inputs for the proof. Order must match Noir circuit. bytes32[] memory noirPublicInputs = new bytes32[](2 + vote.length); - // Set public inputs for the proof. Order must match Noir circuit. noirPublicInputs[0] = bytes32(uint256(uint160(slotAddress))); - // Pass isFirstVote flag to verifier (1 = first vote, 0 = re-vote) noirPublicInputs[1] = bytes32(uint256(isFirstVote ? 1 : 0)); - - // Set the encrypted vote to the noir public inputs. for (uint256 i = 0; i < vote.length; i++) { noirPublicInputs[i + 2] = vote[i]; } // Check if the ciphertext was encrypted correctly - if (!HONK_VERIFIER.verify(noirProof, noirPublicInputs)) { + if (!honkVerifier.verify(noirProof, noirPublicInputs)) { revert InvalidNoirProof(); } emit InputPublished(e3Id, voteBytes, voteIndex); } - /// @notice Process a vote: insert or update in the merkle tree depending - /// on whether it's the first vote or an override. - function _processVote(uint256 e3Id, address slotAddress, bytes memory vote) internal returns (uint40 voteIndex, bool isFirstVote) { - uint40 storedIndexPlusOne = voteSlots[e3Id][slotAddress]; - - // we treat the index 0 as not voted yet - // any valid index will be index + 1 - if (storedIndexPlusOne == 0) { - // FIRST VOTE - isFirstVote = true; - voteIndex = votes[e3Id].numberOfLeaves; - voteSlots[e3Id][slotAddress] = voteIndex + 1; - votes[e3Id]._insert(PoseidonT3.hash([uint256(keccak256(vote)), voteIndex])); - } else { - // RE-VOTE - isFirstVote = false; - voteIndex = storedIndexPlusOne - 1; - votes[e3Id]._update(PoseidonT3.hash([uint256(keccak256(vote)), voteIndex]), voteIndex); - } - } - /// @notice Decode the tally from the plaintext output /// @param e3Id The E3 program ID /// @return yes The number of yes votes @@ -235,30 +189,49 @@ contract CRISPProgram is IE3Program, Ownable { return (yes, no); } - /// @notice Verify the proof - /// @param e3Id The E3 program ID - /// @param ciphertextOutputHash The hash of the ciphertext output - /// @param proof The proof to verify + /// @inheritdoc IE3Program function verify(uint256 e3Id, bytes32 ciphertextOutputHash, bytes memory proof) external view override returns (bool) { - require(paramsHashes[e3Id] != bytes32(0), E3DoesNotExist()); - bytes32 inputRoot = bytes32(votes[e3Id]._root(TREE_DEPTH)); + if (e3Data[e3Id].paramsHash == bytes32(0)) revert E3DoesNotExist(); + bytes32 inputRoot = bytes32(e3Data[e3Id].votes._root(TREE_DEPTH)); bytes memory journal = new bytes(396); // (32 + 1) * 4 * 3 - encodeLengthPrefixAndHash(journal, 0, ciphertextOutputHash); - encodeLengthPrefixAndHash(journal, 132, paramsHashes[e3Id]); - encodeLengthPrefixAndHash(journal, 264, inputRoot); + _encodeLengthPrefixAndHash(journal, 0, ciphertextOutputHash); + _encodeLengthPrefixAndHash(journal, 132, e3Data[e3Id].paramsHash); + _encodeLengthPrefixAndHash(journal, 264, inputRoot); - verifier.verify(proof, imageId, sha256(journal)); + risc0Verifier.verify(proof, imageId, sha256(journal)); return true; } + /// @notice Process a vote: insert or update in the merkle tree depending + /// on whether it's the first vote or an override. + function _processVote(uint256 e3Id, address slotAddress, bytes memory vote) internal returns (uint40 voteIndex, bool isFirstVote) { + uint40 storedIndexPlusOne = e3Data[e3Id].voteSlots[slotAddress]; + + // we treat the index 0 as not voted yet + // any valid index will be index + 1 + if (storedIndexPlusOne == 0) { + // FIRST VOTE + isFirstVote = true; + voteIndex = e3Data[e3Id].votes.numberOfLeaves; + e3Data[e3Id].voteSlots[slotAddress] = voteIndex + 1; + e3Data[e3Id].votes._insert(PoseidonT3.hash([uint256(keccak256(vote)), voteIndex])); + } else { + // RE-VOTE + isFirstVote = false; + voteIndex = storedIndexPlusOne - 1; + e3Data[e3Id].votes._update(PoseidonT3.hash([uint256(keccak256(vote)), voteIndex]), voteIndex); + } + } + /// @notice Encode length prefix and hash /// @param journal The journal to encode into /// @param startIndex The start index in the journal /// @param hashVal The hash value to encode - function encodeLengthPrefixAndHash(bytes memory journal, uint256 startIndex, bytes32 hashVal) internal pure { + function _encodeLengthPrefixAndHash(bytes memory journal, uint256 startIndex, bytes32 hashVal) internal pure { journal[startIndex] = 0x20; startIndex += 4; + for (uint256 i = 0; i < 32; i++) { journal[startIndex + i * 4] = hashVal[i]; } diff --git a/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockCRISPProgram.sol b/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockCRISPProgram.sol deleted file mode 100644 index f817eef810..0000000000 --- a/examples/CRISP/packages/crisp-contracts/contracts/Mocks/MockCRISPProgram.sol +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-only -// -// This file is provided WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY -// or FITNESS FOR A PARTICULAR PURPOSE. -pragma solidity >=0.8.27; - -import { IRiscZeroVerifier } from "risc0/IRiscZeroVerifier.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IE3Program } from "@enclave-e3/contracts/contracts/interfaces/IE3Program.sol"; -import { IEnclave } from "@enclave-e3/contracts/contracts/interfaces/IEnclave.sol"; -import { E3 } from "@enclave-e3/contracts/contracts/interfaces/IE3.sol"; -import { LazyIMTData, InternalLazyIMT } from "@zk-kit/lazy-imt.sol/InternalLazyIMT.sol"; - -import { HonkVerifier } from "../CRISPVerifier.sol"; - -contract MockCRISPProgram is IE3Program, Ownable { - using InternalLazyIMT for LazyIMTData; - /// @notice a structure that holds the round data - struct RoundData { - /// @notice The governance token address. - address token; - /// @notice The minimum balance required to pass the validation. - uint256 balanceThreshold; - /// @notice The Merkle root of the census. - uint256 censusMerkleRoot; - } - - // Constants - bytes32 public constant ENCRYPTION_SCHEME_ID = keccak256("fhe.rs:BFV"); - - // The depth of the input merkle tree - uint8 public constant TREE_DEPTH = 20; - - // State variables - IEnclave public enclave; - IRiscZeroVerifier public verifier; - HonkVerifier private immutable HONK_VERIFIER; - bytes32 public imageId; - - /// @notice the round data - RoundData public roundData; - /// @notice whether the round data has been set - bool public isDataSet; - - /// @notice Half of the largest minimum degree used to fit votes - /// inside the plaintext polynomial - uint256 public constant HALF_LARGEST_MINIMUM_DEGREE = 28; - - // Mappings - mapping(address => bool) public authorizedContracts; - mapping(uint256 e3Id => bytes32 paramsHash) public paramsHashes; - /// @notice Mapping to store votes slot indices. Each eligible voter has their own slot - /// to store their vote inside the merkle tree. - mapping(uint256 e3Id => mapping(address slot => uint40 index)) public voteSlots; - mapping(uint256 e3Id => LazyIMTData) public votes; - - // Errors - error CallerNotAuthorized(); - error E3AlreadyInitialized(); - error E3DoesNotExist(); - error EnclaveAddressZero(); - error VerifierAddressZero(); - - /// @notice The error emitted when the honk verifier address is invalid. - error InvalidHonkVerifier(); - /// @notice The error emitted when the input data is empty. - error EmptyInputData(); - /// @notice The error emitted when the input data is invalid. - error InvalidInputData(bytes reason); - /// @notice The error emitted when the Noir proof is invalid. - error InvalidNoirProof(); - /// @notice The error emitted when the round data is not set. - error RoundDataNotSet(); - /// @notice The error emitted when trying to set the round data more than once. - error RoundDataAlreadySet(); - - /// @notice The event emitted when an input is published. - event InputPublished(uint256 indexed e3Id, bytes vote, uint256 index); - - /// @notice Initialize the contract, binding it to a specified RISC Zero verifier. - /// @param _enclave The enclave address - /// @param _verifier The RISC Zero verifier address - /// @param _honkVerifier The honk verifier address - /// @param _imageId The image ID for the guest program - constructor(IEnclave _enclave, IRiscZeroVerifier _verifier, HonkVerifier _honkVerifier, bytes32 _imageId) Ownable(msg.sender) { - require(address(_enclave) != address(0), EnclaveAddressZero()); - require(address(_verifier) != address(0), VerifierAddressZero()); - require(address(_honkVerifier) != address(0), InvalidHonkVerifier()); - - enclave = _enclave; - verifier = _verifier; - HONK_VERIFIER = _honkVerifier; - authorizedContracts[address(_enclave)] = true; - imageId = _imageId; - } - - /// @notice Sets the Round data. Can only be set once. - /// @param _root The Merkle root to set. - /// @param _token The governance token address. - /// @param _balanceThreshold The minimum balance required. - function setRoundData(uint256 _root, address _token, uint256 _balanceThreshold) external onlyOwner { - if (isDataSet) revert RoundDataAlreadySet(); - - isDataSet = true; - - roundData = RoundData({ token: _token, balanceThreshold: _balanceThreshold, censusMerkleRoot: _root }); - } - - /// @notice Set the Image ID for the guest program - /// @param _imageId The new image ID. - function setImageId(bytes32 _imageId) external onlyOwner { - imageId = _imageId; - } - - /// @notice Set the RISC Zero verifier address - /// @param _verifier The new RISC Zero verifier address - function setVerifier(IRiscZeroVerifier _verifier) external onlyOwner { - if (address(_verifier) == address(0)) revert VerifierAddressZero(); - verifier = _verifier; - } - - /// @notice Get the params hash for an E3 program - /// @param e3Id The E3 program ID - /// @return The params hash - function getParamsHash(uint256 e3Id) public view returns (bytes32) { - return paramsHashes[e3Id]; - } - - /// @notice Validate the E3 program parameters - /// @param e3Id The E3 program ID - /// @param e3ProgramParams The E3 program parameters - function validate(uint256 e3Id, uint256, bytes calldata e3ProgramParams, bytes calldata) external returns (bytes32) { - require(authorizedContracts[msg.sender] || msg.sender == owner(), CallerNotAuthorized()); - require(paramsHashes[e3Id] == bytes32(0), E3AlreadyInitialized()); - paramsHashes[e3Id] = keccak256(e3ProgramParams); - - return ENCRYPTION_SCHEME_ID; - } - - /// @inheritdoc IE3Program - function validateInput(uint256 e3Id, address, bytes memory data) external { - if (data.length == 0) revert EmptyInputData(); - - (, , bytes memory vote, ) = abi.decode(data, (bytes, bytes32[], bytes, address)); - } - - /// @notice Decode the tally from the plaintext output - /// @param e3Id The E3 program ID - /// @return yes The number of yes votes - /// @return no The number of no votes - function decodeTally(uint256 e3Id) public view returns (uint256 yes, uint256 no) { - // fetch from enclave - E3 memory e3 = enclave.getE3(e3Id); - - // abi decode it into an array of uint256 - uint256[] memory tally = abi.decode(e3.plaintextOutput, (uint256[])); - - /// @notice We want to completely ignore anything outside of the coefficients - /// we agreed to store out votes on. - uint256 halfD = tally.length / 2; - uint256 START_INDEX_Y = halfD - HALF_LARGEST_MINIMUM_DEGREE; - uint256 START_INDEX_N = tally.length - HALF_LARGEST_MINIMUM_DEGREE; - - // first weight (we are converting back from bits to integer) - uint256 weight = 2 ** (HALF_LARGEST_MINIMUM_DEGREE - 1); - - // Convert yes votes - for (uint256 i = START_INDEX_Y; i < halfD; i++) { - yes += tally[i] * weight; - weight /= 2; // Right shift equivalent - } - - // Reset weight for no votes - weight = 2 ** (HALF_LARGEST_MINIMUM_DEGREE - 1); - - // Convert no votes - for (uint256 i = START_INDEX_N; i < tally.length; i++) { - no += tally[i] * weight; - weight /= 2; - } - - return (yes, no); - } - - /// @notice Verify the proof - /// @param e3Id The E3 program ID - /// @param ciphertextOutputHash The hash of the ciphertext output - /// @param proof The proof to verify - function verify(uint256 e3Id, bytes32 ciphertextOutputHash, bytes memory proof) external view override returns (bool) { - require(paramsHashes[e3Id] != bytes32(0), E3DoesNotExist()); - bytes32 inputRoot = bytes32(votes[e3Id]._root(TREE_DEPTH)); - bytes memory journal = new bytes(396); // (32 + 1) * 4 * 3 - - encodeLengthPrefixAndHash(journal, 0, ciphertextOutputHash); - encodeLengthPrefixAndHash(journal, 132, paramsHashes[e3Id]); - encodeLengthPrefixAndHash(journal, 264, inputRoot); - - verifier.verify(proof, imageId, sha256(journal)); - return true; - } - - /// @notice Encode length prefix and hash - /// @param journal The journal to encode into - /// @param startIndex The start index in the journal - /// @param hashVal The hash value to encode - function encodeLengthPrefixAndHash(bytes memory journal, uint256 startIndex, bytes32 hashVal) internal pure { - journal[startIndex] = 0x20; - startIndex += 4; - for (uint256 i = 0; i < 32; i++) { - journal[startIndex + i * 4] = hashVal[i]; - } - } -} diff --git a/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts b/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts index a513c7fefd..21bc545892 100644 --- a/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts +++ b/examples/CRISP/packages/crisp-contracts/deploy/crisp.ts @@ -8,10 +8,9 @@ import { readDeploymentArgs, storeDeploymentArgs } from '@enclave-e3/contracts/s import { Enclave__factory as EnclaveFactory } from '@enclave-e3/contracts/types' import { readFileSync } from 'fs' -import { ContractFactory } from 'ethers' import hre from 'hardhat' -import { MockCRISPProgram__factory as MockCRISPProgramFactory, CRISPProgram__factory as CRISPProgramFactory } from '../types' +import { CRISPProgram__factory as CRISPProgramFactory } from '../types' const imageIdContent = readFileSync('../../.enclave/generated/contracts/ImageID.sol', 'utf-8') const match = imageIdContent.match(/bytes32 public constant PROGRAM_ID = bytes32\((0x[a-fA-F0-9]+)\)/) @@ -28,7 +27,6 @@ export const deployCRISPContracts = async () => { const chain = hre.globalOptions.network const useMockVerifier = Boolean(process.env.USE_MOCK_VERIFIER) - const useMockInputValidator = Boolean(process.env.USE_MOCK_INPUT_VALIDATOR) console.log('useMockVerifier', useMockVerifier) @@ -65,26 +63,13 @@ export const deployCRISPContracts = async () => { chain, ) - let crispFactory: ContractFactory - - if (useMockInputValidator) { - console.log('Using MockCRISPProgram') - crispFactory = await ethers.getContractFactory( - MockCRISPProgramFactory.abi, - MockCRISPProgramFactory.linkBytecode({ - 'npm/poseidon-solidity@0.0.5/PoseidonT3.sol:PoseidonT3': poseidonT3Address, - }), - owner, - ) - } else { - crispFactory = await ethers.getContractFactory( - CRISPProgramFactory.abi, - CRISPProgramFactory.linkBytecode({ - 'npm/poseidon-solidity@0.0.5/PoseidonT3.sol:PoseidonT3': poseidonT3Address, - }), - owner, - ) - } + const crispFactory = await ethers.getContractFactory( + CRISPProgramFactory.abi, + CRISPProgramFactory.linkBytecode({ + 'npm/poseidon-solidity@0.0.5/PoseidonT3.sol:PoseidonT3': poseidonT3Address, + }), + owner, + ) const crisp = await crispFactory.deploy(enclaveAddress, verifier, honkVerifierAddress, IMAGE_ID) diff --git a/examples/CRISP/packages/crisp-contracts/package.json b/examples/CRISP/packages/crisp-contracts/package.json index 21360666b2..3134d06c6d 100644 --- a/examples/CRISP/packages/crisp-contracts/package.json +++ b/examples/CRISP/packages/crisp-contracts/package.json @@ -34,7 +34,6 @@ "clean:deployments": "hardhat utils:clean-deployments", "deploy:contracts": "hardhat run deploy/deploy.ts", "deploy:contracts:full": "export DEPLOY_ENCLAVE=true && pnpm deploy:contracts", - "deploy:contracts:full:mock": "export DEPLOY_ENCLAVE=true && export USE_MOCK_VERIFIER=true && export USE_MOCK_INPUT_VALIDATOR=true && pnpm deploy:contracts", "test": "hardhat test mocha", "verify": "hardhat run deploy/verify.ts" }, diff --git a/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts b/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts index 59fa5ea80a..283697fd1d 100644 --- a/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts +++ b/examples/CRISP/packages/crisp-contracts/tests/crisp.contracts.test.ts @@ -15,7 +15,7 @@ import { generateMerkleTree, } from '@crisp-e3/sdk' import { expect } from 'chai' -import { deployCRISPProgram, deployHonkVerifier, deployMockEnclave, nonZeroAddress, ethers } from './utils' +import { deployCRISPProgram, deployHonkVerifier, deployMockEnclave, ethers } from './utils' let publicKey = generatePublicKey() @@ -108,7 +108,7 @@ describe('CRISP Contracts', function () { const encodedProof = encodeSolidityProof(proof) // Call next functions with fake data for testing. - await crispProgram.setRoundData(e3Id, merkleTree.root, nonZeroAddress, 1n) + await crispProgram.setMerkleRoot(e3Id, merkleTree.root) await crispProgram.validate(e3Id, 0n, '0x', '0x') // If it doesn't throw, the test is successful. diff --git a/examples/CRISP/packages/crisp-sdk/README.md b/examples/CRISP/packages/crisp-sdk/README.md index 8b5feec2a2..993584c7ab 100644 --- a/examples/CRISP/packages/crisp-sdk/README.md +++ b/examples/CRISP/packages/crisp-sdk/README.md @@ -1,7 +1,7 @@ # CRISP SDK -TypeScript SDK for interacting with CRISP (Coercion-Resistant Impartial Selection Protocol) and -the CRISP server. +TypeScript SDK for interacting with CRISP (Coercion-Resistant Impartial Selection Protocol) and the +CRISP server. ## Installation diff --git a/examples/CRISP/server/src/server/indexer.rs b/examples/CRISP/server/src/server/indexer.rs index aeaa446576..0706bc350a 100644 --- a/examples/CRISP/server/src/server/indexer.rs +++ b/examples/CRISP/server/src/server/indexer.rs @@ -138,16 +138,12 @@ pub async fn register_e3_requested( .with_context(|| format!("[e3_id={}] Merkle root is not valid hex", e3_id))?; let merkle_root_u256 = U256::from_be_slice(&merkle_root_bytes); - // Convert balance_threshold from BigUint to U256. - let balance_threshold_bytes = balance_threshold.to_bytes_be(); - let balance_threshold_u256 = U256::from_be_slice(&balance_threshold_bytes); - // Convert e3Id from u64 to U256 let e3_id_u256 = U256::from(e3_id); info!( - "[e3_id={}] Calling setRoundData with root: {}, token: {}, threshold: {}", - e3_id, merkle_root_u256, token_address, balance_threshold_u256 + "[e3_id={}] Calling setMerkleRoot with root: {}", + e3_id, merkle_root_u256 ); let contract = CRISPContractFactory::create_write( @@ -161,14 +157,14 @@ pub async fn register_e3_requested( })?; let receipt = contract - .set_round_data(e3_id_u256, merkle_root_u256, token_address, balance_threshold_u256) + .set_merkle_root(e3_id_u256, merkle_root_u256) .await .with_context(|| { - format!("[e3_id={}] Failed to call setRoundData", e3_id) + format!("[e3_id={}] Failed to call setMerkleRoot", e3_id) })?; info!( - "[e3_id={}] setRoundData successful. TxHash: {:?}", + "[e3_id={}] setMerkleRoot successful. TxHash: {:?}", e3_id, receipt.transaction_hash );