Skip to content

Commit b6137e2

Browse files
authored
Merge pull request #111 from anoma/feature/mock-proofs-for-testing
feat: mock ProtocolAdapter and add example library
2 parents cfe3b9d + 058aca6 commit b6137e2

File tree

6 files changed

+536
-21
lines changed

6 files changed

+536
-21
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.30;
3+
4+
import {RiscZeroVerifierRouter} from "@risc0-ethereum/RiscZeroVerifierRouter.sol";
5+
import {RiscZeroMockVerifier} from "@risc0-ethereum/test/RiscZeroMockVerifier.sol";
6+
7+
import {Script} from "forge-std/Script.sol";
8+
9+
bytes4 constant _MOCK_VERIFIER_SELECTOR = bytes4(0xFFFFFFFF);
10+
11+
contract DeployRiscZeroVerifierRouterMock is Script {
12+
RiscZeroVerifierRouter internal _router;
13+
RiscZeroMockVerifier internal _verifier;
14+
15+
function run() public returns (RiscZeroVerifierRouter router, RiscZeroMockVerifier verifier) {
16+
(, address _defaultSender,) = vm.readCallers();
17+
18+
vm.startBroadcast();
19+
20+
verifier = new RiscZeroMockVerifier(_MOCK_VERIFIER_SELECTOR);
21+
_verifier = verifier;
22+
23+
router = new RiscZeroVerifierRouter({admin: _defaultSender});
24+
router.addVerifier({selector: _MOCK_VERIFIER_SELECTOR, verifier: _verifier});
25+
_router = router;
26+
27+
vm.stopBroadcast();
28+
}
29+
}

contracts/src/ProtocolAdapter.sol

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,7 @@ contract ProtocolAdapter is IProtocolAdapter, ReentrancyGuardTransient, Commitme
191191
actionTreeTags[(2 * j) + 1] = cm;
192192
}
193193

194-
// slither-disable-next-line calls-loop
195-
_TRUSTED_RISC_ZERO_VERIFIER_ROUTER.verify({
196-
seal: complianceVerifierInput.proof,
197-
imageId: Compliance._VERIFYING_KEY,
198-
journalDigest: complianceVerifierInput.instance.toJournalDigest()
199-
});
194+
_verifyComplianceProof(complianceVerifierInput);
200195

201196
// Check the logic ref consistency
202197
{
@@ -221,12 +216,13 @@ contract ProtocolAdapter is IProtocolAdapter, ReentrancyGuardTransient, Commitme
221216
}
222217

223218
// Compute transaction delta
224-
transactionDelta = transactionDelta.add(
225-
[
219+
transactionDelta = _addUnitDelta({
220+
transactionDelta: transactionDelta,
221+
unitDelta: [
226222
uint256(complianceVerifierInput.instance.unitDeltaX),
227223
uint256(complianceVerifierInput.instance.unitDeltaY)
228224
]
229-
);
225+
});
230226
}
231227
}
232228

@@ -242,28 +238,57 @@ contract ProtocolAdapter is IProtocolAdapter, ReentrancyGuardTransient, Commitme
242238
revert RootMismatch({expected: computedActionTreeRoot, actual: input.instance.actionTreeRoot});
243239
}
244240

245-
// Check the compliance proof
246-
// slither-disable-next-line calls-loop
247-
_TRUSTED_RISC_ZERO_VERIFIER_ROUTER.verify({
248-
seal: input.proof,
249-
imageId: input.verifyingKey,
250-
journalDigest: input.instance.toJournalDigest()
251-
});
241+
// Check the logic proof
242+
_verifyLogicProof(input);
252243
}
253244
}
254245
}
255246

256247
// Check whether the transaction is empty
257248
if (nActions != 0) {
258249
// Check the delta proof
259-
Delta.verify({
260-
proof: transaction.deltaProof,
261-
instance: transactionDelta,
262-
verifyingKey: Delta.computeVerifyingKey(tags)
263-
});
250+
251+
_verifyDeltaProof({proof: transaction.deltaProof, transactionDelta: transactionDelta, tags: tags});
264252
}
265253
}
266254

