Commit 3f1c9761 authored by George Hotz's avatar George Hotz

write challenge contract

parent 57f9bf9e
......@@ -24,7 +24,7 @@ PreimageOracle -- key value store
returns
$v0 = preimage[$t7...$t0] >> ($a0 * 32)
Program returns a hash in [$t7...$t0] and exits(jump to 0xDEADDEAD) with the hash in the state
Program returns a hash in [$t7...$t0] and exits(jump to 0xDEAD0000) with the hash in the state
Challenge Flow:
C is challenger, D is defender
......@@ -35,9 +35,8 @@ C: InitiateChallenge(uint blockNumberN, bytes blockHeaderN, bytes blockHeaderNp1
* checks hashes of the block headers
* saves inputs for input oracle
* confirms assertionHash != blockHeaderNp1.Hash
* expectCorrect = (assertionHash == blockHeaderNp1.Hash)
* confirm assertionProof[0..7] proves the final state of [$t7...$t0] in finalSystemHash is assertionHash
* confirm assertionProof[8] proves the final state of $pc in finalSystemHash is 0xDEADDEAD
* confirm assertionProof[8] proves the final state of $pc in finalSystemHash is 0xDEAD0000
* L = 0, R = stepCount # we agree at L=0, we disagree at R=stepCount
* return new challengeId
* assertedState[0] = GlobalStartSystemHash + inputOracleMutations
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.3;
import "./lib/Lib_RLPReader.sol";
interface IMIPS {
function Step(bytes32 stateHash) external view returns (bytes32);
function ReadMemory(bytes32 stateHash, uint32 addr) external view returns (uint32);
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) external pure returns (bytes32);
}
contract Challenge {
address payable immutable owner;
IMIPS immutable mips;
bytes32 immutable GlobalStartState;
struct Chal {
uint256 L;
......@@ -16,12 +21,12 @@ contract Challenge {
mapping(uint256 => bytes32) defendedState;
address payable challenger;
}
mapping(uint256 => Chal) challenges;
Chal[] challenges;
constructor(IMIPS imips) {
constructor(IMIPS imips, bytes32 globalStartState) {
owner = msg.sender;
mips = imips;
GlobalStartState = globalStartState;
}
// allow getting money
......@@ -32,17 +37,86 @@ contract Challenge {
owner.transfer(address(this).balance);
}
function InitiateChallenge(uint blockNumberN,
bytes calldata blockHeaderN, bytes calldata blockHeaderNp1,
bytes32 assertionHash, bytes32 finalSystemHash, uint256 stepCount) external {
// is this new?
Chal storage c = challenges[challenges.length];
// memory helpers
function writeBytes32(bytes32 stateHash, uint32 addr, bytes32 val) internal view returns (bytes32) {
for (uint32 i = 0; i < 32; i += 4) {
uint256 tv = uint256(val>>(224-(i*8)));
stateHash = mips.WriteMemory(stateHash, addr+i, uint32(tv));
}
return stateHash;
}
function readBytes32(bytes32 stateHash, uint32 addr) internal view returns (bytes32) {
uint256 ret = 0;
for (uint32 i = 0; i < 32; i += 4) {
ret <<= 32;
ret |= uint256(mips.ReadMemory(stateHash, addr+i));
}
return bytes32(ret);
}
// create challenge
uint256 lastChallengeId = 0;
function newChallengeTrusted(bytes32 startState, bytes32 finalSystemState, uint256 stepCount) internal returns (uint256) {
uint256 challengeId = lastChallengeId;
Chal storage c = challenges[challengeId];
lastChallengeId += 1;
// the challenger arrives
c.challenger = msg.sender;
// the state is set
c.assertedState[0] = startState;
c.defendedState[0] = startState;
c.assertedState[stepCount] = finalSystemState;
// init the binary search
c.L = 0;
c.R = stepCount;
// TODO: this is the function with the complexity
// find me later
return challengeId;
}
function InitiateChallenge(uint blockNumberN,
bytes calldata blockHeaderN, bytes calldata blockHeaderNp1,
bytes32 assertionRoot, bytes32 finalSystemState, uint256 stepCount) external returns (uint256) {
require(blockhash(blockNumberN) == keccak256(blockHeaderN), "start block hash wrong");
require(blockhash(blockNumberN+1) == keccak256(blockHeaderNp1), "end block hash wrong");
// decode the blocks
Lib_RLPReader.RLPItem[] memory blockN = Lib_RLPReader.readList(blockHeaderN);
Lib_RLPReader.RLPItem[] memory blockNp1 = Lib_RLPReader.readList(blockHeaderNp1);
bytes32 newroot = Lib_RLPReader.readBytes32(blockNp1[3]);
require(assertionRoot != newroot, "asserting that the real state is correct is not a challenge");
// input oracle info
bytes32 root = Lib_RLPReader.readBytes32(blockN[3]);
bytes32 txhash = Lib_RLPReader.readBytes32(blockNp1[4]);
address coinbase = Lib_RLPReader.readAddress(blockNp1[2]);
bytes32 uncles = Lib_RLPReader.readBytes32(blockNp1[1]);
// load starting info into the input oracle
// we both agree at the beginning
bytes32 startState = GlobalStartState;
startState = writeBytes32(startState, 0xD0000000, root);
startState = writeBytes32(startState, 0xD0000020, txhash);
startState = writeBytes32(startState, 0xD0000040, bytes32(uint256(coinbase)));
startState = writeBytes32(startState, 0xD0000060, uncles);
// confirm the finalSystemHash asserts the state you claim (in $t0-$t7) and the machine is stopped
// we disagree at the end
require(readBytes32(finalSystemState, 0xC0000020) == assertionRoot, "you are claiming a different state in machine");
require(mips.ReadMemory(finalSystemState, 0xC0000080) == 0xDEAD0000, "machine is not stopped in final state (PC == 0xDEAD0000)");
return newChallengeTrusted(startState, finalSystemState, stepCount);
}
// binary search
function getStepNumber(uint256 challengeId) view public returns (uint256) {
Chal storage c = challenges[challengeId];
return (c.L+c.R)/2;
......@@ -62,6 +136,7 @@ contract Challenge {
require(msg.sender == owner, "must be owner");
uint256 stepNumber = getStepNumber(challengeId);
require(c.assertedState[stepNumber] != bytes32(0), "challenger state not proposed");
require(c.defendedState[stepNumber] == bytes32(0), "state already proposed");
// technically, we don't have to save these states
// but if we want to prove us right and not just the attacker wrong, we do
......@@ -75,6 +150,8 @@ contract Challenge {
}
}
// final payout
function ConfirmStateTransition(uint256 challengeId) external {
Chal storage c = challenges[challengeId];
require(c.challenger == msg.sender, "must be challenger");
......
......@@ -24,10 +24,15 @@ contract MIPS {
state[stateHash][addr] = (1 << 32) | value;
}
uint32 constant REG_OFFSET = 0xc0000000;
uint32 constant REG_PC = REG_OFFSET + 21*4;
uint32 constant public REG_OFFSET = 0xc0000000;
uint32 constant public REG_PC = REG_OFFSET + 0x20*4;
function getState(bytes32 stateHash, uint32 addr) public view returns (uint32) {
function WriteMemory(bytes32 stateHash, uint32 addr, uint32 val) public pure returns (bytes32) {
// TODO: does the stateHash mutation
}
function ReadMemory(bytes32 stateHash, uint32 addr) public view returns (uint32) {
if (addr == REG_OFFSET) {
// zero register is always 0
return 0;
......@@ -38,13 +43,12 @@ contract MIPS {
return uint32(ret);
}
// compute the next state
// will revert if any input state is missing
function Step(bytes32 stateHash) public view returns (bytes32) {
// instruction fetch
uint32 pc = getState(stateHash, REG_PC);
uint32 insn = getState(stateHash, pc);
uint32 pc = ReadMemory(stateHash, REG_PC);
uint32 insn = ReadMemory(stateHash, pc);
uint32 opcode = insn >> 26; // 6-bits
// decode
......@@ -54,10 +58,10 @@ contract MIPS {
uint32 rt;
if (opcode != 2 && opcode != 3) { // j and jal have no register fetch
// R-type or I-type (stores rt)
rs = getState(stateHash, REG_OFFSET + ((insn >> 19) & 0x7C));
rs = ReadMemory(stateHash, REG_OFFSET + ((insn >> 19) & 0x7C));
if (opcode == 0) {
// R-type (stores rd)
rt = getState(stateHash, REG_OFFSET + ((insn >> 14) & 0x7C));
rt = ReadMemory(stateHash, REG_OFFSET + ((insn >> 14) & 0x7C));
}
}
......@@ -67,7 +71,7 @@ contract MIPS {
if (opcode >= 0x20) {
// M[R[rs]+SignExtImm]
uint32 SignExtImm = insn&0xFFFF | (insn&0x8000 != 0 ? 0xFFFF0000 : 0);
mem = getState(stateHash, (rs + SignExtImm) & 0xFFFFFFFC);
mem = ReadMemory(stateHash, (rs + SignExtImm) & 0xFFFFFFFC);
}
// execute
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment