-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add experimental L2 source flag
- Loading branch information
Showing
6 changed files
with
236 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package host | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/ethereum-optimism/optimism/op-program/host/prefetcher" | ||
"github.com/ethereum-optimism/optimism/op-service/client" | ||
"github.com/ethereum-optimism/optimism/op-service/eth" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
) | ||
|
||
type L2ExperimentalClient struct { | ||
*L2Client | ||
client client.RPC | ||
} | ||
|
||
var _ prefetcher.L2Source = &L2ExperimentalClient{} | ||
|
||
func NewL2ExperimentalClient(l2Client *L2Client, client client.RPC) *L2ExperimentalClient { | ||
return &L2ExperimentalClient{ | ||
L2Client: l2Client, | ||
client: client, | ||
} | ||
} | ||
|
||
// CodeByHash implements prefetcher.L2Source. | ||
func (s *L2ExperimentalClient) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { | ||
panic("unsupported") | ||
} | ||
|
||
// NodeByHash implements prefetcher.L2Source. | ||
func (s *L2ExperimentalClient) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { | ||
panic("unsupported") | ||
} | ||
|
||
func (s *L2ExperimentalClient) ExecutionWitness(ctx context.Context, blockNum uint64) (*eth.ExecutionWitness, error) { | ||
var witness eth.ExecutionWitness | ||
|
||
err := s.client.CallContext(ctx, &witness, "debug_executionWitness", hexutil.EncodeUint64(blockNum), true) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &witness, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package host | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"github.com/ethereum-optimism/optimism/op-program/host/config" | ||
"github.com/ethereum-optimism/optimism/op-program/host/prefetcher" | ||
"github.com/ethereum-optimism/optimism/op-service/client" | ||
"github.com/ethereum-optimism/optimism/op-service/eth" | ||
"github.com/ethereum-optimism/optimism/op-service/sources" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/log" | ||
) | ||
|
||
// L2Source is a source of L2 data, it abstracts away the details of how to fetch L2 data between canonical and experimental sources. | ||
// It also tracks metrics for each of the apis. Once experimental sources are stable, this will only route to the "experimental" source. | ||
type L2Source struct { | ||
logger log.Logger | ||
|
||
// canonical source, used as a fallback if experimental source is enabled but fails | ||
// the underlying node should be a geth hash scheme archival node. | ||
canonicalEthClient *L2Client | ||
canonicalDebugClient *sources.DebugClient | ||
|
||
// experimental source, used as the primary source if enabled | ||
experimentalClient *L2ExperimentalClient | ||
|
||
// whether to use the experimental source | ||
experimentalEnabled bool | ||
} | ||
|
||
var _ prefetcher.L2Source = &L2Source{} | ||
|
||
// NewL2SourceWithClient creates a new L2 source with the given client as the canonical client. | ||
// This doesn't configure the experimental source, but is useful for testing. | ||
func NewL2SourceWithClient(logger log.Logger, canonicalL2Client *L2Client, canonicalDebugClient *sources.DebugClient) *L2Source { | ||
source := &L2Source{ | ||
logger: logger, | ||
canonicalEthClient: canonicalL2Client, | ||
canonicalDebugClient: canonicalDebugClient, | ||
} | ||
|
||
return source | ||
} | ||
|
||
func NewL2Source(ctx context.Context, logger log.Logger, config *config.Config) (*L2Source, error) { | ||
logger.Info("Connecting to canonical L2 source", "url", config.L2URL) | ||
|
||
// eth_getProof calls are expensive and takes time, so we use a longer timeout | ||
canonicalL2RPC, err := client.NewRPC(ctx, logger, config.L2URL, client.WithDialBackoff(10), client.WithCallTimeout(5*time.Minute)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
canonicalDebugClient := sources.NewDebugClient(canonicalL2RPC.CallContext) | ||
|
||
canonicalL2ClientCfg := sources.L2ClientDefaultConfig(config.Rollup, true) | ||
canonicalL2Client, err := NewL2Client(canonicalL2RPC, logger, nil, &L2ClientConfig{L2ClientConfig: canonicalL2ClientCfg, L2Head: config.L2Head}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
source := NewL2SourceWithClient(logger, canonicalL2Client, canonicalDebugClient) | ||
|
||
if !config.L2ExperimentalEnabled { | ||
return source, nil | ||
} | ||
|
||
logger.Info("Connecting to experimental L2 source", "url", config.L2ExperimentalURL) | ||
// debug_executionWitness calls are expensive and takes time, so we use a longer timeout | ||
experimentalRPC, err := client.NewRPC(ctx, logger, config.L2ExperimentalURL, client.WithDialBackoff(10), client.WithCallTimeout(5*time.Minute)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
experimentalL2ClientCfg := sources.L2ClientDefaultConfig(config.Rollup, true) | ||
experimentalL2Client, err := NewL2Client(experimentalRPC, logger, nil, &L2ClientConfig{L2ClientConfig: experimentalL2ClientCfg, L2Head: config.L2Head}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
experimentalClient := NewL2ExperimentalClient(experimentalL2Client, experimentalRPC) | ||
source.experimentalClient = experimentalClient | ||
source.experimentalEnabled = true | ||
|
||
return source, nil | ||
} | ||
|
||
// CodeByHash implements prefetcher.L2Source. | ||
func (l *L2Source) CodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { | ||
if l.experimentalEnabled { | ||
// This means experimental source was not able to retrieve relevant information from eth_getProof or debug_executionWitness | ||
// We should fall back to the canonical source, and log a warning, and record a metric | ||
l.logger.Warn("Experimental source failed to retrieve code by hash, falling back to canonical source", "hash", hash) | ||
} | ||
return l.canonicalDebugClient.CodeByHash(ctx, hash) | ||
} | ||
|
||
// NodeByHash implements prefetcher.L2Source. | ||
func (l *L2Source) NodeByHash(ctx context.Context, hash common.Hash) ([]byte, error) { | ||
if l.experimentalEnabled { | ||
// This means experimental source was not able to retrieve relevant information from eth_getProof or debug_executionWitness | ||
// We should fall back to the canonical source, and log a warning, and record a metric | ||
l.logger.Warn("Experimental source failed to retrieve node by hash, falling back to canonical source", "hash", hash) | ||
} | ||
return l.canonicalDebugClient.NodeByHash(ctx, hash) | ||
} | ||
|
||
// InfoAndTxsByHash implements prefetcher.L2Source. | ||
func (l *L2Source) InfoAndTxsByHash(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Transactions, error) { | ||
if l.experimentalEnabled { | ||
return l.experimentalClient.InfoAndTxsByHash(ctx, blockHash) | ||
} | ||
return l.canonicalEthClient.InfoAndTxsByHash(ctx, blockHash) | ||
} | ||
|
||
// OutputByRoot implements prefetcher.L2Source. | ||
func (l *L2Source) OutputByRoot(ctx context.Context, root common.Hash) (eth.Output, error) { | ||
if l.experimentalEnabled { | ||
return l.experimentalClient.OutputByRoot(ctx, root) | ||
} | ||
return l.canonicalEthClient.OutputByRoot(ctx, root) | ||
} | ||
|
||
// ExecutionWitness implements prefetcher.L2Source. | ||
func (l *L2Source) ExecutionWitness(ctx context.Context, blockNum uint64) (*eth.ExecutionWitness, error) { | ||
if !l.experimentalEnabled { | ||
l.logger.Error("Experimental source is not enabled, cannot fetch execution witness", "blockNum", blockNum) | ||
panic("experimental source is not enabled") | ||
} | ||
return l.experimentalClient.ExecutionWitness(ctx, blockNum) | ||
} | ||
|
||
// GetProof implements prefetcher.L2Source. | ||
func (l *L2Source) GetProof(ctx context.Context, address common.Address, storage []common.Hash, blockTag string) (*eth.AccountResult, error) { | ||
if l.experimentalEnabled { | ||
l.experimentalClient.GetProof(ctx, address, storage, blockTag) | ||
} | ||
return l.canonicalEthClient.GetProof(ctx, address, storage, blockTag) | ||
} |