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
9dd47648
Unverified
Commit
9dd47648
authored
Jun 30, 2023
by
OptimismBot
Committed by
GitHub
Jun 30, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6224 from ethereum-optimism/jg/cleanup_challenger_init
op-challenger: Simplify init
parents
1a95d061
fe8808bb
Changes
24
Hide whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
101 additions
and
1934 deletions
+101
-1934
challenger.go
op-challenger/challenger/challenger.go
+0
-177
factory.go
op-challenger/challenger/factory.go
+0
-33
factory_test.go
op-challenger/challenger/factory_test.go
+0
-46
log_store.go
op-challenger/challenger/log_store.go
+0
-154
log_store_test.go
op-challenger/challenger/log_store_test.go
+0
-166
oracle.go
op-challenger/challenger/oracle.go
+0
-36
oracle_test.go
op-challenger/challenger/oracle_test.go
+0
-52
output.go
op-challenger/challenger/output.go
+0
-77
output_test.go
op-challenger/challenger/output_test.go
+0
-236
subscription.go
op-challenger/challenger/subscription.go
+0
-89
subscription_test.go
op-challenger/challenger/subscription_test.go
+0
-83
entrypoint.go
op-challenger/cmd/entrypoint.go
+0
-81
main.go
op-challenger/cmd/main.go
+24
-40
cmd.go
op-challenger/cmd/watch/cmd.go
+0
-46
factory.go
op-challenger/cmd/watch/factory.go
+0
-62
oracle.go
op-challenger/cmd/watch/oracle.go
+0
-62
config.go
op-challenger/config/config.go
+47
-111
config_test.go
op-challenger/config/config_test.go
+20
-46
logging.go
op-challenger/config/logging.go
+0
-21
flags.go
op-challenger/flags/flags.go
+10
-15
metrics.go
op-challenger/metrics/metrics.go
+0
-124
noop.go
op-challenger/metrics/noop.go
+0
-21
game_type.go
op-challenger/types/game_type.go
+0
-73
game_type_test.go
op-challenger/types/game_type_test.go
+0
-83
No files found.
op-challenger/challenger/challenger.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
_
"net/http/pprof"
"sync"
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
ethclient
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/eth"
opclient
"github.com/ethereum-optimism/optimism/op-service/client"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
)
type
OutputAPI
interface
{
OutputAtBlock
(
ctx
context
.
Context
,
blockNum
uint64
)
(
*
eth
.
OutputResponse
,
error
)
}
// Challenger contests invalid L2OutputOracle outputs
type
Challenger
struct
{
txMgr
txmgr
.
TxManager
wg
sync
.
WaitGroup
done
chan
struct
{}
log
log
.
Logger
metr
metrics
.
Metricer
ctx
context
.
Context
cancel
context
.
CancelFunc
l1Client
*
ethclient
.
Client
rollupClient
OutputAPI
// l2 Output Oracle contract
l2ooContract
*
bindings
.
L2OutputOracleCaller
l2ooContractAddr
common
.
Address
l2ooABI
*
abi
.
ABI
// dispute game factory contract
dgfContract
*
bindings
.
DisputeGameFactoryCaller
dgfContractAddr
common
.
Address
dgfABI
*
abi
.
ABI
networkTimeout
time
.
Duration
}
// From returns the address of the account used to send transactions.
func
(
c
*
Challenger
)
From
()
common
.
Address
{
return
c
.
txMgr
.
From
()
}
// Client returns the client for the settlement layer.
func
(
c
*
Challenger
)
Client
()
*
ethclient
.
Client
{
return
c
.
l1Client
}
func
(
c
*
Challenger
)
NewOracleSubscription
()
(
*
Subscription
,
error
)
{
query
,
err
:=
BuildOutputLogFilter
(
c
.
l2ooABI
)
if
err
!=
nil
{
return
nil
,
err
}
return
NewSubscription
(
query
,
c
.
Client
(),
c
.
log
),
nil
}
// NewFactorySubscription creates a new [Subscription] listening to the DisputeGameFactory contract.
func
(
c
*
Challenger
)
NewFactorySubscription
()
(
*
Subscription
,
error
)
{
query
,
err
:=
BuildDisputeGameLogFilter
(
c
.
dgfABI
)
if
err
!=
nil
{
return
nil
,
err
}
return
NewSubscription
(
query
,
c
.
Client
(),
c
.
log
),
nil
}
// NewChallenger creates a new Challenger
func
NewChallenger
(
cfg
config
.
Config
,
l
log
.
Logger
,
m
metrics
.
Metricer
)
(
*
Challenger
,
error
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
txManager
,
err
:=
txmgr
.
NewSimpleTxManager
(
"challenger"
,
l
,
m
,
*
cfg
.
TxMgrConfig
)
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
// Connect to L1 and L2 providers. Perform these last since they are the most expensive.
l1Client
,
err
:=
opclient
.
DialEthClientWithTimeout
(
ctx
,
cfg
.
L1EthRpc
,
opclient
.
DefaultDialTimeout
)
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
rollupClient
,
err
:=
opclient
.
DialRollupClientWithTimeout
(
ctx
,
cfg
.
RollupRpc
,
opclient
.
DefaultDialTimeout
)
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
l2ooContract
,
err
:=
bindings
.
NewL2OutputOracleCaller
(
cfg
.
L2OOAddress
,
l1Client
)
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
dgfContract
,
err
:=
bindings
.
NewDisputeGameFactoryCaller
(
cfg
.
DGFAddress
,
l1Client
)
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
cCtx
,
cCancel
:=
context
.
WithTimeout
(
ctx
,
cfg
.
NetworkTimeout
)
defer
cCancel
()
version
,
err
:=
l2ooContract
.
Version
(
&
bind
.
CallOpts
{
Context
:
cCtx
})
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
l
.
Info
(
"Connected to L2OutputOracle"
,
"address"
,
cfg
.
L2OOAddress
,
"version"
,
version
)
parsedL2oo
,
err
:=
bindings
.
L2OutputOracleMetaData
.
GetAbi
()
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
parsedDgf
,
err
:=
bindings
.
DisputeGameFactoryMetaData
.
GetAbi
()
if
err
!=
nil
{
cancel
()
return
nil
,
err
}
return
&
Challenger
{
txMgr
:
txManager
,
done
:
make
(
chan
struct
{}),
log
:
l
,
metr
:
m
,
ctx
:
ctx
,
cancel
:
cancel
,
rollupClient
:
rollupClient
,
l1Client
:
l1Client
,
l2ooContract
:
l2ooContract
,
l2ooContractAddr
:
cfg
.
L2OOAddress
,
l2ooABI
:
parsedL2oo
,
dgfContract
:
dgfContract
,
dgfContractAddr
:
cfg
.
DGFAddress
,
dgfABI
:
parsedDgf
,
networkTimeout
:
cfg
.
NetworkTimeout
,
},
nil
}
// Start runs the challenger in a goroutine.
func
(
c
*
Challenger
)
Start
()
error
{
c
.
log
.
Error
(
"challenger not implemented."
)
return
nil
}
// Stop closes the challenger and waits for spawned goroutines to exit.
func
(
c
*
Challenger
)
Stop
()
{
c
.
cancel
()
close
(
c
.
done
)
c
.
wg
.
Wait
()
}
op-challenger/challenger/factory.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"errors"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
var
ErrMissingFactoryEvent
=
errors
.
New
(
"missing factory event"
)
// BuildDisputeGameLogFilter creates a filter query for the DisputeGameFactory contract.
//
// The `DisputeGameCreated` event is encoded as:
// 0: address indexed disputeProxy,
// 1: GameType indexed gameType,
// 2: Claim indexed rootClaim,
func
BuildDisputeGameLogFilter
(
contract
*
abi
.
ABI
)
(
ethereum
.
FilterQuery
,
error
)
{
event
:=
contract
.
Events
[
"DisputeGameCreated"
]
if
event
.
ID
==
(
common
.
Hash
{})
{
return
ethereum
.
FilterQuery
{},
ErrMissingFactoryEvent
}
query
:=
ethereum
.
FilterQuery
{
Topics
:
[][]
common
.
Hash
{
{
event
.
ID
},
},
}
return
query
,
nil
}
op-challenger/challenger/factory_test.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"testing"
"github.com/stretchr/testify/require"
eth
"github.com/ethereum/go-ethereum"
abi
"github.com/ethereum/go-ethereum/accounts/abi"
common
"github.com/ethereum/go-ethereum/common"
)
// TestBuildDisputeGameLogFilter_Succeeds tests that the DisputeGame
// Log Filter is built correctly.
func
TestBuildDisputeGameLogFilter_Succeeds
(
t
*
testing
.
T
)
{
event
:=
abi
.
Event
{
ID
:
[
32
]
byte
{
0x01
},
}
filterQuery
:=
eth
.
FilterQuery
{
Topics
:
[][]
common
.
Hash
{
{
event
.
ID
},
},
}
dgfABI
:=
abi
.
ABI
{
Events
:
map
[
string
]
abi
.
Event
{
"DisputeGameCreated"
:
event
,
},
}
query
,
err
:=
BuildDisputeGameLogFilter
(
&
dgfABI
)
require
.
Equal
(
t
,
filterQuery
,
query
)
require
.
NoError
(
t
,
err
)
}
// TestBuildDisputeGameLogFilter_Fails tests that the DisputeGame
// Log Filter fails when the event definition is missing.
func
TestBuildDisputeGameLogFilter_Fails
(
t
*
testing
.
T
)
{
dgfABI
:=
abi
.
ABI
{
Events
:
map
[
string
]
abi
.
Event
{},
}
_
,
err
:=
BuildDisputeGameLogFilter
(
&
dgfABI
)
require
.
ErrorIs
(
t
,
ErrMissingFactoryEvent
,
err
)
}
op-challenger/challenger/log_store.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
"sync"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/backoff"
)
// logStore manages log subscriptions.
type
logStore
struct
{
// The log filter query
query
ethereum
.
FilterQuery
// core sync mutex for log store
// this locks the entire log store
mu
sync
.
Mutex
logList
[]
types
.
Log
logMap
map
[
common
.
Hash
][]
types
.
Log
// Log subscription
subscription
*
Subscription
// Client to query for logs
client
ethereum
.
LogFilterer
// Logger
log
log
.
Logger
}
// NewLogStore creates a new log store.
func
NewLogStore
(
query
ethereum
.
FilterQuery
,
client
ethereum
.
LogFilterer
,
log
log
.
Logger
)
*
logStore
{
return
&
logStore
{
query
:
query
,
mu
:
sync
.
Mutex
{},
logList
:
make
([]
types
.
Log
,
0
),
logMap
:
make
(
map
[
common
.
Hash
][]
types
.
Log
),
subscription
:
NewSubscription
(
query
,
client
,
log
),
client
:
client
,
log
:
log
,
}
}
// Subscribed returns true if the subscription has started.
func
(
l
*
logStore
)
Subscribed
()
bool
{
return
l
.
subscription
.
Started
()
}
// Query returns the log filter query.
func
(
l
*
logStore
)
Query
()
ethereum
.
FilterQuery
{
return
l
.
query
}
// Client returns the log filter client.
func
(
l
*
logStore
)
Client
()
ethereum
.
LogFilterer
{
return
l
.
client
}
// GetLogs returns all logs in the log store.
func
(
l
*
logStore
)
GetLogs
()
[]
types
.
Log
{
l
.
mu
.
Lock
()
defer
l
.
mu
.
Unlock
()
logs
:=
make
([]
types
.
Log
,
len
(
l
.
logList
))
copy
(
logs
,
l
.
logList
)
return
logs
}
// GetLogByBlockHash returns all logs in the log store for a given block hash.
func
(
l
*
logStore
)
GetLogByBlockHash
(
blockHash
common
.
Hash
)
[]
types
.
Log
{
l
.
mu
.
Lock
()
defer
l
.
mu
.
Unlock
()
logs
:=
make
([]
types
.
Log
,
len
(
l
.
logMap
[
blockHash
]))
copy
(
logs
,
l
.
logMap
[
blockHash
])
return
logs
}
// Subscribe starts the subscription.
// This function spawns a new goroutine.
func
(
l
*
logStore
)
Subscribe
(
ctx
context
.
Context
)
error
{
err
:=
l
.
subscription
.
Subscribe
()
if
err
!=
nil
{
l
.
log
.
Error
(
"failed to subscribe"
,
"err"
,
err
)
return
err
}
go
l
.
dispatchLogs
(
ctx
)
return
nil
}
// Quit stops all log store asynchronous tasks.
func
(
l
*
logStore
)
Quit
()
{
l
.
subscription
.
Quit
()
}
// buildBackoffStrategy builds a [backoff.Strategy].
func
(
l
*
logStore
)
buildBackoffStrategy
()
backoff
.
Strategy
{
return
&
backoff
.
ExponentialStrategy
{
Min
:
1000
,
Max
:
20
_000
,
MaxJitter
:
250
,
}
}
// resubscribe attempts to re-establish the log store internal
// subscription with a backoff strategy.
func
(
l
*
logStore
)
resubscribe
(
ctx
context
.
Context
)
error
{
l
.
log
.
Info
(
"log store resubscribing with backoff"
)
backoffStrategy
:=
l
.
buildBackoffStrategy
()
return
backoff
.
DoCtx
(
ctx
,
10
,
backoffStrategy
,
func
()
error
{
if
l
.
subscription
==
nil
{
l
.
log
.
Error
(
"subscription zeroed out"
)
return
nil
}
err
:=
l
.
subscription
.
Subscribe
()
if
err
==
nil
{
l
.
log
.
Info
(
"subscription reconnected"
,
"id"
,
l
.
subscription
.
ID
())
}
return
err
})
}
// insertLog inserts a log into the log store.
func
(
l
*
logStore
)
insertLog
(
log
types
.
Log
)
{
l
.
mu
.
Lock
()
l
.
logList
=
append
(
l
.
logList
,
log
)
l
.
logMap
[
log
.
BlockHash
]
=
append
(
l
.
logMap
[
log
.
BlockHash
],
log
)
l
.
mu
.
Unlock
()
}
// dispatchLogs dispatches logs to the log store.
// This function is intended to be run as a goroutine.
func
(
l
*
logStore
)
dispatchLogs
(
ctx
context
.
Context
)
{
for
{
select
{
case
err
:=
<-
l
.
subscription
.
sub
.
Err
()
:
l
.
log
.
Error
(
"log subscription error"
,
"err"
,
err
)
for
{
err
=
l
.
resubscribe
(
ctx
)
if
err
==
nil
{
break
}
}
case
log
:=
<-
l
.
subscription
.
logs
:
l
.
insertLog
(
log
)
case
<-
l
.
subscription
.
quit
:
l
.
log
.
Info
(
"received quit signal from subscription"
,
"id"
,
l
.
subscription
.
ID
())
return
}
}
}
op-challenger/challenger/log_store_test.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
"errors"
"testing"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/stretchr/testify/require"
)
type
mockLogStoreClient
struct
{
sub
mockSubscription
logs
chan
<-
types
.
Log
subcount
int
}
func
newMockLogStoreClient
()
*
mockLogStoreClient
{
return
&
mockLogStoreClient
{
sub
:
mockSubscription
{
errorChan
:
make
(
chan
error
),
},
}
}
func
(
m
*
mockLogStoreClient
)
FilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
)
([]
types
.
Log
,
error
)
{
panic
(
"this should not be called by the Subscription.Subscribe method"
)
}
func
(
m
*
mockLogStoreClient
)
SubscribeFilterLogs
(
ctx
context
.
Context
,
query
ethereum
.
FilterQuery
,
logs
chan
<-
types
.
Log
)
(
ethereum
.
Subscription
,
error
)
{
m
.
subcount
=
m
.
subcount
+
1
m
.
logs
=
logs
return
m
.
sub
,
nil
}
var
(
ErrTestError
=
errors
.
New
(
"test error"
)
)
// errLogStoreClient implements the [ethereum.LogFilter] interface for testing.
type
errLogStoreClient
struct
{}
func
(
m
errLogStoreClient
)
FilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
)
([]
types
.
Log
,
error
)
{
panic
(
"this should not be called by the Subscription.Subscribe method"
)
}
func
(
m
errLogStoreClient
)
SubscribeFilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
,
chan
<-
types
.
Log
)
(
ethereum
.
Subscription
,
error
)
{
return
nil
,
ErrTestError
}
type
mockSubscription
struct
{
errorChan
chan
error
}
func
(
m
mockSubscription
)
Err
()
<-
chan
error
{
return
m
.
errorChan
}
func
(
m
mockSubscription
)
Unsubscribe
()
{}
func
newLogStore
(
t
*
testing
.
T
)
(
*
logStore
,
*
mockLogStoreClient
)
{
query
:=
ethereum
.
FilterQuery
{}
client
:=
newMockLogStoreClient
()
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
return
NewLogStore
(
query
,
client
,
log
),
client
}
func
newErrorLogStore
(
t
*
testing
.
T
,
client
*
errLogStoreClient
)
(
*
logStore
,
*
errLogStoreClient
)
{
query
:=
ethereum
.
FilterQuery
{}
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
return
NewLogStore
(
query
,
client
,
log
),
client
}
func
TestLogStore_NewLogStore_NotSubscribed
(
t
*
testing
.
T
)
{
logStore
,
_
:=
newLogStore
(
t
)
require
.
False
(
t
,
logStore
.
Subscribed
())
}
func
TestLogStore_NewLogStore_EmptyLogs
(
t
*
testing
.
T
)
{
logStore
,
_
:=
newLogStore
(
t
)
require
.
Empty
(
t
,
logStore
.
GetLogs
())
require
.
Empty
(
t
,
logStore
.
GetLogByBlockHash
(
common
.
Hash
{}))
}
func
TestLogStore_Subscribe_EstablishesSubscription
(
t
*
testing
.
T
)
{
logStore
,
client
:=
newLogStore
(
t
)
defer
logStore
.
Quit
()
require
.
Equal
(
t
,
0
,
client
.
subcount
)
require
.
False
(
t
,
logStore
.
Subscribed
())
require
.
NoError
(
t
,
logStore
.
Subscribe
(
context
.
Background
()))
require
.
True
(
t
,
logStore
.
Subscribed
())
require
.
Equal
(
t
,
1
,
client
.
subcount
)
}
func
TestLogStore_Subscribe_ReceivesLogs
(
t
*
testing
.
T
)
{
logStore
,
client
:=
newLogStore
(
t
)
defer
logStore
.
Quit
()
require
.
NoError
(
t
,
logStore
.
Subscribe
(
context
.
Background
()))
mockLog
:=
types
.
Log
{
BlockHash
:
common
.
HexToHash
(
"0x1"
),
}
client
.
logs
<-
mockLog
timeout
,
tCancel
:=
context
.
WithTimeout
(
context
.
Background
(),
30
*
time
.
Second
)
defer
tCancel
()
err
:=
e2eutils
.
WaitFor
(
timeout
,
500
*
time
.
Millisecond
,
func
()
(
bool
,
error
)
{
result
:=
logStore
.
GetLogByBlockHash
(
mockLog
.
BlockHash
)
return
result
[
0
]
.
BlockHash
==
mockLog
.
BlockHash
,
nil
})
require
.
NoError
(
t
,
err
)
}
func
TestLogStore_Subscribe_SubscriptionErrors
(
t
*
testing
.
T
)
{
logStore
,
client
:=
newLogStore
(
t
)
defer
logStore
.
Quit
()
require
.
NoError
(
t
,
logStore
.
Subscribe
(
context
.
Background
()))
client
.
sub
.
errorChan
<-
ErrTestError
timeout
,
tCancel
:=
context
.
WithTimeout
(
context
.
Background
(),
30
*
time
.
Second
)
defer
tCancel
()
err
:=
e2eutils
.
WaitFor
(
timeout
,
500
*
time
.
Millisecond
,
func
()
(
bool
,
error
)
{
subcount
:=
client
.
subcount
==
2
started
:=
logStore
.
subscription
.
Started
()
return
subcount
&&
started
,
nil
})
require
.
NoError
(
t
,
err
)
}
func
TestLogStore_Subscribe_NoClient_Panics
(
t
*
testing
.
T
)
{
require
.
Panics
(
t
,
func
()
{
logStore
,
_
:=
newErrorLogStore
(
t
,
nil
)
_
=
logStore
.
Subscribe
(
context
.
Background
())
})
}
func
TestLogStore_Subscribe_ErrorSubscribing
(
t
*
testing
.
T
)
{
logStore
,
_
:=
newErrorLogStore
(
t
,
&
errLogStoreClient
{})
require
.
False
(
t
,
logStore
.
Subscribed
())
require
.
EqualError
(
t
,
logStore
.
Subscribe
(
context
.
Background
()),
ErrTestError
.
Error
())
}
func
TestLogStore_Quit_ResetsSubscription
(
t
*
testing
.
T
)
{
logStore
,
_
:=
newLogStore
(
t
)
require
.
False
(
t
,
logStore
.
Subscribed
())
require
.
NoError
(
t
,
logStore
.
Subscribe
(
context
.
Background
()))
require
.
True
(
t
,
logStore
.
Subscribed
())
logStore
.
Quit
()
require
.
False
(
t
,
logStore
.
Subscribed
())
}
func
TestLogStore_Quit_NoSubscription_Panics
(
t
*
testing
.
T
)
{
require
.
Panics
(
t
,
func
()
{
logStore
,
_
:=
newErrorLogStore
(
t
,
nil
)
logStore
.
Quit
()
})
}
op-challenger/challenger/oracle.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"errors"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)
var
ErrMissingEvent
=
errors
.
New
(
"missing event"
)
// BuildOutputLogFilter creates a filter query for the L2OutputOracle contract.
//
// The `OutputProposed` event is encoded as:
// 0: bytes32 indexed outputRoot,
// 1: uint256 indexed l2OutputIndex,
// 2: uint256 indexed l2BlockNumber,
// 3: uint256 l1Timestamp
func
BuildOutputLogFilter
(
l2ooABI
*
abi
.
ABI
)
(
ethereum
.
FilterQuery
,
error
)
{
// Get the L2OutputOracle contract `OutputProposed` event
event
:=
l2ooABI
.
Events
[
"OutputProposed"
]
// Sanity check that the `OutputProposed` event is defined
if
event
.
ID
==
(
common
.
Hash
{})
{
return
ethereum
.
FilterQuery
{},
ErrMissingEvent
}
query
:=
ethereum
.
FilterQuery
{
Topics
:
[][]
common
.
Hash
{
{
event
.
ID
},
},
}
return
query
,
nil
}
op-challenger/challenger/oracle_test.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"testing"
"github.com/stretchr/testify/require"
eth
"github.com/ethereum/go-ethereum"
abi
"github.com/ethereum/go-ethereum/accounts/abi"
common
"github.com/ethereum/go-ethereum/common"
)
// TestBuildOutputLogFilter_Succeeds tests that the Output
// Log Filter is built correctly.
func
TestBuildOutputLogFilter_Succeeds
(
t
*
testing
.
T
)
{
// Create a mock event id
event
:=
abi
.
Event
{
ID
:
[
32
]
byte
{
0x01
},
}
filterQuery
:=
eth
.
FilterQuery
{
Topics
:
[][]
common
.
Hash
{
{
event
.
ID
},
},
}
// Mock the ABI
l2ooABI
:=
abi
.
ABI
{
Events
:
map
[
string
]
abi
.
Event
{
"OutputProposed"
:
event
,
},
}
// Build the filter
query
,
err
:=
BuildOutputLogFilter
(
&
l2ooABI
)
require
.
Equal
(
t
,
filterQuery
,
query
)
require
.
NoError
(
t
,
err
)
}
// TestBuildOutputLogFilter_Fails tests that the Output
// Log Filter fails when the event definition is missing.
func
TestBuildOutputLogFilter_Fails
(
t
*
testing
.
T
)
{
// Mock the ABI
l2ooABI
:=
abi
.
ABI
{
Events
:
map
[
string
]
abi
.
Event
{},
}
// Build the filter
_
,
err
:=
BuildOutputLogFilter
(
&
l2ooABI
)
require
.
Error
(
t
,
err
)
require
.
ErrorIs
(
t
,
err
,
ErrMissingEvent
)
}
op-challenger/challenger/output.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-node/eth"
)
var
(
// supportedL2OutputVersion is the version of the L2 output that the challenger supports.
supportedL2OutputVersion
=
eth
.
Bytes32
{}
// ErrInvalidBlockNumber is returned when the block number of the output does not match the expected block number.
ErrInvalidBlockNumber
=
errors
.
New
(
"invalid block number"
)
// ErrUnsupportedL2OOVersion is returned when the output version is not supported.
ErrUnsupportedL2OOVersion
=
errors
.
New
(
"unsupported l2oo version"
)
// ErrInvalidOutputLogTopic is returned when the output log topic is invalid.
ErrInvalidOutputLogTopic
=
errors
.
New
(
"invalid output log topic"
)
// ErrInvalidOutputTopicLength is returned when the output log topic length is invalid.
ErrInvalidOutputTopicLength
=
errors
.
New
(
"invalid output log topic length"
)
)
// ParseOutputLog parses a log from the L2OutputOracle contract.
func
(
c
*
Challenger
)
ParseOutputLog
(
log
*
types
.
Log
)
(
*
bindings
.
TypesOutputProposal
,
error
)
{
// Check the length of log topics
if
len
(
log
.
Topics
)
!=
4
{
return
nil
,
ErrInvalidOutputTopicLength
}
// Validate the first topic is the output log topic
if
log
.
Topics
[
0
]
!=
c
.
l2ooABI
.
Events
[
"OutputProposed"
]
.
ID
{
return
nil
,
ErrInvalidOutputLogTopic
}
l2BlockNumber
:=
new
(
big
.
Int
)
.
SetBytes
(
log
.
Topics
[
3
][
:
])
expected
:=
log
.
Topics
[
1
]
return
&
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
l2BlockNumber
,
OutputRoot
:
eth
.
Bytes32
(
expected
),
},
nil
}
// ValidateOutput checks that a given output is expected via a trusted rollup node rpc.
// It returns: if the output is correct, the fetched output, error
func
(
c
*
Challenger
)
ValidateOutput
(
ctx
context
.
Context
,
proposal
bindings
.
TypesOutputProposal
)
(
bool
,
eth
.
Bytes32
,
error
)
{
// Fetch the output from the rollup node
ctx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
c
.
networkTimeout
)
defer
cancel
()
output
,
err
:=
c
.
rollupClient
.
OutputAtBlock
(
ctx
,
proposal
.
L2BlockNumber
.
Uint64
())
if
err
!=
nil
{
c
.
log
.
Error
(
"Failed to fetch output"
,
"blockNum"
,
proposal
.
L2BlockNumber
,
"err"
,
err
)
return
false
,
eth
.
Bytes32
{},
err
}
// Compare the output root to the expected output root
equalRoots
,
err
:=
c
.
compareOutputRoots
(
output
,
proposal
)
if
err
!=
nil
{
return
false
,
eth
.
Bytes32
{},
err
}
return
equalRoots
,
output
.
OutputRoot
,
nil
}
// compareOutputRoots compares the output root of the given block number to the expected output root.
func
(
c
*
Challenger
)
compareOutputRoots
(
received
*
eth
.
OutputResponse
,
expected
bindings
.
TypesOutputProposal
)
(
bool
,
error
)
{
if
received
.
Version
!=
supportedL2OutputVersion
{
c
.
log
.
Error
(
"Unsupported l2 output version"
,
"version"
,
received
.
Version
)
return
false
,
ErrUnsupportedL2OOVersion
}
if
received
.
BlockRef
.
Number
!=
expected
.
L2BlockNumber
.
Uint64
()
{
c
.
log
.
Error
(
"Invalid blockNumber"
,
"expected"
,
expected
.
L2BlockNumber
,
"actual"
,
received
.
BlockRef
.
Number
)
return
false
,
ErrInvalidBlockNumber
}
return
received
.
OutputRoot
==
expected
.
OutputRoot
,
nil
}
op-challenger/challenger/output_test.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
"errors"
"math/big"
"testing"
"time"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testlog"
)
func
TestChallenger_OutputProposed_Signature
(
t
*
testing
.
T
)
{
computed
:=
crypto
.
Keccak256Hash
([]
byte
(
"OutputProposed(bytes32,uint256,uint256,uint256)"
))
challenger
:=
newTestChallenger
(
t
,
eth
.
OutputResponse
{},
true
)
expected
:=
challenger
.
l2ooABI
.
Events
[
"OutputProposed"
]
.
ID
require
.
Equal
(
t
,
expected
,
computed
)
}
func
TestParseOutputLog_Succeeds
(
t
*
testing
.
T
)
{
challenger
:=
newTestChallenger
(
t
,
eth
.
OutputResponse
{},
true
)
expectedBlockNumber
:=
big
.
NewInt
(
0x04
)
expectedOutputRoot
:=
[
32
]
byte
{
0x02
}
logTopic
:=
challenger
.
l2ooABI
.
Events
[
"OutputProposed"
]
.
ID
log
:=
types
.
Log
{
Topics
:
[]
common
.
Hash
{
logTopic
,
common
.
Hash
(
expectedOutputRoot
),
{
0x03
},
common
.
BigToHash
(
expectedBlockNumber
)},
}
outputProposal
,
err
:=
challenger
.
ParseOutputLog
(
&
log
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expectedBlockNumber
,
outputProposal
.
L2BlockNumber
)
require
.
Equal
(
t
,
expectedOutputRoot
,
outputProposal
.
OutputRoot
)
}
func
TestParseOutputLog_WrongLogTopic_Errors
(
t
*
testing
.
T
)
{
challenger
:=
newTestChallenger
(
t
,
eth
.
OutputResponse
{},
true
)
_
,
err
:=
challenger
.
ParseOutputLog
(
&
types
.
Log
{
Topics
:
[]
common
.
Hash
{{
0x01
},
{
0x02
},
{
0x03
},
{
0x04
}},
})
require
.
ErrorIs
(
t
,
err
,
ErrInvalidOutputLogTopic
)
}
func
TestParseOutputLog_WrongTopicLength_Errors
(
t
*
testing
.
T
)
{
challenger
:=
newTestChallenger
(
t
,
eth
.
OutputResponse
{},
true
)
logTopic
:=
challenger
.
l2ooABI
.
Events
[
"OutputProposed"
]
.
ID
_
,
err
:=
challenger
.
ParseOutputLog
(
&
types
.
Log
{
Topics
:
[]
common
.
Hash
{
logTopic
,
{
0x02
},
{
0x03
}},
})
require
.
ErrorIs
(
t
,
err
,
ErrInvalidOutputTopicLength
)
}
func
TestChallenger_ValidateOutput_RollupClientErrors
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
supportedL2OutputVersion
,
OutputRoot
:
eth
.
Bytes32
{},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
true
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
0
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
received
,
err
:=
challenger
.
ValidateOutput
(
context
.
Background
(),
checked
)
require
.
False
(
t
,
valid
)
require
.
Equal
(
t
,
eth
.
Bytes32
{},
received
)
require
.
ErrorIs
(
t
,
err
,
mockOutputApiError
)
}
func
TestChallenger_ValidateOutput_ErrorsWithWrongVersion
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
eth
.
Bytes32
{
0x01
},
OutputRoot
:
eth
.
Bytes32
{
0x01
},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
false
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
0
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
received
,
err
:=
challenger
.
ValidateOutput
(
context
.
Background
(),
checked
)
require
.
False
(
t
,
valid
)
require
.
Equal
(
t
,
eth
.
Bytes32
{},
received
)
require
.
ErrorIs
(
t
,
err
,
ErrUnsupportedL2OOVersion
)
}
func
TestChallenger_ValidateOutput_ErrorsInvalidBlockNumber
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
supportedL2OutputVersion
,
OutputRoot
:
eth
.
Bytes32
{
0x01
},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
false
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
1
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
received
,
err
:=
challenger
.
ValidateOutput
(
context
.
Background
(),
checked
)
require
.
False
(
t
,
valid
)
require
.
Equal
(
t
,
eth
.
Bytes32
{},
received
)
require
.
ErrorIs
(
t
,
err
,
ErrInvalidBlockNumber
)
}
func
TestOutput_ValidateOutput
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
eth
.
Bytes32
{},
OutputRoot
:
eth
.
Bytes32
{},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
false
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
0
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
expected
,
err
:=
challenger
.
ValidateOutput
(
context
.
Background
(),
checked
)
require
.
Equal
(
t
,
expected
,
output
.
OutputRoot
)
require
.
True
(
t
,
valid
)
require
.
NoError
(
t
,
err
)
}
func
TestChallenger_CompareOutputRoots_ErrorsWithDifferentRoots
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
eth
.
Bytes32
{
0xFF
,
0xFF
,
0xFF
,
0xFF
},
OutputRoot
:
eth
.
Bytes32
{},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
false
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
0
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
err
:=
challenger
.
compareOutputRoots
(
&
output
,
checked
)
require
.
False
(
t
,
valid
)
require
.
ErrorIs
(
t
,
err
,
ErrUnsupportedL2OOVersion
)
}
func
TestChallenger_CompareOutputRoots_ErrInvalidBlockNumber
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
supportedL2OutputVersion
,
OutputRoot
:
eth
.
Bytes32
{},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
false
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
1
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
err
:=
challenger
.
compareOutputRoots
(
&
output
,
checked
)
require
.
False
(
t
,
valid
)
require
.
ErrorIs
(
t
,
err
,
ErrInvalidBlockNumber
)
}
func
TestChallenger_CompareOutputRoots_Succeeds
(
t
*
testing
.
T
)
{
output
:=
eth
.
OutputResponse
{
Version
:
supportedL2OutputVersion
,
OutputRoot
:
eth
.
Bytes32
{},
BlockRef
:
eth
.
L2BlockRef
{},
}
challenger
:=
newTestChallenger
(
t
,
output
,
false
)
checked
:=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
0
),
OutputRoot
:
output
.
OutputRoot
,
}
valid
,
err
:=
challenger
.
compareOutputRoots
(
&
output
,
checked
)
require
.
True
(
t
,
valid
)
require
.
NoError
(
t
,
err
)
checked
=
bindings
.
TypesOutputProposal
{
L2BlockNumber
:
big
.
NewInt
(
0
),
OutputRoot
:
eth
.
Bytes32
{
0x01
},
}
valid
,
err
=
challenger
.
compareOutputRoots
(
&
output
,
checked
)
require
.
False
(
t
,
valid
)
require
.
NoError
(
t
,
err
)
}
func
newTestChallenger
(
t
*
testing
.
T
,
output
eth
.
OutputResponse
,
errors
bool
)
*
Challenger
{
outputApi
:=
newMockOutputApi
(
output
,
errors
)
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
metr
:=
metrics
.
NewMetrics
(
"test"
)
parsedL2oo
,
err
:=
bindings
.
L2OutputOracleMetaData
.
GetAbi
()
require
.
NoError
(
t
,
err
)
challenger
:=
Challenger
{
rollupClient
:
outputApi
,
log
:
log
,
metr
:
metr
,
networkTimeout
:
time
.
Duration
(
5
)
*
time
.
Second
,
l2ooABI
:
parsedL2oo
,
}
return
&
challenger
}
var
mockOutputApiError
=
errors
.
New
(
"mock output api error"
)
type
mockOutputApi
struct
{
mock
.
Mock
expected
eth
.
OutputResponse
errors
bool
}
func
newMockOutputApi
(
output
eth
.
OutputResponse
,
errors
bool
)
*
mockOutputApi
{
return
&
mockOutputApi
{
expected
:
output
,
errors
:
errors
,
}
}
func
(
m
*
mockOutputApi
)
OutputAtBlock
(
ctx
context
.
Context
,
blockNumber
uint64
)
(
*
eth
.
OutputResponse
,
error
)
{
if
m
.
errors
{
return
nil
,
mockOutputApiError
}
return
&
m
.
expected
,
nil
}
op-challenger/challenger/subscription.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
// SubscriptionId is a unique subscription ID.
type
SubscriptionId
uint64
// Increment returns the next subscription ID.
func
(
s
*
SubscriptionId
)
Increment
()
SubscriptionId
{
*
s
++
return
*
s
}
// Subscription wraps an [ethereum.Subscription] to provide a restart.
type
Subscription
struct
{
// The subscription ID
id
SubscriptionId
// The current subscription
sub
ethereum
.
Subscription
// If the subscription is started
started
bool
// The query used to create the subscription
query
ethereum
.
FilterQuery
// The log channel
logs
chan
types
.
Log
// The quit channel
quit
chan
struct
{}
// Filter client used to open the log subscription
client
ethereum
.
LogFilterer
// Logger
log
log
.
Logger
}
// NewSubscription creates a new subscription.
func
NewSubscription
(
query
ethereum
.
FilterQuery
,
client
ethereum
.
LogFilterer
,
log
log
.
Logger
)
*
Subscription
{
return
&
Subscription
{
id
:
SubscriptionId
(
0
),
sub
:
nil
,
started
:
false
,
query
:
query
,
logs
:
make
(
chan
types
.
Log
),
quit
:
make
(
chan
struct
{}),
client
:
client
,
log
:
log
,
}
}
// ID returns the subscription ID.
func
(
s
*
Subscription
)
ID
()
SubscriptionId
{
return
s
.
id
}
// Started returns true if the subscription has started.
func
(
s
*
Subscription
)
Started
()
bool
{
return
s
.
started
}
// Logs returns the log channel.
func
(
s
*
Subscription
)
Logs
()
<-
chan
types
.
Log
{
return
s
.
logs
}
// Subscribe constructs the subscription.
func
(
s
*
Subscription
)
Subscribe
()
error
{
s
.
log
.
Info
(
"Subscribing to"
,
"query"
,
s
.
query
.
Topics
,
"id"
,
s
.
id
)
sub
,
err
:=
s
.
client
.
SubscribeFilterLogs
(
context
.
Background
(),
s
.
query
,
s
.
logs
)
if
err
!=
nil
{
s
.
log
.
Error
(
"failed to subscribe to logs"
,
"err"
,
err
)
return
err
}
s
.
sub
=
sub
s
.
started
=
true
return
nil
}
// Quit closes the subscription.
func
(
s
*
Subscription
)
Quit
()
{
s
.
log
.
Info
(
"Quitting subscription"
,
"id"
,
s
.
id
)
s
.
sub
.
Unsubscribe
()
s
.
quit
<-
struct
{}{}
s
.
started
=
false
s
.
log
.
Info
(
"Quit subscription"
,
"id"
,
s
.
id
)
}
op-challenger/challenger/subscription_test.go
deleted
100644 → 0
View file @
1a95d061
package
challenger
import
(
"context"
"errors"
"math"
"testing"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/stretchr/testify/require"
)
type
mockLogFilterClient
struct
{}
func
(
m
mockLogFilterClient
)
FilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
)
([]
types
.
Log
,
error
)
{
panic
(
"this should not be called by the Subscription.Subscribe method"
)
}
func
(
m
mockLogFilterClient
)
SubscribeFilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
,
chan
<-
types
.
Log
)
(
ethereum
.
Subscription
,
error
)
{
return
nil
,
nil
}
func
newSubscription
(
t
*
testing
.
T
,
client
*
mockLogFilterClient
)
(
*
Subscription
,
*
mockLogFilterClient
)
{
query
:=
ethereum
.
FilterQuery
{}
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
return
NewSubscription
(
query
,
client
,
log
),
client
}
func
FuzzSubscriptionId_Increment
(
f
*
testing
.
F
)
{
maxUint64
:=
uint64
(
math
.
MaxUint64
)
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
id
uint64
)
{
if
id
>=
maxUint64
{
t
.
Skip
(
"skipping due to overflow"
)
}
else
{
subId
:=
SubscriptionId
(
id
)
require
.
Equal
(
t
,
subId
.
Increment
(),
SubscriptionId
(
id
+
1
))
}
})
}
func
TestSubscription_Subscribe_NilClient_Panics
(
t
*
testing
.
T
)
{
defer
func
()
{
if
recover
()
==
nil
{
t
.
Error
(
"expected nil client to panic"
)
}
}()
subscription
,
_
:=
newSubscription
(
t
,
nil
)
require
.
NoError
(
t
,
subscription
.
Subscribe
())
}
func
TestSubscription_Subscribe
(
t
*
testing
.
T
)
{
subscription
,
_
:=
newSubscription
(
t
,
&
mockLogFilterClient
{})
require
.
NoError
(
t
,
subscription
.
Subscribe
())
require
.
True
(
t
,
subscription
.
Started
())
}
var
ErrSubscriptionFailed
=
errors
.
New
(
"failed to subscribe to logs"
)
type
errLogFilterClient
struct
{}
func
(
m
errLogFilterClient
)
FilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
)
([]
types
.
Log
,
error
)
{
panic
(
"this should not be called by the Subscription.Subscribe method"
)
}
func
(
m
errLogFilterClient
)
SubscribeFilterLogs
(
context
.
Context
,
ethereum
.
FilterQuery
,
chan
<-
types
.
Log
)
(
ethereum
.
Subscription
,
error
)
{
return
nil
,
ErrSubscriptionFailed
}
func
TestSubscription_Subscribe_SubscriptionErrors
(
t
*
testing
.
T
)
{
query
:=
ethereum
.
FilterQuery
{}
log
:=
testlog
.
Logger
(
t
,
log
.
LvlError
)
subscription
:=
Subscription
{
query
:
query
,
client
:
errLogFilterClient
{},
log
:
log
,
}
require
.
EqualError
(
t
,
subscription
.
Subscribe
(),
ErrSubscriptionFailed
.
Error
())
}
op-challenger/cmd/entrypoint.go
deleted
100644 → 0
View file @
1a95d061
package
main
import
(
"context"
"fmt"
_
"net/http/pprof"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
"github.com/ethereum-optimism/optimism/op-service/opio"
"github.com/ethereum-optimism/optimism/op-challenger/challenger"
"github.com/ethereum-optimism/optimism/op-service/pprof"
"github.com/ethereum-optimism/optimism/op-service/rpc"
)
// Main is the entrypoint into the Challenger. This method executes the
// service and blocks until the service exits.
func
Main
(
logger
log
.
Logger
,
version
string
,
cfg
*
config
.
Config
)
error
{
if
err
:=
cfg
.
Check
();
err
!=
nil
{
return
fmt
.
Errorf
(
"invalid config: %w"
,
err
)
}
m
:=
metrics
.
NewMetrics
(
"default"
)
logger
.
Info
(
"Initializing Challenger"
)
service
,
err
:=
challenger
.
NewChallenger
(
*
cfg
,
logger
,
m
)
if
err
!=
nil
{
logger
.
Error
(
"Unable to create the Challenger"
,
"error"
,
err
)
return
err
}
logger
.
Info
(
"Starting Challenger"
)
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
if
err
:=
service
.
Start
();
err
!=
nil
{
cancel
()
logger
.
Error
(
"Unable to start Challenger"
,
"error"
,
err
)
return
err
}
defer
service
.
Stop
()
logger
.
Info
(
"Challenger started"
)
pprofConfig
:=
cfg
.
PprofConfig
if
pprofConfig
.
Enabled
{
logger
.
Info
(
"starting pprof"
,
"addr"
,
pprofConfig
.
ListenAddr
,
"port"
,
pprofConfig
.
ListenPort
)
go
func
()
{
if
err
:=
pprof
.
ListenAndServe
(
ctx
,
pprofConfig
.
ListenAddr
,
pprofConfig
.
ListenPort
);
err
!=
nil
{
logger
.
Error
(
"error starting pprof"
,
"err"
,
err
)
}
}()
}
metricsCfg
:=
cfg
.
MetricsConfig
if
metricsCfg
.
Enabled
{
log
.
Info
(
"starting metrics server"
,
"addr"
,
metricsCfg
.
ListenAddr
,
"port"
,
metricsCfg
.
ListenPort
)
go
func
()
{
if
err
:=
m
.
Serve
(
ctx
,
metricsCfg
.
ListenAddr
,
metricsCfg
.
ListenPort
);
err
!=
nil
{
logger
.
Error
(
"error starting metrics server"
,
err
)
}
}()
m
.
StartBalanceMetrics
(
ctx
,
logger
,
service
.
Client
(),
service
.
From
())
}
rpcCfg
:=
cfg
.
RPCConfig
server
:=
rpc
.
NewServer
(
rpcCfg
.
ListenAddr
,
rpcCfg
.
ListenPort
,
version
,
rpc
.
WithLogger
(
logger
))
if
err
:=
server
.
Start
();
err
!=
nil
{
cancel
()
return
fmt
.
Errorf
(
"error starting RPC server: %w"
,
err
)
}
m
.
RecordInfo
(
version
)
m
.
RecordUp
()
opio
.
BlockOnInterrupts
()
cancel
()
return
nil
}
op-challenger/cmd/main.go
View file @
9dd47648
...
...
@@ -3,14 +3,12 @@ package main
import
(
"os"
log
"github.com/ethereum/go-ethereum/log"
cli
"github.com/urfave/cli/v2"
watch
"github.com/ethereum-optimism/optimism/op-challenger/cmd/watch"
config
"github.com/ethereum-optimism/optimism/op-challenger/config"
flags
"github.com/ethereum-optimism/optimism/op-challenger/flags"
version
"github.com/ethereum-optimism/optimism/op-challenger/version"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
"github.com/ethereum-optimism/optimism/op-challenger/version"
oplog
"github.com/ethereum-optimism/optimism/op-service/log"
)
...
...
@@ -35,47 +33,33 @@ var VersionWithMeta = func() string {
}()
func
main
()
{
args
:=
os
.
Args
if
err
:=
run
(
args
,
Main
);
err
!=
nil
{
log
.
Crit
(
"Application failed"
,
"err"
,
err
)
}
}
type
ConfigAction
func
(
log
log
.
Logger
,
version
string
,
config
*
config
.
Config
)
error
// run parses the supplied args to create a config.Config instance, sets up logging
// then calls the supplied ConfigAction.
// This allows testing the translation from CLI arguments to Config
func
run
(
args
[]
string
,
action
ConfigAction
)
error
{
// Set up logger with a default INFO level in case we fail to parse flags,
// otherwise the final critical log won't show what the parsing error was.
oplog
.
SetupDefaults
()
app
:=
cli
.
NewApp
()
app
.
Version
=
VersionWithMeta
app
.
Flags
=
flags
.
Flags
app
.
Name
=
"op-challenger"
app
.
Usage
=
"Challenge
Invalid L2OutputOracle O
utputs"
app
.
Description
=
"
A modular op-stack challenge agent for dispute games written in golang
."
app
.
Usage
=
"Challenge
o
utputs"
app
.
Description
=
"
Ensures that on chain outputs are correct
."
app
.
Action
=
func
(
ctx
*
cli
.
Context
)
error
{
logger
,
err
:=
config
.
LoggerFromCLI
(
ctx
)
if
err
!=
nil
{
return
err
}
logger
.
Info
(
"Starting challenger"
,
"version"
,
VersionWithMeta
)
cfg
,
err
:=
config
.
NewConfigFromCLI
(
ctx
)
if
err
!=
nil
{
return
err
}
return
action
(
logger
,
VersionWithMeta
,
cfg
)
return
FaultGame
(
VersionWithMeta
,
ctx
)
}
app
.
Commands
=
[]
*
cli
.
Command
{
{
Name
:
"watch"
,
Subcommands
:
watch
.
Subcommands
,
},
if
err
:=
app
.
Run
(
os
.
Args
);
err
!=
nil
{
log
.
Crit
(
"Application failed"
,
"message"
,
err
)
}
}
type
ConfigAction
func
(
log
log
.
Logger
,
version
string
,
config
*
config
.
Config
)
error
return
app
.
Run
(
args
)
func
FaultGame
(
version
string
,
cliCtx
*
cli
.
Context
)
error
{
cfg
,
err
:=
config
.
NewConfigFromCLI
(
cliCtx
)
if
err
!=
nil
{
return
err
}
if
err
:=
cfg
.
Check
();
err
!=
nil
{
return
err
}
log
:=
oplog
.
NewLogger
(
cfg
.
LogConfig
)
log
.
Info
(
"Fault game started"
)
return
nil
}
op-challenger/cmd/watch/cmd.go
deleted
100644 → 0
View file @
1a95d061
package
watch
import
(
"github.com/urfave/cli/v2"
"github.com/ethereum-optimism/optimism/op-challenger/config"
)
var
Subcommands
=
cli
.
Commands
{
{
Name
:
"oracle"
,
Usage
:
"Watches the L2OutputOracle for new output proposals"
,
Action
:
func
(
ctx
*
cli
.
Context
)
error
{
logger
,
err
:=
config
.
LoggerFromCLI
(
ctx
)
if
err
!=
nil
{
return
err
}
logger
.
Info
(
"Listening for new output proposals"
)
cfg
,
err
:=
config
.
NewConfigFromCLI
(
ctx
)
if
err
!=
nil
{
return
err
}
return
Oracle
(
logger
,
cfg
)
},
},
{
Name
:
"factory"
,
Usage
:
"Watches the DisputeGameFactory for new dispute games"
,
Action
:
func
(
ctx
*
cli
.
Context
)
error
{
logger
,
err
:=
config
.
LoggerFromCLI
(
ctx
)
if
err
!=
nil
{
return
err
}
logger
.
Info
(
"Listening for new dispute games"
)
cfg
,
err
:=
config
.
NewConfigFromCLI
(
ctx
)
if
err
!=
nil
{
return
err
}
return
Factory
(
logger
,
cfg
)
},
},
}
op-challenger/cmd/watch/factory.go
deleted
100644 → 0
View file @
1a95d061
package
watch
import
(
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/challenger"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
)
// Factory listens to the DisputeGameFactory for newly created dispute games.
func
Factory
(
logger
log
.
Logger
,
cfg
*
config
.
Config
)
error
{
if
err
:=
cfg
.
Check
();
err
!=
nil
{
return
fmt
.
Errorf
(
"invalid config: %w"
,
err
)
}
m
:=
metrics
.
NewMetrics
(
"default"
)
service
,
err
:=
challenger
.
NewChallenger
(
*
cfg
,
logger
,
m
)
if
err
!=
nil
{
logger
.
Error
(
"Unable to create the Challenger"
,
"error"
,
err
)
return
err
}
logger
.
Info
(
"Listening for DisputeGameCreated events from the DisputeGameFactory contract"
,
"dgf"
,
cfg
.
DGFAddress
.
String
())
subscription
,
err
:=
service
.
NewFactorySubscription
()
if
err
!=
nil
{
logger
.
Error
(
"Unable to create the subscription"
,
"error"
,
err
)
return
err
}
err
=
subscription
.
Subscribe
()
if
err
!=
nil
{
logger
.
Error
(
"Unable to subscribe to the DisputeGameFactory contract"
,
"error"
,
err
)
return
err
}
defer
subscription
.
Quit
()
interruptChannel
:=
make
(
chan
os
.
Signal
,
1
)
signal
.
Notify
(
interruptChannel
,
[]
os
.
Signal
{
os
.
Interrupt
,
os
.
Kill
,
syscall
.
SIGTERM
,
syscall
.
SIGQUIT
,
}
...
)
for
{
select
{
case
log
:=
<-
subscription
.
Logs
()
:
logger
.
Info
(
"Received log"
,
"log"
,
log
)
case
<-
interruptChannel
:
logger
.
Info
(
"Received interrupt signal, exiting..."
)
}
}
}
op-challenger/cmd/watch/oracle.go
deleted
100644 → 0
View file @
1a95d061
package
watch
import
(
"fmt"
"os"
"os/signal"
"syscall"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-challenger/challenger"
"github.com/ethereum-optimism/optimism/op-challenger/config"
"github.com/ethereum-optimism/optimism/op-challenger/metrics"
)
// Oracle listens to the L2OutputOracle for newly proposed outputs.
func
Oracle
(
logger
log
.
Logger
,
cfg
*
config
.
Config
)
error
{
if
err
:=
cfg
.
Check
();
err
!=
nil
{
return
fmt
.
Errorf
(
"invalid config: %w"
,
err
)
}
m
:=
metrics
.
NewMetrics
(
"default"
)
service
,
err
:=
challenger
.
NewChallenger
(
*
cfg
,
logger
,
m
)
if
err
!=
nil
{
logger
.
Error
(
"Unable to create the Challenger"
,
"error"
,
err
)
return
err
}
logger
.
Info
(
"Listening for OutputProposed events from the L2OutputOracle contract"
,
"l2oo"
,
cfg
.
L2OOAddress
.
String
())
subscription
,
err
:=
service
.
NewOracleSubscription
()
if
err
!=
nil
{
logger
.
Error
(
"Unable to create the subscription"
,
"error"
,
err
)
return
err
}
err
=
subscription
.
Subscribe
()
if
err
!=
nil
{
logger
.
Error
(
"Unable to subscribe to the L2OutputOracle contract"
,
"error"
,
err
)
return
err
}
defer
subscription
.
Quit
()
interruptChannel
:=
make
(
chan
os
.
Signal
,
1
)
signal
.
Notify
(
interruptChannel
,
[]
os
.
Signal
{
os
.
Interrupt
,
os
.
Kill
,
syscall
.
SIGTERM
,
syscall
.
SIGQUIT
,
}
...
)
for
{
select
{
case
log
:=
<-
subscription
.
Logs
()
:
logger
.
Info
(
"Received log"
,
"log"
,
log
)
case
<-
interruptChannel
:
logger
.
Info
(
"Received interrupt signal, exiting..."
)
}
}
}
op-challenger/config/config.go
View file @
9dd47648
...
...
@@ -2,94 +2,70 @@ package config
import
(
"errors"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2"
flags
"github.com/ethereum-optimism/optimism/op-challenger/flags"
"github.com/ethereum-optimism/optimism/op-challenger/flags"
opservice
"github.com/ethereum-optimism/optimism/op-service"
oplog
"github.com/ethereum-optimism/optimism/op-service/log"
opmetrics
"github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof
"github.com/ethereum-optimism/optimism/op-service/pprof"
oprpc
"github.com/ethereum-optimism/optimism/op-service/rpc"
txmgr
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
)
var
(
ErrMissingL1EthRPC
=
errors
.
New
(
"missing l1 eth rpc url"
)
ErrMissingRollupRpc
=
errors
.
New
(
"missing rollup rpc url"
)
ErrMissingL2OOAddress
=
errors
.
New
(
"missing l2 output oracle contract address"
)
ErrMissingDGFAddress
=
errors
.
New
(
"missing dispute game factory contract address"
)
ErrInvalidNetworkTimeout
=
errors
.
New
(
"invalid network timeout"
)
ErrMissingTxMgrConfig
=
errors
.
New
(
"missing tx manager config"
)
ErrMissingRPCConfig
=
errors
.
New
(
"missing rpc config"
)
ErrMissingLogConfig
=
errors
.
New
(
"missing log config"
)
ErrMissingMetricsConfig
=
errors
.
New
(
"missing metrics config"
)
ErrMissingPprofConfig
=
errors
.
New
(
"missing pprof config"
)
ErrMissingL1EthRPC
=
errors
.
New
(
"missing l1 eth rpc url"
)
ErrMissingGameAddress
=
errors
.
New
(
"missing game address"
)
ErrMissingAlphabetTrace
=
errors
.
New
(
"missing alphabet trace"
)
)
// Config is a well typed config that is parsed from the CLI params.
// This also contains config options for auxiliary services.
// It is used to initialize the challenger.
type
Config
struct
{
// L1EthRpc is the HTTP provider URL for L1.
L1EthRpc
string
// RollupRpc is the HTTP provider URL for the rollup node.
RollupRpc
string
// L2OOAddress is the L2OutputOracle contract address.
L2OOAddress
common
.
Address
// DGFAddress is the DisputeGameFactory contract address.
DGFAddress
common
.
Address
// NetworkTimeout is the timeout for network requests.
NetworkTimeout
time
.
Duration
TxMgrConfig
*
txmgr
.
CLIConfig
RPCConfig
*
oprpc
.
CLIConfig
LogConfig
*
oplog
.
CLIConfig
MetricsConfig
*
opmetrics
.
CLIConfig
L1EthRpc
string
// L1 RPC Url
GameAddress
common
.
Address
// Address of the fault game
AlphabetTrace
string
// String for the AlphabetTraceProvider
TxMgrConfig
txmgr
.
CLIConfig
RPCConfig
oprpc
.
CLIConfig
LogConfig
oplog
.
CLIConfig
MetricsConfig
opmetrics
.
CLIConfig
PprofConfig
oppprof
.
CLIConfig
}
PprofConfig
*
oppprof
.
CLIConfig
func
NewConfig
(
L1EthRpc
string
,
GameAddress
common
.
Address
,
AlphabetTrace
string
,
TxMgrConfig
txmgr
.
CLIConfig
,
RPCConfig
oprpc
.
CLIConfig
,
LogConfig
oplog
.
CLIConfig
,
MetricsConfig
opmetrics
.
CLIConfig
,
PprofConfig
oppprof
.
CLIConfig
,
)
Config
{
return
Config
{
L1EthRpc
,
GameAddress
,
AlphabetTrace
,
TxMgrConfig
,
RPCConfig
,
LogConfig
,
MetricsConfig
,
PprofConfig
,
}
}
func
(
c
Config
)
Check
()
error
{
if
c
.
L1EthRpc
==
""
{
return
ErrMissingL1EthRPC
}
if
c
.
RollupRpc
==
""
{
return
ErrMissingRollupRpc
}
if
c
.
L2OOAddress
==
(
common
.
Address
{})
{
return
ErrMissingL2OOAddress
}
if
c
.
DGFAddress
==
(
common
.
Address
{})
{
return
ErrMissingDGFAddress
}
if
c
.
NetworkTimeout
==
0
{
return
ErrInvalidNetworkTimeout
if
c
.
GameAddress
==
(
common
.
Address
{})
{
return
ErrMissingGameAddress
}
if
c
.
TxMgrConfig
==
nil
{
return
ErrMissingTxMgrConfig
}
if
c
.
RPCConfig
==
nil
{
return
ErrMissingRPCConfig
}
if
c
.
LogConfig
==
nil
{
return
ErrMissingLogConfig
}
if
c
.
MetricsConfig
==
nil
{
return
ErrMissingMetricsConfig
}
if
c
.
PprofConfig
==
nil
{
return
ErrMissingPprofConfig
if
c
.
AlphabetTrace
==
""
{
return
ErrMissingAlphabetTrace
}
if
err
:=
c
.
RPCConfig
.
Check
();
err
!=
nil
{
return
err
...
...
@@ -109,53 +85,14 @@ func (c Config) Check() error {
return
nil
}
// NewConfig creates a Config with all optional values set to the CLI default value
func
NewConfig
(
L1EthRpc
string
,
RollupRpc
string
,
L2OOAddress
common
.
Address
,
DGFAddress
common
.
Address
,
NetworkTimeout
time
.
Duration
,
TxMgrConfig
*
txmgr
.
CLIConfig
,
RPCConfig
*
oprpc
.
CLIConfig
,
LogConfig
*
oplog
.
CLIConfig
,
MetricsConfig
*
opmetrics
.
CLIConfig
,
PprofConfig
*
oppprof
.
CLIConfig
,
)
*
Config
{
return
&
Config
{
L1EthRpc
:
L1EthRpc
,
RollupRpc
:
RollupRpc
,
L2OOAddress
:
L2OOAddress
,
DGFAddress
:
DGFAddress
,
NetworkTimeout
:
NetworkTimeout
,
TxMgrConfig
:
TxMgrConfig
,
RPCConfig
:
RPCConfig
,
LogConfig
:
LogConfig
,
MetricsConfig
:
MetricsConfig
,
PprofConfig
:
PprofConfig
,
}
}
// 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
}
l1EthRpc
:=
ctx
.
String
(
flags
.
L1EthRpcFlag
.
Name
)
if
l1EthRpc
==
""
{
return
nil
,
ErrMissingL1EthRPC
}
rollupRpc
:=
ctx
.
String
(
flags
.
RollupRpcFlag
.
Name
)
if
rollupRpc
==
""
{
return
nil
,
ErrMissingRollupRpc
}
l2ooAddress
,
err
:=
opservice
.
ParseAddress
(
ctx
.
String
(
flags
.
L2OOAddressFlag
.
Name
))
if
err
!=
nil
{
return
nil
,
ErrMissingL2OOAddress
}
dgfAddress
,
err
:=
opservice
.
ParseAddress
(
ctx
.
String
(
flags
.
DGFAddressFlag
.
Name
))
if
err
!=
nil
{
return
nil
,
ErrMissingDGFAddress
return
nil
,
err
}
txMgrConfig
:=
txmgr
.
ReadCLIConfig
(
ctx
)
...
...
@@ -166,15 +103,14 @@ func NewConfigFromCLI(ctx *cli.Context) (*Config, error) {
return
&
Config
{
// Required Flags
L1EthRpc
:
l1EthRpc
,
RollupRpc
:
rollupRpc
,
L2OOAddress
:
l2ooAddress
,
DGFAddress
:
dgfAddress
,
TxMgrConfig
:
&
txMgrConfig
,
L1EthRpc
:
ctx
.
String
(
flags
.
L1EthRpcFlag
.
Name
),
GameAddress
:
dgfAddress
,
AlphabetTrace
:
ctx
.
String
(
flags
.
AlphabetFlag
.
Name
),
TxMgrConfig
:
txMgrConfig
,
// Optional Flags
RPCConfig
:
&
rpcConfig
,
LogConfig
:
&
logConfig
,
MetricsConfig
:
&
metricsConfig
,
PprofConfig
:
&
pprofConfig
,
RPCConfig
:
rpcConfig
,
LogConfig
:
logConfig
,
MetricsConfig
:
metricsConfig
,
PprofConfig
:
pprofConfig
,
},
nil
}
op-challenger/config/config_test.go
View file @
9dd47648
...
...
@@ -8,8 +8,8 @@ import (
opmetrics
"github.com/ethereum-optimism/optimism/op-service/metrics"
oppprof
"github.com/ethereum-optimism/optimism/op-service/pprof"
oprpc
"github.com/ethereum-optimism/optimism/op-service/rpc"
txmgr
"github.com/ethereum-optimism/optimism/op-service/txmgr"
client
"github.com/ethereum-optimism/optimism/op-signer/client"
"github.com/ethereum-optimism/optimism/op-service/txmgr"
"github.com/ethereum-optimism/optimism/op-signer/client"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
...
...
@@ -17,10 +17,9 @@ import (
var
(
validL1EthRpc
=
"http://localhost:8545"
validRollupRpc
=
"http://localhost:8546"
validL2OOAddress
=
common
.
HexToAddress
(
"0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139"
)
validDGFAddress
=
common
.
HexToAddress
(
"0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139"
)
validGameAddress
=
common
.
HexToAddress
(
"0x7bdd3b028C4796eF0EAf07d11394d0d9d8c24139"
)
validNetworkTimeout
=
time
.
Duration
(
5
)
*
time
.
Second
validAlphabetTrace
=
"abcdefgh"
)
var
validTxMgrConfig
=
txmgr
.
CLIConfig
{
...
...
@@ -53,19 +52,15 @@ var validPprofConfig = oppprof.CLIConfig{
Enabled
:
false
,
}
func
validConfig
()
*
Config
{
cfg
:=
NewConfig
(
validL1EthRpc
,
validRollupRpc
,
validL2OOAddress
,
validDGFAddress
,
validNetworkTimeout
,
&
validTxMgrConfig
,
&
validRPCConfig
,
&
validLogConfig
,
&
validMetricsConfig
,
&
validPprofConfig
,
)
func
validConfig
()
Config
{
cfg
:=
NewConfig
(
validL1EthRpc
,
validGameAddress
,
validAlphabetTrace
,
validTxMgrConfig
,
validRPCConfig
,
validLogConfig
,
validMetricsConfig
,
validPprofConfig
)
return
cfg
}
...
...
@@ -76,16 +71,9 @@ func TestValidConfigIsValid(t *testing.T) {
}
func
TestTxMgrConfig
(
t
*
testing
.
T
)
{
t
.
Run
(
"Required"
,
func
(
t
*
testing
.
T
)
{
config
:=
validConfig
()
config
.
TxMgrConfig
=
nil
err
:=
config
.
Check
()
require
.
ErrorIs
(
t
,
err
,
ErrMissingTxMgrConfig
)
})
t
.
Run
(
"Invalid"
,
func
(
t
*
testing
.
T
)
{
config
:=
validConfig
()
config
.
TxMgrConfig
=
&
txmgr
.
CLIConfig
{}
config
.
TxMgrConfig
=
txmgr
.
CLIConfig
{}
err
:=
config
.
Check
()
require
.
Equal
(
t
,
err
.
Error
(),
"must provide a L1 RPC url"
)
})
...
...
@@ -98,30 +86,16 @@ func TestL1EthRpcRequired(t *testing.T) {
require
.
ErrorIs
(
t
,
err
,
ErrMissingL1EthRPC
)
}
func
TestRollupRpcRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
()
config
.
RollupRpc
=
""
err
:=
config
.
Check
()
require
.
ErrorIs
(
t
,
err
,
ErrMissingRollupRpc
)
}
func
TestL2OOAddressRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
()
config
.
L2OOAddress
=
common
.
Address
{}
err
:=
config
.
Check
()
require
.
ErrorIs
(
t
,
err
,
ErrMissingL2OOAddress
)
}
func
TestDGFAddressRequired
(
t
*
testing
.
T
)
{
func
TestGameAddressRequired
(
t
*
testing
.
T
)
{
config
:=
validConfig
()
config
.
DGF
Address
=
common
.
Address
{}
config
.
Game
Address
=
common
.
Address
{}
err
:=
config
.
Check
()
require
.
ErrorIs
(
t
,
err
,
ErrMissing
DGF
Address
)
require
.
ErrorIs
(
t
,
err
,
ErrMissing
Game
Address
)
}
func
Test
NetworkTimeout
Required
(
t
*
testing
.
T
)
{
func
Test
AlphabetTrace
Required
(
t
*
testing
.
T
)
{
config
:=
validConfig
()
config
.
NetworkTimeout
=
0
config
.
AlphabetTrace
=
""
err
:=
config
.
Check
()
require
.
ErrorIs
(
t
,
err
,
Err
InvalidNetworkTimeout
)
require
.
ErrorIs
(
t
,
err
,
Err
MissingAlphabetTrace
)
}
op-challenger/config/logging.go
deleted
100644 → 0
View file @
1a95d061
package
config
import
(
"fmt"
log
"github.com/ethereum/go-ethereum/log"
cli
"github.com/urfave/cli/v2"
oplog
"github.com/ethereum-optimism/optimism/op-service/log"
)
// LoggerFromCLI creates a [log.Logger] from the
// supplied [cli.Context].
func
LoggerFromCLI
(
ctx
*
cli
.
Context
)
(
log
.
Logger
,
error
)
{
logCfg
:=
oplog
.
ReadCLIConfig
(
ctx
)
if
err
:=
logCfg
.
Check
();
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"log config error: %w"
,
err
)
}
logger
:=
oplog
.
NewLogger
(
logCfg
)
return
logger
,
nil
}
op-challenger/flags/flags.go
View file @
9dd47648
...
...
@@ -26,29 +26,24 @@ var (
Usage
:
"HTTP provider URL for L1."
,
EnvVars
:
prefixEnvVars
(
"L1_ETH_RPC"
),
}
RollupRpcFlag
=
&
cli
.
StringFlag
{
Name
:
"rollup-rpc"
,
Usage
:
"HTTP provider URL for the rollup node."
,
EnvVars
:
prefixEnvVars
(
"ROLLUP_RPC"
),
}
L2OOAddressFlag
=
&
cli
.
StringFlag
{
Name
:
"l2oo-address"
,
Usage
:
"Address of the L2OutputOracle contract."
,
EnvVars
:
prefixEnvVars
(
"L2OO_ADDRESS"
),
}
DGFAddressFlag
=
&
cli
.
StringFlag
{
Name
:
"dgf-address"
,
Usage
:
"Address of the DisputeGameFactory contract."
,
EnvVars
:
prefixEnvVars
(
"DGF_ADDRESS"
),
Name
:
"game-address"
,
Usage
:
"Address of the Fault Game contract."
,
EnvVars
:
prefixEnvVars
(
"GAME_ADDRESS"
),
}
AlphabetFlag
=
&
cli
.
StringFlag
{
Name
:
"alphabet"
,
Usage
:
"Alphabet Trace (temporary)"
,
EnvVars
:
prefixEnvVars
(
"ALPHABET"
),
}
// Optional Flags
)
// requiredFlags are checked by [CheckRequired]
var
requiredFlags
=
[]
cli
.
Flag
{
L1EthRpcFlag
,
RollupRpcFlag
,
L2OOAddressFlag
,
DGFAddressFlag
,
AlphabetFlag
,
}
// optionalFlags is a list of unchecked cli flags
...
...
op-challenger/metrics/metrics.go
deleted
100644 → 0
View file @
1a95d061
package
metrics
import
(
"context"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus"
opmetrics
"github.com/ethereum-optimism/optimism/op-service/metrics"
txmetrics
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
)
const
Namespace
=
"op_challenger"
type
Metricer
interface
{
RecordInfo
(
version
string
)
RecordUp
()
// Records all L1 and L2 block events
opmetrics
.
RefMetricer
// Record Tx metrics
txmetrics
.
TxMetricer
RecordValidOutput
(
l2ref
eth
.
L2BlockRef
)
RecordInvalidOutput
(
l2ref
eth
.
L2BlockRef
)
RecordOutputChallenged
(
l2ref
eth
.
L2BlockRef
)
}
type
Metrics
struct
{
ns
string
registry
*
prometheus
.
Registry
factory
opmetrics
.
Factory
opmetrics
.
RefMetrics
txmetrics
.
TxMetrics
info
prometheus
.
GaugeVec
up
prometheus
.
Gauge
}
var
_
Metricer
=
(
*
Metrics
)(
nil
)
func
NewMetrics
(
procName
string
)
*
Metrics
{
if
procName
==
""
{
procName
=
"default"
}
ns
:=
Namespace
+
"_"
+
procName
registry
:=
opmetrics
.
NewRegistry
()
factory
:=
opmetrics
.
With
(
registry
)
return
&
Metrics
{
ns
:
ns
,
registry
:
registry
,
factory
:
factory
,
RefMetrics
:
opmetrics
.
MakeRefMetrics
(
ns
,
factory
),
TxMetrics
:
txmetrics
.
MakeTxMetrics
(
ns
,
factory
),
info
:
*
factory
.
NewGaugeVec
(
prometheus
.
GaugeOpts
{
Namespace
:
ns
,
Name
:
"info"
,
Help
:
"Pseudo-metric tracking version and config info"
,
},
[]
string
{
"version"
,
}),
up
:
factory
.
NewGauge
(
prometheus
.
GaugeOpts
{
Namespace
:
ns
,
Name
:
"up"
,
Help
:
"1 if the op-proposer has finished starting up"
,
}),
}
}
func
(
m
*
Metrics
)
Serve
(
ctx
context
.
Context
,
host
string
,
port
int
)
error
{
return
opmetrics
.
ListenAndServe
(
ctx
,
m
.
registry
,
host
,
port
)
}
func
(
m
*
Metrics
)
StartBalanceMetrics
(
ctx
context
.
Context
,
l
log
.
Logger
,
client
*
ethclient
.
Client
,
account
common
.
Address
)
{
opmetrics
.
LaunchBalanceMetrics
(
ctx
,
l
,
m
.
registry
,
m
.
ns
,
client
,
account
)
}
// RecordInfo sets a pseudo-metric that contains versioning and
// config info for the op-proposer.
func
(
m
*
Metrics
)
RecordInfo
(
version
string
)
{
m
.
info
.
WithLabelValues
(
version
)
.
Set
(
1
)
}
// RecordUp sets the up metric to 1.
func
(
m
*
Metrics
)
RecordUp
()
{
prometheus
.
MustRegister
()
m
.
up
.
Set
(
1
)
}
const
(
ValidOutput
=
"valid_output"
InvalidOutput
=
"invalid_output"
OutputChallenged
=
"output_challenged"
)
// RecordValidOutput should be called when a valid output is found
func
(
m
*
Metrics
)
RecordValidOutput
(
l2ref
eth
.
L2BlockRef
)
{
m
.
RecordL2Ref
(
ValidOutput
,
l2ref
)
}
// RecordInvalidOutput should be called when an invalid output is found
func
(
m
*
Metrics
)
RecordInvalidOutput
(
l2ref
eth
.
L2BlockRef
)
{
m
.
RecordL2Ref
(
InvalidOutput
,
l2ref
)
}
// RecordOutputChallenged should be called when an output is challenged
func
(
m
*
Metrics
)
RecordOutputChallenged
(
l2ref
eth
.
L2BlockRef
)
{
m
.
RecordL2Ref
(
OutputChallenged
,
l2ref
)
}
func
(
m
*
Metrics
)
Document
()
[]
opmetrics
.
DocumentedMetric
{
return
m
.
factory
.
Document
()
}
op-challenger/metrics/noop.go
deleted
100644 → 0
View file @
1a95d061
package
metrics
import
(
"github.com/ethereum-optimism/optimism/op-node/eth"
opmetrics
"github.com/ethereum-optimism/optimism/op-service/metrics"
txmetrics
"github.com/ethereum-optimism/optimism/op-service/txmgr/metrics"
)
type
noopMetrics
struct
{
opmetrics
.
NoopRefMetrics
txmetrics
.
NoopTxMetrics
}
var
NoopMetrics
Metricer
=
new
(
noopMetrics
)
func
(
*
noopMetrics
)
RecordInfo
(
version
string
)
{}
func
(
*
noopMetrics
)
RecordUp
()
{}
func
(
*
noopMetrics
)
RecordValidOutput
(
l2ref
eth
.
L2BlockRef
)
{}
func
(
*
noopMetrics
)
RecordInvalidOutput
(
l2ref
eth
.
L2BlockRef
)
{}
func
(
*
noopMetrics
)
RecordOutputChallenged
(
l2ref
eth
.
L2BlockRef
)
{}
op-challenger/types/game_type.go
deleted
100644 → 0
View file @
1a95d061
package
types
import
(
"fmt"
"github.com/ethereum-optimism/optimism/op-service/enum"
)
// GameType is the type of dispute game
type
GameType
uint8
// DefaultGameType returns the default dispute game type.
func
DefaultGameType
()
GameType
{
return
AttestationDisputeGameType
}
// String returns the string value of a dispute game type.
func
(
g
GameType
)
String
()
string
{
return
DisputeGameTypes
[
g
]
}
const
(
// AttestationDisputeGameType is the uint8 enum value for the attestation dispute game
AttestationDisputeGameType
GameType
=
iota
// FaultDisputeGameType is the uint8 enum value for the fault dispute game
FaultDisputeGameType
// ValidityDisputeGameType is the uint8 enum value for the validity dispute game
ValidityDisputeGameType
)
// DisputeGameTypes is a list of dispute game types.
var
DisputeGameTypes
=
[]
string
{
"attestation"
,
"fault"
,
"validity"
}
// Valid returns true if the game type is within the valid range.
func
(
g
GameType
)
Valid
()
bool
{
return
g
>=
AttestationDisputeGameType
&&
g
<=
ValidityDisputeGameType
}
// DisputeGameType is a custom flag type for dispute game type.
type
DisputeGameType
struct
{
Enum
[]
enum
.
Stringered
selected
GameType
}
// NewDisputeGameType returns a new dispute game type.
func
NewDisputeGameType
()
*
DisputeGameType
{
return
&
DisputeGameType
{
Enum
:
enum
.
StringeredList
(
DisputeGameTypes
),
selected
:
DefaultGameType
(),
}
}
// Set sets the dispute game type.
func
(
d
*
DisputeGameType
)
Set
(
value
string
)
error
{
for
i
,
enum
:=
range
d
.
Enum
{
if
enum
.
String
()
==
value
{
d
.
selected
=
GameType
(
i
)
return
nil
}
}
return
fmt
.
Errorf
(
"allowed values are %s"
,
enum
.
EnumString
(
d
.
Enum
))
}
// String returns the selected dispute game type.
func
(
d
DisputeGameType
)
String
()
string
{
return
d
.
selected
.
String
()
}
// Type maps the [DisputeGameType] string value to a [GameType] enum value.
func
(
d
DisputeGameType
)
Type
()
GameType
{
return
d
.
selected
}
op-challenger/types/game_type_test.go
deleted
100644 → 0
View file @
1a95d061
package
types
import
(
"testing"
"github.com/stretchr/testify/require"
)
var
(
disputeGames
=
[]
struct
{
name
string
gameType
GameType
}{
{
"attestation"
,
AttestationDisputeGameType
},
{
"fault"
,
FaultDisputeGameType
},
{
"validity"
,
ValidityDisputeGameType
},
}
)
// TestDefaultGameType returns the default dispute game type.
func
TestDefaultGameType
(
t
*
testing
.
T
)
{
defaultGameType
:=
disputeGames
[
0
]
.
gameType
require
.
Equal
(
t
,
defaultGameType
,
DefaultGameType
())
}
// TestGameType_Valid tests the Valid function with valid inputs.
func
TestGameType_Valid
(
t
*
testing
.
T
)
{
for
_
,
game
:=
range
disputeGames
{
require
.
True
(
t
,
game
.
gameType
.
Valid
())
}
}
// TestGameType_Invalid tests the Valid function with an invalid input.
func
TestGameType_Invalid
(
t
*
testing
.
T
)
{
invalid
:=
disputeGames
[
len
(
disputeGames
)
-
1
]
.
gameType
+
1
require
.
False
(
t
,
GameType
(
invalid
)
.
Valid
())
}
// FuzzGameType_Invalid checks that invalid game types are correctly
// returned as invalid by the validation [Valid] function.
func
FuzzGameType_Invalid
(
f
*
testing
.
F
)
{
maxCount
:=
len
(
DisputeGameTypes
)
f
.
Fuzz
(
func
(
t
*
testing
.
T
,
number
uint8
)
{
if
number
>=
uint8
(
maxCount
)
{
require
.
False
(
t
,
GameType
(
number
)
.
Valid
())
}
else
{
require
.
True
(
t
,
GameType
(
number
)
.
Valid
())
}
})
}
// TestGameType_Default tests the default value of the DisputeGameType.
func
TestGameType_Default
(
t
*
testing
.
T
)
{
d
:=
NewDisputeGameType
()
require
.
Equal
(
t
,
DefaultGameType
(),
d
.
selected
)
require
.
Equal
(
t
,
DefaultGameType
(),
d
.
Type
())
}
// TestGameType_String tests the Set and String function on the DisputeGameType.
func
TestGameType_String
(
t
*
testing
.
T
)
{
for
_
,
dg
:=
range
disputeGames
{
t
.
Run
(
dg
.
name
,
func
(
t
*
testing
.
T
)
{
d
:=
NewDisputeGameType
()
require
.
Equal
(
t
,
dg
.
name
,
dg
.
gameType
.
String
())
require
.
NoError
(
t
,
d
.
Set
(
dg
.
name
))
require
.
Equal
(
t
,
dg
.
name
,
d
.
String
())
require
.
Equal
(
t
,
dg
.
gameType
,
d
.
selected
)
})
}
}
// TestGameType_Type tests the Type function on the DisputeGameType.
func
TestGameType_Type
(
t
*
testing
.
T
)
{
for
_
,
dg
:=
range
disputeGames
{
t
.
Run
(
dg
.
name
,
func
(
t
*
testing
.
T
)
{
d
:=
NewDisputeGameType
()
require
.
Equal
(
t
,
dg
.
name
,
dg
.
gameType
.
String
())
require
.
NoError
(
t
,
d
.
Set
(
dg
.
name
))
require
.
Equal
(
t
,
dg
.
gameType
,
d
.
Type
())
require
.
Equal
(
t
,
dg
.
gameType
,
d
.
selected
)
})
}
}
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