Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ethereum/dapp-bin
Browse files Browse the repository at this point in the history
  • Loading branch information
vub authored and vub committed Jan 20, 2016
2 parents 00a61e0 + fda68a2 commit 162bbce
Showing 1 changed file with 201 additions and 0 deletions.
201 changes: 201 additions & 0 deletions scrypt/scrypt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/// This contract can be used (with some small additions to create an economic
/// incentive for the claimant to answer) to claim computation results for
/// scrypt (actually only the part where we do not have precompiled contracts)
/// and interactively convict a false claim.
/// Scrypt first applies a certain hash function to the input in 1024 iterations.
/// Then it applies the hash function another 1024 times but looks up an
/// additional input in the table of the first 1024 values where the look
/// up index depends on the first input.
/// Formally: Assume that H computes the salsa8 hash function as defined below.
/// If value[0] is the input and value[2048] is the output, we have the following
/// relation:
/// value[i] = H(value[i-1]) for 0 < i <= 1024
/// value[i] = H(value[i-1] ^ value[value[i-1] % 1024]) for 1024 < i <= 2048
/// Strictly, not the lowermost byte in value[i-1] is used for addressing,
/// but that does not matter here.
///
/// In this contract, a challenger can ask for each of the value[i]. If the
/// claimant was wrong and the challenger asks in a clever way, she can
/// force the claimant to commit to values for a certain step that do not
/// match the computation done in the actual step.
///
/// The strategy is as follows:
/// We start with a binary search for a point where value[i] is correct but
/// value[i+1] is incorrect (if the response is correct we go up, if it is
/// incorrect we go down).
/// If i <= 1024, the challenger can directly call convict because
/// the commited values do not match the computation.
/// If this ends up at a point i > 1024 it is more complicated because of
/// the second auxiliary input. The challenger has to compute the index
/// of the auxiliary input (value[i] % 1024) and query that. If it is correct,
/// we can again call convict because the output will not match the committed
/// value. If the auxiliary input value is incorrect, it might be that the
/// computation still matches. In this case, we cannot directly call convict
/// but we found an incorrect value at an index smaller than i. This means
/// that we can continue our binary search inside the first half and
/// will end up at a point i' <= 1024 where we can directyl call convict.
///
/// Overall, this should take not more than 22 rounds. Each round costs approximately
/// 140000 gas and a call to convict is about 80000 gas.
/// This means in total, convicting a false claim should not cost more than
/// around three million gas.
/// Of course, as usual, having to convict a false claimant is only a matter
/// of last resort, because every claimant knows that a false claim can be
/// detected and if this is connected with deposits from both sides that
/// go to the winning party, a false claim will probably not be attempted.
///
/// Disclaimer: This has not been tested and is only meant as a proof of concept
/// and a way to compute the gas costs.
contract Scrypt {
address claimant;
address challenger;

function Scrypt(uint[4] _input, uint[4] _output) {
claimant = msg.sender;
queries.length = 2;
queries.push(0);
queries.push(2048);
values.push(_input);
values.push(_output);
}
event Convicted();

modifier onlyClaimant() { if (msg.sender != claimant) throw; _ }
modifier onlyChallenger() {
if (challenger == 0) challenger = msg.sender;
else if (msg.sender != challenger) throw;
_
}

uint16[] public queries;
/// Challenger queries claimant for the value on a wire `_i`.
/// Value 0 is the input, value 1024 is the first input to the second
/// half of the computation, value 2048 is the output.
function query(uint16 _i) onlyChallenger {
if (_i > 2048) throw;
queries.push(_i);
}

uint[4][] public values;
/// Claimant responds to challenge, committing to a value.
function respond(uint[4] _value) onlyClaimant {
if (values.length >= queries.length) throw;
values.push(_value);
}

/// Convicts the claimant to have provided inputs and outputs for a single
/// step that do not match the computation of the step.
/// q1, q2 and q3 are query indices providing the relevant values.
/// q1 is the query index of the first input, q2 the query index of
/// the output and q2 is the query index of the auxiliary input only
/// used in the second half of the scrypt computation.
function convict(uint q1, uint q2, uint q3) {
var i = queries[q1];
if (queries[q2] != i + 1) throw;
var input = values[q1];
var output = values[q2];
if (i < 1024) {
if (!verifyFirstHalf(input, output))
Convicted();
} else {
var auxIndex = queries[q3];
if (auxIndex != (input[2] / 0x100000000000000000000000000000000000000000000000000000000) % 1024)
throw;
var auxInput = values[q3];
if (!verifySecondHalf(input, auxInput, output))
Convicted();
}
}

/// Verifies a salsa step in the first half of the scrypt computation.
function verifyFirstHalf(uint[4] input, uint[4] output) constant returns (bool) {
var (a, b, c, d) = Salsa8.round(input[0], input[1], input[2], input[3]);
return (a == output[0] && b == output[1] && c == output[2] && d == output[3]);
}
/// Verifies a salsa step in the second half of the scrypt computation.
function verifySecondHalf(uint[4] input, uint[4] vinput, uint[4] output) constant returns (bool) {
input[0] ^= vinput[0];
input[1] ^= vinput[1];
input[2] ^= vinput[2];
input[3] ^= vinput[3];
return verifyFirstHalf(input, output);
}

}

library Salsa8 {
uint constant m0 = 0x100000000000000000000000000000000000000000000000000000000;
uint constant m1 = 0x1000000000000000000000000000000000000000000000000;
uint constant m2 = 0x10000000000000000000000000000000000000000;
uint constant m3 = 0x100000000000000000000000000000000;
uint constant m4 = 0x1000000000000000000000000;
uint constant m5 = 0x10000000000000000;
uint constant m6 = 0x100000000;
uint constant m7 = 0x1;
function quarter(uint32 y0, uint32 y1, uint32 y2, uint32 y3)
internal returns (uint32, uint32, uint32, uint32)
{
uint32 t;
t = y0 + y3;
y1 = y1 ^ ((t * 2**7) | (t / 2**(32-7)));
t = y1 + y0;
y2 = y2 ^ ((t * 2**9) | (t / 2**(32-9)));
t = y2 + y1;
y3 = y3 ^ ((t * 2**13) | (t / 2**(32-13)));
t = y3 + y2;
y0 = y0 ^ ((t * 2**18) | (t / 2**(32-18)));
return (y0, y1, y2, y3);
}
function get(uint data, uint word) internal returns (uint32 x)
{
return uint32(data / 2**(256 - word * 32 - 32));
}
function put(uint x, uint word) internal returns (uint) {
return x * 2**(256 - word * 32 - 32);
}
function rowround(uint first, uint second) internal returns (uint f, uint s)
{
var (a,b,c,d) = quarter(uint32(first / m0), uint32(first / m1), uint32(first / m2), uint32(first / m3));
f = (((((uint(a) * 2**32) | uint(b)) * 2 ** 32) | uint(c)) * 2**32) | uint(d);
(b,c,d,a) = quarter(uint32(first / m5), uint32(first / m6), uint32(first / m7), uint32(first / m4));
f = (((((((f * 2**32) | uint(a)) * 2**32) | uint(b)) * 2 ** 32) | uint(c)) * 2**32) | uint(d);
(c,d,a,b) = quarter(uint32(second / m2), uint32(second / m3), uint32(second / m0), uint32(second / m1));
s = (((((uint(a) * 2**32) | uint(b)) * 2 ** 32) | uint(c)) * 2**32) | uint(d);
(d,a,b,c) = quarter(uint32(second / m7), uint32(second / m4), uint32(second / m5), uint32(second / m6));
s = (((((((s * 2**32) | uint(a)) * 2**32) | uint(b)) * 2 ** 32) | uint(c)) * 2**32) | uint(d);
}
function columnround(uint first, uint second) internal returns (uint f, uint s)
{
var (a,b,c,d) = quarter(uint32(first / m0), uint32(first / m4), uint32(second / m0), uint32(second / m4));
f = (uint(a) * m0) | (uint(b) * m4);
s = (uint(c) * m0) | (uint(d) * m4);
(a,b,c,d) = quarter(uint32(first / m5), uint32(second / m1), uint32(second / m5), uint32(first / m1));
f |= (uint(a) * m5) | (uint(d) * m1);
s |= (uint(b) * m1) | (uint(c) * m5);
(a,b,c,d) = quarter(uint32(second / m2), uint32(second / m6), uint32(first / m2), uint32(first / m6));
f |= (uint(c) * m2) | (uint(d) * m6);
s |= (uint(a) * m2) | (uint(b) * m6);
(a,b,c,d) = quarter(uint32(second / m7), uint32(first / m3), uint32(first / m7), uint32(second / m3));
f |= (uint(b) * m3) | (uint(c) * m7);
s |= (uint(a) * m7) | (uint(d) * m3);
}
function salsa20_8(uint _first, uint _second) internal returns (uint rfirst, uint rsecond) {
uint first = _first;
uint second = _second;
for (uint i = 0; i < 8; i += 2)
{
(first, second) = columnround(first, second);
(first, second) = rowround(first, second);
}
for (i = 0; i < 8; i++)
{
rfirst |= put(get(_first, i) + get(first, i), i);
rsecond |= put(get(_second, i) + get(second, i), i);
}
}
function round(uint _a, uint _b, uint _c, uint _d) constant returns (uint, uint, uint, uint) {
(_a, _b) = salsa20_8(_a ^ _c, _b ^ _d);
(_c, _d) = salsa20_8(_a ^ _c, _b ^ _d);
return (_a, _b, _c, _d);
}
}

0 comments on commit 162bbce

Please sign in to comment.