mirror of
https://github.com/0xbow-io/privacy-pools-core.git
synced 2026-01-10 09:58:00 -05:00
test: complex integration test cases (#54)
This commit is contained in:
2
.github/workflows/contracts.yml
vendored
2
.github/workflows/contracts.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
- name: Install Foundry
|
||||
uses: foundry-rs/foundry-toolchain@v1
|
||||
with:
|
||||
version: nightly
|
||||
version: v0.3.0
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
@@ -1,30 +1,59 @@
|
||||
#!/usr/bin/env node
|
||||
import { ethers } from "ethers";
|
||||
import { generateMerkleProof } from "@privacy-pool-core/sdk";
|
||||
|
||||
async function main() {
|
||||
// Fetch script arguments
|
||||
const args = process.argv.slice(2);
|
||||
// Function to temporarily redirect stdout
|
||||
function withSilentStdout(fn) {
|
||||
const originalStdoutWrite = process.stdout.write;
|
||||
const originalStderrWrite = process.stderr.write;
|
||||
|
||||
// Leaf we want to prove
|
||||
const leaf = BigInt(args[0]);
|
||||
// All leaves in tree
|
||||
const leaves = args.slice(1).map(BigInt);
|
||||
return async (...args) => {
|
||||
// Temporarily disable stdout/stderr
|
||||
process.stdout.write = () => true;
|
||||
process.stderr.write = () => true;
|
||||
|
||||
// Generate merkle proof using the SDK method
|
||||
const proof = generateMerkleProof(leaves, leaf);
|
||||
|
||||
// Fix the issue with index being NaN for depth 0 (LeanIMT issue)
|
||||
proof.index = Object.is(proof.index, NaN) ? 0 : proof.index;
|
||||
|
||||
// Convert proof to ABI-encoded bytes
|
||||
const abiCoder = new ethers.AbiCoder();
|
||||
const encodedProof = abiCoder.encode(
|
||||
["uint256", "uint256", "uint256[]"],
|
||||
[proof.root, proof.index, proof.siblings],
|
||||
);
|
||||
|
||||
// Write to stdout as hex string
|
||||
process.stdout.write(encodedProof);
|
||||
try {
|
||||
const result = await fn(...args);
|
||||
// Restore stdout/stderr
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Restore stdout/stderr
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
async function main() {
|
||||
try {
|
||||
const args = process.argv.slice(2);
|
||||
const leaf = BigInt(args[0]);
|
||||
const leaves = args.slice(1).map(BigInt);
|
||||
|
||||
// Wrap the generateMerkleProof call with stdout redirection
|
||||
const silentGenerateProof = withSilentStdout(() =>
|
||||
generateMerkleProof(leaves, leaf),
|
||||
);
|
||||
|
||||
const proof = await silentGenerateProof();
|
||||
proof.index = Object.is(proof.index, NaN) ? 0 : proof.index;
|
||||
|
||||
const abiCoder = new ethers.AbiCoder();
|
||||
const encodedProof = abiCoder.encode(
|
||||
["uint256", "uint256", "uint256[]"],
|
||||
[proof.root, proof.index, proof.siblings],
|
||||
);
|
||||
|
||||
process.stdout.write(encodedProof);
|
||||
process.exit(0);
|
||||
} catch {
|
||||
// Exit silently on any error
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Catch any uncaught errors and exit silently
|
||||
main().catch(() => process.exit(1));
|
||||
|
||||
@@ -1,26 +1,51 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { PrivacyPoolSDK, Circuits } from "@privacy-pool-core/sdk";
|
||||
import { encodeAbiParameters } from "viem";
|
||||
|
||||
// Function to temporarily redirect stdout
|
||||
function withSilentStdout(fn) {
|
||||
const originalStdoutWrite = process.stdout.write;
|
||||
const originalStderrWrite = process.stderr.write;
|
||||
|
||||
return async (...args) => {
|
||||
// Temporarily disable stdout/stderr
|
||||
process.stdout.write = () => true;
|
||||
process.stderr.write = () => true;
|
||||
|
||||
try {
|
||||
const result = await fn(...args);
|
||||
// Restore stdout/stderr
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Restore stdout/stderr
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Get command line arguments
|
||||
const [value, label, nullifier, secret] = process.argv.slice(2).map(BigInt);
|
||||
|
||||
// Initialize SDK with circuits
|
||||
const circuits = new Circuits();
|
||||
const privacyPoolSDK = new PrivacyPoolSDK(circuits);
|
||||
|
||||
try {
|
||||
// Generate the commitment proof
|
||||
const { proof, publicSignals } = await privacyPoolSDK.proveCommitment(
|
||||
const circuits = new Circuits();
|
||||
const privacyPoolSDK = new PrivacyPoolSDK(circuits);
|
||||
|
||||
// Wrap the proveCommitment call with stdout redirection
|
||||
const silentProveCommitment = withSilentStdout(
|
||||
privacyPoolSDK.proveCommitment.bind(privacyPoolSDK),
|
||||
);
|
||||
|
||||
const { proof, publicSignals } = await silentProveCommitment(
|
||||
value,
|
||||
label,
|
||||
nullifier,
|
||||
secret,
|
||||
);
|
||||
|
||||
// Format the proof to match the Solidity struct
|
||||
const ragequitProof = {
|
||||
_pA: [BigInt(proof.pi_a[0]), BigInt(proof.pi_a[1])],
|
||||
_pB: [
|
||||
@@ -37,7 +62,6 @@ async function main() {
|
||||
].map((x) => BigInt(x)),
|
||||
};
|
||||
|
||||
// ABI encode the proof
|
||||
const encodedProof = encodeAbiParameters(
|
||||
[
|
||||
{
|
||||
@@ -53,13 +77,13 @@ async function main() {
|
||||
[ragequitProof],
|
||||
);
|
||||
|
||||
// Output the encoded proof directly to stdout
|
||||
process.stdout.write(encodedProof);
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error("Error generating proof:", error);
|
||||
} catch {
|
||||
// Exit silently on any error
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
// Catch any uncaught errors and exit silently
|
||||
main().catch(() => process.exit(1));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { ethers } from "ethers";
|
||||
import {
|
||||
PrivacyPoolSDK,
|
||||
@@ -16,6 +15,31 @@ function padSiblings(siblings, treeDepth) {
|
||||
return paddedSiblings;
|
||||
}
|
||||
|
||||
// Function to temporarily redirect stdout
|
||||
function withSilentStdout(fn) {
|
||||
const originalStdoutWrite = process.stdout.write;
|
||||
const originalStderrWrite = process.stderr.write;
|
||||
|
||||
return async (...args) => {
|
||||
// Temporarily disable stdout/stderr
|
||||
process.stdout.write = () => true;
|
||||
process.stderr.write = () => true;
|
||||
|
||||
try {
|
||||
const result = await fn(...args);
|
||||
// Restore stdout/stderr
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
return result;
|
||||
} catch (error) {
|
||||
// Restore stdout/stderr
|
||||
process.stdout.write = originalStdoutWrite;
|
||||
process.stderr.write = originalStderrWrite;
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const [
|
||||
existingValue,
|
||||
@@ -32,94 +56,97 @@ async function main() {
|
||||
aspTreeDepth,
|
||||
] = process.argv.slice(2);
|
||||
|
||||
const circuits = new Circuits();
|
||||
const sdk = new PrivacyPoolSDK(circuits);
|
||||
try {
|
||||
const circuits = new Circuits();
|
||||
const sdk = new PrivacyPoolSDK(circuits);
|
||||
|
||||
// Decode the Merkle proofs
|
||||
const abiCoder = new ethers.AbiCoder();
|
||||
const stateMerkleProof = abiCoder.decode(
|
||||
["uint256", "uint256", "uint256[]"],
|
||||
stateMerkleProofHex,
|
||||
);
|
||||
const aspMerkleProof = abiCoder.decode(
|
||||
["uint256", "uint256", "uint256[]"],
|
||||
aspMerkleProofHex,
|
||||
);
|
||||
const abiCoder = new ethers.AbiCoder();
|
||||
const stateMerkleProof = abiCoder.decode(
|
||||
["uint256", "uint256", "uint256[]"],
|
||||
stateMerkleProofHex,
|
||||
);
|
||||
const aspMerkleProof = abiCoder.decode(
|
||||
["uint256", "uint256", "uint256[]"],
|
||||
aspMerkleProofHex,
|
||||
);
|
||||
|
||||
const commitment = getCommitment(
|
||||
existingValue,
|
||||
label,
|
||||
existingNullifier,
|
||||
existingSecret,
|
||||
);
|
||||
const commitment = getCommitment(
|
||||
existingValue,
|
||||
label,
|
||||
existingNullifier,
|
||||
existingSecret,
|
||||
);
|
||||
|
||||
// Pad siblings arrays to required length
|
||||
const paddedStateSiblings = padSiblings(stateMerkleProof[2], 32);
|
||||
const paddedAspSiblings = padSiblings(aspMerkleProof[2], 32);
|
||||
const paddedStateSiblings = padSiblings(stateMerkleProof[2], 32);
|
||||
const paddedAspSiblings = padSiblings(aspMerkleProof[2], 32);
|
||||
|
||||
// Generate withdrawal proof
|
||||
const { proof, publicSignals } = await sdk.proveWithdrawal(commitment, {
|
||||
context,
|
||||
withdrawalAmount: withdrawnValue,
|
||||
stateMerkleProof: {
|
||||
root: stateMerkleProof[0],
|
||||
leaf: commitment.hash,
|
||||
index: stateMerkleProof[1],
|
||||
siblings: paddedStateSiblings,
|
||||
},
|
||||
aspMerkleProof: {
|
||||
root: aspMerkleProof[0],
|
||||
leaf: commitment.hash,
|
||||
index: aspMerkleProof[1],
|
||||
siblings: paddedAspSiblings,
|
||||
},
|
||||
stateRoot: stateMerkleProof[0],
|
||||
stateTreeDepth: parseInt(stateTreeDepth),
|
||||
aspRoot: aspMerkleProof[0],
|
||||
aspTreeDepth: parseInt(aspTreeDepth),
|
||||
newSecret,
|
||||
newNullifier,
|
||||
});
|
||||
// Wrap the proveWithdrawal call with stdout redirection
|
||||
const silentProveWithdrawal = withSilentStdout(
|
||||
sdk.proveWithdrawal.bind(sdk),
|
||||
);
|
||||
|
||||
// Format the proof to match the Solidity struct
|
||||
const withdrawalProof = {
|
||||
_pA: [BigInt(proof.pi_a[0]), BigInt(proof.pi_a[1])],
|
||||
_pB: [
|
||||
[BigInt(proof.pi_b[0][1]), BigInt(proof.pi_b[0][0])],
|
||||
[BigInt(proof.pi_b[1][1]), BigInt(proof.pi_b[1][0])],
|
||||
],
|
||||
_pC: [BigInt(proof.pi_c[0]), BigInt(proof.pi_c[1])],
|
||||
_pubSignals: [
|
||||
publicSignals[0], // new commitment hash
|
||||
publicSignals[1], // existing nullifier hash
|
||||
publicSignals[2], // withdrawn value
|
||||
publicSignals[3], // state root
|
||||
publicSignals[4], // state depth
|
||||
publicSignals[5], // asp root
|
||||
publicSignals[6], // asp depth
|
||||
publicSignals[7], // context
|
||||
].map((x) => BigInt(x)),
|
||||
};
|
||||
|
||||
// ABI encode the proof
|
||||
const encodedProof = encodeAbiParameters(
|
||||
[
|
||||
{
|
||||
type: "tuple",
|
||||
components: [
|
||||
{ name: "_pA", type: "uint256[2]" },
|
||||
{ name: "_pB", type: "uint256[2][2]" },
|
||||
{ name: "_pC", type: "uint256[2]" },
|
||||
{ name: "_pubSignals", type: "uint256[8]" },
|
||||
],
|
||||
const { proof, publicSignals } = await silentProveWithdrawal(commitment, {
|
||||
context,
|
||||
withdrawalAmount: withdrawnValue,
|
||||
stateMerkleProof: {
|
||||
root: stateMerkleProof[0],
|
||||
leaf: commitment.hash,
|
||||
index: stateMerkleProof[1],
|
||||
siblings: paddedStateSiblings,
|
||||
},
|
||||
],
|
||||
[withdrawalProof],
|
||||
);
|
||||
aspMerkleProof: {
|
||||
root: aspMerkleProof[0],
|
||||
leaf: commitment.hash,
|
||||
index: aspMerkleProof[1],
|
||||
siblings: paddedAspSiblings,
|
||||
},
|
||||
stateRoot: stateMerkleProof[0],
|
||||
stateTreeDepth: parseInt(stateTreeDepth),
|
||||
aspRoot: aspMerkleProof[0],
|
||||
aspTreeDepth: parseInt(aspTreeDepth),
|
||||
newSecret,
|
||||
newNullifier,
|
||||
});
|
||||
|
||||
// Write to stdout as hex string
|
||||
process.stdout.write(encodedProof);
|
||||
process.exit(0);
|
||||
const withdrawalProof = {
|
||||
_pA: [BigInt(proof.pi_a[0]), BigInt(proof.pi_a[1])],
|
||||
_pB: [
|
||||
[BigInt(proof.pi_b[0][1]), BigInt(proof.pi_b[0][0])],
|
||||
[BigInt(proof.pi_b[1][1]), BigInt(proof.pi_b[1][0])],
|
||||
],
|
||||
_pC: [BigInt(proof.pi_c[0]), BigInt(proof.pi_c[1])],
|
||||
_pubSignals: [
|
||||
publicSignals[0],
|
||||
publicSignals[1],
|
||||
publicSignals[2],
|
||||
publicSignals[3],
|
||||
publicSignals[4],
|
||||
publicSignals[5],
|
||||
publicSignals[6],
|
||||
publicSignals[7],
|
||||
].map((x) => BigInt(x)),
|
||||
};
|
||||
|
||||
const encodedProof = encodeAbiParameters(
|
||||
[
|
||||
{
|
||||
type: "tuple",
|
||||
components: [
|
||||
{ name: "_pA", type: "uint256[2]" },
|
||||
{ name: "_pB", type: "uint256[2][2]" },
|
||||
{ name: "_pC", type: "uint256[2]" },
|
||||
{ name: "_pubSignals", type: "uint256[8]" },
|
||||
],
|
||||
},
|
||||
],
|
||||
[withdrawalProof],
|
||||
);
|
||||
|
||||
process.stdout.write(encodedProof);
|
||||
process.exit(0);
|
||||
} catch {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
main().catch(() => process.exit(1));
|
||||
|
||||
@@ -27,6 +27,10 @@ import {Constants} from 'test/helper/Constants.sol';
|
||||
contract IntegrationBase is Test {
|
||||
using InternalLeanIMT for LeanIMTData;
|
||||
|
||||
error WithdrawalProofGenerationFailed();
|
||||
error RagequitProofGenerationFailed();
|
||||
error MerkleProofGenerationFailed();
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
STRUCTS
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
@@ -274,7 +278,7 @@ contract IntegrationBase is Test {
|
||||
IPrivacyPool.Withdrawal memory _withdrawal,
|
||||
WithdrawalParams memory _params,
|
||||
bool _direct
|
||||
) private returns (Commitment memory _commitment) {
|
||||
) internal returns (Commitment memory _commitment) {
|
||||
// Fetch balances before withdrawal
|
||||
uint256 _recipientInitialBalance = _balance(_params.recipient, _params.commitment.asset);
|
||||
uint256 _entrypointInitialBalance = _balance(address(_entrypoint), _params.commitment.asset);
|
||||
@@ -406,7 +410,7 @@ contract IntegrationBase is Test {
|
||||
uint256 _label,
|
||||
uint256 _nullifier,
|
||||
uint256 _secret
|
||||
) private returns (ProofLib.RagequitProof memory _proof) {
|
||||
) internal returns (ProofLib.RagequitProof memory _proof) {
|
||||
// Generate real proof using the helper script
|
||||
string[] memory _inputs = new string[](5);
|
||||
_inputs[0] = vm.toString(_value);
|
||||
@@ -420,12 +424,16 @@ contract IntegrationBase is Test {
|
||||
_scriptArgs[1] = 'test/helper/RagequitProofGenerator.mjs';
|
||||
bytes memory _proofData = vm.ffi(_concat(_scriptArgs, _inputs));
|
||||
|
||||
if (_proofData.length == 0) {
|
||||
revert RagequitProofGenerationFailed();
|
||||
}
|
||||
|
||||
// Decode the ABI-encoded proof directly
|
||||
_proof = abi.decode(_proofData, (ProofLib.RagequitProof));
|
||||
}
|
||||
|
||||
function _generateWithdrawalProof(WithdrawalProofParams memory _params)
|
||||
private
|
||||
internal
|
||||
returns (ProofLib.WithdrawProof memory _proof)
|
||||
{
|
||||
// Generate state merkle proof
|
||||
@@ -433,6 +441,10 @@ contract IntegrationBase is Test {
|
||||
// Generate ASP merkle proof
|
||||
bytes memory _aspMerkleProof = _generateMerkleProof(_aspLeaves, _params.label);
|
||||
|
||||
if (_aspMerkleProof.length == 0 || _stateMerkleProof.length == 0) {
|
||||
revert MerkleProofGenerationFailed();
|
||||
}
|
||||
|
||||
string[] memory _inputs = new string[](12);
|
||||
_inputs[0] = vm.toString(_params.existingValue);
|
||||
_inputs[1] = vm.toString(_params.label);
|
||||
@@ -453,11 +465,14 @@ contract IntegrationBase is Test {
|
||||
_scriptArgs[1] = 'test/helper/WithdrawalProofGenerator.mjs';
|
||||
bytes memory _proofData = vm.ffi(_concat(_scriptArgs, _inputs));
|
||||
|
||||
// Decode the ABI-encoded proof directly
|
||||
if (_proofData.length == 0) {
|
||||
revert WithdrawalProofGenerationFailed();
|
||||
}
|
||||
|
||||
_proof = abi.decode(_proofData, (ProofLib.WithdrawProof));
|
||||
}
|
||||
|
||||
function _generateMerkleProof(uint256[] storage _leaves, uint256 _leaf) private returns (bytes memory _proof) {
|
||||
function _generateMerkleProof(uint256[] storage _leaves, uint256 _leaf) internal returns (bytes memory _proof) {
|
||||
uint256 _leavesAmt = _leaves.length;
|
||||
string[] memory inputs = new string[](_leavesAmt + 1);
|
||||
inputs[0] = vm.toString(_leaf);
|
||||
@@ -532,7 +547,7 @@ contract IntegrationBase is Test {
|
||||
_commitmentHash = PoseidonT4.hash([_amount, _label, _precommitment]);
|
||||
}
|
||||
|
||||
function _genSecretBySeed(string memory _seed) private pure returns (uint256 _secret) {
|
||||
function _genSecretBySeed(string memory _seed) internal pure returns (uint256 _secret) {
|
||||
_secret = uint256(keccak256(bytes(_seed))) % Constants.SNARK_SCALAR_FIELD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ import {IntegrationBase} from './IntegrationBase.sol';
|
||||
import {InternalLeanIMT, LeanIMTData} from 'lean-imt/InternalLeanIMT.sol';
|
||||
|
||||
import {IPrivacyPool} from 'interfaces/IPrivacyPool.sol';
|
||||
import {IState} from 'interfaces/IState.sol';
|
||||
|
||||
contract IntegrationERC20 is IntegrationBase {
|
||||
using InternalLeanIMT for LeanIMTData;
|
||||
|
||||
Commitment internal _commitment;
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and fully withdraw its value (after fees) directly, without a relayer
|
||||
*/
|
||||
function test_fullDirectWithdrawal() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -34,6 +38,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and fully withdraw its value (after fees) through a relayer
|
||||
*/
|
||||
function test_fullRelayedWithdrawal() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -57,6 +64,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and partially withdraw, without a relayer
|
||||
*/
|
||||
function test_partialDirectWithdrawal() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -80,6 +90,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and do multiple partial withdrawals without a relayer
|
||||
*/
|
||||
function test_multiplePartialDirectWithdrawals() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -139,6 +152,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and do a partial withdrawal through a relayer
|
||||
*/
|
||||
function test_partialRelayedWithdrawal() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -162,6 +178,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and do multiple partial withdrawals through a relayer
|
||||
*/
|
||||
function test_multiplePartialRelayedWithdrawals() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -221,6 +240,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can ragequit a commitment when their label is not in the ASP tree
|
||||
*/
|
||||
function test_ragequit() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -247,6 +269,9 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
_ragequit(_ALICE, _commitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can get approved by the ASP, make a partial withdrawal, and if removed from the ASP set, they can only ragequit
|
||||
*/
|
||||
function test_aspRemoval() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
@@ -288,4 +313,109 @@ contract IntegrationERC20 is IntegrationBase {
|
||||
// Ragequit
|
||||
_ragequit(_ALICE, _commitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can't spend a commitment more than once
|
||||
*/
|
||||
function test_failWhenCommitmentAlreadySpent() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _DAI, amount: 5000 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Fully spend child commitment
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
newNullifier: 'nullifier_2',
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: NONE
|
||||
})
|
||||
);
|
||||
|
||||
// Fail to spend same commitment that was just spent
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
newNullifier: 'nullifier_2',
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: IState.NullifierAlreadySpent.selector
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that commitments with reused nullifiers can not be spent
|
||||
*/
|
||||
function test_failWhenReusingNullifier() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _DAI, amount: 5000 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Bob withdraws some of Alice's commitment
|
||||
_commitment = _selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 2000 ether,
|
||||
newNullifier: 'nullifier_1', // Reusing nullifier of deposit for new commitment
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: NONE
|
||||
})
|
||||
);
|
||||
|
||||
// Fail to spend the child commitment
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 2000 ether,
|
||||
newNullifier: 'nullifier_3',
|
||||
newSecret: 'secret_3',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: IState.NullifierAlreadySpent.selector
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that spent commitments can not be ragequitted (and spent again)
|
||||
*/
|
||||
function test_failWhenTryingToSpendRagequitCommitment() public {
|
||||
// Alice deposits 5000 DAI
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _DAI, amount: 5000 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Ragequit full amount
|
||||
_ragequit(_ALICE, _commitment);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Fail to withdraw commitment that was already ragequitted
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
newNullifier: 'nullifier_2',
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: IState.NullifierAlreadySpent.selector
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ import {IntegrationBase} from './IntegrationBase.sol';
|
||||
import {InternalLeanIMT, LeanIMTData} from 'lean-imt/InternalLeanIMT.sol';
|
||||
|
||||
import {IPrivacyPool} from 'interfaces/IPrivacyPool.sol';
|
||||
import {IState} from 'interfaces/IState.sol';
|
||||
|
||||
contract IntegrationETH is IntegrationBase {
|
||||
contract IntegrationNative is IntegrationBase {
|
||||
using InternalLeanIMT for LeanIMTData;
|
||||
|
||||
Commitment internal _commitment;
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and fully withdraw its value (after fees) directly, without a relayer
|
||||
*/
|
||||
function test_fullDirectWithdrawal() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -34,6 +38,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and fully withdraw its value (after fees) through a relayer
|
||||
*/
|
||||
function test_fullRelayedWithdrawal() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -44,7 +51,7 @@ contract IntegrationETH is IntegrationBase {
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Bob receives the total amount of Alice's commitment
|
||||
// Bob receives withdraws total amount of Alice's commitment through a relayer
|
||||
_withdrawThroughRelayer(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
@@ -57,6 +64,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and partially withdraw, without a relayer
|
||||
*/
|
||||
function test_partialDirectWithdrawal() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -80,6 +90,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and do multiple partial withdrawals without a relayer
|
||||
*/
|
||||
function test_multiplePartialDirectWithdrawals() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -151,6 +164,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and do a partial withdrawal through a relayer
|
||||
*/
|
||||
function test_partialRelayedWithdrawal() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -161,7 +177,7 @@ contract IntegrationETH is IntegrationBase {
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Bob receives the total amount of Alice's commitment
|
||||
// Bob receives the total amount of Alice's commitment through a relayer
|
||||
_withdrawThroughRelayer(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 40 ether,
|
||||
@@ -174,6 +190,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can make a deposit and do multiple partial withdrawals through a relayer
|
||||
*/
|
||||
function test_multiplePartialRelayedWithdrawals() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -245,6 +264,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can ragequit a commitment when their label is not in the ASP tree
|
||||
*/
|
||||
function test_ragequit() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -255,7 +277,7 @@ contract IntegrationETH is IntegrationBase {
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(uint256(keccak256('some_root')) % SNARK_SCALAR_FIELD, bytes32('IPFS_HASH'));
|
||||
|
||||
// Fail to withdraw
|
||||
// Fail to withdraw because the label is not included in the latest ASP root
|
||||
_withdrawThroughRelayer(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 40 ether,
|
||||
@@ -271,6 +293,9 @@ contract IntegrationETH is IntegrationBase {
|
||||
_ragequit(_ALICE, _commitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can get approved by the ASP, make a partial withdrawal, and if removed from the ASP set, they can only ragequit
|
||||
*/
|
||||
function test_aspRemoval() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
@@ -297,7 +322,7 @@ contract IntegrationETH is IntegrationBase {
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(uint256(keccak256('some_root')) % SNARK_SCALAR_FIELD, bytes32('IPFS_HASH'));
|
||||
|
||||
// Fail to withdraw
|
||||
// Fail to withdraw because label is not included in the latest ASP root
|
||||
_withdrawThroughRelayer(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 40 ether,
|
||||
@@ -312,4 +337,109 @@ contract IntegrationETH is IntegrationBase {
|
||||
// Ragequit
|
||||
_ragequit(_ALICE, _commitment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that users can't spend a commitment more than once
|
||||
*/
|
||||
function test_failWhenCommitmentAlreadySpent() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _ETH, amount: 100 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Fully spend commitment
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
newNullifier: 'nullifier_2',
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: NONE
|
||||
})
|
||||
);
|
||||
|
||||
// Fail to spend same commitment that was just spent
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
newNullifier: 'nullifier_2',
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: IState.NullifierAlreadySpent.selector
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that commitments with reused nullifiers can not be spent
|
||||
*/
|
||||
function test_failWhenReusingNullifier() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _ETH, amount: 100 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Bob partially withdraws Alice's commitment
|
||||
_commitment = _selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 20 ether,
|
||||
newNullifier: 'nullifier_1', // Reusing nullifier of deposit for new commitment
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: NONE
|
||||
})
|
||||
);
|
||||
|
||||
// Fail to spend the child commitment because it was generated using an already spent nullifier
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: 20 ether,
|
||||
newNullifier: 'nullifier_3',
|
||||
newSecret: 'secret_3',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: IState.NullifierAlreadySpent.selector
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Test that spent commitments can not be ragequitted (and spent again)
|
||||
*/
|
||||
function test_failWhenTryingToSpendRagequitCommitment() public {
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _ETH, amount: 100 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Ragequit full amount
|
||||
_ragequit(_ALICE, _commitment);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
// Fail to withdraw commitment that was already ragequitted
|
||||
_selfWithdraw(
|
||||
WithdrawalParams({
|
||||
withdrawnAmount: _commitment.value,
|
||||
newNullifier: 'nullifier_2',
|
||||
newSecret: 'secret_2',
|
||||
recipient: _BOB,
|
||||
commitment: _commitment,
|
||||
revertReason: IState.NullifierAlreadySpent.selector
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
269
packages/contracts/test/integration/Proofs.t.sol
Normal file
269
packages/contracts/test/integration/Proofs.t.sol
Normal file
@@ -0,0 +1,269 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import {IntegrationBase} from './IntegrationBase.sol';
|
||||
|
||||
import {ProofLib} from 'contracts/lib/ProofLib.sol';
|
||||
import {InternalLeanIMT, LeanIMTData} from 'lean-imt/InternalLeanIMT.sol';
|
||||
|
||||
import {IPrivacyPool} from 'interfaces/IPrivacyPool.sol';
|
||||
|
||||
contract IntegrationProofs is IntegrationBase {
|
||||
using InternalLeanIMT for LeanIMTData;
|
||||
|
||||
Commitment internal _commitment;
|
||||
IPrivacyPool.Withdrawal internal _withdrawal;
|
||||
uint256 internal _context;
|
||||
|
||||
function setUp() public override {
|
||||
super.setUp();
|
||||
|
||||
// Alice deposits 100 ETH
|
||||
_commitment = _deposit(
|
||||
DepositParams({depositor: _ALICE, asset: _ETH, amount: 100 ether, nullifier: 'nullifier_1', secret: 'secret_1'})
|
||||
);
|
||||
|
||||
// Push ASP root with label included
|
||||
vm.prank(_POSTMAN);
|
||||
_entrypoint.updateRoot(_shadowASPMerkleTree._root(), bytes32('IPFS_HASH'));
|
||||
|
||||
_withdrawal =
|
||||
IPrivacyPool.Withdrawal({processooor: _BOB, scope: _ethPool.SCOPE(), data: abi.encode(_BOB, address(0), 0)});
|
||||
|
||||
_context = uint256(keccak256(abi.encode(_withdrawal, _ethPool.SCOPE()))) % SNARK_SCALAR_FIELD;
|
||||
}
|
||||
|
||||
function test_failToGenerateProof_whenCommitmentHashMismatches() public {
|
||||
// Try to withdraw more value than commitment
|
||||
vm.expectRevert(MerkleProofGenerationFailed.selector);
|
||||
_generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash - 1, // Mismatching existing commitment hash
|
||||
withdrawnValue: _commitment.value,
|
||||
context: _context,
|
||||
label: _commitment.label,
|
||||
existingValue: _commitment.value,
|
||||
existingNullifier: _commitment.nullifier,
|
||||
existingSecret: _commitment.secret,
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_failToGenerateProof_whenInvalidWithdrawnValue() public {
|
||||
// Try to withdraw more value than commitment
|
||||
vm.expectRevert(WithdrawalProofGenerationFailed.selector);
|
||||
_generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash,
|
||||
withdrawnValue: _commitment.value + 1, // Greater withdrawn value that existing value
|
||||
context: _context,
|
||||
label: _commitment.label,
|
||||
existingValue: _commitment.value,
|
||||
existingNullifier: _commitment.nullifier,
|
||||
existingSecret: _commitment.secret,
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_failToGenerateProof_whenLabelMismatches() public {
|
||||
// Try to withdraw more value than commitment
|
||||
vm.expectRevert(MerkleProofGenerationFailed.selector);
|
||||
_generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash,
|
||||
withdrawnValue: _commitment.value,
|
||||
context: _context,
|
||||
label: _commitment.label - 1,
|
||||
existingValue: _commitment.value,
|
||||
existingNullifier: _commitment.nullifier,
|
||||
existingSecret: _commitment.secret,
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_failToGenerateProof_whenWithdrawnValueGreaterThanCommitment() public {
|
||||
// Try to witdhraw with an invalid commitment value
|
||||
vm.expectRevert(WithdrawalProofGenerationFailed.selector);
|
||||
_generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash,
|
||||
withdrawnValue: _commitment.value,
|
||||
context: _context,
|
||||
label: _commitment.label,
|
||||
existingValue: _commitment.value + 1, // Greater existing value than actual
|
||||
existingNullifier: _commitment.nullifier,
|
||||
existingSecret: _commitment.secret,
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_failToGenerateProof_whenExistingNullifierMismatches() public {
|
||||
// Try to witdhraw with an invalid commitment value
|
||||
vm.expectRevert(WithdrawalProofGenerationFailed.selector);
|
||||
_generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash,
|
||||
withdrawnValue: _commitment.value,
|
||||
context: _context,
|
||||
label: _commitment.label,
|
||||
existingValue: _commitment.value,
|
||||
existingNullifier: _commitment.nullifier - 1, // Different existing nullifier
|
||||
existingSecret: _commitment.secret,
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_failToGenerateProof_whenExistingSecretMismatches() public {
|
||||
// Try to witdhraw with an invalid commitment value
|
||||
vm.expectRevert(WithdrawalProofGenerationFailed.selector);
|
||||
_generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash,
|
||||
withdrawnValue: _commitment.value,
|
||||
context: _context,
|
||||
label: _commitment.label,
|
||||
existingValue: _commitment.value,
|
||||
existingNullifier: _commitment.nullifier,
|
||||
existingSecret: _commitment.secret - 1, // Different existing secret
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function test_failToWithdraw_whenPublicSignalMismatch() public {
|
||||
// Generate a valid proof
|
||||
ProofLib.WithdrawProof memory _proof = _generateWithdrawalProof(
|
||||
WithdrawalProofParams({
|
||||
existingCommitment: _commitment.hash,
|
||||
withdrawnValue: _commitment.value,
|
||||
context: _context,
|
||||
label: _commitment.label,
|
||||
existingValue: _commitment.value,
|
||||
existingNullifier: _commitment.nullifier,
|
||||
existingSecret: _commitment.secret,
|
||||
newNullifier: _genSecretBySeed('nullifier_2'),
|
||||
newSecret: _genSecretBySeed('secret_2')
|
||||
})
|
||||
);
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
NEW COMMITMENT HASH
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the new commitment hash
|
||||
_proof.pubSignals[0] = _proof.pubSignals[0] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.InvalidProof.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[0] = _proof.pubSignals[0] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
EXISTING NULLIFIER HASH
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the existing commitment hash
|
||||
_proof.pubSignals[1] = _proof.pubSignals[1] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.InvalidProof.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[1] = _proof.pubSignals[1] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
WITHDRAWN VALUE
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the withdrawn value
|
||||
_proof.pubSignals[2] = _proof.pubSignals[2] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.InvalidProof.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[2] = _proof.pubSignals[2] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
STATE ROOT
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the state root value
|
||||
_proof.pubSignals[3] = _proof.pubSignals[3] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.UnknownStateRoot.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[3] = _proof.pubSignals[3] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
STATE TREE DEPTH
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the state tree depth value
|
||||
_proof.pubSignals[4] = _proof.pubSignals[4] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.InvalidProof.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[4] = _proof.pubSignals[4] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
ASP ROOT
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the asp root value
|
||||
_proof.pubSignals[5] = _proof.pubSignals[5] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.IncorrectASPRoot.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[5] = _proof.pubSignals[5] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
ASP TREE DEPTH
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the asp tree depth value
|
||||
_proof.pubSignals[6] = _proof.pubSignals[6] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.InvalidProof.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
|
||||
// Reset
|
||||
_proof.pubSignals[6] = _proof.pubSignals[6] - 1;
|
||||
|
||||
/*///////////////////////////////////////////////////////////////
|
||||
CONTEXT
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
// Change the context value
|
||||
_proof.pubSignals[7] = _proof.pubSignals[7] + 1;
|
||||
|
||||
vm.expectRevert(IPrivacyPool.ContextMismatch.selector);
|
||||
vm.prank(_BOB);
|
||||
_ethPool.withdraw(_withdrawal, _proof);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user