Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/IDisputeResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ pragma solidity ^0.4.18;
contract IDisputeResolver {
enum Status { NONE, UNRESOLVED, SOLVER_WINS, VERIFIER_WINS }
function status(bytes32 gameId) public view returns (Status);
function newGame(address solver, address verifier, bytes32 spec) public returns (bytes32 gameId);
function newGame(address solver, address verifier, bytes32 spec, uint when) public returns (bytes32 gameId);
}

2 changes: 1 addition & 1 deletion contracts/custom.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract GenericCustom {
function clock(bytes32 id) public view returns (uint) {
return tasks[id].clock;
}

// Check if has resolved into correct state: merkle root of output data and output size
function resolved(bytes32 id, bytes32 state, uint size) public view returns (bool) {
Task storage t = tasks[id];
Expand Down
241 changes: 234 additions & 7 deletions contracts/dispute.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ pragma solidity ^0.4.15;
import "./common-onchain.sol";
import "./IDisputeResolver.sol";

contract Dispute is CommonOnchain, IDisputeResolver {
// dispute for one computation step, divide into phases
contract WasmDispute is CommonOnchain, IDisputeResolver {

uint constant TIMEOUT = 10;

Expand All @@ -19,7 +20,7 @@ contract Dispute is CommonOnchain, IDisputeResolver {

mapping (bytes32 => Task) tasks;

function newGame(address s, address v, bytes32 spec) public returns (bytes32) {
function newGame(address s, address v, bytes32 spec, uint) public returns (bytes32) {
require(spec != 0);
bytes32 id = keccak256(abi.encodePacked(s, v, spec));
Task storage t = tasks[id];
Expand Down Expand Up @@ -105,18 +106,20 @@ contract PhaseDispute is IDisputeResolver {
bytes32[] phases;
address res;
bytes32 dispute_id;
uint when;
}

mapping (bytes32 => Task) tasks;

function newGame(address s, address v, bytes32 spec) public returns (bytes32) {
function newGame(address s, address v, bytes32 spec, uint when) public returns (bytes32) {
require(spec != 0);
bytes32 id = keccak256(abi.encodePacked(s, v, spec));
Task storage t = tasks[id];
t.solver = s;
t.verifier = v;
t.spec = spec;
t.bnum = block.number;
t.when = when;
return id;
}

Expand All @@ -142,11 +145,12 @@ contract PhaseDispute is IDisputeResolver {
Task storage t = tasks[dispute_id];
require(msg.sender == t.solver);
require(q < t.phases.length-1);
t.dispute_id = IDisputeResolver(t.res).newGame(t.solver, t.verifier, keccak256(abi.encodePacked(t.phases[q], t.phases[q+1], q)));
t.dispute_id = IDisputeResolver(t.res).newGame(t.solver, t.verifier, keccak256(abi.encodePacked(t.phases[q], t.phases[q+1], q)), t.when);
}

}

// abstract dispute for handling a computation step
contract TransitionDispute is IDisputeResolver {

uint constant TIMEOUT = 10;
Expand All @@ -161,7 +165,7 @@ contract TransitionDispute is IDisputeResolver {

mapping (bytes32 => Task) tasks;

function newGame(address s, address v, bytes32 spec) public returns (bytes32) {
function newGame(address s, address v, bytes32 spec, uint) public returns (bytes32) {
require(spec != 0);
bytes32 id = keccak256(abi.encodePacked(s, v, spec));
Task storage t = tasks[id];
Expand Down Expand Up @@ -190,6 +194,7 @@ contract TransitionDispute is IDisputeResolver {

}

// handling a single phase. The idea is that by composing this with PhaseDispute, we get the same results as WasmDispute
contract StepDispute is CommonOnchain, TransitionDispute {

bytes32 mask = 0xffffffffffffffffffffffffffffffffffffffffffffffff;
Expand Down Expand Up @@ -237,6 +242,7 @@ contract StepDispute is CommonOnchain, TransitionDispute {

}

// implements the interactive search
contract InteractiveDispute is IDisputeResolver {

uint constant TIMEOUT = 10;
Expand All @@ -247,6 +253,7 @@ contract InteractiveDispute is IDisputeResolver {
uint bnum;
bool verifier_turn;
bytes32 spec;
uint when;
bytes32[] proof;
address res;
bytes32 dispute_id;
Expand All @@ -257,14 +264,15 @@ contract InteractiveDispute is IDisputeResolver {

mapping (bytes32 => Task) tasks;

function newGame(address s, address v, bytes32 spec) public returns (bytes32) {
function newGame(address s, address v, bytes32 spec, uint when) public returns (bytes32) {
require(spec != 0);
bytes32 id = keccak256(abi.encodePacked(s, v, spec));
Task storage t = tasks[id];
t.solver = s;
t.verifier = v;
t.spec = spec;
t.bnum = block.number;
t.when = when;
return id;
}

Expand Down Expand Up @@ -321,7 +329,226 @@ contract InteractiveDispute is IDisputeResolver {
emit Queried(id, r.idx1, r.idx2);
// Size eventually becomes zero here
// Then call step resolver
if (r.size == 0) r.dispute_id = IDisputeResolver(r.res).newGame(r.solver, r.verifier, keccak256(abi.encodePacked(r.proof[r.idx1], r.proof[r.idx1+1])));
if (r.size == 0) r.dispute_id = IDisputeResolver(r.res).newGame(r.solver, r.verifier, keccak256(abi.encodePacked(r.proof[r.idx1], r.proof[r.idx1+1])), r.when);
}

}

// helper class
contract BasicDispute is IDisputeResolver {

uint constant TIMEOUT = 10;

struct Task {
address solver;
address verifier;
uint bnum;
bytes32 spec;
uint when;
}

mapping (bytes32 => Task) tasks;

function newGame(address s, address v, bytes32 spec, uint when) public returns (bytes32) {
require(spec != 0);
bytes32 id = keccak256(s, v, spec);
Task storage t = tasks[id];
t.solver = s;
t.verifier = v;
t.spec = spec;
t.bnum = block.number;
t.when = when;
return id;
}
}

// combination of two disputes
contract AndDispute is BasicDispute {
IDisputeResolver a;
IDisputeResolver b;

mapping (bytes32 => bytes32) dispute_a;
mapping (bytes32 => bytes32) dispute_b;

constructor (address aa, address ab) public {
a = IDisputeResolver(aa);
b = IDisputeResolver(ab);
}

function init(bytes32 id, bytes32 da, bytes32 db, uint when) public {
Task storage t = tasks[id];
require(t.spec == keccak256(da, db));
dispute_a[id] = a.newGame(t.solver, t.verifier, da, when);
dispute_b[id] = b.newGame(t.solver, t.verifier, db, when);
}

function status(bytes32 id) public view returns (Status) {
Task storage t = tasks[id];
if (t.spec == 0) return Status.NONE;
Status sa = a.status(dispute_a[id]);
Status sb = b.status(dispute_b[id]);
if (sa == Status.VERIFIER_WINS) return Status.VERIFIER_WINS;
if (sb == Status.VERIFIER_WINS) return Status.VERIFIER_WINS;
if (sa == Status.SOLVER_WINS && sb == Status.SOLVER_WINS) return Status.SOLVER_WINS;
return Status.UNRESOLVED;
}

}

// Combine multiple similar disputes. Solver must be able to win every subdispute to win this
contract MultipleDispute is BasicDispute {
IDisputeResolver a;

struct Dispute {
bytes32[] lst;
bytes32[] ids;
}

mapping (bytes32 => Dispute) disputes;

constructor (address aa) public {
a = IDisputeResolver(aa);
}

function init(bytes32 id, bytes32[] lst) public {
Task storage t = tasks[id];
Dispute storage d = disputes[id];
require(t.spec == keccak256(lst));
d.lst = lst;
for (uint i = 0; i < lst.length; i++) d.ids.push(a.newGame(t.solver, t.verifier, lst[i], t.when));
}

function status(bytes32 id) public view returns (Status) {
Task storage t = tasks[id];
if (t.spec == 0) return Status.NONE;
Dispute storage d = disputes[id];
for (uint i = 0; i < d.ids.length; i++) {
Status sa = a.status(d.ids[i]);
if (sa == Status.VERIFIER_WINS) return Status.VERIFIER_WINS;
if (sa != Status.SOLVER_WINS) return Status.UNRESOLVED;
}
return Status.SOLVER_WINS;
}

}

// Handling uploading files using dispute resolution layer needs time stamps

import "./fs.sol";

contract UploadManager {

Filesystem fs;

enum Storage {
IPFS,
BLOCKCHAIN
}

struct RequiredFile {
bytes32 name_hash;
Storage file_storage;
bytes32 file_id;
}

struct Spec {
RequiredFile[] reqs;
bool locked;
uint filled;
address owner;

bytes32 name;
bytes32 data;
}

mapping (bytes32 => Spec) uploads;

constructor(address f) public {
fs = Filesystem(f);
}

function init(bytes32 id, bytes32 name, bytes32 data) public {
Spec storage io = uploads[id];
require(io.owner == 0);
io.owner = msg.sender;
io.name = name;
io.data = data;
}

function requireFile(bytes32 id, bytes32 hash, Storage st) public {
Spec storage io = uploads[id];
require(!io.locked);
require(io.owner == msg.sender);
io.reqs.push(RequiredFile(hash, st, 0));
}

function enough(bytes32 id) public {
Spec storage io = uploads[id];
require(io.owner == 0 || io.owner == msg.sender);
io.locked = true;
}

function uploadFile(bytes32 id, uint num, bytes32 file_id, bytes32[] name_proof, bytes32[] data_proof, uint file_num) public returns (bool) {
Spec storage io = uploads[id];
RequiredFile storage file = io.reqs[num];
if (!checkProof(fs.getRoot(file_id), io.data, data_proof, file_num) || !checkProof(fs.getNameHash(file_id), io.name, name_proof, file_num)) return false;

file.file_id = file_id;
return true;
}

function checkProof(bytes32 hash, bytes32 root, bytes32[] proof, uint loc) public pure returns (bool) {
return uint(hash) == getLeaf(proof, loc) && root == getRoot(proof, loc);
}

function getLeaf(bytes32[] proof, uint loc) internal pure returns (uint) {
require(proof.length >= 2);
if (loc%2 == 0) return uint(proof[0]);
else return uint(proof[1]);
}

function getRoot(bytes32[] proof, uint loc) internal pure returns (bytes32) {
require(proof.length >= 2);
bytes32 res = keccak256(proof[0], proof[1]);
for (uint i = 2; i < proof.length; i++) {
loc = loc/2;
if (loc%2 == 0) res = keccak256(res, proof[i]);
else res = keccak256(proof[i], res);
}
require(loc < 2); // This should be runtime error, access over bounds
return res;
}

function fill(bytes32 id) public {
Spec storage io = uploads[id];
require(io.filled == 0);
io.filled = block.number;
for (uint i = 0; i < io.reqs.length; i++) require(io.reqs[i].file_id != 0);
}

function good(bytes32 id, uint when) public view returns (bool) {
Spec storage io = uploads[id];
return io.filled < when;
}

}

interface CheckTime {
function good(bytes32 id, uint when) external view returns (bool);
}

contract TimedDispute is BasicDispute {
CheckTime a;

constructor(address aa) public {
a = CheckTime(aa);
}

function status(bytes32 id) public view returns (Status) {
Task storage t = tasks[id];
if (a.good(t.spec, t.when)) return Status.SOLVER_WINS;
else return Status.VERIFIER_WINS;
}

}

Loading