Commit bfb1ad93 authored by protolambda's avatar protolambda Committed by GitHub

op-supervisor,op-e2e: op-supervisor action test (#12691)

parent 36b41bae
......@@ -72,7 +72,7 @@ func NewL2AltDA(t helpers.Testing, params ...AltDAParam) *L2AltDA {
l1Client := miner.EthClient()
jwtPath := e2eutils.WriteDefaultJWT(t)
engine := helpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
engine := helpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath)
engCl := engine.EngineClient(t, sd.RollupCfg)
storage := &altda.DAErrFaker{Client: altda.NewMockDAClient(log)}
......@@ -136,7 +136,7 @@ func (a *L2AltDA) StorageClient() *altda.DAErrFaker {
func (a *L2AltDA) NewVerifier(t helpers.Testing) *helpers.L2Verifier {
jwtPath := e2eutils.WriteDefaultJWT(t)
engine := helpers.NewL2Engine(t, a.log, a.sd.L2Cfg, a.sd.RollupCfg.Genesis.L1, jwtPath)
engine := helpers.NewL2Engine(t, a.log, a.sd.L2Cfg, jwtPath)
engCl := engine.EngineClient(t, a.sd.RollupCfg)
l1F, err := sources.NewL1Client(a.miner.RPCClient(), a.log, nil, sources.L1ClientDefaultConfig(a.sd.RollupCfg, false, sources.RPCKindBasic))
require.NoError(t, err)
......
......@@ -592,7 +592,7 @@ func RestartOpGeth(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard))
require.NoError(t, err)
// Sequencer
seqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, dbOption)
seqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath, dbOption)
engRpc := &rpcWrapper{seqEng.RPCClient()}
l2Cl, err := sources.NewEngineClient(engRpc, log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err)
......@@ -647,7 +647,7 @@ func RestartOpGeth(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
// close the sequencer engine
require.NoError(t, seqEng.Close())
// and start a new one with same db path
seqEngNew := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, dbOption)
seqEngNew := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath, dbOption)
// swap in the new rpc. This is as close as we can get to reconnecting to a new in-memory rpc connection
engRpc.RPC = seqEngNew.RPCClient()
......@@ -679,7 +679,7 @@ func ConflictingL2Blocks(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
// Extra setup: a full alternative sequencer, sequencer engine, and batcher
jwtPath := e2eutils.WriteDefaultJWT(t)
altSeqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
altSeqEng := actionsHelpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath)
altSeqEngCl, err := sources.NewEngineClient(altSeqEng.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err)
l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard))
......
......@@ -24,7 +24,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/rollup"
"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-optimism/optimism/op-service/testutils"
)
......@@ -37,8 +36,6 @@ type L2Engine struct {
node *node.Node
Eth *geth.Ethereum
rollupGenesis *rollup.Genesis
// L2 evm / chain
l2Chain *core.BlockChain
l2Signer types.Signer
......@@ -50,20 +47,14 @@ type L2Engine struct {
type EngineOption func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error
func NewL2Engine(t Testing, log log.Logger, genesis *core.Genesis, rollupGenesisL1 eth.BlockID, jwtPath string, options ...EngineOption) *L2Engine {
func NewL2Engine(t Testing, log log.Logger, genesis *core.Genesis, jwtPath string, options ...EngineOption) *L2Engine {
n, ethBackend, apiBackend := newBackend(t, genesis, jwtPath, options)
engineApi := engineapi.NewL2EngineAPI(log, apiBackend, ethBackend.Downloader())
chain := ethBackend.BlockChain()
genesisBlock := chain.Genesis()
eng := &L2Engine{
log: log,
node: n,
Eth: ethBackend,
rollupGenesis: &rollup.Genesis{
L1: rollupGenesisL1,
L2: eth.BlockID{Hash: genesisBlock.Hash(), Number: genesisBlock.NumberU64()},
L2Time: genesis.Timestamp,
},
l2Chain: chain,
l2Signer: types.LatestSigner(genesis.Config),
EngineApi: engineApi,
......
......@@ -40,7 +40,7 @@ func TestL2EngineAPI(gt *testing.T) {
tdb := triedb.NewDatabase(db, &triedb.Config{HashDB: hashdb.Defaults})
sd.L2Cfg.MustCommit(db, tdb)
engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
engine := NewL2Engine(t, log, sd.L2Cfg, jwtPath)
l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err)
......@@ -115,7 +115,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) {
tdb := triedb.NewDatabase(db, &triedb.Config{HashDB: hashdb.Defaults})
sd.L2Cfg.MustCommit(db, tdb)
engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
engine := NewL2Engine(t, log, sd.L2Cfg, jwtPath)
t.Cleanup(func() {
_ = engine.Close()
})
......@@ -211,7 +211,7 @@ func TestL2EngineAPIFail(gt *testing.T) {
dp := e2eutils.MakeDeployParams(t, DefaultRollupTestParams())
sd := e2eutils.Setup(t, dp, DefaultAlloc)
log := testlog.Logger(t, log.LevelDebug)
engine := NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
engine := NewL2Engine(t, log, sd.L2Cfg, jwtPath)
// mock an RPC failure
mockErr := errors.New("mock L2 RPC error")
engine.ActL2RPCFail(t, mockErr)
......
......@@ -25,7 +25,7 @@ func SetupSequencerTest(t Testing, sd *e2eutils.SetupData, log log.Logger, opts
l1F, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard))
require.NoError(t, err)
engine := NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, EngineWithP2P())
engine := NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, jwtPath, EngineWithP2P())
l2Cl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err)
......@@ -40,7 +40,7 @@ func SetupVerifier(t Testing, sd *e2eutils.SetupData, log log.Logger,
opt(cfg)
}
jwtPath := e2eutils.WriteDefaultJWT(t)
engine := NewL2Engine(t, log.New("role", "verifier-engine"), sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, EngineWithP2P())
engine := NewL2Engine(t, log.New("role", "verifier-engine"), sd.L2Cfg, jwtPath, EngineWithP2P())
engCl := engine.EngineClient(t, sd.RollupCfg)
verifier := NewL2Verifier(t, log.New("role", "verifier"), l1F, blobSrc, altda.Disabled, engCl, sd.RollupCfg, syncCfg, cfg.SafeHeadListener, cfg.InteropBackend)
return engine, verifier
......
package interop
import (
"context"
"os"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
altda "github.com/ethereum-optimism/optimism/op-alt-da"
batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags"
"github.com/ethereum-optimism/optimism/op-chain-ops/devkeys"
"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-chain-ops/interopgen"
"github.com/ethereum-optimism/optimism/op-e2e/actions/helpers"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/interop"
"github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/testlog"
"github.com/ethereum-optimism/optimism/op-supervisor/config"
"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/depset"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/frontend"
"github.com/ethereum-optimism/optimism/op-supervisor/supervisor/types"
)
const (
foundryArtifactsDir = "../../../packages/contracts-bedrock/forge-artifacts"
sourceMapDir = "../../../packages/contracts-bedrock"
)
// Chain holds the most common per-chain action-test data and actors
type Chain struct {
ChainID types.ChainID
RollupCfg *rollup.Config
ChainCfg *params.ChainConfig
BatcherAddr common.Address
Sequencer *helpers.L2Sequencer
SequencerEngine *helpers.L2Engine
Batcher *helpers.L2Batcher
}
// InteropSetup holds the chain deployment and config contents, before instantiating any services.
type InteropSetup struct {
Log log.Logger
Deployment *interopgen.WorldDeployment
Out *interopgen.WorldOutput
DepSet *depset.StaticConfigDependencySet
Keys devkeys.Keys
T helpers.Testing
}
// InteropActors holds a bundle of global actors and actors of 2 chains.
type InteropActors struct {
L1Miner *helpers.L1Miner
Supervisor *SupervisorActor
ChainA *Chain
ChainB *Chain
}
// SetupInterop creates an InteropSetup to instantiate actors on, with 2 L2 chains.
func SetupInterop(t helpers.Testing) *InteropSetup {
logger := testlog.Logger(t, log.LevelDebug)
recipe := interopgen.InteropDevRecipe{
L1ChainID: 900100,
L2ChainIDs: []uint64{900200, 900201},
GenesisTimestamp: uint64(time.Now().Unix() + 3),
}
hdWallet, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic)
require.NoError(t, err)
worldCfg, err := recipe.Build(hdWallet)
require.NoError(t, err)
// create the foundry artifacts and source map
foundryArtifacts := foundry.OpenArtifactsDir(foundryArtifactsDir)
sourceMap := foundry.NewSourceMapFS(os.DirFS(sourceMapDir))
// deploy the world, using the logger, foundry artifacts, source map, and world configuration
worldDeployment, worldOutput, err := interopgen.Deploy(logger, foundryArtifacts, sourceMap, worldCfg)
require.NoError(t, err)
return &InteropSetup{
Log: logger,
Deployment: worldDeployment,
Out: worldOutput,
DepSet: worldToDepSet(t, worldOutput),
Keys: hdWallet,
T: t,
}
}
func (is *InteropSetup) CreateActors() *InteropActors {
l1Miner := helpers.NewL1Miner(is.T, is.Log.New("role", "l1Miner"), is.Out.L1.Genesis)
supervisorAPI := NewSupervisor(is.T, is.Log, is.DepSet)
require.NoError(is.T, supervisorAPI.Start(is.T.Ctx()))
is.T.Cleanup(func() {
require.NoError(is.T, supervisorAPI.Stop(context.Background()))
})
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)
// Hook up L2 RPCs to supervisor, to fetch event data from
require.NoError(is.T, supervisorAPI.AddL2RPC(is.T.Ctx(), chainA.SequencerEngine.HTTPEndpoint()))
require.NoError(is.T, supervisorAPI.AddL2RPC(is.T.Ctx(), chainB.SequencerEngine.HTTPEndpoint()))
return &InteropActors{
L1Miner: l1Miner,
Supervisor: supervisorAPI,
ChainA: chainA,
ChainB: chainB,
}
}
// SupervisorActor represents a supervisor, instrumented to run synchronously for action-test purposes.
type SupervisorActor struct {
backend *backend.SupervisorBackend
frontend.QueryFrontend
frontend.AdminFrontend
frontend.UpdatesFrontend
}
func (sa *SupervisorActor) SyncEvents(t helpers.Testing, chainID types.ChainID) {
require.NoError(t, sa.backend.SyncEvents(chainID))
}
func (sa *SupervisorActor) SyncCrossUnsafe(t helpers.Testing, chainID types.ChainID) {
require.NoError(t, sa.backend.SyncCrossUnsafe(chainID))
}
func (sa *SupervisorActor) SyncCrossSafe(t helpers.Testing, chainID types.ChainID) {
require.NoError(t, sa.backend.SyncCrossSafe(chainID))
}
// worldToDepSet converts a set of chain configs into a dependency-set for the supervisor.
func worldToDepSet(t helpers.Testing, worldOutput *interopgen.WorldOutput) *depset.StaticConfigDependencySet {
depSetCfg := make(map[types.ChainID]*depset.StaticConfigDependency)
for _, out := range worldOutput.L2s {
depSetCfg[types.ChainIDFromBig(out.Genesis.Config.ChainID)] = &depset.StaticConfigDependency{
ChainIndex: types.ChainIndex(out.Genesis.Config.ChainID.Uint64()),
ActivationTime: 0,
HistoryMinTime: 0,
}
}
depSet, err := depset.NewStaticConfigDependencySet(depSetCfg)
require.NoError(t, err)
return depSet
}
// NewSupervisor creates a new SupervisorActor, to action-test the supervisor with.
func NewSupervisor(t helpers.Testing, logger log.Logger, depSet depset.DependencySetSource) *SupervisorActor {
logger = logger.New("role", "supervisor")
supervisorDataDir := t.TempDir()
logger.Info("supervisor data dir", "dir", supervisorDataDir)
svCfg := &config.Config{
DependencySetSource: depSet,
SynchronousProcessors: true,
Datadir: supervisorDataDir,
}
b, err := backend.NewSupervisorBackend(t.Ctx(),
logger.New("role", "supervisor"), metrics.NoopMetrics, svCfg)
require.NoError(t, err)
return &SupervisorActor{
backend: b,
QueryFrontend: frontend.QueryFrontend{
Supervisor: b,
},
AdminFrontend: frontend.AdminFrontend{
Supervisor: b,
},
UpdatesFrontend: frontend.UpdatesFrontend{
Supervisor: b,
},
}
}
// createL2Services creates a Chain bundle, with the given configs, and attached to the given L1 miner.
func createL2Services(
t helpers.Testing,
logger log.Logger,
l1Miner *helpers.L1Miner,
keys devkeys.Keys,
output *interopgen.L2Output,
interopBackend interop.InteropBackend,
) *Chain {
logger = logger.New("chain", output.Genesis.Config.ChainID)
jwtPath := e2eutils.WriteDefaultJWT(t)
eng := helpers.NewL2Engine(t, logger.New("role", "engine"), output.Genesis, jwtPath)
seqCl, err := sources.NewEngineClient(eng.RPCClient(), logger, nil, sources.EngineClientDefaultConfig(output.RollupCfg))
require.NoError(t, err)
l1F, err := sources.NewL1Client(l1Miner.RPCClient(), logger, nil,
sources.L1ClientDefaultConfig(output.RollupCfg, false, sources.RPCKindStandard))
require.NoError(t, err)
seq := helpers.NewL2Sequencer(t, logger.New("role", "sequencer"), l1F,
l1Miner.BlobStore(), altda.Disabled, seqCl, output.RollupCfg,
0, interopBackend)
batcherKey, err := keys.Secret(devkeys.ChainOperatorKey{
ChainID: output.Genesis.Config.ChainID,
Role: devkeys.BatcherRole,
})
require.NoError(t, err)
batcherCfg := &helpers.BatcherCfg{
MinL1TxSize: 0,
MaxL1TxSize: 128_000,
BatcherKey: batcherKey,
DataAvailabilityType: batcherFlags.CalldataType,
}
batcher := helpers.NewL2Batcher(logger.New("role", "batcher"), output.RollupCfg, batcherCfg,
seq.RollupClient(), l1Miner.EthClient(),
eng.EthClient(), eng.EngineClient(t, output.RollupCfg))
return &Chain{
ChainID: types.ChainIDFromBig(output.Genesis.Config.ChainID),
RollupCfg: output.RollupCfg,
ChainCfg: output.Genesis.Config,
BatcherAddr: crypto.PubkeyToAddress(batcherKey.PublicKey),
Sequencer: seq,
SequencerEngine: eng,
Batcher: batcher,
}
}
This diff is collapsed.
......@@ -74,7 +74,7 @@ func NewL2FaultProofEnv[c any](t helpers.Testing, testCfg *TestCfg[c], tp *e2eut
l1Cl, err := sources.NewL1Client(miner.RPCClient(), log, nil, sources.L1ClientDefaultConfig(sd.RollupCfg, false, sources.RPCKindStandard))
require.NoError(t, err)
engine := helpers.NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath, helpers.EngineWithP2P())
engine := helpers.NewL2Engine(t, log.New("role", "sequencer-engine"), sd.L2Cfg, jwtPath, helpers.EngineWithP2P())
l2EngineCl, err := sources.NewEngineClient(engine.RPCClient(), log, nil, sources.EngineClientDefaultConfig(sd.RollupCfg))
require.NoError(t, err)
......
......@@ -190,7 +190,7 @@ func aliceSimpleBlobTx(t helpers.Testing, dp *e2eutils.DeployParams) *types.Tran
func newEngine(t helpers.Testing, sd *e2eutils.SetupData, log log.Logger) *helpers.L2Engine {
jwtPath := e2eutils.WriteDefaultJWT(t)
return helpers.NewL2Engine(t, log, sd.L2Cfg, sd.RollupCfg.Genesis.L1, jwtPath)
return helpers.NewL2Engine(t, log, sd.L2Cfg, jwtPath)
}
// TestDencunBlobTxRPC tries to send a Blob tx to the L2 engine via RPC, it should not be accepted.
......
......@@ -439,3 +439,37 @@ func (su *SupervisorBackend) UpdateFinalizedL1(ctx context.Context, chainID type
return su.chainDBs.UpdateFinalizedL1(finalized)
}
// Access to synchronous processing for tests
// ----------------------------
func (su *SupervisorBackend) SyncEvents(chainID types.ChainID) error {
su.mu.RLock()
defer su.mu.RUnlock()
ch, ok := su.chainProcessors[chainID]
if !ok {
return types.ErrUnknownChain
}
ch.ProcessToHead()
return nil
}
func (su *SupervisorBackend) SyncCrossUnsafe(chainID types.ChainID) error {
su.mu.RLock()
defer su.mu.RUnlock()
ch, ok := su.crossUnsafeProcessors[chainID]
if !ok {
return types.ErrUnknownChain
}
return ch.ProcessWork()
}
func (su *SupervisorBackend) SyncCrossSafe(chainID types.ChainID) error {
su.mu.RLock()
defer su.mu.RUnlock()
ch, ok := su.crossSafeProcessors[chainID]
if !ok {
return types.ErrUnknownChain
}
return ch.ProcessWork()
}
......@@ -134,7 +134,7 @@ func (s *ChainProcessor) work() {
target := s.nextNum()
if err := s.update(target); err != nil {
if errors.Is(err, ethereum.NotFound) {
s.log.Info("Cannot find next block yet", "target", target)
s.log.Info("Cannot find next block yet", "target", target, "err", err)
} else if errors.Is(err, types.ErrNoRPCSource) {
s.log.Warn("No RPC source configured, cannot process new blocks")
} else {
......
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