11/// This contract can be used (with some small additions to create an economic
22/// incentive for the claimant to answer) to claim computation results for
3- /// scrypt (actually only the part where we do not have precompiled contracts)
4- /// and interactively convict a false claim.
5- /// Scrypt first applies a certain hash function to the input in 1024 iterations.
3+ /// scrypt and interactively convict a false claim.
4+ /// Scrypt starts and ends with a certain key derivation function that can be
5+ /// more or less directly computed by the EVM. The computation in the middle
6+ /// is much more complicated and needs interactive verification.
7+ /// Here, scrypt applies a certain hash function to the input in 1024 iterations.
68/// Then it applies the hash function another 1024 times but looks up an
79/// additional input in the table of the first 1024 values where the look
810/// up index depends on the first input.
4648///
4749/// Disclaimer: This has not been tested and is only meant as a proof of concept
4850/// and a way to compute the gas costs.
49- contract Scrypt {
50- address claimant;
51- address challenger;
51+ contract ScryptVerifier {
52+
53+ struct VerificationSession {
54+ address claimant;
55+ address challenger;
56+ bytes data;
57+ bytes32 hash;
58+ uint16 [] queries;
59+ uint [4 ][] values;
60+ }
61+ VerificationSession[] sessions;
5262
53- function Scrypt (uint [4 ] _input , uint [4 ] _output ) {
54- claimant = msg .sender ;
55- queries.length = 2 ;
56- queries.push (0 );
57- queries.push (2048 );
58- values.push (_input);
59- values.push (_output);
60- }
61- event Convicted ();
63+ /// Claim that scrypt(data) == hash. With reference to above,
64+ /// values[0] = pbkdf2(data) (the "input") and
65+ /// pbkdf2(values[2048]) should be equal to hash, values[2048] is called "output".
66+ function claimComputation (bytes _data , bytes32 _hash ) {
67+ sessions.push (VerificationSession ({
68+ claimant: msg .sender ,
69+ challenger: address (0 ),
70+ data: _data,
71+ hash: _hash,
72+ queries: new uint16 [](0 ),
73+ values: new uint [4 ][](0 )
74+ }));
75+ NewClaim (sessions.length - 1 );
76+ }
77+ event NewClaim (uint sessionId );
78+ event Convicted (uint sessionId );
79+ event NewQuery (uint sessionId );
80+ event NewResponse (uint sessionId );
6281
63- modifier onlyClaimant () { if (msg .sender != claimant) throw ; _ }
64- modifier onlyChallenger () {
65- if (challenger == 0 ) challenger = msg .sender ;
66- else if (msg .sender != challenger) throw ;
82+ modifier onlyClaimant (uint id ) { if (msg .sender != sessions[id].claimant) throw ; _ }
83+ modifier onlyChallenger (uint id ) {
84+ var session = sessions[id];
85+ if (session.challenger == 0 ) session.challenger = msg .sender ;
86+ else if (msg .sender != session.challenger) throw ;
6787 _
6888 }
6989
70- uint16 [] public queries;
7190 /// Challenger queries claimant for the value on a wire `_i`.
7291 /// Value 0 is the input, value 1024 is the first input to the second
7392 /// half of the computation, value 2048 is the output.
74- function query (uint16 _i ) onlyChallenger {
93+ function query (uint session , uint16 _i ) onlyChallenger (session) {
7594 if (_i > 2048 ) throw ;
76- queries.push (_i);
95+ sessions[session].queries.push (_i);
96+ NewQuery (session);
7797 }
7898
79- uint [4 ][] public values;
8099 /// Claimant responds to challenge, committing to a value.
81- function respond (uint [4 ] _value ) onlyClaimant {
82- if (values.length >= queries.length ) throw ;
83- values.push (_value);
100+ function respond (uint session , uint [4 ] _value ) onlyClaimant (session) {
101+ var s = sessions[session];
102+ if (s.values.length >= s.queries.length ) throw ;
103+ s.values.push (_value);
104+ NewResponse (session);
84105 }
85106
86107 /// Convicts the claimant to have provided inputs and outputs for a single
@@ -89,24 +110,48 @@ contract Scrypt {
89110 /// q1 is the query index of the first input, q2 the query index of
90111 /// the output and q2 is the query index of the auxiliary input only
91112 /// used in the second half of the scrypt computation.
92- function convict (uint q1 , uint q2 , uint q3 ) {
93- var i = queries[q1];
94- if (queries[q2] != i + 1 ) throw ;
95- var input = values[q1];
96- var output = values[q2];
113+ function convict (uint session , uint q1 , uint q2 , uint q3 ) onlyChallenger (session) {
114+ var s = sessions[session];
115+ var i = s.queries[q1];
116+ if (s.queries[q2] != i + 1 ) throw ;
117+ var input = s.values[q1];
118+ var output = s.values[q2];
97119 if (i < 1024 ) {
98120 if (! verifyFirstHalf (input, output))
99- Convicted ();
121+ Convicted (session );
100122 } else {
101- var auxIndex = queries[q3];
123+ var auxIndex = s. queries[q3];
102124 if (auxIndex != (input[2 ] / 0x100000000000000000000000000000000000000000000000000000000 ) % 1024 )
103125 throw ;
104- var auxInput = values[q3];
126+ var auxInput = s. values[q3];
105127 if (! verifySecondHalf (input, auxInput, output))
106- Convicted ();
128+ Convicted (session );
107129 }
108130 }
109131
132+ /// Convicts the claimant to have provided an incorrect value for value[0].
133+ function convictInitial (uint session , uint q ) onlyChallenger (session) {
134+ var s = sessions[session];
135+ if (s.queries[q] != 0 ) throw ;
136+ var v = s.values[q];
137+ var h = KeyDeriv.pbkdf2 (s.data, s.data, 128 );
138+ if (uint (h[0 ]) != v[0 ] || uint (h[1 ]) != v[1 ] || uint (h[2 ]) != v[2 ] || uint (h[3 ]) != v[3 ])
139+ Convicted (session);
140+ }
141+
142+ /// Convicts the claimant to have provided an incorrect value for value[2048].
143+ function convictFinal (uint session , uint q ) onlyChallenger (session) {
144+ var s = sessions[session];
145+ if (s.queries[q] != 2048 ) throw ;
146+ var v = s.values[q];
147+ bytes memory val = new bytes (128 );
148+ for (uint i = 0 ; i < 128 ; i ++ )
149+ val[i] = byte (uint8 (v[i / 32 ] / 2 ** ((32 - (i % 32 )) * 8 )));
150+ var h = KeyDeriv.pbkdf2 (val, val, 32 );
151+ if (h[0 ] != s.hash)
152+ Convicted (session);
153+ }
154+
110155 /// Verifies a salsa step in the first half of the scrypt computation.
111156 function verifyFirstHalf (uint [4 ] input , uint [4 ] output ) constant returns (bool ) {
112157 var (a, b, c, d) = Salsa8.round (input[0 ], input[1 ], input[2 ], input[3 ]);
@@ -121,6 +166,48 @@ contract Scrypt {
121166 return verifyFirstHalf (input, output);
122167 }
123168
169+ // /// This function can be used to compute the correct response to a
170+ // /// challenge. It is only intended to be called without a transaction
171+ // /// because it consumes tremendous amounts of gas.
172+ // function computeResponse(uint16 _i) constant returns (uint[4]) {
173+ // if (_i <= 1024)
174+ // return computeResponseFirstHalf(_i);
175+ // else
176+ // return computeResponseSecondHalf(_i);
177+ // }
178+ // function computeResponseSecondHalf(uint16 _i) constant returns (uint[4]) {
179+ // uint[4 * 1024] memory lookup;
180+ // uint a = values[0][0];
181+ // uint b = values[0][1];
182+ // uint c = values[0][2];
183+ // uint d = values[0][3];
184+ // uint l = 0;
185+ // lookup[l++] = a;
186+ // lookup[l++] = b;
187+ // lookup[l++] = c;
188+ // lookup[l++] = d;
189+ // for (uint16 i = 1; i <= 1024; i++) {
190+ // (a, b, c, d) = Salsa8.round(a, b, c, d);
191+ // lookup[l++] = a;
192+ // lookup[l++] = b;
193+ // lookup[l++] = c;
194+ // lookup[l++] = d;
195+ // }
196+ // for (; i <= _i; i++) {
197+ // uint auxIndex = ((c / 0x100000000000000000000000000000000000000000000000000000000) % 1024) * 4;
198+ // (a, b, c, d) = Salsa8.round(a ^ lookup[auxIndex], b ^ lookup[auxIndex + 1], c ^ lookup[auxIndex + 2], d ^ lookup[auxIndex + 3]);
199+ // }
200+ // return [a, b, c, d];
201+ // }
202+ // function computeResponseFirstHalf(uint16 _i) constant internal returns (uint[4]) {
203+ // uint a = values[0][0];
204+ // uint b = values[0][1];
205+ // uint c = values[0][2];
206+ // uint d = values[0][3];
207+ // for (uint16 i = 1; i <= _i; i++)
208+ // (a, b, c, d) = Salsa8.round(a, b, c, d);
209+ // return [a, b, c, d];
210+ // }
124211}
125212
126213library Salsa8 {
@@ -199,3 +286,31 @@ library Salsa8 {
199286 return (_a, _b, _c, _d);
200287 }
201288}
289+ library KeyDeriv {
290+ function hmacsha256 (bytes key , bytes message ) constant returns (bytes32 ) {
291+ bytes32 keyl;
292+ bytes32 keyr;
293+ uint i;
294+ if (key.length > 64 ) {
295+ keyl = sha256 (key);
296+ } else {
297+ for (i = 0 ; i < key.length && i < 32 ; i++ )
298+ keyl |= bytes32 (uint (key[i]) * 2 ** (8 * (31 - i)));
299+ for (i = 32 ; i < key.length && i < 64 ; i++ )
300+ keyr |= bytes32 (uint (key[i]) * 2 ** (8 * (63 - i)));
301+ }
302+ bytes32 threesix = 0x3636363636363636363636363636363636363636363636363636363636363636 ;
303+ bytes32 fivec = 0x5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c ;
304+ return sha256 (fivec ^ keyl, fivec ^ keyr, sha256 (threesix ^ keyl, threesix ^ keyr, message));
305+ }
306+ /// PBKDF2 restricted to c=1, hash = hmacsha256 and dklen being a multiple of 32 not larger than 128
307+ function pbkdf2 (bytes key , bytes salt , uint dklen ) constant returns (bytes32 [4 ] r ) {
308+ var msg = new bytes (salt.length + 4 );
309+ for (uint i = 0 ; i < salt.length ; i++ )
310+ msg[i] = salt[i];
311+ for (i = 0 ; i * 32 < dklen; i++ ) {
312+ msg[msg .length - 1 ] = bytes1 (uint8 (i + 1 ));
313+ r[i] = hmacsha256 (key, msg);
314+ }
315+ }
316+ }
0 commit comments