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
071ee384
Unverified
Commit
071ee384
authored
Jul 01, 2023
by
mergify[bot]
Committed by
GitHub
Jul 01, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into deps/ctb-remove-core-utils
parents
0e969ffb
9dd47648
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
137 additions
and
1952 deletions
+137
-1952
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
alphabet_provider.go
op-challenger/fault/alphabet_provider.go
+18
-10
alphabet_provider_test.go
op-challenger/fault/alphabet_provider_test.go
+17
-8
types.go
op-challenger/fault/types.go
+1
-0
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
071ee384
...
...
@@ -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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
071ee384
...
...
@@ -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 @
071ee384
...
...
@@ -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 @
0e969ffb
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/fault/alphabet_provider.go
View file @
071ee384
...
...
@@ -18,27 +18,35 @@ type AlphabetProvider struct {
func
NewAlphabetProvider
(
state
string
,
depth
uint64
)
*
AlphabetProvider
{
return
&
AlphabetProvider
{
state
:
strings
.
Split
(
state
,
""
),
maxLen
:
(
1
<<
depth
),
maxLen
:
uint64
(
1
<<
depth
),
}
}
// Get
returns the claim value at the given index in the trace
.
func
(
ap
*
AlphabetProvider
)
Get
(
i
uint64
)
(
common
.
Hash
,
error
)
{
// Get
Preimage returns the preimage for the given hash
.
func
(
ap
*
AlphabetProvider
)
Get
Preimage
(
i
uint64
)
([]
byte
,
error
)
{
// The index cannot be larger than the maximum index as computed by the depth.
if
i
>=
ap
.
maxLen
{
return
common
.
Hash
{},
ErrIndexTooLarge
return
[]
byte
{},
ErrIndexTooLarge
}
// We extend the deepest hash to the maximum depth if the trace is not expansive.
if
i
>=
uint64
(
len
(
ap
.
state
))
{
return
ap
.
Get
(
uint64
(
len
(
ap
.
state
))
-
1
)
return
ap
.
GetPreimage
(
uint64
(
len
(
ap
.
state
))
-
1
)
}
return
buildAlphabetClaimBytes
(
i
,
ap
.
state
[
i
]),
nil
}
// Get returns the claim value at the given index in the trace.
func
(
ap
*
AlphabetProvider
)
Get
(
i
uint64
)
(
common
.
Hash
,
error
)
{
claimBytes
,
err
:=
ap
.
GetPreimage
(
i
)
if
err
!=
nil
{
return
common
.
Hash
{},
err
}
return
ap
.
ComputeAlphabetClaim
(
i
),
nil
return
common
.
BytesToHash
(
claimBytes
),
nil
}
// ComputeAlphabetClaim computes the claim for the given index in the trace.
func
(
ap
*
AlphabetProvider
)
ComputeAlphabetClaim
(
i
uint64
)
common
.
Hash
{
concatenated
:=
append
(
IndexToBytes
(
i
),
[]
byte
(
ap
.
state
[
i
])
...
)
return
common
.
BytesToHash
(
concatenated
)
// buildAlphabetClaimBytes constructs the claim bytes for the index and state item.
func
buildAlphabetClaimBytes
(
i
uint64
,
letter
string
)
[]
byte
{
return
append
(
IndexToBytes
(
i
),
[]
byte
(
letter
)
...
)
}
// IndexToBytes converts an index to a byte slice big endian
...
...
op-challenger/fault/alphabet_provider_test.go
View file @
071ee384
...
...
@@ -50,17 +50,26 @@ func FuzzIndexToBytes(f *testing.F) {
})
}
// TestComputeAlphabetClaim tests the ComputeAlphabetClaim function.
func
TestComputeAlphabetClaim
(
t
*
testing
.
T
)
{
// TestGetPreimage_Succeeds tests the GetPreimage function
// returns the correct pre-image for a index.
func
TestGetPreimage_Succeeds
(
t
*
testing
.
T
)
{
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
claim
:=
ap
.
ComputeAlphabetClaim
(
0
)
concatenated
:=
append
(
IndexToBytes
(
0
),
[]
byte
(
"a"
)
...
)
expected
:=
common
.
BytesToHash
(
concatenated
)
require
.
Equal
(
t
,
expected
,
claim
)
expected
:=
append
(
IndexToBytes
(
uint64
(
0
)),
[]
byte
(
"a"
)
...
)
retrieved
,
err
:=
ap
.
GetPreimage
(
uint64
(
0
))
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
expected
,
retrieved
)
}
// TestGetPreimage_TooLargeIndex_Fails tests the GetPreimage
// function errors if the index is too large.
func
TestGetPreimage_TooLargeIndex_Fails
(
t
*
testing
.
T
)
{
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
_
,
err
:=
ap
.
GetPreimage
(
4
)
require
.
ErrorIs
(
t
,
err
,
ErrIndexTooLarge
)
}
// TestGet tests the Get function.
func
TestGet
(
t
*
testing
.
T
)
{
// TestGet
_Succeeds
tests the Get function.
func
TestGet
_Succeeds
(
t
*
testing
.
T
)
{
ap
:=
NewAlphabetProvider
(
"abc"
,
2
)
claim
,
err
:=
ap
.
Get
(
0
)
require
.
NoError
(
t
,
err
)
...
...
op-challenger/fault/types.go
View file @
071ee384
...
...
@@ -26,6 +26,7 @@ type StepCallData struct {
// The [AlphabetProvider] is a minimal implementation of this interface.
type
TraceProvider
interface
{
Get
(
i
uint64
)
(
common
.
Hash
,
error
)
GetPreimage
(
i
uint64
)
([]
byte
,
error
)
}
// ClaimData is the core of a claim. It must be unique inside a specific game.
...
...
op-challenger/flags/flags.go
View file @
071ee384
...
...
@@ -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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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 @
0e969ffb
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