diff --git a/build/ptau/pot12_final.ptau b/build/ptau/pot12_final.ptau deleted file mode 100644 index 67fd416c7..000000000 Binary files a/build/ptau/pot12_final.ptau and /dev/null differ diff --git a/circuits/bridgePoseidon/withdraw.circom b/circuits/bridgePoseidon/withdraw.circom index 9ef1cb733..49154ec24 100644 --- a/circuits/bridgePoseidon/withdraw.circom +++ b/circuits/bridgePoseidon/withdraw.circom @@ -1,5 +1,3 @@ -include "../../node_modules/circomlib/circuits/bitify.circom"; -include "../../node_modules/circomlib/circuits/pedersen.circom"; include "manyMerkleTree.circom"; // computes Poseidon(chainID, nullifier, secret) diff --git a/contracts/Bridge.sol b/contracts/Bridge.sol index 5638819e8..e43786ecf 100644 --- a/contracts/Bridge.sol +++ b/contracts/Bridge.sol @@ -11,7 +11,6 @@ import "./utils/Pausable.sol"; import "./utils/SafeMath.sol"; import "./utils/SafeCast.sol"; import "./interfaces/IDepositExecute.sol"; -import "./interfaces/IUpdateExecute.sol"; import "./interfaces/IExecutor.sol"; import "./interfaces/IERCHandler.sol"; diff --git a/contracts/anchors/LinkableAnchor.sol b/contracts/anchors/LinkableAnchor.sol deleted file mode 100644 index af397a795..000000000 --- a/contracts/anchors/LinkableAnchor.sol +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright 2021 Webb Technologies - * SPDX-License-Identifier: LGPL-3.0-only - */ - -pragma solidity ^0.8.0; - -import "./Anchor.sol"; - -abstract contract LinkableAnchor is Anchor { - constructor( - IVerifier _verifier, - IHasher _hasher, - uint256 _denomination, - uint32 _merkleTreeHeight, - uint32 _maxRoots - ) Anchor(_verifier, _hasher, _denomination, _merkleTreeHeight, _maxRoots) { - // set the sender as admin & bridge & handler address - // TODO: Properly set addresses and permissions - bridge = msg.sender; - admin = msg.sender; - handler = msg.sender; - } - - function setHandler(address _handler) onlyBridge external { - handler = _handler; - } - - function setBridge(address _bridge) onlyAdmin external { - bridge = _bridge; - } - - function recordHistory() external { - // add a new historical record by snapshotting the Anchor's current neighbors - bytes32[] memory history = getLatestNeighborRoots(); - rootHistory[latestHistoryIndex] = history; - // set the next history index modulo pruning length - latestHistoryIndex = latestHistoryIndex % pruningLength; - emit RootHistoryRecorded(block.timestamp, history); - } - - function addEdge( - uint8 sourceChainID, - bytes32 resourceID, - bytes32 root, - uint256 height - ) onlyHandler external payable nonReentrant { - require(edgeList.length < maxRoots, "This Anchor is at capacity"); - edgeExistsForChain[sourceChainID] = true; - uint index = edgeList.length; - Edge memory edge = Edge({ - chainID: sourceChainID, - resourceID: resourceID, - root: root, - height: height - }); - edgeList.push(edge); - edgeIndex[sourceChainID] = index; - emit EdgeAddition(sourceChainID, resourceID, height, root); - // emit update event - bytes32[] memory neighbors = getLatestNeighborRoots(); - neighbors[index] = root; - emit RootHistoryUpdate(block.timestamp, neighbors); - - } - - function updateEdge( - uint8 sourceChainID, - bytes32 resourceID, - bytes32 root, - uint256 height - ) onlyHandler external payable nonReentrant { - require(edgeExistsForChain[sourceChainID], "Chain must be integrated from the bridge before updates"); - require(edgeList[edgeIndex[sourceChainID]].height < height, "New height must be greater"); - uint index = edgeIndex[sourceChainID]; - // update the edge in the edge list - edgeList[index] = Edge({ - chainID: sourceChainID, - resourceID: resourceID, - root: root, - height: height - }); - emit EdgeUpdate(sourceChainID, resourceID, height, root); - // emit update event - bytes32[] memory neighbors = getLatestNeighborRoots(); - neighbors[index] = root; - emit RootHistoryUpdate(block.timestamp, neighbors); - } -} diff --git a/contracts/anchors/LinkableERC20Anchor.sol b/contracts/anchors/LinkableERC20Anchor.sol deleted file mode 100644 index 45661ce07..000000000 --- a/contracts/anchors/LinkableERC20Anchor.sol +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2021 Webb Technologies - * SPDX-License-Identifier: LGPL-3.0-only - */ - -pragma solidity ^0.8.0; - -import "./LinkableAnchor.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -contract LinkableERC20Anchor is LinkableAnchor { - using SafeERC20 for IERC20; - IERC20 public immutable token; - - constructor( - IVerifier _verifier, - IHasher _hasher, - uint256 _denomination, - uint32 _merkleTreeHeight, - uint32 _maxRoots, - IERC20 _token - ) LinkableAnchor(_verifier, _hasher, _denomination, _merkleTreeHeight, _maxRoots) { - token = _token; - } - - function _processDeposit() internal override { - require(msg.value == 0, "ETH value is supposed to be 0 for ERC20 instance"); - token.safeTransferFrom(msg.sender, address(this), denomination); - } - - function _processWithdraw( - address payable _recipient, - address payable _relayer, - uint256 _fee, - uint256 _refund - ) internal override { - require(msg.value == _refund, "Incorrect refund amount received by the contract"); - - token.safeTransfer(_recipient, denomination - _fee); - if (_fee > 0) { - token.safeTransfer(_relayer, _fee); - } - - if (_refund > 0) { - (bool success, ) = _recipient.call{ value: _refund }(""); - if (!success) { - // let's return _refund back to the relayer - _relayer.transfer(_refund); - } - } - } -} diff --git a/contracts/anchors/bridged/AnchorFactoryPoseidon.sol b/contracts/anchors/bridged/AnchorFactoryPoseidon.sol new file mode 100644 index 000000000..4f9a178c6 --- /dev/null +++ b/contracts/anchors/bridged/AnchorFactoryPoseidon.sol @@ -0,0 +1,47 @@ +/** + * Copyright 2021 Webb Technologies + * SPDX-License-Identifier: LGPL-3.0-only + */ + +pragma solidity ^0.8.0; + +import "./LinkableERC20AnchorPoseidon2.sol"; + +contract AnchorFactoryPoseidon { + function createAnchor2( + IVerifier _verifier, + IPoseidonT3 _hasher, + uint256 _denomination, + uint32 _merkleTreeHeight, + uint32 _chainID, + IERC20 _token + ) external { + new LinkableERC20AnchorPoseidon2( + _verifier, + _hasher, + _denomination, + _merkleTreeHeight, + _chainID, + _token + ); + } + + function create2Anchor2( + bytes32 _salt, + IVerifier _verifier, + IPoseidonT3 _hasher, + uint256 _denomination, + uint32 _merkleTreeHeight, + uint32 _chainID, + IERC20 _token + ) external { + new LinkableERC20AnchorPoseidon2{salt: _salt}( + _verifier, + _hasher, + _denomination, + _merkleTreeHeight, + _chainID, + _token + ); + } +} \ No newline at end of file diff --git a/contracts/anchors/poseidon/AnchorPoseidon2.sol b/contracts/anchors/bridged/AnchorPoseidon2.sol similarity index 98% rename from contracts/anchors/poseidon/AnchorPoseidon2.sol rename to contracts/anchors/bridged/AnchorPoseidon2.sol index e9dd08d01..1afe95f3f 100644 --- a/contracts/anchors/poseidon/AnchorPoseidon2.sol +++ b/contracts/anchors/bridged/AnchorPoseidon2.sol @@ -169,10 +169,6 @@ abstract contract AnchorPoseidon2 is MerkleTreePoseidon, ReentrancyGuard { } } - function hasEdge(uint8 _chainID) public view returns (bool) { - return edgeExistsForChain[_chainID]; - } - modifier onlyAdmin() { require(msg.sender == admin, 'sender is not the admin'); _; diff --git a/contracts/anchors/poseidon/LinkableAnchorPoseidon2.sol b/contracts/anchors/bridged/LinkableAnchorPoseidon2.sol similarity index 79% rename from contracts/anchors/poseidon/LinkableAnchorPoseidon2.sol rename to contracts/anchors/bridged/LinkableAnchorPoseidon2.sol index 639012055..40205923d 100644 --- a/contracts/anchors/poseidon/LinkableAnchorPoseidon2.sol +++ b/contracts/anchors/bridged/LinkableAnchorPoseidon2.sol @@ -6,15 +6,16 @@ pragma solidity ^0.8.0; import "./AnchorPoseidon2.sol"; +import "../../interfaces/ILinkableAnchor.sol"; -abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2 { +abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2, ILinkableAnchor { constructor( IVerifier _verifier, IPoseidonT3 _hasher, uint256 _denomination, uint32 _merkleTreeHeight, - uint32 _maxRoots - ) AnchorPoseidon2(_verifier, _hasher, _denomination, _merkleTreeHeight, _maxRoots) { + uint32 _chainID + ) AnchorPoseidon2(_verifier, _hasher, _denomination, _merkleTreeHeight, _chainID) { // set the sender as admin & bridge & handler address // TODO: Properly set addresses and permissions bridge = msg.sender; @@ -22,15 +23,19 @@ abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2 { handler = msg.sender; } - function setHandler(address _handler) onlyBridge external { + function setHandler(address _handler) onlyBridge override external { handler = _handler; } - function setBridge(address _bridge) onlyAdmin external { + function setBridge(address _bridge) onlyAdmin override external { bridge = _bridge; } - function recordHistory() external { + function hasEdge(uint8 _chainID) override external view returns (bool) { + return edgeExistsForChain[_chainID]; + } + + function recordHistory() override external { // add a new historical record by snapshotting the Anchor's current neighbors bytes32[1] memory history = getLatestNeighborRoots(); rootHistory[latestHistoryIndex] = history; @@ -44,7 +49,7 @@ abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2 { bytes32 resourceID, bytes32 root, uint256 height - ) onlyHandler external payable nonReentrant { + ) onlyHandler override external payable nonReentrant { require(edgeList.length < 1, "This Anchor is at capacity"); edgeExistsForChain[sourceChainID] = true; uint index = edgeList.length; @@ -68,7 +73,7 @@ abstract contract LinkableAnchorPoseidon2 is AnchorPoseidon2 { bytes32 resourceID, bytes32 root, uint256 height - ) onlyHandler external payable nonReentrant { + ) onlyHandler override external payable nonReentrant { require(edgeExistsForChain[sourceChainID], "Chain must be integrated from the bridge before updates"); require(edgeList[edgeIndex[resourceID]].height < height, "New height must be greater"); uint index = edgeIndex[resourceID]; diff --git a/contracts/anchors/poseidon/LinkableERC20AnchorPoseidon2.sol b/contracts/anchors/bridged/LinkableERC20AnchorPoseidon2.sol similarity index 96% rename from contracts/anchors/poseidon/LinkableERC20AnchorPoseidon2.sol rename to contracts/anchors/bridged/LinkableERC20AnchorPoseidon2.sol index aea81f74d..717451cf1 100644 --- a/contracts/anchors/poseidon/LinkableERC20AnchorPoseidon2.sol +++ b/contracts/anchors/bridged/LinkableERC20AnchorPoseidon2.sol @@ -18,9 +18,9 @@ contract LinkableERC20AnchorPoseidon2 is LinkableAnchorPoseidon2 { IPoseidonT3 _hasher, uint256 _denomination, uint32 _merkleTreeHeight, - uint32 _maxRoots, + uint32 _chainID, IERC20 _token - ) LinkableAnchorPoseidon2(_verifier, _hasher, _denomination, _merkleTreeHeight, _maxRoots) { + ) LinkableAnchorPoseidon2(_verifier, _hasher, _denomination, _merkleTreeHeight, _chainID) { token = _token; } diff --git a/contracts/anchors/poseidon/AnchorFactoryPoseidon.sol b/contracts/anchors/poseidon/AnchorFactoryPoseidon.sol deleted file mode 100644 index dcf54c846..000000000 --- a/contracts/anchors/poseidon/AnchorFactoryPoseidon.sol +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright 2021 Webb Technologies - * SPDX-License-Identifier: LGPL-3.0-only - */ - -pragma solidity ^0.8.0; - -import "./AnchorPoseidon2.sol"; - -contract AnchorFactoryPoseidon { - function createAnchor(uint8 chainID, bytes32 resourceID, uint size) external { - - } -} \ No newline at end of file diff --git a/contracts/anchors/Anchor.sol b/contracts/anchors/solo/AnchorPoseidon.sol similarity index 65% rename from contracts/anchors/Anchor.sol rename to contracts/anchors/solo/AnchorPoseidon.sol index d837195c4..004997678 100644 --- a/contracts/anchors/Anchor.sol +++ b/contracts/anchors/solo/AnchorPoseidon.sol @@ -5,53 +5,29 @@ pragma solidity ^0.8.0; -import "../trees/MerkleTreeMiMC.sol"; +import "../../trees/MerkleTreePoseidon.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; interface IVerifier { function verifyProof(bytes memory _proof, uint256[6] memory _input) external returns (bool); } -abstract contract Anchor is MerkleTreeMiMC, ReentrancyGuard { +abstract contract AnchorPoseidon is MerkleTreePoseidon, ReentrancyGuard { address public bridge; address public admin; address public handler; IVerifier public immutable verifier; uint256 public immutable denomination; - uint256 public immutable maxRoots; - struct Edge { - uint8 chainID; - bytes32 resourceID; - bytes32 root; - uint256 height; - } - - // maps anchor sourceChainIDs to the index in the edge list - mapping(uint8 => uint256) public edgeIndex; - mapping(uint8 => bool) public edgeExistsForChain; - Edge[] public edgeList; // map to store used nullifier hashes mapping(bytes32 => bool) public nullifierHashes; // map to store all commitments to prevent accidental deposits with the same commitment mapping(bytes32 => bool) public commitments; - // map to store the history of root updates - mapping(uint => bytes32[]) public rootHistory; - // pruning length for root history (i.e. the # of history items to persist) - uint pruningLength; - // the latest history index that represents the next index to store history at % pruningLength - uint latestHistoryIndex; - // currency events event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp); event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee); - // bridge events - event EdgeAddition(uint8 chainID, bytes32 destResourceID, uint256 height, bytes32 merkleRoot); - event EdgeUpdate(uint8 chainID, bytes32 destResourceID, uint256 height, bytes32 merkleRoot); - event RootHistoryRecorded(uint timestamp, bytes32[] roots); - event RootHistoryUpdate(uint timestamp, bytes32[] roots); /** @dev The constructor @@ -62,20 +38,13 @@ abstract contract Anchor is MerkleTreeMiMC, ReentrancyGuard { */ constructor( IVerifier _verifier, - IHasher _hasher, + IPoseidonT3 _hasher, uint256 _denomination, - uint32 _merkleTreeHeight, - uint32 _maxRoots - ) MerkleTreeMiMC(_merkleTreeHeight, _hasher) { + uint32 _merkleTreeHeight + ) MerkleTreePoseidon(_merkleTreeHeight, _hasher) { require(_denomination > 0, "denomination should be greater than 0"); verifier = _verifier; denomination = _denomination; - maxRoots = _maxRoots; - // TODO: Handle pruning length in function signature - pruningLength = 100; - latestHistoryIndex = 0; - // TODO: Parameterize max rooots (length of array should be max roots) - rootHistory[latestHistoryIndex] = new bytes32[](_maxRoots); } /** @@ -152,30 +121,4 @@ abstract contract Anchor is MerkleTreeMiMC, ReentrancyGuard { } } } - /** @dev */ - function getLatestNeighborRoots() public view returns (bytes32[] memory roots) { - roots = new bytes32[](maxRoots); - for (uint256 i = 0; i < edgeList.length; i++) { - roots[i] = edgeList[i].root; - } - } - - function hasEdge(uint8 chainID) public view returns (bool) { - return edgeExistsForChain[chainID]; - } - - modifier onlyAdmin() { - require(msg.sender == admin, 'sender is not the admin'); - _; - } - - modifier onlyBridge() { - require(msg.sender == bridge, 'sender is not the bridge'); - _; - } - - modifier onlyHandler() { - require(msg.sender == handler, 'sender is not the handler'); - _; - } } diff --git a/contracts/anchors/ERC20Anchor.sol b/contracts/anchors/solo/ERC20AnchorPoseidon.sol similarity index 87% rename from contracts/anchors/ERC20Anchor.sol rename to contracts/anchors/solo/ERC20AnchorPoseidon.sol index 4d3928e36..1e73d246e 100644 --- a/contracts/anchors/ERC20Anchor.sol +++ b/contracts/anchors/solo/ERC20AnchorPoseidon.sol @@ -5,22 +5,21 @@ pragma solidity ^0.8.0; -import "./Anchor.sol"; +import "./AnchorPoseidon.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -contract ERC20Anchor is Anchor { +contract ERC20AnchorPoseidon is AnchorPoseidon { using SafeERC20 for IERC20; IERC20 public immutable token; constructor( IVerifier _verifier, - IHasher _hasher, + IPoseidonT3 _hasher, uint256 _denomination, uint32 _merkleTreeHeight, - uint32 _maxRoots, IERC20 _token - ) Anchor(_verifier, _hasher, _denomination, _merkleTreeHeight, _maxRoots) { + ) AnchorPoseidon(_verifier, _hasher, _denomination, _merkleTreeHeight) { token = _token; } diff --git a/contracts/anchors/NativeAnchor.sol b/contracts/anchors/solo/NativeAnchorPoseidon.sol similarity index 82% rename from contracts/anchors/NativeAnchor.sol rename to contracts/anchors/solo/NativeAnchorPoseidon.sol index 0af52fd9d..ff568fe8a 100644 --- a/contracts/anchors/NativeAnchor.sol +++ b/contracts/anchors/solo/NativeAnchorPoseidon.sol @@ -5,16 +5,15 @@ pragma solidity ^0.8.0; -import "./Anchor.sol"; +import "./AnchorPoseidon.sol"; -contract NativeAnchor is Anchor { +contract NativeAnchorPoseidon is AnchorPoseidon { constructor( IVerifier _verifier, - IHasher _hasher, + IPoseidonT3 _hasher, uint256 _denomination, - uint32 _merkleTreeHeight, - uint32 _maxRoots - ) Anchor(_verifier, _hasher, _denomination, _merkleTreeHeight, _maxRoots) {} + uint32 _merkleTreeHeight + ) AnchorPoseidon(_verifier, _hasher, _denomination, _merkleTreeHeight) {} function _processDeposit() internal override { require(msg.value == denomination, "Please send `mixDenomination` ETH along with transaction"); diff --git a/contracts/handlers/AnchorHandler.sol b/contracts/handlers/AnchorHandler.sol index 4297b4783..e561e74fc 100644 --- a/contracts/handlers/AnchorHandler.sol +++ b/contracts/handlers/AnchorHandler.sol @@ -6,9 +6,8 @@ pragma solidity ^0.8.0; pragma experimental ABIEncoderV2; -import "../interfaces/IUpdateExecute.sol"; +import "../interfaces/ILinkableAnchor.sol"; import "./HandlerHelpers.sol"; -import "../anchors/LinkableAnchor.sol"; import "../interfaces/IExecutor.sol"; /** @@ -16,7 +15,7 @@ import "../interfaces/IExecutor.sol"; @author Webb Technologies. @notice This contract is intended to be used with the Bridge contract. */ -contract AnchorHandler is IUpdateExecute, IExecutor, HandlerHelpers { +contract AnchorHandler is IExecutor, HandlerHelpers { struct UpdateRecord { address _tokenAddress; uint8 _sourceChainID; @@ -70,36 +69,6 @@ contract AnchorHandler is IUpdateExecute, IExecutor, HandlerHelpers { return _updateRecords[destId][updateNonce]; } - /** - @notice An update is initiatied by making a deposit in the destination Anchor contract. - @param sourceChainID Chain ID of chain tokens are expected to be bridged to. - @param updateNonce This value is generated as an ID by the Bridge contract. - @param updater The updater address. - @param data Consists of: {height} and {merkleRoot} - @notice Data passed into the function should be constructed as follows: - height uint256 bytes 0 - 32 - merkleRoot bytes32 bytes 32 - 64 - @dev Depending if the corresponding {tokenAddress} for the parsed {resourceID} is - marked true in {_burnList}, deposited tokens will be burned, if not, they will be locked. - */ - function update( - bytes32 resourceID, - uint8 sourceChainID, - uint64 updateNonce, - address updater, - bytes calldata data - ) external override onlyBridge { - bytes32 merkleRoot; - uint256 height; - - (height, merkleRoot) = abi.decode(data, (uint, bytes32)); - - address anchorAddress = _resourceIDToContractAddress[resourceID]; - require(_contractWhitelist[anchorAddress], "provided tokenAddress is not whitelisted"); - - - } - /** @notice Proposal execution should be initiated when a proposal is finalized in the Bridge contract. by a relayer on the deposit's destination chain. @@ -119,7 +88,7 @@ contract AnchorHandler is IUpdateExecute, IExecutor, HandlerHelpers { require(_contractWhitelist[anchorAddress], "provided tokenAddress is not whitelisted"); - LinkableAnchor anchor = LinkableAnchor(anchorAddress); + ILinkableAnchor anchor = ILinkableAnchor(anchorAddress); if (anchor.hasEdge(chainID)) { anchor.updateEdge( diff --git a/contracts/interfaces/ILinkableAnchor.sol b/contracts/interfaces/ILinkableAnchor.sol new file mode 100644 index 000000000..334dbce3a --- /dev/null +++ b/contracts/interfaces/ILinkableAnchor.sol @@ -0,0 +1,25 @@ +/** + * Copyright 2021 Webb Technologies + * SPDX-License-Identifier: LGPL-3.0-only + */ + +pragma solidity ^0.8.0; + +interface ILinkableAnchor { + function setHandler(address _handler) external; + function setBridge(address _bridge) external; + function recordHistory() external; + function hasEdge(uint8 _chainID) external view returns (bool); + function addEdge( + uint8 sourceChainID, + bytes32 resourceID, + bytes32 root, + uint256 height + ) external payable; + function updateEdge( + uint8 sourceChainID, + bytes32 resourceID, + bytes32 root, + uint256 height + ) external payable; +} diff --git a/contracts/interfaces/IUpdateExecute.sol b/contracts/interfaces/IUpdateExecute.sol deleted file mode 100644 index 6c366d641..000000000 --- a/contracts/interfaces/IUpdateExecute.sol +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright 2021 Webb Technologies - * SPDX-License-Identifier: LGPL-3.0-only - */ - -pragma solidity ^0.8.0; - -/** - @title Interface for handler contracts that support Anchor updates. - @author ChainSafe Systems. - */ -interface IUpdateExecute { - /** - @notice It is intended that deposit are made using the Bridge contract. - @param destinationChainID Chain ID deposit is expected to be bridged to. - @param depositNonce This value is generated as an ID by the Bridge contract. - @param updater The account triggering the update. - @param data Consists of additional data needed for a specific update like height and root data - */ - function update(bytes32 resourceID, uint8 destinationChainID, uint64 depositNonce, address updater, bytes calldata data) external; -} diff --git a/contracts/mocks/HasherMock.sol b/contracts/mocks/HasherMock.sol deleted file mode 100644 index 31d9c5d64..000000000 --- a/contracts/mocks/HasherMock.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../trees/MerkleTreeWithHistory.sol"; - -contract HasherMock is IHasher { - function MiMCSponge(uint256 in_xL, uint256 in_xR, uint256 key) override external pure returns (uint256 xL, uint256 xR) { - return (in_xL, in_xR); - } -} - -contract MiMCSponge220 is IHasher { - function MiMCSponge(uint256 in_xL, uint256 in_xR, uint256 key) external override view returns (uint256 xL, uint256 xR) { - return (0, 0); - } -} \ No newline at end of file diff --git a/contracts/mocks/MerkleTreeMiMCMock.sol b/contracts/mocks/MerkleTreeMiMCMock.sol deleted file mode 100644 index 54dd0f643..000000000 --- a/contracts/mocks/MerkleTreeMiMCMock.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../trees/MerkleTreeMiMC.sol"; - -contract MerkleTreeMiMCMock is MerkleTreeMiMC { - constructor(uint32 _treeLevels, IHasher _hasher) MerkleTreeMiMC(_treeLevels, _hasher) {} - - function insert(bytes32 _leaf) public { - _insert(_leaf); - } -} \ No newline at end of file diff --git a/contracts/trees/MerkleTreeMiMC.sol b/contracts/trees/MerkleTreeMiMC.sol deleted file mode 100644 index d432389fc..000000000 --- a/contracts/trees/MerkleTreeMiMC.sol +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2021 Webb Technologies - * SPDX-License-Identifier: LGPL-3.0-only - */ - -pragma solidity ^0.8.0; - -import "./MerkleTreeWithHistory.sol"; - -contract MerkleTreeMiMC is MerkleTreeWithHistory { - constructor(uint32 _levels, IHasher _hasher) MerkleTreeWithHistory(_levels, _hasher) { - for (uint32 i = 0; i < _levels; i++) { - filledSubtrees[i] = zeros(i); - } - - roots[0] = zeros(_levels - 1); - } - - /** - @dev Hash 2 tree leaves, returns MiMC(_left, _right) - */ - function hashLeftRight( - IHasher _hasher, - bytes32 _left, - bytes32 _right - ) override public view returns (bytes32) { - require(uint256(_left) < FIELD_SIZE, "_left should be inside the field"); - require(uint256(_right) < FIELD_SIZE, "_right should be inside the field"); - uint256 R = uint256(_left); - uint256 C = 0; - (R, C) = _hasher.MiMCSponge(R, C, 0); - R = addmod(R, uint256(_right), FIELD_SIZE); - (R, C) = _hasher.MiMCSponge(R, C, 0); - return bytes32(R); - } - - /// @dev provides Zero (Empty) elements for a MiMC MerkleTree. Up to 32 levels - function zeros(uint256 i) override public pure returns (bytes32) { - if (i == 0) return bytes32(0x2fe54c60d3acabf3343a35b6eba15db4821b340f76e741e2249685ed4899af6c); - else if (i == 1) return bytes32(0x256a6135777eee2fd26f54b8b7037a25439d5235caee224154186d2b8a52e31d); - else if (i == 2) return bytes32(0x1151949895e82ab19924de92c40a3d6f7bcb60d92b00504b8199613683f0c200); - else if (i == 3) return bytes32(0x20121ee811489ff8d61f09fb89e313f14959a0f28bb428a20dba6b0b068b3bdb); - else if (i == 4) return bytes32(0x0a89ca6ffa14cc462cfedb842c30ed221a50a3d6bf022a6a57dc82ab24c157c9); - else if (i == 5) return bytes32(0x24ca05c2b5cd42e890d6be94c68d0689f4f21c9cec9c0f13fe41d566dfb54959); - else if (i == 6) return bytes32(0x1ccb97c932565a92c60156bdba2d08f3bf1377464e025cee765679e604a7315c); - else if (i == 7) return bytes32(0x19156fbd7d1a8bf5cba8909367de1b624534ebab4f0f79e003bccdd1b182bdb4); - else if (i == 8) return bytes32(0x261af8c1f0912e465744641409f622d466c3920ac6e5ff37e36604cb11dfff80); - else if (i == 9) return bytes32(0x0058459724ff6ca5a1652fcbc3e82b93895cf08e975b19beab3f54c217d1c007); - else if (i == 10) return bytes32(0x1f04ef20dee48d39984d8eabe768a70eafa6310ad20849d4573c3c40c2ad1e30); - else if (i == 11) return bytes32(0x1bea3dec5dab51567ce7e200a30f7ba6d4276aeaa53e2686f962a46c66d511e5); - else if (i == 12) return bytes32(0x0ee0f941e2da4b9e31c3ca97a40d8fa9ce68d97c084177071b3cb46cd3372f0f); - else if (i == 13) return bytes32(0x1ca9503e8935884501bbaf20be14eb4c46b89772c97b96e3b2ebf3a36a948bbd); - else if (i == 14) return bytes32(0x133a80e30697cd55d8f7d4b0965b7be24057ba5dc3da898ee2187232446cb108); - else if (i == 15) return bytes32(0x13e6d8fc88839ed76e182c2a779af5b2c0da9dd18c90427a644f7e148a6253b6); - else if (i == 16) return bytes32(0x1eb16b057a477f4bc8f572ea6bee39561098f78f15bfb3699dcbb7bd8db61854); - else if (i == 17) return bytes32(0x0da2cb16a1ceaabf1c16b838f7a9e3f2a3a3088d9e0a6debaa748114620696ea); - else if (i == 18) return bytes32(0x24a3b3d822420b14b5d8cb6c28a574f01e98ea9e940551d2ebd75cee12649f9d); - else if (i == 19) return bytes32(0x198622acbd783d1b0d9064105b1fc8e4d8889de95c4c519b3f635809fe6afc05); - else if (i == 20) return bytes32(0x29d7ed391256ccc3ea596c86e933b89ff339d25ea8ddced975ae2fe30b5296d4); - else if (i == 21) return bytes32(0x19be59f2f0413ce78c0c3703a3a5451b1d7f39629fa33abd11548a76065b2967); - else if (i == 22) return bytes32(0x1ff3f61797e538b70e619310d33f2a063e7eb59104e112e95738da1254dc3453); - else if (i == 23) return bytes32(0x10c16ae9959cf8358980d9dd9616e48228737310a10e2b6b731c1a548f036c48); - else if (i == 24) return bytes32(0x0ba433a63174a90ac20992e75e3095496812b652685b5e1a2eae0b1bf4e8fcd1); - else if (i == 25) return bytes32(0x019ddb9df2bc98d987d0dfeca9d2b643deafab8f7036562e627c3667266a044c); - else if (i == 26) return bytes32(0x2d3c88b23175c5a5565db928414c66d1912b11acf974b2e644caaac04739ce99); - else if (i == 27) return bytes32(0x2eab55f6ae4e66e32c5189eed5c470840863445760f5ed7e7b69b2a62600f354); - else if (i == 28) return bytes32(0x002df37a2642621802383cf952bf4dd1f32e05433beeb1fd41031fb7eace979d); - else if (i == 29) return bytes32(0x104aeb41435db66c3e62feccc1d6f5d98d0a0ed75d1374db457cf462e3a1f427); - else if (i == 30) return bytes32(0x1f3c6fd858e9a7d4b0d1f38e256a09d81d5a5e3c963987e2d4b814cfab7c6ebb); - else if (i == 31) return bytes32(0x2c7a07d20dff79d01fecedc1134284a8d08436606c93693b67e333f671bf69cc); - else revert("Index out of bounds"); - } -} \ No newline at end of file diff --git a/package.json b/package.json index 2a5eaeabf..1f7d72704 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "protocol-solidity", - "version": "1.0.0", + "name": "darkwebb-solidity", + "version": "0.1.0", "main": "index.js", - "repository": "https://github.com/webb-tools/protocol-solidity.git", + "repository": "https://github.com/webb-tools/darkwebb-solidity.git", "author": "Webb Technologies", "license": "LGPL-3.0", "scripts": { diff --git a/test/anchor/addEdges.js b/test/anchor/addEdges.js index 98d48ec2c..44d83d5e1 100644 --- a/test/anchor/addEdges.js +++ b/test/anchor/addEdges.js @@ -8,9 +8,9 @@ const Ethers = require('ethers'); const Helpers = require('../helpers'); const assert = require('assert'); -const LinkableAnchorContract = artifacts.require("LinkableERC20Anchor"); +const LinkableAnchorContract = artifacts.require("LinkableERC20AnchorPoseidon2"); const Verifier = artifacts.require("Verifier"); -const Hasher = artifacts.require("HasherMock"); +const Hasher = artifacts.require("PoseidonT3"); const Token = artifacts.require("ERC20Mock"); const USDTToken = artifacts.require('IUSDT') diff --git a/test/anchor/anchor.js b/test/anchor/anchor.js index c2fc42086..8270e0266 100644 --- a/test/anchor/anchor.js +++ b/test/anchor/anchor.js @@ -243,7 +243,7 @@ contract('AnchorPoseidon2', (accounts) => { }) describe('#withdraw', () => { - it.only('should work', async () => { + it('should work', async () => { const chainID = 125; const deposit = generateDeposit(chainID); const user = accounts[4] diff --git a/test/anchor/updateEdges.js b/test/anchor/updateEdges.js index 40cccbaff..666118b61 100644 --- a/test/anchor/updateEdges.js +++ b/test/anchor/updateEdges.js @@ -8,9 +8,9 @@ const Helpers = require('../helpers'); const assert = require('assert'); - const LinkableAnchorContract = artifacts.require("LinkableERC20Anchor"); + const LinkableAnchorContract = artifacts.require("LinkableERC20AnchorPoseidon2"); const Verifier = artifacts.require("Verifier"); - const Hasher = artifacts.require("HasherMock"); + const Hasher = artifacts.require("PoseidonT3"); const Token = artifacts.require("ERC20Mock"); const USDTToken = artifacts.require('IUSDT') @@ -148,14 +148,14 @@ await TruffleAssert.passes(addEdge(edge, accounts[0])); const roots = await LinkableAnchorInstance.getLatestNeighborRoots(); - assert(roots.length == 100); - assert(roots[0] == edge.root); + assert.strictEqual(roots.length, 1); + assert.strictEqual(roots[0], edge.root); await TruffleAssert.passes(updateEdge(edgeUpdated, accounts[0])); const rootsUpdated = await LinkableAnchorInstance.getLatestNeighborRoots(); - assert(rootsUpdated.length == 100); - assert(rootsUpdated[0] == edgeUpdated.root); + assert.strictEqual(rootsUpdated.length, 1); + assert.strictEqual(rootsUpdated[0], edgeUpdated.root); }); it('Updating edge should emit correct EdgeUpdate event', async () => { @@ -198,7 +198,7 @@ const roots = await LinkableAnchorInstance.getLatestNeighborRoots(); TruffleAssert.eventEmitted(result, 'RootHistoryUpdate', (ev) => { - return ev.roots.toString().split(',')[0] == roots[0] + return ev.roots[0] == roots[0] }); }); }); diff --git a/test/bridge/cancelUpdateProposal.js b/test/bridge/cancelUpdateProposal.js index ac76f1d5c..ef60d3a28 100644 --- a/test/bridge/cancelUpdateProposal.js +++ b/test/bridge/cancelUpdateProposal.js @@ -10,9 +10,9 @@ const Helpers = require('../helpers'); const BridgeContract = artifacts.require("Bridge"); const AnchorHandlerContract = artifacts.require("AnchorHandler"); -const LinkableAnchorContract = artifacts.require("LinkableERC20Anchor"); +const LinkableAnchorContract = artifacts.require("LinkableERC20AnchorPoseidon2"); const Verifier = artifacts.require("Verifier"); -const Hasher = artifacts.require("HasherMock"); +const Hasher = artifacts.require("PoseidonT3"); const Token = artifacts.require("ERC20Mock"); contract('Bridge - [CancelUpdateProposal with relayerThreshold == 3]', async (accounts) => { diff --git a/test/bridge/createUpdateProposal.js b/test/bridge/createUpdateProposal.js index 8570de249..8e09e3bb9 100644 --- a/test/bridge/createUpdateProposal.js +++ b/test/bridge/createUpdateProposal.js @@ -10,9 +10,9 @@ const Helpers = require('../helpers'); const BridgeContract = artifacts.require("Bridge"); const AnchorHandlerContract = artifacts.require("AnchorHandler"); -const LinkableAnchorContract = artifacts.require("LinkableERC20Anchor"); +const LinkableAnchorContract = artifacts.require("LinkableERC20AnchorPoseidon2"); const Verifier = artifacts.require("Verifier"); -const Hasher = artifacts.require("HasherMock"); +const Hasher = artifacts.require("PoseidonT3"); const Token = artifacts.require("ERC20Mock"); diff --git a/test/bridge/executeUpdateProposal.js b/test/bridge/executeUpdateProposal.js index 44b7d7bc7..58cab72a1 100644 --- a/test/bridge/executeUpdateProposal.js +++ b/test/bridge/executeUpdateProposal.js @@ -10,9 +10,9 @@ const BridgeContract = artifacts.require("Bridge"); const AnchorHandlerContract = artifacts.require("AnchorHandler"); - const LinkableAnchorContract = artifacts.require("LinkableERC20Anchor"); + const LinkableAnchorContract = artifacts.require("LinkableERC20AnchorPoseidon2"); const Verifier = artifacts.require("Verifier"); - const Hasher = artifacts.require("HasherMock"); + const Hasher = artifacts.require("PoseidonT3"); const Token = artifacts.require("ERC20Mock"); contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (accounts) => { @@ -32,7 +32,7 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco const expectedFinalizedEventStatus = 2; const expectedExecutedEventStatus = 3; const merkleTreeHeight = 31; - const maxRoots = 2; + const maxRoots = 1; const sender = accounts[5] const operator = accounts[5] @@ -78,7 +78,7 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco hasher.address, tokenDenomination, merkleTreeHeight, - maxRoots, + originChainID, token.address, {from: sender}); LinkableAnchorDestChainInstance = await LinkableAnchorContract.new( @@ -86,7 +86,7 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco hasher.address, tokenDenomination, merkleTreeHeight, - maxRoots, + originChainID, token.address, {from: sender}); @@ -115,8 +115,18 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco BridgeInstance.adminSetResource(DestinationAnchorHandlerInstance.address, resourceID, LinkableAnchorDestChainInstance.address) ]); - vote = (relayer) => BridgeInstance.voteProposal(originChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer }); - executeProposal = (relayer) => BridgeInstance.executeProposal(originChainID, expectedUpdateNonce, data, resourceID, { from: relayer }); + vote = (relayer) => BridgeInstance.voteProposal( + originChainID, + expectedUpdateNonce, + resourceID, + dataHash, + { from: relayer }); + executeProposal = (relayer) => BridgeInstance.executeProposal( + originChainID, + expectedUpdateNonce, + data, + resourceID, + { from: relayer }); }); it('[sanity] bridge configured with threshold and relayers', async () => { @@ -179,8 +189,8 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco await TruffleAssert.passes(BridgeInstance.executeProposal(originChainID, expectedUpdateNonce, data, resourceID, { from: relayer2Address })); const newRoots = await LinkableAnchorDestChainInstance.getLatestNeighborRoots(); + // bridge between ONLY 2 chains means neighbors should be length 1 assert.strictEqual(newRoots.length, maxRoots); - assert.strictEqual(newRoots[1], "0x0000000000000000000000000000000000000000000000000000000000000000"); assert.strictEqual(newRoots[0], merkleRoot); }); @@ -199,12 +209,13 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco await TruffleAssert.passes(BridgeInstance.voteProposal(originChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer1Address })); await TruffleAssert.passes(BridgeInstance.voteProposal(originChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer2Address })); await TruffleAssert.passes(BridgeInstance.voteProposal(originChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer3Address })); - await TruffleAssert.reverts(BridgeInstance.executeProposal(originChainID, expectedUpdateNonce, data, resourceID, { from: relayer2Address }), + await TruffleAssert.reverts( + BridgeInstance.executeProposal(originChainID, expectedUpdateNonce, data, resourceID, { from: relayer2Address }), "New height must be greater"); }); - it("updateProposal for adding 2 edges should work", async () => { + it("updateProposal for adding more than allowed edges should fail with capacity error", async () => { await TruffleAssert.passes(vote(relayer1Address)); await TruffleAssert.passes(vote(relayer2Address)); await TruffleAssert.passes(vote(relayer3Address)); @@ -219,7 +230,7 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco hasher.address, tokenDenomination, merkleTreeHeight, - maxRoots, + originChainID, token.address, {from: sender} ); @@ -234,16 +245,19 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer1Address })); await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer2Address })); await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer3Address })); - await TruffleAssert.passes(BridgeInstance.executeProposal(thirdChainID, expectedUpdateNonce, data, resourceID, { from: relayer3Address })); + await TruffleAssert.reverts( + BridgeInstance.executeProposal(thirdChainID, expectedUpdateNonce, data, resourceID, { from: relayer3Address }), + "This Anchor is at capacity" + ); const newRoots = await LinkableAnchorDestChainInstance.getLatestNeighborRoots(); + // bridge between ONLY 2 chains means neighbors should be length 1 assert.strictEqual(newRoots.length, maxRoots); assert.strictEqual(newRoots[0], merkleRoot); - assert.strictEqual(newRoots[1], newMerkleRoot); }); - it("updateProposal for adding more edges than maxRoots (2) should fail", async () => { + it("updateProposal for adding more edges than maxRoots (1) should fail", async () => { await TruffleAssert.passes(vote(relayer1Address)); await TruffleAssert.passes(vote(relayer2Address)); await TruffleAssert.passes(vote(relayer3Address)); @@ -258,7 +272,7 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco hasher.address, tokenDenomination, merkleTreeHeight, - maxRoots, + originChainID, token.address, {from: sender} ); @@ -274,66 +288,18 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer1Address })); await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer2Address })); await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer3Address })); - await TruffleAssert.passes(BridgeInstance.executeProposal(thirdChainID, expectedUpdateNonce, data, resourceID, { from: relayer3Address })); - - LinkableAnchorFourthChainInstance = await LinkableAnchorContract.new( - verifier.address, - hasher.address, - tokenDenomination, - merkleTreeHeight, - maxRoots, - token.address, - {from: sender} + await TruffleAssert.reverts( + BridgeInstance.executeProposal(thirdChainID, expectedUpdateNonce, data, resourceID, { from: relayer3Address }), + "This Anchor is at capacity", ); - await token.increaseAllowance(LinkableAnchorFourthChainInstance.address, 1000000000, {from: sender}); - await LinkableAnchorFourthChainInstance.deposit('0x023328', {from: sender}); - - newMerkleRoot = await LinkableAnchorFourthChainInstance.getLastRoot(); - data = Helpers.createUpdateProposalData(fourthChainID, blockHeight, newMerkleRoot); - dataHash = Ethers.utils.keccak256(DestinationAnchorHandlerInstance.address + data.substr(2)); - expectedUpdateNonce++; - - await TruffleAssert.passes(BridgeInstance.voteProposal(fourthChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer1Address })); - await TruffleAssert.passes(BridgeInstance.voteProposal(fourthChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer2Address })); - await TruffleAssert.passes(BridgeInstance.voteProposal(fourthChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer3Address })); - await TruffleAssert.reverts(BridgeInstance.executeProposal(fourthChainID, expectedUpdateNonce, data, resourceID, { from: relayer3Address }), - "This Anchor is at capacity"); - }); - it("updateProposal for updating an edge on anchor with 2 existing edges should work", async () => { + it("updateProposal for updating an edge on anchor with 1 existing edge should work", async () => { await TruffleAssert.passes(vote(relayer1Address)); await TruffleAssert.passes(vote(relayer2Address)); await TruffleAssert.passes(vote(relayer3Address)); await TruffleAssert.passes(executeProposal(relayer1Address)); - hasher = await Hasher.new(); - verifier = await Verifier.new(); - token = await Token.new(); - await token.mint(sender, tokenDenomination); - LinkableAnchorThirdChainInstance = await LinkableAnchorContract.new( - verifier.address, - hasher.address, - tokenDenomination, - merkleTreeHeight, - maxRoots, - token.address, - {from: sender} - ); - - await token.increaseAllowance(LinkableAnchorThirdChainInstance.address, 1000000000, {from: sender}); - await LinkableAnchorThirdChainInstance.deposit('0x023888', {from: sender}); - - newMerkleRoot = await LinkableAnchorThirdChainInstance.getLastRoot(); - data = Helpers.createUpdateProposalData(thirdChainID, blockHeight, newMerkleRoot); - dataHash = Ethers.utils.keccak256(DestinationAnchorHandlerInstance.address + data.substr(2)); - expectedUpdateNonce++; - - await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer1Address })); - await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer2Address })); - await TruffleAssert.passes(BridgeInstance.voteProposal(thirdChainID, expectedUpdateNonce, resourceID, dataHash, { from: relayer3Address })); - await TruffleAssert.passes(BridgeInstance.executeProposal(thirdChainID, expectedUpdateNonce, data, resourceID, { from: relayer3Address })); - await LinkableAnchorOriginChainInstance.deposit('0x22222', {from: sender}); merkleRoot = await LinkableAnchorOriginChainInstance.getLastRoot(); data = Helpers.createUpdateProposalData(originChainID, blockHeight + 10, merkleRoot); @@ -347,9 +313,9 @@ contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (acco newRoots = await LinkableAnchorDestChainInstance.getLatestNeighborRoots(); + // bridge between ONLY 2 chains means neighbors should be length 1 assert.strictEqual(newRoots.length, maxRoots); assert.strictEqual(newRoots[0], merkleRoot); - assert.strictEqual(newRoots[1], newMerkleRoot); }); diff --git a/test/bridge/voteUpdateProposal.js b/test/bridge/voteUpdateProposal.js index fd2dfe86c..938d66c7f 100644 --- a/test/bridge/voteUpdateProposal.js +++ b/test/bridge/voteUpdateProposal.js @@ -10,9 +10,9 @@ const BridgeContract = artifacts.require("Bridge"); const AnchorHandlerContract = artifacts.require("AnchorHandler"); - const LinkableAnchorContract = artifacts.require("LinkableERC20Anchor"); + const LinkableAnchorContract = artifacts.require("LinkableERC20AnchorPoseidon2"); const Verifier = artifacts.require("Verifier"); - const Hasher = artifacts.require("HasherMock"); + const Hasher = artifacts.require("PoseidonT3"); const Token = artifacts.require("ERC20Mock"); contract('Bridge - [voteUpdateProposal with relayerThreshold == 3]', async (accounts) => { diff --git a/test/merkleTree/MerkleTreeMiMC.test.js b/test/merkleTree/MerkleTreeMiMC.test.js deleted file mode 100644 index 0908ca003..000000000 --- a/test/merkleTree/MerkleTreeMiMC.test.js +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Copyright 2021 Webb Technologies - * SPDX-License-Identifier: LGPL-3.0-only - */ -const TruffleAssert = require('truffle-assertions'); -const { ethers } = require('hardhat'); -const BN = require('bn.js'); -const Helpers = require('../helpers'); -const assert = require('assert'); - -const MerkleTreeWithHistory = artifacts.require('MerkleTreeMiMCMock') -const MiMC = artifacts.require('MiMCSponge220'); - -const MerkleTree = require('../../lib/tornado-withdraw/MerkleTree') -const hasherImpl = require('../../lib/tornado-withdraw/MiMC') -const snarkjs = require('snarkjs') - -const { ETH_AMOUNT, MERKLE_TREE_HEIGHT } = process.env - -// eslint-disable-next-line no-unused-vars -function BNArrayToStringArray(array) { - const arrayToPrint = [] - array.forEach((item) => { - arrayToPrint.push(item.toString()) - }) - return arrayToPrint -} - -function toFixedHex(number, length = 32) { - let str = BigInt(number).toString(16) - while (str.length < length * 2) str = '0' + str - str = '0x' + str - return str -} - -contract('MerkleTreeMiMC', (accounts) => { - let web3; - let merkleTreeWithHistory; - let HasherFactory; - let hasherInstance; - let levels = MERKLE_TREE_HEIGHT || 16; - const sender = accounts[0]; - // eslint-disable-next-line no-unused-vars - const value = ETH_AMOUNT || '1000000000000000000'; - let snapshotId; - let prefix = 'test'; - let tree; - - beforeEach(async () => { - const accounts = await hre.ethers.getSigners() - // const mimcGenContract = require('circomlib/src/mimcsponge_gencontract.js'); - // let gen = mimcGenContract.createCode('mimcsponge', 220); - // let HasherFactory = new ethers.ContractFactory(mimcGenContract.abi, gen, accounts[0]); - // hasherInstance = await HasherFactory.deploy(); - hasherInstance = await MiMC.new(); - tree = new MerkleTree(levels, null, prefix) - merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels, hasherInstance.address) - }) - - describe('#constructor', () => { - it('should initialize', async () => { - const zeroValue = await merkleTreeWithHistory.ZERO_VALUE() - const firstSubtree = await merkleTreeWithHistory.filledSubtrees(0) - assert(firstSubtree == toFixedHex(zeroValue)); - - const firstZero = await merkleTreeWithHistory.zeros(0) - assert(firstZero == toFixedHex(zeroValue)); - }); - }); - - describe('merkleTreeLib', () => { - it('index_to_key', () => { - assert(MerkleTree.index_to_key('test', 5, 20) == 'test_tree_5_20') - }); - - it('tests insert', async () => { - hasher = new hasherImpl() - tree = new MerkleTree(2, null, prefix) - await tree.insert(toFixedHex('5')) - let { root, path_elements } = await tree.path(0); - const calculated_root = hasher.hash( - null, - hasher.hash(null, '5', path_elements[0]), - path_elements[1] - ); - // console.log(root) - assert(root == calculated_root) - }); - - it('creation odd elements count', async () => { - tree = new MerkleTree(levels, null, prefix) - const elements = [12, 13, 14, 15, 16, 17, 18, 19, 20] - for (const [, el] of Object.entries(elements)) { - await tree.insert(el) - } - - const batchTree = new MerkleTree(levels, elements, prefix) - for (const [i] of Object.entries(elements)) { - const pathViaConstructor = await batchTree.path(i) - const pathViaUpdate = await tree.path(i) - assert.deepStrictEqual(pathViaConstructor, pathViaUpdate); - } - }); - - it('should find an element', async () => { - tree = new MerkleTree(levels, null, prefix) - const elements = [12, 13, 14, 15, 16, 17, 18, 19, 20] - for (const [, el] of Object.entries(elements)) { - await tree.insert(el) - } - let index = tree.getIndexByElement(13) - assert(index == 1); - - index = tree.getIndexByElement(19) - assert(index == 7); - - index = tree.getIndexByElement(12) - assert(index == 0); - - index = tree.getIndexByElement(20) - assert(index == 8); - - index = tree.getIndexByElement(42) - assert(index == false); - }) - - it('creation even elements count', async () => { - const tree = new MerkleTree(levels, null, prefix) - const elements = [12, 13, 14, 15, 16, 17] - for (const [, el] of Object.entries(elements)) { - await tree.insert(el) - } - - const batchTree = new MerkleTree(levels, elements, prefix) - for (const [i] of Object.entries(elements)) { - const pathViaConstructor = await batchTree.path(i) - const pathViaUpdate = await tree.path(i) - assert.deepStrictEqual(pathViaConstructor, pathViaUpdate); - } - }) - - it.skip('creation using 30000 elements', () => { - const elements = [] - for (let i = 1000; i < 31001; i++) { - elements.push(i) - } - console.time('MerkleTree') - tree = new MerkleTree(levels, elements, prefix) - console.timeEnd('MerkleTree') - // 2,7 GHz Intel Core i7 - // 1000 : 1949.084ms - // 10000: 19456.220ms - // 30000: 63406.679ms - }) - }) - - describe('#hash', () => { - it('should hash', async () => { - hasher = new hasherImpl() - let zero_values = []; - let current_zero_value = '21663839004416932945382355908790599225266501822907911457504978515578255421292' - zero_values.push(current_zero_value) - for (let i = 0; i < 32; i++) { - current_zero_value = hasher.hash(i, current_zero_value, current_zero_value) - zero_values.push(current_zero_value.toString()) - } - }); - }); - - describe('#insert', () => { - it('should insert', async () => { - let rootFromContract - - for (let i = 1; i < 11; i++) { - await merkleTreeWithHistory.insert(toFixedHex(i), { from: sender }) - await tree.insert(i) - let { root } = await tree.path(i - 1) - rootFromContract = await merkleTreeWithHistory.getLastRoot() - assert(toFixedHex(root) == rootFromContract.toString()); - } - }); - - it('should reject if tree is full', async () => { - const levels = 6 - const merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels, hasherInstance.address) - - for (let i = 0; i < 2 ** levels; i++) { - TruffleAssert.passes(await merkleTreeWithHistory.insert(toFixedHex(i + 42))) - } - - await TruffleAssert.reverts( - merkleTreeWithHistory.insert(toFixedHex(1337)), - 'Merkle tree is full. No more leaves can be added' - ); - - await TruffleAssert.reverts( - merkleTreeWithHistory.insert(toFixedHex(1)), - 'Merkle tree is full. No more leaves can be added' - ); - }) - - it.skip('hasher gas', async () => { - const levels = 6 - const merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels) - const zeroValue = await merkleTreeWithHistory.zeroValue() - - const gas = await merkleTreeWithHistory.hashLeftRight.estimateGas(zeroValue, zeroValue) - console.log('gas', gas - 21000) - }) - }) - - describe('#isKnownRoot', () => { - it('should work', async () => { - let path - - for (let i = 1; i < 5; i++) { - TruffleAssert.passes(await merkleTreeWithHistory.insert(toFixedHex(i), { from: sender })) - await tree.insert(i) - path = await tree.path(i - 1) - let isKnown = await merkleTreeWithHistory.isKnownRoot(toFixedHex(path.root)) - assert(isKnown == true); - } - - TruffleAssert.passes(await merkleTreeWithHistory.insert(toFixedHex(42), { from: sender })); - // check outdated root - let isKnown = await merkleTreeWithHistory.isKnownRoot(toFixedHex(path.root)) - assert(isKnown == true); - }) - - it('should not return uninitialized roots', async () => { - TruffleAssert.passes(await merkleTreeWithHistory.insert(toFixedHex(42), { from: sender })); - let isKnown = await merkleTreeWithHistory.isKnownRoot(toFixedHex(0)) - assert(isKnown == false); - }) - }); -}); diff --git a/test/tornado/withdraw.js b/test/tornado/withdraw.js deleted file mode 100644 index fb12dd939..000000000 --- a/test/tornado/withdraw.js +++ /dev/null @@ -1,567 +0,0 @@ -/* global artifacts, web3, contract */ -require('chai').use(require('bn-chai')(web3.utils.BN)).use(require('chai-as-promised')).should() -const fs = require('fs') -const path = require("path"); - -const { toBN, randomHex } = require('web3-utils') -const { NATIVE_AMOUNT, MERKLE_TREE_HEIGHT } = process.env -const snarkjs = require('snarkjs') -const bigInt = BigInt; -const crypto = require('crypto') -const circomlib = require('circomlib') -const MerkleTree = require('../../lib/tornado-withdraw/MerkleTree') -const BN = require('bn.js'); -const utils = require("ffjavascript").utils; -const TruffleAssert = require('truffle-assertions'); -const { - leBuff2int, - leInt2Buff, -} = utils; - -const rbigint = (nbytes) => leBuff2int(crypto.randomBytes(nbytes)) -const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0] -const toFixedHex = (number, length = 32) => - '0x' + - bigInt(number) - .toString(16) - .padStart(length * 2, '0') -const getRandomRecipient = () => rbigint(20) - -function generateDeposit() { - let deposit = { - secret: rbigint(31), - nullifier: rbigint(31), - } - const preimage = Buffer.concat([leInt2Buff(deposit.nullifier, 31), leInt2Buff(deposit.secret, 31)]) - deposit.commitment = pedersenHash(preimage) - return deposit -} - -function snarkVerify(proof) { - const verification_key = unstringifyBigInts2(require('../build/tornado/verification_key.json')) - return snarkjs['groth'].isValid(verification_key, proof, proof.publicSignals) -} - -const HasherMimcContract = artifacts.require("MiMCSponge220"); -const VerifierMimcContract = artifacts.require("Verifier"); -const NativeAnchorContract = artifacts.require("NativeAnchor"); - -contract('NativeAnchor', (accounts) => { - let hasher; - let verifier; - let anchor; - const sender = accounts[0] - const operator = accounts[0] - const levels = MERKLE_TREE_HEIGHT || 30 - const value = NATIVE_AMOUNT || '1000000000000000000' // 1 ether - const maxRoots = 100; - let prefix = 'test' - let tree - const fee = BigInt((new BN(`${NATIVE_AMOUNT}`).shrn(1)).toString()) || BigInt((new BN(`${1e17}`)).toString()) - const refund = BigInt((new BN('0')).toString()) - const recipient = getRandomRecipient() - const relayer = accounts[1] - let createWitness - - beforeEach(async () => { - hasher = await HasherMimcContract.new(); - verifier = await VerifierMimcContract.new(); - anchor = await NativeAnchorContract.new(verifier.address, hasher.address, value, levels, maxRoots); - tree = new MerkleTree(levels, null, prefix) - createWitness = async (data) => { - const wtns = {type: "mem"}; - await snarkjs.wtns.calculate(data, path.join( - "artifacts/circuits", - "tornado", - "withdraw_30.wasm" - ), wtns); - return wtns; - } - }) - - describe('#constructor', () => { - it('should initialize', async () => { - const etherDenomination = await anchor.denomination() - etherDenomination.should.be.eq.BN(toBN(value)) - }) - }) - - describe('#deposit', () => { - it('should emit event', async () => { - let commitment = toFixedHex(42) - let { logs } = await anchor.deposit(commitment, { value, from: sender }) - - logs[0].event.should.be.equal('Deposit') - logs[0].args.commitment.should.be.equal(commitment) - logs[0].args.leafIndex.should.be.eq.BN(0) - - commitment = toFixedHex(12); - ({ logs } = await anchor.deposit(commitment, { value, from: accounts[2] })) - - logs[0].event.should.be.equal('Deposit') - logs[0].args.commitment.should.be.equal(commitment) - logs[0].args.leafIndex.should.be.eq.BN(1) - }) - - it('should throw if there is a such commitment', async () => { - const commitment = toFixedHex(42) - await anchor.deposit(commitment, { value, from: sender }).should.be.fulfilled - await TruffleAssert.reverts(anchor.deposit(commitment, { value, from: sender }), 'The commitment has been submitted'); - }) - }) - - // Use Node version >=12 - describe('snark proof verification on js side', () => { - it('should detect tampering', async () => { - const deposit = generateDeposit(); - await tree.insert(deposit.commitment); - const { root, path_elements, path_index } = await tree.path(0); - - const witness = { - root, - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee, - refund, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - let tempSignals = publicSignals; - // const vKey = JSON.parse(fs.readFileSync("./build/tornado/verification_key.json")); - const vKey = await snarkjs.zKey.exportVerificationKey('build/tornado/circuit_final.zkey'); - - let result = await snarkjs.groth16.verify(vKey, publicSignals, proof); - result.should.be.equal(true) - - // nullifier - publicSignals[1] = - '133792158246920651341275668520530514036799294649489851421007411546007850802'; - result = snarkVerify(proofData); - result.should.be.equal(false); - publicSignals = tempSignals; - - // try to cheat with recipient - publicSignals[2] = '133738360804642228759657445999390850076318544422'; - result = snarkVerify(proofData); - result.should.be.equal(false); - publicSignals = tempSignals; - - // fee - publicSignals[3] = '1337100000000000000000'; - result = snarkVerify(proofData); - result.should.be.equal(false); - publicSignals = tempSignals; - }) - }) - - describe('#withdraw', () => { - it('should work', async () => { - const deposit = generateDeposit() - const user = accounts[4] - await tree.insert(deposit.commitment) - const balanceUserBefore = await web3.eth.getBalance(user) - - // Uncomment to measure gas usage - // let gas = await anchor.deposit.estimateGas(toBN(deposit.commitment.toString()), { value, from: user, gasPrice: '0' }) - // console.log('deposit gas:', gas) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: user, gasPrice: '0' }) - - const balanceUserAfter = await web3.eth.getBalance(user) - balanceUserAfter.should.be.eq.BN(toBN(balanceUserBefore).sub(toBN(value))) - - const { root, path_elements, path_index } = await tree.path(0) - - // Circuit input - const witness = { - // public - root, - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - relayer: operator, - recipient, - fee, - refund, - - // private - nullifier: deposit.nullifier, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const balanceAnchorBefore = await web3.eth.getBalance(anchor.address) - const balanceRelayerBefore = await web3.eth.getBalance(relayer) - const balanceOperatorBefore = await web3.eth.getBalance(operator) - const balanceRecieverBefore = await web3.eth.getBalance(toFixedHex(recipient, 20)) - let isSpent = await anchor.isSpent(toFixedHex(input.nullifierHash)) - isSpent.should.be.equal(false) - - // Uncomment to measure gas usage - // gas = await anchor.withdraw.estimateGas(proof, publicSignals, { from: relayer, gasPrice: '0' }) - // console.log('withdraw gas:', gas) - const args = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - const { logs } = await anchor.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) - - const balanceAnchorAfter = await web3.eth.getBalance(anchor.address) - const balanceRelayerAfter = await web3.eth.getBalance(relayer) - const balanceOperatorAfter = await web3.eth.getBalance(operator) - const balanceRecieverAfter = await web3.eth.getBalance(toFixedHex(recipient, 20)) - const feeBN = toBN(fee.toString()) - balanceAnchorAfter.should.be.eq.BN(toBN(balanceAnchorBefore).sub(toBN(value))) - balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore)) - balanceOperatorAfter.should.be.eq.BN(toBN(balanceOperatorBefore).add(feeBN)) - balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN)) - - logs[0].event.should.be.equal('Withdrawal') - logs[0].args.nullifierHash.should.be.equal(toFixedHex(input.nullifierHash)) - logs[0].args.relayer.should.be.eq.BN(operator) - logs[0].args.fee.should.be.eq.BN(feeBN) - isSpent = await anchor.isSpent(toFixedHex(input.nullifierHash)) - isSpent.should.be.equal(true) - }) - - it('should prevent double spend', async () => { - const deposit = generateDeposit() - await tree.insert(deposit.commitment) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: sender }) - - const { root, path_elements, path_index } = await tree.path(0) - - const witness = { - root, - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee, - refund, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - await anchor.withdraw(proof, ...args, { from: relayer }).should.be.fulfilled - const error = await anchor.withdraw(proof, ...args, { from: relayer }).should.be.rejected - error.reason.should.be.equal('The note has been already spent') - }) - - it('should prevent double spend with overflow', async () => { - const deposit = generateDeposit() - await tree.insert(deposit.commitment) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: sender }) - - const { root, path_elements, path_index } = await tree.path(0) - - const witness = { - root, - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee, - refund, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(input.root), - toFixedHex( - toBN(input.nullifierHash).add( - toBN('21888242871839275222246405745257275088548364400416034343698204186575808495617'), - ), - ), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - const error = await anchor.withdraw(proof, ...args, { from: relayer }).should.be.rejected - error.reason.should.be.equal('verifier-gte-snark-scalar-field') - }) - - it('fee should be less or equal transfer value', async () => { - const deposit = generateDeposit() - await tree.insert(deposit.commitment) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: sender }) - - const { root, path_elements, path_index } = await tree.path(0) - const largeFee = bigInt(value).add(bigInt(1)) - const witness = { - root, - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee: largeFee, - refund, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - const error = await anchor.withdraw(proof, ...args, { from: relayer }).should.be.rejected - error.reason.should.be.equal('Fee exceeds transfer value') - }) - - it('should throw for corrupted merkle tree root', async () => { - const deposit = generateDeposit() - await tree.insert(deposit.commitment) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: sender }) - - const { root, path_elements, path_index } = await tree.path(0) - - const witness = { - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - root, - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee, - refund, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(randomHex(32)), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - const error = await anchor.withdraw(proof, ...args, { from: relayer }).should.be.rejected - error.reason.should.be.equal('Cannot find your merkle root') - }) - - it('should reject with tampered public inputs', async () => { - const deposit = generateDeposit() - await tree.insert(deposit.commitment) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: sender }) - - let { root, path_elements, path_index } = await tree.path(0) - - const witness = { - root, - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee, - refund, - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - let incorrectArgs - const originalProof = proof.slice() - - // recipient - incorrectArgs = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex('0x0000000000000000000000007a1f9131357404ef86d7c38dbffed2da70321337', 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - let error = await anchor.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected - error.reason.should.be.equal('Invalid withdraw proof') - - // fee - incorrectArgs = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex('0x000000000000000000000000000000000000000000000000015345785d8a0000'), - toFixedHex(input.refund), - ] - error = await anchor.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected - error.reason.should.be.equal('Invalid withdraw proof') - - // nullifier - incorrectArgs = [ - toFixedHex(input.root), - toFixedHex('0x00abdfc78211f8807b9c6504a6e537e71b8788b2f529a95f1399ce124a8642ad'), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - error = await anchor.withdraw(proof, ...incorrectArgs, { from: relayer }).should.be.rejected - error.reason.should.be.equal('Invalid withdraw proof') - - // proof itself - proof = '0xbeef' + proof.substr(6) - await anchor.withdraw(proof, ...args, { from: relayer }).should.be.rejected - - // should work with original values - await anchor.withdraw(originalProof, ...args, { from: relayer }).should.be.fulfilled - }) - - it('should reject with non zero refund', async () => { - const deposit = generateDeposit() - await tree.insert(deposit.commitment) - await anchor.deposit(toFixedHex(deposit.commitment), { value, from: sender }) - - const { root, path_elements, path_index } = await tree.path(0) - - const witness = { - nullifierHash: pedersenHash(leInt2Buff(deposit.nullifier, 31)), - root, - nullifier: deposit.nullifier, - relayer: operator, - recipient, - fee, - refund: bigInt(1), - secret: deposit.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - const error = await anchor.withdraw(proof, ...args, { from: relayer }).should.be.rejected - error.reason.should.be.equal('Refund value is supposed to be zero for ETH instance') - }) - }) - - describe('#isSpent', () => { - it('should work', async () => { - const deposit1 = generateDeposit() - const deposit2 = generateDeposit() - await tree.insert(deposit1.commitment) - await tree.insert(deposit2.commitment) - await anchor.deposit(toFixedHex(deposit1.commitment), { value, gasPrice: '0' }) - await anchor.deposit(toFixedHex(deposit2.commitment), { value, gasPrice: '0' }) - - const { root, path_elements, path_index } = await tree.path(1) - - // Circuit input - const witness = { - // public - root, - nullifierHash: pedersenHash(leInt2Buff(deposit2.nullifier, 31)), - relayer: operator, - recipient, - fee, - refund, - - // private - nullifier: deposit2.nullifier, - secret: deposit2.secret, - pathElements: path_elements, - pathIndices: path_index, - }; - const wtns = await createWitness(witness); - - let res = await snarkjs.groth16.prove('build/tornado/circuit_final.zkey', wtns); - proof = res.proof; - publicSignals = res.publicSignals; - - const args = [ - toFixedHex(input.root), - toFixedHex(input.nullifierHash), - toFixedHex(input.recipient, 20), - toFixedHex(input.relayer, 20), - toFixedHex(input.fee), - toFixedHex(input.refund), - ] - - await anchor.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) - - const nullifierHash1 = toFixedHex(pedersenHash(leInt2Buff(deposit1.nullifier, 31))) - const nullifierHash2 = toFixedHex(pedersenHash(leInt2Buff(deposit2.nullifier, 31))) - const spentArray = await anchor.isSpentArray([nullifierHash1, nullifierHash2]) - spentArray.should.be.deep.equal([false, true]) - }) - }) - - afterEach(async () => { - tree = new MerkleTree(levels, null, prefix) - }) -}) \ No newline at end of file