Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
03413842
Commit
03413842
authored
Nov 09, 2021
by
George Hotz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
big progress on challenge
parent
b800b38f
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
156 additions
and
51 deletions
+156
-51
test_challenge.yml
.github/workflows/test_challenge.yml
+1
-1
README.md
README.md
+19
-12
Challenge.sol
contracts/Challenge.sol
+30
-12
hardhat.config.js
hardhat.config.js
+1
-1
main.go
mipsevm/main.go
+10
-3
assert.js
scripts/assert.js
+53
-0
challenge.js
scripts/challenge.js
+5
-5
lib.js
scripts/lib.js
+13
-1
respond.js
scripts/respond.js
+24
-16
No files found.
.github/workflows/test_challenge.yml
View file @
03413842
...
...
@@ -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
README.md
View file @
03413842
...
...
@@ -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
...
...
contracts/Challenge.sol
View file @
03413842
...
...
@@ -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);
...
...
hardhat.config.js
View file @
03413842
...
...
@@ -15,7 +15,7 @@ try {
module
.
exports
=
{
defaultNetwork
:
"
cheapeth
"
,
defaultNetwork
:
"
hosthat
"
,
networks
:
{
hosthat
:
{
url
:
"
http://localhost:8545/
"
,
...
...
mipsevm/main.go
View file @
03413842
...
...
@@ -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
)
}
...
...
scripts/assert.js
0 → 100644
View file @
03413842
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
);
});
scripts/challenge.js
View file @
03413842
...
...
@@ -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
),
tru
e
])
const
blockNp1
=
await
network
.
provider
.
send
(
"
eth_getBlockByNumber
"
,
[
"
0x
"
+
(
blockNumberN
+
1
).
toString
(
16
),
fals
e
])
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
=
"
0x
b135cb00efbc2341905eafc034eca0dcec40b039a1b28860bf7c309c872e5644
"
let
finalTrie
=
JSON
.
parse
(
fs
.
readFileSync
(
"
/tmp/cannon/0_1
171896
/checkpoint_final.json
"
))
//
we are submitting the (wrong) transition for block 10 as the attacker
const
assertionRoot
=
"
0x
03f930c087b70f3385db68fe6bf128719e2d9a4b0a133e53b32db2fa25d345fd
"
let
finalTrie
=
JSON
.
parse
(
fs
.
readFileSync
(
"
/tmp/cannon/0_1
0
/checkpoint_final.json
"
))
let
preimages
=
Object
.
assign
({},
startTrie
[
'
preimages
'
],
finalTrie
[
'
preimages
'
]);
const
finalSystemState
=
finalTrie
[
'
root
'
]
...
...
scripts/lib.js
View file @
03413842
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
}
scripts/respond.js
View file @
03413842
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
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment