Commit d1baa7a0 authored by Francis Li's avatar Francis Li Committed by GitHub

[op-conductor] admin api - PostUnsafePayload (#8800)

* Add PostUnsafePayload admin API

* Add PostUnsafePayload to rollup client

* Add test

* update test based on suggestion
parent ffd953a1
......@@ -14,6 +14,7 @@ type SequencerControl interface {
StartSequencer(ctx context.Context, hash common.Hash) error
StopSequencer(ctx context.Context) (common.Hash, error)
LatestUnsafeBlock(ctx context.Context) (eth.BlockInfo, error)
PostUnsafePayload(ctx context.Context, payload *eth.ExecutionPayload) error
}
// NewSequencerControl creates a new SequencerControl instance.
......@@ -45,3 +46,8 @@ func (s *sequencerController) StartSequencer(ctx context.Context, hash common.Ha
func (s *sequencerController) StopSequencer(ctx context.Context) (common.Hash, error) {
return s.node.StopSequencer(ctx)
}
// PostUnsafePayload implements SequencerControl.
func (s *sequencerController) PostUnsafePayload(ctx context.Context, payload *eth.ExecutionPayload) error {
return s.node.PostUnsafePayload(ctx, payload)
}
......@@ -127,6 +127,10 @@ func (s *l2VerifierBackend) SequencerActive(ctx context.Context) (bool, error) {
return false, nil
}
func (s *l2VerifierBackend) OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPayload) error {
return nil
}
func (s *L2Verifier) L2Finalized() eth.L2BlockRef {
return s.derivation.Finalized()
}
......
......@@ -2,16 +2,19 @@ package op_e2e
import (
"context"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/node"
"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/rpc"
"github.com/stretchr/testify/require"
)
func TestStopStartSequencer(t *testing.T) {
......@@ -172,6 +175,48 @@ func TestLoadSequencerStateOnStarted_Started(t *testing.T) {
assertPersistedSequencerState(t, stateFile, node.StateStarted)
}
func TestPostUnsafePayload(t *testing.T) {
InitParallel(t)
ctx := context.Background()
cfg := DefaultSystemConfig(t)
cfg.Nodes["verifier"].RPC.EnableAdmin = true
cfg.DisableBatcher = true
sys, err := cfg.Start(t)
require.NoError(t, err)
defer sys.Close()
l2Seq := sys.Clients["sequencer"]
l2Ver := sys.Clients["verifier"]
rollupClient := sys.RollupClient("verifier")
require.NoError(t, wait.ForBlock(ctx, l2Seq, 2), "Chain did not advance after starting sequencer")
verBlock, err := l2Ver.BlockByNumber(ctx, nil)
require.NoError(t, err)
require.Equal(t, uint64(0), verBlock.NumberU64(), "Verifier should not have advanced any blocks since p2p & batcher are not enabled")
blockNumberOne, err := l2Seq.BlockByNumber(ctx, big.NewInt(1))
require.NoError(t, err)
payload, err := eth.BlockAsPayload(blockNumberOne, sys.RollupConfig.CanyonTime)
require.NoError(t, err)
err = rollupClient.PostUnsafePayload(ctx, payload)
require.NoError(t, err)
ss, err := rollupClient.SyncStatus(ctx)
require.NoError(t, err)
require.Equal(t, uint64(1), ss.UnsafeL2.Number)
// Test validation
blockNumberTwo, err := l2Seq.BlockByNumber(ctx, big.NewInt(2))
require.NoError(t, err)
payload, err = eth.BlockAsPayload(blockNumberTwo, sys.RollupConfig.CanyonTime)
require.NoError(t, err)
payload.BlockHash = common.Hash{0xaa}
err = rollupClient.PostUnsafePayload(ctx, payload)
require.ErrorContains(t, err, "payload has bad block hash")
}
func assertPersistedSequencerState(t *testing.T, stateFile string, expected node.RunningState) {
configReader := node.NewConfigPersistence(stateFile)
state, err := configReader.SequencerState()
......
......@@ -30,6 +30,7 @@ type driverClient interface {
StartSequencer(ctx context.Context, blockHash common.Hash) error
StopSequencer(context.Context) (common.Hash, error)
SequencerActive(context.Context) (bool, error)
OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPayload) error
}
type adminAPI struct {
......@@ -68,6 +69,20 @@ func (n *adminAPI) SequencerActive(ctx context.Context) (bool, error) {
return n.dr.SequencerActive(ctx)
}
// PostUnsafePayload is a special API that allow posting an unsafe payload to the L2 derivation pipeline.
// It should only be used by op-conductor for sequencer failover scenarios.
func (n *adminAPI) PostUnsafePayload(ctx context.Context, payload *eth.ExecutionPayload) error {
recordDur := n.M.RecordRPCServerRequest("admin_postUnsafePayload")
defer recordDur()
if actual, ok := payload.CheckBlockHash(); !ok {
log.Error("payload has bad block hash", "bad_hash", payload.BlockHash.String(), "actual", actual.String())
return fmt.Errorf("payload has bad block hash: %s, actual block hash is: %s", payload.BlockHash.String(), actual.String())
}
return n.dr.OnUnsafeL2Payload(ctx, payload)
}
type nodeAPI struct {
config *rollup.Config
client l2EthClient
......
......@@ -7,13 +7,12 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/version"
......@@ -230,3 +229,7 @@ func (c *mockDriverClient) StopSequencer(ctx context.Context) (common.Hash, erro
func (c *mockDriverClient) SequencerActive(ctx context.Context) (bool, error) {
return c.Mock.MethodCalled("SequencerActive").Get(0).(bool), nil
}
func (c *mockDriverClient) OnUnsafeL2Payload(ctx context.Context, payload *eth.ExecutionPayload) error {
return c.Mock.MethodCalled("OnUnsafeL2Payload").Get(0).(error)
}
......@@ -60,6 +60,10 @@ func (r *RollupClient) SequencerActive(ctx context.Context) (bool, error) {
return result, err
}
func (r *RollupClient) PostUnsafePayload(ctx context.Context, payload *eth.ExecutionPayload) error {
return r.rpc.CallContext(ctx, nil, "admin_postUnsafePayload", payload)
}
func (r *RollupClient) SetLogLevel(ctx context.Context, lvl log.Lvl) error {
return r.rpc.CallContext(ctx, nil, "admin_setLogLevel", lvl.String())
}
......
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