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