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;
// find me later
return challengeId;
}
// TODO: this is the function with the complexity
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
......
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
/**
* @title Lib_RLPReader
* @dev Adapted from "RLPReader" by Hamdi Allam (hamdi.allam97@gmail.com).
*/
library Lib_RLPReader {
/*************
* Constants *
*************/
uint256 constant internal MAX_LIST_LENGTH = 32;
/*********
* Enums *
*********/
enum RLPItemType {
DATA_ITEM,
LIST_ITEM
}
/***********
* Structs *
***********/
struct RLPItem {
uint256 length;
uint256 ptr;
}
/**********************
* Internal Functions *
**********************/
/**
* Converts bytes to a reference to memory position and length.
* @param _in Input bytes to convert.
* @return Output memory reference.
*/
function toRLPItem(
bytes memory _in
)
internal
pure
returns (
RLPItem memory
)
{
uint256 ptr;
assembly {
ptr := add(_in, 32)
}
return RLPItem({
length: _in.length,
ptr: ptr
});
}
/**
* Reads an RLP list value into a list of RLP items.
* @param _in RLP list value.
* @return Decoded RLP list items.
*/
function readList(
RLPItem memory _in
)
internal
pure
returns (
RLPItem[] memory
)
{
(
uint256 listOffset,
,
RLPItemType itemType
) = _decodeLength(_in);
require(
itemType == RLPItemType.LIST_ITEM,
"Invalid RLP list value."
);
// Solidity in-memory arrays can't be increased in size, but *can* be decreased in size by
// writing to the length. Since we can't know the number of RLP items without looping over
// the entire input, we'd have to loop twice to accurately size this array. It's easier to
// simply set a reasonable maximum list length and decrease the size before we finish.
RLPItem[] memory out = new RLPItem[](MAX_LIST_LENGTH);
uint256 itemCount = 0;
uint256 offset = listOffset;
while (offset < _in.length) {
require(
itemCount < MAX_LIST_LENGTH,
"Provided RLP list exceeds max list length."
);
(
uint256 itemOffset,
uint256 itemLength,
) = _decodeLength(RLPItem({
length: _in.length - offset,
ptr: _in.ptr + offset
}));
out[itemCount] = RLPItem({
length: itemLength + itemOffset,
ptr: _in.ptr + offset
});
itemCount += 1;
offset += itemOffset + itemLength;
}
// Decrease the array size to match the actual item count.
assembly {
mstore(out, itemCount)
}
return out;
}
/**
* Reads an RLP list value into a list of RLP items.
* @param _in RLP list value.
* @return Decoded RLP list items.
*/
function readList(
bytes memory _in
)
internal
pure
returns (
RLPItem[] memory
)
{
return readList(
toRLPItem(_in)
);
}
/**
* Reads an RLP bytes value into bytes.
* @param _in RLP bytes value.
* @return Decoded bytes.
*/
function readBytes(
RLPItem memory _in
)
internal
pure
returns (
bytes memory
)
{
(
uint256 itemOffset,
uint256 itemLength,
RLPItemType itemType
) = _decodeLength(_in);
require(
itemType == RLPItemType.DATA_ITEM,
"Invalid RLP bytes value."
);
return _copy(_in.ptr, itemOffset, itemLength);
}
/**
* Reads an RLP bytes value into bytes.
* @param _in RLP bytes value.
* @return Decoded bytes.
*/
function readBytes(
bytes memory _in
)
internal
pure
returns (
bytes memory
)
{
return readBytes(
toRLPItem(_in)
);
}
/**
* Reads an RLP string value into a string.
* @param _in RLP string value.
* @return Decoded string.
*/
function readString(
RLPItem memory _in
)
internal
pure
returns (
string memory
)
{
return string(readBytes(_in));
}
/**
* Reads an RLP string value into a string.
* @param _in RLP string value.
* @return Decoded string.
*/
function readString(
bytes memory _in
)
internal
pure
returns (
string memory
)
{
return readString(
toRLPItem(_in)
);
}
/**
* Reads an RLP bytes32 value into a bytes32.
* @param _in RLP bytes32 value.
* @return Decoded bytes32.
*/
function readBytes32(
RLPItem memory _in
)
internal
pure
returns (
bytes32
)
{
require(
_in.length <= 33,
"Invalid RLP bytes32 value."
);
(
uint256 itemOffset,
uint256 itemLength,
RLPItemType itemType
) = _decodeLength(_in);
require(
itemType == RLPItemType.DATA_ITEM,
"Invalid RLP bytes32 value."
);
uint256 ptr = _in.ptr + itemOffset;
bytes32 out;
assembly {
out := mload(ptr)
// Shift the bytes over to match the item size.
if lt(itemLength, 32) {
out := div(out, exp(256, sub(32, itemLength)))
}
}
return out;
}
/**
* Reads an RLP bytes32 value into a bytes32.
* @param _in RLP bytes32 value.
* @return Decoded bytes32.
*/
function readBytes32(
bytes memory _in
)
internal
pure
returns (
bytes32
)
{
return readBytes32(
toRLPItem(_in)
);
}
/**
* Reads an RLP uint256 value into a uint256.
* @param _in RLP uint256 value.
* @return Decoded uint256.
*/
function readUint256(
RLPItem memory _in
)
internal
pure
returns (
uint256
)
{
return uint256(readBytes32(_in));
}
/**
* Reads an RLP uint256 value into a uint256.
* @param _in RLP uint256 value.
* @return Decoded uint256.
*/
function readUint256(
bytes memory _in
)
internal
pure
returns (
uint256
)
{
return readUint256(
toRLPItem(_in)
);
}
/**
* Reads an RLP bool value into a bool.
* @param _in RLP bool value.
* @return Decoded bool.
*/
function readBool(
RLPItem memory _in
)
internal
pure
returns (
bool
)
{
require(
_in.length == 1,
"Invalid RLP boolean value."
);
uint256 ptr = _in.ptr;
uint256 out;
assembly {
out := byte(0, mload(ptr))
}
return out != 0;
}
/**
* Reads an RLP bool value into a bool.
* @param _in RLP bool value.
* @return Decoded bool.
*/
function readBool(
bytes memory _in
)
internal
pure
returns (
bool
)
{
return readBool(
toRLPItem(_in)
);
}
/**
* Reads an RLP address value into a address.
* @param _in RLP address value.
* @return Decoded address.
*/
function readAddress(
RLPItem memory _in
)
internal
pure
returns (
address
)
{
if (_in.length == 1) {
return address(0);
}
require(
_in.length == 21,
"Invalid RLP address value."
);
return address(readUint256(_in));
}
/**
* Reads an RLP address value into a address.
* @param _in RLP address value.
* @return Decoded address.
*/
function readAddress(
bytes memory _in
)
internal
pure
returns (
address
)
{
return readAddress(
toRLPItem(_in)
);
}
/**
* Reads the raw bytes of an RLP item.
* @param _in RLP item to read.
* @return Raw RLP bytes.
*/
function readRawBytes(
RLPItem memory _in
)
internal
pure
returns (
bytes memory
)
{
return _copy(_in);
}
/*********************
* Private Functions *
*********************/
/**
* Decodes the length of an RLP item.
* @param _in RLP item to decode.
* @return Offset of the encoded data.
* @return Length of the encoded data.
* @return RLP item type (LIST_ITEM or DATA_ITEM).
*/
function _decodeLength(
RLPItem memory _in
)
private
pure
returns (
uint256,
uint256,
RLPItemType
)
{
require(
_in.length > 0,
"RLP item cannot be null."
);
uint256 ptr = _in.ptr;
uint256 prefix;
assembly {
prefix := byte(0, mload(ptr))
}
if (prefix <= 0x7f) {
// Single byte.
return (0, 1, RLPItemType.DATA_ITEM);
} else if (prefix <= 0xb7) {
// Short string.
uint256 strLen = prefix - 0x80;
require(
_in.length > strLen,
"Invalid RLP short string."
);
return (1, strLen, RLPItemType.DATA_ITEM);
} else if (prefix <= 0xbf) {
// Long string.
uint256 lenOfStrLen = prefix - 0xb7;
require(
_in.length > lenOfStrLen,
"Invalid RLP long string length."
);
uint256 strLen;
assembly {
// Pick out the string length.
strLen := div(
mload(add(ptr, 1)),
exp(256, sub(32, lenOfStrLen))
)
}
require(
_in.length > lenOfStrLen + strLen,
"Invalid RLP long string."
);
return (1 + lenOfStrLen, strLen, RLPItemType.DATA_ITEM);
} else if (prefix <= 0xf7) {
// Short list.
uint256 listLen = prefix - 0xc0;
require(
_in.length > listLen,
"Invalid RLP short list."
);
return (1, listLen, RLPItemType.LIST_ITEM);
} else {
// Long list.
uint256 lenOfListLen = prefix - 0xf7;
require(
_in.length > lenOfListLen,
"Invalid RLP long list length."
);
uint256 listLen;
assembly {
// Pick out the list length.
listLen := div(
mload(add(ptr, 1)),
exp(256, sub(32, lenOfListLen))
)
}
require(
_in.length > lenOfListLen + listLen,
"Invalid RLP long list."
);
return (1 + lenOfListLen, listLen, RLPItemType.LIST_ITEM);
}
}
/**
* Copies the bytes from a memory location.
* @param _src Pointer to the location to read from.
* @param _offset Offset to start reading from.
* @param _length Number of bytes to read.
* @return Copied bytes.
*/
function _copy(
uint256 _src,
uint256 _offset,
uint256 _length
)
private
pure
returns (
bytes memory
)
{
bytes memory out = new bytes(_length);
if (out.length == 0) {
return out;
}
uint256 src = _src + _offset;
uint256 dest;
assembly {
dest := add(out, 32)
}
// Copy over as many complete words as we can.
for (uint256 i = 0; i < _length / 32; i++) {
assembly {
mstore(dest, mload(src))
}
src += 32;
dest += 32;
}
// Pick out the remaining bytes.
uint256 mask = 256 ** (32 - (_length % 32)) - 1;
assembly {
mstore(
dest,
or(
and(mload(src), not(mask)),
and(mload(dest), mask)
)
)
}
return out;
}
/**
* Copies an RLP item into bytes.
* @param _in RLP item to copy.
* @return Copied bytes.
*/
function _copy(
RLPItem memory _in
)
private
pure
returns (
bytes memory
)
{
return _copy(_in.ptr, 0, _in.length);
}
}
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