255+
/// @notice An internal function verifying a RISC0 compliance proof.
256+
/// @param input The verifier input of the compliance proof.
257+
/// @dev This function is virtual to allow for it to be overridden, e.g., to use mock proofs with a mock verifier.
258+
function _verifyComplianceProof(Compliance.VerifierInput calldata input) internal view virtual {
259+
// slither-disable-next-line calls-loop
260+
_TRUSTED_RISC_ZERO_VERIFIER_ROUTER.verify({
261+
seal: input.proof,
262+
imageId: Compliance._VERIFYING_KEY,
263+
journalDigest: input.instance.toJournalDigest()
264+
});
265+
}
266+
267+
/// @notice An internal function verifying a RISC0 logic proof.
268+
/// @param input The verifier input of the logic proof.
269+
/// @dev This function is virtual to allow for it to be overridden, e.g., to mock proofs with a mock verifier.
270+
function _verifyLogicProof(Logic.VerifierInput calldata input) internal view virtual {
271+
// slither-disable-next-line calls-loop
272+
_TRUSTED_RISC_ZERO_VERIFIER_ROUTER.verify({
273+
seal: input.proof,
274+
imageId: input.verifyingKey,
275+
journalDigest: input.instance.toJournalDigest()
276+
});
277+
}
278+
279+
/// @notice An internal function verifying a delta proof.
280+
/// @param proof The delta proof.
281+
/// @param transactionDelta The transaction delta.
282+
/// @param tags The tags being part of the transaction in the order of appearance in the compliance units.
283+
/// @dev This function is virtual to allow for it to be overridden, e.g., to mock proofs.
284+
function _verifyDeltaProof(bytes calldata proof, uint256[2] memory transactionDelta, bytes32[] memory tags)
285+
internal
286+
view
287+
virtual
288+
{
289+
Delta.verify({proof: proof, instance: transactionDelta, verifyingKey: Delta.computeVerifyingKey(tags)});
290+
}
291+
267292
/// @notice Verifies the forwarder calls of a given action.
268293
/// @param action The action to verify the forwarder calls for.
269294
function _verifyForwarderCalls(Action calldata action) internal view {
@@ -299,4 +324,18 @@ contract ProtocolAdapter is IProtocolAdapter, ReentrancyGuardTransient, Commitme
299324
}
300325
}
301326
}
327+
328+
/// @notice An internal function adding a unit delta to the transactionDelta.
329+
/// @param transactionDelta The transaction delta.
330+
/// @param unitDelta The unit delta to add.
331+
/// @return sum The sum of the transaction delta and the unit delta.
332+
/// @dev This function is virtual to allow for it to be overridden, e.g., to mock delta proofs.
333+
function _addUnitDelta(uint256[2] memory transactionDelta, uint256[2] memory unitDelta)
334+
internal
335+
pure
336+
virtual
337+
returns (uint256[2] memory sum)
338+
{
339+
sum = transactionDelta.add(unitDelta);
340+
}
302341
}

contracts/src/libs/ComputableComponents.sol

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ library ComputableComponents {
1515
cm = sha256(abi.encode(resource));
1616
}
1717

18+
/// @notice Computes the resource commitment.
19+
/// @param resource The resource object.
20+
/// @return cm The computed commitment.
21+
function commitment_(Resource memory resource) internal pure returns (bytes32 cm) {
22+
cm = sha256(abi.encode(resource));
23+
}
24+
1825
/// @notice Computes the resource nullifier given a nullifier key.
1926
/// @param resource The resource object.
2027
/// @param nullifierKey The nullifier key.
@@ -24,6 +31,15 @@ library ComputableComponents {
2431
nf = sha256(abi.encode(resource, nullifierKey));
2532
}
2633

