Commit 9309f9d4 authored by clabby's avatar clabby

Init backup sync via RPC

parent 1729edd3
...@@ -185,6 +185,12 @@ var ( ...@@ -185,6 +185,12 @@ var (
EnvVar: prefixEnvVar("HEARTBEAT_URL"), EnvVar: prefixEnvVar("HEARTBEAT_URL"),
Value: "https://heartbeat.optimism.io", Value: "https://heartbeat.optimism.io",
} }
BackupL2UnsafeSyncRPC = cli.StringFlag{
Name: "l2.backup-unsafe-sync-rpc",
Usage: "Set the backup L2 unsafe sync RPC endpoint.",
EnvVar: prefixEnvVar("L2_BACKUP_UNSAFE_SYNC_RPC"),
Required: false,
}
) )
var requiredFlags = []cli.Flag{ var requiredFlags = []cli.Flag{
...@@ -219,6 +225,7 @@ var optionalFlags = append([]cli.Flag{ ...@@ -219,6 +225,7 @@ var optionalFlags = append([]cli.Flag{
HeartbeatEnabledFlag, HeartbeatEnabledFlag,
HeartbeatMonikerFlag, HeartbeatMonikerFlag,
HeartbeatURLFlag, HeartbeatURLFlag,
BackupL2UnsafeSyncRPC,
}, p2pFlags...) }, p2pFlags...)
// Flags contains the list of configuration options available to the binary. // Flags contains the list of configuration options available to the binary.
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup"
...@@ -370,6 +371,54 @@ func (eq *EngineQueue) tryNextUnsafePayload(ctx context.Context) error { ...@@ -370,6 +371,54 @@ func (eq *EngineQueue) tryNextUnsafePayload(ctx context.Context) error {
eq.log.Info("skipping unsafe payload, since it does not build onto the existing unsafe chain", "safe", eq.safeHead.ID(), "unsafe", first.ID(), "payload", first.ID()) eq.log.Info("skipping unsafe payload, since it does not build onto the existing unsafe chain", "safe", eq.safeHead.ID(), "unsafe", first.ID(), "payload", first.ID())
eq.unsafePayloads.Pop() eq.unsafePayloads.Pop()
} }
// Request the payload that builds upon the current unsafe head from the fallback RPC.
// This is a temporary alternative sync method- in the future, this will be done over the p2p network.
if eq.cfg.BackupL2UnsafeSyncRPC != "" {
eq.log.Info("requesting unsafe payload from backup RPC", "unsafe head", eq.unsafeHead.ID(), "first unsafe payload", first.ID(), "backup rpc", eq.cfg.BackupL2UnsafeSyncRPC)
// TODO: Create a client for the backup RPC and request the payload from the backup sync RPC via the `eth_getBlockByNumber` method.
// Once the payload has been received, verify its integrity and push it into the priority queue.
// TODO: Post Shanghai hardfork, the engine API's `PayloadBodiesByRange` method will be much more efficient, but for now,
// the `eth_getBlockByNumber` method is more widely available.
// Dial the backup unsafe sync RPC.
// TODO: Should this request block this thread (with a reasonable timeout) so that we can attempt to continue when the payload
// has been received and pushed into the priority queue? Or should it be made concurrently?
client, err := rpc.DialHTTP(eq.cfg.BackupL2UnsafeSyncRPC)
if err != nil {
return NewTemporaryError(fmt.Errorf("failed to dial backup unsafe sync RPC: %w", err))
}
// Fetch the next unsafe block from the backup unsafe sync RPC.
var block *types.Block
timeoutCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
if err = client.CallContext(timeoutCtx, &block, "eth_getBlockByNumber", eq.unsafeHead.Number+1); err != nil {
return NewTemporaryError(fmt.Errorf("failed to get next unsafe block from backup unsafe sync RPC: %w", err))
}
// Convert the received block to a `eth.ExecutionPayload`.
payload, err := eth.BlockAsPayload(block)
if err != nil {
return NewTemporaryError(fmt.Errorf("failed to convert block to execution payload: %w", err))
}
// TODO: Validate the integrity of the payload.
if _, ok := payload.CheckBlockHash(); !ok {
return NewTemporaryError(fmt.Errorf("received invalid payload from backup unsafe sync RPC; invalid block hash"))
}
eq.log.Info("received unsafe payload from backup RPC", "payload", payload.ID(), "backup rpc", eq.cfg.BackupL2UnsafeSyncRPC)
// Add the received execution payload to the unsafe payload priority queue.
eq.AddUnsafePayload(payload)
eq.log.Info("inserted received unsafe payload into priority queue", "payload", payload.ID(), "backup rpc", eq.cfg.BackupL2UnsafeSyncRPC)
// TODO: Should we attempt to continue here, or wait for the next iteration of the state loop and still return EOF?
}
return io.EOF // time to go to next stage if we cannot process the first unsafe payload return io.EOF // time to go to next stage if we cannot process the first unsafe payload
} }
......
...@@ -36,6 +36,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { ...@@ -36,6 +36,11 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) {
return nil, err return nil, err
} }
// If the backup sync RPC flag is set, then use it over the value present in the config file.
if backupSyncRPC := ctx.GlobalString(flags.BackupL2UnsafeSyncRPC.Name); backupSyncRPC != "" {
rollupConfig.BackupL2UnsafeSyncRPC = backupSyncRPC
}
driverConfig, err := NewDriverConfig(ctx) driverConfig, err := NewDriverConfig(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment