Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 9 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,23 @@
<img src="./Dispute Resolution Layer.jpg"/>
</p>

## Intent
Codebase to provide ways for different incentive layers and computation layers to interface with a general purpose verification game.

The goal of this repository is to create a discussion around defining a dispute resolution layer protocol that is general enough to work with Truebit General (WASM) and Truebit Lite (scrypt-interactive) mechanisms. Other possibilities may be different types of verification games (Proof of Steak).

The idea is that different types of incentive layers can plug in as well as different computation layers. Benefits of this approach are upgradeability and flexibility. In case there are bugs in the incentive layer or dispute resolution layer we could swap them out. The other benefit is flexibility to allow other projects to build with the Truebit system.

The initial idea for this is that there currently exist two different implemented dispute resolution layers.

Scrypt-Interactive (Doge-Eth Bridge):
https://github.com/TrueBitFoundation/scrypt-interactive/blob/master/contracts/Verifier.sol

webasm-solidity (General Truebit):
https://github.com/TrueBitFoundation/webasm-solidity/blob/master/contracts/interactive2.sol
Go [here](https://github.com/TrueBitFoundation/Developer-Resources/blob/master/docs/DisputeResolutionLayer.md) for the Dispute Resolution Layer Overview.

## Installation

Ensure you have truffle and an Ethereum test net like ganache installed
```
npm install -g truffle ganache-cli
```

## Test
Then install the package's dependencies
```
npm install
```

## Testing

After following the above installation instructions you can start up the dev blockchain in a separate tab:
```
Expand Down
81 changes: 51 additions & 30 deletions contracts/BasicVerificationGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ contract BasicVerificationGame {
game.lastParticipantTime = block.number;

game.lowStep = 0;
game.lowHash = keccak256(0);//initial state hash
bytes32[3] memory initialState = [bytes32(0), bytes32(0), bytes32(0)];
game.lowHash = game.vm.merklizeState(initialState);
game.medStep = 0;
game.medHash = bytes32(0);
game.highStep = numSteps;
Expand All @@ -59,6 +60,17 @@ contract BasicVerificationGame {
NewGame(gameId, solver, verifier);
}

function status(bytes32 gameId) public view returns (uint8) {
return uint8(games[gameId].state);
}

function gameData(bytes32 gameId) public view returns (uint low, uint med, uint high) {
VerificationGame storage game = games[gameId];
low = game.lowStep;
med = game.medStep;
high = game.highStep;
}

function query(bytes32 gameId, uint stepNumber) public {
VerificationGame storage game = games[gameId];

Expand Down Expand Up @@ -148,18 +160,45 @@ contract BasicVerificationGame {
}
}

function merklizeProof(bytes32[] proof) public returns (bytes32 merkleRoot) {
for (uint i = 0; i < proof.length; i++) {
if (i == 0)
merkleRoot = proof[0];
else
merkleRoot = keccak256(merkleRoot, proof[i]);
//https://github.com/ameensol/merkle-tree-solidity/blob/master/src/MerkleProof.sol
function checkProofOrdered(bytes proof, bytes32 root, bytes32 hash, uint256 index) public pure returns (bool) {
// use the index to determine the node ordering
// index ranges 1 to n

bytes32 el;
bytes32 h = hash;
uint256 remaining;

for (uint256 j = 32; j <= proof.length; j += 32) {
assembly {
el := mload(add(proof, j))
}

// calculate remaining elements in proof
remaining = (proof.length - j + 32) / 32;

// we don't assume that the tree is padded to a power of 2
// if the index is odd then the proof will start with a hash at a higher
// layer, so we have to adjust the index to be the index at that layer
while (remaining > 0 && index % 2 == 1 && index > 2 ** remaining) {
index = uint(index) / 2 + 1;
}

if (index % 2 == 0) {
h = keccak256(el, h);
index = index / 2;
} else {
h = keccak256(h, el);
index = uint(index) / 2 + 1;
}
}

return h == root;
}


//Should probably replace preValue and postValue with preInstruction and postInstruction
function performStepVerification(bytes32 gameId, bytes32[4] preStepState, bytes32[4] postStepState, bytes32[] proof) public {
function performStepVerification(bytes32 gameId, bytes32[3] lowStepState, bytes32[3] highStepState, bytes proof) public {
VerificationGame storage game = games[gameId];

require(game.state == State.Unresolved);
Expand All @@ -168,20 +207,13 @@ contract BasicVerificationGame {
require(game.lowStep + 1 == game.highStep);
// ^ must be at the end of the binary search according to the smart contract

require(game.vm.merklizeState(preStepState) == game.lowHash);
require(game.vm.merklizeState(postStepState) == game.highHash);
require(game.vm.merklizeState(lowStepState) == game.lowHash);
require(game.vm.merklizeState(highStepState) == game.highHash);

//require that the next instruction be included in the program merkle root
require(game.programMerkleRoot == merklizeProof(proof));
require(checkProofOrdered(proof, game.programMerkleRoot, keccak256(highStepState[0]), game.highStep));

uint currentStep = uint(preStepState[3]);

bytes32[4] memory newState;
if (currentStep % 2 == 0) {
newState = game.vm.runStep(preStepState, proof[0]);//pass in next instruction
} else {
newState = game.vm.runStep(preStepState, proof[1]);
}
bytes32[3] memory newState = game.vm.runStep(lowStepState, game.highStep, highStepState[0]);

if (game.vm.merklizeState(newState) == game.highHash) {
game.state = State.SolverWon;
Expand All @@ -190,15 +222,4 @@ contract BasicVerificationGame {
}
//FinalData(stepOutput, keccak256(stepOutput));
}

function status(bytes32 gameId) public view returns (uint8) {
return uint8(games[gameId].state);
}

function gameData(bytes32 gameId) public view returns (bytes32 lowHash, bytes32 medHash, bytes32 highHash) {
VerificationGame storage game = games[gameId];
lowHash = game.lowHash;
medHash = game.medHash;
highHash = game.highHash;
}
}
4 changes: 2 additions & 2 deletions contracts/IComputationLayer.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity ^0.4.18;

interface IComputationLayer {
function runStep(bytes32[4] currentState, bytes32 nextInput) public pure returns (bytes32[4] newState);
function merklizeState(bytes32[4] state) public pure returns (bytes32 merkleRoot);
function runStep(bytes32[3] currentState, uint stepNumber, bytes32 nextInput) public pure returns (bytes32[3] newState);
function merklizeState(bytes32[3] state) public pure returns (bytes32 merkleRoot);
}
1 change: 1 addition & 0 deletions contracts/IDisputeResolutionLayer.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pragma solidity ^0.4.18;

//TODO: Should maybe enforce checkProofOrdered
interface IDisputeResolutionLayer {
function status(bytes32 id) public returns (uint8); //returns State enum
}
34 changes: 19 additions & 15 deletions contracts/test/SimpleAdderVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@ contract SimpleAdderVM {
//Reg1: Stack1 Accum
//Reg2: Stack2 Result
//Reg3: StepCounter
function runStep(bytes32[4] currentState, bytes32 nextInput) public pure returns (bytes32[4] newState) {
function runStep(bytes32[3] currentState, uint stepNumber, bytes32 nextInput) public pure returns (bytes32[3] newState) {

newState[0] = nextInput;//Copy input
newState[1] = currentState[2];//Copy last result as new accum
require(currentState[2] == bytes32(stepNumber-1));

//Use Stack0 and Stack1 registers to compute result
bytes32 sum = bytes32(uint(newState[0]) + uint(currentState[1]));//Add input by the previous state's accum
newState[2] = sum;//Store result in Stack2 register

newState[3] = bytes32(uint(currentState[3]) + 1);//Increment step counter
if (stepNumber == 0) {
require(currentState[0] == 0x0 && currentState[1] == 0x0 && currentState[2] == 0x0);
return currentState;
} else {
newState[0] = nextInput;
newState[1] = bytes32(uint(currentState[1]) + uint(nextInput));
newState[2] = bytes32(stepNumber);
}
}

//Simple list merklization (works like sum)
function merklizeState(bytes32[4] state) public pure returns (bytes32 merkleRoot) {
function merklizeState(bytes32[3] state) public pure returns (bytes32 merkleRoot) {
for (uint i = 0; i < state.length; i++) {
if (i == 0) {
merkleRoot = state[0];
Expand All @@ -33,16 +35,18 @@ contract SimpleAdderVM {

//Used for generating results for query/response
//Run offchain
function runSteps(uint[] program, uint numSteps) public pure returns (bytes32[4] state, bytes32 stateHash) {
function runSteps(bytes32[] program, uint numSteps) public pure returns (bytes32[3] state, bytes32 stateHash) {
uint i = 0;

while (i < program.length && i <= numSteps-1) {
bytes32 nextInstruction = bytes32(program[uint(state[3])]);
state = runStep(state, nextInstruction);
i+=1;
while (i < program.length && i <= numSteps) {
if (i > 0) {
bytes32 nextInstruction = program[i-1];
state = runStep(state, i, nextInstruction);
}
i += 1;
}

stateHash = merklizeState(state);
}

}
Loading