34+
/// @notice Computes the resource nullifier given a nullifier key.
35+
/// @param resource The resource object.
36+
/// @param nullifierKey The nullifier key.
37+
/// @return nf The computed nullifier.
38+
/// @dev This methods does not check that the nullifier key commitment matches the nullifier key.
39+
function nullifier_(Resource memory resource, bytes32 nullifierKey) internal pure returns (bytes32 nf) {
40+
nf = sha256(abi.encode(resource, nullifierKey));
41+
}
42+
2743
/// @notice Computes the resource kind.
2844
/// @param resource The resource object.
2945
/// @return k The computed kind.
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.30;
3+
4+
import {RiscZeroVerifierRouter} from "@risc0-ethereum/RiscZeroVerifierRouter.sol";
5+
import {RiscZeroMockVerifier} from "@risc0-ethereum/test/RiscZeroMockVerifier.sol";
6+
7+
import {Test} from "forge-std/Test.sol";
8+
9+
import {DeployRiscZeroVerifierRouterMock} from "../script/DeployRiscZeroVerifierRouterMock.s.sol";
10+
11+
import {TagLookup} from "../src/libs/TagLookup.sol";
12+
import {CommitmentAccumulator} from "../src/state/CommitmentAccumulator.sol";
13+
import {NullifierSet} from "../src/state/NullifierSet.sol";
14+
import {Transaction} from "../src/Types.sol";
15+
16+
import {ExampleGen} from "./mocks/ExampleGen.sol";
17+
import {ProtocolAdapterMock} from "./mocks/ProtocolAdapter.m.sol";
18+
19+
contract ProtocolAdapterMockTest is Test {
20+
using ExampleGen for RiscZeroMockVerifier;
21+
using ExampleGen for Transaction;
22+
23+
RiscZeroVerifierRouter internal _mockRouter;
24+
RiscZeroMockVerifier internal _mockVerifier;
25+
ProtocolAdapterMock internal _mockPa;
26+
27+
function setUp() public {
28+
(_mockRouter, _mockVerifier) = (new DeployRiscZeroVerifierRouterMock()).run();
29+
30+
_mockPa = new ProtocolAdapterMock(_mockRouter, 32, 4);
31+
}
32+
33+
function test_execute_1_txn_with_1_action_and_0_cus() public {
34+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({nActions: 1, nCUs: 0});
35+
36+
(Transaction memory txn,) = _mockVerifier.transaction(0, configs);
37+
_mockPa.execute(txn);
38+
}
39+
40+
function test_execute_1_txn_with_2_action_with_1_and_0_cus() public {
41+
ExampleGen.ActionConfig[] memory configs = new ExampleGen.ActionConfig[](2);
42+
configs[0] = ExampleGen.ActionConfig({nCUs: 1});
43+
configs[1] = ExampleGen.ActionConfig({nCUs: 0});
44+
45+
(Transaction memory txn,) = _mockVerifier.transaction(0, configs);
46+
_mockPa.execute(txn);
47+
}
48+
49+
function test_execute_1_txn_with_n_actions_and_n_cus(uint8 nActions, uint8 nCUs) public {
50+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({
51+
nActions: uint8(bound(nActions, 0, 5)),
52+
nCUs: uint8(bound(nCUs, 0, 2 ** (_mockPa.actionTreeDepth() - 1)))
53+
});
54+
55+
(Transaction memory txn,) = _mockVerifier.transaction(0, configs);
56+
_mockPa.execute(txn);
57+
}
58+
59+
function test_execute_2_txns_with_n_actions_and_n_cus(uint8 nActions, uint8 nCUs) public {
60+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({
61+
nActions: uint8(bound(nActions, 0, 5)),
62+
nCUs: uint8(bound(nCUs, 0, 2 ** (_mockPa.actionTreeDepth() - 1)))
63+
});
64+
65+
(Transaction memory txn, uint256 updatedNonce) = _mockVerifier.transaction({nonce: 0, configs: configs});
66+
_mockPa.execute(txn);
67+
68+
(txn,) = _mockVerifier.transaction(updatedNonce, configs);
69+
_mockPa.execute(txn);
70+
}
71+
72+
function test_verify_reverts_on_pre_existing_nullifier() public {
73+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({nActions: 1, nCUs: 1});
74+
75+
(Transaction memory tx1, uint256 updatedNonce) = _mockVerifier.transaction({nonce: 0, configs: configs});
76+
bytes32 preExistingNf = tx1.actions[0].complianceVerifierInputs[0].instance.consumed.nullifier;
77+
_mockPa.execute(tx1);
78+
79+
(Transaction memory tx2,) = _mockVerifier.transaction({nonce: updatedNonce, configs: configs});
80+
tx2.actions[0].complianceVerifierInputs[0].instance.consumed.nullifier = preExistingNf;
81+
vm.expectRevert(
82+
abi.encodeWithSelector(NullifierSet.PreExistingNullifier.selector, preExistingNf), address(_mockPa)
83+
);
84+
_mockPa.verify(tx2);
85+
}
86+
87+
function test_verify_reverts_on_pre_existing_commitment() public {
88+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({nActions: 1, nCUs: 1});
89+
90+
(Transaction memory tx1, uint256 updatedNonce) = _mockVerifier.transaction({nonce: 0, configs: configs});
91+
bytes32 preExistingCm = tx1.actions[0].complianceVerifierInputs[0].instance.created.commitment;
92+
_mockPa.execute(tx1);
93+
94+
(Transaction memory tx2,) = _mockVerifier.transaction({nonce: updatedNonce, configs: configs});
95+
tx2.actions[0].complianceVerifierInputs[0].instance.created.commitment = preExistingCm;
96+
vm.expectRevert(
97+
abi.encodeWithSelector(CommitmentAccumulator.PreExistingCommitment.selector, preExistingCm),
98+
address(_mockPa)
99+
);
100+
_mockPa.verify(tx2);
101+
}
102+
103+
function test_verify_reverts_on_duplicated_nullifier() public {
104+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({nActions: 1, nCUs: 2});
105+
106+
(Transaction memory txn,) = _mockVerifier.transaction({nonce: 0, configs: configs});
107+
bytes32 duplicatedNf = txn.actions[0].complianceVerifierInputs[0].instance.consumed.nullifier;
108+
txn.actions[0].complianceVerifierInputs[1].instance.consumed.nullifier = duplicatedNf;
109+
110+
vm.expectRevert(abi.encodeWithSelector(TagLookup.NullifierDuplicated.selector, duplicatedNf), address(_mockPa));
111+
_mockPa.verify(txn);
112+
}
113+
114+
function test_verify_reverts_on_duplicated_commitment() public {
115+
ExampleGen.ActionConfig[] memory configs = ExampleGen.generateActionConfigs({nActions: 1, nCUs: 2});
116+
117+
(Transaction memory txn,) = _mockVerifier.transaction({nonce: 0, configs: configs});
118+
bytes32 duplicatedCm = txn.actions[0].complianceVerifierInputs[0].instance.created.commitment;
119+
txn.actions[0].complianceVerifierInputs[1].instance.created.commitment = duplicatedCm;
120+
121+
vm.expectRevert(abi.encodeWithSelector(TagLookup.CommitmentDuplicated.selector, duplicatedCm), address(_mockPa));
122+
_mockPa.verify(txn);
123+
}
124+
}

0 commit comments

Comments
 (0)