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
e0bb0187
Unverified
Commit
e0bb0187
authored
Jul 24, 2023
by
refcell.eth
Committed by
GitHub
Jul 24, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into aj/cannon-executor
parents
8d7584ee
d653ea62
Changes
26
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
277 additions
and
233 deletions
+277
-233
main.go
op-challenger/cmd/main.go
+1
-1
main_test.go
op-challenger/cmd/main_test.go
+4
-5
config.go
op-challenger/config/config.go
+38
-39
config_test.go
op-challenger/config/config_test.go
+7
-8
abi_test.go
op-challenger/fault/abi_test.go
+2
-1
agent.go
op-challenger/fault/agent.go
+16
-6
alphabet_provider.go
op-challenger/fault/alphabet_provider.go
+3
-2
alphabet_provider_test.go
op-challenger/fault/alphabet_provider_test.go
+3
-2
caller.go
op-challenger/fault/caller.go
+7
-6
caller_test.go
op-challenger/fault/caller_test.go
+3
-2
loader.go
op-challenger/fault/loader.go
+12
-11
loader_test.go
op-challenger/fault/loader_test.go
+14
-13
monitor.go
op-challenger/fault/monitor.go
+5
-4
monitor_test.go
op-challenger/fault/monitor_test.go
+8
-7
responder.go
op-challenger/fault/responder.go
+5
-4
responder_test.go
op-challenger/fault/responder_test.go
+21
-20
service.go
op-challenger/fault/service.go
+4
-4
solver.go
op-challenger/fault/solver.go
+16
-15
solver_test.go
op-challenger/fault/solver_test.go
+64
-27
game.go
op-challenger/fault/types/game.go
+2
-2
game_test.go
op-challenger/fault/types/game_test.go
+1
-1
position.go
op-challenger/fault/types/position.go
+1
-1
position_test.go
op-challenger/fault/types/position_test.go
+1
-1
types.go
op-challenger/fault/types/types.go
+1
-11
flags.go
op-challenger/flags/flags.go
+35
-37
helper.go
op-e2e/e2eutils/disputegame/helper.go
+3
-3
No files found.
op-challenger/cmd/main.go
View file @
e0bb0187
...
@@ -60,7 +60,7 @@ func run(args []string, action ConfigAction) error {
...
@@ -60,7 +60,7 @@ func run(args []string, action ConfigAction) error {
}
}
logger
.
Info
(
"Starting op-challenger"
,
"version"
,
VersionWithMeta
)
logger
.
Info
(
"Starting op-challenger"
,
"version"
,
VersionWithMeta
)
cfg
,
err
:=
config
.
NewConfigFromCLI
(
ctx
)
cfg
,
err
:=
flags
.
NewConfigFromCLI
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
...
op-challenger/cmd/main_test.go
View file @
e0bb0187
...
@@ -6,7 +6,6 @@ import (
...
@@ -6,7 +6,6 @@ import (
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
...
@@ -39,12 +38,12 @@ func TestLogLevel(t *testing.T) {
...
@@ -39,12 +38,12 @@ func TestLogLevel(t *testing.T) {
func
TestDefaultCLIOptionsMatchDefaultConfig
(
t
*
testing
.
T
)
{
func
TestDefaultCLIOptionsMatchDefaultConfig
(
t
*
testing
.
T
)
{
cfg
:=
configForArgs
(
t
,
addRequiredArgs
())
cfg
:=
configForArgs
(
t
,
addRequiredArgs
())
defaultCfg
:=
config
.
NewConfig
(
l1EthRpc
,
common
.
HexToAddress
(
gameAddressValue
),
flags
.
TraceTypeAlphabet
,
alphabetTrace
,
cannonDatadir
,
true
,
4
)
defaultCfg
:=
config
.
NewConfig
(
l1EthRpc
,
common
.
HexToAddress
(
gameAddressValue
),
config
.
TraceTypeAlphabet
,
alphabetTrace
,
cannonDatadir
,
true
,
4
)
require
.
Equal
(
t
,
defaultCfg
,
cfg
)
require
.
Equal
(
t
,
defaultCfg
,
cfg
)
}
}
func
TestDefaultConfigIsValid
(
t
*
testing
.
T
)
{
func
TestDefaultConfigIsValid
(
t
*
testing
.
T
)
{
cfg
:=
config
.
NewConfig
(
l1EthRpc
,
common
.
HexToAddress
(
gameAddressValue
),
flags
.
TraceTypeAlphabet
,
alphabetTrace
,
cannonDatadir
,
true
,
4
)
cfg
:=
config
.
NewConfig
(
l1EthRpc
,
common
.
HexToAddress
(
gameAddressValue
),
config
.
TraceTypeAlphabet
,
alphabetTrace
,
cannonDatadir
,
true
,
4
)
require
.
NoError
(
t
,
cfg
.
Check
())
require
.
NoError
(
t
,
cfg
.
Check
())
}
}
...
@@ -66,7 +65,7 @@ func TestTraceType(t *testing.T) {
...
@@ -66,7 +65,7 @@ func TestTraceType(t *testing.T) {
verifyArgsInvalid
(
t
,
"flag trace-type is required"
,
addRequiredArgsExcept
(
"--trace-type"
))
verifyArgsInvalid
(
t
,
"flag trace-type is required"
,
addRequiredArgsExcept
(
"--trace-type"
))
})
})
for
_
,
traceType
:=
range
flags
.
TraceTypes
{
for
_
,
traceType
:=
range
config
.
TraceTypes
{
traceType
:=
traceType
traceType
:=
traceType
t
.
Run
(
"Valid_"
+
traceType
.
String
(),
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"Valid_"
+
traceType
.
String
(),
func
(
t
*
testing
.
T
)
{
cfg
:=
configForArgs
(
t
,
addRequiredArgsExcept
(
"--trace-type"
,
"--trace-type"
,
traceType
.
String
()))
cfg
:=
configForArgs
(
t
,
addRequiredArgsExcept
(
"--trace-type"
,
"--trace-type"
,
traceType
.
String
()))
...
@@ -172,7 +171,7 @@ func requiredArgs() map[string]string {
...
@@ -172,7 +171,7 @@ func requiredArgs() map[string]string {
"--agree-with-proposed-output"
:
agreeWithProposedOutput
,
"--agree-with-proposed-output"
:
agreeWithProposedOutput
,
"--l1-eth-rpc"
:
l1EthRpc
,
"--l1-eth-rpc"
:
l1EthRpc
,
"--game-address"
:
gameAddressValue
,
"--game-address"
:
gameAddressValue
,
"--trace-type"
:
flags
.
TraceTypeAlphabet
.
String
()
,
"--trace-type"
:
"alphabet"
,
"--alphabet"
:
alphabetTrace
,
"--alphabet"
:
alphabetTrace
,
"--cannon-datadir"
:
cannonDatadir
,
"--cannon-datadir"
:
cannonDatadir
,
}
}
...
...
op-challenger/config/config.go
View file @
e0bb0187
...
@@ -2,14 +2,10 @@ package config
...
@@ -2,14 +2,10 @@ package config
import
(
import
(
"errors"
"errors"
"
strings
"
"
fmt
"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
opservice
"github.com/ethereum-optimism/optimism/op-service"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
)
)
var
(
var
(
...
@@ -20,6 +16,36 @@ var (
...
@@ -20,6 +16,36 @@ var (
ErrMissingGameAddress
=
errors
.
New
(
"missing game address"
)
ErrMissingGameAddress
=
errors
.
New
(
"missing game address"
)
)
)
type
TraceType
string
const
(
TraceTypeAlphabet
TraceType
=
"alphabet"
TraceTypeCannon
TraceType
=
"cannon"
)
var
TraceTypes
=
[]
TraceType
{
TraceTypeAlphabet
,
TraceTypeCannon
}
func
(
t
TraceType
)
String
()
string
{
return
string
(
t
)
}
func
(
t
*
TraceType
)
Set
(
value
string
)
error
{
if
!
ValidTraceType
(
TraceType
(
value
))
{
return
fmt
.
Errorf
(
"unknown trace type: %q"
,
value
)
}
*
t
=
TraceType
(
value
)
return
nil
}
func
ValidTraceType
(
value
TraceType
)
bool
{
for
_
,
t
:=
range
TraceTypes
{
if
t
==
value
{
return
true
}
}
return
false
}
// Config is a well typed config that is parsed from the CLI params.
// Config is a well typed config that is parsed from the CLI params.
// This also contains config options for auxiliary services.
// This also contains config options for auxiliary services.
// It is used to initialize the challenger.
// It is used to initialize the challenger.
...
@@ -29,9 +55,9 @@ type Config struct {
...
@@ -29,9 +55,9 @@ type Config struct {
AgreeWithProposedOutput
bool
// Temporary config if we agree or disagree with the posted output
AgreeWithProposedOutput
bool
// Temporary config if we agree or disagree with the posted output
GameDepth
int
// Depth of the game tree
GameDepth
int
// Depth of the game tree
TraceType
flags
.
TraceType
// Type of trace
TraceType
TraceType
// Type of trace
AlphabetTrace
string
// String for the AlphabetTraceProvider
AlphabetTrace
string
// String for the AlphabetTraceProvider
CannonDatadir
string
// Cannon Data Directory for the CannonTraceProvider
CannonDatadir
string
// Cannon Data Directory for the CannonTraceProvider
TxMgrConfig
txmgr
.
CLIConfig
TxMgrConfig
txmgr
.
CLIConfig
}
}
...
@@ -39,7 +65,7 @@ type Config struct {
...
@@ -39,7 +65,7 @@ type Config struct {
func
NewConfig
(
func
NewConfig
(
l1EthRpc
string
,
l1EthRpc
string
,
gameAddress
common
.
Address
,
gameAddress
common
.
Address
,
traceType
flags
.
TraceType
,
traceType
TraceType
,
alphabetTrace
string
,
alphabetTrace
string
,
cannonDatadir
string
,
cannonDatadir
string
,
agreeWithProposedOutput
bool
,
agreeWithProposedOutput
bool
,
...
@@ -70,10 +96,10 @@ func (c Config) Check() error {
...
@@ -70,10 +96,10 @@ func (c Config) Check() error {
if
c
.
TraceType
==
""
{
if
c
.
TraceType
==
""
{
return
ErrMissingTraceType
return
ErrMissingTraceType
}
}
if
c
.
TraceType
==
flags
.
TraceTypeCannon
&&
c
.
CannonDatadir
==
""
{
if
c
.
TraceType
==
TraceTypeCannon
&&
c
.
CannonDatadir
==
""
{
return
ErrMissingCannonDatadir
return
ErrMissingCannonDatadir
}
}
if
c
.
TraceType
==
flags
.
TraceTypeAlphabet
&&
c
.
AlphabetTrace
==
""
{
if
c
.
TraceType
==
TraceTypeAlphabet
&&
c
.
AlphabetTrace
==
""
{
return
ErrMissingAlphabetTrace
return
ErrMissingAlphabetTrace
}
}
if
err
:=
c
.
TxMgrConfig
.
Check
();
err
!=
nil
{
if
err
:=
c
.
TxMgrConfig
.
Check
();
err
!=
nil
{
...
@@ -81,30 +107,3 @@ func (c Config) Check() error {
...
@@ -81,30 +107,3 @@ func (c Config) Check() error {
}
}
return
nil
return
nil
}
}
// NewConfigFromCLI parses the Config from the provided flags or environment variables.
func
NewConfigFromCLI
(
ctx
*
cli
.
Context
)
(
*
Config
,
error
)
{
if
err
:=
flags
.
CheckRequired
(
ctx
);
err
!=
nil
{
return
nil
,
err
}
dgfAddress
,
err
:=
opservice
.
ParseAddress
(
ctx
.
String
(
flags
.
DGFAddressFlag
.
Name
))
if
err
!=
nil
{
return
nil
,
err
}
txMgrConfig
:=
txmgr
.
ReadCLIConfig
(
ctx
)
traceTypeFlag
:=
flags
.
TraceType
(
strings
.
ToLower
(
ctx
.
String
(
flags
.
TraceTypeFlag
.
Name
)))
return
&
Config
{
// Required Flags
L1EthRpc
:
ctx
.
String
(
flags
.
L1EthRpcFlag
.
Name
),
TraceType
:
traceTypeFlag
,
GameAddress
:
dgfAddress
,
AlphabetTrace
:
ctx
.
String
(
flags
.
AlphabetFlag
.
Name
),
CannonDatadir
:
ctx
.
String
(
flags
.
CannonDatadirFlag
.
Name
),
AgreeWithProposedOutput
:
ctx
.
Bool
(
flags
.
AgreeWithProposedOutputFlag
.
Name
),
GameDepth
:
ctx
.
Int
(
flags
.
GameDepthFlag
.
Name
),
TxMgrConfig
:
txMgrConfig
,
},
nil
}
op-challenger/config/config_test.go
View file @
e0bb0187
...
@@ -3,7 +3,6 @@ package config
...
@@ -3,7 +3,6 @@ package config
import
(
import
(
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -18,27 +17,27 @@ var (
...
@@ -18,27 +17,27 @@ var (
gameDepth
=
4
gameDepth
=
4
)
)
func
validConfig
(
traceType
flags
.
TraceType
)
Config
{
func
validConfig
(
traceType
TraceType
)
Config
{
cfg
:=
NewConfig
(
validL1EthRpc
,
validGameAddress
,
traceType
,
validAlphabetTrace
,
validCannonDatadir
,
agreeWithProposedOutput
,
gameDepth
)
cfg
:=
NewConfig
(
validL1EthRpc
,
validGameAddress
,
traceType
,
validAlphabetTrace
,
validCannonDatadir
,
agreeWithProposedOutput
,
gameDepth
)
return
cfg
return
cfg
}
}
// TestValidConfigIsValid checks that the config provided by validConfig is actually valid
// TestValidConfigIsValid checks that the config provided by validConfig is actually valid
func
TestValidConfigIsValid
(
t
*
testing
.
T
)
{
func
TestValidConfigIsValid
(
t
*
testing
.
T
)
{
err
:=
validConfig
(
flags
.
TraceTypeCannon
)
.
Check
()
err
:=
validConfig
(
TraceTypeCannon
)
.
Check
()
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
}
}
func
TestTxMgrConfig
(
t
*
testing
.
T
)
{
func
TestTxMgrConfig
(
t
*
testing
.
T
)
{
t
.
Run
(
"Invalid"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"Invalid"
,
func
(
t
*
testing
.
T
)
{
config
:=
validConfig
(
flags
.
TraceTypeCannon
)
config
:=
validConfig
(
TraceTypeCannon
)
config
.
TxMgrConfig
=
txmgr
.
CLIConfig
{}
config
.
TxMgrConfig
=
txmgr
.
CLIConfig
{}
require
.
Equal
(
t
,
config
.
Check
()
.
Error
(),
"must provide a L1 RPC url"
)
require
.
Equal
(
t
,
config
.
Check
()
.
Error
(),
"must provide a L1 RPC url"
)
})
})
}
}
func
TestL1EthRpcRequired
(
t
*
testing
.
T
)
{
func
TestL1EthRpcRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
(
flags
.
TraceTypeCannon
)
config
:=
validConfig
(
TraceTypeCannon
)
config
.
L1EthRpc
=
""
config
.
L1EthRpc
=
""
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingL1EthRPC
)
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingL1EthRPC
)
config
.
L1EthRpc
=
validL1EthRpc
config
.
L1EthRpc
=
validL1EthRpc
...
@@ -46,7 +45,7 @@ func TestL1EthRpcRequired(t *testing.T) {
...
@@ -46,7 +45,7 @@ func TestL1EthRpcRequired(t *testing.T) {
}
}
func
TestGameAddressRequired
(
t
*
testing
.
T
)
{
func
TestGameAddressRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
(
flags
.
TraceTypeCannon
)
config
:=
validConfig
(
TraceTypeCannon
)
config
.
GameAddress
=
common
.
Address
{}
config
.
GameAddress
=
common
.
Address
{}
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingGameAddress
)
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingGameAddress
)
config
.
GameAddress
=
validGameAddress
config
.
GameAddress
=
validGameAddress
...
@@ -54,7 +53,7 @@ func TestGameAddressRequired(t *testing.T) {
...
@@ -54,7 +53,7 @@ func TestGameAddressRequired(t *testing.T) {
}
}
func
TestAlphabetTraceRequired
(
t
*
testing
.
T
)
{
func
TestAlphabetTraceRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
(
flags
.
TraceTypeAlphabet
)
config
:=
validConfig
(
TraceTypeAlphabet
)
config
.
AlphabetTrace
=
""
config
.
AlphabetTrace
=
""
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingAlphabetTrace
)
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingAlphabetTrace
)
config
.
AlphabetTrace
=
validAlphabetTrace
config
.
AlphabetTrace
=
validAlphabetTrace
...
@@ -62,7 +61,7 @@ func TestAlphabetTraceRequired(t *testing.T) {
...
@@ -62,7 +61,7 @@ func TestAlphabetTraceRequired(t *testing.T) {
}
}
func
TestCannonTraceRequired
(
t
*
testing
.
T
)
{
func
TestCannonTraceRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
(
flags
.
TraceTypeCannon
)
config
:=
validConfig
(
TraceTypeCannon
)
config
.
CannonDatadir
=
""
config
.
CannonDatadir
=
""
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingCannonDatadir
)
require
.
ErrorIs
(
t
,
config
.
Check
(),
ErrMissingCannonDatadir
)
config
.
CannonDatadir
=
validCannonDatadir
config
.
CannonDatadir
=
validCannonDatadir
...
...
op-challenger/fault/abi_test.go
View file @
e0bb0187
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -81,7 +82,7 @@ func TestBuildFaultStepData(t *testing.T) {
...
@@ -81,7 +82,7 @@ func TestBuildFaultStepData(t *testing.T) {
responder
,
_
:=
newTestFaultResponder
(
t
,
false
)
responder
,
_
:=
newTestFaultResponder
(
t
,
false
)
data
,
err
:=
responder
.
buildStepTxData
(
StepCallData
{
data
,
err
:=
responder
.
buildStepTxData
(
types
.
StepCallData
{
ClaimIndex
:
2
,
ClaimIndex
:
2
,
IsAttack
:
false
,
IsAttack
:
false
,
StateData
:
[]
byte
{
0x01
},
StateData
:
[]
byte
{
0x01
},
...
...
op-challenger/fault/agent.go
View file @
e0bb0187
...
@@ -5,9 +5,19 @@ import (
...
@@ -5,9 +5,19 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
)
)
// Responder takes a response action & executes.
// For full op-challenger this means executing the transaction on chain.
type
Responder
interface
{
CanResolve
(
ctx
context
.
Context
)
bool
Resolve
(
ctx
context
.
Context
)
error
Respond
(
ctx
context
.
Context
,
response
types
.
Claim
)
error
Step
(
ctx
context
.
Context
,
stepData
types
.
StepCallData
)
error
}
type
Agent
struct
{
type
Agent
struct
{
solver
*
Solver
solver
*
Solver
loader
Loader
loader
Loader
...
@@ -17,7 +27,7 @@ type Agent struct {
...
@@ -17,7 +27,7 @@ type Agent struct {
log
log
.
Logger
log
log
.
Logger
}
}
func
NewAgent
(
loader
Loader
,
maxDepth
int
,
trace
TraceProvider
,
responder
Responder
,
agreeWithProposedOutput
bool
,
log
log
.
Logger
)
*
Agent
{
func
NewAgent
(
loader
Loader
,
maxDepth
int
,
trace
types
.
TraceProvider
,
responder
Responder
,
agreeWithProposedOutput
bool
,
log
log
.
Logger
)
*
Agent
{
return
&
Agent
{
return
&
Agent
{
solver
:
NewSolver
(
maxDepth
,
trace
),
solver
:
NewSolver
(
maxDepth
,
trace
),
loader
:
loader
,
loader
:
loader
,
...
@@ -67,7 +77,7 @@ func (a *Agent) tryResolve(ctx context.Context) bool {
...
@@ -67,7 +77,7 @@ func (a *Agent) tryResolve(ctx context.Context) bool {
}
}
// newGameFromContracts initializes a new game state from the state in the contract
// newGameFromContracts initializes a new game state from the state in the contract
func
(
a
*
Agent
)
newGameFromContracts
(
ctx
context
.
Context
)
(
Game
,
error
)
{
func
(
a
*
Agent
)
newGameFromContracts
(
ctx
context
.
Context
)
(
types
.
Game
,
error
)
{
claims
,
err
:=
a
.
loader
.
FetchClaims
(
ctx
)
claims
,
err
:=
a
.
loader
.
FetchClaims
(
ctx
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to fetch claims: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to fetch claims: %w"
,
err
)
...
@@ -75,7 +85,7 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
...
@@ -75,7 +85,7 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
if
len
(
claims
)
==
0
{
if
len
(
claims
)
==
0
{
return
nil
,
errors
.
New
(
"no claims"
)
return
nil
,
errors
.
New
(
"no claims"
)
}
}
game
:=
NewGameState
(
a
.
agreeWithProposedOutput
,
claims
[
0
],
uint64
(
a
.
maxDepth
))
game
:=
types
.
NewGameState
(
a
.
agreeWithProposedOutput
,
claims
[
0
],
uint64
(
a
.
maxDepth
))
if
err
:=
game
.
PutAll
(
claims
[
1
:
]);
err
!=
nil
{
if
err
:=
game
.
PutAll
(
claims
[
1
:
]);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to load claims into the local state: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to load claims into the local state: %w"
,
err
)
}
}
...
@@ -83,7 +93,7 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
...
@@ -83,7 +93,7 @@ func (a *Agent) newGameFromContracts(ctx context.Context) (Game, error) {
}
}
// move determines & executes the next move given a claim
// move determines & executes the next move given a claim
func
(
a
*
Agent
)
move
(
ctx
context
.
Context
,
claim
Claim
,
game
Game
)
error
{
func
(
a
*
Agent
)
move
(
ctx
context
.
Context
,
claim
types
.
Claim
,
game
types
.
Game
)
error
{
nextMove
,
err
:=
a
.
solver
.
NextMove
(
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
nextMove
,
err
:=
a
.
solver
.
NextMove
(
claim
,
game
.
AgreeWithClaimLevel
(
claim
))
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"execute next move: %w"
,
err
)
return
fmt
.
Errorf
(
"execute next move: %w"
,
err
)
...
@@ -105,7 +115,7 @@ func (a *Agent) move(ctx context.Context, claim Claim, game Game) error {
...
@@ -105,7 +115,7 @@ func (a *Agent) move(ctx context.Context, claim Claim, game Game) error {
}
}
// step determines & executes the next step against a leaf claim through the responder
// step determines & executes the next step against a leaf claim through the responder
func
(
a
*
Agent
)
step
(
ctx
context
.
Context
,
claim
Claim
,
game
Game
)
error
{
func
(
a
*
Agent
)
step
(
ctx
context
.
Context
,
claim
types
.
Claim
,
game
types
.
Game
)
error
{
if
claim
.
Depth
()
!=
a
.
maxDepth
{
if
claim
.
Depth
()
!=
a
.
maxDepth
{
return
nil
return
nil
}
}
...
@@ -129,7 +139,7 @@ func (a *Agent) step(ctx context.Context, claim Claim, game Game) error {
...
@@ -129,7 +139,7 @@ func (a *Agent) step(ctx context.Context, claim Claim, game Game) error {
a
.
log
.
Info
(
"Performing step"
,
"is_attack"
,
step
.
IsAttack
,
a
.
log
.
Info
(
"Performing step"
,
"is_attack"
,
step
.
IsAttack
,
"depth"
,
step
.
LeafClaim
.
Depth
(),
"index_at_depth"
,
step
.
LeafClaim
.
IndexAtDepth
(),
"value"
,
step
.
LeafClaim
.
Value
)
"depth"
,
step
.
LeafClaim
.
Depth
(),
"index_at_depth"
,
step
.
LeafClaim
.
IndexAtDepth
(),
"value"
,
step
.
LeafClaim
.
Value
)
callData
:=
StepCallData
{
callData
:=
types
.
StepCallData
{
ClaimIndex
:
uint64
(
step
.
LeafClaim
.
ContractIndex
),
ClaimIndex
:
uint64
(
step
.
LeafClaim
.
ContractIndex
),
IsAttack
:
step
.
IsAttack
,
IsAttack
:
step
.
IsAttack
,
StateData
:
step
.
PreState
,
StateData
:
step
.
PreState
,
...
...
op-challenger/fault/alphabet_provider.go
View file @
e0bb0187
...
@@ -4,11 +4,12 @@ import (
...
@@ -4,11 +4,12 @@ import (
"math/big"
"math/big"
"strings"
"strings"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
)
)
var
_
TraceProvider
=
(
*
AlphabetProvider
)(
nil
)
var
_
types
.
TraceProvider
=
(
*
AlphabetProvider
)(
nil
)
// AlphabetProvider is a [TraceProvider] that provides claims for specific
// AlphabetProvider is a [TraceProvider] that provides claims for specific
// indices in the given trace.
// indices in the given trace.
...
@@ -29,7 +30,7 @@ func NewAlphabetProvider(state string, depth uint64) *AlphabetProvider {
...
@@ -29,7 +30,7 @@ func NewAlphabetProvider(state string, depth uint64) *AlphabetProvider {
func
(
ap
*
AlphabetProvider
)
GetPreimage
(
i
uint64
)
([]
byte
,
[]
byte
,
error
)
{
func
(
ap
*
AlphabetProvider
)
GetPreimage
(
i
uint64
)
([]
byte
,
[]
byte
,
error
)
{
// The index cannot be larger than the maximum index as computed by the depth.
// The index cannot be larger than the maximum index as computed by the depth.
if
i
>=
ap
.
maxLen
{
if
i
>=
ap
.
maxLen
{
return
nil
,
nil
,
ErrIndexTooLarge
return
nil
,
nil
,
types
.
ErrIndexTooLarge
}
}
// We extend the deepest hash to the maximum depth if the trace is not expansive.
// We extend the deepest hash to the maximum depth if the trace is not expansive.
if
i
>=
uint64
(
len
(
ap
.
state
))
{
if
i
>=
uint64
(
len
(
ap
.
state
))
{
...
...
op-challenger/fault/alphabet_provider_test.go
View file @
e0bb0187
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"math/big"
"math/big"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -66,7 +67,7 @@ func TestGetPreimage_Succeeds(t *testing.T) {
...
@@ -66,7 +67,7 @@ func TestGetPreimage_Succeeds(t *testing.T) {
func
TestGetPreimage_TooLargeIndex_Fails
(
t
*
testing
.
T
)
{
func
TestGetPreimage_TooLargeIndex_Fails
(
t
*
testing
.
T
)
{
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
_
,
_
,
err
:=
ap
.
GetPreimage
(
4
)
_
,
_
,
err
:=
ap
.
GetPreimage
(
4
)
require
.
ErrorIs
(
t
,
err
,
ErrIndexTooLarge
)
require
.
ErrorIs
(
t
,
err
,
types
.
ErrIndexTooLarge
)
}
}
// TestGet_Succeeds tests the Get function.
// TestGet_Succeeds tests the Get function.
...
@@ -83,7 +84,7 @@ func TestGet_Succeeds(t *testing.T) {
...
@@ -83,7 +84,7 @@ func TestGet_Succeeds(t *testing.T) {
func
TestGet_IndexTooLarge
(
t
*
testing
.
T
)
{
func
TestGet_IndexTooLarge
(
t
*
testing
.
T
)
{
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
_
,
err
:=
ap
.
Get
(
4
)
_
,
err
:=
ap
.
Get
(
4
)
require
.
ErrorIs
(
t
,
err
,
ErrIndexTooLarge
)
require
.
ErrorIs
(
t
,
err
,
types
.
ErrIndexTooLarge
)
}
}
// TestGet_Extends tests the Get function with an index that is larger
// TestGet_Extends tests the Get function with an index that is larger
...
...
op-challenger/fault/caller.go
View file @
e0bb0187
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"math/big"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -59,9 +60,9 @@ func (fc *FaultCaller) LogGameInfo(ctx context.Context) {
...
@@ -59,9 +60,9 @@ func (fc *FaultCaller) LogGameInfo(ctx context.Context) {
// 0: In Progress
// 0: In Progress
// 1: Challenger Won
// 1: Challenger Won
// 2: Defender Won
// 2: Defender Won
func
(
fc
*
FaultCaller
)
GetGameStatus
(
ctx
context
.
Context
)
(
GameStatus
,
error
)
{
func
(
fc
*
FaultCaller
)
GetGameStatus
(
ctx
context
.
Context
)
(
types
.
GameStatus
,
error
)
{
status
,
err
:=
fc
.
Status
(
&
bind
.
CallOpts
{
Context
:
ctx
})
status
,
err
:=
fc
.
Status
(
&
bind
.
CallOpts
{
Context
:
ctx
})
return
GameStatus
(
status
),
err
return
types
.
GameStatus
(
status
),
err
}
}
// GetClaimDataLength returns the number of claims in the game.
// GetClaimDataLength returns the number of claims in the game.
...
@@ -79,13 +80,13 @@ func (fc *FaultCaller) LogClaimDataLength(ctx context.Context) {
...
@@ -79,13 +80,13 @@ func (fc *FaultCaller) LogClaimDataLength(ctx context.Context) {
}
}
// GameStatusString returns the current game status as a string.
// GameStatusString returns the current game status as a string.
func
GameStatusString
(
status
GameStatus
)
string
{
func
GameStatusString
(
status
types
.
GameStatus
)
string
{
switch
status
{
switch
status
{
case
GameStatusInProgress
:
case
types
.
GameStatusInProgress
:
return
"In Progress"
return
"In Progress"
case
GameStatusChallengerWon
:
case
types
.
GameStatusChallengerWon
:
return
"Challenger Won"
return
"Challenger Won"
case
GameStatusDefenderWon
:
case
types
.
GameStatusDefenderWon
:
return
"Defender Won"
return
"Defender Won"
default
:
default
:
return
"Unknown"
return
"Unknown"
...
...
op-challenger/fault/caller_test.go
View file @
e0bb0187
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"math/big"
"math/big"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -40,7 +41,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
...
@@ -40,7 +41,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
tests
:=
[]
struct
{
tests
:=
[]
struct
{
name
string
name
string
caller
FaultDisputeGameCaller
caller
FaultDisputeGameCaller
expectedStatus
GameStatus
expectedStatus
types
.
GameStatus
expectedErr
error
expectedErr
error
}{
}{
{
{
...
@@ -48,7 +49,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
...
@@ -48,7 +49,7 @@ func TestFaultCaller_GetGameStatus(t *testing.T) {
caller
:
&
mockFaultDisputeGameCaller
{
caller
:
&
mockFaultDisputeGameCaller
{
status
:
1
,
status
:
1
,
},
},
expectedStatus
:
GameStatusChallengerWon
,
expectedStatus
:
types
.
GameStatusChallengerWon
,
expectedErr
:
nil
,
expectedErr
:
nil
,
},
},
{
{
...
...
op-challenger/fault/loader.go
View file @
e0bb0187
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"context"
"context"
"math/big"
"math/big"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
)
...
@@ -22,7 +23,7 @@ type ClaimFetcher interface {
...
@@ -22,7 +23,7 @@ type ClaimFetcher interface {
// Loader is a minimal interface for loading onchain [Claim] data.
// Loader is a minimal interface for loading onchain [Claim] data.
type
Loader
interface
{
type
Loader
interface
{
FetchClaims
(
ctx
context
.
Context
)
([]
Claim
,
error
)
FetchClaims
(
ctx
context
.
Context
)
([]
types
.
Claim
,
error
)
}
}
// loader pulls in fault dispute game claim data periodically and over subscriptions.
// loader pulls in fault dispute game claim data periodically and over subscriptions.
...
@@ -38,20 +39,20 @@ func NewLoader(claimFetcher ClaimFetcher) *loader {
...
@@ -38,20 +39,20 @@ func NewLoader(claimFetcher ClaimFetcher) *loader {
}
}
// fetchClaim fetches a single [Claim] with a hydrated parent.
// fetchClaim fetches a single [Claim] with a hydrated parent.
func
(
l
*
loader
)
fetchClaim
(
ctx
context
.
Context
,
arrIndex
uint64
)
(
Claim
,
error
)
{
func
(
l
*
loader
)
fetchClaim
(
ctx
context
.
Context
,
arrIndex
uint64
)
(
types
.
Claim
,
error
)
{
callOpts
:=
bind
.
CallOpts
{
callOpts
:=
bind
.
CallOpts
{
Context
:
ctx
,
Context
:
ctx
,
}
}
fetchedClaim
,
err
:=
l
.
claimFetcher
.
ClaimData
(
&
callOpts
,
new
(
big
.
Int
)
.
SetUint64
(
arrIndex
))
fetchedClaim
,
err
:=
l
.
claimFetcher
.
ClaimData
(
&
callOpts
,
new
(
big
.
Int
)
.
SetUint64
(
arrIndex
))
if
err
!=
nil
{
if
err
!=
nil
{
return
Claim
{},
err
return
types
.
Claim
{},
err
}
}
claim
:=
Claim
{
claim
:=
types
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
fetchedClaim
.
Claim
,
Value
:
fetchedClaim
.
Claim
,
Position
:
NewPositionFromGIndex
(
fetchedClaim
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
fetchedClaim
.
Position
.
Uint64
()),
},
},
Countered
:
fetchedClaim
.
Countered
,
Countered
:
fetchedClaim
.
Countered
,
Clock
:
fetchedClaim
.
Clock
.
Uint64
(),
Clock
:
fetchedClaim
.
Clock
.
Uint64
(),
...
@@ -63,11 +64,11 @@ func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (Claim, error)
...
@@ -63,11 +64,11 @@ func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (Claim, error)
parentIndex
:=
uint64
(
fetchedClaim
.
ParentIndex
)
parentIndex
:=
uint64
(
fetchedClaim
.
ParentIndex
)
parentClaim
,
err
:=
l
.
claimFetcher
.
ClaimData
(
&
callOpts
,
new
(
big
.
Int
)
.
SetUint64
(
parentIndex
))
parentClaim
,
err
:=
l
.
claimFetcher
.
ClaimData
(
&
callOpts
,
new
(
big
.
Int
)
.
SetUint64
(
parentIndex
))
if
err
!=
nil
{
if
err
!=
nil
{
return
Claim
{},
err
return
types
.
Claim
{},
err
}
}
claim
.
Parent
=
ClaimData
{
claim
.
Parent
=
types
.
ClaimData
{
Value
:
parentClaim
.
Claim
,
Value
:
parentClaim
.
Claim
,
Position
:
NewPositionFromGIndex
(
parentClaim
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
parentClaim
.
Position
.
Uint64
()),
}
}
}
}
...
@@ -75,7 +76,7 @@ func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (Claim, error)
...
@@ -75,7 +76,7 @@ func (l *loader) fetchClaim(ctx context.Context, arrIndex uint64) (Claim, error)
}
}
// FetchClaims fetches all claims from the fault dispute game.
// FetchClaims fetches all claims from the fault dispute game.
func
(
l
*
loader
)
FetchClaims
(
ctx
context
.
Context
)
([]
Claim
,
error
)
{
func
(
l
*
loader
)
FetchClaims
(
ctx
context
.
Context
)
([]
types
.
Claim
,
error
)
{
// Get the current claim count.
// Get the current claim count.
claimCount
,
err
:=
l
.
claimFetcher
.
ClaimDataLen
(
&
bind
.
CallOpts
{
claimCount
,
err
:=
l
.
claimFetcher
.
ClaimDataLen
(
&
bind
.
CallOpts
{
Context
:
ctx
,
Context
:
ctx
,
...
@@ -85,7 +86,7 @@ func (l *loader) FetchClaims(ctx context.Context) ([]Claim, error) {
...
@@ -85,7 +86,7 @@ func (l *loader) FetchClaims(ctx context.Context) ([]Claim, error) {
}
}
// Fetch each claim and build a list.
// Fetch each claim and build a list.
claimList
:=
make
([]
Claim
,
claimCount
.
Uint64
())
claimList
:=
make
([]
types
.
Claim
,
claimCount
.
Uint64
())
for
i
:=
uint64
(
0
);
i
<
claimCount
.
Uint64
();
i
++
{
for
i
:=
uint64
(
0
);
i
<
claimCount
.
Uint64
();
i
++
{
claim
,
err
:=
l
.
fetchClaim
(
ctx
,
i
)
claim
,
err
:=
l
.
fetchClaim
(
ctx
,
i
)
if
err
!=
nil
{
if
err
!=
nil
{
...
...
op-challenger/fault/loader_test.go
View file @
e0bb0187
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"math/big"
"math/big"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
...
@@ -94,41 +95,41 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
...
@@ -94,41 +95,41 @@ func TestLoader_FetchClaims_Succeeds(t *testing.T) {
loader
:=
NewLoader
(
mockClaimFetcher
)
loader
:=
NewLoader
(
mockClaimFetcher
)
claims
,
err
:=
loader
.
FetchClaims
(
context
.
Background
())
claims
,
err
:=
loader
.
FetchClaims
(
context
.
Background
())
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
err
)
require
.
ElementsMatch
(
t
,
[]
Claim
{
require
.
ElementsMatch
(
t
,
[]
types
.
Claim
{
{
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
0
]
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
expectedClaims
[
0
]
.
Position
.
Uint64
()),
},
},
Parent
:
ClaimData
{
Parent
:
types
.
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
0
]
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
expectedClaims
[
0
]
.
Position
.
Uint64
()),
},
},
Countered
:
false
,
Countered
:
false
,
Clock
:
uint64
(
0
),
Clock
:
uint64
(
0
),
ContractIndex
:
0
,
ContractIndex
:
0
,
},
},
{
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
expectedClaims
[
1
]
.
Claim
,
Value
:
expectedClaims
[
1
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
1
]
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
expectedClaims
[
1
]
.
Position
.
Uint64
()),
},
},
Parent
:
ClaimData
{
Parent
:
types
.
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
1
]
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
expectedClaims
[
1
]
.
Position
.
Uint64
()),
},
},
Countered
:
false
,
Countered
:
false
,
Clock
:
uint64
(
0
),
Clock
:
uint64
(
0
),
ContractIndex
:
1
,
ContractIndex
:
1
,
},
},
{
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
expectedClaims
[
2
]
.
Claim
,
Value
:
expectedClaims
[
2
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
2
]
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
expectedClaims
[
2
]
.
Position
.
Uint64
()),
},
},
Parent
:
ClaimData
{
Parent
:
types
.
ClaimData
{
Value
:
expectedClaims
[
0
]
.
Claim
,
Value
:
expectedClaims
[
0
]
.
Claim
,
Position
:
NewPositionFromGIndex
(
expectedClaims
[
2
]
.
Position
.
Uint64
()),
Position
:
types
.
NewPositionFromGIndex
(
expectedClaims
[
2
]
.
Position
.
Uint64
()),
},
},
Countered
:
false
,
Countered
:
false
,
Clock
:
uint64
(
0
),
Clock
:
uint64
(
0
),
...
...
op-challenger/fault/monitor.go
View file @
e0bb0187
...
@@ -4,11 +4,12 @@ import (
...
@@ -4,11 +4,12 @@ import (
"context"
"context"
"time"
"time"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
)
)
type
GameInfo
interface
{
type
GameInfo
interface
{
GetGameStatus
(
context
.
Context
)
(
GameStatus
,
error
)
GetGameStatus
(
context
.
Context
)
(
types
.
GameStatus
,
error
)
LogGameInfo
(
ctx
context
.
Context
)
LogGameInfo
(
ctx
context
.
Context
)
}
}
...
@@ -43,11 +44,11 @@ func progressGame(ctx context.Context, logger log.Logger, agreeWithProposedOutpu
...
@@ -43,11 +44,11 @@ func progressGame(ctx context.Context, logger log.Logger, agreeWithProposedOutpu
if
status
,
err
:=
caller
.
GetGameStatus
(
ctx
);
err
!=
nil
{
if
status
,
err
:=
caller
.
GetGameStatus
(
ctx
);
err
!=
nil
{
logger
.
Warn
(
"Unable to retrieve game status"
,
"err"
,
err
)
logger
.
Warn
(
"Unable to retrieve game status"
,
"err"
,
err
)
}
else
if
status
!=
0
{
}
else
if
status
!=
0
{
var
expectedStatus
GameStatus
var
expectedStatus
types
.
GameStatus
if
agreeWithProposedOutput
{
if
agreeWithProposedOutput
{
expectedStatus
=
GameStatusChallengerWon
expectedStatus
=
types
.
GameStatusChallengerWon
}
else
{
}
else
{
expectedStatus
=
GameStatusDefenderWon
expectedStatus
=
types
.
GameStatusDefenderWon
}
}
if
expectedStatus
==
status
{
if
expectedStatus
==
status
{
logger
.
Info
(
"Game won"
,
"status"
,
GameStatusString
(
status
))
logger
.
Info
(
"Game won"
,
"status"
,
GameStatusString
(
status
))
...
...
op-challenger/fault/monitor_test.go
View file @
e0bb0187
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"errors"
"errors"
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -43,7 +44,7 @@ func TestProgressGame_LogErrorFromAct(t *testing.T) {
...
@@ -43,7 +44,7 @@ func TestProgressGame_LogErrorFromAct(t *testing.T) {
func
TestProgressGame_LogErrorWhenGameLost
(
t
*
testing
.
T
)
{
func
TestProgressGame_LogErrorWhenGameLost
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
tests
:=
[]
struct
{
name
string
name
string
status
GameStatus
status
types
.
GameStatus
agreeWithOutput
bool
agreeWithOutput
bool
logLevel
log
.
Lvl
logLevel
log
.
Lvl
logMsg
string
logMsg
string
...
@@ -51,7 +52,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
...
@@ -51,7 +52,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
}{
}{
{
{
name
:
"GameLostAsDefender"
,
name
:
"GameLostAsDefender"
,
status
:
GameStatusChallengerWon
,
status
:
types
.
GameStatusChallengerWon
,
agreeWithOutput
:
false
,
agreeWithOutput
:
false
,
logLevel
:
log
.
LvlError
,
logLevel
:
log
.
LvlError
,
logMsg
:
"Game lost"
,
logMsg
:
"Game lost"
,
...
@@ -59,7 +60,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
...
@@ -59,7 +60,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
},
},
{
{
name
:
"GameLostAsChallenger"
,
name
:
"GameLostAsChallenger"
,
status
:
GameStatusDefenderWon
,
status
:
types
.
GameStatusDefenderWon
,
agreeWithOutput
:
true
,
agreeWithOutput
:
true
,
logLevel
:
log
.
LvlError
,
logLevel
:
log
.
LvlError
,
logMsg
:
"Game lost"
,
logMsg
:
"Game lost"
,
...
@@ -67,7 +68,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
...
@@ -67,7 +68,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
},
},
{
{
name
:
"GameWonAsDefender"
,
name
:
"GameWonAsDefender"
,
status
:
GameStatusDefenderWon
,
status
:
types
.
GameStatusDefenderWon
,
agreeWithOutput
:
false
,
agreeWithOutput
:
false
,
logLevel
:
log
.
LvlInfo
,
logLevel
:
log
.
LvlInfo
,
logMsg
:
"Game won"
,
logMsg
:
"Game won"
,
...
@@ -75,7 +76,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
...
@@ -75,7 +76,7 @@ func TestProgressGame_LogErrorWhenGameLost(t *testing.T) {
},
},
{
{
name
:
"GameWonAsChallenger"
,
name
:
"GameWonAsChallenger"
,
status
:
GameStatusChallengerWon
,
status
:
types
.
GameStatusChallengerWon
,
agreeWithOutput
:
true
,
agreeWithOutput
:
true
,
logLevel
:
log
.
LvlInfo
,
logLevel
:
log
.
LvlInfo
,
logMsg
:
"Game won"
,
logMsg
:
"Game won"
,
...
@@ -120,12 +121,12 @@ func (a *stubActor) Act(ctx context.Context) error {
...
@@ -120,12 +121,12 @@ func (a *stubActor) Act(ctx context.Context) error {
}
}
type
stubGameInfo
struct
{
type
stubGameInfo
struct
{
status
GameStatus
status
types
.
GameStatus
err
error
err
error
logCount
int
logCount
int
}
}
func
(
s
*
stubGameInfo
)
GetGameStatus
(
ctx
context
.
Context
)
(
GameStatus
,
error
)
{
func
(
s
*
stubGameInfo
)
GetGameStatus
(
ctx
context
.
Context
)
(
types
.
GameStatus
,
error
)
{
return
s
.
status
,
s
.
err
return
s
.
status
,
s
.
err
}
}
...
...
op-challenger/fault/responder.go
View file @
e0bb0187
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"math/big"
"math/big"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
types2
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum"
...
@@ -62,7 +63,7 @@ func (r *faultResponder) buildResolveData() ([]byte, error) {
...
@@ -62,7 +63,7 @@ func (r *faultResponder) buildResolveData() ([]byte, error) {
}
}
// BuildTx builds the transaction for the [faultResponder].
// BuildTx builds the transaction for the [faultResponder].
func
(
r
*
faultResponder
)
BuildTx
(
ctx
context
.
Context
,
response
Claim
)
([]
byte
,
error
)
{
func
(
r
*
faultResponder
)
BuildTx
(
ctx
context
.
Context
,
response
types2
.
Claim
)
([]
byte
,
error
)
{
if
response
.
DefendsParent
()
{
if
response
.
DefendsParent
()
{
txData
,
err
:=
r
.
buildFaultDefendData
(
response
.
ParentContractIndex
,
response
.
ValueBytes
())
txData
,
err
:=
r
.
buildFaultDefendData
(
response
.
ParentContractIndex
,
response
.
ValueBytes
())
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -103,7 +104,7 @@ func (r *faultResponder) Resolve(ctx context.Context) error {
...
@@ -103,7 +104,7 @@ func (r *faultResponder) Resolve(ctx context.Context) error {
}
}
// Respond takes a [Claim] and executes the response action.
// Respond takes a [Claim] and executes the response action.
func
(
r
*
faultResponder
)
Respond
(
ctx
context
.
Context
,
response
Claim
)
error
{
func
(
r
*
faultResponder
)
Respond
(
ctx
context
.
Context
,
response
types2
.
Claim
)
error
{
txData
,
err
:=
r
.
BuildTx
(
ctx
,
response
)
txData
,
err
:=
r
.
BuildTx
(
ctx
,
response
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
...
@@ -131,7 +132,7 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
...
@@ -131,7 +132,7 @@ func (r *faultResponder) sendTxAndWait(ctx context.Context, txData []byte) error
}
}
// buildStepTxData creates the transaction data for the step function.
// buildStepTxData creates the transaction data for the step function.
func
(
r
*
faultResponder
)
buildStepTxData
(
stepData
StepCallData
)
([]
byte
,
error
)
{
func
(
r
*
faultResponder
)
buildStepTxData
(
stepData
types2
.
StepCallData
)
([]
byte
,
error
)
{
return
r
.
fdgAbi
.
Pack
(
return
r
.
fdgAbi
.
Pack
(
"step"
,
"step"
,
big
.
NewInt
(
int64
(
stepData
.
ClaimIndex
)),
big
.
NewInt
(
int64
(
stepData
.
ClaimIndex
)),
...
@@ -142,7 +143,7 @@ func (r *faultResponder) buildStepTxData(stepData StepCallData) ([]byte, error)
...
@@ -142,7 +143,7 @@ func (r *faultResponder) buildStepTxData(stepData StepCallData) ([]byte, error)
}
}
// Step accepts step data and executes the step on the fault dispute game contract.
// Step accepts step data and executes the step on the fault dispute game contract.
func
(
r
*
faultResponder
)
Step
(
ctx
context
.
Context
,
stepData
StepCallData
)
error
{
func
(
r
*
faultResponder
)
Step
(
ctx
context
.
Context
,
stepData
types2
.
StepCallData
)
error
{
txData
,
err
:=
r
.
buildStepTxData
(
stepData
)
txData
,
err
:=
r
.
buildStepTxData
(
stepData
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
...
...
op-challenger/fault/responder_test.go
View file @
e0bb0187
...
@@ -7,6 +7,7 @@ import (
...
@@ -7,6 +7,7 @@ import (
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
types2
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
...
@@ -107,14 +108,14 @@ func TestResponder_Resolve_Success(t *testing.T) {
...
@@ -107,14 +108,14 @@ func TestResponder_Resolve_Success(t *testing.T) {
// bubbles up the error returned by the [txmgr.Send] method.
// bubbles up the error returned by the [txmgr.Send] method.
func
TestResponder_Respond_SendFails
(
t
*
testing
.
T
)
{
func
TestResponder_Respond_SendFails
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
,
true
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
,
true
)
err
:=
responder
.
Respond
(
context
.
Background
(),
Claim
{
err
:=
responder
.
Respond
(
context
.
Background
(),
types2
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x01
},
Value
:
common
.
Hash
{
0x01
},
Position
:
NewPositionFromGIndex
(
2
),
Position
:
types2
.
NewPositionFromGIndex
(
2
),
},
},
Parent
:
ClaimData
{
Parent
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x02
},
Value
:
common
.
Hash
{
0x02
},
Position
:
NewPositionFromGIndex
(
1
),
Position
:
types2
.
NewPositionFromGIndex
(
1
),
},
},
ContractIndex
:
0
,
ContractIndex
:
0
,
ParentContractIndex
:
0
,
ParentContractIndex
:
0
,
...
@@ -127,14 +128,14 @@ func TestResponder_Respond_SendFails(t *testing.T) {
...
@@ -127,14 +128,14 @@ func TestResponder_Respond_SendFails(t *testing.T) {
// succeeds when the tx candidate is successfully sent through the txmgr.
// succeeds when the tx candidate is successfully sent through the txmgr.
func
TestResponder_Respond_Success
(
t
*
testing
.
T
)
{
func
TestResponder_Respond_Success
(
t
*
testing
.
T
)
{
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
,
false
)
responder
,
mockTxMgr
:=
newTestFaultResponder
(
t
,
false
)
err
:=
responder
.
Respond
(
context
.
Background
(),
Claim
{
err
:=
responder
.
Respond
(
context
.
Background
(),
types2
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x01
},
Value
:
common
.
Hash
{
0x01
},
Position
:
NewPositionFromGIndex
(
2
),
Position
:
types2
.
NewPositionFromGIndex
(
2
),
},
},
Parent
:
ClaimData
{
Parent
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x02
},
Value
:
common
.
Hash
{
0x02
},
Position
:
NewPositionFromGIndex
(
1
),
Position
:
types2
.
NewPositionFromGIndex
(
1
),
},
},
ContractIndex
:
0
,
ContractIndex
:
0
,
ParentContractIndex
:
0
,
ParentContractIndex
:
0
,
...
@@ -147,14 +148,14 @@ func TestResponder_Respond_Success(t *testing.T) {
...
@@ -147,14 +148,14 @@ func TestResponder_Respond_Success(t *testing.T) {
// returns a tx candidate with the correct data for an attack tx.
// returns a tx candidate with the correct data for an attack tx.
func
TestResponder_BuildTx_Attack
(
t
*
testing
.
T
)
{
func
TestResponder_BuildTx_Attack
(
t
*
testing
.
T
)
{
responder
,
_
:=
newTestFaultResponder
(
t
,
false
)
responder
,
_
:=
newTestFaultResponder
(
t
,
false
)
responseClaim
:=
Claim
{
responseClaim
:=
types2
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x01
},
Value
:
common
.
Hash
{
0x01
},
Position
:
NewPositionFromGIndex
(
2
),
Position
:
types2
.
NewPositionFromGIndex
(
2
),
},
},
Parent
:
ClaimData
{
Parent
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x02
},
Value
:
common
.
Hash
{
0x02
},
Position
:
NewPositionFromGIndex
(
1
),
Position
:
types2
.
NewPositionFromGIndex
(
1
),
},
},
ContractIndex
:
0
,
ContractIndex
:
0
,
ParentContractIndex
:
7
,
ParentContractIndex
:
7
,
...
@@ -178,14 +179,14 @@ func TestResponder_BuildTx_Attack(t *testing.T) {
...
@@ -178,14 +179,14 @@ func TestResponder_BuildTx_Attack(t *testing.T) {
// returns a tx candidate with the correct data for a defend tx.
// returns a tx candidate with the correct data for a defend tx.
func
TestResponder_BuildTx_Defend
(
t
*
testing
.
T
)
{
func
TestResponder_BuildTx_Defend
(
t
*
testing
.
T
)
{
responder
,
_
:=
newTestFaultResponder
(
t
,
false
)
responder
,
_
:=
newTestFaultResponder
(
t
,
false
)
responseClaim
:=
Claim
{
responseClaim
:=
types2
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x01
},
Value
:
common
.
Hash
{
0x01
},
Position
:
NewPositionFromGIndex
(
3
),
Position
:
types2
.
NewPositionFromGIndex
(
3
),
},
},
Parent
:
ClaimData
{
Parent
:
types2
.
ClaimData
{
Value
:
common
.
Hash
{
0x02
},
Value
:
common
.
Hash
{
0x02
},
Position
:
NewPositionFromGIndex
(
6
),
Position
:
types2
.
NewPositionFromGIndex
(
6
),
},
},
ContractIndex
:
0
,
ContractIndex
:
0
,
ParentContractIndex
:
7
,
ParentContractIndex
:
7
,
...
...
op-challenger/fault/service.go
View file @
e0bb0187
...
@@ -7,7 +7,7 @@ import (
...
@@ -7,7 +7,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/fault/cannon"
"github.com/ethereum-optimism/optimism/op-challenger/f
lag
s"
"github.com/ethereum-optimism/optimism/op-challenger/f
ault/type
s"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient"
...
@@ -52,11 +52,11 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
...
@@ -52,11 +52,11 @@ func NewService(ctx context.Context, logger log.Logger, cfg *config.Config) (*se
return
nil
,
fmt
.
Errorf
(
"failed to create the responder: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to create the responder: %w"
,
err
)
}
}
var
trace
TraceProvider
var
trace
types
.
TraceProvider
switch
cfg
.
TraceType
{
switch
cfg
.
TraceType
{
case
flags
.
TraceTypeCannon
:
case
config
.
TraceTypeCannon
:
trace
=
cannon
.
NewCannonTraceProvider
(
logger
,
cfg
.
CannonDatadir
)
trace
=
cannon
.
NewCannonTraceProvider
(
logger
,
cfg
.
CannonDatadir
)
case
flags
.
TraceTypeAlphabet
:
case
config
.
TraceTypeAlphabet
:
trace
=
NewAlphabetProvider
(
cfg
.
AlphabetTrace
,
uint64
(
cfg
.
GameDepth
))
trace
=
NewAlphabetProvider
(
cfg
.
AlphabetTrace
,
uint64
(
cfg
.
GameDepth
))
default
:
default
:
return
nil
,
fmt
.
Errorf
(
"unsupported trace type: %v"
,
cfg
.
TraceType
)
return
nil
,
fmt
.
Errorf
(
"unsupported trace type: %v"
,
cfg
.
TraceType
)
...
...
op-challenger/fault/solver.go
View file @
e0bb0187
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
)
)
...
@@ -13,12 +14,12 @@ var (
...
@@ -13,12 +14,12 @@ var (
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
// Solver uses a [TraceProvider] to determine the moves to make in a dispute game.
type
Solver
struct
{
type
Solver
struct
{
TraceProvider
types
.
TraceProvider
gameDepth
int
gameDepth
int
}
}
// NewSolver creates a new [Solver] using the provided [TraceProvider].
// NewSolver creates a new [Solver] using the provided [TraceProvider].
func
NewSolver
(
gameDepth
int
,
traceProvider
TraceProvider
)
*
Solver
{
func
NewSolver
(
gameDepth
int
,
traceProvider
types
.
TraceProvider
)
*
Solver
{
return
&
Solver
{
return
&
Solver
{
traceProvider
,
traceProvider
,
gameDepth
,
gameDepth
,
...
@@ -26,7 +27,7 @@ func NewSolver(gameDepth int, traceProvider TraceProvider) *Solver {
...
@@ -26,7 +27,7 @@ func NewSolver(gameDepth int, traceProvider TraceProvider) *Solver {
}
}
// NextMove returns the next move to make given the current state of the game.
// NextMove returns the next move to make given the current state of the game.
func
(
s
*
Solver
)
NextMove
(
claim
Claim
,
agreeWithClaimLevel
bool
)
(
*
Claim
,
error
)
{
func
(
s
*
Solver
)
NextMove
(
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
*
types
.
Claim
,
error
)
{
if
agreeWithClaimLevel
{
if
agreeWithClaimLevel
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -38,7 +39,7 @@ func (s *Solver) NextMove(claim Claim, agreeWithClaimLevel bool) (*Claim, error)
...
@@ -38,7 +39,7 @@ func (s *Solver) NextMove(claim Claim, agreeWithClaimLevel bool) (*Claim, error)
return
s
.
handleMiddle
(
claim
)
return
s
.
handleMiddle
(
claim
)
}
}
func
(
s
*
Solver
)
handleRoot
(
claim
Claim
)
(
*
Claim
,
error
)
{
func
(
s
*
Solver
)
handleRoot
(
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
agree
,
err
:=
s
.
agreeWithClaim
(
claim
.
ClaimData
)
agree
,
err
:=
s
.
agreeWithClaim
(
claim
.
ClaimData
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -53,7 +54,7 @@ func (s *Solver) handleRoot(claim Claim) (*Claim, error) {
...
@@ -53,7 +54,7 @@ func (s *Solver) handleRoot(claim Claim) (*Claim, error) {
}
}
}
}
func
(
s
*
Solver
)
handleMiddle
(
claim
Claim
)
(
*
Claim
,
error
)
{
func
(
s
*
Solver
)
handleMiddle
(
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
claimCorrect
,
err
:=
s
.
agreeWithClaim
(
claim
.
ClaimData
)
claimCorrect
,
err
:=
s
.
agreeWithClaim
(
claim
.
ClaimData
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
...
@@ -69,7 +70,7 @@ func (s *Solver) handleMiddle(claim Claim) (*Claim, error) {
...
@@ -69,7 +70,7 @@ func (s *Solver) handleMiddle(claim Claim) (*Claim, error) {
}
}
type
StepData
struct
{
type
StepData
struct
{
LeafClaim
Claim
LeafClaim
types
.
Claim
IsAttack
bool
IsAttack
bool
PreState
[]
byte
PreState
[]
byte
ProofData
[]
byte
ProofData
[]
byte
...
@@ -77,7 +78,7 @@ type StepData struct {
...
@@ -77,7 +78,7 @@ type StepData struct {
// AttemptStep determines what step should occur for a given leaf claim.
// AttemptStep determines what step should occur for a given leaf claim.
// An error will be returned if the claim is not at the max depth.
// An error will be returned if the claim is not at the max depth.
func
(
s
*
Solver
)
AttemptStep
(
claim
Claim
,
agreeWithClaimLevel
bool
)
(
StepData
,
error
)
{
func
(
s
*
Solver
)
AttemptStep
(
claim
types
.
Claim
,
agreeWithClaimLevel
bool
)
(
StepData
,
error
)
{
if
claim
.
Depth
()
!=
s
.
gameDepth
{
if
claim
.
Depth
()
!=
s
.
gameDepth
{
return
StepData
{},
errors
.
New
(
"cannot step on non-leaf claims"
)
return
StepData
{},
errors
.
New
(
"cannot step on non-leaf claims"
)
}
}
...
@@ -114,41 +115,41 @@ func (s *Solver) AttemptStep(claim Claim, agreeWithClaimLevel bool) (StepData, e
...
@@ -114,41 +115,41 @@ func (s *Solver) AttemptStep(claim Claim, agreeWithClaimLevel bool) (StepData, e
}
}
// attack returns a response that attacks the claim.
// attack returns a response that attacks the claim.
func
(
s
*
Solver
)
attack
(
claim
Claim
)
(
*
Claim
,
error
)
{
func
(
s
*
Solver
)
attack
(
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
position
:=
claim
.
Attack
()
position
:=
claim
.
Attack
()
value
,
err
:=
s
.
traceAtPosition
(
position
)
value
,
err
:=
s
.
traceAtPosition
(
position
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"attack claim: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"attack claim: %w"
,
err
)
}
}
return
&
Claim
{
return
&
types
.
Claim
{
ClaimData
:
ClaimData
{
Value
:
value
,
Position
:
position
},
ClaimData
:
types
.
ClaimData
{
Value
:
value
,
Position
:
position
},
Parent
:
claim
.
ClaimData
,
Parent
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ContractIndex
,
ParentContractIndex
:
claim
.
ContractIndex
,
},
nil
},
nil
}
}
// defend returns a response that defends the claim.
// defend returns a response that defends the claim.
func
(
s
*
Solver
)
defend
(
claim
Claim
)
(
*
Claim
,
error
)
{
func
(
s
*
Solver
)
defend
(
claim
types
.
Claim
)
(
*
types
.
Claim
,
error
)
{
position
:=
claim
.
Defend
()
position
:=
claim
.
Defend
()
value
,
err
:=
s
.
traceAtPosition
(
position
)
value
,
err
:=
s
.
traceAtPosition
(
position
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"defend claim: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"defend claim: %w"
,
err
)
}
}
return
&
Claim
{
return
&
types
.
Claim
{
ClaimData
:
ClaimData
{
Value
:
value
,
Position
:
position
},
ClaimData
:
types
.
ClaimData
{
Value
:
value
,
Position
:
position
},
Parent
:
claim
.
ClaimData
,
Parent
:
claim
.
ClaimData
,
ParentContractIndex
:
claim
.
ContractIndex
,
ParentContractIndex
:
claim
.
ContractIndex
,
},
nil
},
nil
}
}
// agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider].
// agreeWithClaim returns true if the claim is correct according to the internal [TraceProvider].
func
(
s
*
Solver
)
agreeWithClaim
(
claim
ClaimData
)
(
bool
,
error
)
{
func
(
s
*
Solver
)
agreeWithClaim
(
claim
types
.
ClaimData
)
(
bool
,
error
)
{
ourValue
,
err
:=
s
.
traceAtPosition
(
claim
.
Position
)
ourValue
,
err
:=
s
.
traceAtPosition
(
claim
.
Position
)
return
ourValue
==
claim
.
Value
,
err
return
ourValue
==
claim
.
Value
,
err
}
}
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
// traceAtPosition returns the [common.Hash] from internal [TraceProvider] at the given [Position].
func
(
s
*
Solver
)
traceAtPosition
(
p
Position
)
(
common
.
Hash
,
error
)
{
func
(
s
*
Solver
)
traceAtPosition
(
p
types
.
Position
)
(
common
.
Hash
,
error
)
{
index
:=
p
.
TraceIndex
(
s
.
gameDepth
)
index
:=
p
.
TraceIndex
(
s
.
gameDepth
)
hash
,
err
:=
s
.
Get
(
index
)
hash
,
err
:=
s
.
Get
(
index
)
return
hash
,
err
return
hash
,
err
...
...
op-challenger/fault/solver_test.go
View file @
e0bb0187
...
@@ -3,6 +3,7 @@ package fault
...
@@ -3,6 +3,7 @@ package fault
import
(
import
(
"testing"
"testing"
"github.com/ethereum-optimism/optimism/op-challenger/fault/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
...
@@ -23,52 +24,52 @@ func TestSolver_NextMove_Opponent(t *testing.T) {
...
@@ -23,52 +24,52 @@ func TestSolver_NextMove_Opponent(t *testing.T) {
// The following claims are created using the state: "abcdexyz".
// The following claims are created using the state: "abcdexyz".
// The responses are the responses we expect from the solver.
// The responses are the responses we expect from the solver.
indices
:=
[]
struct
{
indices
:=
[]
struct
{
claim
Claim
claim
types
.
Claim
response
ClaimData
response
types
.
ClaimData
}{
}{
{
{
Claim
{
types
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
alphabetClaim
(
7
,
"z"
),
Value
:
alphabetClaim
(
7
,
"z"
),
Position
:
NewPosition
(
0
,
0
),
Position
:
types
.
NewPosition
(
0
,
0
),
},
},
// Root claim has no parent
// Root claim has no parent
},
},
ClaimData
{
types
.
ClaimData
{
Value
:
alphabetClaim
(
3
,
"d"
),
Value
:
alphabetClaim
(
3
,
"d"
),
Position
:
NewPosition
(
1
,
0
),
Position
:
types
.
NewPosition
(
1
,
0
),
},
},
},
},
{
{
Claim
{
types
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
alphabetClaim
(
3
,
"d"
),
Value
:
alphabetClaim
(
3
,
"d"
),
Position
:
NewPosition
(
1
,
0
),
Position
:
types
.
NewPosition
(
1
,
0
),
},
},
Parent
:
ClaimData
{
Parent
:
types
.
ClaimData
{
Value
:
alphabetClaim
(
7
,
"h"
),
Value
:
alphabetClaim
(
7
,
"h"
),
Position
:
NewPosition
(
0
,
0
),
Position
:
types
.
NewPosition
(
0
,
0
),
},
},
},
},
ClaimData
{
types
.
ClaimData
{
Value
:
alphabetClaim
(
5
,
"f"
),
Value
:
alphabetClaim
(
5
,
"f"
),
Position
:
NewPosition
(
2
,
2
),
Position
:
types
.
NewPosition
(
2
,
2
),
},
},
},
},
{
{
Claim
{
types
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
alphabetClaim
(
5
,
"x"
),
Value
:
alphabetClaim
(
5
,
"x"
),
Position
:
NewPosition
(
2
,
2
),
Position
:
types
.
NewPosition
(
2
,
2
),
},
},
Parent
:
ClaimData
{
Parent
:
types
.
ClaimData
{
Value
:
alphabetClaim
(
7
,
"h"
),
Value
:
alphabetClaim
(
7
,
"h"
),
Position
:
NewPosition
(
1
,
1
),
Position
:
types
.
NewPosition
(
1
,
1
),
},
},
},
},
ClaimData
{
types
.
ClaimData
{
Value
:
alphabetClaim
(
4
,
"e"
),
Value
:
alphabetClaim
(
4
,
"e"
),
Position
:
NewPosition
(
3
,
4
),
Position
:
types
.
NewPosition
(
3
,
4
),
},
},
},
},
}
}
...
@@ -85,10 +86,10 @@ func TestNoMoveAgainstOwnLevel(t *testing.T) {
...
@@ -85,10 +86,10 @@ func TestNoMoveAgainstOwnLevel(t *testing.T) {
mallory
:=
NewAlphabetProvider
(
"abcdepqr"
,
uint64
(
maxDepth
))
mallory
:=
NewAlphabetProvider
(
"abcdepqr"
,
uint64
(
maxDepth
))
solver
:=
NewSolver
(
maxDepth
,
mallory
)
solver
:=
NewSolver
(
maxDepth
,
mallory
)
claim
:=
Claim
{
claim
:=
types
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
Value
:
alphabetClaim
(
7
,
"z"
),
Value
:
alphabetClaim
(
7
,
"z"
),
Position
:
NewPosition
(
0
,
0
),
Position
:
types
.
NewPosition
(
0
,
0
),
},
},
// Root claim has no parent
// Root claim has no parent
}
}
...
@@ -104,10 +105,10 @@ func TestAttemptStep(t *testing.T) {
...
@@ -104,10 +105,10 @@ func TestAttemptStep(t *testing.T) {
solver
:=
NewSolver
(
maxDepth
,
canonicalProvider
)
solver
:=
NewSolver
(
maxDepth
,
canonicalProvider
)
_
,
_
,
middle
,
bottom
:=
createTestClaims
()
_
,
_
,
middle
,
bottom
:=
createTestClaims
()
zero
:=
Claim
{
zero
:=
types
.
Claim
{
ClaimData
:
ClaimData
{
ClaimData
:
types
.
ClaimData
{
// Zero value is a purposely disagree with claim value "a"
// Zero value is a purposely disagree with claim value "a"
Position
:
NewPosition
(
3
,
0
),
Position
:
types
.
NewPosition
(
3
,
0
),
},
},
}
}
...
@@ -150,3 +151,39 @@ func (a *alphabetWithProofProvider) GetPreimage(i uint64) ([]byte, []byte, error
...
@@ -150,3 +151,39 @@ func (a *alphabetWithProofProvider) GetPreimage(i uint64) ([]byte, []byte, error
}
}
return
preimage
,
[]
byte
{
byte
(
i
)},
nil
return
preimage
,
[]
byte
{
byte
(
i
)},
nil
}
}
func
createTestClaims
()
(
types
.
Claim
,
types
.
Claim
,
types
.
Claim
,
types
.
Claim
)
{
// root & middle are from the trace "abcdexyz"
// top & bottom are from the trace "abcdefgh"
root
:=
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
common
.
HexToHash
(
"0x000000000000000000000000000000000000000000000000000000000000077a"
),
Position
:
types
.
NewPosition
(
0
,
0
),
},
// Root claim has no parent
}
top
:=
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000364"
),
Position
:
types
.
NewPosition
(
1
,
0
),
},
Parent
:
root
.
ClaimData
,
}
middle
:=
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000578"
),
Position
:
types
.
NewPosition
(
2
,
2
),
},
Parent
:
top
.
ClaimData
,
}
bottom
:=
types
.
Claim
{
ClaimData
:
types
.
ClaimData
{
Value
:
common
.
HexToHash
(
"0x0000000000000000000000000000000000000000000000000000000000000465"
),
Position
:
types
.
NewPosition
(
3
,
4
),
},
Parent
:
middle
.
ClaimData
,
}
return
root
,
top
,
middle
,
bottom
}
op-challenger/fault/game.go
→
op-challenger/fault/
types/
game.go
View file @
e0bb0187
package
fault
package
types
import
(
import
(
"errors"
"errors"
...
@@ -26,7 +26,7 @@ type Game interface {
...
@@ -26,7 +26,7 @@ type Game interface {
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
// IsDuplicate returns true if the provided [Claim] already exists in the game state.
IsDuplicate
(
claim
Claim
)
bool
IsDuplicate
(
claim
Claim
)
bool
// AgreeWithLevel returns if the game state agrees with the provided claim level.
// AgreeWith
Claim
Level returns if the game state agrees with the provided claim level.
AgreeWithClaimLevel
(
claim
Claim
)
bool
AgreeWithClaimLevel
(
claim
Claim
)
bool
}
}
...
...
op-challenger/fault/game_test.go
→
op-challenger/fault/
types/
game_test.go
View file @
e0bb0187
package
fault
package
types
import
(
import
(
"testing"
"testing"
...
...
op-challenger/fault/position.go
→
op-challenger/fault/
types/
position.go
View file @
e0bb0187
package
fault
package
types
import
"fmt"
import
"fmt"
...
...
op-challenger/fault/position_test.go
→
op-challenger/fault/
types/
position_test.go
View file @
e0bb0187
package
fault
package
types
import
(
import
(
"testing"
"testing"
...
...
op-challenger/fault/types.go
→
op-challenger/fault/types
/types
.go
View file @
e0bb0187
package
fault
package
types
import
(
import
(
"context"
"errors"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
...
@@ -83,12 +82,3 @@ func (c *Claim) IsRoot() bool {
...
@@ -83,12 +82,3 @@ func (c *Claim) IsRoot() bool {
func
(
c
*
Claim
)
DefendsParent
()
bool
{
func
(
c
*
Claim
)
DefendsParent
()
bool
{
return
(
c
.
IndexAtDepth
()
>>
1
)
!=
c
.
Parent
.
IndexAtDepth
()
return
(
c
.
IndexAtDepth
()
>>
1
)
!=
c
.
Parent
.
IndexAtDepth
()
}
}
// Responder takes a response action & executes.
// For full op-challenger this means executing the transaction on chain.
type
Responder
interface
{
CanResolve
(
ctx
context
.
Context
)
bool
Resolve
(
ctx
context
.
Context
)
error
Respond
(
ctx
context
.
Context
,
response
Claim
)
error
Step
(
ctx
context
.
Context
,
stepData
StepCallData
)
error
}
op-challenger/flags/flags.go
View file @
e0bb0187
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"fmt"
"fmt"
"strings"
"strings"
"github.com/ethereum-optimism/optimism/op-challenger/config"
openum
"github.com/ethereum-optimism/optimism/op-service/enum"
openum
"github.com/ethereum-optimism/optimism/op-service/enum"
"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2"
...
@@ -20,36 +21,6 @@ func prefixEnvVars(name string) []string {
...
@@ -20,36 +21,6 @@ func prefixEnvVars(name string) []string {
return
opservice
.
PrefixEnvVar
(
envVarPrefix
,
name
)
return
opservice
.
PrefixEnvVar
(
envVarPrefix
,
name
)
}
}
type
TraceType
string
const
(
TraceTypeAlphabet
TraceType
=
"alphabet"
TraceTypeCannon
TraceType
=
"cannon"
)
var
TraceTypes
=
[]
TraceType
{
TraceTypeAlphabet
,
TraceTypeCannon
}
func
(
t
TraceType
)
String
()
string
{
return
string
(
t
)
}
func
(
t
*
TraceType
)
Set
(
value
string
)
error
{
if
!
ValidTraceType
(
TraceType
(
value
))
{
return
fmt
.
Errorf
(
"unknown trace type: %q"
,
value
)
}
*
t
=
TraceType
(
value
)
return
nil
}
func
ValidTraceType
(
value
TraceType
)
bool
{
for
_
,
t
:=
range
TraceTypes
{
if
t
==
value
{
return
true
}
}
return
false
}
var
(
var
(
// Required Flags
// Required Flags
L1EthRpcFlag
=
&
cli
.
StringFlag
{
L1EthRpcFlag
=
&
cli
.
StringFlag
{
...
@@ -64,10 +35,10 @@ var (
...
@@ -64,10 +35,10 @@ var (
}
}
TraceTypeFlag
=
&
cli
.
GenericFlag
{
TraceTypeFlag
=
&
cli
.
GenericFlag
{
Name
:
"trace-type"
,
Name
:
"trace-type"
,
Usage
:
"The trace type. Valid options: "
+
openum
.
EnumString
(
TraceTypes
),
Usage
:
"The trace type. Valid options: "
+
openum
.
EnumString
(
config
.
TraceTypes
),
EnvVars
:
prefixEnvVars
(
"TRACE_TYPE"
),
EnvVars
:
prefixEnvVars
(
"TRACE_TYPE"
),
Value
:
func
()
*
TraceType
{
Value
:
func
()
*
config
.
TraceType
{
out
:=
TraceType
(
""
)
// No default value
out
:=
config
.
TraceType
(
""
)
// No default value
return
&
out
return
&
out
}(),
}(),
}
}
...
@@ -125,18 +96,45 @@ func CheckRequired(ctx *cli.Context) error {
...
@@ -125,18 +96,45 @@ func CheckRequired(ctx *cli.Context) error {
return
fmt
.
Errorf
(
"flag %s is required"
,
f
.
Names
()[
0
])
return
fmt
.
Errorf
(
"flag %s is required"
,
f
.
Names
()[
0
])
}
}
}
}
gameType
:=
TraceType
(
strings
.
ToLower
(
ctx
.
String
(
TraceTypeFlag
.
Name
)))
gameType
:=
config
.
TraceType
(
strings
.
ToLower
(
ctx
.
String
(
TraceTypeFlag
.
Name
)))
switch
gameType
{
switch
gameType
{
case
TraceTypeCannon
:
case
config
.
TraceTypeCannon
:
if
!
ctx
.
IsSet
(
CannonDatadirFlag
.
Name
)
{
if
!
ctx
.
IsSet
(
CannonDatadirFlag
.
Name
)
{
return
fmt
.
Errorf
(
"flag %s is required"
,
"cannon-datadir"
)
return
fmt
.
Errorf
(
"flag %s is required"
,
"cannon-datadir"
)
}
}
case
TraceTypeAlphabet
:
case
config
.
TraceTypeAlphabet
:
if
!
ctx
.
IsSet
(
AlphabetFlag
.
Name
)
{
if
!
ctx
.
IsSet
(
AlphabetFlag
.
Name
)
{
return
fmt
.
Errorf
(
"flag %s is required"
,
"alphabet"
)
return
fmt
.
Errorf
(
"flag %s is required"
,
"alphabet"
)
}
}
default
:
default
:
return
fmt
.
Errorf
(
"invalid trace type. must be one of %v"
,
TraceTypes
)
return
fmt
.
Errorf
(
"invalid trace type. must be one of %v"
,
config
.
TraceTypes
)
}
}
return
nil
return
nil
}
}
// NewConfigFromCLI parses the Config from the provided flags or environment variables.
func
NewConfigFromCLI
(
ctx
*
cli
.
Context
)
(
*
config
.
Config
,
error
)
{
if
err
:=
CheckRequired
(
ctx
);
err
!=
nil
{
return
nil
,
err
}
dgfAddress
,
err
:=
opservice
.
ParseAddress
(
ctx
.
String
(
DGFAddressFlag
.
Name
))
if
err
!=
nil
{
return
nil
,
err
}
txMgrConfig
:=
txmgr
.
ReadCLIConfig
(
ctx
)
traceTypeFlag
:=
config
.
TraceType
(
strings
.
ToLower
(
ctx
.
String
(
TraceTypeFlag
.
Name
)))
return
&
config
.
Config
{
// Required Flags
L1EthRpc
:
ctx
.
String
(
L1EthRpcFlag
.
Name
),
TraceType
:
traceTypeFlag
,
GameAddress
:
dgfAddress
,
AlphabetTrace
:
ctx
.
String
(
AlphabetFlag
.
Name
),
CannonDatadir
:
ctx
.
String
(
CannonDatadirFlag
.
Name
),
AgreeWithProposedOutput
:
ctx
.
Bool
(
AgreeWithProposedOutputFlag
.
Name
),
GameDepth
:
ctx
.
Int
(
GameDepthFlag
.
Name
),
TxMgrConfig
:
txMgrConfig
,
},
nil
}
op-e2e/e2eutils/disputegame/helper.go
View file @
e0bb0187
...
@@ -11,7 +11,7 @@ import (
...
@@ -11,7 +11,7 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/fault"
"github.com/ethereum-optimism/optimism/op-challenger/fault"
"github.com/ethereum-optimism/optimism/op-challenger/f
lag
s"
"github.com/ethereum-optimism/optimism/op-challenger/f
ault/type
s"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
...
@@ -107,7 +107,7 @@ func (g *FaultGameHelper) StartChallenger(ctx context.Context, l1Endpoint string
...
@@ -107,7 +107,7 @@ func (g *FaultGameHelper) StartChallenger(ctx context.Context, l1Endpoint string
func
(
c
*
config
.
Config
)
{
func
(
c
*
config
.
Config
)
{
c
.
GameAddress
=
g
.
addr
c
.
GameAddress
=
g
.
addr
c
.
GameDepth
=
alphabetGameDepth
c
.
GameDepth
=
alphabetGameDepth
c
.
TraceType
=
flags
.
TraceTypeAlphabet
c
.
TraceType
=
config
.
TraceTypeAlphabet
// By default the challenger agrees with the root claim (thus disagrees with the proposed output)
// By default the challenger agrees with the root claim (thus disagrees with the proposed output)
// This can be overridden by passing in options
// This can be overridden by passing in options
c
.
AlphabetTrace
=
g
.
claimedAlphabet
c
.
AlphabetTrace
=
g
.
claimedAlphabet
...
@@ -169,7 +169,7 @@ func (g *FaultGameHelper) WaitForClaim(ctx context.Context, predicate func(claim
...
@@ -169,7 +169,7 @@ func (g *FaultGameHelper) WaitForClaim(ctx context.Context, predicate func(claim
func
(
g
*
FaultGameHelper
)
WaitForClaimAtMaxDepth
(
ctx
context
.
Context
,
countered
bool
)
{
func
(
g
*
FaultGameHelper
)
WaitForClaimAtMaxDepth
(
ctx
context
.
Context
,
countered
bool
)
{
g
.
WaitForClaim
(
ctx
,
func
(
claim
ContractClaim
)
bool
{
g
.
WaitForClaim
(
ctx
,
func
(
claim
ContractClaim
)
bool
{
pos
:=
fault
.
NewPositionFromGIndex
(
claim
.
Position
.
Uint64
())
pos
:=
types
.
NewPositionFromGIndex
(
claim
.
Position
.
Uint64
())
return
pos
.
Depth
()
==
g
.
maxDepth
&&
claim
.
Countered
==
countered
return
pos
.
Depth
()
==
g
.
maxDepth
&&
claim
.
Countered
==
countered
})
})
}
}
...
...
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