Commit 35757456 authored by Maurelian's avatar Maurelian Committed by GitHub

bedrock: use block number for key in output oracle (#2707)

* bedrock: use block number for key in output oracle

* contracts: Apply seaport style to natspec comments

Inspo: https://github.com/ProjectOpenSea/seaport/blob/main/contracts/Seaport.sol

* bedrock: Improve oracle error messages

* bedrock: update node and proposer with new oracle interface

* bedrock: add tests for oracle reorg protection.

* Fix op-proposer & withdrawals logic

* Golang PR Fixes

* specs: use block number for key in output oracle

* bedrock: Fix CamelCase on oracle events

* bedrock: Update arg name in usage of getL2Output

* bedrock: Oracle add computeL2Timestamp and check on append
Co-authored-by: default avatarJoshua Gutow <jgutow@optimism.io>
parent b81eae57
---
'@eth-optimism/contracts-bedrock': minor
---
Replaces L2 timestamps with block numbers as the key in mapping(uint => OutputProposal).
This diff is collapsed.
This diff is collapsed.
...@@ -52,10 +52,11 @@ func deriveAccount(w accounts.Wallet, path string) accounts.Account { ...@@ -52,10 +52,11 @@ func deriveAccount(w accounts.Wallet, path string) accounts.Account {
type L2OOContractConfig struct { type L2OOContractConfig struct {
SubmissionFrequency *big.Int SubmissionFrequency *big.Int
L2StartTime *big.Int L2StartingBlock *big.Int
L2BlockTime *big.Int
GenesisL2Output [32]byte GenesisL2Output [32]byte
HistoricalTotalBlocks *big.Int HistoricalTotalBlocks *big.Int
L2StartingTimeStamp *big.Int
L2BlockTime *big.Int
} }
type DepositContractConfig struct { type DepositContractConfig struct {
...@@ -82,9 +83,11 @@ type SystemConfig struct { ...@@ -82,9 +83,11 @@ type SystemConfig struct {
JWTFilePath string JWTFilePath string
JWTSecret [32]byte JWTSecret [32]byte
Nodes map[string]*rollupNode.Config // Per node config. Don't use populate rollup.Config Nodes map[string]*rollupNode.Config // Per node config. Don't use populate rollup.Config
Loggers map[string]log.Logger Loggers map[string]log.Logger
RollupConfig rollup.Config // Shared rollup configs ProposerLogger log.Logger
BatcherLogger log.Logger
RollupConfig rollup.Config // Shared rollup configs
L1BlockTime uint64 L1BlockTime uint64
...@@ -392,7 +395,9 @@ func (cfg SystemConfig) start() (*System, error) { ...@@ -392,7 +395,9 @@ func (cfg SystemConfig) start() (*System, error) {
sys.cfg.RollupConfig.Genesis = sys.RolupGenesis sys.cfg.RollupConfig.Genesis = sys.RolupGenesis
sys.cfg.RollupConfig.BatchSenderAddress = batchSubmitterAddr sys.cfg.RollupConfig.BatchSenderAddress = batchSubmitterAddr
sys.cfg.RollupConfig.P2PSequencerAddress = p2pSignerAddr sys.cfg.RollupConfig.P2PSequencerAddress = p2pSignerAddr
sys.cfg.L2OOCfg.L2StartTime = new(big.Int).SetUint64(l2GenesisTime) sys.cfg.L2OOCfg.L2StartingBlock = new(big.Int).SetUint64(l2GenesisID.Number)
sys.cfg.L2OOCfg.L2StartingTimeStamp = new(big.Int).SetUint64(l2Genesis.Timestamp)
sys.cfg.L2OOCfg.L2BlockTime = new(big.Int).SetUint64(2)
// Deploy Deposit Contract // Deploy Deposit Contract
deployerPrivKey, err := sys.wallet.PrivateKey(accounts.Account{ deployerPrivKey, err := sys.wallet.PrivateKey(accounts.Account{
...@@ -414,10 +419,11 @@ func (cfg SystemConfig) start() (*System, error) { ...@@ -414,10 +419,11 @@ func (cfg SystemConfig) start() (*System, error) {
opts, opts,
l1Client, l1Client,
sys.cfg.L2OOCfg.SubmissionFrequency, sys.cfg.L2OOCfg.SubmissionFrequency,
sys.cfg.L2OOCfg.L2BlockTime,
sys.cfg.L2OOCfg.GenesisL2Output, sys.cfg.L2OOCfg.GenesisL2Output,
sys.cfg.L2OOCfg.HistoricalTotalBlocks, sys.cfg.L2OOCfg.HistoricalTotalBlocks,
sys.cfg.L2OOCfg.L2StartTime, sys.cfg.L2OOCfg.L2StartingBlock,
sys.cfg.L2OOCfg.L2StartingTimeStamp,
sys.cfg.L2OOCfg.L2BlockTime,
l2OutputSubmitterAddr, l2OutputSubmitterAddr,
) )
sys.cfg.DepositCFG.L2Oracle = sys.L2OOContractAddr sys.cfg.DepositCFG.L2Oracle = sys.L2OOContractAddr
...@@ -554,7 +560,7 @@ func (cfg SystemConfig) start() (*System, error) { ...@@ -554,7 +560,7 @@ func (cfg SystemConfig) start() (*System, error) {
LogTerminal: true, LogTerminal: true,
Mnemonic: sys.cfg.Mnemonic, Mnemonic: sys.cfg.Mnemonic,
L2OutputHDPath: sys.cfg.L2OutputHDPath, L2OutputHDPath: sys.cfg.L2OutputHDPath,
}, "", log.New()) }, "", cfg.ProposerLogger)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to setup l2 output submitter: %w", err) return nil, fmt.Errorf("unable to setup l2 output submitter: %w", err)
} }
...@@ -590,7 +596,7 @@ func (cfg SystemConfig) start() (*System, error) { ...@@ -590,7 +596,7 @@ func (cfg SystemConfig) start() (*System, error) {
SequencerHistoryDBFilename: sys.sequencerHistoryDBFileName, SequencerHistoryDBFilename: sys.sequencerHistoryDBFileName,
SequencerGenesisHash: sys.RolupGenesis.L2.Hash.String(), SequencerGenesisHash: sys.RolupGenesis.L2.Hash.String(),
SequencerBatchInboxAddress: sys.cfg.RollupConfig.BatchInboxAddress.String(), SequencerBatchInboxAddress: sys.cfg.RollupConfig.BatchInboxAddress.String(),
}, "", log.New()) }, "", cfg.BatcherLogger)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to setup batch submitter: %w", err) return nil, fmt.Errorf("failed to setup batch submitter: %w", err)
} }
......
...@@ -88,7 +88,6 @@ func defaultSystemConfig(t *testing.T) SystemConfig { ...@@ -88,7 +88,6 @@ func defaultSystemConfig(t *testing.T) SystemConfig {
L2OOCfg: L2OOContractConfig{ L2OOCfg: L2OOContractConfig{
// L2 Start time is set based off of the L2 Genesis time // L2 Start time is set based off of the L2 Genesis time
SubmissionFrequency: big.NewInt(2), SubmissionFrequency: big.NewInt(2),
L2BlockTime: big.NewInt(1),
HistoricalTotalBlocks: big.NewInt(0), HistoricalTotalBlocks: big.NewInt(0),
}, },
L2OutputHDPath: l2OutputHDPath, L2OutputHDPath: l2OutputHDPath,
...@@ -117,6 +116,8 @@ func defaultSystemConfig(t *testing.T) SystemConfig { ...@@ -117,6 +116,8 @@ func defaultSystemConfig(t *testing.T) SystemConfig {
"verifier": testlog.Logger(t, log.LvlError).New("role", "verifier"), "verifier": testlog.Logger(t, log.LvlError).New("role", "verifier"),
"sequencer": testlog.Logger(t, log.LvlError).New("role", "sequencer"), "sequencer": testlog.Logger(t, log.LvlError).New("role", "sequencer"),
}, },
ProposerLogger: testlog.Logger(t, log.LvlCrit).New("role", "proposer"), // Proposer is noisy on shutdown
BatcherLogger: testlog.Logger(t, log.LvlCrit).New("role", "batcher"), // Batcher (txmgr really) is noisy on shutdown
RollupConfig: rollup.Config{ RollupConfig: rollup.Config{
BlockTime: 1, BlockTime: 1,
MaxSequencerDrift: 10, MaxSequencerDrift: 10,
...@@ -153,11 +154,11 @@ func TestL2OutputSubmitter(t *testing.T) { ...@@ -153,11 +154,11 @@ func TestL2OutputSubmitter(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
rollupClient := rollupclient.NewRollupClient(rollupRPCClient) rollupClient := rollupclient.NewRollupClient(rollupRPCClient)
// StateRootOracle is already deployed // OutputOracle is already deployed
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(sys.L2OOContractAddr, l1Client) l2OutputOracle, err := bindings.NewL2OutputOracleCaller(sys.L2OOContractAddr, l1Client)
require.Nil(t, err) require.Nil(t, err)
initialSroTimestamp, err := l2OutputOracle.LatestBlockTimestamp(&bind.CallOpts{}) initialOutputBlockNumber, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{})
require.Nil(t, err) require.Nil(t, err)
// Wait until the second output submission from L2. The output submitter submits outputs from the // Wait until the second output submission from L2. The output submitter submits outputs from the
...@@ -174,20 +175,15 @@ func TestL2OutputSubmitter(t *testing.T) { ...@@ -174,20 +175,15 @@ func TestL2OutputSubmitter(t *testing.T) {
ticker := time.NewTicker(1 * time.Second) ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() defer ticker.Stop()
for { for {
l2ooTimestamp, err := l2OutputOracle.LatestBlockTimestamp(&bind.CallOpts{}) l2ooBlockNumber, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{})
require.Nil(t, err) require.Nil(t, err)
// Wait for the L2 output oracle to have been changed from the initial // Wait for the L2 output oracle to have been changed from the initial
// timestamp set in the contract constructor. // timestamp set in the contract constructor.
if l2ooTimestamp.Cmp(initialSroTimestamp) > 0 { if l2ooBlockNumber.Cmp(initialOutputBlockNumber) > 0 {
// Retrieve the l2 output committed at this updated timestamp. // Retrieve the l2 output committed at this updated timestamp.
committedL2Output, err := l2OutputOracle.GetL2Output(&bind.CallOpts{}, l2ooTimestamp) committedL2Output, err := l2OutputOracle.GetL2Output(&bind.CallOpts{}, l2ooBlockNumber)
require.Nil(t, err) require.NotEqual(t, [32]byte{}, committedL2Output.OutputRoot, "Empty L2 Output")
// Compute the committed L2 output's L2 block number.
l2ooBlockNumber, err := l2OutputOracle.ComputeL2BlockNumber(
&bind.CallOpts{}, l2ooTimestamp,
)
require.Nil(t, err) require.Nil(t, err)
// Fetch the corresponding L2 block and assert the committed L2 // Fetch the corresponding L2 block and assert the committed L2
...@@ -765,19 +761,19 @@ func TestWithdrawals(t *testing.T) { ...@@ -765,19 +761,19 @@ func TestWithdrawals(t *testing.T) {
tx, err = l2withdrawer.InitiateWithdrawal(l2opts, fromAddr, big.NewInt(21000), nil) tx, err = l2withdrawer.InitiateWithdrawal(l2opts, fromAddr, big.NewInt(21000), nil)
require.Nil(t, err, "sending initiate withdraw tx") require.Nil(t, err, "sending initiate withdraw tx")
receipt, err = waitForTransaction(tx.Hash(), l2Seq, 3*time.Duration(cfg.L1BlockTime)*time.Second) receipt, err = waitForTransaction(tx.Hash(), l2Verif, 5*time.Duration(cfg.L1BlockTime)*time.Second)
require.Nil(t, err, "withdrawal initiated on L2 sequencer") require.Nil(t, err, "withdrawal initiated on L2 sequencer")
require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed") require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed")
// Verify L2 balance after withdrawal // Verify L2 balance after withdrawal
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
header, err := l2Seq.HeaderByNumber(ctx, receipt.BlockNumber) header, err := l2Verif.HeaderByNumber(ctx, receipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
endBalance, err = l2Seq.BalanceAt(ctx, fromAddr, nil) endBalance, err = l2Verif.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
// Take fee into account // Take fee into account
...@@ -793,25 +789,17 @@ func TestWithdrawals(t *testing.T) { ...@@ -793,25 +789,17 @@ func TestWithdrawals(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
// Wait for finalization and then create the Finalized Withdrawal Transaction // Wait for finalization and then create the Finalized Withdrawal Transaction
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(sys.L2OOContractAddr, l1Client)
require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 10*time.Duration(cfg.L1BlockTime)*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 10*time.Duration(cfg.L1BlockTime)*time.Second)
defer cancel() defer cancel()
timestamp, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, sys.DepositContractAddr, header.Time) blockNumber, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, sys.DepositContractAddr, receipt.BlockNumber)
require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
blockNumber, err := l2OutputOracle.ComputeL2BlockNumber(&bind.CallOpts{Context: ctx}, new(big.Int).SetUint64(timestamp))
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
header, err = l2Seq.HeaderByNumber(ctx, blockNumber) header, err = l2Verif.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNumber))
require.Nil(t, err) require.Nil(t, err)
rpc, err := rpc.Dial(sys.nodes["sequencer"].WSEndpoint()) rpc, err := rpc.Dial(sys.nodes["verifier"].WSEndpoint())
require.Nil(t, err) require.Nil(t, err)
l2client := withdrawals.NewClient(rpc) l2client := withdrawals.NewClient(rpc)
...@@ -831,7 +819,7 @@ func TestWithdrawals(t *testing.T) { ...@@ -831,7 +819,7 @@ func TestWithdrawals(t *testing.T) {
params.Value, params.Value,
params.GasLimit, params.GasLimit,
params.Data, params.Data,
params.Timestamp, params.BlockNumber,
params.OutputRootProof, params.OutputRootProof,
params.WithdrawalProof, params.WithdrawalProof,
) )
......
...@@ -20,13 +20,13 @@ import ( ...@@ -20,13 +20,13 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
) )
// WaitForFinalizationPeriod waits until the timestamp has been submitted to the L2 Output Oracle on L1 and // WaitForFinalizationPeriod waits until there is OutputProof for an L2 block number larger than the supplied l2BlockNumber
// then waits for the finalization period to be up. // and that the output is finalized.
// This functions polls and can block for a very long time if used on mainnet. // This functions polls and can block for a very long time if used on mainnet.
// This returns the timestamp to use for the proof generation. // This returns the block number to use for the proof generation.
func WaitForFinalizationPeriod(ctx context.Context, client *ethclient.Client, portalAddr common.Address, timestamp uint64) (uint64, error) { func WaitForFinalizationPeriod(ctx context.Context, client *ethclient.Client, portalAddr common.Address, l2BlockNumber *big.Int) (uint64, error) {
l2BlockNumber = new(big.Int).Set(l2BlockNumber) // Don't clobber caller owned l2BlockNumber
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
timestampBig := new(big.Int).SetUint64(timestamp)
portal, err := bindings.NewOptimismPortalCaller(portalAddr, client) portal, err := bindings.NewOptimismPortalCaller(portalAddr, client)
if err != nil { if err != nil {
...@@ -40,21 +40,32 @@ func WaitForFinalizationPeriod(ctx context.Context, client *ethclient.Client, po ...@@ -40,21 +40,32 @@ func WaitForFinalizationPeriod(ctx context.Context, client *ethclient.Client, po
if err != nil { if err != nil {
return 0, err return 0, err
} }
submissionInterval, err := l2OO.SUBMISSIONINTERVAL(opts)
if err != nil {
return 0, err
}
// Convert blockNumber to submission interval boundary
rem := new(big.Int)
l2BlockNumber, rem = l2BlockNumber.DivMod(l2BlockNumber, submissionInterval, rem)
if rem.Cmp(common.Big0) != 0 {
l2BlockNumber = l2BlockNumber.Add(l2BlockNumber, common.Big1)
}
l2BlockNumber = l2BlockNumber.Mul(l2BlockNumber, submissionInterval)
finalizationPeriod, err := portal.FINALIZATIONPERIODSECONDS(opts) finalizationPeriod, err := portal.FINALIZATIONPERIODSECONDS(opts)
if err != nil { if err != nil {
return 0, err return 0, err
} }
next, err := l2OO.LatestBlockTimestamp(opts) latest, err := l2OO.LatestBlockNumber(opts)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Now poll // Now poll for the output to be submitted on chain
var ticker *time.Ticker var ticker *time.Ticker
diff := new(big.Int).Sub(timestampBig, next) diff := new(big.Int).Sub(l2BlockNumber, latest)
if diff.Cmp(big.NewInt(60)) > 0 { if diff.Cmp(big.NewInt(10)) > 0 {
ticker = time.NewTicker(time.Minute) ticker = time.NewTicker(time.Minute)
} else { } else {
ticker = time.NewTicker(time.Second) ticker = time.NewTicker(time.Second)
...@@ -64,12 +75,12 @@ loop: ...@@ -64,12 +75,12 @@ loop:
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
next, err = l2OO.LatestBlockTimestamp(opts) latest, err = l2OO.LatestBlockNumber(opts)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Already passed next // Already passed the submitted block (likely just equals rather than >= here).
if next.Cmp(timestampBig) > 0 { if latest.Cmp(l2BlockNumber) >= 0 {
break loop break loop
} }
case <-ctx.Done(): case <-ctx.Done():
...@@ -78,10 +89,13 @@ loop: ...@@ -78,10 +89,13 @@ loop:
} }
// Now wait for it to be finalized // Now wait for it to be finalized
output, err := l2OO.GetL2Output(opts, next) output, err := l2OO.GetL2Output(opts, l2BlockNumber)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if output.OutputRoot == [32]byte{} {
return 0, errors.New("empty output root. likely no proposal at timestamp")
}
targetTimestamp := new(big.Int).Add(output.Timestamp, finalizationPeriod) targetTimestamp := new(big.Int).Add(output.Timestamp, finalizationPeriod)
targetTime := time.Unix(targetTimestamp.Int64(), 0) targetTime := time.Unix(targetTimestamp.Int64(), 0)
// Assume clock is relatively correct // Assume clock is relatively correct
...@@ -96,7 +110,7 @@ loop: ...@@ -96,7 +110,7 @@ loop:
return 0, err return 0, err
} }
if header.Time > targetTimestamp.Uint64() { if header.Time > targetTimestamp.Uint64() {
return next.Uint64(), nil return l2BlockNumber.Uint64(), nil
} }
case <-ctx.Done(): case <-ctx.Done():
return 0, ctx.Err() return 0, ctx.Err()
...@@ -131,21 +145,21 @@ func NewClient(client *rpc.Client) *Client { ...@@ -131,21 +145,21 @@ func NewClient(client *rpc.Client) *Client {
} }
// FinalizedWithdrawalParameters is the set of paramets to pass to the FinalizedWithdrawal function // FinalizedWithdrawalParameters is the set of parameters to pass to the FinalizedWithdrawal function
type FinalizedWithdrawalParameters struct { type FinalizedWithdrawalParameters struct {
Nonce *big.Int Nonce *big.Int
Sender common.Address Sender common.Address
Target common.Address Target common.Address
Value *big.Int Value *big.Int
GasLimit *big.Int GasLimit *big.Int
Timestamp *big.Int BlockNumber *big.Int
Data []byte Data []byte
OutputRootProof bindings.WithdrawalVerifierOutputRootProof OutputRootProof bindings.WithdrawalVerifierOutputRootProof
WithdrawalProof []byte // RLP Encoded list of trie nodes to prove L2 storage WithdrawalProof []byte // RLP Encoded list of trie nodes to prove L2 storage
} }
// FinalizeWithdrawalParameters queries L2 to generate all withdrawal parameters and proof necessary to finalize an withdrawal on L1. // FinalizeWithdrawalParameters queries L2 to generate all withdrawal parameters and proof necessary to finalize an withdrawal on L1.
// The header provided is very imporant. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle // The header provided is very important. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle
// contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root. // contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root.
func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txHash common.Hash, header *types.Header) (FinalizedWithdrawalParameters, error) { func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txHash common.Hash, header *types.Header) (FinalizedWithdrawalParameters, error) {
// Transaction receipt // Transaction receipt
...@@ -189,13 +203,13 @@ func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txH ...@@ -189,13 +203,13 @@ func FinalizeWithdrawalParameters(ctx context.Context, l2client ProofClient, txH
} }
return FinalizedWithdrawalParameters{ return FinalizedWithdrawalParameters{
Nonce: ev.Nonce, Nonce: ev.Nonce,
Sender: ev.Sender, Sender: ev.Sender,
Target: ev.Target, Target: ev.Target,
Value: ev.Value, Value: ev.Value,
GasLimit: ev.GasLimit, GasLimit: ev.GasLimit,
Timestamp: new(big.Int).SetUint64(header.Time), BlockNumber: new(big.Int).Set(header.Number),
Data: ev.Data, Data: ev.Data,
OutputRootProof: bindings.WithdrawalVerifierOutputRootProof{ OutputRootProof: bindings.WithdrawalVerifierOutputRootProof{
Version: [32]byte{}, // Empty for version 1 Version: [32]byte{}, // Empty for version 1
StateRoot: header.Root, StateRoot: header.Root,
......
...@@ -95,58 +95,41 @@ func (d *Driver) GetBlockRange( ...@@ -95,58 +95,41 @@ func (d *Driver) GetBlockRange(
Context: ctx, Context: ctx,
} }
// Determine the next uncommitted L2 block number. We do so by transforming // Determine the last committed L2 Block Number
// the timestamp of the latest committed L2 block into its block number and start, err := d.l2ooContract.LatestBlockNumber(callOpts)
// adding one.
l2ooTimestamp, err := d.l2ooContract.LatestBlockTimestamp(callOpts)
if err != nil { if err != nil {
d.l.Error(name+" unable to get latest block timestamp", "err", err) d.l.Error(name+" unable to get latest block number", "err", err)
return nil, nil, err
}
start, err := d.l2ooContract.ComputeL2BlockNumber(callOpts, l2ooTimestamp)
if err != nil {
d.l.Error(name+" unable to compute latest l2 block number", "err", err)
return nil, nil, err return nil, nil, err
} }
start.Add(start, bigOne) start.Add(start, bigOne)
// Next we need to obtain the current timestamp and the next timestamp at // Next determine the L2 block that we need to commit
// which we will need to submit an L2 output. The former is done by simply nextBlockNumber, err := d.l2ooContract.NextBlockNumber(callOpts)
// adding the submission interval to the latest committed block's timestamp;
// the latter inspects the timestamp of the latest block.
nextTimestamp, err := d.l2ooContract.NextTimestamp(callOpts)
if err != nil { if err != nil {
d.l.Error(name+" unable to get next block timestamp", "err", err) d.l.Error(name+" unable to get next block number", "err", err)
return nil, nil, err return nil, nil, err
} }
latestHeader, err := d.cfg.L1Client.HeaderByNumber(ctx, nil) latestHeader, err := d.cfg.L2Client.HeaderByNumber(ctx, nil)
if err != nil { if err != nil {
d.l.Error(name+" unable to retrieve latest header", "err", err) d.l.Error(name+" unable to retrieve latest header", "err", err)
return nil, nil, err return nil, nil, err
} }
currentTimestamp := big.NewInt(int64(latestHeader.Time)) currentBlockNumber := big.NewInt(latestHeader.Number.Int64())
// If the submission window has yet to elapsed, we must wait before // If we do not have the new L2 Block number
// submitting our L2 output commitment. Return start as the end value which if currentBlockNumber.Cmp(nextBlockNumber) < 0 {
// will signal that there is no work to be done.
if currentTimestamp.Cmp(nextTimestamp) < 0 {
d.l.Info(name+" submission interval has not elapsed", d.l.Info(name+" submission interval has not elapsed",
"currentTimestamp", currentTimestamp, "nextTimestamp", nextTimestamp) "currentBlockNumber", currentBlockNumber, "nextBlockNumber", nextBlockNumber)
return start, start, nil return start, start, nil
} }
d.l.Info(name+" submission interval has elapsed", d.l.Info(name+" submission interval has elapsed",
"currentTimestamp", currentTimestamp, "nextTimestamp", nextTimestamp) "currentBlockNumber", currentBlockNumber, "nextBlockNumber", nextBlockNumber)
// Otherwise the submission interval has elapsed. Transform the next // Otherwise the submission interval has elapsed. Transform the next
// expected timestamp into its L2 block number, and add one since end is // expected timestamp into its L2 block number, and add one since end is
// exclusive. // exclusive.
end, err := d.l2ooContract.ComputeL2BlockNumber(callOpts, nextTimestamp) end := new(big.Int).Add(nextBlockNumber, bigOne)
if err != nil {
d.l.Error(name+" unable to compute next l2 block number", "err", err)
return nil, nil, err
}
end.Add(end, bigOne)
return start, end, nil return start, end, nil
} }
...@@ -174,35 +157,11 @@ func (d *Driver) CraftTx( ...@@ -174,35 +157,11 @@ func (d *Driver) CraftTx(
return nil, err return nil, err
} }
// Fetch the next expected timestamp that we will submit along with the
// L2Output.
callOpts := &bind.CallOpts{
Pending: false,
Context: ctx,
}
timestamp, err := d.l2ooContract.NextTimestamp(callOpts)
if err != nil {
return nil, err
}
// Sanity check that we are submitting against the same expected timestamp.
expCheckpointBlock, err := d.l2ooContract.ComputeL2BlockNumber(
callOpts, timestamp,
)
if err != nil {
return nil, err
}
if nextCheckpointBlock.Cmp(expCheckpointBlock) != 0 {
return nil, fmt.Errorf("expected next checkpoint block to be %d, "+
"found %d", nextCheckpointBlock.Uint64(),
expCheckpointBlock.Uint64())
}
numElements := new(big.Int).Sub(start, end).Uint64() numElements := new(big.Int).Sub(start, end).Uint64()
d.l.Info(name+" checkpoint constructed", "start", start, "end", end, d.l.Info(name+" checkpoint constructed", "start", start, "end", end,
"nonce", nonce, "blocks_committed", numElements, "checkpoint_block", nextCheckpointBlock) "nonce", nonce, "blocks_committed", numElements, "checkpoint_block", nextCheckpointBlock)
header, err := d.cfg.L1Client.HeaderByNumber(ctx, nil) l1Header, err := d.cfg.L1Client.HeaderByNumber(ctx, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("error resolving checkpoint block: %v", err) return nil, fmt.Errorf("error resolving checkpoint block: %v", err)
} }
...@@ -212,8 +171,8 @@ func (d *Driver) CraftTx( ...@@ -212,8 +171,8 @@ func (d *Driver) CraftTx(
return nil, fmt.Errorf("error resolving checkpoint block: %v", err) return nil, fmt.Errorf("error resolving checkpoint block: %v", err)
} }
if l2Header.Time != timestamp.Uint64() { if l2Header.Number.Cmp(nextCheckpointBlock) != 0 {
return nil, fmt.Errorf("invalid timestamp: next timestamp is %v, timestamp of block is %v", timestamp, l2Header.Time) return nil, fmt.Errorf("invalid blockNumber: next blockNumber is %v, blockNumber of block is %v", nextCheckpointBlock, l2Header.Number)
} }
opts, err := bind.NewKeyedTransactorWithChainID( opts, err := bind.NewKeyedTransactorWithChainID(
...@@ -226,7 +185,7 @@ func (d *Driver) CraftTx( ...@@ -226,7 +185,7 @@ func (d *Driver) CraftTx(
opts.Nonce = nonce opts.Nonce = nonce
opts.NoSend = true opts.NoSend = true
return d.l2ooContract.AppendL2Output(opts, l2OutputRoot, timestamp, header.Hash(), header.Number) return d.l2ooContract.AppendL2Output(opts, l2OutputRoot, nextCheckpointBlock, l1Header.Hash(), l1Header.Number)
} }
// UpdateGasPrice signs an otherwise identical txn to the one provided but with // UpdateGasPrice signs an otherwise identical txn to the one provided but with
......
...@@ -38,7 +38,7 @@ L1StandardBridge_Test:test_depositERC20To() (gas: 454650) ...@@ -38,7 +38,7 @@ L1StandardBridge_Test:test_depositERC20To() (gas: 454650)
L1StandardBridge_Test:test_depositETH() (gas: 247054) L1StandardBridge_Test:test_depositETH() (gas: 247054)
L1StandardBridge_Test:test_depositETHTo() (gas: 204938) L1StandardBridge_Test:test_depositETHTo() (gas: 204938)
L1StandardBridge_Test:test_donateETH() (gas: 17545) L1StandardBridge_Test:test_donateETH() (gas: 17545)
L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438817) L1StandardBridge_Test:test_finalizeERC20Withdrawal() (gas: 438824)
L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005) L1StandardBridge_Test:test_finalizeETHWithdrawal() (gas: 48005)
L1StandardBridge_Test:test_initialize() (gas: 14885) L1StandardBridge_Test:test_initialize() (gas: 14885)
L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085) L1StandardBridge_Test:test_onlyEOADepositERC20() (gas: 12085)
...@@ -56,21 +56,21 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119659) ...@@ -56,21 +56,21 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 119659)
L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133096) L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 133096)
L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10588) L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10588)
L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54859) L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 54859)
L2OutputOracleTest:testCannot_appendCurrentTimestamp() (gas: 18627) L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 18442)
L2OutputOracleTest:testCannot_appendEmptyOutput() (gas: 16756) L2OutputOracleTest:testCannot_appendFutureTimetamp() (gas: 20072)
L2OutputOracleTest:testCannot_appendFutureTimestamp() (gas: 18708) L2OutputOracleTest:testCannot_appendOnWrongFork() (gas: 20710)
L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 16458) L2OutputOracleTest:testCannot_appendOutputIfNotSequencer() (gas: 17829)
L2OutputOracleTest:testCannot_appendUnexpectedTimestamp() (gas: 18893) L2OutputOracleTest:testCannot_appendUnexpectedBlockNumber() (gas: 20313)
L2OutputOracleTest:testCannot_computePreHistoricalL2BlockNumber() (gas: 11093) L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 18805)
L2OutputOracleTest:testCannot_deleteL2Output_ifNotSequencer() (gas: 18793) L2OutputOracleTest:testCannot_deleteWrongL2Output() (gas: 79498)
L2OutputOracleTest:testCannot_deleteWrongL2Output() (gas: 77352) L2OutputOracleTest:test_appendWithBlockhashAndHeight() (gas: 69365)
L2OutputOracleTest:test_appendingAnotherOutput() (gas: 68582) L2OutputOracleTest:test_appendingAnotherOutput() (gas: 70714)
L2OutputOracleTest:test_computeL2BlockNumber() (gas: 14755) L2OutputOracleTest:test_computeL2Timestamp() (gas: 19230)
L2OutputOracleTest:test_constructor() (gas: 33752) L2OutputOracleTest:test_constructor() (gas: 33908)
L2OutputOracleTest:test_deleteL2Output() (gas: 64338) L2OutputOracleTest:test_deleteL2Output() (gas: 66081)
L2OutputOracleTest:test_getL2Output() (gas: 74601) L2OutputOracleTest:test_getL2Output() (gas: 76274)
L2OutputOracleTest:test_latestBlockTimestamp() (gas: 68377) L2OutputOracleTest:test_latestBlockNumber() (gas: 70075)
L2OutputOracleTest:test_nextTimestamp() (gas: 9236) L2OutputOracleTest:test_nextBlockNumber() (gas: 9279)
L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21578) L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21578)
L2StandardBridge_Test:test_finalizeDeposit() (gas: 93165) L2StandardBridge_Test:test_finalizeDeposit() (gas: 93165)
L2StandardBridge_Test:test_finalizeDeposit_failsToCompleteOutboundTransfer() (gas: 140106) L2StandardBridge_Test:test_finalizeDeposit_failsToCompleteOutboundTransfer() (gas: 140106)
...@@ -108,7 +108,7 @@ OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696) ...@@ -108,7 +108,7 @@ OptimismMintableTokenFactory_Test:test_initializeShouldRevert() (gas: 12696)
OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11413) OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 11413)
OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9214) OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 9214)
OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 121706) OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 121706)
OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 21863) OptimismPortal_Test:test_cannotVerifyRecentWithdrawal() (gas: 21886)
OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 70746) OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 70746)
OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 71114) OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 71114)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 70773) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 70773)
...@@ -117,7 +117,7 @@ OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreat ...@@ -117,7 +117,7 @@ OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreat
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 69947) OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 69947)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 77478) OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 77478)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 78049) OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 78049)
OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 28541) OptimismPortal_Test:test_invalidWithdrawalProof() (gas: 33702)
Proxy_Test:test_clashingFunctionSignatures() (gas: 101427) Proxy_Test:test_clashingFunctionSignatures() (gas: 101427)
Proxy_Test:test_implementationKey() (gas: 20942) Proxy_Test:test_implementationKey() (gas: 20942)
Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021) Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021)
...@@ -163,5 +163,5 @@ ResourceMetering_Test:test_useMoreThanMaxReverts() (gas: 16024) ...@@ -163,5 +163,5 @@ ResourceMetering_Test:test_useMoreThanMaxReverts() (gas: 16024)
SequencerFeeVault_Test:test_constructor() (gas: 7611) SequencerFeeVault_Test:test_constructor() (gas: 7611)
SequencerFeeVault_Test:test_minWithdrawalAmount() (gas: 5429) SequencerFeeVault_Test:test_minWithdrawalAmount() (gas: 5429)
SequencerFeeVault_Test:test_receive() (gas: 17280) SequencerFeeVault_Test:test_receive() (gas: 17280)
SequencerFeeVault_Test:test_revertWithdraw() (gas: 9245) SequencerFeeVault_Test:test_revertWithdraw() (gas: 9266)
SequencerFeeVault_Test:test_withdraw() (gas: 147297) SequencerFeeVault_Test:test_withdraw() (gas: 147300)
...@@ -150,7 +150,7 @@ contract OptimismPortal is ResourceMetering { ...@@ -150,7 +150,7 @@ contract OptimismPortal is ResourceMetering {
* @param _value ETH to send to the target. * @param _value ETH to send to the target.
* @param _gasLimit Minumum gas to be forwarded to the target. * @param _gasLimit Minumum gas to be forwarded to the target.
* @param _data Data to send to the target. * @param _data Data to send to the target.
* @param _l2Timestamp L2 timestamp of the outputRoot. * @param _l2BlockNumber L2 block number of the outputRoot.
* @param _outputRootProof Inclusion proof of the withdrawer contracts storage root. * @param _outputRootProof Inclusion proof of the withdrawer contracts storage root.
* @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract. * @param _withdrawalProof Inclusion proof for the given withdrawal in the withdrawer contract.
*/ */
...@@ -161,7 +161,7 @@ contract OptimismPortal is ResourceMetering { ...@@ -161,7 +161,7 @@ contract OptimismPortal is ResourceMetering {
uint256 _value, uint256 _value,
uint256 _gasLimit, uint256 _gasLimit,
bytes calldata _data, bytes calldata _data,
uint256 _l2Timestamp, uint256 _l2BlockNumber,
WithdrawalVerifier.OutputRootProof calldata _outputRootProof, WithdrawalVerifier.OutputRootProof calldata _outputRootProof,
bytes calldata _withdrawalProof bytes calldata _withdrawalProof
) external payable { ) external payable {
...@@ -179,7 +179,7 @@ contract OptimismPortal is ResourceMetering { ...@@ -179,7 +179,7 @@ contract OptimismPortal is ResourceMetering {
); );
// Get the output root. // Get the output root.
L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2Timestamp); L2OutputOracle.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber);
// Ensure that enough time has passed since the proposal was submitted before allowing a // Ensure that enough time has passed since the proposal was submitted before allowing a
// withdrawal. Under the assumption that the fault proof mechanism is operating correctly, // withdrawal. Under the assumption that the fault proof mechanism is operating correctly,
......
...@@ -47,33 +47,35 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -47,33 +47,35 @@ contract L2OutputOracle_Initializer is CommonTest {
L2OutputOracle oracle; L2OutputOracle oracle;
// Constructor arguments // Constructor arguments
address sequencer = 0x000000000000000000000000000000000000AbBa; uint256 submissionInterval = 42;
uint256 submissionInterval = 1800;
uint256 l2BlockTime = 2;
bytes32 genesisL2Output = keccak256(abi.encode(0)); bytes32 genesisL2Output = keccak256(abi.encode(0));
uint256 historicalTotalBlocks = 100; uint256 historicalTotalBlocks = 199;
uint256 startingBlockNumber = 200;
// Cache of the initial L2 timestamp uint256 startingTimestamp = 1000;
uint256 startingBlockTimestamp; uint256 l2BlockTime = 2;
address sequencer = 0x000000000000000000000000000000000000AbBa;
// By default the first block has timestamp zero, which will cause underflows in the tests // Test data
uint256 initTime = 1000; uint256 initL1Time;
function setUp() public virtual { function setUp() public virtual {
_setUp(); _setUp();
// Move time forward so we have a non-zero starting timestamp // By default the first block has timestamp and number zero, which will cause underflows in the
vm.warp(initTime); // tests, so we'll move forward to these block values.
initL1Time = startingTimestamp + 1;
vm.warp(initL1Time);
vm.roll(startingBlockNumber);
// Deploy the L2OutputOracle and transfer owernship to the sequencer // Deploy the L2OutputOracle and transfer owernship to the sequencer
oracle = new L2OutputOracle( oracle = new L2OutputOracle(
submissionInterval, submissionInterval,
l2BlockTime,
genesisL2Output, genesisL2Output,
historicalTotalBlocks, historicalTotalBlocks,
initTime, startingBlockNumber,
startingTimestamp,
l2BlockTime,
sequencer sequencer
); );
startingBlockTimestamp = block.timestamp;
} }
} }
......
//SPDX-License-Identifier: MIT //SPDX-License-Identifier: MIT
pragma solidity 0.8.10; pragma solidity 0.8.10;
import { CommonTest } from "./CommonTest.t.sol"; import { L2OutputOracle_Initializer } from "./CommonTest.t.sol";
import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../libraries/AddressAliasHelper.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol"; import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol"; import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol"; import { WithdrawalVerifier } from "../libraries/Lib_WithdrawalVerifier.sol";
contract OptimismPortal_Test is CommonTest { contract OptimismPortal_Test is L2OutputOracle_Initializer {
event TransactionDeposited( event TransactionDeposited(
address indexed from, address indexed from,
address indexed to, address indexed to,
...@@ -20,19 +20,11 @@ contract OptimismPortal_Test is CommonTest { ...@@ -20,19 +20,11 @@ contract OptimismPortal_Test is CommonTest {
); );
// Dependencies // Dependencies
L2OutputOracle oracle; // L2OutputOracle oracle;
OptimismPortal op; OptimismPortal op;
function setUp() external { function setUp() public override {
_setUp(); L2OutputOracle_Initializer.setUp();
oracle = new L2OutputOracle(
1800,
2,
keccak256(abi.encode(0)),
100,
1,
address(666)
);
op = new OptimismPortal(oracle, 7 days); op = new OptimismPortal(oracle, 7 days);
} }
...@@ -44,15 +36,7 @@ contract OptimismPortal_Test is CommonTest { ...@@ -44,15 +36,7 @@ contract OptimismPortal_Test is CommonTest {
function test_OptimismPortalReceiveEth() external { function test_OptimismPortalReceiveEth() external {
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
emit TransactionDeposited( emit TransactionDeposited(alice, alice, 100, 100, 100_000, false, hex"");
alice,
alice,
100,
100,
100_000,
false,
hex""
);
// give alice money and send as an eoa // give alice money and send as an eoa
vm.deal(alice, 2**64); vm.deal(alice, 2**64);
...@@ -254,47 +238,35 @@ contract OptimismPortal_Test is CommonTest { ...@@ -254,47 +238,35 @@ contract OptimismPortal_Test is CommonTest {
// function test_verifyWithdrawal() external {} // function test_verifyWithdrawal() external {}
function test_cannotVerifyRecentWithdrawal() external { function test_cannotVerifyRecentWithdrawal() external {
WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({ WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier
version: bytes32(0), .OutputRootProof({
stateRoot: bytes32(0), version: bytes32(0),
withdrawerStorageRoot: bytes32(0), stateRoot: bytes32(0),
latestBlockhash: bytes32(0) withdrawerStorageRoot: bytes32(0),
}); latestBlockhash: bytes32(0)
});
vm.expectRevert("OptimismPortal: proposal is not yet finalized"); vm.expectRevert("OptimismPortal: proposal is not yet finalized");
op.finalizeWithdrawalTransaction( op.finalizeWithdrawalTransaction(0, alice, alice, 0, 0, hex"", 0, outputRootProof, hex"");
0,
alice,
alice,
0,
0,
hex"",
0,
outputRootProof,
hex""
);
} }
function test_invalidWithdrawalProof() external { function test_invalidWithdrawalProof() external {
WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier.OutputRootProof({ WithdrawalVerifier.OutputRootProof memory outputRootProof = WithdrawalVerifier
version: bytes32(0), .OutputRootProof({
stateRoot: bytes32(0), version: bytes32(0),
withdrawerStorageRoot: bytes32(0), stateRoot: bytes32(0),
latestBlockhash: bytes32(0) withdrawerStorageRoot: bytes32(0),
}); latestBlockhash: bytes32(0)
});
vm.warp(oracle.nextTimestamp() + op.FINALIZATION_PERIOD_SECONDS());
vm.expectRevert("OptimismPortal: invalid output root proof"); vm.warp(
op.finalizeWithdrawalTransaction( oracle.getL2Output(
0, oracle.latestBlockNumber()
alice, ).timestamp
alice, + op.FINALIZATION_PERIOD_SECONDS()
0,
0,
hex"",
0,
outputRootProof,
hex""
); );
vm.expectRevert("OptimismPortal: invalid output root proof");
op.finalizeWithdrawalTransaction(0, alice, alice, 0, 0, hex"", 0, outputRootProof, hex"");
} }
} }
...@@ -91,28 +91,28 @@ where: ...@@ -91,28 +91,28 @@ where:
## L2 Output Oracle Smart Contract ## L2 Output Oracle Smart Contract
L2 blocks are produced at a constant rate of `L2_BLOCK_TIME` (2 seconds). L2 blocks are produced at a constant rate of `L2_BLOCK_TIME` (2 seconds).
A new L2 output MUST be appended to the chain once per `SUBMISSION_INTERVAL` (1800 seconds). Note that this interval is\ A new L2 output MUST be appended to the chain once per `SUBMISSION_INTERVAL` which is based on a number of blocks.
based on L2 time. It is OK to have L2 outputs submitted at larger or small intervals. The exact number is yet to be determined, and will depend on the design of the fault proving game.
The L2 Output Oracle contract implements the following interface: The L2 Output Oracle contract implements the following interface:
```js ```js
/** /**
* Accepts an L2 output checkpoint and the timestamp of the corresponding L2 * @notice Accepts an L2 outputRoot and the timestamp of the corresponding L2 block. The
* block. The timestamp must be equal to the current value returned by * timestamp must be equal to the current value returned by `nextTimestamp()` in order to be
* `nextTimestamp()` in order to be accepted. * accepted.
* This function may only be called by the Sequencer. * This function may only be called by the Sequencer.
* @param _l2Output The L2 output of the checkpoint block. * @param _l2Output The L2 output of the checkpoint block.
* @param _l2timestamp The L2 block timestamp that resulted in _l2Output. * @param _l2BlockNumber The L2 block number that resulted in _l2Output.
* @param _l1Blockhash A block hash which must be included in the current chain. * @param _l1Blockhash A block hash which must be included in the current chain.
* @param _l1Blocknumber The block number with the specified block hash. * @param _l1BlockNumber The block number with the specified block hash.
*/ */
function appendL2Output( function appendL2Output(
bytes32 _l2Output, bytes32 _l2Output,
uint256 _l2timestamp, uint256 _l2BlockNumber,
bytes32 _l1Blockhash, bytes32 _l1Blockhash,
uint256 _l1Blocknumber uint256 _l1BlockNumber
) )
/** /**
* @notice Deletes the most recent output. * @notice Deletes the most recent output.
...@@ -122,15 +122,9 @@ function appendL2Output( ...@@ -122,15 +122,9 @@ function appendL2Output(
function deleteL2Output(bytes32 _l2Output) external function deleteL2Output(bytes32 _l2Output) external
/** /**
* Computes the timestamp of the next L2 block that needs to be checkpointed. * @notice Computes the block number of the next L2 block that needs to be checkpointed.
*/
function nextTimestamp() public view returns (uint256)
/**
* Computes the L2 block number given a target L2 block timestamp.
* @param _timestamp The L2 block timestamp of the target block.
*/ */
function computeL2BlockNumber(uint256 _timestamp) public view returns (uint256) function nextBlockNumber() public view returns (uint256) {
``` ```
## Security Considerations ## Security Considerations
......
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