Skip to content

Commit

Permalink
op-proposer: cleanup proposer logic, update outputAtBlock API (ethere…
Browse files Browse the repository at this point in the history
…um-optimism#3805)

* op-proposer: cleanup proposer logic, update outputAtBlock API

* op-e2e,op-proposer: action test proposer

* fix flags

Co-authored-by: Matthew Slipper <[email protected]>
  • Loading branch information
protolambda and mslipper authored Nov 2, 2022
1 parent 3f6c670 commit 05d367e
Show file tree
Hide file tree
Showing 16 changed files with 425 additions and 206 deletions.
78 changes: 78 additions & 0 deletions op-e2e/actions/l2_proposer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package actions

import (
"crypto/ecdsa"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"

"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-proposer/drivers/l2output"
)

type ProposerCfg struct {
OutputOracleAddr common.Address
ProposerKey *ecdsa.PrivateKey
AllowNonFinalized bool
}

type L2Proposer struct {
log log.Logger
l1 *ethclient.Client
driver *l2output.Driver
address common.Address
lastTx common.Hash
}

func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Client, rollupCl *sources.RollupClient) *L2Proposer {
chainID, err := l1.ChainID(t.Ctx())
require.NoError(t, err)
dr, err := l2output.NewDriver(l2output.Config{
Log: log,
Name: "proposer",
L1Client: l1,
RollupClient: rollupCl,
AllowNonFinalized: cfg.AllowNonFinalized,
L2OOAddr: cfg.OutputOracleAddr,
ChainID: chainID,
PrivKey: cfg.ProposerKey,
})
require.NoError(t, err)
return &L2Proposer{
log: log,
l1: l1,
driver: dr,
address: crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey),
}
}

func (p *L2Proposer) CanPropose(t Testing) bool {
start, end, err := p.driver.GetBlockRange(t.Ctx())
require.NoError(t, err)
return start.Cmp(end) < 0
}

func (p *L2Proposer) ActMakeProposalTx(t Testing) {
start, end, err := p.driver.GetBlockRange(t.Ctx())
require.NoError(t, err)
if start.Cmp(end) == 0 {
t.InvalidAction("nothing to propose, block range starts and ends at %s", start.String())
}
nonce, err := p.l1.PendingNonceAt(t.Ctx(), p.address)
require.NoError(t, err)

tx, err := p.driver.CraftTx(t.Ctx(), start, end, new(big.Int).SetUint64(nonce))
require.NoError(t, err)

err = p.driver.SendTransaction(t.Ctx(), tx)
require.NoError(t, err)
p.lastTx = tx.Hash()
}

func (p *L2Proposer) LastProposalTx() common.Hash {
return p.lastTx
}
82 changes: 82 additions & 0 deletions op-e2e/actions/l2_proposer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package actions

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"

"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testlog"
)

func TestProposer(gt *testing.T) {
t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)

rollupSeqCl := sequencer.RollupClient()
batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{
MinL1TxSize: 0,
MaxL1TxSize: 128_000,
BatcherKey: dp.Secrets.Batcher,
}, rollupSeqCl, miner.EthClient(), seqEngine.EthClient())

proposer := NewL2Proposer(t, log, &ProposerCfg{
OutputOracleAddr: sd.DeploymentsL1.L2OutputOracleProxy,
ProposerKey: dp.Secrets.Proposer,
AllowNonFinalized: false,
}, miner.EthClient(), sequencer.RollupClient())

// L1 block
miner.ActEmptyBlock(t)
// L2 block
sequencer.ActL1HeadSignal(t)
sequencer.ActL2PipelineFull(t)
sequencer.ActBuildToL1Head(t)
// submit and include in L1
batcher.ActSubmitAll(t)
miner.ActL1StartBlock(12)(t)
miner.ActL1IncludeTx(dp.Addresses.Batcher)(t)
miner.ActL1EndBlock(t)
// finalize the first and second L1 blocks, including the batch
miner.ActL1SafeNext(t)
miner.ActL1SafeNext(t)
miner.ActL1FinalizeNext(t)
miner.ActL1FinalizeNext(t)
// derive and see the L2 chain fully finalize
sequencer.ActL2PipelineFull(t)
sequencer.ActL1SafeSignal(t)
sequencer.ActL1FinalizedSignal(t)
require.Equal(t, sequencer.SyncStatus().UnsafeL2, sequencer.SyncStatus().FinalizedL2)
// make proposals until there is nothing left to propose
for proposer.CanPropose(t) {
// and propose it to L1
proposer.ActMakeProposalTx(t)
// include proposal on L1
miner.ActL1StartBlock(12)(t)
miner.ActL1IncludeTx(dp.Addresses.Proposer)(t)
miner.ActL1EndBlock(t)
// Check proposal was successful
receipt, err := miner.EthClient().TransactionReceipt(t.Ctx(), proposer.LastProposalTx())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "proposal failed")
}

