Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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