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
5bdc9ac0
Unverified
Commit
5bdc9ac0
authored
Dec 11, 2024
by
Axel Kingsley
Committed by
GitHub
Dec 11, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add SyncNode and Controller (#13344)
* Add SyncNode and Controller * Add Tests; Real DeriveFromL1 function
parent
55165087
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
506 additions
and
92 deletions
+506
-92
l2_verifier.go
op-e2e/actions/helpers/l2_verifier.go
+3
-3
interop.go
op-e2e/actions/interop/interop.go
+6
-6
supersystem.go
op-e2e/interop/supersystem.go
+2
-2
rwmap.go
op-service/locks/rwmap.go
+6
-0
main_test.go
op-supervisor/cmd/main_test.go
+3
-3
config.go
op-supervisor/config/config.go
+3
-3
config_test.go
op-supervisor/config/config_test.go
+2
-2
flags.go
op-supervisor/flags/flags.go
+3
-3
backend.go
op-supervisor/supervisor/backend/backend.go
+30
-24
backend_test.go
op-supervisor/supervisor/backend/backend_test.go
+2
-2
l1_processor.go
op-supervisor/supervisor/backend/processors/l1_processor.go
+12
-5
l1_processor_test.go
...rvisor/supervisor/backend/processors/l1_processor_test.go
+88
-6
collection.go
op-supervisor/supervisor/backend/syncnode/collection.go
+6
-8
controller.go
op-supervisor/supervisor/backend/syncnode/controller.go
+100
-0
controller_test.go
op-supervisor/supervisor/backend/syncnode/controller_test.go
+205
-0
dial.go
op-supervisor/supervisor/backend/syncnode/dial.go
+4
-6
iface.go
op-supervisor/supervisor/backend/syncnode/iface.go
+14
-9
rpc.go
op-supervisor/supervisor/backend/syncnode/rpc.go
+17
-10
No files found.
op-e2e/actions/helpers/l2_verifier.go
View file @
5bdc9ac0
...
@@ -33,7 +33,7 @@ import (
...
@@ -33,7 +33,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/safego"
"github.com/ethereum-optimism/optimism/op-service/safego"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum-optimism/optimism/op-service/testutils"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
)
)
// L2Verifier is an actor that functions like a rollup node,
// L2Verifier is an actor that functions like a rollup node,
...
@@ -219,11 +219,11 @@ func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher,
...
@@ -219,11 +219,11 @@ func NewL2Verifier(t Testing, log log.Logger, l1 derive.L1Fetcher,
return
rollupNode
return
rollupNode
}
}
func
(
v
*
L2Verifier
)
InteropSync
Source
(
t
Testing
)
syncsrc
.
SyncSourc
e
{
func
(
v
*
L2Verifier
)
InteropSync
Node
(
t
Testing
)
syncnode
.
SyncNod
e
{
require
.
NotNil
(
t
,
v
.
interopRPC
,
"interop rpc must be running"
)
require
.
NotNil
(
t
,
v
.
interopRPC
,
"interop rpc must be running"
)
cl
:=
rpc
.
DialInProc
(
v
.
interopRPC
)
cl
:=
rpc
.
DialInProc
(
v
.
interopRPC
)
bCl
:=
client
.
NewBaseRPCClient
(
cl
)
bCl
:=
client
.
NewBaseRPCClient
(
cl
)
return
sync
src
.
NewRPCSyncSourc
e
(
"action-tests-l2-verifier"
,
bCl
)
return
sync
node
.
NewRPCSyncNod
e
(
"action-tests-l2-verifier"
,
bCl
)
}
}
type
l2VerifierBackend
struct
{
type
l2VerifierBackend
struct
{
...
...
op-e2e/actions/interop/interop.go
View file @
5bdc9ac0
...
@@ -28,7 +28,7 @@ import (
...
@@ -28,7 +28,7 @@ import (
"github.com/ethereum-optimism/optimism/op-supervisor/metrics"
"github.com/ethereum-optimism/optimism/op-supervisor/metrics"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
...
@@ -111,10 +111,10 @@ func (is *InteropSetup) CreateActors() *InteropActors {
...
@@ -111,10 +111,10 @@ func (is *InteropSetup) CreateActors() *InteropActors {
chainA
:=
createL2Services
(
is
.
T
,
is
.
Log
,
l1Miner
,
is
.
Keys
,
is
.
Out
.
L2s
[
"900200"
],
supervisorAPI
)
chainA
:=
createL2Services
(
is
.
T
,
is
.
Log
,
l1Miner
,
is
.
Keys
,
is
.
Out
.
L2s
[
"900200"
],
supervisorAPI
)
chainB
:=
createL2Services
(
is
.
T
,
is
.
Log
,
l1Miner
,
is
.
Keys
,
is
.
Out
.
L2s
[
"900201"
],
supervisorAPI
)
chainB
:=
createL2Services
(
is
.
T
,
is
.
Log
,
l1Miner
,
is
.
Keys
,
is
.
Out
.
L2s
[
"900201"
],
supervisorAPI
)
// Hook up L2 RPCs to supervisor, to fetch event data from
// Hook up L2 RPCs to supervisor, to fetch event data from
srcA
:=
chainA
.
Sequencer
.
InteropSync
Sourc
e
(
is
.
T
)
srcA
:=
chainA
.
Sequencer
.
InteropSync
Nod
e
(
is
.
T
)
srcB
:=
chainB
.
Sequencer
.
InteropSync
Sourc
e
(
is
.
T
)
srcB
:=
chainB
.
Sequencer
.
InteropSync
Nod
e
(
is
.
T
)
require
.
NoError
(
is
.
T
,
supervisorAPI
.
backend
.
AttachSync
Sourc
e
(
is
.
T
.
Ctx
(),
srcA
))
require
.
NoError
(
is
.
T
,
supervisorAPI
.
backend
.
AttachSync
Nod
e
(
is
.
T
.
Ctx
(),
srcA
))
require
.
NoError
(
is
.
T
,
supervisorAPI
.
backend
.
AttachSync
Sourc
e
(
is
.
T
.
Ctx
(),
srcB
))
require
.
NoError
(
is
.
T
,
supervisorAPI
.
backend
.
AttachSync
Nod
e
(
is
.
T
.
Ctx
(),
srcB
))
return
&
InteropActors
{
return
&
InteropActors
{
L1Miner
:
l1Miner
,
L1Miner
:
l1Miner
,
Supervisor
:
supervisorAPI
,
Supervisor
:
supervisorAPI
,
...
@@ -172,7 +172,7 @@ func NewSupervisor(t helpers.Testing, logger log.Logger, depSet depset.Dependenc
...
@@ -172,7 +172,7 @@ func NewSupervisor(t helpers.Testing, logger log.Logger, depSet depset.Dependenc
DependencySetSource
:
depSet
,
DependencySetSource
:
depSet
,
SynchronousProcessors
:
true
,
SynchronousProcessors
:
true
,
Datadir
:
supervisorDataDir
,
Datadir
:
supervisorDataDir
,
SyncSources
:
&
sync
src
.
CLISyncSourc
es
{},
// sources are added dynamically afterwards
SyncSources
:
&
sync
node
.
CLISyncNod
es
{},
// sources are added dynamically afterwards
}
}
b
,
err
:=
backend
.
NewSupervisorBackend
(
t
.
Ctx
(),
b
,
err
:=
backend
.
NewSupervisorBackend
(
t
.
Ctx
(),
logger
.
New
(
"role"
,
"supervisor"
),
metrics
.
NoopMetrics
,
svCfg
)
logger
.
New
(
"role"
,
"supervisor"
),
metrics
.
NoopMetrics
,
svCfg
)
...
...
op-e2e/interop/supersystem.go
View file @
5bdc9ac0
...
@@ -61,7 +61,7 @@ import (
...
@@ -61,7 +61,7 @@ import (
supervisorConfig
"github.com/ethereum-optimism/optimism/op-supervisor/config"
supervisorConfig
"github.com/ethereum-optimism/optimism/op-supervisor/config"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
supervisortypes
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
supervisortypes
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
...
@@ -482,7 +482,7 @@ func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService {
...
@@ -482,7 +482,7 @@ func (s *interopE2ESystem) prepareSupervisor() *supervisor.SupervisorService {
ListenPort
:
0
,
ListenPort
:
0
,
EnableAdmin
:
true
,
EnableAdmin
:
true
,
},
},
SyncSources
:
&
sync
src
.
CLISyncSourc
es
{},
// no sync-sources
SyncSources
:
&
sync
node
.
CLISyncNod
es
{},
// no sync-sources
L1RPC
:
s
.
l1
.
UserRPC
()
.
RPC
(),
L1RPC
:
s
.
l1
.
UserRPC
()
.
RPC
(),
Datadir
:
path
.
Join
(
s
.
t
.
TempDir
(),
"supervisor"
),
Datadir
:
path
.
Join
(
s
.
t
.
TempDir
(),
"supervisor"
),
}
}
...
...
op-service/locks/rwmap.go
View file @
5bdc9ac0
...
@@ -35,6 +35,12 @@ func (m *RWMap[K, V]) Set(key K, value V) {
...
@@ -35,6 +35,12 @@ func (m *RWMap[K, V]) Set(key K, value V) {
m
.
inner
[
key
]
=
value
m
.
inner
[
key
]
=
value
}
}
func
(
m
*
RWMap
[
K
,
V
])
Len
()
int
{
m
.
mu
.
RLock
()
defer
m
.
mu
.
RUnlock
()
return
len
(
m
.
inner
)
}
// Range calls f sequentially for each key and value present in the map.
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
// If f returns false, range stops the iteration.
func
(
m
*
RWMap
[
K
,
V
])
Range
(
f
func
(
key
K
,
value
V
)
bool
)
{
func
(
m
*
RWMap
[
K
,
V
])
Range
(
f
func
(
key
K
,
value
V
)
bool
)
{
...
...
op-supervisor/cmd/main_test.go
View file @
5bdc9ac0
...
@@ -14,12 +14,12 @@ import (
...
@@ -14,12 +14,12 @@ import (
"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/ethereum-optimism/optimism/op-service/cliapp"
"github.com/ethereum-optimism/optimism/op-supervisor/config"
"github.com/ethereum-optimism/optimism/op-supervisor/config"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
)
)
var
(
var
(
ValidL1RPC
=
"http://localhost:8545"
ValidL1RPC
=
"http://localhost:8545"
ValidL2RPCs
=
&
sync
src
.
CLISyncSourc
es
{
ValidL2RPCs
=
&
sync
node
.
CLISyncNod
es
{
JWTSecretPaths
:
[]
string
{
"./jwt_secret.txt"
},
JWTSecretPaths
:
[]
string
{
"./jwt_secret.txt"
},
}
}
ValidDatadir
=
"./supervisor_test_datadir"
ValidDatadir
=
"./supervisor_test_datadir"
...
@@ -58,7 +58,7 @@ func TestL2ConsensusNodes(t *testing.T) {
...
@@ -58,7 +58,7 @@ func TestL2ConsensusNodes(t *testing.T) {
url2
:=
"http://foobar.com:1234"
url2
:=
"http://foobar.com:1234"
cfg
:=
configForArgs
(
t
,
addRequiredArgsExcept
(
cfg
:=
configForArgs
(
t
,
addRequiredArgsExcept
(
"--l2-consensus-nodes"
,
"--l2-consensus.nodes="
+
url1
+
","
+
url2
))
"--l2-consensus-nodes"
,
"--l2-consensus.nodes="
+
url1
+
","
+
url2
))
require
.
Equal
(
t
,
[]
string
{
url1
,
url2
},
cfg
.
SyncSources
.
(
*
sync
src
.
CLISyncSourc
es
)
.
Endpoints
)
require
.
Equal
(
t
,
[]
string
{
url1
,
url2
},
cfg
.
SyncSources
.
(
*
sync
node
.
CLISyncNod
es
)
.
Endpoints
)
})
})
}
}
...
...
op-supervisor/config/config.go
View file @
5bdc9ac0
...
@@ -8,7 +8,7 @@ import (
...
@@ -8,7 +8,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/oppprof"
oprpc
"github.com/ethereum-optimism/optimism/op-service/rpc"
oprpc
"github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
)
)
var
(
var
(
...
@@ -37,7 +37,7 @@ type Config struct {
...
@@ -37,7 +37,7 @@ type Config struct {
L1RPC
string
L1RPC
string
// SyncSources lists the consensus nodes that help sync the supervisor
// SyncSources lists the consensus nodes that help sync the supervisor
SyncSources
sync
src
.
SyncSourc
eCollection
SyncSources
sync
node
.
SyncNod
eCollection
Datadir
string
Datadir
string
}
}
...
@@ -63,7 +63,7 @@ func (c *Config) Check() error {
...
@@ -63,7 +63,7 @@ func (c *Config) Check() error {
// NewConfig creates a new config using default values whenever possible.
// NewConfig creates a new config using default values whenever possible.
// Required options with no suitable default are passed as parameters.
// Required options with no suitable default are passed as parameters.
func
NewConfig
(
l1RPC
string
,
syncSrcs
sync
src
.
SyncSourc
eCollection
,
depSet
depset
.
DependencySetSource
,
datadir
string
)
*
Config
{
func
NewConfig
(
l1RPC
string
,
syncSrcs
sync
node
.
SyncNod
eCollection
,
depSet
depset
.
DependencySetSource
,
datadir
string
)
*
Config
{
return
&
Config
{
return
&
Config
{
LogConfig
:
oplog
.
DefaultCLIConfig
(),
LogConfig
:
oplog
.
DefaultCLIConfig
(),
MetricsConfig
:
opmetrics
.
DefaultCLIConfig
(),
MetricsConfig
:
opmetrics
.
DefaultCLIConfig
(),
...
...
op-supervisor/config/config_test.go
View file @
5bdc9ac0
...
@@ -9,7 +9,7 @@ import (
...
@@ -9,7 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/oppprof"
"github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
...
@@ -68,5 +68,5 @@ func validConfig() *Config {
...
@@ -68,5 +68,5 @@ func validConfig() *Config {
panic
(
err
)
panic
(
err
)
}
}
// Should be valid using only the required arguments passed in via the constructor.
// Should be valid using only the required arguments passed in via the constructor.
return
NewConfig
(
"http://localhost:8545"
,
&
sync
src
.
CLISyncSourc
es
{},
depSet
,
"./supervisor_testdir"
)
return
NewConfig
(
"http://localhost:8545"
,
&
sync
node
.
CLISyncNod
es
{},
depSet
,
"./supervisor_testdir"
)
}
}
op-supervisor/flags/flags.go
View file @
5bdc9ac0
...
@@ -12,7 +12,7 @@ import (
...
@@ -12,7 +12,7 @@ import (
oprpc
"github.com/ethereum-optimism/optimism/op-service/rpc"
oprpc
"github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-supervisor/config"
"github.com/ethereum-optimism/optimism/op-supervisor/config"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
)
)
const
EnvVarPrefix
=
"OP_SUPERVISOR"
const
EnvVarPrefix
=
"OP_SUPERVISOR"
...
@@ -110,8 +110,8 @@ func ConfigFromCLI(ctx *cli.Context, version string) *config.Config {
...
@@ -110,8 +110,8 @@ func ConfigFromCLI(ctx *cli.Context, version string) *config.Config {
// syncSourceSetups creates a sync source collection, from CLI arguments.
// syncSourceSetups creates a sync source collection, from CLI arguments.
// These sources can share JWT secret configuration.
// These sources can share JWT secret configuration.
func
syncSourceSetups
(
ctx
*
cli
.
Context
)
sync
src
.
SyncSourc
eCollection
{
func
syncSourceSetups
(
ctx
*
cli
.
Context
)
sync
node
.
SyncNod
eCollection
{
return
&
sync
src
.
CLISyncSourc
es
{
return
&
sync
node
.
CLISyncNod
es
{
Endpoints
:
filterEmpty
(
ctx
.
StringSlice
(
L2ConsensusNodesFlag
.
Name
)),
Endpoints
:
filterEmpty
(
ctx
.
StringSlice
(
L2ConsensusNodesFlag
.
Name
)),
JWTSecretPaths
:
filterEmpty
(
ctx
.
StringSlice
(
L2ConsensusJWTSecret
.
Name
)),
JWTSecretPaths
:
filterEmpty
(
ctx
.
StringSlice
(
L2ConsensusJWTSecret
.
Name
)),
}
}
...
...
op-supervisor/supervisor/backend/backend.go
View file @
5bdc9ac0
...
@@ -18,7 +18,7 @@ import (
...
@@ -18,7 +18,7 @@ import (
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/db"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
...
@@ -32,28 +32,28 @@ type SupervisorBackend struct {
...
@@ -32,28 +32,28 @@ type SupervisorBackend struct {
// depSet is the dependency set that the backend uses to know about the chains it is indexing
// depSet is the dependency set that the backend uses to know about the chains it is indexing
depSet
depset
.
DependencySet
depSet
depset
.
DependencySet
// chainDBs
holds on to the DB indices for each chai
n
// chainDBs
is the primary interface to the databases, including logs, derived-from information and L1 finalizatio
n
chainDBs
*
db
.
ChainsDB
chainDBs
*
db
.
ChainsDB
// l1Processor watches for new
L1 blocks, updates the local-safe DB, and kicks off derivation orchestr
ation
// l1Processor watches for new
data from the L1 chain including new blocks and block finaliz
ation
l1Processor
*
processors
.
L1Processor
l1Processor
*
processors
.
L1Processor
// chainProcessors are notified of new unsafe blocks, and add the unsafe log events data into the events DB
// chainProcessors are notified of new unsafe blocks, and add the unsafe log events data into the events DB
chainProcessors
locks
.
RWMap
[
types
.
ChainID
,
*
processors
.
ChainProcessor
]
chainProcessors
locks
.
RWMap
[
types
.
ChainID
,
*
processors
.
ChainProcessor
]
// crossProcessors are used to index cross-chain dependency validity data once the log events are indexed
crossSafeProcessors
locks
.
RWMap
[
types
.
ChainID
,
*
cross
.
Worker
]
crossUnsafeProcessors
locks
.
RWMap
[
types
.
ChainID
,
*
cross
.
Worker
]
//
crossSafeProcessors take local-safe data and promote it to cross-safe when verified
//
syncNodesController controls the derivation or reset of the sync nodes
crossSafeProcessors
locks
.
RWMap
[
types
.
ChainID
,
*
cross
.
Worker
]
syncNodesController
*
syncnode
.
SyncNodesController
// crossUnsafeProcessors take local-unsafe data and promote it to cross-unsafe when verified
// synchronousProcessors disables background-workers,
crossUnsafeProcessors
locks
.
RWMap
[
types
.
ChainID
,
*
cross
.
Worker
]
// requiring manual triggers for the backend to process l2 data.
synchronousProcessors
bool
// chainMetrics are used to track metrics for each chain
// chainMetrics are used to track metrics for each chain
// they are reused for processors and databases of the same chain
// they are reused for processors and databases of the same chain
chainMetrics
locks
.
RWMap
[
types
.
ChainID
,
*
chainMetrics
]
chainMetrics
locks
.
RWMap
[
types
.
ChainID
,
*
chainMetrics
]
// synchronousProcessors disables background-workers,
// requiring manual triggers for the backend to process anything.
synchronousProcessors
bool
}
}
var
_
frontend
.
Backend
=
(
*
SupervisorBackend
)(
nil
)
var
_
frontend
.
Backend
=
(
*
SupervisorBackend
)(
nil
)
...
@@ -75,13 +75,17 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg
...
@@ -75,13 +75,17 @@ func NewSupervisorBackend(ctx context.Context, logger log.Logger, m Metrics, cfg
// create initial per-chain resources
// create initial per-chain resources
chainsDBs
:=
db
.
NewChainsDB
(
logger
,
depSet
)
chainsDBs
:=
db
.
NewChainsDB
(
logger
,
depSet
)
// create node controller
controllers
:=
syncnode
.
NewSyncNodesController
(
logger
,
depSet
,
chainsDBs
)
// create the supervisor backend
// create the supervisor backend
super
:=
&
SupervisorBackend
{
super
:=
&
SupervisorBackend
{
logger
:
logger
,
logger
:
logger
,
m
:
m
,
m
:
m
,
dataDir
:
cfg
.
Datadir
,
dataDir
:
cfg
.
Datadir
,
depSet
:
depSet
,
depSet
:
depSet
,
chainDBs
:
chainsDBs
,
chainDBs
:
chainsDBs
,
syncNodesController
:
controllers
,
// For testing we can avoid running the processors.
// For testing we can avoid running the processors.
synchronousProcessors
:
cfg
.
SynchronousProcessors
,
synchronousProcessors
:
cfg
.
SynchronousProcessors
,
}
}
...
@@ -145,7 +149,7 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf
...
@@ -145,7 +149,7 @@ func (su *SupervisorBackend) initResources(ctx context.Context, cfg *config.Conf
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to set up sync source: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to set up sync source: %w"
,
err
)
}
}
if
err
:=
su
.
AttachSync
Sourc
e
(
ctx
,
src
);
err
!=
nil
{
if
err
:=
su
.
AttachSync
Nod
e
(
ctx
,
src
);
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to attach sync source %s: %w"
,
src
,
err
)
return
fmt
.
Errorf
(
"failed to attach sync source %s: %w"
,
src
,
err
)
}
}
}
}
...
@@ -207,7 +211,7 @@ func (su *SupervisorBackend) openChainDBs(chainID types.ChainID) error {
...
@@ -207,7 +211,7 @@ func (su *SupervisorBackend) openChainDBs(chainID types.ChainID) error {
return
nil
return
nil
}
}
func
(
su
*
SupervisorBackend
)
AttachSync
Source
(
ctx
context
.
Context
,
src
syncsrc
.
SyncSourc
e
)
error
{
func
(
su
*
SupervisorBackend
)
AttachSync
Node
(
ctx
context
.
Context
,
src
syncnode
.
SyncNod
e
)
error
{
su
.
logger
.
Info
(
"attaching sync source to chain processor"
,
"source"
,
src
)
su
.
logger
.
Info
(
"attaching sync source to chain processor"
,
"source"
,
src
)
chainID
,
err
:=
src
.
ChainID
(
ctx
)
chainID
,
err
:=
src
.
ChainID
(
ctx
)
...
@@ -217,12 +221,14 @@ func (su *SupervisorBackend) AttachSyncSource(ctx context.Context, src syncsrc.S
...
@@ -217,12 +221,14 @@ func (su *SupervisorBackend) AttachSyncSource(ctx context.Context, src syncsrc.S
if
!
su
.
depSet
.
HasChain
(
chainID
)
{
if
!
su
.
depSet
.
HasChain
(
chainID
)
{
return
fmt
.
Errorf
(
"chain %s is not part of the interop dependency set: %w"
,
chainID
,
types
.
ErrUnknownChain
)
return
fmt
.
Errorf
(
"chain %s is not part of the interop dependency set: %w"
,
chainID
,
types
.
ErrUnknownChain
)
}
}
return
su
.
AttachProcessorSource
(
chainID
,
src
)
err
=
su
.
AttachProcessorSource
(
chainID
,
src
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to attach sync source to processor: %w"
,
err
)
}
return
su
.
syncNodesController
.
AttachNodeController
(
chainID
,
src
)
}
}
func
(
su
*
SupervisorBackend
)
AttachProcessorSource
(
chainID
types
.
ChainID
,
src
processors
.
Source
)
error
{
func
(
su
*
SupervisorBackend
)
AttachProcessorSource
(
chainID
types
.
ChainID
,
src
processors
.
Source
)
error
{
// TODO: register sync sources in the backend, to trigger derivation work etc.
proc
,
ok
:=
su
.
chainProcessors
.
Get
(
chainID
)
proc
,
ok
:=
su
.
chainProcessors
.
Get
(
chainID
)
if
!
ok
{
if
!
ok
{
return
fmt
.
Errorf
(
"unknown chain %s, cannot attach RPC to processor"
,
chainID
)
return
fmt
.
Errorf
(
"unknown chain %s, cannot attach RPC to processor"
,
chainID
)
...
@@ -256,7 +262,7 @@ func (su *SupervisorBackend) attachL1RPC(ctx context.Context, l1RPCAddr string)
...
@@ -256,7 +262,7 @@ func (su *SupervisorBackend) attachL1RPC(ctx context.Context, l1RPCAddr string)
// If the L1 processor does not exist, it is created and started.
// If the L1 processor does not exist, it is created and started.
func
(
su
*
SupervisorBackend
)
AttachL1Source
(
source
processors
.
L1Source
)
{
func
(
su
*
SupervisorBackend
)
AttachL1Source
(
source
processors
.
L1Source
)
{
if
su
.
l1Processor
==
nil
{
if
su
.
l1Processor
==
nil
{
su
.
l1Processor
=
processors
.
NewL1Processor
(
su
.
logger
,
su
.
chainDBs
,
source
)
su
.
l1Processor
=
processors
.
NewL1Processor
(
su
.
logger
,
su
.
chainDBs
,
s
u
.
syncNodesController
,
s
ource
)
su
.
l1Processor
.
Start
()
su
.
l1Processor
.
Start
()
}
else
{
}
else
{
su
.
l1Processor
.
AttachClient
(
source
)
su
.
l1Processor
.
AttachClient
(
source
)
...
@@ -338,7 +344,7 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error {
...
@@ -338,7 +344,7 @@ func (su *SupervisorBackend) Stop(ctx context.Context) error {
// AddL2RPC attaches an RPC as the RPC for the given chain, overriding the previous RPC source, if any.
// AddL2RPC attaches an RPC as the RPC for the given chain, overriding the previous RPC source, if any.
func
(
su
*
SupervisorBackend
)
AddL2RPC
(
ctx
context
.
Context
,
rpc
string
,
jwtSecret
eth
.
Bytes32
)
error
{
func
(
su
*
SupervisorBackend
)
AddL2RPC
(
ctx
context
.
Context
,
rpc
string
,
jwtSecret
eth
.
Bytes32
)
error
{
setupSrc
:=
&
sync
src
.
RPCDialSetup
{
setupSrc
:=
&
sync
node
.
RPCDialSetup
{
JWTSecret
:
jwtSecret
,
JWTSecret
:
jwtSecret
,
Endpoint
:
rpc
,
Endpoint
:
rpc
,
}
}
...
@@ -346,7 +352,7 @@ func (su *SupervisorBackend) AddL2RPC(ctx context.Context, rpc string, jwtSecret
...
@@ -346,7 +352,7 @@ func (su *SupervisorBackend) AddL2RPC(ctx context.Context, rpc string, jwtSecret
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"failed to set up sync source from RPC: %w"
,
err
)
return
fmt
.
Errorf
(
"failed to set up sync source from RPC: %w"
,
err
)
}
}
return
su
.
AttachSync
Sourc
e
(
ctx
,
src
)
return
su
.
AttachSync
Nod
e
(
ctx
,
src
)
}
}
// Internal methods, for processors
// Internal methods, for processors
...
...
op-supervisor/supervisor/backend/backend_test.go
View file @
5bdc9ac0
...
@@ -24,7 +24,7 @@ import (
...
@@ -24,7 +24,7 @@ import (
"github.com/ethereum-optimism/optimism/op-supervisor/metrics"
"github.com/ethereum-optimism/optimism/op-supervisor/metrics"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/processors"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
src
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/sync
node
"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
...
@@ -57,7 +57,7 @@ func TestBackendLifetime(t *testing.T) {
...
@@ -57,7 +57,7 @@ func TestBackendLifetime(t *testing.T) {
DependencySetSource
:
depSet
,
DependencySetSource
:
depSet
,
SynchronousProcessors
:
true
,
SynchronousProcessors
:
true
,
MockRun
:
false
,
MockRun
:
false
,
SyncSources
:
&
sync
src
.
CLISyncSourc
es
{},
SyncSources
:
&
sync
node
.
CLISyncNod
es
{},
Datadir
:
dataDir
,
Datadir
:
dataDir
,
}
}
...
...
op-supervisor/supervisor/backend/processors/l1_processor.go
View file @
5bdc9ac0
...
@@ -19,6 +19,10 @@ type chainsDB interface {
...
@@ -19,6 +19,10 @@ type chainsDB interface {
UpdateFinalizedL1
(
finalized
eth
.
BlockRef
)
error
UpdateFinalizedL1
(
finalized
eth
.
BlockRef
)
error
}
}
type
controller
interface
{
DeriveFromL1
(
eth
.
BlockRef
)
error
}
type
L1Source
interface
{
type
L1Source
interface
{
L1BlockRefByNumber
(
ctx
context
.
Context
,
number
uint64
)
(
eth
.
L1BlockRef
,
error
)
L1BlockRefByNumber
(
ctx
context
.
Context
,
number
uint64
)
(
eth
.
L1BlockRef
,
error
)
L1BlockRefByLabel
(
ctx
context
.
Context
,
label
eth
.
BlockLabel
)
(
eth
.
L1BlockRef
,
error
)
L1BlockRefByLabel
(
ctx
context
.
Context
,
label
eth
.
BlockLabel
)
(
eth
.
L1BlockRef
,
error
)
...
@@ -34,19 +38,21 @@ type L1Processor struct {
...
@@ -34,19 +38,21 @@ type L1Processor struct {
currentNumber
uint64
currentNumber
uint64
tickDuration
time
.
Duration
tickDuration
time
.
Duration
db
chainsDB
db
chainsDB
snc
controller
ctx
context
.
Context
ctx
context
.
Context
cancel
context
.
CancelFunc
cancel
context
.
CancelFunc
wg
sync
.
WaitGroup
wg
sync
.
WaitGroup
}
}
func
NewL1Processor
(
log
log
.
Logger
,
cdb
chainsDB
,
client
L1Source
)
*
L1Processor
{
func
NewL1Processor
(
log
log
.
Logger
,
cdb
chainsDB
,
snc
controller
,
client
L1Source
)
*
L1Processor
{
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
tickDuration
:=
6
*
time
.
Second
tickDuration
:=
6
*
time
.
Second
return
&
L1Processor
{
return
&
L1Processor
{
client
:
client
,
client
:
client
,
db
:
cdb
,
db
:
cdb
,
snc
:
snc
,
log
:
log
.
New
(
"service"
,
"l1-processor"
),
log
:
log
.
New
(
"service"
,
"l1-processor"
),
tickDuration
:
tickDuration
,
tickDuration
:
tickDuration
,
ctx
:
ctx
,
ctx
:
ctx
,
...
@@ -143,9 +149,10 @@ func (p *L1Processor) work() error {
...
@@ -143,9 +149,10 @@ func (p *L1Processor) work() error {
return
err
return
err
}
}
// go drive derivation on this new L1 input
// send the new L1 block to the sync nodes for derivation
// only possible once bidirectional RPC and new derivers are in place
if
err
:=
p
.
snc
.
DeriveFromL1
(
ref
);
err
!=
nil
{
// could do this as a function callback to a more appropriate driver
return
err
}
// update the target number
// update the target number
p
.
currentNumber
=
nextNumber
p
.
currentNumber
=
nextNumber
...
...
op-supervisor/supervisor/backend/processors/l1_processor_test.go
View file @
5bdc9ac0
...
@@ -13,6 +13,17 @@ import (
...
@@ -13,6 +13,17 @@ import (
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/require"
)
)
type
mockController
struct
{
deriveFromL1Fn
func
(
ref
eth
.
BlockRef
)
error
}
func
(
m
*
mockController
)
DeriveFromL1
(
ref
eth
.
BlockRef
)
error
{
if
m
.
deriveFromL1Fn
!=
nil
{
return
m
.
deriveFromL1Fn
(
ref
)
}
return
nil
}
type
mockChainsDB
struct
{
type
mockChainsDB
struct
{
recordNewL1Fn
func
(
ref
eth
.
BlockRef
)
error
recordNewL1Fn
func
(
ref
eth
.
BlockRef
)
error
lastCommonL1Fn
func
()
(
types
.
BlockSeal
,
error
)
lastCommonL1Fn
func
()
(
types
.
BlockSeal
,
error
)
...
@@ -68,6 +79,7 @@ func TestL1Processor(t *testing.T) {
...
@@ -68,6 +79,7 @@ func TestL1Processor(t *testing.T) {
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
proc
:=
&
L1Processor
{
proc
:=
&
L1Processor
{
log
:
testlog
.
Logger
(
t
,
log
.
LvlInfo
),
log
:
testlog
.
Logger
(
t
,
log
.
LvlInfo
),
snc
:
&
mockController
{},
client
:
&
mockL1BlockRefByNumberFetcher
{},
client
:
&
mockL1BlockRefByNumberFetcher
{},
currentNumber
:
0
,
currentNumber
:
0
,
tickDuration
:
1
*
time
.
Second
,
tickDuration
:
1
*
time
.
Second
,
...
@@ -101,7 +113,7 @@ func TestL1Processor(t *testing.T) {
...
@@ -101,7 +113,7 @@ func TestL1Processor(t *testing.T) {
// the error means the current number should still be 0
// the error means the current number should still be 0
require
.
Equal
(
t
,
uint64
(
0
),
proc
.
currentNumber
)
require
.
Equal
(
t
,
uint64
(
0
),
proc
.
currentNumber
)
})
})
t
.
Run
(
"
Record
s new L1"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"
Handle
s new L1"
,
func
(
t
*
testing
.
T
)
{
proc
:=
processorForTesting
()
proc
:=
processorForTesting
()
// return a new block number each time
// return a new block number each time
num
:=
uint64
(
0
)
num
:=
uint64
(
0
)
...
@@ -109,19 +121,89 @@ func TestL1Processor(t *testing.T) {
...
@@ -109,19 +121,89 @@ func TestL1Processor(t *testing.T) {
defer
func
()
{
num
++
}()
defer
func
()
{
num
++
}()
return
eth
.
L1BlockRef
{
Number
:
num
},
nil
return
eth
.
L1BlockRef
{
Number
:
num
},
nil
}
}
// confirm that recordNewL1 is
c
alled for each block number received
// confirm that recordNewL1 is
recordC
alled for each block number received
c
alled
:=
uint64
(
0
)
recordC
alled
:=
uint64
(
0
)
proc
.
db
.
(
*
mockChainsDB
)
.
recordNewL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
proc
.
db
.
(
*
mockChainsDB
)
.
recordNewL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
require
.
Equal
(
t
,
called
,
ref
.
Number
)
require
.
Equal
(
t
,
recordCalled
,
ref
.
Number
)
called
++
recordCalled
++
return
nil
}
// confirm that deriveFromL1 is called for each block number received
deriveCalled
:=
uint64
(
0
)
proc
.
snc
.
(
*
mockController
)
.
deriveFromL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
require
.
Equal
(
t
,
deriveCalled
,
ref
.
Number
)
deriveCalled
++
return
nil
return
nil
}
}
proc
.
Start
()
proc
.
Start
()
defer
proc
.
Stop
()
defer
proc
.
Stop
()
// the new L1 blocks should be recorded
require
.
Eventually
(
t
,
func
()
bool
{
require
.
Eventually
(
t
,
func
()
bool
{
return
c
alled
>=
1
&&
proc
.
currentNumber
>=
1
return
recordC
alled
>=
1
&&
proc
.
currentNumber
>=
1
},
10
*
time
.
Second
,
100
*
time
.
Millisecond
)
},
10
*
time
.
Second
,
100
*
time
.
Millisecond
)
// confirm that the db record and derive call counts match
require
.
Equal
(
t
,
recordCalled
,
deriveCalled
)
})
t
.
Run
(
"Handles L1 record error"
,
func
(
t
*
testing
.
T
)
{
proc
:=
processorForTesting
()
// return a new block number each time
num
:=
uint64
(
0
)
proc
.
client
.
(
*
mockL1BlockRefByNumberFetcher
)
.
l1BlockByNumberFn
=
func
()
(
eth
.
L1BlockRef
,
error
)
{
defer
func
()
{
num
++
}()
return
eth
.
L1BlockRef
{
Number
:
num
},
nil
}
// confirm that recordNewL1 is recordCalled for each block number received
recordCalled
:=
0
proc
.
db
.
(
*
mockChainsDB
)
.
recordNewL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
recordCalled
++
return
fmt
.
Errorf
(
"error"
)
}
// confirm that deriveFromL1 is called for each block number received
deriveCalled
:=
0
proc
.
snc
.
(
*
mockController
)
.
deriveFromL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
deriveCalled
++
return
nil
}
proc
.
Start
()
defer
proc
.
Stop
()
// because the record call fails, the current number should not be updated
require
.
Never
(
t
,
func
()
bool
{
return
recordCalled
>=
1
&&
proc
.
currentNumber
>=
1
},
10
*
time
.
Second
,
100
*
time
.
Millisecond
)
// confirm derive was never called because the record call failed
require
.
Equal
(
t
,
0
,
deriveCalled
)
})
t
.
Run
(
"Handles L1 derive error"
,
func
(
t
*
testing
.
T
)
{
proc
:=
processorForTesting
()
// return a new block number each time
num
:=
uint64
(
0
)
proc
.
client
.
(
*
mockL1BlockRefByNumberFetcher
)
.
l1BlockByNumberFn
=
func
()
(
eth
.
L1BlockRef
,
error
)
{
defer
func
()
{
num
++
}()
return
eth
.
L1BlockRef
{
Number
:
num
},
nil
}
// confirm that recordNewL1 is recordCalled for each block number received
recordCalled
:=
uint64
(
0
)
proc
.
db
.
(
*
mockChainsDB
)
.
recordNewL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
require
.
Equal
(
t
,
recordCalled
,
ref
.
Number
)
recordCalled
++
return
nil
}
// confirm that deriveFromL1 is called for each block number received
deriveCalled
:=
uint64
(
0
)
proc
.
snc
.
(
*
mockController
)
.
deriveFromL1Fn
=
func
(
ref
eth
.
BlockRef
)
error
{
deriveCalled
++
return
fmt
.
Errorf
(
"error"
)
}
proc
.
Start
()
defer
proc
.
Stop
()
// because the derive call fails, the current number should not be updated
require
.
Never
(
t
,
func
()
bool
{
return
recordCalled
>=
1
&&
proc
.
currentNumber
>=
1
},
10
*
time
.
Second
,
100
*
time
.
Millisecond
)
// confirm that the db record and derive call counts match
// (because the derive call fails after the record call)
require
.
Equal
(
t
,
recordCalled
,
deriveCalled
)
})
})
t
.
Run
(
"Updates L1 Finalized"
,
func
(
t
*
testing
.
T
)
{
t
.
Run
(
"Updates L1 Finalized"
,
func
(
t
*
testing
.
T
)
{
proc
:=
processorForTesting
()
proc
:=
processorForTesting
()
...
...
op-supervisor/supervisor/backend/sync
src
/collection.go
→
op-supervisor/supervisor/backend/sync
node
/collection.go
View file @
5bdc9ac0
package
sync
src
package
sync
node
import
(
import
(
"context"
"context"
...
@@ -11,16 +11,14 @@ import (
...
@@ -11,16 +11,14 @@ import (
"github.com/ethereum-optimism/optimism/op-service/rpc"
"github.com/ethereum-optimism/optimism/op-service/rpc"
)
)
// CLISyncSources is a bundle of CLI-specified (and thus stringified) sync-source options.
type
CLISyncNodes
struct
{
// These sources can be loaded into SyncSourceSetup instances, to retrieve the active sync sources from.
type
CLISyncSources
struct
{
Endpoints
[]
string
Endpoints
[]
string
JWTSecretPaths
[]
string
JWTSecretPaths
[]
string
}
}
var
_
Sync
SourceCollection
=
(
*
CLISyncSourc
es
)(
nil
)
var
_
Sync
NodeCollection
=
(
*
CLISyncNod
es
)(
nil
)
func
(
p
*
CLISync
Sources
)
Load
(
ctx
context
.
Context
,
logger
log
.
Logger
)
([]
SyncSourc
eSetup
,
error
)
{
func
(
p
*
CLISync
Nodes
)
Load
(
ctx
context
.
Context
,
logger
log
.
Logger
)
([]
SyncNod
eSetup
,
error
)
{
if
err
:=
p
.
Check
();
err
!=
nil
{
// sanity-check, in case the caller did not check.
if
err
:=
p
.
Check
();
err
!=
nil
{
// sanity-check, in case the caller did not check.
return
nil
,
err
return
nil
,
err
}
}
...
@@ -39,7 +37,7 @@ func (p *CLISyncSources) Load(ctx context.Context, logger log.Logger) ([]SyncSou
...
@@ -39,7 +37,7 @@ func (p *CLISyncSources) Load(ctx context.Context, logger log.Logger) ([]SyncSou
}
}
secrets
=
append
(
secrets
,
secret
)
secrets
=
append
(
secrets
,
secret
)
}
}
setups
:=
make
([]
Sync
Sourc
eSetup
,
0
,
len
(
p
.
Endpoints
))
setups
:=
make
([]
Sync
Nod
eSetup
,
0
,
len
(
p
.
Endpoints
))
for
i
,
endpoint
:=
range
p
.
Endpoints
{
for
i
,
endpoint
:=
range
p
.
Endpoints
{
var
secret
eth
.
Bytes32
var
secret
eth
.
Bytes32
if
i
>=
len
(
secrets
)
{
if
i
>=
len
(
secrets
)
{
...
@@ -53,7 +51,7 @@ func (p *CLISyncSources) Load(ctx context.Context, logger log.Logger) ([]SyncSou
...
@@ -53,7 +51,7 @@ func (p *CLISyncSources) Load(ctx context.Context, logger log.Logger) ([]SyncSou
return
setups
,
nil
return
setups
,
nil
}
}
func
(
p
*
CLISync
Sourc
es
)
Check
()
error
{
func
(
p
*
CLISync
Nod
es
)
Check
()
error
{
if
len
(
p
.
Endpoints
)
==
len
(
p
.
JWTSecretPaths
)
{
if
len
(
p
.
Endpoints
)
==
len
(
p
.
JWTSecretPaths
)
{
return
nil
return
nil
}
}
...
...
op-supervisor/supervisor/backend/syncnode/controller.go
0 → 100644
View file @
5bdc9ac0
package
syncnode
import
(
"context"
"fmt"
"sync"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/locks"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum/go-ethereum/log"
)
type
chainsDB
interface
{
UpdateLocalSafe
(
types
.
ChainID
,
eth
.
BlockRef
,
eth
.
BlockRef
)
error
}
// SyncNodeController handles the sync node operations across multiple sync nodes
type
SyncNodesController
struct
{
logger
log
.
Logger
controllers
locks
.
RWMap
[
types
.
ChainID
,
SyncControl
]
db
chainsDB
depSet
depset
.
DependencySet
}
// NewSyncNodeController creates a new SyncNodeController
func
NewSyncNodesController
(
l
log
.
Logger
,
depset
depset
.
DependencySet
,
db
chainsDB
)
*
SyncNodesController
{
return
&
SyncNodesController
{
logger
:
l
,
depSet
:
depset
,
db
:
db
,
}
}
func
(
snc
*
SyncNodesController
)
AttachNodeController
(
id
types
.
ChainID
,
ctrl
SyncControl
)
error
{
if
!
snc
.
depSet
.
HasChain
(
id
)
{
return
fmt
.
Errorf
(
"chain %v not in dependency set"
,
id
)
}
snc
.
controllers
.
Set
(
id
,
ctrl
)
return
nil
}
// DeriveFromL1 derives the L2 blocks from the L1 block reference for all the chains
// if any chain fails to derive, the first error is returned
func
(
snc
*
SyncNodesController
)
DeriveFromL1
(
ref
eth
.
BlockRef
)
error
{
snc
.
logger
.
Debug
(
"deriving from L1"
,
"ref"
,
ref
)
returns
:=
make
(
chan
error
,
len
(
snc
.
depSet
.
Chains
()))
wg
:=
sync
.
WaitGroup
{}
// for now this function just prints all the chain-ids of controlled nodes, as a placeholder
for
_
,
chain
:=
range
snc
.
depSet
.
Chains
()
{
wg
.
Add
(
1
)
go
func
()
{
returns
<-
snc
.
DeriveToEnd
(
chain
,
ref
)
wg
.
Done
()
}()
}
wg
.
Wait
()
// collect all errors
errors
:=
[]
error
{}
for
i
:=
0
;
i
<
len
(
snc
.
depSet
.
Chains
());
i
++
{
err
:=
<-
returns
if
err
!=
nil
{
errors
=
append
(
errors
,
err
)
}
}
// log all errors, but only return the first one
if
len
(
errors
)
>
0
{
snc
.
logger
.
Warn
(
"sync nodes failed to derive from L1"
,
"errors"
,
errors
)
return
errors
[
0
]
}
return
nil
}
// DeriveToEnd derives the L2 blocks from the L1 block reference for a single chain
// it will continue to derive until no more blocks are derived
func
(
snc
*
SyncNodesController
)
DeriveToEnd
(
id
types
.
ChainID
,
ref
eth
.
BlockRef
)
error
{
ctrl
,
ok
:=
snc
.
controllers
.
Get
(
id
)
if
!
ok
{
snc
.
logger
.
Warn
(
"missing controller for chain. Not attempting derivation"
,
"chain"
,
id
)
return
nil
// maybe return an error?
}
for
{
derived
,
err
:=
ctrl
.
TryDeriveNext
(
context
.
Background
(),
ref
)
if
err
!=
nil
{
return
err
}
// if no more blocks are derived, we are done
// (or something? this exact behavior is yet to be defined by the node)
if
derived
==
(
eth
.
BlockRef
{})
{
return
nil
}
// record the new L2 to the local database
if
err
:=
snc
.
db
.
UpdateLocalSafe
(
id
,
ref
,
derived
);
err
!=
nil
{
return
err
}
}
}
op-supervisor/supervisor/backend/syncnode/controller_test.go
0 → 100644
View file @
5bdc9ac0
package
syncnode
import
(
"context"
"fmt"
"sync"
"testing"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/backend/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
type
mockChainsDB
struct
{
updateLocalSafeFn
func
(
chainID
types
.
ChainID
,
ref
eth
.
BlockRef
,
derived
eth
.
BlockRef
)
error
}
func
(
m
*
mockChainsDB
)
UpdateLocalSafe
(
chainID
types
.
ChainID
,
ref
eth
.
BlockRef
,
derived
eth
.
BlockRef
)
error
{
if
m
.
updateLocalSafeFn
!=
nil
{
return
m
.
updateLocalSafeFn
(
chainID
,
ref
,
derived
)
}
return
nil
}
type
mockSyncControl
struct
{
TryDeriveNextFn
func
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
}
func
(
m
*
mockSyncControl
)
TryDeriveNext
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
{
if
m
.
TryDeriveNextFn
!=
nil
{
return
m
.
TryDeriveNextFn
(
ctx
,
ref
)
}
return
eth
.
BlockRef
{},
nil
}
func
sampleDepSet
(
t
*
testing
.
T
)
depset
.
DependencySet
{
depSet
,
err
:=
depset
.
NewStaticConfigDependencySet
(
map
[
types
.
ChainID
]
*
depset
.
StaticConfigDependency
{
types
.
ChainIDFromUInt64
(
900
)
:
{
ChainIndex
:
900
,
ActivationTime
:
42
,
HistoryMinTime
:
100
,
},
types
.
ChainIDFromUInt64
(
901
)
:
{
ChainIndex
:
901
,
ActivationTime
:
30
,
HistoryMinTime
:
20
,
},
})
require
.
NoError
(
t
,
err
)
return
depSet
}
// TestAttachNodeController tests the AttachNodeController function of the SyncNodesController.
// Only controllers for chains in the dependency set can be attached.
func
TestAttachNodeController
(
t
*
testing
.
T
)
{
logger
:=
log
.
New
()
depSet
:=
sampleDepSet
(
t
)
controller
:=
NewSyncNodesController
(
logger
,
depSet
,
nil
)
require
.
Zero
(
t
,
controller
.
controllers
.
Len
(),
"controllers should be empty to start"
)
// Attach a controller for chain 900
ctrl
:=
mockSyncControl
{}
err
:=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
900
),
&
ctrl
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
1
,
controller
.
controllers
.
Len
(),
"controllers should have 1 entry"
)
// Attach a controller for chain 901
ctrl2
:=
mockSyncControl
{}
err
=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
901
),
&
ctrl2
)
require
.
NoError
(
t
,
err
)
require
.
Equal
(
t
,
2
,
controller
.
controllers
.
Len
(),
"controllers should have 2 entries"
)
// Attach a controller for chain 902 (which is not in the dependency set)
ctrl3
:=
mockSyncControl
{}
err
=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
902
),
&
ctrl3
)
require
.
Error
(
t
,
err
)
require
.
Equal
(
t
,
2
,
controller
.
controllers
.
Len
(),
"controllers should still have 2 entries"
)
}
// TestDeriveFromL1 tests the DeriveFromL1 function of the SyncNodesController for multiple chains
func
TestDeriveFromL1
(
t
*
testing
.
T
)
{
logger
:=
log
.
New
()
depSet
:=
sampleDepSet
(
t
)
// keep track of the updates for each chain with the mock
updates
:=
map
[
types
.
ChainID
][]
eth
.
BlockRef
{}
mockChainsDB
:=
mockChainsDB
{}
updateMu
:=
sync
.
Mutex
{}
mockChainsDB
.
updateLocalSafeFn
=
func
(
chainID
types
.
ChainID
,
ref
eth
.
BlockRef
,
derived
eth
.
BlockRef
)
error
{
updateMu
.
Lock
()
defer
updateMu
.
Unlock
()
updates
[
chainID
]
=
append
(
updates
[
chainID
],
derived
)
return
nil
}
controller
:=
NewSyncNodesController
(
logger
,
depSet
,
&
mockChainsDB
)
refA
:=
eth
.
BlockRef
{
Number
:
1
}
refB
:=
eth
.
BlockRef
{
Number
:
2
}
refC
:=
eth
.
BlockRef
{
Number
:
3
}
derived
:=
[]
eth
.
BlockRef
{
refA
,
refB
,
refC
}
// Attach a controller for chain 900 with a mock controller function
ctrl1
:=
mockSyncControl
{}
ctrl1i
:=
0
// the controller will return the next derived block each time TryDeriveNext is called
ctrl1
.
TryDeriveNextFn
=
func
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
{
defer
func
()
{
ctrl1i
++
}()
if
ctrl1i
>=
len
(
derived
)
{
return
eth
.
BlockRef
{},
nil
}
return
derived
[
ctrl1i
],
nil
}
err
:=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
900
),
&
ctrl1
)
require
.
NoError
(
t
,
err
)
// Attach a controller for chain 900 with a mock controller function
ctrl2
:=
mockSyncControl
{}
ctrl2i
:=
0
// the controller will return the next derived block each time TryDeriveNext is called
ctrl2
.
TryDeriveNextFn
=
func
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
{
defer
func
()
{
ctrl2i
++
}()
if
ctrl2i
>=
len
(
derived
)
{
return
eth
.
BlockRef
{},
nil
}
return
derived
[
ctrl2i
],
nil
}
err
=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
901
),
&
ctrl2
)
require
.
NoError
(
t
,
err
)
// Derive from L1
err
=
controller
.
DeriveFromL1
(
refA
)
require
.
NoError
(
t
,
err
)
// Check that the derived blocks were recorded for each chain
require
.
Equal
(
t
,
[]
eth
.
BlockRef
{
refA
,
refB
,
refC
},
updates
[
types
.
ChainIDFromUInt64
(
900
)])
require
.
Equal
(
t
,
[]
eth
.
BlockRef
{
refA
,
refB
,
refC
},
updates
[
types
.
ChainIDFromUInt64
(
901
)])
}
// TestDeriveFromL1Error tests that if a chain fails to derive from L1, the derived blocks up to the error are still recorded
// for that chain, and all other chains that derived successfully are also recorded.
func
TestDeriveFromL1Error
(
t
*
testing
.
T
)
{
logger
:=
log
.
New
()
depSet
:=
sampleDepSet
(
t
)
// keep track of the updates for each chain with the mock
updates
:=
map
[
types
.
ChainID
][]
eth
.
BlockRef
{}
mockChainsDB
:=
mockChainsDB
{}
updateMu
:=
sync
.
Mutex
{}
mockChainsDB
.
updateLocalSafeFn
=
func
(
chainID
types
.
ChainID
,
ref
eth
.
BlockRef
,
derived
eth
.
BlockRef
)
error
{
updateMu
.
Lock
()
defer
updateMu
.
Unlock
()
updates
[
chainID
]
=
append
(
updates
[
chainID
],
derived
)
return
nil
}
controller
:=
NewSyncNodesController
(
logger
,
depSet
,
&
mockChainsDB
)
refA
:=
eth
.
BlockRef
{
Number
:
1
}
refB
:=
eth
.
BlockRef
{
Number
:
2
}
refC
:=
eth
.
BlockRef
{
Number
:
3
}
derived
:=
[]
eth
.
BlockRef
{
refA
,
refB
,
refC
}
// Attach a controller for chain 900 with a mock controller function
ctrl1
:=
mockSyncControl
{}
ctrl1i
:=
0
// the controller will return the next derived block each time TryDeriveNext is called
ctrl1
.
TryDeriveNextFn
=
func
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
{
defer
func
()
{
ctrl1i
++
}()
if
ctrl1i
>=
len
(
derived
)
{
return
eth
.
BlockRef
{},
nil
}
return
derived
[
ctrl1i
],
nil
}
err
:=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
900
),
&
ctrl1
)
require
.
NoError
(
t
,
err
)
// Attach a controller for chain 900 with a mock controller function
ctrl2
:=
mockSyncControl
{}
ctrl2i
:=
0
// this controller will error on the last derived block
ctrl2
.
TryDeriveNextFn
=
func
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
{
defer
func
()
{
ctrl2i
++
}()
if
ctrl2i
>=
len
(
derived
)
-
1
{
return
eth
.
BlockRef
{},
fmt
.
Errorf
(
"error"
)
}
return
derived
[
ctrl2i
],
nil
}
err
=
controller
.
AttachNodeController
(
types
.
ChainIDFromUInt64
(
901
),
&
ctrl2
)
require
.
NoError
(
t
,
err
)
// Derive from L1
err
=
controller
.
DeriveFromL1
(
refA
)
require
.
Error
(
t
,
err
)
// Check that the derived blocks were recorded for each chain
// and in the case of the error, the derived blocks up to the error are recorded
require
.
Equal
(
t
,
[]
eth
.
BlockRef
{
refA
,
refB
,
refC
},
updates
[
types
.
ChainIDFromUInt64
(
900
)])
require
.
Equal
(
t
,
[]
eth
.
BlockRef
{
refA
,
refB
},
updates
[
types
.
ChainIDFromUInt64
(
901
)])
}
op-supervisor/supervisor/backend/sync
src
/dial.go
→
op-supervisor/supervisor/backend/sync
node
/dial.go
View file @
5bdc9ac0
package
sync
src
package
sync
node
import
(
import
(
"context"
"context"
...
@@ -13,16 +13,14 @@ import (
...
@@ -13,16 +13,14 @@ import (
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/eth"
)
)
// RPCDialSetup implements SyncSourceSetup by dialing an RPC
// and providing an RPC-client binding to sync with.
type
RPCDialSetup
struct
{
type
RPCDialSetup
struct
{
JWTSecret
eth
.
Bytes32
JWTSecret
eth
.
Bytes32
Endpoint
string
Endpoint
string
}
}
var
_
Sync
Sourc
eSetup
=
(
*
RPCDialSetup
)(
nil
)
var
_
Sync
Nod
eSetup
=
(
*
RPCDialSetup
)(
nil
)
func
(
r
*
RPCDialSetup
)
Setup
(
ctx
context
.
Context
,
logger
log
.
Logger
)
(
Sync
Sourc
e
,
error
)
{
func
(
r
*
RPCDialSetup
)
Setup
(
ctx
context
.
Context
,
logger
log
.
Logger
)
(
Sync
Nod
e
,
error
)
{
ctx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
time
.
Second
*
60
)
ctx
,
cancel
:=
context
.
WithTimeout
(
ctx
,
time
.
Second
*
60
)
defer
cancel
()
defer
cancel
()
...
@@ -35,7 +33,7 @@ func (r *RPCDialSetup) Setup(ctx context.Context, logger log.Logger) (SyncSource
...
@@ -35,7 +33,7 @@ func (r *RPCDialSetup) Setup(ctx context.Context, logger log.Logger) (SyncSource
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
return
&
RPCSync
Sourc
e
{
return
&
RPCSync
Nod
e
{
name
:
fmt
.
Sprintf
(
"RPCSyncSource(%s)"
,
r
.
Endpoint
),
name
:
fmt
.
Sprintf
(
"RPCSyncSource(%s)"
,
r
.
Endpoint
),
cl
:
rpcCl
,
cl
:
rpcCl
,
},
nil
},
nil
...
...
op-supervisor/supervisor/backend/sync
src
/iface.go
→
op-supervisor/supervisor/backend/sync
node
/iface.go
View file @
5bdc9ac0
package
sync
src
package
sync
node
import
(
import
(
"context"
"context"
...
@@ -11,19 +11,15 @@ import (
...
@@ -11,19 +11,15 @@ import (
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
// SyncSourceCollection turns a bundle of options into individual options for SyncSourceSetup.
type
SyncNodeCollection
interface
{
// This enables configurations to share properties.
Load
(
ctx
context
.
Context
,
logger
log
.
Logger
)
([]
SyncNodeSetup
,
error
)
type
SyncSourceCollection
interface
{
Load
(
ctx
context
.
Context
,
logger
log
.
Logger
)
([]
SyncSourceSetup
,
error
)
Check
()
error
Check
()
error
}
}
// SyncSourceSetup sets up a new active SyncSource. Setup may fail, e.g. an RPC endpoint is invalid.
type
SyncNodeSetup
interface
{
type
SyncSourceSetup
interface
{
Setup
(
ctx
context
.
Context
,
logger
log
.
Logger
)
(
SyncNode
,
error
)
Setup
(
ctx
context
.
Context
,
logger
log
.
Logger
)
(
SyncSource
,
error
)
}
}
// SyncSource provides an interface to interact with a source node to e.g. sync event data.
type
SyncSource
interface
{
type
SyncSource
interface
{
BlockRefByNumber
(
ctx
context
.
Context
,
number
uint64
)
(
eth
.
BlockRef
,
error
)
BlockRefByNumber
(
ctx
context
.
Context
,
number
uint64
)
(
eth
.
BlockRef
,
error
)
FetchReceipts
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
gethtypes
.
Receipts
,
error
)
FetchReceipts
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
gethtypes
.
Receipts
,
error
)
...
@@ -31,3 +27,12 @@ type SyncSource interface {
...
@@ -31,3 +27,12 @@ type SyncSource interface {
// String identifies the sync source
// String identifies the sync source
String
()
string
String
()
string
}
}
type
SyncControl
interface
{
TryDeriveNext
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
}
type
SyncNode
interface
{
SyncSource
SyncControl
}
op-supervisor/supervisor/backend/sync
src
/rpc.go
→
op-supervisor/supervisor/backend/sync
node
/rpc.go
View file @
5bdc9ac0
package
sync
src
package
sync
node
import
(
import
(
"context"
"context"
...
@@ -14,22 +14,23 @@ import (
...
@@ -14,22 +14,23 @@ import (
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
)
// RPCSyncSource is an active RPC, wrapped with bindings, implementing the SyncSource interface.
type
RPCSyncNode
struct
{
type
RPCSyncSource
struct
{
name
string
name
string
cl
client
.
RPC
cl
client
.
RPC
}
}
func
NewRPCSync
Source
(
name
string
,
cl
client
.
RPC
)
*
RPCSyncSourc
e
{
func
NewRPCSync
Node
(
name
string
,
cl
client
.
RPC
)
*
RPCSyncNod
e
{
return
&
RPCSync
Sourc
e
{
return
&
RPCSync
Nod
e
{
name
:
name
,
name
:
name
,
cl
:
cl
,
cl
:
cl
,
}
}
}
}
var
_
SyncSource
=
(
*
RPCSyncSource
)(
nil
)
var
_
SyncSource
=
(
*
RPCSyncNode
)(
nil
)
var
_
SyncControl
=
(
*
RPCSyncNode
)(
nil
)
var
_
SyncNode
=
(
*
RPCSyncNode
)(
nil
)
func
(
rs
*
RPCSync
Sourc
e
)
BlockRefByNumber
(
ctx
context
.
Context
,
number
uint64
)
(
eth
.
BlockRef
,
error
)
{
func
(
rs
*
RPCSync
Nod
e
)
BlockRefByNumber
(
ctx
context
.
Context
,
number
uint64
)
(
eth
.
BlockRef
,
error
)
{
var
out
*
eth
.
BlockRef
var
out
*
eth
.
BlockRef
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
out
,
"interop_blockRefByNumber"
,
number
)
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
out
,
"interop_blockRefByNumber"
,
number
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -44,7 +45,7 @@ func (rs *RPCSyncSource) BlockRefByNumber(ctx context.Context, number uint64) (e
...
@@ -44,7 +45,7 @@ func (rs *RPCSyncSource) BlockRefByNumber(ctx context.Context, number uint64) (e
return
*
out
,
nil
return
*
out
,
nil
}
}
func
(
rs
*
RPCSync
Sourc
e
)
FetchReceipts
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
gethtypes
.
Receipts
,
error
)
{
func
(
rs
*
RPCSync
Nod
e
)
FetchReceipts
(
ctx
context
.
Context
,
blockHash
common
.
Hash
)
(
gethtypes
.
Receipts
,
error
)
{
var
out
gethtypes
.
Receipts
var
out
gethtypes
.
Receipts
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
out
,
"interop_fetchReceipts"
,
blockHash
)
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
out
,
"interop_fetchReceipts"
,
blockHash
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -59,12 +60,18 @@ func (rs *RPCSyncSource) FetchReceipts(ctx context.Context, blockHash common.Has
...
@@ -59,12 +60,18 @@ func (rs *RPCSyncSource) FetchReceipts(ctx context.Context, blockHash common.Has
return
out
,
nil
return
out
,
nil
}
}
func
(
rs
*
RPCSync
Sourc
e
)
ChainID
(
ctx
context
.
Context
)
(
types
.
ChainID
,
error
)
{
func
(
rs
*
RPCSync
Nod
e
)
ChainID
(
ctx
context
.
Context
)
(
types
.
ChainID
,
error
)
{
var
chainID
types
.
ChainID
var
chainID
types
.
ChainID
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
chainID
,
"interop_chainID"
)
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
chainID
,
"interop_chainID"
)
return
chainID
,
err
return
chainID
,
err
}
}
func
(
rs
*
RPCSync
Sourc
e
)
String
()
string
{
func
(
rs
*
RPCSync
Nod
e
)
String
()
string
{
return
rs
.
name
return
rs
.
name
}
}
func
(
rs
*
RPCSyncNode
)
TryDeriveNext
(
ctx
context
.
Context
,
ref
eth
.
BlockRef
)
(
eth
.
BlockRef
,
error
)
{
err
:=
rs
.
cl
.
CallContext
(
ctx
,
&
ref
,
"interop_tryDeriveNext"
)
// the node only returns an error currently
return
eth
.
BlockRef
{},
err
}
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