// check that L1 stored the expected output root
outputOracleContract, err := bindings.NewL2OutputOracle(sd.DeploymentsL1.L2OutputOracleProxy, miner.EthClient())
require.NoError(t, err)
block := sequencer.SyncStatus().FinalizedL2
outputOnL1, err := outputOracleContract.GetL2Output(nil, new(big.Int).SetUint64(block.Number))
require.NoError(t, err)
require.Less(t, block.Time, outputOnL1.Timestamp.Uint64(), "output is registered with L1 timestamp of proposal tx, past L2 block")
outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), block.Number)
require.NoError(t, err)
require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match")
}
13 changes: 11 additions & 2 deletions op-e2e/actions/l2_verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ import (
type L2Verifier struct {
log log.Logger

eng derive.Engine
eng interface {
derive.Engine
L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error)
}

// L2 rollup
derivation *derive.DerivationPipeline
Expand All @@ -46,7 +49,8 @@ type L2Verifier struct {

type L2API interface {
derive.Engine
InfoByRpcNumber(ctx context.Context, num rpc.BlockNumber) (eth.BlockInfo, error)
L2BlockRefByNumber(ctx context.Context, num uint64) (eth.L2BlockRef, error)
InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error)
// GetProof returns a proof of the account, it may return a nil result without error if the address was not found.
GetProof(ctx context.Context, address common.Address, blockTag string) (*eth.AccountResult, error)
}
Expand Down Expand Up @@ -95,6 +99,11 @@ type l2VerifierBackend struct {
verifier *L2Verifier
}

func (s *l2VerifierBackend) BlockRefWithStatus(ctx context.Context, num uint64) (eth.L2BlockRef, *eth.SyncStatus, error) {
ref, err := s.verifier.eng.L2BlockRefByNumber(ctx, num)
return ref, s.verifier.SyncStatus(), err
}

func (s *l2VerifierBackend) SyncStatus(ctx context.Context) (*eth.SyncStatus, error) {
return s.verifier.SyncStatus(), nil
}
Expand Down
29 changes: 17 additions & 12 deletions op-e2e/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ import (
"testing"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
geth_eth "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
"github.com/stretchr/testify/require"

bss "github.com/ethereum-optimism/optimism/op-batcher"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
Expand All @@ -24,16 +35,6 @@ import (
"github.com/ethereum-optimism/optimism/op-node/testlog"
l2os "github.com/ethereum-optimism/optimism/op-proposer"
oplog "github.com/ethereum-optimism/optimism/op-service/log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
geth_eth "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
"github.com/stretchr/testify/require"
)

var (
Expand Down Expand Up @@ -139,7 +140,8 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
"batcher": testlog.Logger(t, log.LvlInfo).New("role", "batcher"),
"proposer": testlog.Logger(t, log.LvlCrit).New("role", "proposer"),
},
P2PTopology: nil, // no P2P connectivity by default
P2PTopology: nil, // no P2P connectivity by default
NonFinalizedProposals: false,
}
}

Expand Down Expand Up @@ -181,6 +183,9 @@ type SystemConfig struct {
// A nil map disables P2P completely.
// Any node name not in the topology will not have p2p enabled.
P2PTopology map[string][]string

// If the proposer can make proposals for L2 blocks derived from L1 blocks which are not finalized on L1 yet.
NonFinalizedProposals bool
}

type System struct {
Expand Down Expand Up @@ -479,13 +484,13 @@ func (cfg SystemConfig) Start() (*System, error) {
// L2Output Submitter
sys.L2OutputSubmitter, err = l2os.NewL2OutputSubmitter(l2os.Config{
L1EthRpc: sys.Nodes["l1"].WSEndpoint(),
L2EthRpc: sys.Nodes["sequencer"].WSEndpoint(),
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
L2OOAddress: predeploys.DevL2OutputOracleAddr.String(),
PollInterval: 50 * time.Millisecond,
NumConfirmations: 1,
ResubmissionTimeout: 3 * time.Second,
SafeAbortNonceTooLowCount: 3,
AllowNonFinalized: cfg.NonFinalizedProposals,
LogConfig: oplog.CLIConfig{
Level: "info",
Format: "text",
Expand Down
24 changes: 12 additions & 12 deletions op-e2e/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ import (
"testing"
"time"

"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore"
Expand All @@ -27,6 +19,15 @@ import (
"github.com/ethereum/go-ethereum/rpc"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/stretchr/testify/require"

"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
)

// Init testing to enable test flags
Expand All @@ -49,6 +50,7 @@ func TestL2OutputSubmitter(t *testing.T) {
}

cfg := DefaultSystemConfig(t)
cfg.NonFinalizedProposals = true // speed up the time till we see output proposals

sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system")
Expand Down Expand Up @@ -99,11 +101,9 @@ func TestL2OutputSubmitter(t *testing.T) {
// finalized.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
l2Output, err := rollupClient.OutputAtBlock(ctx, l2ooBlockNumber)
l2Output, err := rollupClient.OutputAtBlock(ctx, l2ooBlockNumber.Uint64())
require.Nil(t, err)
require.Len(t, l2Output, 2)

require.Equal(t, l2Output[1][:], committedL2Output.OutputRoot[:])
require.Equal(t, l2Output.OutputRoot[:], committedL2Output.OutputRoot[:])
break
}

Expand Down
14 changes: 14 additions & 0 deletions op-node/eth/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package eth

import (
"github.com/ethereum/go-ethereum/common"
)

type OutputResponse struct {
Version Bytes32 `json:"version"`
OutputRoot Bytes32 `json:"outputRoot"`
BlockRef L2BlockRef `json:"blockRef"`
WithdrawalStorageRoot common.Hash `json:"withdrawalStorageRoot"`
StateRoot common.Hash `json:"stateRoot"`
Status *SyncStatus `json:"syncStatus"`
}
Loading

0 comments on commit 05d367e

Please sign in to comment.