Skip to content
This repository was archived by the owner on Jan 14, 2025. It is now read-only.

Commit 0aeff24

Browse files
committed
Merge pull request #56 from ethereum/chriseth-patch-2
Add initial and final kdf, allow multiple verifications.
2 parents 162bbce + 261bb28 commit 0aeff24

File tree

1 file changed

+150
-35
lines changed

1 file changed

+150
-35
lines changed

scrypt/scrypt.sol

Lines changed: 150 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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.
@@ -46,41 +48,60 @@
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

126213
library 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

Comments
 (0)