Commit 03413842 authored by George Hotz's avatar George Hotz

big progress on challenge

parent b800b38f
......@@ -34,6 +34,6 @@ jobs:
cd mipsevm
go build
- name: Generate checkpoints for 13284469
run: cd mipsevm && ./mipsevm 13284469
run: mipsevm/mipsevm 13284469
- name: Run test challenge
run: npx hardhat test test/challenge_test.js
......@@ -56,22 +56,29 @@ process done with hash 0x5c45998dfbf9ce70bcbb80574ed7a622922d2c775e0a2331fe5a8b8
## Workflow
```
# testing on cheapeth
# only works for pre fork blocks
minigeth/go-ethereum 1171895
(cd mipsevm && ./mipsevm 1171895)
npx hardhat node --fork https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161
# testing on hardhat (forked mainnet)
rm -rf /tmp/cannon/*
mipsevm/mipsevm
npx hardhat run scripts/deploy.js
minigeth/go-ethereum 1171895 && mipsevm/mipsevm 1171895
minigeth/go-ethereum 10 && mipsevm/mipsevm 10
BLOCK=1171895 npx hardhat run scripts/challenge.js
# "attacker" is block 1171896
minigeth/go-ethereum 1171896
for i in {1..23}
do
ID=0 BLOCK=10 CHALLENGER=1 npx hardhat run scripts/respond.js
ID=0 BLOCK=1171895 npx hardhat run scripts/respond.js
done
# assert as challenger (fails)
ID=0 BLOCK=10 CHALLENGER=1 npx hardhat run scripts/assert.js
# assert as defender (passes)
ID=0 BLOCK=1171895 npx hardhat run scripts/assert.js
ID=0 npx hardhat run scripts/respond.js
(cd mipsevm && ./mipsevm 1171896 11226379)
ID=0 BLOCK=1171896 PROPOSE=1 npx hardhat run scripts/respond.js
# "defender" is real block 1171896
(cd mipsevm && ./mipsevm 1171895 11226379)
ID=0 BLOCK=1171895 RESPOND=1 npx hardhat run scripts/respond.js
```
## State Oracle API
......
......@@ -143,6 +143,12 @@ contract Challenge {
// binary search
function isSearching(uint256 challengeId) view public returns (bool) {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
return c.L + 1 != c.R;
}
function getStepNumber(uint256 challengeId) view public returns (uint256) {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
......@@ -160,6 +166,7 @@ contract Challenge {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(c.challenger == msg.sender, "must be challenger");
require(isSearching(challengeId), "must be searching");
uint256 stepNumber = getStepNumber(challengeId);
require(c.assertedState[stepNumber] == bytes32(0), "state already proposed");
......@@ -170,6 +177,7 @@ contract Challenge {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(owner == msg.sender, "must be owner");
require(isSearching(challengeId), "must be searching");
uint256 stepNumber = getStepNumber(challengeId);
require(c.assertedState[stepNumber] != bytes32(0), "challenger state not proposed");
......@@ -187,17 +195,22 @@ contract Challenge {
}
// final payout
// anyone can call these, right?
event ChallengerWins(uint256 challengeId);
event ChallengerLoses(uint256 challengeId);
event ChallengerLosesByDefault(uint256 challengeId);
function ConfirmStateTransition(uint256 challengeId) external {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(c.challenger == msg.sender, "must be challenger");
require(c.L + 1 == c.R, "binary search not finished");
//require(c.challenger == msg.sender, "must be challenger");
require(!isSearching(challengeId), "binary search not finished");
bytes32 stepState = mips.Step(c.assertedState[c.L]);
require(mips.Step(c.assertedState[c.L]) == c.assertedState[c.R], "wrong asserted state");
console.logBytes32(stepState);
console.logBytes32(c.assertedState[c.R]);
require(stepState == c.assertedState[c.R], "wrong asserted state for challenger");
// pay out bounty!!
c.challenger.transfer(address(this).balance);
......@@ -205,18 +218,23 @@ contract Challenge {
emit ChallengerWins(challengeId);
}
function DenyStateTransition(uint256 challengeId, bytes32 finalRiscState) external {
function DenyStateTransition(uint256 challengeId) external {
Chal storage c = challenges[challengeId];
require(c.challenger != address(0), "invalid challenge");
require(owner == msg.sender, "must be owner");
require(c.L + 1 == c.R, "binary search not finished");
require(finalRiscState != c.assertedState[c.R], "you can't agree with the challenger");
//require(owner == msg.sender, "must be owner");
require(!isSearching(challengeId), "binary search not finished");
bytes32 stepState = mips.Step(c.defendedState[c.L]);
// NOTE: challenger can make c.defendedState[c.R] 0 if the search always went right
// while the challenger can't win, you can't make them lose
if (c.defendedState[c.R] == bytes32(0)) {
emit ChallengerLosesByDefault(challengeId);
return;
}
// it's 0 if you agree with all attacker states except the final one
// in which case, you get a free pass to submit now
require(c.defendedState[c.R] == finalRiscState || c.defendedState[c.R] == bytes32(0), "must be consistent with state");
require(mips.Step(c.defendedState[c.L]) == finalRiscState, "wrong asserted state");
console.logBytes32(stepState);
console.logBytes32(c.defendedState[c.R]);
require(stepState == c.defendedState[c.R], "wrong asserted state for defender");
// consider the challenger mocked
emit ChallengerLoses(challengeId);
......
......@@ -15,7 +15,7 @@ try {
module.exports = {
defaultNetwork: "cheapeth",
defaultNetwork: "hosthat",
networks: {
hosthat: {
url: "http://localhost:8545/",
......
......@@ -37,7 +37,7 @@ func main() {
lastStep := 1
if evm {
ZeroRegisters(ram)
LoadMappedFile("../mipigo/minigeth.bin", ram, 0)
LoadMappedFile("mipigo/minigeth.bin", ram, 0)
WriteCheckpoint(ram, "/tmp/cannon/golden.json", -1)
LoadMappedFile(fmt.Sprintf("%s/input", root), ram, 0x30000000)
RunWithRam(ram, target-1, 0, root, nil)
......@@ -62,9 +62,9 @@ func main() {
ZeroRegisters(ram)
// not ready for golden yet
LoadMappedFileUnicorn(mu, "../mipigo/minigeth.bin", ram, 0)
WriteCheckpoint(ram, "/tmp/cannon/golden.json", -1)
LoadMappedFileUnicorn(mu, "mipigo/minigeth.bin", ram, 0)
if root == "" {
WriteCheckpoint(ram, "/tmp/cannon/golden.json", -1)
fmt.Println("exiting early without a block number")
os.Exit(0)
}
......@@ -72,6 +72,13 @@ func main() {
// TODO: this is actually step 0->1. Renumber as appropriate
LoadMappedFileUnicorn(mu, fmt.Sprintf("%s/input", root), ram, 0x30000000)
if target == 0 {
// no actual running at step 0
fn := fmt.Sprintf("%s/checkpoint_%d.json", root, target)
WriteCheckpoint(ram, fn, target)
return
}
mu.Start(0, 0x5ead0004)
SyncRegs(mu, ram)
}
......
const { deployed, getTrieNodesForCall, getTrieAtStep } = require("../scripts/lib")
async function main() {
let [c, m, mm] = await deployed()
const challengeId = parseInt(process.env.ID)
const blockNumberN = parseInt(process.env.BLOCK)
const isChallenger = process.env.CHALLENGER == "1"
let step = (await c.getStepNumber(challengeId)).toNumber()
console.log("searching step", step, "at block", blockNumberN)
if (await c.isSearching(challengeId)) {
console.log("search is NOT done")
return
}
let cdat
if (isChallenger) {
// challenger declare victory
cdat = c.interface.encodeFunctionData("ConfirmStateTransition", [challengeId])
} else {
// defender declare victory
// note: not always possible
cdat = c.interface.encodeFunctionData("DenyStateTransition", [challengeId])
}
let startTrie = getTrieAtStep(blockNumberN, step)
let finalTrie = getTrieAtStep(blockNumberN, step+1)
let preimages = Object.assign({}, startTrie['preimages'], finalTrie['preimages']);
let nodes = await getTrieNodesForCall(c, cdat, preimages)
for (n of nodes) {
await mm.AddTrieNode(n)
}
let ret
if (isChallenger) {
ret = await c.ConfirmStateTransition(challengeId)
} else {
ret = await c.DenyStateTransition(challengeId)
}
let receipt = await ret.wait()
console.log(receipt)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
......@@ -6,11 +6,11 @@ async function main() {
const blockNumberN = parseInt(process.env.BLOCK)
if (isNaN(blockNumberN)) {
throw "usage: challenge.js <block number>"
throw "usage: BLOCK=<number> npx hardhat run challenge.js"
}
console.log("challenging block number", blockNumberN)
// sadly this doesn't work on hosthat
const blockNp1 = await network.provider.send("eth_getBlockByNumber", ["0x"+(blockNumberN+1).toString(16), true])
const blockNp1 = await network.provider.send("eth_getBlockByNumber", ["0x"+(blockNumberN+1).toString(16), false])
console.log(blockNp1)
const blockNp1Rlp = getBlockRlp(blockNp1)
......@@ -21,9 +21,9 @@ async function main() {
/*const assertionRoot = "0x1111111111111111111111111111111111111111111111111111111111111111"
let finalTrie = JSON.parse(fs.readFileSync("/tmp/cannon/0_"+blockNumberN.toString()+"/checkpoint_final.json"))*/
// fake for testing (it's the next block)
const assertionRoot = "0xb135cb00efbc2341905eafc034eca0dcec40b039a1b28860bf7c309c872e5644"
let finalTrie = JSON.parse(fs.readFileSync("/tmp/cannon/0_1171896/checkpoint_final.json"))
// we are submitting the (wrong) transition for block 10 as the attacker
const assertionRoot = "0x03f930c087b70f3385db68fe6bf128719e2d9a4b0a133e53b32db2fa25d345fd"
let finalTrie = JSON.parse(fs.readFileSync("/tmp/cannon/0_10/checkpoint_final.json"))
let preimages = Object.assign({}, startTrie['preimages'], finalTrie['preimages']);
const finalSystemState = finalTrie['root']
......
const fs = require("fs")
const rlp = require('rlp')
const child_process = require("child_process")
async function deploy() {
const MIPS = await ethers.getContractFactory("MIPS")
......@@ -91,4 +92,15 @@ async function getTrieNodesForCall(c, cdat, preimages) {
return nodes
}
module.exports = { deploy, deployed, getTrieNodesForCall, getBlockRlp }
function getTrieAtStep(blockNumberN, step) {
const fn = "/tmp/cannon/0_"+blockNumberN.toString()+"/checkpoint_"+step.toString()+".json"
if (!fs.existsSync(fn)) {
console.log("running mipsevm")
child_process.execSync("mipsevm/mipsevm "+blockNumberN.toString()+" "+step.toString(), {stdio: 'inherit'})
}
return JSON.parse(fs.readFileSync(fn))
}
module.exports = { deploy, deployed, getTrieNodesForCall, getBlockRlp, getTrieAtStep }
const fs = require("fs")
const { deployed, getTrieNodesForCall } = require("../scripts/lib")
const { deployed, getTrieNodesForCall, getTrieAtStep } = require("../scripts/lib")
async function main() {
let [c, m, mm] = await deployed()
const challengeId = parseInt(process.env.ID)
const blockNumberN = parseInt(process.env.BLOCK)
const isChallenger = process.env.CHALLENGER == "1"
let step = (await c.getStepNumber(challengeId)).toNumber()
console.log("searching step", step)
console.log("searching step", step, "at block", blockNumberN)
// see if it's proposed or not
//await c.getProposedState(challengeId)
if (!(await c.isSearching(challengeId))) {
console.log("search is done")
return
}
const blockNumberN = parseInt(process.env.BLOCK)
let thisTrie = JSON.parse(fs.readFileSync("/tmp/cannon/0_"+blockNumberN.toString()+"/checkpoint_"+step.toString()+".json"))
// see if it's proposed or not
const proposed = await c.getProposedState(challengeId)
const isProposing = proposed == "0x0000000000000000000000000000000000000000000000000000000000000000"
if (isProposing != isChallenger) {
console.log("bad challenger state")
return
}
console.log("isProposing", isProposing)
let thisTrie = getTrieAtStep(blockNumberN, step)
const root = thisTrie['root']
console.log("new root", root)
if (process.env.PROPOSE == "1") {
let ret = await c.ProposeState(challengeId, root)
let receipt = await ret.wait()
console.log(receipt)
}
if (process.env.RESPOND == "1") {
let ret = await c.RespondState(challengeId, root)
let receipt = await ret.wait()
console.log(receipt)
let ret
if (isProposing) {
ret = await c.ProposeState(challengeId, root)
} else {
ret = await c.RespondState(challengeId, root)
}
let receipt = await ret.wait()
console.log("done", receipt.blockNumber)
}
main()
......
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