@@ -5,20 +5,31 @@ import {Output, OutputLib, Receipt, ReceiptClaim, ReceiptClaimLib, IRiscZeroVeri
5
5
import {Groth16Verifier} from "./groth16/Groth16Verifier.sol " ;
6
6
import {SafeCast} from "openzeppelin-contracts/contracts/utils/math/SafeCast.sol " ;
7
7
8
- contract Dispute is IRiscZeroVerifier , Groth16Verifier {
8
+ /// @notice A Groth16 seal over the claimed receipt claim.
9
+ struct Seal {
10
+ uint256 [2 ] a;
11
+ uint256 [2 ][2 ] b;
12
+ uint256 [2 ] c;
13
+ }
14
+
15
+ contract RStarM is IRiscZeroVerifier , Groth16Verifier {
16
+ using ReceiptClaimLib for ReceiptClaim;
17
+ using OutputLib for Output;
18
+ using SafeCast for uint256 ;
19
+
9
20
struct Validator {
10
21
bool isRegistered;
11
22
uint256 stake;
12
23
}
13
24
14
25
struct OptimisticCommitment {
15
- bytes blockHash;
26
+ bytes32 blockHash;
16
27
bytes stateCommitment; // The state commitment associated with the block
17
28
uint256 validatorCount; // Number of validators who have submitted this commitment
18
29
}
19
30
20
31
struct Dispute {
21
- bytes blockHash;
32
+ bytes32 blockHash;
22
33
uint256 timestamp;
23
34
DisputeState state;
24
35
}
@@ -45,17 +56,27 @@ contract Dispute is IRiscZeroVerifier, Groth16Verifier {
45
56
uint256 public p; // Time to run the zero-knowledge proof
46
57
uint256 public m; // Minimum number of validators required to accept a block
47
58
59
+ /// @notice Control ID hash for the identity_p254 predicate decomposed by `splitDigest`.
60
+ /// @dev This value controls what set of recursion programs, and therefore what version of the
61
+ /// zkVM circuit, will be accepted by this contract. Each instance of this verifier contract
62
+ /// will accept a single release of the RISC Zero circuits.
63
+ ///
64
+ /// New releases of RISC Zero's zkVM require updating these values. These values can be
65
+ /// obtained by running `cargo run --bin bonsai-ethereum-contracts -F control-id`
66
+ uint256 public immutable CONTROL_ID_0;
67
+ uint256 public immutable CONTROL_ID_1;
68
+
48
69
mapping (address => Validator) public validators;
49
- mapping (bytes => Dispute) public disputes;
70
+ mapping (bytes32 => Dispute) public disputes;
50
71
mapping (bytes32 => Proof) public verifiedProofs; // Maps blockHash to Proof
51
72
mapping (bytes32 => OptimisticCommitment) public optimisticCommitments; // Maps blockHash to OptimisticCommitment
52
73
53
74
IRiscZeroVerifier public verifier;
54
75
55
76
event ValidatorRegistered (address indexed validator , uint256 stake );
56
77
event ValidatorDeregistered (address indexed validator );
57
- event DisputeSubmitted (bytes indexed disputeHash , bytes blockHash , address indexed submitter );
58
- event DisputeResolved (bytes indexed disputeHash , DisputeState state );
78
+ event DisputeSubmitted (bytes32 indexed disputeHash , bytes32 blockHash , address indexed submitter );
79
+ event DisputeResolved (bytes32 indexed disputeHash , DisputeState state );
59
80
event ProofSubmitted (bytes32 indexed blockHash , bool isValid );
60
81
event ProofVerified (bytes32 indexed blockHash , bool isValid );
61
82
event BlockAccepted (bytes32 indexed blockHash );
@@ -86,14 +107,16 @@ contract Dispute is IRiscZeroVerifier, Groth16Verifier {
86
107
emit ValidatorDeregistered (msg .sender );
87
108
}
88
109
89
- function submitDispute (bytes calldata blockHash ) external {
90
- bytes memory disputeHash = abi.encodePacked (blockHash, msg .sender , block .timestamp );
110
+ // We probably want to use bytes calldata here to reduce gas but going with bytes32 for now as we
111
+ // need it elsewhere.
112
+ function submitDispute (bytes32 blockHash ) external {
113
+ bytes32 disputeHash = keccak256 (abi.encodePacked (blockHash, msg .sender , block .timestamp ));
91
114
require (disputes[disputeHash].timestamp == 0 , "Dispute already submitted " );
92
115
disputes[disputeHash] = Dispute (blockHash, block .timestamp , DisputeState.SUBMITTED);
93
116
emit DisputeSubmitted (disputeHash, blockHash, msg .sender );
94
117
}
95
118
96
- function resolveDispute (bytes calldata disputeHash , DisputeState state ) external {
119
+ function resolveDispute (bytes32 disputeHash , DisputeState state ) external {
97
120
require (state > DisputeState.SUBMITTED && state <= DisputeState.UNVERIFIABLE, "Invalid state transition " );
98
121
Dispute storage dispute = disputes[disputeHash];
99
122
require (dispute.timestamp != 0 , "Dispute not found " );
@@ -102,17 +125,53 @@ contract Dispute is IRiscZeroVerifier, Groth16Verifier {
102
125
emit DisputeResolved (disputeHash, state);
103
126
}
104
127
105
- function submitProof (bytes32 blockHash , Receipt calldata receipt , bytes32 [] calldata publicInputs ) external {
128
+ // removed publicInputs from the function signature as I don't think we need it.
129
+ function submitProof (bytes32 blockHash , Receipt calldata receipt ) external {
106
130
require (! verifiedProofs[blockHash].exists, "Proof already submitted for this block " );
107
131
bool isValid = verifier.verify_integrity (receipt);
108
132
verifiedProofs[blockHash] = Proof (blockHash, isValid, true );
109
133
emit ProofSubmitted (blockHash, isValid);
110
134
}
111
135
112
- // Additional checks or logic could be implemented here based on the application's needs
113
- function verifyProof (bytes32 blockHash ) external view returns (bool isValid , bool exists ) {
114
- require (verifiedProofs[blockHash].exists, "Proof not found " );
115
- return (verifiedProofs[blockHash].isValid, verifiedProofs[blockHash].exists);
136
+ /// @notice splits a digest into two 128-bit words to use as public signal inputs.
137
+ /// @dev RISC Zero's Circom verifier circuit takes each of two hash digests in two 128-bit
138
+ /// chunks. These values can be derived from the digest by splitting the digest in half and
139
+ /// then reversing the bytes of each.
140
+ function splitDigest (bytes32 digest ) internal pure returns (uint256 , uint256 ) {
141
+ uint256 reversed = reverseByteOrderUint256 (uint256 (digest));
142
+ return (uint256 (uint128 (uint256 (reversed))), uint256 (reversed >> 128 ));
143
+ }
144
+
145
+ function verify (bytes calldata seal , bytes32 imageId , bytes32 postStateDigest , bytes32 journalDigest )
146
+ public
147
+ view
148
+ returns (bool )
149
+ {
150
+ //require(verifiedProofs[blockHash].exists, "Proof not found");
151
+ Receipt memory receipt = Receipt (
152
+ seal,
153
+ ReceiptClaim (
154
+ imageId,
155
+ postStateDigest,
156
+ ExitCode (SystemExitCode.Halted, 0 ),
157
+ bytes32 (0 ),
158
+ Output (journalDigest, bytes32 (0 )).digest ()
159
+ )
160
+ );
161
+ return grothVerify (receipt);
162
+ }
163
+
164
+ function grothVerify (Receipt memory receipt ) public view returns (bool ) {
165
+ (uint256 claim0 , uint256 claim1 ) = splitDigest (receipt.claim.digest ());
166
+ Seal memory seal = abi.decode (receipt.seal, (Seal));
167
+ return this .verifyProof (seal.a, seal.b, seal.c, [CONTROL_ID_0, CONTROL_ID_1, claim0, claim1]);
168
+ }
169
+
170
+ // The camel case here is not standard solidity practice. But we use it because its the implemntation of the interface.
171
+ function verify_integrity (Receipt memory receipt ) public view returns (bool ) {
172
+ (uint256 claim0 , uint256 claim1 ) = splitDigest (receipt.claim.digest ());
173
+ Seal memory seal = abi.decode (receipt.seal, (Seal));
174
+ return this .verifyProof (seal.a, seal.b, seal.c, [CONTROL_ID_0, CONTROL_ID_1, claim0, claim1]);
116
175
}
117
176
118
177
function submitOptimisticCommitment (bytes32 blockHash , bytes calldata stateCommitment ) external {
@@ -134,4 +193,31 @@ contract Dispute is IRiscZeroVerifier, Groth16Verifier {
134
193
emit BlockAccepted (blockHash);
135
194
}
136
195
}
196
+
197
+ /// @notice reverse the byte order of the uint256 value.
198
+ /// @dev Soldity uses a big-endian ABI encoding. Reversing the byte order before encoding
199
+ /// ensure that the encoded value will be little-endian.
200
+ /// Written by k06a. https://ethereum.stackexchange.com/a/83627
201
+ function reverseByteOrderUint256 (uint256 input ) public pure returns (uint256 v ) {
202
+ v = input;
203
+
204
+ // swap bytes
205
+ v = ((v & 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00 ) >> 8 )
206
+ | ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF ) << 8 );
207
+
208
+ // swap 2-byte long pairs
209
+ v = ((v & 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000 ) >> 16 )
210
+ | ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF ) << 16 );
211
+
212
+ // swap 4-byte long pairs
213
+ v = ((v & 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000 ) >> 32 )
214
+ | ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF ) << 32 );
215
+
216
+ // swap 8-byte long pairs
217
+ v = ((v & 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000 ) >> 64 )
218
+ | ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF ) << 64 );
219
+
220
+ // swap 16-byte long pairs
221
+ v = (v >> 128 ) | (v << 128 );
222
+ }
137
223
}
0 commit comments