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
d4051e33
Commit
d4051e33
authored
Aug 18, 2023
by
protolambda
Committed by
clabby
Sep 02, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
cannon,op-challenger,dispute-game: VM Status prefix byte in claim hash
parent
cf040a8c
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
388 additions
and
25 deletions
+388
-25
run.go
cannon/cmd/run.go
+2
-3
witness.go
cannon/cmd/witness.go
+1
-2
state.go
cannon/mipsevm/state.go
+40
-1
service.go
op-challenger/fault/service.go
+128
-0
service_test.go
op-challenger/fault/service_test.go
+113
-0
loader.go
op-challenger/game/fault/loader.go
+3
-4
provider.go
op-challenger/game/fault/trace/alphabet/provider.go
+23
-1
provider_test.go
op-challenger/game/fault/trace/alphabet/provider_test.go
+2
-3
provider.go
op-challenger/game/fault/trace/cannon/provider.go
+13
-5
provider_test.go
op-challenger/game/fault/trace/cannon/provider_test.go
+6
-5
types.go
op-challenger/game/fault/types/types.go
+10
-0
MIPS.sol
packages/contracts-bedrock/src/cannon/MIPS.sol
+14
-0
DisputeGameFactory.sol
...ages/contracts-bedrock/src/dispute/DisputeGameFactory.sol
+4
-0
DisputeErrors.sol
packages/contracts-bedrock/src/libraries/DisputeErrors.sol
+5
-0
DisputeGameFactory.t.sol
packages/contracts-bedrock/test/DisputeGameFactory.t.sol
+23
-0
FaultDisputeGame.t.sol
packages/contracts-bedrock/test/FaultDisputeGame.t.sol
+1
-1
No files found.
cannon/cmd/run.go
View file @
d4051e33
...
...
@@ -9,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
...
...
@@ -331,12 +330,12 @@ func Run(ctx *cli.Context) error {
}
if
proofAt
(
state
)
{
preStateHash
:=
crypto
.
Keccak256Hash
(
state
.
EncodeWitness
()
)
preStateHash
:=
state
.
EncodeWitness
()
.
StateHash
(
)
witness
,
err
:=
stepFn
(
true
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed at proof-gen step %d (PC: %08x): %w"
,
step
,
state
.
PC
,
err
)
}
postStateHash
:=
crypto
.
Keccak256Hash
(
state
.
EncodeWitness
()
)
postStateHash
:=
state
.
EncodeWitness
()
.
StateHash
(
)
proof
:=
&
Proof
{
Step
:
step
,
Pre
:
preStateHash
,
...
...
cannon/cmd/witness.go
View file @
d4051e33
...
...
@@ -5,7 +5,6 @@ import (
"os"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/urfave/cli/v2"
)
...
...
@@ -31,7 +30,7 @@ func Witness(ctx *cli.Context) error {
return
fmt
.
Errorf
(
"invalid input state (%v): %w"
,
input
,
err
)
}
witness
:=
state
.
EncodeWitness
()
h
:=
crypto
.
Keccak256Hash
(
witness
)
h
:=
witness
.
StateHash
(
)
if
output
!=
""
{
if
err
:=
os
.
WriteFile
(
output
,
witness
,
0755
);
err
!=
nil
{
return
fmt
.
Errorf
(
"writing output to %v: %w"
,
output
,
err
)
...
...
cannon/mipsevm/state.go
View file @
d4051e33
...
...
@@ -5,6 +5,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
type
State
struct
{
...
...
@@ -37,7 +38,11 @@ type State struct {
LastHint
hexutil
.
Bytes
`json:"lastHint,omitempty"`
}
func
(
s
*
State
)
EncodeWitness
()
[]
byte
{
func
(
s
*
State
)
VMStatus
()
uint8
{
return
vmStatus
(
s
.
Exited
,
s
.
ExitCode
)
}
func
(
s
*
State
)
EncodeWitness
()
StateWitness
{
out
:=
make
([]
byte
,
0
)
memRoot
:=
s
.
Memory
.
MerkleRoot
()
out
=
append
(
out
,
memRoot
[
:
]
...
)
...
...
@@ -60,3 +65,37 @@ func (s *State) EncodeWitness() []byte {
}
return
out
}
type
StateWitness
[]
byte
const
(
VMStatusValid
=
0
VMStatusInvalid
=
1
VMStatusPanic
=
2
VMStatusUnfinished
=
3
)
func
(
sw
StateWitness
)
StateHash
()
common
.
Hash
{
hash
:=
crypto
.
Keccak256Hash
(
sw
)
offset
:=
32
*
2
+
4
*
6
exited
:=
sw
[
offset
]
exitCode
:=
sw
[
offset
+
1
]
status
:=
vmStatus
(
exited
==
1
,
exitCode
)
hash
[
0
]
=
status
return
hash
}
func
vmStatus
(
exited
bool
,
exitCode
uint8
)
uint8
{
if
exited
{
switch
exitCode
{
case
0
:
return
VMStatusValid
case
1
:
return
VMStatusInvalid
default
:
return
VMStatusPanic
}
}
else
{
return
VMStatusUnfinished
}
}
op-challenger/fault/service.go
0 → 100644
View file @
d4051e33
package
fault
import
(
"context"
"fmt"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/alphabet"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
)
// Service provides a clean interface for the challenger to interact
// with the fault package.
type
Service
interface
{
// MonitorGame monitors the fault dispute game and attempts to progress it.
MonitorGame
(
context
.
Context
)
error
}
type
service
struct
{
agent
*
Agent
agreeWithProposedOutput
bool
caller
*
FaultCaller
logger
log
.
Logger
}
// NewService creates a new Service.
func
NewService
(
ctx
context
.
Context
,
logger
log
.
Logger
,
cfg
*
config
.
Config
)
(
*
service
,
error
)
{
txMgr
,
err
:=
txmgr
.
NewSimpleTxManager
(
"challenger"
,
logger
,
&
metrics
.
NoopTxMetrics
{},
cfg
.
TxMgrConfig
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create the transaction manager: %w"
,
err
)
}
client
,
err
:=
client
.
DialEthClientWithTimeout
(
client
.
DefaultDialTimeout
,
logger
,
cfg
.
L1EthRpc
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to dial L1: %w"
,
err
)
}
contract
,
err
:=
bindings
.
NewFaultDisputeGameCaller
(
cfg
.
GameAddress
,
client
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to bind the fault dispute game contract: %w"
,
err
)
}
loader
:=
NewLoader
(
contract
)
gameDepth
,
err
:=
loader
.
FetchGameDepth
(
ctx
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to fetch the game depth: %w"
,
err
)
}
gameDepth
=
uint64
(
gameDepth
)
var
trace
types
.
TraceProvider
var
updater
types
.
OracleUpdater
switch
cfg
.
TraceType
{
case
config
.
TraceTypeCannon
:
trace
,
err
=
cannon
.
NewTraceProvider
(
ctx
,
logger
,
cfg
,
client
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"create cannon trace provider: %w"
,
err
)
}
updater
,
err
=
cannon
.
NewOracleUpdater
(
ctx
,
logger
,
txMgr
,
cfg
.
GameAddress
,
client
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create the cannon updater: %w"
,
err
)
}
case
config
.
TraceTypeAlphabet
:
trace
=
alphabet
.
NewTraceProvider
(
cfg
.
AlphabetTrace
,
gameDepth
)
updater
=
alphabet
.
NewOracleUpdater
(
logger
)
default
:
return
nil
,
fmt
.
Errorf
(
"unsupported trace type: %v"
,
cfg
.
TraceType
)
}
return
newTypedService
(
ctx
,
logger
,
cfg
,
loader
,
gameDepth
,
client
,
trace
,
updater
,
txMgr
)
}
// newTypedService creates a new Service from a provided trace provider.
func
newTypedService
(
ctx
context
.
Context
,
logger
log
.
Logger
,
cfg
*
config
.
Config
,
loader
Loader
,
gameDepth
uint64
,
client
*
ethclient
.
Client
,
provider
types
.
TraceProvider
,
updater
types
.
OracleUpdater
,
txMgr
txmgr
.
TxManager
)
(
*
service
,
error
)
{
if
err
:=
ValidateAbsolutePrestate
(
ctx
,
provider
,
loader
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to validate absolute prestate: %w"
,
err
)
}
gameLogger
:=
logger
.
New
(
"game"
,
cfg
.
GameAddress
)
responder
,
err
:=
NewFaultResponder
(
gameLogger
,
txMgr
,
cfg
.
GameAddress
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create the responder: %w"
,
err
)
}
caller
,
err
:=
NewFaultCallerFromBindings
(
cfg
.
GameAddress
,
client
,
gameLogger
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to bind the fault contract: %w"
,
err
)
}
return
&
service
{
agent
:
NewAgent
(
loader
,
int
(
gameDepth
),
provider
,
responder
,
updater
,
cfg
.
AgreeWithProposedOutput
,
gameLogger
),
agreeWithProposedOutput
:
cfg
.
AgreeWithProposedOutput
,
caller
:
caller
,
logger
:
gameLogger
,
},
nil
}
// ValidateAbsolutePrestate validates the absolute prestate of the fault game.
func
ValidateAbsolutePrestate
(
ctx
context
.
Context
,
trace
types
.
TraceProvider
,
loader
Loader
)
error
{
providerPrestate
,
err
:=
trace
.
AbsolutePreState
(
ctx
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to get the trace provider's absolute prestate: %w"
,
err
)
}
providerPrestateHash
,
err
:=
trace
.
StateHash
(
ctx
,
providerPrestate
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to compute state-hash: %w"
,
err
)
}
onchainPrestateHash
,
err
:=
loader
.
FetchAbsolutePrestateHash
(
ctx
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to get the onchain absolute prestate: %w"
,
err
)
}
if
providerPrestateHash
!=
onchainPrestateHash
{
return
fmt
.
Errorf
(
"trace provider's absolute prestate does not match onchain absolute prestate"
)
}
return
nil
}
// MonitorGame monitors the fault dispute game and attempts to progress it.
func
(
s
*
service
)
MonitorGame
(
ctx
context
.
Context
)
error
{
return
MonitorGame
(
ctx
,
s
.
logger
,
s
.
agreeWithProposedOutput
,
s
.
agent
,
s
.
caller
)
}
op-challenger/fault/service_test.go
0 → 100644
View file @
d4051e33
package
fault
import
(
"context"
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
)
var
(
mockTraceProviderError
=
fmt
.
Errorf
(
"mock trace provider error"
)
mockLoaderError
=
fmt
.
Errorf
(
"mock loader error"
)
)
// TestValidateAbsolutePrestate tests that the absolute prestate is validated
// correctly by the service component.
func
TestValidateAbsolutePrestate
(
t
*
testing
.
T
)
{
t
.
Run
(
"ValidPrestates"
,
func
(
t
*
testing
.
T
)
{
prestate
:=
[]
byte
{
0x00
,
0x01
,
0x02
,
0x03
}
prestateHash
:=
mockStateHash
(
prestate
)
mockTraceProvider
:=
newMockTraceProvider
(
false
,
prestate
)
mockLoader
:=
newMockLoader
(
false
,
prestateHash
)
err
:=
ValidateAbsolutePrestate
(
context
.
Background
(),
mockTraceProvider
,
mockLoader
)
require
.
NoError
(
t
,
err
)
})
t
.
Run
(
"TraceProviderErrors"
,
func
(
t
*
testing
.
T
)
{
prestate
:=
[]
byte
{
0x00
,
0x01
,
0x02
,
0x03
}
mockTraceProvider
:=
newMockTraceProvider
(
true
,
prestate
)
mockLoader
:=
newMockLoader
(
false
,
mockStateHash
(
prestate
))
err
:=
ValidateAbsolutePrestate
(
context
.
Background
(),
mockTraceProvider
,
mockLoader
)
require
.
ErrorIs
(
t
,
err
,
mockTraceProviderError
)
})
t
.
Run
(
"LoaderErrors"
,
func
(
t
*
testing
.
T
)
{
prestate
:=
[]
byte
{
0x00
,
0x01
,
0x02
,
0x03
}
mockTraceProvider
:=
newMockTraceProvider
(
false
,
prestate
)
mockLoader
:=
newMockLoader
(
true
,
mockStateHash
(
prestate
))
err
:=
ValidateAbsolutePrestate
(
context
.
Background
(),
mockTraceProvider
,
mockLoader
)
require
.
ErrorIs
(
t
,
err
,
mockLoaderError
)
})
t
.
Run
(
"PrestateMismatch"
,
func
(
t
*
testing
.
T
)
{
mockTraceProvider
:=
newMockTraceProvider
(
false
,
[]
byte
{
0x00
,
0x01
,
0x02
,
0x03
})
mockLoader
:=
newMockLoader
(
false
,
common
.
Hash
{
0x00
})
err
:=
ValidateAbsolutePrestate
(
context
.
Background
(),
mockTraceProvider
,
mockLoader
)
require
.
Error
(
t
,
err
)
})
}
type
mockTraceProvider
struct
{
prestateErrors
bool
prestate
[]
byte
}
func
newMockTraceProvider
(
prestateErrors
bool
,
prestate
[]
byte
)
*
mockTraceProvider
{
return
&
mockTraceProvider
{
prestateErrors
:
prestateErrors
,
prestate
:
prestate
,
}
}
func
(
m
*
mockTraceProvider
)
Get
(
ctx
context
.
Context
,
i
uint64
)
(
common
.
Hash
,
error
)
{
panic
(
"not implemented"
)
}
func
(
m
*
mockTraceProvider
)
GetStepData
(
ctx
context
.
Context
,
i
uint64
)
(
prestate
[]
byte
,
proofData
[]
byte
,
preimageData
*
types
.
PreimageOracleData
,
err
error
)
{
panic
(
"not implemented"
)
}
func
(
m
*
mockTraceProvider
)
AbsolutePreState
(
ctx
context
.
Context
)
([]
byte
,
error
)
{
if
m
.
prestateErrors
{
return
nil
,
mockTraceProviderError
}
return
m
.
prestate
,
nil
}
func
mockStateHash
(
state
[]
byte
)
common
.
Hash
{
h
:=
crypto
.
Keccak256Hash
(
state
)
h
[
0
]
=
types
.
VMStatusValid
return
h
}
func
(
m
*
mockTraceProvider
)
StateHash
(
ctx
context
.
Context
,
state
[]
byte
)
(
common
.
Hash
,
error
)
{
return
mockStateHash
(
state
),
nil
}
type
mockLoader
struct
{
prestateError
bool
prestateHash
common
.
Hash
}
func
newMockLoader
(
prestateError
bool
,
prestateHash
common
.
Hash
)
*
mockLoader
{
return
&
mockLoader
{
prestateError
:
prestateError
,
prestateHash
:
prestateHash
,
}
}
func
(
m
*
mockLoader
)
FetchClaims
(
ctx
context
.
Context
)
([]
types
.
Claim
,
error
)
{
panic
(
"not implemented"
)
}
func
(
m
*
mockLoader
)
FetchGameDepth
(
ctx
context
.
Context
)
(
uint64
,
error
)
{
panic
(
"not implemented"
)
}
func
(
m
*
mockLoader
)
FetchAbsolutePrestateHash
(
ctx
context
.
Context
)
(
common
.
Hash
,
error
)
{
if
m
.
prestateError
{
return
common
.
Hash
{},
mockLoaderError
}
return
m
.
prestateHash
,
nil
}
op-challenger/game/fault/loader.go
View file @
d4051e33
...
...
@@ -138,16 +138,15 @@ func (l *loader) FetchClaims(ctx context.Context) ([]types.Claim, error) {
}
// FetchAbsolutePrestateHash fetches the hashed absolute prestate from the fault dispute game.
func
(
l
*
loader
)
FetchAbsolutePrestateHash
(
ctx
context
.
Context
)
(
[]
byte
,
error
)
{
func
(
l
*
loader
)
FetchAbsolutePrestateHash
(
ctx
context
.
Context
)
(
common
.
Hash
,
error
)
{
callOpts
:=
bind
.
CallOpts
{
Context
:
ctx
,
}
absolutePrestate
,
err
:=
l
.
caller
.
ABSOLUTEPRESTATE
(
&
callOpts
)
if
err
!=
nil
{
return
nil
,
err
return
common
.
Hash
{}
,
err
}
returnValue
:=
absolutePrestate
[
:
]
return
returnValu
e
,
nil
return
absolutePrestat
e
,
nil
}
op-challenger/game/fault/trace/alphabet/provider.go
View file @
d4051e33
...
...
@@ -9,6 +9,8 @@ import (
"github.com/ethereum-optimism/optimism/op-challenger/game/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
)
var
(
...
...
@@ -58,7 +60,7 @@ func (ap *AlphabetTraceProvider) Get(ctx context.Context, i uint64) (common.Hash
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
return
crypto
.
Keccak256
Hash
(
claimBytes
),
nil
return
alphabetState
Hash
(
claimBytes
),
nil
}
// AbsolutePreState returns the absolute pre-state for the alphabet trace.
...
...
@@ -66,11 +68,31 @@ func (ap *AlphabetTraceProvider) AbsolutePreState(ctx context.Context) ([]byte,
return
common
.
Hex2Bytes
(
"0000000000000000000000000000000000000000000000000000000000000060"
),
nil
}
func
(
ap
*
AlphabetTraceProvider
)
StateHash
(
ctx
context
.
Context
,
state
[]
byte
)
(
common
.
Hash
,
error
)
{
return
alphabetStateHash
(
state
),
nil
}
// BuildAlphabetPreimage constructs the claim bytes for the index and state item.
func
BuildAlphabetPreimage
(
i
uint64
,
letter
string
)
[]
byte
{
return
append
(
IndexToBytes
(
i
),
LetterToBytes
(
letter
)
...
)
}
const
maxAlphabet
=
26
func
alphabetStateHash
(
state
[]
byte
)
common
.
Hash
{
h
:=
crypto
.
Keccak256Hash
(
state
)
// instead of the state containing an "exited" boolean, we just check if the index reached the end
i
:=
new
(
big
.
Int
)
.
SetBytes
(
state
[
:
32
])
if
!
i
.
IsUint64
()
||
i
.
Uint64
()
>
maxAlphabet
{
h
[
0
]
=
types
.
VMStatusPanic
// this state should never be reached, if we increment by 1 per step
}
else
if
i
.
Uint64
()
==
maxAlphabet
{
h
[
0
]
=
types
.
VMStatusValid
}
else
{
h
[
0
]
=
types
.
VMStatusUnfinished
}
return
h
}
// IndexToBytes converts an index to a byte slice big endian
func
IndexToBytes
(
i
uint64
)
[]
byte
{
big
:=
new
(
big
.
Int
)
...
...
op-challenger/game/fault/trace/alphabet/provider_test.go
View file @
d4051e33
...
...
@@ -6,12 +6,11 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
)
func
alphabetClaim
(
index
uint64
,
letter
string
)
common
.
Hash
{
return
crypto
.
Keccak256
Hash
(
BuildAlphabetPreimage
(
index
,
letter
))
return
alphabetState
Hash
(
BuildAlphabetPreimage
(
index
,
letter
))
}
// TestAlphabetProvider_Get_ClaimsByTraceIndex tests the [fault.AlphabetProvider] Get function.
...
...
@@ -60,7 +59,7 @@ func FuzzIndexToBytes(f *testing.F) {
// returns the correct pre-image for a index.
func
TestGetStepData_Succeeds
(
t
*
testing
.
T
)
{
ap
:=
NewTraceProvider
(
"abc"
,
2
)
expected
:=
BuildAlphabetPreimage
(
0
,
"a
'
"
)
expected
:=
BuildAlphabetPreimage
(
0
,
"a"
)
retrieved
,
proof
,
data
,
err
:=
ap
.
GetStepData
(
context
.
Background
(),
uint64
(
1
))
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
,
retrieved
)
...
...
op-challenger/game/fault/trace/cannon/provider.go
View file @
d4051e33
...
...
@@ -15,9 +15,13 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/cannon/mipsevm"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
)
const
(
...
...
@@ -25,7 +29,7 @@ const (
)
type
proofData
struct
{
ClaimValue
hexutil
.
Bytes
`json:"post"`
ClaimValue
common
.
Hash
`json:"post"`
StateData
hexutil
.
Bytes
`json:"state-data"`
ProofData
hexutil
.
Bytes
`json:"proof-data"`
OracleKey
hexutil
.
Bytes
`json:"oracle-key,omitempty"`
...
...
@@ -86,7 +90,7 @@ func (p *CannonTraceProvider) Get(ctx context.Context, i uint64) (common.Hash, e
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
value
:=
common
.
BytesToHash
(
proof
.
ClaimValue
)
value
:=
proof
.
ClaimValue
if
value
==
(
common
.
Hash
{})
{
return
common
.
Hash
{},
errors
.
New
(
"proof missing post hash"
)
...
...
@@ -152,8 +156,8 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofDa
// No execution is done, so no proof-data or oracle values are required.
witness
:=
state
.
EncodeWitness
()
proof
:=
&
proofData
{
ClaimValue
:
crypto
.
Keccak256
(
witness
),
StateData
:
witness
,
ClaimValue
:
witness
.
StateHash
(
),
StateData
:
hexutil
.
Bytes
(
witness
)
,
ProofData
:
[]
byte
{},
OracleKey
:
nil
,
OracleValue
:
nil
,
...
...
@@ -177,3 +181,7 @@ func (p *CannonTraceProvider) loadProof(ctx context.Context, i uint64) (*proofDa
}
return
&
proof
,
nil
}
func
(
p
*
CannonTraceProvider
)
StateHash
(
ctx
context
.
Context
,
state
[]
byte
)
(
common
.
Hash
,
error
)
{
return
mipsevm
.
StateWitness
(
state
)
.
StateHash
(),
nil
}
op-challenger/game/fault/trace/cannon/provider_test.go
View file @
d4051e33
...
...
@@ -15,7 +15,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/ioutil"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
...
...
@@ -43,7 +42,9 @@ func TestGet(t *testing.T) {
value
,
err
:=
provider
.
Get
(
context
.
Background
(),
7000
)
require
.
NoError
(
t
,
err
)
require
.
Contains
(
t
,
generator
.
generated
,
7000
,
"should have tried to generate the proof"
)
require
.
Equal
(
t
,
crypto
.
Keccak256Hash
(
generator
.
finalState
.
EncodeWitness
()),
value
)
stateHash
,
err
:=
provider
.
StateHash
(
context
.
Background
(),
generator
.
finalState
.
EncodeWitness
())
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
stateHash
,
value
)
})
t
.
Run
(
"MissingPostHash"
,
func
(
t
*
testing
.
T
)
{
...
...
@@ -86,7 +87,7 @@ func TestGetStepData(t *testing.T) {
Exited
:
true
,
}
generator
.
proof
=
&
proofData
{
ClaimValue
:
common
.
Hash
{
0xaa
}
.
Bytes
()
,
ClaimValue
:
common
.
Hash
{
0xaa
},
StateData
:
[]
byte
{
0xbb
},
ProofData
:
[]
byte
{
0xcc
},
OracleKey
:
common
.
Hash
{
0xdd
}
.
Bytes
(),
...
...
@@ -111,7 +112,7 @@ func TestGetStepData(t *testing.T) {
Exited
:
true
,
}
generator
.
proof
=
&
proofData
{
ClaimValue
:
common
.
Hash
{
0xaa
}
.
Bytes
()
,
ClaimValue
:
common
.
Hash
{
0xaa
},
StateData
:
[]
byte
{
0xbb
},
ProofData
:
[]
byte
{
0xcc
},
OracleKey
:
common
.
Hash
{
0xdd
}
.
Bytes
(),
...
...
@@ -185,7 +186,7 @@ func TestAbsolutePreState(t *testing.T) {
Step
:
0
,
Registers
:
[
32
]
uint32
{},
}
require
.
Equal
(
t
,
state
.
EncodeWitness
(
),
preState
)
require
.
Equal
(
t
,
[]
byte
(
state
.
EncodeWitness
()
),
preState
)
})
}
...
...
op-challenger/game/fault/types/types.go
View file @
d4051e33
...
...
@@ -105,8 +105,18 @@ type TraceProvider interface {
// AbsolutePreState is the pre-image value of the trace that transitions to the trace value at index 0
AbsolutePreState
(
ctx
context
.
Context
)
(
preimage
[]
byte
,
err
error
)
// StateHash computes the state-hash of the given state, or returns an error if the state is invalid.
StateHash
(
ctx
context
.
Context
,
state
[]
byte
)
(
common
.
Hash
,
error
)
}
const
(
VMStatusValid
=
0
VMStatusInvalid
=
1
VMStatusPanic
=
2
VMStatusUnfinished
=
3
)
// ClaimData is the core of a claim. It must be unique inside a specific game.
type
ClaimData
struct
{
Value
common
.
Hash
...
...
packages/contracts-bedrock/src/cannon/MIPS.sol
View file @
d4051e33
...
...
@@ -103,7 +103,9 @@ contract MIPS {
from, to := copyMem(from, to, 4) // lo
from, to := copyMem(from, to, 4) // hi
from, to := copyMem(from, to, 4) // heap
let exitCode := shr(248, mload(from))
from, to := copyMem(from, to, 1) // exitCode
let exited := shr(248, mload(from))
from, to := copyMem(from, to, 1) // exited
from, to := copyMem(from, to, 8) // step
from := add(from, 32) // offset to registers
...
...
@@ -117,8 +119,20 @@ contract MIPS {
// Log the resulting MIPS state, for debugging
log0(start, sub(to, start))
function vmStatus(_exited, _exitCode) -> status_ {
switch _exited
case 1 {
switch _exitCode
case 0 { status_ := 0 } // VMStatusValid
case 1 { status_ := 1 } // VMStatusInvalid
default { status_ := 2 } // VMStatusPanic
} default { status_ := 3 } // VMStatusUnfinished
}
let status := vmStatus(exited, exitCode)
// Compute the hash of the resulting MIPS state
out_ := keccak256(start, sub(to, start))
out_ := or(shl(248, status), and(not(shl(248, 0xff)), out_))
}
}
...
...
packages/contracts-bedrock/src/dispute/DisputeGameFactory.sol
View file @
d4051e33
...
...
@@ -91,6 +91,10 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
// If there is no implementation to clone for the given `GameType`, revert.
if (address(impl) == address(0)) revert NoImplementation(_gameType);
// The VMStatus must indicate (1) 'invalid', to argue that disputed thing is invalid.
// Games that agree with the existing outcome are not allowed.
if (uint8(Claim.unwrap(_rootClaim)[0]) != 1) revert UnexpectedRootClaim(_rootClaim);
// Clone the implementation contract and initialize it with the given parameters.
proxy_ = IDisputeGame(address(impl).clone(abi.encodePacked(_rootClaim, _extraData)));
proxy_.initialize();
...
...
packages/contracts-bedrock/src/libraries/DisputeErrors.sol
View file @
d4051e33
...
...
@@ -15,6 +15,11 @@ error NoImplementation(GameType gameType);
/// @param uuid The UUID of the dispute game that already exists.
error GameAlreadyExists(Hash uuid);
/// @notice Thrown when the root claim has an unexpected VM status.
/// Some games can only start with a root-claim with a specific status.
/// @param rootClaim is the claim that was unexpected.
error UnexpectedRootClaim(Claim rootClaim);
////////////////////////////////////////////////////////////////
// `FaultDisputeGame` Errors //
////////////////////////////////////////////////////////////////
...
...
packages/contracts-bedrock/test/DisputeGameFactory.t.sol
View file @
d4051e33
...
...
@@ -36,11 +36,19 @@ contract DisputeGameFactory_Init is L2OutputOracle_Initializer {
}
contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
function changeClaimStatus(Claim claim, uint8 status) public pure returns (Claim _out) {
bytes32 hash = Claim.unwrap(claim);
hash = bytes32((uint256(hash) & (~(uint256(0xff) << 248))) | (uint256(status) << 248));
return Claim.wrap(hash);
}
/// @dev Tests that the `create` function succeeds when creating a new dispute game
/// with a `GameType` that has an implementation set.
function testFuzz_create_succeeds(uint8 gameType, Claim rootClaim, bytes calldata extraData) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Ensure the rootClaim has a VMStatus that disagrees with the validity.
rootClaim = changeClaimStatus(rootClaim, 1);
// Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) {
...
...
@@ -68,15 +76,30 @@ contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
function testFuzz_create_noImpl_reverts(uint8 gameType, Claim rootClaim, bytes calldata extraData) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Ensure the rootClaim has a VMStatus that disagrees with the validity.
rootClaim = changeClaimStatus(rootClaim, 1);
vm.expectRevert(abi.encodeWithSelector(NoImplementation.selector, gt));
factory.create(gt, rootClaim, extraData);
}
/// @dev Tests that the `create` function reverts when the rootClaim does not disagree with the outcome.
function testFuzz_create_badRootStatus_reverts(uint8 gameType, Claim rootClaim, bytes calldata extraData) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Ensure the root claim does not have the correct VM status
if (uint8(Claim.unwrap(rootClaim)[0]) == 1) rootClaim = changeClaimStatus(rootClaim, 0);
vm.expectRevert(abi.encodeWithSelector(UnexpectedRootClaim.selector, rootClaim));
factory.create(gt, rootClaim, extraData);
}
/// @dev Tests that the `create` function reverts when there exists a dispute game with the same UUID.
function testFuzz_create_sameUUID_reverts(uint8 gameType, Claim rootClaim, bytes calldata extraData) public {
// Ensure that the `gameType` is within the bounds of the `GameType` enum's possible values.
GameType gt = GameType.wrap(uint8(bound(gameType, 0, 2)));
// Ensure the rootClaim has a VMStatus that disagrees with the validity.
rootClaim = changeClaimStatus(rootClaim, 1);
// Set all three implementations to the same `FakeClone` contract.
for (uint8 i; i < 3; i++) {
...
...
packages/contracts-bedrock/test/FaultDisputeGame.t.sol
View file @
d4051e33
...
...
@@ -77,7 +77,7 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
contract FaultDisputeGame_Test is FaultDisputeGame_Init {
/// @dev The root claim of the game.
Claim internal constant ROOT_CLAIM = Claim.wrap(bytes32(uint256(10)));
Claim internal constant ROOT_CLAIM = Claim.wrap(bytes32(
(uint256(1) << 248) +
uint256(10)));
/// @dev The absolute prestate of the trace.
Claim internal constant ABSOLUTE_PRESTATE = Claim.wrap(bytes32(uint256(0)));
...
...
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