From 22779e4b340430675e00dbca2cfa13744e9df749 Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Sun, 26 Mar 2023 21:44:48 +0800 Subject: [PATCH 01/17] fix optimims port to bsc --- op-batcher/batcher/txmgr.go | 31 ++++++++----- op-chain-ops/genesis/config.go | 15 +++++-- op-e2e/actions/action.go | 2 + op-e2e/system_test.go | 2 + op-node/eth/heads.go | 24 +++++++++- op-node/flags/flags.go | 8 ++++ op-node/node/node.go | 4 +- op-node/rollup/derive/l1_block_info.go | 16 +++++-- op-node/rollup/derive/l1_block_info_test.go | 2 + op-node/rollup/driver/config.go | 3 ++ op-node/service.go | 9 ++-- op-proposer/proposer/l2_output_submitter.go | 50 +++++++++++++++++++-- op-service/util.go | 2 + op-signer/client/transaction_args.go | 36 +++++++++------ 14 files changed, 161 insertions(+), 43 deletions(-) diff --git a/op-batcher/batcher/txmgr.go b/op-batcher/batcher/txmgr.go index 3cc6e89812ccd..3d19879c9a1ac 100644 --- a/op-batcher/batcher/txmgr.go +++ b/op-batcher/batcher/txmgr.go @@ -6,6 +6,7 @@ import ( "math/big" "time" + opservice "github.com/ethereum-optimism/optimism/op-service" "github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -86,10 +87,14 @@ func (t *TransactionManager) calcGasTipAndFeeCap(ctx context.Context) (gasTipCap if err != nil || head == nil { return nil, nil, fmt.Errorf("failed to get L1 head block for fee cap: %w", err) } - if head.BaseFee == nil { - return nil, nil, fmt.Errorf("failed to get L1 basefee in block %d for fee cap", head.Number) + if opservice.ForBSC { + gasFeeCap = txmgr.CalcGasFeeCap(big.NewInt(0), gasTipCap) + } else { + if head.BaseFee == nil { + return nil, nil, fmt.Errorf("failed to get L1 basefee in block %d for fee cap", head.Number) + } + gasFeeCap = txmgr.CalcGasFeeCap(head.BaseFee, gasTipCap) } - gasFeeCap = txmgr.CalcGasFeeCap(head.BaseFee, gasTipCap) return gasTipCap, gasFeeCap, nil } @@ -110,14 +115,20 @@ func (t *TransactionManager) CraftTx(ctx context.Context, data []byte) (*types.T return nil, fmt.Errorf("failed to get nonce: %w", err) } - rawTx := &types.DynamicFeeTx{ - ChainID: t.chainID, - Nonce: nonce, - To: &t.batchInboxAddress, - GasTipCap: gasTipCap, - GasFeeCap: gasFeeCap, - Data: data, + rawTx := &types.LegacyTx{ + Nonce: nonce, + To: &t.batchInboxAddress, + GasPrice: big.NewInt(0).Add(gasTipCap, gasFeeCap), + Data: data, } + // rawTx := &types.DynamicFeeTx{ + // ChainID: t.chainID, + // Nonce: nonce, + // To: &t.batchInboxAddress, + // GasTipCap: gasTipCap, + // GasFeeCap: gasFeeCap, + // Data: data, + // } t.log.Info("creating tx", "to", rawTx.To, "from", t.senderAddress) gas, err := core.IntrinsicGas(rawTx.Data, nil, false, true, true, false) diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index f8b5c8454f328..b2182e7526336 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -20,6 +20,8 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var ( @@ -420,8 +422,10 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage if block.Number() == nil { return storage, errors.New("block number not set") } - if block.BaseFee() == nil { - return storage, errors.New("block base fee not set") + if !opservice.ForBSC { + if block.BaseFee() == nil { + return storage, errors.New("block base fee not set") + } } storage["L2ToL1MessagePasser"] = state.StorageValues{ @@ -434,8 +438,8 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage "msgNonce": 0, } storage["L1Block"] = state.StorageValues{ - "number": block.Number(), - "timestamp": block.Time(), + "number": block.Number(), + "timestamp": block.Time(), "basefee": block.BaseFee(), "hash": block.Hash(), "sequenceNumber": 0, @@ -443,6 +447,9 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage "l1FeeOverhead": config.GasPriceOracleOverhead, "l1FeeScalar": config.GasPriceOracleScalar, } + if opservice.ForBSC { + storage["L1Block"]["basefee"] = derive.BSCFakeBaseFee + } storage["LegacyERC20ETH"] = state.StorageValues{ "_name": "Ether", "_symbol": "ETH", diff --git a/op-e2e/actions/action.go b/op-e2e/actions/action.go index 53b710a44e210..50ffadb35147d 100644 --- a/op-e2e/actions/action.go +++ b/op-e2e/actions/action.go @@ -5,6 +5,7 @@ import ( "os" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var enableParallelTesting bool = true @@ -13,6 +14,7 @@ func init() { if os.Getenv("OP_E2E_DISABLE_PARALLEL") == "true" { enableParallelTesting = false } + opservice.ForBSC = false } func parallel(t e2eutils.TestingBase) { diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index ce6b30bccd02e..07e1bdcb54502 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -35,6 +35,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/withdrawals" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var enableParallelTesting bool = true @@ -53,6 +54,7 @@ func init() { if os.Getenv("OP_E2E_DISABLE_PARALLEL") == "true" { enableParallelTesting = false } + opservice.ForBSC = false } func parallel(t *testing.T) { diff --git a/op-node/eth/heads.go b/op-node/eth/heads.go index af92990f0f1d6..0ea669553a2d1 100644 --- a/op-node/eth/heads.go +++ b/op-node/eth/heads.go @@ -8,6 +8,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + + opservice "github.com/ethereum-optimism/optimism/op-service" ) // HeadSignalFn is used as callback function to accept head-signals @@ -48,8 +50,11 @@ func WatchHeadChanges(ctx context.Context, src NewHeadSource, fn HeadSignalFn) ( type L1BlockRefsSource interface { L1BlockRefByLabel(ctx context.Context, label BlockLabel) (L1BlockRef, error) + L1BlockRefByNumber(ctx context.Context, num uint64) (L1BlockRef, error) } +var finalizedBlockNumberForBSC uint64 = 15 + // PollBlockChanges opens a polling loop to fetch the L1 block reference with the given label, // on provided interval and with request timeout. Results are returned with provided callback fn, // which may block to pause/back-pressure polling. @@ -72,7 +77,24 @@ func PollBlockChanges(ctx context.Context, log log.Logger, src L1BlockRefsSource if err != nil { log.Warn("failed to poll L1 block", "label", label, "err", err) } else { - fn(ctx, ref) + if opservice.ForBSC { + reqCtx, reqCancel := context.WithTimeout(ctx, timeout) + number := ref.Number + if number < finalizedBlockNumberForBSC { + number = 0 + } else { + number -= finalizedBlockNumberForBSC + } + ref, err := src.L1BlockRefByNumber(reqCtx, number) + reqCancel() + if err != nil { + log.Warn("failed to poll L1 block", "number", number, "err", err) + } else { + fn(ctx, ref) + } + } else { + fn(ctx, ref) + } } case <-ctx.Done(): return ctx.Err() diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index df0deb56be5f4..1f6c418510fee 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -99,6 +99,13 @@ var ( Usage: "Initialize the sequencer in a stopped state. The sequencer can be started using the admin_startSequencer RPC", EnvVar: prefixEnvVar("SEQUENCER_STOPPED"), } + SequencerMaxSafeLagFlag = cli.Uint64Flag{ + Name: "sequencer.max-safe-lag", + Usage: "Maximum number of L2 blocks for restricting the distance between L2 safe and unsafe", + EnvVar: prefixEnvVar("SEQUENCER_MAX_SAFE_LAG"), + Required: false, + Value: 0, + } SequencerL1Confs = cli.Uint64Flag{ Name: "sequencer.l1-confs", Usage: "Number of L1 blocks to keep distance from the L1 head as a sequencer for picking an L1 origin.", @@ -203,6 +210,7 @@ var optionalFlags = append([]cli.Flag{ VerifierL1Confs, SequencerEnabledFlag, SequencerStoppedFlag, + SequencerMaxSafeLagFlag, SequencerL1Confs, L1EpochPollIntervalFlag, LogLevelFlag, diff --git a/op-node/node/node.go b/op-node/node/node.go index ed194818d2f8f..b44ce2fce6055 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -145,9 +145,9 @@ func (n *OpNode) initL1(ctx context.Context, cfg *Config) error { // Poll for the safe L1 block and finalized block, // which only change once per epoch at most and may be delayed. - n.l1SafeSub = eth.PollBlockChanges(n.resourcesCtx, n.log, n.l1Source, n.OnNewL1Safe, eth.Safe, + n.l1SafeSub = eth.PollBlockChanges(n.resourcesCtx, n.log, n.l1Source, n.OnNewL1Safe, eth.Unsafe, cfg.L1EpochPollInterval, time.Second*10) - n.l1FinalizedSub = eth.PollBlockChanges(n.resourcesCtx, n.log, n.l1Source, n.OnNewL1Finalized, eth.Finalized, + n.l1FinalizedSub = eth.PollBlockChanges(n.resourcesCtx, n.log, n.l1Source, n.OnNewL1Finalized, eth.Unsafe, cfg.L1EpochPollInterval, time.Second*10) return nil } diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index 0e4733d710692..7c45ac08571db 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-node/eth" + opservice "github.com/ethereum-optimism/optimism/op-service" ) const ( @@ -54,11 +55,13 @@ func (info *L1BlockInfo) MarshalBinary() ([]byte, error) { offset += 32 binary.BigEndian.PutUint64(data[offset+24:offset+32], info.Time) offset += 32 - // Ensure that the baseFee is not too large. - if info.BaseFee.BitLen() > 256 { - return nil, fmt.Errorf("base fee exceeds 256 bits: %d", info.BaseFee) + if info.BaseFee != nil { + // Ensure that the baseFee is not too large. + if info.BaseFee.BitLen() > 256 { + return nil, fmt.Errorf("base fee exceeds 256 bits: %d", info.BaseFee) + } + info.BaseFee.FillBytes(data[offset : offset+32]) } - info.BaseFee.FillBytes(data[offset : offset+32]) offset += 32 copy(data[offset:offset+32], info.BlockHash.Bytes()) offset += 32 @@ -117,6 +120,8 @@ func L1InfoDepositTxData(data []byte) (L1BlockInfo, error) { return info, err } +var BSCFakeBaseFee = big.NewInt(5000000000) + // L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block, // and the L2 block-height difference with the start of the epoch. func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfig, regolith bool) (*types.DepositTx, error) { @@ -130,6 +135,9 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi L1FeeOverhead: sysCfg.Overhead, L1FeeScalar: sysCfg.Scalar, } + if opservice.ForBSC { + infoDat.BaseFee = BSCFakeBaseFee + } data, err := infoDat.MarshalBinary() if err != nil { return nil, err diff --git a/op-node/rollup/derive/l1_block_info_test.go b/op-node/rollup/derive/l1_block_info_test.go index 721046178eeed..7a841e5b5f414 100644 --- a/op-node/rollup/derive/l1_block_info_test.go +++ b/op-node/rollup/derive/l1_block_info_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/testutils" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var _ eth.BlockInfo = (*testutils.MockBlockInfo)(nil) @@ -35,6 +36,7 @@ func randomL1Cfg(rng *rand.Rand, l1Info eth.BlockInfo) eth.SystemConfig { var MockDepositContractAddr = common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeef00000000") func TestParseL1InfoDepositTxData(t *testing.T) { + opservice.ForBSC = false randomSeqNr := func(rng *rand.Rand) uint64 { return rng.Uint64() } diff --git a/op-node/rollup/driver/config.go b/op-node/rollup/driver/config.go index e59ae55c9bae6..906770e8af323 100644 --- a/op-node/rollup/driver/config.go +++ b/op-node/rollup/driver/config.go @@ -16,4 +16,7 @@ type Config struct { // SequencerStopped is false when the driver should sequence new blocks. SequencerStopped bool `json:"sequencer_stopped"` + + // SequencerMaxSafeLag is the maximum number of L2 blocks for restricting the distance between L2 safe and unsafe. + SequencerMaxSafeLag uint64 `json:"sequencer_max_safe_lag"` } diff --git a/op-node/service.go b/op-node/service.go index 39c3a17ab7d90..ce737dcdefffa 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -136,10 +136,11 @@ func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConf func NewDriverConfig(ctx *cli.Context) (*driver.Config, error) { return &driver.Config{ - VerifierConfDepth: ctx.GlobalUint64(flags.VerifierL1Confs.Name), - SequencerConfDepth: ctx.GlobalUint64(flags.SequencerL1Confs.Name), - SequencerEnabled: ctx.GlobalBool(flags.SequencerEnabledFlag.Name), - SequencerStopped: ctx.GlobalBool(flags.SequencerStoppedFlag.Name), + VerifierConfDepth: ctx.GlobalUint64(flags.VerifierL1Confs.Name), + SequencerConfDepth: ctx.GlobalUint64(flags.SequencerL1Confs.Name), + SequencerEnabled: ctx.GlobalBool(flags.SequencerEnabledFlag.Name), + SequencerStopped: ctx.GlobalBool(flags.SequencerStoppedFlag.Name), + SequencerMaxSafeLag: ctx.GlobalUint64(flags.SequencerMaxSafeLagFlag.Name), }, nil } diff --git a/op-proposer/proposer/l2_output_submitter.go b/op-proposer/proposer/l2_output_submitter.go index cde9c485b673d..c38d301b327f8 100644 --- a/op-proposer/proposer/l2_output_submitter.go +++ b/op-proposer/proposer/l2_output_submitter.go @@ -19,11 +19,13 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli" "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/sources" + opservice "github.com/ethereum-optimism/optimism/op-service" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" oplog "github.com/ethereum-optimism/optimism/op-service/log" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" @@ -321,6 +323,39 @@ func (l *L2OutputSubmitter) FetchNextOutputInfo(ctx context.Context) (*eth.Outpu return output, true, nil } +// calcGasTipAndFeeCap queries L1 to determine what a suitable miner tip & basefee limit would be for timely inclusion +func (t *L2OutputSubmitter) calcGasTipAndFeeCap(ctx context.Context) (gasTipCap *big.Int, gasFeeCap *big.Int, err error) { + networkTimeout := 10 * time.Second + childCtx, cancel := context.WithTimeout(ctx, networkTimeout) + gasTipCap, err = t.l1Client.SuggestGasTipCap(childCtx) + cancel() + if err != nil { + return nil, nil, fmt.Errorf("failed to get suggested gas tip cap: %w", err) + } + + if gasTipCap == nil { + t.log.Warn("unexpected unset gasTipCap, using default 2 gwei") + gasTipCap = new(big.Int).SetUint64(params.GWei * 2) + } + + childCtx, cancel = context.WithTimeout(ctx, networkTimeout) + head, err := t.l1Client.HeaderByNumber(childCtx, nil) + cancel() + if err != nil || head == nil { + return nil, nil, fmt.Errorf("failed to get L1 head block for fee cap: %w", err) + } + if opservice.ForBSC { + gasFeeCap = txmgr.CalcGasFeeCap(big.NewInt(0), gasTipCap) + } else { + if head.BaseFee == nil { + return nil, nil, fmt.Errorf("failed to get L1 basefee in block %d for fee cap", head.Number) + } + gasFeeCap = txmgr.CalcGasFeeCap(head.BaseFee, gasTipCap) + } + + return gasTipCap, gasFeeCap, nil +} + // CreateProposalTx transforms an output response into a signed output transaction. // It does not send the transaction to the transaction pool. func (l *L2OutputSubmitter) CreateProposalTx(ctx context.Context, output *eth.OutputResponse) (*types.Transaction, error) { @@ -330,21 +365,28 @@ func (l *L2OutputSubmitter) CreateProposalTx(ctx context.Context, output *eth.Ou return nil, err } + gasTipCap, gasFeeCap, err := l.calcGasTipAndFeeCap(ctx) + if err != nil { + return nil, err + } + opts := &bind.TransactOpts{ From: l.from, Signer: func(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return l.signerFn(ctx, addr, tx) }, - Context: ctx, - Nonce: new(big.Int).SetUint64(nonce), - NoSend: true, + Context: ctx, + Nonce: new(big.Int).SetUint64(nonce), + GasPrice: big.NewInt(0).Add(gasTipCap, gasFeeCap), + NoSend: true, } tx, err := l.l2ooContract.ProposeL2Output( opts, output.OutputRoot, new(big.Int).SetUint64(output.BlockRef.Number), - output.Status.CurrentL1.Hash, + // output.Status.CurrentL1.Hash, + [32]byte{}, new(big.Int).SetUint64(output.Status.CurrentL1.Number)) if err != nil { l.log.Error("failed to create the ProposeL2Output transaction", "err", err) diff --git a/op-service/util.go b/op-service/util.go index 2f282dccffa0b..41e1497c6a34c 100644 --- a/op-service/util.go +++ b/op-service/util.go @@ -9,6 +9,8 @@ import ( "time" ) +var ForBSC = true + func PrefixEnvVar(prefix, suffix string) string { return prefix + "_" + suffix } diff --git a/op-signer/client/transaction_args.go b/op-signer/client/transaction_args.go index 480d46a15e103..7cfec5069fdb3 100644 --- a/op-signer/client/transaction_args.go +++ b/op-signer/client/transaction_args.go @@ -65,20 +65,28 @@ func (args *TransactionArgs) data() []byte { // ToTransaction converts the arguments to a transaction. func (args *TransactionArgs) ToTransaction() *types.Transaction { var data types.TxData - al := types.AccessList{} - if args.AccessList != nil { - al = *args.AccessList - } - data = &types.DynamicFeeTx{ - To: args.To, - ChainID: (*big.Int)(args.ChainID), - Nonce: uint64(*args.Nonce), - Gas: uint64(*args.Gas), - GasFeeCap: (*big.Int)(args.MaxFeePerGas), - GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas), - Value: (*big.Int)(args.Value), - Data: args.data(), - AccessList: al, + // al := types.AccessList{} + // if args.AccessList != nil { + // al = *args.AccessList + // } + // data = &types.DynamicFeeTx{ + // To: args.To, + // ChainID: (*big.Int)(args.ChainID), + // Nonce: uint64(*args.Nonce), + // Gas: uint64(*args.Gas), + // GasFeeCap: (*big.Int)(args.MaxFeePerGas), + // GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas), + // Value: (*big.Int)(args.Value), + // Data: args.data(), + // AccessList: al, + // } + data = &types.LegacyTx{ + To: args.To, + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasPrice: args.GasPrice.ToInt(), + Value: (*big.Int)(args.Value), + Data: args.data(), } return types.NewTx(data) } From d75fb80c5db39cbb10c222dd55e02fb6c181783b Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Tue, 28 Mar 2023 16:51:12 +0800 Subject: [PATCH 02/17] fix basefee --- packages/contracts-bedrock/contracts/L1/ResourceMetering.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol index b2322f2ea73b6..1393c0ca67c39 100644 --- a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol +++ b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol @@ -157,7 +157,8 @@ abstract contract ResourceMetering is Initializable { // division by zero for L1s that don't support 1559 or to avoid excessive gas burns during // periods of extremely low L1 demand. One-day average gas fee hasn't dipped below 1 gwei // during any 1 day period in the last 5 years, so should be fine. - uint256 gasCost = resourceCost / Math.max(block.basefee, 1 gwei); + // uint256 gasCost = resourceCost / Math.max(block.basefee, 1 gwei); + uint256 gasCost = resourceCost / 5 gwei; // Give the user a refund based on the amount of gas they used to do all of the work up to // this point. Since we're at the end of the modifier, this should be pretty accurate. Acts From 2bf1e92adad9462d2659cbccee778d5fd62a1ada Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Thu, 6 Apr 2023 16:47:03 +0800 Subject: [PATCH 03/17] fix gasprice --- op-batcher/batcher/txmgr.go | 3 ++- op-proposer/proposer/l2_output_submitter.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/op-batcher/batcher/txmgr.go b/op-batcher/batcher/txmgr.go index 3d19879c9a1ac..25637a39377d2 100644 --- a/op-batcher/batcher/txmgr.go +++ b/op-batcher/batcher/txmgr.go @@ -107,6 +107,7 @@ func (t *TransactionManager) CraftTx(ctx context.Context, data []byte) (*types.T if err != nil { return nil, err } + _ = gasTipCap childCtx, cancel := context.WithTimeout(ctx, networkTimeout) nonce, err := t.l1Client.NonceAt(childCtx, t.senderAddress, nil) @@ -118,7 +119,7 @@ func (t *TransactionManager) CraftTx(ctx context.Context, data []byte) (*types.T rawTx := &types.LegacyTx{ Nonce: nonce, To: &t.batchInboxAddress, - GasPrice: big.NewInt(0).Add(gasTipCap, gasFeeCap), + GasPrice: gasFeeCap, Data: data, } // rawTx := &types.DynamicFeeTx{ diff --git a/op-proposer/proposer/l2_output_submitter.go b/op-proposer/proposer/l2_output_submitter.go index c38d301b327f8..6a6000f447490 100644 --- a/op-proposer/proposer/l2_output_submitter.go +++ b/op-proposer/proposer/l2_output_submitter.go @@ -369,6 +369,7 @@ func (l *L2OutputSubmitter) CreateProposalTx(ctx context.Context, output *eth.Ou if err != nil { return nil, err } + _ = gasTipCap opts := &bind.TransactOpts{ From: l.from, @@ -377,7 +378,7 @@ func (l *L2OutputSubmitter) CreateProposalTx(ctx context.Context, output *eth.Ou }, Context: ctx, Nonce: new(big.Int).SetUint64(nonce), - GasPrice: big.NewInt(0).Add(gasTipCap, gasFeeCap), + GasPrice: gasFeeCap, NoSend: true, } From 307353a3b952c25082bb347372b18a3e859e914f Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Mon, 10 Apr 2023 16:16:52 +0800 Subject: [PATCH 04/17] add avarage gasprice to l1 block info --- op-node/rollup/derive/attributes.go | 53 ++++++++++++++++++- .../rollup/derive/attributes_queue_test.go | 2 + 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 9383c642c901a..3bb01345e3667 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -3,6 +3,7 @@ package derive import ( "context" "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -11,12 +12,14 @@ import ( "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" + opservice "github.com/ethereum-optimism/optimism/op-service" ) // L1ReceiptsFetcher fetches L1 header info and receipts for the payload attributes derivation (the info tx and deposits) type L1ReceiptsFetcher interface { InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) + InfoAndTxsByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, types.Transactions, error) } type SystemConfigL2Fetcher interface { @@ -53,6 +56,14 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex return nil, NewTemporaryError(fmt.Errorf("failed to retrieve L2 parent block: %w", err)) } + var gasPrice *big.Int + if opservice.ForBSC { + gasPrice, err = ba.prepareAverageGasPrice(ctx, epoch) + if err != nil { + return nil, NewTemporaryError(fmt.Errorf("failed to prepare average gas price: %w", err)) + } + } + // If the L1 origin changed this block, then we are in the first block of the epoch. In this // case we need to fetch all transaction receipts from the L1 origin block so we can scan for // user deposits. @@ -76,7 +87,9 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex if err := UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, ba.cfg); err != nil { return nil, NewCriticalError(fmt.Errorf("failed to apply derived L1 sysCfg updates: %w", err)) } - + if opservice.ForBSC { + info = newGasPriceWrapper(info, gasPrice) + } l1Info = info depositTxs = deposits seqNumber = 0 @@ -88,6 +101,9 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex if err != nil { return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info: %w", err)) } + if opservice.ForBSC { + info = newGasPriceWrapper(info, gasPrice) + } l1Info = info depositTxs = nil seqNumber = l2Parent.SequenceNumber + 1 @@ -118,3 +134,38 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex GasLimit: (*eth.Uint64Quantity)(&sysConfig.GasLimit), }, nil } + +var bscDefaultGasPrice = big.NewInt(5000000000) + +func (ba *FetchingAttributesBuilder) prepareAverageGasPrice(ctx context.Context, epoch eth.BlockID) (*big.Int, error) { + _, txs, err := ba.l1.InfoAndTxsByHash(ctx, epoch.Hash) + if err != nil { + return nil, err + } + var sum big.Int + for _, tx := range txs { + sum.Add(&sum, tx.GasPrice()) + } + if len(txs) == 0 { + return bscDefaultGasPrice, nil + } + return sum.Div(&sum, big.NewInt(int64(len(txs)))), nil +} + +type gasPriceWrapper struct { + eth.BlockInfo + gasprice *big.Int +} + +var _ (eth.BlockInfo) = (*gasPriceWrapper)(nil) + +func newGasPriceWrapper(info eth.BlockInfo, gasprice *big.Int) *gasPriceWrapper { + return &gasPriceWrapper{ + BlockInfo: info, + gasprice: gasprice, + } +} + +func (w *gasPriceWrapper) BaseFee() *big.Int { + return w.gasprice +} diff --git a/op-node/rollup/derive/attributes_queue_test.go b/op-node/rollup/derive/attributes_queue_test.go index d6f2f5b3449f0..2c3bc935c24bb 100644 --- a/op-node/rollup/derive/attributes_queue_test.go +++ b/op-node/rollup/derive/attributes_queue_test.go @@ -16,12 +16,14 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testutils" + observice "github.com/ethereum-optimism/optimism/op-service" ) // TestAttributesQueue checks that it properly uses the PreparePayloadAttributes function // (which is well tested) and that it properly sets NoTxPool and adds in the candidate // transactions. func TestAttributesQueue(t *testing.T) { + observice.ForBSC = false // test config, only init the necessary fields cfg := &rollup.Config{ BlockTime: 2, From 2f7128c47a95ec55a93197af1b8d2b9ecd4c573d Mon Sep 17 00:00:00 2001 From: arthurma Date: Tue, 11 Apr 2023 14:24:56 +0800 Subject: [PATCH 05/17] [beta] new a CI flow to export image to nr ecr --- .github/workflows/ci-with-pr.yml | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 .github/workflows/ci-with-pr.yml diff --git a/.github/workflows/ci-with-pr.yml b/.github/workflows/ci-with-pr.yml new file mode 100644 index 0000000000000..a204bede87927 --- /dev/null +++ b/.github/workflows/ci-with-pr.yml @@ -0,0 +1,92 @@ +name: CI-trigger-by-tag + +on: + create: + tags: + - v* + +env: + REGION: us-east-1 + ECR_REGISTRY_ID: 553885929720 + AWS_ASSUMED_ROLE_ARN: arn:aws:iam::553885929720:role/tf_nodereal_prod_ecr_cicd_deployment_assume_role + +jobs: + ci-lint: + runs-on: [self-hosted,qa-infra-k8s] + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: static check + id: static-check + # TODO: developers need to add static check scripts + run: | + echo "static check" + ci-tests: + runs-on: [self-hosted,qa-infra-k8s] + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: unit tests + id: unit-tests + # TODO: developers need to add unit test scripts + run: | + echo "unit tests" + ci-build: + runs-on: [self-hosted,qa-infra-k8s] + needs: [ci-lint, ci-tests] + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: aws assume role + id: aws-assume-role + run: | + UUID=$(cat /proc/sys/kernel/random/uuid) + OUT=$(aws sts assume-role --role-arn $AWS_ASSUMED_ROLE_ARN --role-session-name $UUID) + echo ::set-output name=aws_access_key_id::"$(echo $OUT | jq -r '.Credentials''.AccessKeyId')" + echo ::set-output name=aws_secret_key::"$(echo $OUT | jq -r '.Credentials''.SecretAccessKey')" + echo ::set-output name=aws_sessions_token::"$(echo $OUT | jq -r '.Credentials''.SessionToken')" + - name: Build, tag, and push image to Amazon ECR + env: + AWS_ACCESS_KEY_ID: ${{ steps.aws-assume-role.outputs.aws_access_key_id }} + AWS_SECRET_ACCESS_KEY: ${{ steps.aws-assume-role.outputs.aws_secret_key }} + AWS_SESSION_TOKEN: ${{ steps.aws-assume-role.outputs.aws_sessions_token }} + OP_NODE_IMAGE_REPO: op-node + OP_BATCHER_IMAGE_REPO: op-batcher + OP_PROPOSER_IMAGE_REPO: op-proposer + run: | + OP_NODE_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_NODE_IMAGE_REPO:${GITHUB_REF#refs/*/}" + OP_BATCHER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_BATCHER_IMAGE_REPO:${GITHUB_REF#refs/*/}" + OP_PROPOSER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_PROPOSER_IMAGE_REPO:${GITHUB_REF#refs/*/}" + + aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com + + aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_NODE_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_NODE_IMAGE_REPO + aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_NODE_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json + + aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_BATCHER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_BATCHER_IMAGE_REPO + aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_BATCHER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json + + aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_PROPOSER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO + aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json + + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME op-node/Dockerfile + docker push $OP_NODE_IMAGE_NAME + + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME op-node/Dockerfile + docker push $OP_BATCHER_IMAGE_NAME + + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME op-node/Dockerfile + docker push $OP_PROPOSER_IMAGE_NAME + + - name: notify comment + uses: peter-evans/create-or-update-comment@v1 + env: + GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + CI is complete: image tag => ${GITHUB_REF#refs/*/} + reactions: eyes From 40a7234bb94ad561310e51b2c1ccbd950ac98cd9 Mon Sep 17 00:00:00 2001 From: ArthurMa <4406arthur@gmail.com> Date: Tue, 11 Apr 2023 17:10:56 +0800 Subject: [PATCH 06/17] Update and rename ci-with-pr.yml to ci-with-tag.yml --- .github/workflows/{ci-with-pr.yml => ci-with-tag.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{ci-with-pr.yml => ci-with-tag.yml} (99%) diff --git a/.github/workflows/ci-with-pr.yml b/.github/workflows/ci-with-tag.yml similarity index 99% rename from .github/workflows/ci-with-pr.yml rename to .github/workflows/ci-with-tag.yml index a204bede87927..d10bf29c0e467 100644 --- a/.github/workflows/ci-with-pr.yml +++ b/.github/workflows/ci-with-tag.yml @@ -3,7 +3,7 @@ name: CI-trigger-by-tag on: create: tags: - - v* + - * env: REGION: us-east-1 From 023aa14ab2346e9ed83d7fac226150800b7253fe Mon Sep 17 00:00:00 2001 From: ArthurMa <4406arthur@gmail.com> Date: Tue, 11 Apr 2023 17:12:30 +0800 Subject: [PATCH 07/17] Update ci-with-tag.yml --- .github/workflows/ci-with-tag.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml index d10bf29c0e467..b53b150ce8e52 100644 --- a/.github/workflows/ci-with-tag.yml +++ b/.github/workflows/ci-with-tag.yml @@ -3,7 +3,7 @@ name: CI-trigger-by-tag on: create: tags: - - * + - '*' env: REGION: us-east-1 From 1d83273d3ca077a939d3e37727f72741b3eb0ed7 Mon Sep 17 00:00:00 2001 From: ArthurMa <4406arthur@gmail.com> Date: Tue, 11 Apr 2023 17:44:58 +0800 Subject: [PATCH 08/17] Update ci-with-tag.yml --- .github/workflows/ci-with-tag.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml index b53b150ce8e52..1d912282bfc7b 100644 --- a/.github/workflows/ci-with-tag.yml +++ b/.github/workflows/ci-with-tag.yml @@ -72,13 +72,13 @@ jobs: aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_PROPOSER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME op-node/Dockerfile + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME -f op-node/Dockerfile docker push $OP_NODE_IMAGE_NAME - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME op-node/Dockerfile + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME -f op-batcher/Dockerfile docker push $OP_BATCHER_IMAGE_NAME - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME op-node/Dockerfile + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME -f op-proposer/Dockerfile docker push $OP_PROPOSER_IMAGE_NAME - name: notify comment From 071c9d80652b1e8b5e9a4d8abe570545417eb535 Mon Sep 17 00:00:00 2001 From: ArthurMa <4406arthur@gmail.com> Date: Tue, 11 Apr 2023 17:51:44 +0800 Subject: [PATCH 09/17] Update ci-with-tag.yml --- .github/workflows/ci-with-tag.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml index 1d912282bfc7b..9c96f911f7a1d 100644 --- a/.github/workflows/ci-with-tag.yml +++ b/.github/workflows/ci-with-tag.yml @@ -57,6 +57,11 @@ jobs: OP_BATCHER_IMAGE_REPO: op-batcher OP_PROPOSER_IMAGE_REPO: op-proposer run: | + + echo "::add-mask::$AWS_ACCESS_KEY_ID" + echo "::add-mask::$AWS_SECRET_ACCESS_KEY" + echo "::add-mask::$AWS_SESSION_TOKEN" + OP_NODE_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_NODE_IMAGE_REPO:${GITHUB_REF#refs/*/}" OP_BATCHER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_BATCHER_IMAGE_REPO:${GITHUB_REF#refs/*/}" OP_PROPOSER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_PROPOSER_IMAGE_REPO:${GITHUB_REF#refs/*/}" From a7672a549deede2da3b5f1befb7388895f0ee542 Mon Sep 17 00:00:00 2001 From: ArthurMa <4406arthur@gmail.com> Date: Tue, 11 Apr 2023 18:44:26 +0800 Subject: [PATCH 10/17] Update ci-with-tag.yml --- .github/workflows/ci-with-tag.yml | 40 ++++++++----------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml index 9c96f911f7a1d..ae9766c2488d0 100644 --- a/.github/workflows/ci-with-tag.yml +++ b/.github/workflows/ci-with-tag.yml @@ -4,11 +4,9 @@ on: create: tags: - '*' - env: REGION: us-east-1 ECR_REGISTRY_ID: 553885929720 - AWS_ASSUMED_ROLE_ARN: arn:aws:iam::553885929720:role/tf_nodereal_prod_ecr_cicd_deployment_assume_role jobs: ci-lint: @@ -40,28 +38,19 @@ jobs: - uses: actions/checkout@v3 with: token: ${{ secrets.GITHUB_TOKEN }} - - name: aws assume role - id: aws-assume-role - run: | - UUID=$(cat /proc/sys/kernel/random/uuid) - OUT=$(aws sts assume-role --role-arn $AWS_ASSUMED_ROLE_ARN --role-session-name $UUID) - echo ::set-output name=aws_access_key_id::"$(echo $OUT | jq -r '.Credentials''.AccessKeyId')" - echo ::set-output name=aws_secret_key::"$(echo $OUT | jq -r '.Credentials''.SecretAccessKey')" - echo ::set-output name=aws_sessions_token::"$(echo $OUT | jq -r '.Credentials''.SessionToken')" + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 # More information on this action can be found below in the 'AWS Credentials' section + with: + role-to-assume: arn:aws:iam::553885929720:role/tf_nodereal_prod_ecr_cicd_deployment_assume_role + aws-region: us-east-1 + role-duration-seconds: 1800 - name: Build, tag, and push image to Amazon ECR env: - AWS_ACCESS_KEY_ID: ${{ steps.aws-assume-role.outputs.aws_access_key_id }} - AWS_SECRET_ACCESS_KEY: ${{ steps.aws-assume-role.outputs.aws_secret_key }} - AWS_SESSION_TOKEN: ${{ steps.aws-assume-role.outputs.aws_sessions_token }} OP_NODE_IMAGE_REPO: op-node OP_BATCHER_IMAGE_REPO: op-batcher OP_PROPOSER_IMAGE_REPO: op-proposer run: | - echo "::add-mask::$AWS_ACCESS_KEY_ID" - echo "::add-mask::$AWS_SECRET_ACCESS_KEY" - echo "::add-mask::$AWS_SESSION_TOKEN" - OP_NODE_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_NODE_IMAGE_REPO:${GITHUB_REF#refs/*/}" OP_BATCHER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_BATCHER_IMAGE_REPO:${GITHUB_REF#refs/*/}" OP_PROPOSER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_PROPOSER_IMAGE_REPO:${GITHUB_REF#refs/*/}" @@ -77,21 +66,12 @@ jobs: aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_PROPOSER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME -f op-node/Dockerfile + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME op-node/ docker push $OP_NODE_IMAGE_NAME - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME -f op-batcher/Dockerfile + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME op-batcher/ docker push $OP_BATCHER_IMAGE_NAME - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME -f op-proposer/Dockerfile + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME op-proposer/ docker push $OP_PROPOSER_IMAGE_NAME - - - name: notify comment - uses: peter-evans/create-or-update-comment@v1 - env: - GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - CI is complete: image tag => ${GITHUB_REF#refs/*/} - reactions: eyes + From 4173294881509bf52e842f9212bcdc06c159dbc4 Mon Sep 17 00:00:00 2001 From: ArthurMa <4406arthur@gmail.com> Date: Tue, 11 Apr 2023 19:30:23 +0800 Subject: [PATCH 11/17] Update ci-with-tag.yml --- .github/workflows/ci-with-tag.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml index ae9766c2488d0..646efec309a09 100644 --- a/.github/workflows/ci-with-tag.yml +++ b/.github/workflows/ci-with-tag.yml @@ -66,12 +66,12 @@ jobs: aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_PROPOSER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME op-node/ + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME -f op-node/Dockerfile . docker push $OP_NODE_IMAGE_NAME - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME op-batcher/ + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME -f op-batcher/Dockerfile . docker push $OP_BATCHER_IMAGE_NAME - docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME op-proposer/ + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME -f op-proposer/Dockerfile . docker push $OP_PROPOSER_IMAGE_NAME From 25954a2f428549ebc31a2c4391f39d243e3bc25d Mon Sep 17 00:00:00 2001 From: keroro Date: Tue, 11 Apr 2023 21:15:56 +0800 Subject: [PATCH 12/17] fix(.github): change create trigger to push trigger --- .github/workflows/ci-with-tag.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml index 646efec309a09..56ad0e69abab6 100644 --- a/.github/workflows/ci-with-tag.yml +++ b/.github/workflows/ci-with-tag.yml @@ -1,7 +1,7 @@ name: CI-trigger-by-tag on: - create: + push: tags: - '*' env: From a582e6f9a7c40c7e1b92950f9a1b37f728b21662 Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Wed, 12 Apr 2023 18:24:56 +0800 Subject: [PATCH 13/17] filter system trxs which gas price is zero --- op-node/rollup/derive/attributes.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 3bb01345e3667..361321186aa7e 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -142,14 +142,19 @@ func (ba *FetchingAttributesBuilder) prepareAverageGasPrice(ctx context.Context, if err != nil { return nil, err } + count := 0 var sum big.Int for _, tx := range txs { + if tx.GasPrice().Cmp(common.Big0) <= 0 { + continue + } sum.Add(&sum, tx.GasPrice()) + count += 1 } - if len(txs) == 0 { + if count == 0 { return bscDefaultGasPrice, nil } - return sum.Div(&sum, big.NewInt(int64(len(txs)))), nil + return sum.Div(&sum, big.NewInt(int64(count))), nil } type gasPriceWrapper struct { From 8fa4ea66cd201c9cfd2a7b7e4b24023b64cef3c0 Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Thu, 13 Apr 2023 13:22:43 +0800 Subject: [PATCH 14/17] update to for_bsc 0.5 --- .changeset/clever-jokes-notice.md | 5 + .changeset/cuddly-turkeys-burn.md | 5 + .changeset/flat-adults-hope.md | 5 - .changeset/fluffy-geese-yell.md | 5 - .changeset/long-frogs-study.md | 5 - .changeset/metal-apes-taste.md | 5 - .changeset/metal-feet-give.md | 5 - ...icked-ads-pump.md => pink-chicken-hear.md} | 2 +- .changeset/poor-buses-hug.md | 5 - .changeset/quiet-crews-leave.md | 5 - .changeset/sharp-islands-attend.md | 6 + .changeset/sixty-days-explain.md | 5 - .changeset/slimy-mangos-brake.md | 5 - .changeset/three-apes-study.md | 5 - .changeset/tricky-donkeys-lick.md | 9 + .circleci/config.yml | 15 +- LICENSE | 2 +- README.md | 4 +- batch-submitter/config.go | 2 +- batch-submitter/drivers/proposer/driver.go | 2 +- docs/op-stack/src/.vuepress/config.js | 17 +- docs/op-stack/src/.vuepress/enhanceApp.js | 14 + docs/op-stack/src/.vuepress/styles/index.styl | 20 +- .../.vuepress/theme/components/PageMeta.vue | 5 + .../docs/understand/superchain-diag.png | Bin 68170 -> 65703 bytes docs/op-stack/src/docs/build/explorer.md | 63 ++ .../src/docs/build/getting-started.md | 27 +- docs/op-stack/src/docs/build/sdk.md | 155 ++++ docs/op-stack/src/docs/contribute.md | 6 +- docs/op-stack/yarn.lock | 182 ++++- go.mod | 12 +- go.sum | 180 +---- integration-tests/package.json | 4 +- op-batcher/Dockerfile | 2 +- op-batcher/Makefile | 13 +- op-batcher/batcher/batch_submitter.go | 13 +- op-batcher/batcher/channel_builder.go | 105 ++- op-batcher/batcher/channel_builder_test.go | 750 ++++++++++++++++++ op-batcher/batcher/channel_config_test.go | 129 +++ op-batcher/batcher/channel_manager.go | 111 ++- op-batcher/batcher/channel_manager_test.go | 320 +++++++- op-batcher/batcher/config.go | 29 +- op-batcher/batcher/driver.go | 60 +- op-batcher/batcher/txmgr.go | 2 +- op-batcher/metrics/metrics.go | 249 ++++++ op-batcher/metrics/noop.go | 30 + op-chain-ops/Dockerfile | 2 +- op-chain-ops/cmd/check-migration/main.go | 210 +++++ op-chain-ops/cmd/op-migrate/main.go | 5 + op-chain-ops/ether/cli.go | 10 + op-chain-ops/ether/migrate.go | 239 +++--- op-chain-ops/ether/migrate_test.go | 143 +++- op-chain-ops/genesis/check.go | 124 ++- op-chain-ops/genesis/config.go | 5 + op-chain-ops/genesis/db_migration.go | 41 +- op-chain-ops/genesis/layer_one.go | 2 +- .../testdata/test-deploy-config-full.json | 1 + op-chain-ops/util/state_iterator.go | 161 ++++ op-chain-ops/util/state_iterator_test.go | 211 +++++ op-e2e/actions/blocktime_test.go | 4 +- op-e2e/actions/fork_test.go | 53 ++ op-e2e/actions/l1_miner.go | 46 +- op-e2e/actions/l1_miner_test.go | 8 +- op-e2e/actions/l1_replica.go | 18 +- op-e2e/actions/l1_replica_test.go | 6 +- op-e2e/actions/l2_batcher.go | 14 +- op-e2e/actions/l2_batcher_test.go | 136 +++- op-e2e/actions/l2_engine_api.go | 16 +- op-e2e/actions/l2_engine_test.go | 12 +- op-e2e/actions/l2_sequencer_test.go | 6 +- op-e2e/actions/l2_verifier_test.go | 8 +- op-e2e/actions/system_config_test.go | 14 +- op-e2e/actions/user_test.go | 2 +- op-e2e/bridge_test.go | 120 +++ op-e2e/e2eutils/setup.go | 4 +- op-e2e/e2eutils/setup_test.go | 4 +- op-e2e/geth.go | 4 +- op-e2e/migration_test.go | 3 +- op-e2e/setup.go | 74 +- op-e2e/system_test.go | 86 ++ op-exporter/Dockerfile | 2 +- op-node/Dockerfile | 2 +- op-node/cmd/batch_decoder/README.md | 11 +- op-node/cmd/batch_decoder/main.go | 41 + .../batch_decoder/reassemble/reassemble.go | 25 +- op-node/flags/flags.go | 40 +- op-node/node/api.go | 12 +- op-node/node/client.go | 49 ++ op-node/node/config.go | 5 +- op-node/node/node.go | 42 +- op-node/rollup/derive/batch_queue.go | 5 +- op-node/rollup/derive/channel_bank.go | 7 +- op-node/rollup/derive/channel_out.go | 89 ++- op-node/rollup/derive/channel_out_test.go | 83 ++ op-node/rollup/derive/engine_queue.go | 46 +- op-node/rollup/derive/engine_queue_test.go | 190 ++++- op-node/rollup/derive/l2block_util.go | 64 ++ op-node/rollup/derive/payloads_queue.go | 7 + op-node/rollup/derive/payloads_queue_test.go | 35 +- op-node/rollup/derive/pipeline.go | 13 + op-node/rollup/driver/driver.go | 6 +- op-node/rollup/driver/sequencer.go | 19 +- op-node/rollup/driver/state.go | 54 +- op-node/rollup/output_root.go | 33 +- op-node/rollup/sync/start.go | 13 +- op-node/rollup/types.go | 2 +- op-node/service.go | 29 +- op-node/sources/l1_client.go | 4 +- op-node/sources/l2_client.go | 4 +- op-node/sources/sync_client.go | 122 +++ op-node/sources/types.go | 64 +- op-node/sources/types_test.go | 35 + op-proposer/Dockerfile | 2 +- op-proposer/proposer/l2_output_submitter.go | 20 +- op-service/metrics/event.go | 58 ++ op-service/metrics/ref_metrics.go | 118 +++ op-service/txmgr/txmgr.go | 19 +- op-service/txmgr/txmgr_test.go | 78 ++ op-wheel/Dockerfile | 2 +- op-wheel/cheat/cheat.go | 30 +- packages/actor-tests/CHANGELOG.md | 10 + packages/actor-tests/package.json | 6 +- packages/atst/CHANGELOG.md | 15 + packages/atst/package.json | 2 +- packages/chain-mon/CHANGELOG.md | 9 + packages/chain-mon/package.json | 6 +- .../.depcheckrc | 3 +- packages/common-ts/CHANGELOG.md | 6 + packages/common-ts/package.json | 7 +- packages/common-ts/test/metrics.spec.ts | 4 +- packages/contracts-bedrock/.env.example | 4 + packages/contracts-bedrock/.gas-snapshot | 5 +- packages/contracts-bedrock/.gitignore | 1 + packages/contracts-bedrock/CHANGELOG.md | 7 + .../contracts/L1/OptimismPortal.sol | 2 +- .../contracts/deployment/SystemDictator.sol | 27 + .../contracts/test/CommonTest.t.sol | 150 ++-- .../contracts/test/Encoding.t.sol | 2 +- .../contracts/test/Hashing.t.sol | 2 +- .../contracts/test/L2ERC721Bridge.t.sol | 57 ++ .../deploy-config/devnetL1.json | 1 + .../final-migration-rehearsal.json | 1 + .../deploy-config/getting-started.json | 1 + .../deploy-config/goerli-forked.json | 1 + .../deploy-config/goerli.json | 1 + .../deploy-config/hardhat.json | 1 + .../deploy-config/internal-devnet.json | 1 + .../deploy/013-OptimismPortalImpl.ts | 16 +- .../deploy/020-SystemDictatorSteps-1.ts | 10 +- .../deploy/021-SystemDictatorSteps-2.ts | 10 +- .../goerli/L1CrossDomainMessengerProxy.json | 119 +++ .../goerli/L1StandardBridgeProxy.json | 178 +++++ packages/contracts-bedrock/hardhat.config.ts | 2 +- packages/contracts-bedrock/package.json | 6 +- .../scripts/differential-testing.ts | 229 ------ .../differential-testing.go | 318 ++++++++ .../scripts/differential-testing/utils.go | 129 +++ .../contracts-bedrock/src/deploy-config.ts | 9 + .../contracts-bedrock/src/deploy-utils.ts | 12 + packages/contracts-periphery/package.json | 2 +- packages/data-transport-layer/CHANGELOG.md | 7 + packages/data-transport-layer/package.json | 4 +- packages/fault-detector/CHANGELOG.md | 10 + packages/fault-detector/package.json | 6 +- packages/message-relayer/CHANGELOG.md | 9 + packages/message-relayer/package.json | 6 +- packages/replica-healthcheck/CHANGELOG.md | 7 + packages/replica-healthcheck/package.json | 4 +- packages/sdk/CHANGELOG.md | 9 + packages/sdk/package.json | 4 +- packages/sdk/src/utils/contracts.ts | 6 +- packages/two-step-monitor/.env.example | 11 - packages/two-step-monitor/.eslintrc.js | 3 - packages/two-step-monitor/.gitignore | 2 - packages/two-step-monitor/.lintstagedrc.yml | 2 - packages/two-step-monitor/.prettierrc.js | 3 - packages/two-step-monitor/LICENSE | 22 - packages/two-step-monitor/README.md | 62 -- packages/two-step-monitor/hardhat.config.ts | 13 - packages/two-step-monitor/package.json | 44 - packages/two-step-monitor/src/index.ts | 1 - packages/two-step-monitor/src/service.ts | 1 - packages/two-step-monitor/test/setup.ts | 10 - packages/two-step-monitor/tsconfig.json | 10 - patches/@changesets+cli+2.26.0.patch | 66 ++ specs/assets/components.svg | 2 +- specs/assets/propagation.svg | 2 +- specs/derivation.md | 20 +- specs/overview.md | 10 +- specs/withdrawals.md | 2 +- yarn.lock | 124 +-- 191 files changed, 6196 insertions(+), 1530 deletions(-) create mode 100644 .changeset/clever-jokes-notice.md create mode 100644 .changeset/cuddly-turkeys-burn.md delete mode 100644 .changeset/flat-adults-hope.md delete mode 100644 .changeset/fluffy-geese-yell.md delete mode 100644 .changeset/long-frogs-study.md delete mode 100644 .changeset/metal-apes-taste.md delete mode 100644 .changeset/metal-feet-give.md rename .changeset/{wicked-ads-pump.md => pink-chicken-hear.md} (50%) delete mode 100644 .changeset/poor-buses-hug.md delete mode 100644 .changeset/quiet-crews-leave.md create mode 100644 .changeset/sharp-islands-attend.md delete mode 100644 .changeset/sixty-days-explain.md delete mode 100644 .changeset/slimy-mangos-brake.md delete mode 100644 .changeset/three-apes-study.md create mode 100644 .changeset/tricky-donkeys-lick.md create mode 100644 docs/op-stack/src/docs/build/sdk.md create mode 100644 op-batcher/batcher/channel_builder_test.go create mode 100644 op-batcher/batcher/channel_config_test.go create mode 100644 op-batcher/metrics/metrics.go create mode 100644 op-batcher/metrics/noop.go create mode 100644 op-chain-ops/cmd/check-migration/main.go create mode 100644 op-chain-ops/util/state_iterator.go create mode 100644 op-chain-ops/util/state_iterator_test.go create mode 100644 op-e2e/actions/fork_test.go create mode 100644 op-e2e/bridge_test.go create mode 100644 op-node/rollup/derive/l2block_util.go create mode 100644 op-node/sources/sync_client.go create mode 100644 op-node/sources/types_test.go create mode 100644 op-service/metrics/event.go create mode 100644 op-service/metrics/ref_metrics.go rename packages/{two-step-monitor => common-ts}/.depcheckrc (87%) create mode 100644 packages/contracts-bedrock/deployments/goerli/L1CrossDomainMessengerProxy.json create mode 100644 packages/contracts-bedrock/deployments/goerli/L1StandardBridgeProxy.json delete mode 100644 packages/contracts-bedrock/scripts/differential-testing.ts create mode 100644 packages/contracts-bedrock/scripts/differential-testing/differential-testing.go create mode 100644 packages/contracts-bedrock/scripts/differential-testing/utils.go delete mode 100644 packages/two-step-monitor/.env.example delete mode 100644 packages/two-step-monitor/.eslintrc.js delete mode 100644 packages/two-step-monitor/.gitignore delete mode 100644 packages/two-step-monitor/.lintstagedrc.yml delete mode 100644 packages/two-step-monitor/.prettierrc.js delete mode 100644 packages/two-step-monitor/LICENSE delete mode 100644 packages/two-step-monitor/README.md delete mode 100644 packages/two-step-monitor/hardhat.config.ts delete mode 100644 packages/two-step-monitor/package.json delete mode 100644 packages/two-step-monitor/src/index.ts delete mode 100644 packages/two-step-monitor/src/service.ts delete mode 100644 packages/two-step-monitor/test/setup.ts delete mode 100644 packages/two-step-monitor/tsconfig.json create mode 100644 patches/@changesets+cli+2.26.0.patch diff --git a/.changeset/clever-jokes-notice.md b/.changeset/clever-jokes-notice.md new file mode 100644 index 0000000000000..c5d0e63fa3cd2 --- /dev/null +++ b/.changeset/clever-jokes-notice.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/contracts-bedrock': patch +--- + +Reduce the time that the system dictator deploy scripts wait before checking the chain state. diff --git a/.changeset/cuddly-turkeys-burn.md b/.changeset/cuddly-turkeys-burn.md new file mode 100644 index 0000000000000..011183959a043 --- /dev/null +++ b/.changeset/cuddly-turkeys-burn.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/sdk': patch +--- + +Have SDK automatically create Standard and ETH bridges when L1StandardBridge is provided. diff --git a/.changeset/flat-adults-hope.md b/.changeset/flat-adults-hope.md deleted file mode 100644 index fdf2e8342898a..0000000000000 --- a/.changeset/flat-adults-hope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': minor ---- - -Update readAttestations and prepareWriteAttestation to handle keys longer than 32 bytes diff --git a/.changeset/fluffy-geese-yell.md b/.changeset/fluffy-geese-yell.md deleted file mode 100644 index b88f17394596d..0000000000000 --- a/.changeset/fluffy-geese-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': minor ---- - -Remove broken allowFailures as option diff --git a/.changeset/long-frogs-study.md b/.changeset/long-frogs-study.md deleted file mode 100644 index e84fd35989e80..0000000000000 --- a/.changeset/long-frogs-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/common-ts': patch ---- - -Fix BaseServiceV2 configuration for caseCase options diff --git a/.changeset/metal-apes-taste.md b/.changeset/metal-apes-taste.md deleted file mode 100644 index 40bd1c54d7a31..0000000000000 --- a/.changeset/metal-apes-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': patch ---- - -Update docs diff --git a/.changeset/metal-feet-give.md b/.changeset/metal-feet-give.md deleted file mode 100644 index d3b64a12708ec..0000000000000 --- a/.changeset/metal-feet-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': minor ---- - -Move react api to @eth-optimism/atst/react so react isn't required to run the core sdk diff --git a/.changeset/wicked-ads-pump.md b/.changeset/pink-chicken-hear.md similarity index 50% rename from .changeset/wicked-ads-pump.md rename to .changeset/pink-chicken-hear.md index d0aab06d37bcf..052329fe14d96 100644 --- a/.changeset/wicked-ads-pump.md +++ b/.changeset/pink-chicken-hear.md @@ -2,4 +2,4 @@ '@eth-optimism/contracts-bedrock': patch --- -Print tenderly simulation links during deployment +Added a contsructor to the System Dictator diff --git a/.changeset/poor-buses-hug.md b/.changeset/poor-buses-hug.md deleted file mode 100644 index 66db062bbf533..0000000000000 --- a/.changeset/poor-buses-hug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/sdk': patch ---- - -Update migrated withdrawal gaslimit calculation diff --git a/.changeset/quiet-crews-leave.md b/.changeset/quiet-crews-leave.md deleted file mode 100644 index 9780f99ca98b8..0000000000000 --- a/.changeset/quiet-crews-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': minor ---- - -Fix main and module in atst package.json diff --git a/.changeset/sharp-islands-attend.md b/.changeset/sharp-islands-attend.md new file mode 100644 index 0000000000000..c2d981c087acc --- /dev/null +++ b/.changeset/sharp-islands-attend.md @@ -0,0 +1,6 @@ +--- +'@eth-optimism/batch-submitter-service': patch +--- + +fix flag name for MaxStateRootElements in batch-submitter +fix log package for proposer diff --git a/.changeset/sixty-days-explain.md b/.changeset/sixty-days-explain.md deleted file mode 100644 index f6f621878bfab..0000000000000 --- a/.changeset/sixty-days-explain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': patch ---- - -Fixed bug with atst not defaulting to currently connected chain diff --git a/.changeset/slimy-mangos-brake.md b/.changeset/slimy-mangos-brake.md deleted file mode 100644 index 2ec9d8e5c848c..0000000000000 --- a/.changeset/slimy-mangos-brake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/atst': minor ---- - -Deprecate parseAttestationBytes and createRawKey in favor for createKey, createValue diff --git a/.changeset/three-apes-study.md b/.changeset/three-apes-study.md deleted file mode 100644 index 0b6ff4bebe244..0000000000000 --- a/.changeset/three-apes-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@eth-optimism/fault-detector': patch ---- - -Fixes a bug that would cause the fault detector to error out if no outputs had been proposed yet. diff --git a/.changeset/tricky-donkeys-lick.md b/.changeset/tricky-donkeys-lick.md new file mode 100644 index 0000000000000..99e2591b59aa3 --- /dev/null +++ b/.changeset/tricky-donkeys-lick.md @@ -0,0 +1,9 @@ +--- +'@eth-optimism/chain-mon': patch +'@eth-optimism/data-transport-layer': patch +'@eth-optimism/fault-detector': patch +'@eth-optimism/message-relayer': patch +'@eth-optimism/replica-healthcheck': patch +--- + +Empty patch release to re-release packages that failed to be released by a bug in the release process. diff --git a/.circleci/config.yml b/.circleci/config.yml index d32fb5a54c231..36ebc6bee9c5f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -97,7 +97,6 @@ jobs: - "packages/migration-data/node_modules" - "packages/replica-healthcheck/node_modules" - "packages/sdk/node_modules" - - "packages/two-step-monitor/node_modules" - run: name: print forge version command: forge --version @@ -327,6 +326,8 @@ jobs: name: Restore Yarn Package Cache keys: - yarn-packages-v2-{{ checksum "yarn.lock" }} + - check-changed: + patterns: contracts-bedrock,hardhat-deploy-config - run: name: lint command: yarn lint:check @@ -364,6 +365,8 @@ jobs: name: Restore Yarn Package Cache keys: - yarn-packages-v2-{{ checksum "yarn.lock" }} + - check-changed: + patterns: contracts-bedrock,hardhat-deploy-config - run: name: validate spacers command: yarn validate-spacers @@ -515,6 +518,10 @@ jobs: patterns: packages # Note: The below needs to be manually configured whenever we # add a new package to CI. + - run: + name: Check common-ts + command: npx depcheck + working_directory: packages/common-ts - run: name: Check contracts command: npx depcheck @@ -539,10 +546,6 @@ jobs: name: Check integration-tests command: npx depcheck working_directory: integration-tests - - run: - name: Check two-step-monitor - command: npx depcheck - working_directory: packages/two-step-monitor go-lint: parameters: @@ -607,7 +610,7 @@ jobs: command: | # Note: We don't use circle CI test splits because we need to split by test name, not by package. There is an additional # constraint that gotestsum does not currently (nor likely will) accept files from different pacakges when building. - OP_TESTLOG_DISABLE_COLOR=true OP_E2E_DISABLE_PARALLEL=false OP_E2E_USE_HTTP=<> gotestsum \ + OP_TESTLOG_DISABLE_COLOR=true OP_E2E_DISABLE_PARALLEL=true OP_E2E_USE_HTTP=<> gotestsum \ --format=standard-verbose --junitfile=/tmp/test-results/<>_http_<>.xml \ -- -timeout=20m ./... working_directory: <> diff --git a/LICENSE b/LICENSE index e82529982686e..a19c072c21af7 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ (The MIT License) -Copyright 2020-2022 Optimism +Copyright 2020-2023 Optimism Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 3e01556fb21c5..12984d86916f9 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ If you want to build Optimism, check out the [Protocol Specs](./specs/). ## Community -General discussion happens most frequently on the [Optimism discord](https://discord.optimism.io). +General discussion happens most frequently on the [Optimism discord](https://discord-gateway.optimism.io). Governance discussion can also be found on the [Optimism Governance Forum](https://gov.optimism.io/). ## Contributing @@ -138,7 +138,7 @@ When merging commits to the `develop` branch you MUST include a changeset file i To add a changeset, run the command `yarn changeset` in the root of this monorepo. You will be presented with a small prompt to select the packages to be released, the scope of the release (major, minor, or patch), and the reason for the release. -Comments with in changeset files will be automatically included in the changelog of the package. +Comments within changeset files will be automatically included in the changelog of the package. ### Triggering Releases diff --git a/batch-submitter/config.go b/batch-submitter/config.go index 724e489497ba7..30ceccf3389d9 100644 --- a/batch-submitter/config.go +++ b/batch-submitter/config.go @@ -209,7 +209,7 @@ func NewConfig(ctx *cli.Context) (Config, error) { MaxL1TxSize: ctx.GlobalUint64(flags.MaxL1TxSizeFlag.Name), MaxPlaintextBatchSize: ctx.GlobalUint64(flags.MaxPlaintextBatchSizeFlag.Name), MinStateRootElements: ctx.GlobalUint64(flags.MinStateRootElementsFlag.Name), - MaxStateRootElements: ctx.GlobalUint64(flags.MinStateRootElementsFlag.Name), + MaxStateRootElements: ctx.GlobalUint64(flags.MaxStateRootElementsFlag.Name), MaxBatchSubmissionTime: ctx.GlobalDuration(flags.MaxBatchSubmissionTimeFlag.Name), PollInterval: ctx.GlobalDuration(flags.PollIntervalFlag.Name), NumConfirmations: ctx.GlobalUint64(flags.NumConfirmationsFlag.Name), diff --git a/batch-submitter/drivers/proposer/driver.go b/batch-submitter/drivers/proposer/driver.go index c096d88ad0d92..8ddedb62e0edd 100644 --- a/batch-submitter/drivers/proposer/driver.go +++ b/batch-submitter/drivers/proposer/driver.go @@ -13,13 +13,13 @@ import ( "github.com/ethereum-optimism/optimism/bss-core/metrics" "github.com/ethereum-optimism/optimism/bss-core/txmgr" l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient" - "github.com/ethereum-optimism/optimism/l2geth/log" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" ) // stateRootSize is the size in bytes of a state root. diff --git a/docs/op-stack/src/.vuepress/config.js b/docs/op-stack/src/.vuepress/config.js index 9d9c027fc9ba4..3d67a2ea98f3d 100644 --- a/docs/op-stack/src/.vuepress/config.js +++ b/docs/op-stack/src/.vuepress/config.js @@ -38,8 +38,8 @@ module.exports = { offset: -200, }, algolia: { - appId: '7Q6XITDI0Z', - apiKey: '9d55a31a04b210cd26f97deabd161705', + appId: 'O9WKE9RMCV', + apiKey: '00cf17cba30b374d08d7f7afead974be', indexName: 'optimism' }, nav: [ @@ -117,7 +117,7 @@ module.exports = { { title: "OP Stack", collapsable: false, - children: [ + children: [ '/', [ '/docs/understand/design-principles.md', @@ -126,7 +126,7 @@ module.exports = { '/docs/understand/landscape.md', '/docs/understand/explainer.md' ] - }, + }, { title: "Releases", collapsable: false, @@ -150,6 +150,7 @@ module.exports = { '/docs/build/getting-started.md', '/docs/build/conf.md', '/docs/build/explorer.md', + '/docs/build/sdk.md', { title: "OP Stack Hacks", collapsable: true, @@ -164,10 +165,10 @@ module.exports = { title: "Sample Hacks", children: [ "/docs/build/tutorials/add-attr.md", - "/docs/build/tutorials/new-precomp.md", + "/docs/build/tutorials/new-precomp.md", ] - } // End of tutorials - ], + } // End of tutorials + ], }, // End of OP Stack hacks ], }, // End of Building OP Stack Rollups @@ -185,7 +186,7 @@ module.exports = { '/docs/security/faq.md', '/docs/security/policy.md', ] - }, + }, ], // end of sidebar plugins: [ "@vuepress/pwa", diff --git a/docs/op-stack/src/.vuepress/enhanceApp.js b/docs/op-stack/src/.vuepress/enhanceApp.js index 6e6764caf7988..89bc2f0e60422 100644 --- a/docs/op-stack/src/.vuepress/enhanceApp.js +++ b/docs/op-stack/src/.vuepress/enhanceApp.js @@ -1,5 +1,19 @@ +import event from '@vuepress/plugin-pwa/lib/event' + export default ({ router }) => { + registerAutoReload(); + router.addRoutes([ { path: '/docs/', redirect: '/' }, ]) } + +// When new content is detected by the app, this will automatically +// refresh the page, so that users do not need to manually click +// the refresh button. For more details see: +// https://linear.app/optimism/issue/FE-1003/investigate-archive-issue-on-docs +const registerAutoReload = () => { + event.$on('sw-updated', e => e.skipWaiting().then(() => { + location.reload(true); + })) +} diff --git a/docs/op-stack/src/.vuepress/styles/index.styl b/docs/op-stack/src/.vuepress/styles/index.styl index b4491323149da..d24d368f3a2e9 100644 --- a/docs/op-stack/src/.vuepress/styles/index.styl +++ b/docs/op-stack/src/.vuepress/styles/index.styl @@ -19,13 +19,13 @@ aside.sidebar { p.sidebar-heading { color: #323A43 !important; font-family: 'Open Sans', sans-serif; - font-weight: 600; + font-weight: 600 !important; font-size: 14px !important; line-height: 24px !important; min-height: 36px; - margin-left: 32px; + margin-left: 20px; padding: 8px 16px !important; - width: calc(100% - 64px) !important; + width: calc(100% - 60px) !important; border-radius: 8px; } @@ -34,18 +34,20 @@ a.sidebar-link { font-size: 14px !important; line-height: 24px !important; min-height: 36px; - margin-left: 32px; + margin-top: 3px; + margin-left: 20px; padding: 8px 16px !important; - width: calc(100% - 64px) !important; + width: calc(100% - 60px) !important; border-radius: 8px; } -section.sidebar-group a.sidebar-link { - margin-left: 44px; - width: calc(100% - 64px) !important; +section.sidebar-group a.sidebar-link, +section.sidebar-group p.sidebar-heading.clickable { + margin-left: 32px; + width: calc(100% - 60px) !important; } -.sidebar-links:not(.sidebar-group-items) > li > a.sidebar-link { +.sidebar-links:not(.sidebar-group-items) > li > a.sidebar-link { font-weight: 600 !important; color: #323A43 !important; } diff --git a/docs/op-stack/src/.vuepress/theme/components/PageMeta.vue b/docs/op-stack/src/.vuepress/theme/components/PageMeta.vue index c70c35427d2b2..cfb021035661e 100644 --- a/docs/op-stack/src/.vuepress/theme/components/PageMeta.vue +++ b/docs/op-stack/src/.vuepress/theme/components/PageMeta.vue @@ -32,6 +32,11 @@ Discord community +
  • + + Get support for going live + +
  • diff --git a/docs/op-stack/src/assets/docs/understand/superchain-diag.png b/docs/op-stack/src/assets/docs/understand/superchain-diag.png index 02e639d5a99abd124e106f80099c97128c7c0f86..79ea11f1e34e0dd2137d2b706fb885cb04c11ffa 100644 GIT binary patch literal 65703 zcmeFaXIN8P*EXsMTM!il6_u(ql`1GT5VokO2-pyis-a2;A+!*%AyT5!YgANJdXY{7 zibMz`DAG%S&=V3NK!A|sET6pr_kMiOxz3O8$M;_T@M4*@)?8zba*unCIkFOY(b!{=6EnnLJ>b>)vH8J~TxXB;dTgI;6@z8u>|aCfs_Ny48(``?5el*hpBwCfSoU z-H9K!428*E?$L-p8aZ+=LZ)~i;^-sm!CN^JoDF<>FPH91+yJ*-?s2bZNcv+0tBJC$ z3MB=QRq^vxGbI>ZSKJBI5%+@kUhR!-zLs|E+5q^U&!ZdHp zsS-5y$SZ+6wEuP6-vbqrI52}9en~b(8qT4Xb?N)DcvuNZCAjCzFCO*d9+jNsuX5Ft zr7@@L>ARG{?1r~q%Psmfhkhj*aJ@AhN6*}r8ujVP+{+d##R46HpJm^eO*l&L8d_ua z+msG1Ow(0yVu$d5P4use&7~hFDBB<%ntR4K(|k)rJFRrNM$rGmf z+SL)T0=WBsP=nqxrSEgJY#9adz^N3L2cG92Dqa_}E+{&ET?LrssvlRbd#*s&dG$b+dB78W1J<;?d-&f>H!j@aCZFc<+Dr@EBY)RA9e6%j! zKMwxj;27WhFzJ8LOiEZ-psK9FuZOPF)2jn*ntl3@@z%XgO^{#NbA0shujjvyn>%@{ zgw3zbT;~V~)tWAH`q$32W%Fj*rUt`b58Qo0udXH~E^A$me;n9JNZ5&m1)Tf!z-c_( z$?Cp7yML|f?SLM)@GJe==60%pP>#m2zjpBOa6pfnMx=i|u$zp8-C#@8rC-}XnV(;o zPp?Mmmyr|x+qfHg`EA?{1StGJ7`Y;%HH#ZTUw1M*P6c^EJU2n8>f8Yi&I${(o!+^2HNS)x1qjRuTyMg9q&sq;X~E)DF?J(6 z)iPvSkbXM}O{%q8KhMpd{00cf7x&xU{|1pdHLh4|9~UU2Ak^PosQ6T)7xuES35*mu z>{0Mjz$ZTgZo6~3-C^yvGOk!W$2gp}g<)$M*vIsT{RB$W+qjcSLAPSo;L$F!E(=au zSk|_{R=Tk#gyDTUOtx{WBkIx~VYl``^Wy+PPE5LGRhGfJsS`YD{3xt&efE$?KKDG z*N$V<%F@-R!5hj3fYq^l^BN4*Y3{0+0T0ZVkwSdR#C&^nnU-|)?ZK>@Y0B&jH$Q;4u@rluD;V@koNUcD5B29m;~= z=3~uLp>(hpb&BZNHM6z0(s)p5B3-vIjjKG(+9}~r0u_N;O$u&>yYCgCqxp|PmJvv_ zpV(`v=<(pWaPj4-9&i$JL`s9zVmLFu+S4&=5mAGJL|k~uo2 zv8ML$Jf2ksp?1nW+-2&*ov$rtjWx9T#a5=pZzI#V+OcIV&c>fJF8Y009;=B!twcCI zz>dX~i@91mwUJS_x+l_hpblFZ>rWA94P)_nIPT_Pvvc3mjXwA5&&IEt@fF|$(g^i+ zx46Y)zt^jbkiu2va4w>yTfvqy*)K!LJw-EJp(GysJqvsSp84v8ikxQnt+IP9;;T=P zsS(*U(Gs?Or!UybIBZoFyv$2;keD0PhkI)s$B_r*1E0K7-s?W4&r?(o>N+!J7@ILo z4J{~fu6%U53zP=qgER5t+0qiYr$Vx}8pn`<8D5%wW{!UbCN>5T+|tzty-5AKhVfG7 ziWB33j`s^J^X&(pvDdlEaU}^m57ypIYslG+*6X}vqsCiCxDKCsRD8_`d{)u*4?2Fo zu8G77Nqz8`?a1?*q_SexTtUdlgFw4fNb&Jc?`vNv7QZ~xBk=+ZYsGv&wiTv6LhD)Z2jP6_W!_6Fh0s;7HLiPOD<+++->3K!V#J0nh?5L$? z6bo{v!%Y($KKnuedtJVFrORUX9Xh^p#g^T+>X1*fSK3Y$_idZV$vxkP8NA+`RkL_@ zovsW?a5yT9pTbxE9RlHJxKs=muOKUeOS9l)s`1vA6qnj2H_~<)(k`Xc%{6Y%oPsli zj5BwBOnUDQUW@xp;YR@am3>hfw#F)=FWQ0sBy(367;eNC_f>#-nCX36XP$KdT*bFP zZH13!t4$A?mMHB6_03pWPq;BAKpXRpI4EJKyWP}%o!PmW4(XIggPQ$UpArYE{F1Rn z3|KBKL$KEGB?C59pfo_BYHkST&b#y!?QT}LupVCTc&(tVh?)1x2?ea-LE32(F(mBZ zP|qI~A4$2lWVy+_&vw7kpsNRnr$)ca%rAHdbo z*T4@^)4|3{*x4a*HmmRCqyc-JG@xU+f_~>YD7pSnFGWuIC*F$j)_4B~jY0^AMf0#P zdkptJ>wG{E(`5C-evCr~(ZKP!OBpBHq0=MQ(9Y78@m`oZ89FAY-r^(+pdXr$5iooz z3AoZCDuO+Yui~t7VALTfU^`qTqZYRS4jk7g(Y?)6CkT?ghYP|bp&8AJ!#=rlHR9)! zbc5Gy%P&#r4k)2aZfIpq2|U}R$RU$TEIW6+YE-~JWp<1FSyr>{>M%$dHf?Eg+o6*R zWN@R7_fM6Oc^^Ya^7bgwp!P&p&}0t0ZOrLQq8|MLAE9D0?>)mOqYu7NG9U}55?u=f z5yO50@0^yF%$4opr7@m~U z!kl@+4!m{E7f$#S$^L$VinNv6JoiFjcqT@@zn8fp|4{dHqSCIGnS&^ZXXwp5rGb&wI1`ofLwbXr@yU)>hqGFv|vX=JrVzh z*Yi!U@{v-v)*4%=jLt!=E&Q4ut9T$bB)$cta@Sk8qSkW2dOZF(*ui6dDC_x6;iKzb z{PV&8$F1DS!bi)VuLsLIMgs@WUWbKMzJ6V{F7?j`ZI6MIzaPr^TfP55ogKi+^|vGa zUl=kXnm(be`qd&U)> zB?K%}qLf^3ylLDZ6aPT56I@Rf#4Epl&Br1(;Pan&=XU7v*s-T_;!X8QHkD0BVh^q- zynh~8)0I&4_zxgE4&y<|K76LkOc*)`S@ z`{%R?!9namPh45MQZ*$7O|6PEq0caMow&`k)JfttqIuVwzZ>zXR*78vgg-i2zRbP z0MoC1$ZR+$M}v`RdSEk}bt1XzGm$Z8N96YH?)#PGa%lLywp|KB++UXj!uX{Fy zpX0Ft1RIzoVq`m?gD4UvKaG&~smR+l9I58eOBchccc2e?x#->nSC zE_@zVD_FS3c@LX+wO8X`dJligD`l@6I5+w&;yGJ#7r!#%CI1!58B|@t>Vj)Cu?@Px zM*osbPmW`mm20o1#<3MxFN$AnTmIlc|F@3y?Cx;jL9`am;5;k@s9hrR4hfP>SYH=L zqLhqtyQ&QT%gw$w%N5*KLa(ZDb8Hm=#(juyviMa+T-hl6UJLMSK++%7eZaM#_DuMH zKmG+!^o-o=acW?%GUCm?E96IEfsahvo&G(Ux+9x=Vzq~#0axGMrWVQ)_N@6*5jOl^ zRsYv=5`8HiGmT$;H`C0IJ{SMDk^ZCpES z+0gnhb~k_MY0m}dzr5lv8I|_}RQLCE|1!yM>;B~Qzn%G~82=r+KSA1WsM-KUAw8*3fug+rV`Jt;zvy|zaf=srEw!fPvdmmEjDyx;S+FI+itcqN2sUh@D zlVJ3Lt})p@#vmO<;k8}*P*&xG%V&K5$On}*PB#y%?Y~l)bn`MJ^PNj3;|Mac*cv+E z*TJyt-KZvdQMiDOlfnKye`V5e(LGDeW~i?^-|MzGablUz$7|ihSBJJ%BSJKP-t-%{gTS7sVGMDfm~tNnc#P?11YbnDOIkX+?CYw zK=6!TgWwwUgdYWx_tEGBe=i-b@BQo}fMk-Ls#~-M>rch5Nxg$Xq&ynfPF~xiomshc1#WMLdYevMW7snQ zzv#=U-1RprO5iF^Kjql5SgvBIir{%h3x%hI-Gi17_*>GaOyDc&_gg9*r#>p5qR@3p zZj^xZIcP1%ncpM8{cuJ)7)^}T9yhdqX)=X(E|sJ$Ir_#v1OFd&&NJ47Au_)8SLuu zTrc2uqXAaTcEqi1r?%*S6X?DVLEkVvIi5RURd%n5nbOL`6S3LL;&K*x+yiF<_6a>X zp5}?FJYGrf_RJ=Wlo60z+966etWGu+j|IJItw~Z|z-d$ad6gL$X)i@v$K+6_7VHXA zcYeotZI`T;W+U)kg@Gq+$FMQm`;3J@$xbM)NY;7PymbI!_Zfocl0)YiDPWa(Hvh#~ zqfMz`kI>E_f`!*__X+SL#@>9bHj{mG)2SMQKjFX`= zw8PYFCPT%|lNwW=DWze(_gNxD(0LG0|Jayja7$9X0tvp7p`?1DTYdZS2+<49=8ms- z5-b;jEc!mSDZ?zfBCKQ&`tT?VS*ugWk{yP9G-a9R?Sd4`48&o#sVbG_o$haYUMPnS z1TyJF{<}9StYXhGD#g_+jbkgv5-k%44O~<)qpF2X*C>&o{NBqLBTA&H`}$(JttxDP zOCDi!qsd>KcqviAkk?IMI)R8K^09{aa88hMKXfgy+jHzs8^_*pEr&)2o1~s} z=>|i)#GxuMmTqXf^0nqIXPZNr`-iTUKNKt*z0@r8kzSgHhhExN`mcQD+uM#8h|OkC zS!yaWTTObtq8$t^;vo+ZF4gzaQxwuHN>WU%9dWOjobNPW&*y;`Zv;>2 zC#1wYY61*a(p}K^$VoF_zUT?#6vh7b6Ju#a_#4GO+Wd~4PlF!?-UaXOypi_Wi- zU6~19FN=5qb>!s>dWqx@Jfj?70qS^dYN-{esv7AK5&?UC6xqTK>IL5HIn?%OP`ed(X+znBB69Ax2P3c zjs_K9&2YCnLpN-b78Gu{^eS3@7`suI+Qrj`bsJ&Lj@1l9R%}B6SAHEMAeJ37e~c(5 zmIT4R|LVar6w%c^?-$*E1|D^(^Kh&syEe_EYRa8RMJ7$SD8VRnSU^iv7xS_DPFO*D z^=CJWv?4P5$wB(Q;I|p;wJbY0ka?Ja znyG(tox(lgoDNZ)X2wo)gL&nHT6`%qt<5)4XA8hlnhZWy!#A4!0y|~8TU$uQ3M3Q9 zFNtBA7#R(tO%+XvMT_O3&IAI|i}54xo|%#P96jZQ-996rJW=a_8N0q8o?^+K?S&%^d+h>%oDU|$#?VUPxyv+bP%Q_KW2 z^l(y2?Zi!N?x4es?_NDI4wxPK!vZZHU)eX(c;u2Yz#xAM29KDKJB&~RCPUfjtnP?| z0qlzFc)?gli-&%H-p)@#Npphx^1u)+cPVGv_X;%miFr19vsxwI?0y{+HAX9@^ui)o zm9IQEDq@|y$@MjZpLeY#4*gx`CvtoP>P>GrYO1+vCG{jTT&mOIYKV_*ex||^rAnd2$Sd|0Kkb#3Lu(M*5tKnPwwus66yTq_pDP6 zR^J-vy8+o~(SCja$9|Ldx%7Ssx2Y`eB2zB4e> z`}--Ic@jL?nxe!P$FELjy~_*X87_vX(5l>Aa_+S{*s_mN^QvsvD11>)?{qn7y~14A z!^@qFAH=2nWf46h2Tw9XRYvdt{Vwq|h^MA_uUE=)I^-HG+|otz`E?7{GcpOhv!IV} zW(AWl!6YTYu7tE)_@0}yYKWdU^zCwhbT3Hj`@rojqKg;whl&e2A(T9n6Y8p0Poe~x zP;eS!9X#{iiHvijou)f2j@6o4a*T%(D8OV*Bk)OvN9eeB_^#$NHES)9&HzR2U)o`} z^p}8uyi%W!l30n?1mHVffruz$%c;I9Jq?WXZVDb`9K4)9YJ;b(j@q=MKvV)o6J7xWxaL4#0)Dt~ajdm8 z#Wg-ymid02_mXYv4zw`TegF7-F>hxgQEz^7JjC0oN8qZvz_qb{BlJD_5|Cixm=;e< z^Y#v_Y;k+p5KA@56@BrQHc+%8^MbPVhw|tf=byXo*KeF3y30qopSIuR!g;&>X(5{- z1ZRs)w!I>Kkm*dMQTaV|gf-SG>t4x}*@_Td-r%VP(;{E;hF6(LH3hN1k}dF5mfV_% zVL9DG`7R<$k@%C9o>&%(xX@(_MxLJiE z6bOY)8QP{sxbk#2lbcw31YStbH=ATqp@sngzzR0dxtH&=z+%FtAnR%uNsLs$4E%^y z;*CkqoZhB|X2w_%+#@g(l^=u_Ny3<7@~UlcMtaHLl_{_^pO|mi-AOPe#w6;cURNnSfinaI%@k(Vw*0Er^hWe zGxwugt{d-AAMjz8Cir&3#LOz)gLE)!&U1aQ2DP=PQzfiO{K8PAY^H|!7TjMuDBALm zr8xwuq{&qykK_^`z@xYYn#Zu1=ETBWup~{wJxQ&-oD<9QJ@Nj-vF;-}jrsRloFL#= zMX_%SPEeYR8m~BJPjuh=y8IDWr9cm|D3+u*gtC^3^4+Qb(OkGSf5lw09Q4M%#zJX8 zrRAK|;=f~$!fRjK^|AL>X~r}6q&s6bGupi4_LqzWApAIQ^~a^P_$#WWwUdmf1d(#& z{x-i;r6>PhXgccyf#CkF=kP{NJo>UkNtCMG@uvXS{#^1;imc&gYutoiqODXfv7lmkJgxdi5rFdeaV1E}&`!!u%wvpKE`m|JzE7o5 z4p1^*(#);(Y{kjFopX*W(L!Cje$H!rZm4a!3M8qwn0!^Md0IK%Qd3F=jc{>;frPWTsqSf4v0Z5hK3`=DPn=6Pio&Mt7d5q)ApEI z9pg)YSr8P>2;Dzuv7IG{nN8qWnc_^9HqaqH7=FmZmz!hUeY#rhr#j((o<1}L2z^($ ztpASEvTNl^<2ud$scy6>d<)QD#TPbl^(T$4(Qi)fQhA{N?tk5_-}1HDtF&WloOENl z7q04+sebto^QMnueAgbxBWul|`N`eYc^f2U-K;@GWz;@5!X5|6nCGg4iyo}7h!D)R&&I@9<`9!Yj)rDNFZ^VOrM$gLHd#uVRhy2Sj zc27Bytb8J+Rb(tGz5TA&f%1bc6suE1CbX+LTh;z`F|dKng%scrwYIIoOoU{mGlYpYEy>a(11nbV%yE7K@oKOV8gdjTo4U6K;gp{fF7MNpi zI9m&9=FZc<8Y;Cq9Y@bL3R})}2P&aUJ4n?U;YOT;HD2W`+xQTQ+S@8fnS$~%et=Zq zmjq(WQ0^qzka6&tjvl;gpNt=j!W+A*H!avpD5okI zlDbN*-(fgHt2h|{vFzwY1}*vGvM4pc019khD;lPazmKWA@gtt}P1A_{0gEa5j72a$ z6xHFUga*axPH_R^IKnSc6e~+fgA0^=Ml4oM69U34^~YrI4JrnQ9|@(*MYLmBWu(kt ze>lYNc=)kxHH=63jC?Ak>Mi?u2etm#p!G^=WZiB!U%qe>o~U(@iZHbk0wW;rI}2_B z0e>t0z=4>$CP8a@42F3(_&C>>CtG-GXxdd27(tx{_hR&mzG*QQOR3wf3pEKXA+cD( z*6L}sZjyzn93?%b1JF>DF6-rtcHyUlyX`qCVoE~EAE@DIW?W9c|XA5z)R$W~N-kH1+16Uz?mnqsQ}DiBMG`NYcDoopmnxT|~D zX7&W|>8idxXMo0rxgH>>U0UoHgT0cd zry#;aYQ3rHY@pREV(nYr3o|qgKTjs1JglRZZZv-H(zYdWL7)z5Y`XU|`%S=zxSV=W z*mwwKXV}EIChq7d-=q^s_px!6ez)htIF?0K7nPhBgTIHJ_Fs|WY@84F+LMeY9u=j| zJgU2@i>MxP7hwRq>eMJ~eb(;pe3VOxr?YpSlORPQhQ<4Jp%GW@T4OKU$s?=LNG^{~ zE3q+!7gr)AzW2JpW-%=K!Q)nM&!M-<{dvn#fCI9uQ?VU8;hhrAtUj!~h$kwh3b&sD z%o>v{a{N~K>fOFGK=Z*d;O|8KD{Y*)WZ?|nnl-zcm;LRIo+SrfM|BYVZb_%Rv(~w^ zkF8Vj?hjo=&h47=D2_fB;Z?QstqWTLvxz5P++w*0kTh?udMS}}j*sHx1n%n%p_EBe ztNM0#FHTU9Y?rhws5?iW;L`ZR#0N*Wh4Ss?K`8LJF#xN%A~6msi`3#eb1WsbKjos@d*lmsWW>C9py6LSA8vkZew1C zPDOm9u(o1hE8#BI877w0)Qu)_KvqGNs><2C-TcZ0qqF0aTs9h-D~i(+;rhF}$H5ga z%Fb6W4`hVZsez%gOjKH(=!>X`gO7V6A$_Z2B9^D;C#-nfX!N1tV=ExVxFUEL;3a|m ze!fJ@BNBEtO~lqOyh~f@lZG$vMo4}C5UGDF;I1Q#qNeBb*?5~9&36LJ^q)&qd9#DF zai20E;Ge@H#c>>ctAJI${bdzJDYSVxF-!b>IpE+e4xtsqiiv?UxVK zojWHywn#;gU;dC$o9EU=PMM=a^r2I{*?n90?jutFL}IoinQjr>vyV98{UW}~+CZ<) z3Uf(rBNJ?7Bz|Y0lf2)mX-D!J1zIcE|0jw20d&YuG!y{RzHAh>1ffpML+aNtz#w$y=R1BJpdXPpsBu7^Cw?XTWGB*8;z-WNT2 zY?jQarAnOSkvfp%)^=DUuKUaADgL;MX9nEU$`(@|tv!d|$Z~UM?oy9Y)-q;!k%H4! z{00~~Rpk`Jfgr{=%>nvgP~dc+t_ciox{z1px8A36O;V!1=77PgpW0P6T2}46I0Rbm zl6-coH|PH4i+^>`)ZOBgN;V_D+5KOAkUs!yyRqJ#Krie3`d_x%n8$>1(r$?){JNW_ zp69GGUq1H4uN!f4c0C5ZGo5w$Cy>|>OIc$x&v?CG zX8NB_pFdOmEXU3R~mQ0^IELMLL+|MLAtbwhwIwswsjxbTmjp}*%I-obrVxuEgV z+81d5`SgA1fG&ortnRKG<>#3d9snlPp>g#7MiBgT`(44uc4AgZuF5}swxRIcgk9TFeoMXZ& zT#d}WU4C-boWhtJXtJgcB!N;{F#|rMPnx_jB5~;PKd99e9XepKK+U`*{^n!U)1fAjE z+?2(uYpR4BrAs>~JR*KM?3u}X&cPue22oMtP6&_TH9uzS4>~}~^N~;Yj3RzfaUqiU z`4MTRpNN3W+}lqB&U#oUFN2r2p=mPMbE*4V+!ykxrKj`vZg+YuMCgLLcox~|Vpe#q zFB$J~tMg|+Py*S05ZM>YQ?t6p>BMKOIhyUUg*sigV!>XGdJnDo5PB`$Ln>7F_Lb?mJ(0V1VxTLw+1mOcA1h+fs zRzLND*e;CISL?sQ0?SxwU!w|UsZVLRb8!~GIPDTX zt>574w$>(J3ZSe&iaF{Xn)HSEiI)Bp?X!(-qqVC(RWW1P<_v3wtnM=!Q zfDBk7=XgrZ>Qs~a`qXcA7S=tlzEN;KpVfxMfeyAB-@}JuJ#*)KA})EPk2ZGM5Ghj* z5#7V+l}CiZv)fGMl#Cl~6L7-k9^`M2lfB|M=-s|2F2CbSe`IyOdkHMplk6)Khm8=p zP<;k^)Zm4zV(xfEx9uk%QzTn>>=R+K2_6TP!&!QujC&XrX-B8`M$%UKNspqkA`~Bc zgnl!Ss;DmJ+m1Q=;dI&v)6_EnhI%^)HJ2#s>kcirpfq|4bV2y$;KKop(3}HN?akQN zrz>+nVzpBx$ea++0SwyP`;A=DKF2srmbcma=i(eAmq3dU!_I9S3)~fVE5*!S`CHk| zUE;Oy9l57PIUH*M0X@|nKa@F;JWwx8vzPr~N*!r##x^6m4y*69dDP5|(_!BluKqYu z2Qp5i>HFUipXo^r-7!aDP)sPP6}%djuX&-~pUy2LOm)Q*8^%TjjlVApg*ZWnYAdwU zHTa>T-FCs(KP~-X$hzZ5mK}@_O_P3F7^hFpo;z3ICcgAs2mSs?y-KB9{|<{q^T;L3 z@6|3khmMKp$5&4(pQ{UxN*BWjvPKJlw@R_v(YSa~g}lSVE-;1LY!g0fG!@r`AaJ z&PNI(1TpQWsOLRx*06rcw8;GxCJH&XYH|MTcXyR`LotmhqzP zhc;)RiJDI*zZh=`_v39?Ofyl$Yl*emCP&V&=IB;&S1X59Ndfb)*^1Qp5!m)i6cwQm zz)=Am5RY+sf02tCUg{G#H~E6shD1rTAo+r{4BrH|f659V4_?b@ZH zw2q1-<)W3nZGw{Oph>9jco~M`oS9xg_LX{7QvD2)dG>sQbP~;>Ar$=7riY&~FaISY zMlLvJ`Ov|=iL*+BX4Y!W+sozOCMroTvP;AQm8&#F5DRD7M)|7MZ6fV+Lg^4#QU7RF zbFb6t3g-(x8YZiU3%Tlj1EtpEO&l!~W>&ZGkJ-%15!Ee#*Q-U&E*%_S<%dd|Xp6;2 z%^)v?NFI6`@&^ZLFF&Gb@(;1u3W@@Lh5NQ)ooLIzw9XhsVDs(&$=fO(PG}ox%4~_G3h+u&iu$rqB6T3ypKYY0w=hIY|+sYqQg9+Zr<9< zr8LpgJ=9i^5$OVVrMX6-Q)U&A6(scsWY2&_bu};*M5V#ekV-fB#rM$&us_B@jSaHB zf~cAh94n$Gm$9SN3`XRT{4JfrJg8!fk3FrymFx)_bcw8fW7Z?dp&PHr7%JzRfhwKD zV!NR6cE(uzgR`XujGIV{Qj{s%8B%6b(7{zqEO3i_Clqq%hA;wz9SJlAU~F}%YChnI z@W>s!1zuZ+p+P%w^DrEA01kP#8uJkHSz|8_bTc@nY^G9W-kmcVRvV51IaWFSX&y2vc7+HBhY-MG(`{91^;)3IiZcmhv6SKR# zhmhcBseX4vW!av_)I^-Xdy5Xr1wz9)dahun#@Wb)gaLO!5~8~Pg^l+M!SOhK6w3SZ zQB3^@ShGZ(^xyDYJ+`ydIOEtH{lb4iFuX)|nOQU!B+3`^afYk=Dlt zynY;MVwb|JTl6|aMWd{Gl6thM-{pOaxXGswn$7n}VZJ&20ilqQh|MKZ!J)N1vje4^ zHxh_n9xk7Kicy`8VqI$uu9t0=144xe#bhnpSW$y#H7KHR!|~cpa$=YrutRNT0JNeO zbNv;hlmjJF*{L@YIAl!SoM^0o@qhy0+@49sG{>&fh{T$ac7NSGE z_nuq{`;x!ZK?}J9M!T_K>2{c)IX|pP!3v6Or)#M@EdU+WtjC?24s7+zD*6L}+6HT# zBDK74OW|E^57ErHrk8HLrCkG4>fqw2ROoBMq=kQSKXSLUcXQ{lz~Q5mP3h3j1aA%Z zp0oBC)}g0*1?L56vj_ZQUfPWB!eR(jXAhZ#Mmei!iY|I5`NRwA$BWYJ;c{8gC0{W< zrg?-nk0aS5mXen+`eW&!AaO_WD(PBYEo}6_*;q^|}tVjAdEoydmNNc0yf_44KcV*bD-3fw4`gDPh~yvU8R49qSdv$WADB+ zH_26cU~ZBg;N<6hM`!Ewosvh@tzM?(uHNN6xa3cqBXSEp%J@(jOBovjENrnYd8uzd z+fn+?9M%MdXS+Xip?#C<%n%>9P(xcK(2LLy=OmlrJv|$|LtGCB-##0^2Qia;L3x7S zQKZx?`}Jmu#@@=K3heaq)$vBuZE7p>dH`FpSIkWjXZS^|*?+64iB=q%E{^`kPed`hpT1O5Bf*$6#K!YPVF|ZZD}A5-cd9 zkTq0t3>w_OWDI!crg*yMs#Cb}oydeZu$V%X*KrfZNMGQvCXijB^icbDLPTIEWbhMb z1JSWuUQEqozj&Nz_9Kk95R!Y|RPRn@fliJ!FV84k&na`u#ARByK-k%IT#I( z&LLz)^Ykvp^L^rQUWoYBuH^D!rY*{7Iq)5sAkG|I($zt))=-IQXB_mUIz|+6ZVyL0OBWqz zfz}y%q1rAVxsctV#QbG9X9gFtn0d3o!i~{RA?N#m4(#03H7nA)FR^w=btd3A~Vmc_{PO30Gw7MM54_VfLt4BLF4;B?RvxQ((RmNGnT6wo2*1u zjg8aj21#=colM=QpWBV~Oti;FV;18FdPKLjT*8$OKk4W3Uv2xM^0Cy($5>`gHf{l% z+f}^4`>{c69pVu$m`&Tc%!@mXZBg~;<(g2KLUp(0>InkO6rrgvQC3@lg{oZZ z3#oVNoSmFpQiM7|FPWHWidm0wA+LOGfc7W{T8Y3^W&)~nMTkN|{D`Q&NDScP=bl9Y zg&b@02|thbdaR?+tG9dq90JL9&#soMA?=icF!WMfv>>S>P2p)Z^p$K^mUiFQYUgVw zE|Aii?$z0&^WW`kwuq^-XI~&$daDmu^&8QrNZ%f&EC~yL+(Hn8FTy+2Oa`BT6n5ss z?}tY~2<4v5$|cCJoas!$pLvGyINOCd-$QGg-olo zinQJP$0uems|{y95_eX1D~=CDw9@p$FD;Jh9zDu`Zm7a8aE?bdI#j}QP|H3hIpo{r z(zki~Avv<_^o|!nXM#T*s^-3U9O2E}kHeD?OKm1aJ9HIPja(eVxZB1n!i0eR=*54l*D zqKZ-utnhFBKC2(*uyytZzaL`CxKMcdW$HCK%B?$j7jr9e!j3<-7;(+*p`9v0m&Dn& zW}be6R@Ru2BU~~j6Y?vTURaz~1nDg9??Y%h5?^?KK?JrQ4FTZ4zgyPbM3CSY67}YA zr3mGT9pA@pszcKl{WUnqnXR7FwQ@`cUN5@>y6H0rsjtAQ2|J2195CiRt39rJ4X|Pe zV}RqRDfkdiT`SPC&7lZo?Oa9&`gNY`Y2Ee^Grsx>;=9;+!EP52PN%=gw_Ao^@!{#w zNJebI1On^3Q^(+i`y)$S^ao_SpS+2UyX$PYJu2Of3KUR;jWibGuiOIFiWg5(&nDU7gpd8PPYm8Cro(Ur^0)2pBR-cMD$`#6f2%Sd4{J%S$I z^IC-X6?tYlA;lOpa>V-=qO;~sur6DVHE@GoxJHOgO~QmcK1BaNOnXz)uO14h?v%|52{shT1+SP z>G%vg&*@{WSnWW+}U% zL^0hw1Bpdg93!e$9scfXv*Zz9cMrCZ&>xmX?BKyE$O7V?w|~8#+sXQfCg!q*OL0}~ zLFBVXr#eet_S_|SP$9b0h5LO{yl=o#h+h!d;N2PGIIY^u8Fh551(inFluQpe&T1sr zj>Wwf|6$+C=^X`7kX8<4tFkpH7Frx2LjNAQDf}Y<91#jNlFC}ih-$}@gTU920V2u% z5S(s@_boqHhBHNHvPXns_GkiYe-+wb;@})1x`duzt10JWS?98-d}pq__!=mJfx{|;)12Ud;5qbXaYUS zB}|HnQV$-4Gh3v5t*mfVyyUU6Sl*2Gr-b3v<@w|W%+eF*|B2IPAMbaHMR}JiU<=beb! zhCit**bB_-yb5NsOS;`egq&+gnSH2E$)He%9i}9f20mkc^^8>I+}2)_R{bD=@4HQ% zjw>1$I!SAv7gBrP!7)O)NPk#;&GU6_fmQk`6Wwhit<>ea$BzULbh9KRZi)*;cAMGG z_CXK<4`HWS#-Cn)IpiY7Q|9&%_BrRPmH~$*T0Htx*K^pi>A8>v9pMk7X0Ab?Gr5v% zxBiOm)PgJ5BHr{-Uo%EmKXtpMcCofK43LYM4*FK#Q~W>&gf5K)97k5TW4Vw`Qr2#G zQ-p!__ogzA0wbP>82vf#^rK>Fz1#AV^cY;AyOm7X%LlG|AKr5VCQ3aNeRrjrTCL7p z`R;2Vd)NE1$X2Sigt)DK3)MNRswhO5VuPCmDmIzu^o(>dt<9B3^CmQf!H0I!8Y5@i z89SxK&!ZnCw+a)9qcu1|@@yP?%T$$E@9lDP|0Jt#m^Sc_>ds2JDYWFYw}2?4d{y$r zP4T3ei4p2T7J)fv&LQXOB4tR3*VV5u(kB-bz(|&jl;T*Xj;o zhfLI>QKp4(_9SIo0~Z|##IpwOM~MmBVBO|kH6#ePeUaJYpDv2X%+STjpqk>Xy&{QU zV792>+o04|;hDFLfZL@kd}HcP9lBO$M#ofJx5hP=f3IxUZdU8(@!4MR=GntyjB-=g zUEv+Pz}Cg!)kq!wWWPH)d67=p=O)iimbqx$ergp8e30+0ERNUDr;+NNUdisYP?0o_ z#|2_NV~rn<7+Bq`(AKF(oiGZ)l)f?1UDnB?y!JEH>ldXcc|*HDR4?#0=*qLkF}?}1 z>_OvD+%2KVT&(RvGZ~U&L*%$7l@dj1h-IE%O%plv$B&hB+Ec?gngT;}5kLDebXm$* z_c<~_{9L|S?d1J)plfx?BH;n2^!rO6i%Oox5{((F>MhO3M*vhjw!e>I>S8q}o+Ajq zGGe)9*>mUa_eT09pQo_eLKHwoy{63Vy(_PH`wbgTVHh|u1Euvlb#&_ z0%B=1M+C6%z-Jj?V0Xc;muIm%8UcGK>YmPd>S=BaP&Z|w<|SCKBgNrIay1Wy8lr{$>O)$;)V2a z%SP1ZIL|9&x_>tQWQ-@hbjnhDS<8{iH?xHRsuI>%40qtEayIBcw|@q)m5W`ayf*dTpnOW@O~FS)^BkB*aqbJh)!a!c9Nd|9Z~dAQAEt)sw)QtGQA(CWJuty z*L$02shtypI_E1GZe4-&r8>r?oM@eLXc0Guypze(a5Y8(qWoj zU9-dxV80OULCFjnSl(pwyXUA_rG=JcPe0D{?Op8wT>{Lg2YMj2Xz}E8^K^Xcm20@; z1PkmqHae8O>PN*Q=1@1nD|}U0pNcGdxSn>0Rs&TeOsKb2c{A}#o@#8KlPIs)O6wDS zb=3f~btD#iZihg?m4{;$y8oe2 z9T)D+BrU7s>$d79(QZLvc>smfm)LmxwgVsrltE1MMm@&^vbc?(k^FOful6O=S9i<$)VW0Izx%c4Jlz^w zDdN6e==eQwQLGK*2;!d~)XXqxNyU(%B`LAhZb8*L`(rJskSC)Yq4!oE!CtE*{J%!=W!FoFP;Bj z1RhrAZhi*sdc0}WBF-cRT7o7+`yQQEyRVQO7^)<^dYTeuV)0|XmYq#Q*iEbc!o7jH zZY^z}#gR*d&yTyuJ&oz9rcdKIyFpPu?KyzLf^|1E=;5Lj4GUvCavmZ!|5BMu(z1L5 z60|d=A-(w5i&|Wg4-p#BF~12dn}~u+{T)LQ-=8fNP{s7ITPZsCn+JC)*pRQU8Efq7 zNLKT~P}fvG%CO+#bV!wAHSUy>pLy#i+mc0|nwvh|sg({CytLq>BqwDhRS%N&Mk27i zRzaeXRW<$Z!;4lv%2q%7Adw4UEdxRFP4%kIWckBOGRWxZ)Dta!{87uU%gA$)`!5=FIGyOS7w)P^0^&vMf$ zSwc~`_PfVaGqXzv%lKjhJ46D!ZK=Ltk|-8Lo=Yq<2mge0Hbk{Rl|41a2}=1GiVId` z;cVRC^CR}3A4(YBUcLVBy8xos)Po!5p_^~4K6TR?Tll}cX3W;)2a%+7J}6ob6KBT+ zGsHH_2p=?lD898y;ll1e`?A30VJ^?U4`!g*qBka6iLr zBm&DrA5j2;?|Jov{&)o6QGE5*ShTMy=UuZ`0`#|ce&X*BwM`laR&PNmPaU^#$6r!& zcO{gX+Eh%kWr^Lr*-Hg8(S)F>y+;ewaL{@}(jQlI_?59QamOP>=3??rd0LXrvF#W3QbQmqvNEkN$8Y8uJI(J*o zT&*xcT;LuVB+fc!Cb<mLdA@b6Me6|xDxc7Ola=YF@!zRu%kB7s_DQ(qU=nG%a5 zL&H87xb}jVMFpbUFi&&xk*h<6+Lbv@DJs@J=5aoeZsscyK9x<+tEvc|QO`hijjV zcJ-)QRpU<@^qZr&g0_9}uM-QOhK!@KR>G^cQ|rKC+l6vYyQ;It#?g>;d&s;HN-4;( zLdu<2d-$y9K4z}9o)8Nk3hi5br7eHa2IMm|g+cW+$s|pg3U>MdNbAFVO<72ao{54G z*mWj5jbdn3EC1tSFF$j4UDNNM2EU`zu%VyPz=*mkR z85Ugw=eivN`=f3q2WWDhIcT^_>*Bxp2)G?(2;N%whFHum4^(OZW{xygJGa;h;~NFX z;8u+!cn9efbY+x4pr+uXZGOTipcG+K#`@0{FZc>PiJYi-9XaqHcEl!WT4JbWndz{GIrn3DScB#1MM$KEfjcb2y zWVxNRWm~Q#*!dT=2bU zw2A$=B`TW8D0H}U-%QG1F~C_%zK-`1sHt)UICuVJKi@u0_?OA)19G*<$bb6F_BPyr zgaI9N@i&yAj<>iO+4m7DMb*R_KT|U$E!|?39w=vRSwq&%*LIm@T;ykR| zuxq?%|E0O&B`=C8ZnY-+j(D1p-YKq^c7MSVxTpa+N|d&2rYwylRx<(JM6ho)9%kka zj9fi-=rybL2a~nZV-k0+V5xTA2?WYj?b0N<`gZNSm>XOn@v@5C7@r61dSI~kJ^6b0 z(Unhjt(%jp0r5tIlh;OO>6yNiaD(fm)o`OSH9Jt~fyWrTji9X8KULTr zK|3=|OhX1fUd~_LTj)+#Bm7?|J~c$rpSzl98-s~2VZepgJNr&E_F8VYNz~Ar+{_eI zGc<7LF2MUce*`g^eQAbZYBzXi3~-aNB|e_MVp2~##7LC7Ue(?jYiDKTPpQ4HT{0@Q zoB3{I_kT(W594chdMX~=&e#R~9G*7$?Aaq|Jb zRKin&R4zo`W}{dJ)@GVP=00vO@oVfuJK)5r;&jtB`5kyvYb-Y&4LY#b{iuPvmu*lr;w&-aK*a$2(0=TA-(Cv{kWqU^P_x-uT$2Aqd z+}7|7Tb=yil+M+{ZdFqD11UA40UVuaIk;7{R0m+`fPhyjP+2aRmCct&ZAZeLYk(J- zW(YsB?(f`LvOd5i#g6SRsqvwLU01OxiB@`>^)rqa%yb zFpHYb1#g{9KS=D}W!d|M!*?`)h=FKO^|Ey(r2A4kv3*LQQdtd zPBs3B)y^IFm9MI_2O3Huow3Uh+F_ZC&vh=^6cg z;1vOs;q}zl?KwW&CP!A>)@`QCg~Rdu@RU%*CkU?e=gAMg5LO0nEbX_KHl9Y7ps0ec zJXrBtW}*-0UZtrRPse)A5Z5=vvtF-WIjSXis%>TzJk#+I>11 z8D%9`J>oO$pWVsP(?=d!nU)nF>A)D&Ed1iM0ABX1mU6-=lrlf-nL+SW!gt!lHs`Y< zc&WJiK@Ytb1KG7p5zn=Q@6Xwc;EFW7=BwQS`bmiAzn2d=xiT|dH`D0G%Y2Yl!3T(e zqFcr9O%Shq7$aj7zLK6~Udhlr{_*~jsV=rGM}&Xi8rJaPoi%5zX3La_1M8HI7H?$5 z<*)V9xFYEQer>;=1ed5*wkLN!;vj*OZb&%|`q}3VAlsO^<0}_X;^<&|!feC#N?zqf-s(?op^Q zzTqy=u3yY|V>z(4dUJF@LVsW8M^52RKy0T;ALgyV0M505jUmVbSr%lE93(2L-#t$f zPQSnq5KDhg;0Ik5EMxJy+iUN|`x%kF+NkmAFBL*;(t|z8jE0>+U@EyakklPqJ$a&v(#Tt|T|bRLiF?eenpX&zyUosyKaBLlJf zk=eh&sq=0MYZ=neB@`u33+#XEPZ40*>^2s*sdf&2Px z8$tWb;tu2GcI~s;_G;591{31HN-iyXFDK)-1<-T5j>y4zwo|Ble0sA0Ntyb;2j0_E z!A2w34@0~cZP}xGwY^3b)q&I;2EU(EAo1UjGbdj;om|ntT*8}-c{bk7;Z5Qy@*i3d zxjyDSclW6wcS00D14_#3u$5hFETEI3SMEiMp^JU7a2GjTCH(&8&+=xQ zjnuS~Fdz?w#lFjuZRCNsQ0)GbUjbwFB=46;qYmbOt-I)hAJ{-|V-9oUbxiUxWG`t6qjy)RXlzCL?Ng$-KnF|&AEd$_l3k$|mU zM$FaJZ*^7h1a0FL%0|m9N^zA}cWX*+CTsgzxQ{FanM-Jp%v=2OVeeLalKjk2Xm9j$ zm<;iC1l`@;^Z4v5QT1=-M>Edj&4$8o1qq|Y)#{Q!LxeuCpKA-p6w|z(nP|4jku`bl z^$s&#=nNt-B9qB0RY>fzp1DLQ8QS2-qxvd^Dx!#z-W}v9dmUE*cgxW?y$k90rbdW% zzBB7`>c(pYN(XCi+X&x|YQcLT7h?qifi25qFhDn)AVORJm5G2LRYc(}t0g@QWjQb8$@(r>hV@SO zU1`vwyZ_k=@TY31$(4#PW>+pq7HV-`lDo7hGk2kl*gYz`C7$G_m65O?TEBP0ePMjx z#T_1?u)V_7))+E=hOypdL@$e-66>`-Fj-S#mHd3C#(_^RW8-m~q0WK-%FktPOuFAq z(xESMsspKSO~rs{xfJQfNmYrBtdlt5@#8GZ&Ev&L-5Hwr878lw7)Q?hXddCEt| zX@Ne|*ucTA@yVJ!_j;9kan86koZ3?TFM^I^aRowZwMKWaahciJw&sI90wuFWzK7K40C!!hOtf%tK&@OyNO!ZZj<4W>lPR5Dh z^KjzucY*mn6xiP_wt+e+>TrzIW~40LX=YdsU|rF{Kah%-8#3m{(Zfe^#t)(%Rc2Lw zdBT&#tR|?F$ZppWZ!7G_kJMzuP1z<7ks*hM)1sFh3kWJS;x1 z3@GO^3ct7z3!v}xW=5GLsHMRJzZWjH9(2i1IM^3RYX~zd9QT?2$RmEu)G)py$#OUK z@PH21>|PhJ7;=2em7 zsg|2)5&tQHP=W6|c@8mvA#m@utF8PIpMpEE(?iY-G;|J3`oqiHQ?FJ~%1aD@s1FL^)F!BC&(?h3iYg7#S* zt6VDdMRKGWuZ=u8#Pc}l*K@r!1sYo$6lfJqDif~{cbx@RdgD!r1t?MR9Ih|}a*L)F zO7UQhrESUtPTvWNnh6%0kyd9rJ(`=D6B0{imX%oLcADpX%|0|zHAACYdj(Eof|~J3a2Ol0Zy%W8&+UwqJQfLUV-x&Fx+WkGbrHQIQse zt(_pvMlW4LLn^qHc zu)Fs3Qlik=x+lCPIW!~XtcEg|*qU$szgB$L72@c)z=*W6r-%qk(oN)Z3Z@t4qyg*N~$8?z?%>e&= zZ#Dr%6@U*5#=oDXEH(N8&ZC}5MMmImFLkT&86W#yi|JQC8^*QjL#iClOn{zg#QsJV zsw_VrXmh?o*3AGW7%Bj7>9PzVNApBoJ1c3!>}MK)FCc( zGTSRC>h~o5{75iVaUHo)<2+RT?wJ!CB;eHfC@R8W>)1f{e*^CT83LPLr~B7Sxc?)R zgi_vD2gy?X(Kr8k2idPe#oxI^M-qR-Z}*DUuZ;cw^dr27q+i;eNnifVS|ITfmgbI2 z{;`i9kpmVe*Kf(M@n2JlzaNC&1R7P5BhvTqe}3k#aOVZcJK_ycZw3F$QvN;d;s5_F zc%_p4^;N%jS-jBcQPO}@fF9v2b9B?k%pPvlLO#{`HMBM5_+TQe$I#!uhb23;0<&6Y(P zTsD4{?59x8=HD_W3X-|iQio|1`*XqwCIbdYGePlL85eVTCRAL}5kMN~6P4}dYJ?;;13y-S{Rqq*mS%`Y z1ZJnFY1IFwzzdA(-esipc%oQtr9hU`9VHW^47O$)Q-)@z6iveRZFQ+O!zuKWlq&)7aCe+&{^I?d-O#Pg2sxsZ4|d|I?bch2?@&vT3Tx1}@2zD4v$wEz zUO18XzU0v~>QT*h9I18+~Jl_H4G{?T{oEd^L&#rCm_$&~_JcYZIi$;Py|$tk5{ zkq{EmTi!geSRkevOQK6*t3n*jx=)_RQG0SH(X>-F*(e9f2dJ!h5OWTZRS$;i?(0*W zQi#Ijr8Qge7Gdu$Pv~HUD$tsOLU>*T)Bbrp(?j$7MKQ}1&7-eamFg^(CtPc)!jwQL zBySNyK(WTQ5?am?xwUD{1=k9;ki+HP7TG0&BR=G@pJ)ATQ5)pUJqe)(4O=pBVUv(!p+m+nYRzc2sNlVBOkr9`;4 z@xogPh#f@de3~_Ijth+6*NC*-g!TsIPvv~Xd+tL8IXoE9m%3i)nEo_P?cdDA5ZhrEEERa$j^;MGe-?{I7a%rZTJ{~lG4JGXM zZCIZ3B=1<|!rL^y*EX)B74`RO;-DAF!D0kA`H4&(EW0;wu2rNlXQcbyG>^I~05-*1 z$$d!xW;)+G3;%*N=v|p6ATyR$#E5t92ooX6g4FID`8+^PEc4jo^3ostaZStU~3E~}JeIEK` z^GtvKf+3B8}bNUHiLE^LN^j@&xEA_JNJ&#r?;3|MowCW>VY0 zU}W_gP?%=p8yc4n^;a9~4}IMi4rqBO>Nf7V1LcytGe%N(dZjC;4~0iIN^M*9R5#xa z4PVJ*1bB;aG?V@a0}OR!;a;apC_t3T(z#IdTVR;@VK`Q+V1%5Sfm!TByZ0=cq|A-#MdZtq9IYMp6}39zf#xSM<(+GAInS=97{J zf)ZRQmFEICxWWJ^;?0b=VjyndblQ(+->wJL8D+-Li4m_$0nerRXt4=Drc&R)z5MaE zz&EiNKwA)e)HLcW-~ls92(C0wcxpx zHAW;hu@-l!+oJNsar)Emg11>f5Q&Nh$5t(1f$d#z<*&b!Ky;Agt6@ncAp){e(|ZUS z%2E}0fv!lrMb}w+HG9+z`?EKx3>aWR{m(m;trLLZPDuU2sjG#+8hTu0M)MwRa*E#_ z(8^2$n>)HFUB>&t9lcGci*l{=u$xkOdg-L*2`FU;6D+7Cy5q7nXnb((fAxh+)Et5B zlmZ-#Z1C@U6|4`S)fWJ4>I~Gs3lU%rmy4OOe&t-bVzqyJb*`RB+olvaai|u!GHXeS zbDvmv%^huviBG>#9*#-Yu+xN#3d4yl3jUlQPJ+{V(r$tD1)lUpj|w?*xaFoEA7qJn z<;3*nR@_4E{yjm zJk0%6YAN&dNX#5jJ&9A`wuQAN8N!7uxwHz_0E58wmOYmG&4v~~H%i!dK{!z=?ex`5 zS8}9r#fMcYXMl0jv>tQxNP!{C+4R^Uqrku8wXNP>Ko3J*F^A@%cC9vIyvr8CsbWt% zkbE9rVH6htf8Yy<(W;^^0HX^-dA9L`_Z|mV3RH-NBFw=ioBLi-puA5i)%yixw*Y3C z?&jjuT9@ywDNjqUxx3UeLIRk3_FPmabTb82n#Rebk7?aZ1FFVS#z?Y=D3()WFvt90 z85ftq+|%d*K+LVEi!ykEIDptu(DS`P@fx$@=+4+FZ5tZ}_7``mW_CmD2pq&+?85Ls zDL7Hp6nR16VE4mo3pyoBx{ic?iypbj6Mf zScl6zjpvz2pseaTcO8;|vLyNGj8f$R`mT>c*T9v`e#l7r_NUN|y^rGoRrmbAe)hv3 zsE~3PdJ-3gkbQBX6TsZS)tWN$gBx~_HjVCf=EntBme9Z)>%m%r*xz!X%SjxPGKapX zhO8~&yOCCq;v*Z zUK(mQ5XvB)my>uXhy`HA%*U1auLH`Qcx=#(pX!`W7Kt)lA~2N5vd^C1YL?Vk66RFKcsLNy!%=k#ptZ;9$Q=Xuz=rWvtMO2k-T)hxF#75SxJKmOn0`f4 zpw^*XVU5J4mF87dyKC+{J4%P`Z(>_3f8=PJx!L4JBU%A8XqDCU^y2`5`q${QUx5P~ zEIO$_3gXFLQas6E5&?*C>Zcj6WR5@xdHK?K-#fRsPAcxdn6#UzSBw~Z#eug-2g)(p z%5GkojD%9k^TLVF)X)JDIDWv*H5%IV!X+5A1~obD?Oms#DkXJhlVUUIfZ#oVCK8CJ zt+=1K?QiEu^`C#*HF!u+m5%OK@!w7A`aI^#gRqVSpzwhWJvtTIWU2Kbl1Ee-fz@gh zivS$gCMONqE0UD=0X?wFhBdpIlu3BsFBc5oRazocL4@g1RGM6N`E~Y_Ye8<&&igik9a*JfN)V{KI=oKv#9H7*=Gov0|H>vkK zG>+muqZ-@*awgr`21uAjllT+Ts4@ zw=zpYximL2$9)JEF|$BiVYQ1_jaZP;1up>p0D@!}&}E1i1-6}(_W%|J|I4Dd0EgqE z@JbO{%>#^UKJkV*$+#9G5_p198HB~jFBJ#vDGS(umTqphn)L@CcW{X2tc%o6kGY?O zE3uIOZ!H|ikh^*;=dz$vk8&367aUj#SZK&^aKd2ZLD>V$?sUg=a<;16@!SbC>W zhG=e4UEmaZVg?sgK;|oJ-$+px07lZ^+En4azam=4Hz4vQC73AlRN)0Tl4y*(yd_k= zy0ahjDxD87TBEV@``}17L3zRmu}@RJAX#%6s5QLKkk$y~yGVau1#7+f;>|m#8A#(| zzs2YYK^9=)a#2x_+P}40TCB_`F>0mC3a9t`E4Oh;-nK;AVF3}7gPh=?0sjDucSWGb zU2Ztu`3Q7a1Z?iO>YeMEOJH+hF~(Jzcs*QG-}@zK!-_Qtt(JZ01{2@Ea!VSYoWp&aus;PtM~vHH+2M7 zlE*b2Q^=dkg24>M`tv>=I-I3RQ|@U648- zz8eKcYH$v(zk{X>??vA)wWrV312^_a;`mb$BS*Ue=8<>b<5*A%E-nkjoDfR@qAh26 zJQslvSul^iqr(hNJF~AEYnU)xJFM=gqY`=wEHX*j0D|tKUQ{H0pNz(&oB16~2pBW( z_eGQywo?C} zCEefFCj`gOGZ`s@r#H>5K2nE^+5#8Eu~k>jaQ!H*>T?`f!G!j7B|V#6(t+c{lW@`d zK&b0Nh`lu61GRF^XL`UuJr!h2%MZY2T@iW9h2A9jj|#UwX9NU<`WKv+NOs`owqfk! zkce@)HPbewn#~|BVNG_sq7K&1IL#RLi14OW+4y#f`a_*i!IAP)BTQY+lW<^cR!4=2 ziPGoHsBo@3Y|N;3mXrBVWQBih6lRJhLDUjdJh$FnG<9jf4r4N4Gv&jN)xUS=Z3_*6uy8@ zT=qd$)covGhoRcHDl5&$bg6oQSng-=>o+?w@~i=19W3`7lzSXs90SSblo9-)(NAwK z>Eqb#G770!&DKTl)y@9-9Fzuz{;y5?$tn;raa{vXB)$>!Fef+%1yZMj!( zBs2E86C&-u)Y!?@D+aQ%rZQvKAt}Mz?c7X3j6qQ&G@fceAl)Q^gc&`kG_PxI9tGQ& z474g|&me_g`~CTpu)kvokK@nxJRD95GjzvRZxF+%bI$mffLctw^aa10o+(Q|-_){H zBZj0niSJy_;0jaYtA1Jd)|!l_(VE-Sw7@K2z2N_SL`F(&7yB6#UX(MB`a9g%yn0mp zI9tPsTO~6wB8=q{crr@!BY+%_b4%@7cfz?<$G#D zwlV$SXf?Ao&*8ihxj#??#WXz6AJRY#751JJIh9C{noi`=Jtu_j=uR1h7CCMQJ7=w{C1cpRe2vx_svjTvr*fZ>V=RLv=seZ(E{1xH4^A z6Z^lUatr~i*pY0W+qX~r>(aN_271Wg0lS-k`Q6j}6Y(5l=DQx*=uZ zL%7?{dqyWy7qQP+?0ohDQ7Z0q(Zk@%_mBAS?=GdhPX!;Rxqp2GnFX^>pz)+w!;^Bq zCt>bG45KLp+6;=XuSmR|#xH@d4%h~jMmgemy-40i*75z;$xo>9jQwqfAeo_^*Dj%J z>jAd=vp>7XSE?o%tPZ>1Xw@z<9?I`T57t!Q_jThv7j#2hYt9sF7Y>p}=Yf{Jjjj|J zM=&LG;_b5{Fvlhe^894$KpxJH)z7=8QqH+(&QgD`QH#;X`C98lQJyKTRC9X88<}cW zD#S*MibWkcMM`?y3SJtRtfRyt{jAK2zs7_(J=obPJS>}RFs`c>E12yhZD3L zAsliF9OakB1Zlh&C@fR!CbID0TMhs`X`R*AM;hxUe9dKB1X(BVnMe#W_H9Pfgh9HY z_NknSRNQ^6&H7sxf{Ctj)+3O+m49A`chK;=7i`rh2AC?HlRHU7Gy1|u^N=_T;<{wL z)vn;eJddYs7pNxto<*4!M%8QHt{ZG@%BW0p`;;qFQTd)yQViUfL1sGGnofG=ZI%ve zT*2d=HB_!2N^!=5C=$GtM@`WFFw-paX!Gu@67EyA~BjD7pg%9X8#*V9g1tf zOhN&q>$~5 zs{W)DvB{Eftmst{&Q;N4pWwd8B7c-kP_X<9OE;lYY-3~EVJ*nduho4ID4C*ba||B};uiunH#7&cduA10 z0t=AJDg-~#y9WXFoZ zp*j+aDPFRYPi&L8c=P#5dD5bzub~slL9wz*NZ}=06xE4IvLLZ@8XGq=-yWex4(@4b z9LWI)=Yk9`U9K9bPxo#NAniORTm{#(UY)gbS&T%0B(;F+ylGT0`_B7N!%9RDMmS~m*(DwIe4=T6!~0A2RRTh0F5Noxj5Eq8Ajp8sRDWS7)Qd#E^Y<&Sx; zK2FdOa{T?3KhCKq2zUhIRuR*m>px83S2GjFf3=qUogE|P;?P7=CvAOf*`GUU|0*c{ zNmA=pU@Hy-@Fz@FBPu?5*0p( z{VvBJ#dwF*N&C;%{Fuw_~%`Sszo?JR$omKPwvIg54Z3$&uQ8A-kc|P(EI-xa1uL=#yjU zkitL~9ob{!7sv)USDN?=>?A`4=un}8Dkl+{ZIyEKx#VPbInfX_yHd8b?We3o3KqcJ zb<@mj1DUX_W*d;D2w--!k>4D(*!$NVD@7X+Ro2m6{&#QyQ`6VS`)@eExzcRO!z;d_ z3x@Pt)%T@*rv|#e_OBgh=-3Qll}@GWO9t34fL-sV-PG8Ma$E}(x49O1L!LG z%--LE#{V^7%&?J0Q35q1Dj~U}?<$&iLOA{E$mFiy_ZO*sr^(6+Qx3n zL($n2!jCg{*SU8%l)R-Lc51QcqrVTtDiysrwHdOs$Xxc$IFn|t@fA~KD%bP6wCLS`#KFCfb zQCk8mS(a~hrP?Vat9i3NK$vfa*bU%{z!hqW)TIh+dyWNK2>|B{h^4&khamf9@$(nK zoi7bB8P2U#6_qR>2ePA65au}H`L)T35oFwj5`uc>Np#NZwy7ndAkZsk&8kq?QBGaz zq-0{t%@$?wc;P)_WTL1LKP;&aB+DCjMwVlRp_Nx{|D_~swxmJr@aofj@9O*tJHS$y zquR*T&vZdDyC@J}U)__BZO<*4Nf>>IaPFn^;Cnsd&3tUHQF#5Stgt{&%Yn6CT9Xan zR|PQ4s>^S7TKe%*$>S;Jbe*)t>1H1^9B%18^*>;LI=OdPcD&3`719sIJ=}ZdP`*V` z*JN21C}CUy9cDwlJhNB5SC`IBdobfV^iBxPiO!1Au8;F_m#cfDZ&2?vu}G0KmpovI z7G9)?a&9I)(2{xfj=a~X<-y_MW*3jqyNTUFxk31G+mO{Bnk`6NxS$4tQ~tv#275hc z3Y|u=UYW)-)99SWlPPDFvXRa@FCJ4k;+U zzvXiLrh7S(w~i&yO@{zZQ+N@&r_09N^l|Mr6yFJC`+}1 zXTFt3+@^)JYk{n(IiC2*k8)ibK}!$DYp)+ zm!l^rOdCoOcepskahVLVL@+($begbn zN$3%R2V%#@+X3e+iL*YAwugpe?Dx;ppk<+7B2DdC;)bq&2u18c!&^5~Zqq?Lsa7n^ zlFm&5z8ZwGV`ART>^NE-`c7Um`PMaiu0|&C?kN~*qf(C=qD5)ISdu6F`&+Oswh_|0 zBzPJ;ob(?&3v2MEgp0=J6gpji^3&z6iQZy_Uu2;whvlk(;zmmp7Umd&uoP>bSFx3% zcjoAM4g~O=g9+Zl1SC1a8No{VID{mC-4xIrM33gBWiX=qy#RNx0QcvgkD{fhBp6FN znEy2`Dp>5skRCn6h2ck4smrVHAq^|wRM6JPI&pvrI8NO9$baqEqn_vlSGLc5GNinJ zIa5fi-T&&ZZ*^ag5lmj3;eq!sf(c+aqbw;;q=5;1*h?C?3??AO&YO4p_oI+t2c92y z;OOB&mp2QpG5(gI57+@8qsfPeL9hc$siK=4q831cpO(zcjsXdNk=Jw{DHn1QUp~v} zLC?ahq78I)hb(i)gF{!Pml7|07WCT>xEt+bI4JbqCJ5W`Rlh zlJt;_P|3|2nwAA`@R&PaouYCjQyU<{od`l%ID`neObjOKkkt&``8r3CBWs`|X@$Egqb(@}o6aE>Y6IJ8!8n z-sPITPVzw!U9Ny|6azt~{Oc7z3~$fmf%Q%Kmi_4i>&MhHa8XT?YAqU@9Yvofoeg_Y z9?`?S@xyXKadS3)`+N69uRydWv=BrJj(GD-XTPR{8B^cyr)Q8RfennijT-yyZ!DMQOBo-1>j z4lRWf7AapXphIz;b(HXYgwPk$Xj#22uxzG>ZIk*Xn=3#z8almQphs=W!)adO74pLF zVeVnRL)Y`RgO&$Fw~v!k3i1L2)x5}D^8oQ4I_#Y+8g@fGS!rjIp`CtoRhO>$V;ebx zqX7QK-BBu5r1TH+YrS{qD)0ULc&(vR;GFq_e8;#Q_N{P4$7%u;&A|X?3&UbcU4|&{ zA6TSHhYXnz0(*dAYY-(Fff6exMyNgIEP4GrZB*y!dlYZOC<5m5D3WRnreET)RUSUP zmB-L;IC@mD7qVBc+2UNC9vz9FnC`1q9+feVjac4bMJJF|P>G;Be&ca~f zR!;Vwj}=R~rZO#~v@7-pO-18vOeu6Y5_3_^ke3!&QF@iCRRDIL{%$+5UVuU0ZuM02 zi_}Cj)!Hj1ZPe&cZiKwBZN-ni>r|wbZV^Qv)yy7pEmPR^$xxu1`GOm2wgVo9!Db&L zhNMSc9CVkS6w^2CpObtBI&iM%iq+LBz9h(sG_M%TyWTp84UC6i{)oaClgB|5) zjOW5Yf(zRY{DT4T2yf8PDy-}N#tX~!hs0Ugq+Pw&ta4tqTcaqe3nQxs*Q3 z(W>on5(3M;4qtA)bXC8}^7=zS592~$`zKT`0Cxqz#shvS4&7J;KbyV=-@yCO8?fMp ziwwXlPmg^Oer)FWUA!ds1yw@HmwrD2{n%U>s_JwVCGh^?81>4}C(zmer2KNc{B-A8ViO)eJp+NA76fLn z(MyOX1fc|#h`;D2HMKNSv4?zU2LCV#mC)ec;NrjaJhZiLZg-OmITP`L7A--J6YhK@ z0Gmw%ph+4zAA(3lBzS9@|D*;qz2k$w8 zt{HkZSZLJ^VE5M$W&?8c6wke>FA3PPB%5)1Ku4>nC-{p2{fhcZ>rGA$z~C7Qn8`Va zCk~0MX=R`!0S8#}jB>g!ho~sTZ*x(te0G0lE#5St?F8^S;Z(B%;p~Cix1J*IWEWGz zP-@N=!3bfnOlDeUjvfJWT#+%>;24mr7EWwXl$nDJ-3YXv&7Zj!0YJF*L`(G7Lc)75 zh1~15yZ~k2C5;yuXY#bN=GLc2-`})d!U;z_Iu85HgTPMVesV$q>05p?GVKfeiAJWH z{PfvrP9QmNAi0p`+h-}k?Wl|2i@4%{Flw0bPb>!=`YqA}pKAv34IdP_-+Ck6dvT2c zm40jwb{-rAMsZFG=q@K1P&w5XuB(|D?JZakdJdc@U6Su9b8upYP6KB3?naf>P`x*S zb3v^8rB$xG7?q=oyTa$38)K`?btjNBPGD#K!AVeLFSwGa1N1GU^zCuT4)6@E`(~BY zxi&uqUXW?Bj65;J z@j)HX(Cr%8pwn95(4%N|m7qpcrL3{<6Nf^;c>qDMk7liwIY$ozdK80l07xKmzzYnz z*pVV0-k~S^{M-?8g{BMzKdCA_xbH;liqUVYlm|B5$n*mR+6Q>vnb9MCv}i+!W%I-x znnfUg8;;-x`NZ_>7qHoshOAh{)VcS2MXxE&T$?_9ri&1phUp0kfAR@LiJ(62oFeV=!GJIgmpy7=jX3J z?Qqba_YF5rV-#nRa?Ty~#}!D)QlETAMxt@QG1X85uaN%Y`e_bLKg-2N5gB6-=0yc| zm`e0HuqvqxJ=Gs}hXeWN8jrB?()Igq4PT zPXvW&W_Ej@yQn&Rj1(zXE;lDg_(gJ&LhU8GZ5(sGj99i6Zwyasf&$_qFQVf4s@k@b)D8S#q4Kqk_6$VhX_N9kpT0SGh!E zz0aM!I(&p2q`2UstyTA!ATYaLrRo)NR2{N`_r8E#5zJ?RiMX_YVv}iV}Q=Pfb1VBf< z(P?LLv z_;nlRw?{8wCZ*ozsEvoo$tm-A!0=_7GC)S4GsCL88d$koLL(J!8G@`0&eK;6Y)!}@ zb`D{m`xX2)ZO@Je@Zt(|hWq-Nfjt-b)^`Mb8=NmOE_YH`5XBZ7fXrkA?otV;$3amx zFD0UZuF_}C-V2Yd(xg>Vrg~Ww4jY_NLns+sAQ*`(&Ab7a*gjTq*RkBGH@n99g(VkW z_G;A6bSfB1;Yf}wyd(99RCrg zqV1wv3%sqFLHZzr4B~Ov@sugy|Gwu zv(`+;X2^f2$fk9&Rba`RuDofKa~-_W6E@C=%? z1m6U$vsq?tLxGNk&Q*1*bSMyhrON`3V|Zu39vG!De{kO4Vyq%~XRIAbMo4q$x%C_I5-!0X75KPx&(t8zS|G?48JfEuIpxVha=-JcC z_ahU}-jhr#j|w)w*xF4IxKBfT(**zV&1sMmdQOYF6G{dh24)aTN*aO9hdEAMu0K*C zN`*d8-kBM+{WFZ&>(b-g-njTCODJ^?Qz99%;)7*~4Hv-oF&}!JRzK38R+kqRv_1#y z^Z*Pv(2$1=z^A;TL#&vUvNf`EWttLp8`biU6j1DN@Zemp*Ivpi2RD$+>zI{b!E~nZ zN;T|EXpYv255AED2$NPc(j`)!m zKo70vwWeJzU#hNCE(J~Jw+l~<@I^_FoWzt72Ix>o;pa;k!ObsS_O`5vlJv1_%SYrZ zRU2?U<@W%thKss^QP=R~|D(Mx|A(^e->+`>t+^95ku6QhmL(xlVTw|=M)sx1Hd$tp zioz^ylr2g25JJ{rm{FD?%wVi#9g6H3`!LL6`CMA==XrfzpXdAi2cG%ydd=&)&hvdP z$MHUn_jR50r`veM_7#c7CVtK5#a3O~<8D}k5#2CyWpthd^K|p<;gk zCei4@{gz1p$(M#bD}WaJ`8)3hCeqfj;2IYLs-ohy?IErA=Ds96Yu(Z)j^(5Qtqrd! z_nlVNE;7;@EOM$FBls^(H0?h}lZzPw-y64$I3?@Tdvku@y;;VAB=wQvuLorh4ve4D z@Two}3wXVvd);O$qTQ*bfBkQ*^&fHctT)fbMQ`;h|1U+AFBCxaH|5a)H~mld`5p=M z1Dl@%klz2G@BUlX{?Ehzml6z+nnTA|8xj-$(;onW@!zKW9|8VvRQ|7N_ur`e|D#fa z-}i$?ERK+8W@shRv-mRJP43}^9(!*$z`1Bn!EUf;=-qki7KTb0hu#E!2Hu`&Mr>}Z zZ^5(vC%72sUrvr`M2+xV;akrOjk|wcJe$NLwUe|+5vNUcW@n6i8>|W}`Gc3=6$V|u zo7C-`C-boGsQ`=&d=C(-6}U(i_OkQL?gOL?{A5btO;6*|@mb&#oCiz(08S@;e&E#0 zWKADu&ynw+GH3!=R^61!nLRdwJ2nRMx!;VGt4iexhk1Y6Lf02~dUlh7PVD*F|DqZm z2Q;&LCdS&KiMaq|4G9j^z%|z5OmV}(=GJ!dP0*KeUoXzNn(S0eKhjM)-?73EDH8{O z{SuJ+AKubZ(IYA0XK7bbSs2mj&7D~96|Mdw5z@aMEHfS($s=3x=#P!>AwTF+>8ZQl zP1D}bTxmCe1nxhZ|~q0IM@Pc0Xk_#04tlxhOSRFhIFiHfYxSiK$D&; zN2tM-%M8jS>^U#YHpQ1ehu;DJV{`$Tv|2n@<}&)%bA)yzM&#Qz#Y*w%aA!&qNIhO9 z?NYj4N)soCPWp|+TI@hajED44+cxmr$G*0d9?@^k^I5_!cjpjxk*F+)#W zwQnF*7Sc`{Qz=2K779)9HV^JERcRxS#Vy&B@Vq|}gFPz|NKoh)8yp7M)*s?H*54OTW9;kG>1f&j9P(=ieRva8bY8y_*DTP7|cRE8Oe=_L59E?1Y>Rg*Om^F(Tuk* z*N%41Y55%;C|+VT(Rbmd6YHep>N1k<-mIEOD~k^7=jP&C-;zvI8~SnO z^|+?0dWv2yWSV$^lsUJ{GYo~H=VYh5oU*z>jcw2KNsVj2A5$glKj6<53k(hF(zFbW zD4`fQ;JHLZ7Uv6MJc6LAnENGe2{i$*?w@mZcHi~Ce$m@4?sy5j;N7!6GwS;An=Z|k z5O4%{l4%G|C$yCi9bQ?b0ASF*Qzp@acxSTBfFz*9x~~5uCO%WtJ(0FwCPd0X%k&^& z{v$8sbxTLIl6`<)`f3@8S&%?O$mc zpz#{hUn=HFXPBKVcl3;f*}+jnbL1u0D^UADORMzE{(WuV%x&i*`<47wy6>YeeCby@ zL*;&*$CTZ!u$NghPX#Q;x^L#-$dj7HW5S zeF1U1`ZFQ5uLMZ2Ze=h*y#i`HEE211_s2CC8>Z2v5*sdzS zswj%9K)Zyg*&K9W{rTB&w5hbpoHv%6^g(sXJK_rS@%$b3Fgj)byW%681RoC&$mXv25nxuR`V zv5(i)HEkGMNyiskTI}K1QaHD+lcUYqRwIiQ=!!2EAq1$O`7&ofdd;aFD)Vg}V7PQ6 z*c$N#S&A+E_P^n7;?lQczga$Tgj{~h`wA`rhemQZt=^bU&ng8YCu5|&w!)*aYu(~T z3c5YU8$wR*CPbl6BXiW54<00M)TsEqO(qW8I<^(zlU0QQTUt@u)!uXN7cuO&E=#t| zqu6+>`W}yi;{{ZhMgc$4G7=GdbnM*PC^k$<;sl$ zv{F=mmNWD~S#f|{`|3G_mRzJXwmpOy!9{c6DFdO0)CwH@)$25j6P@0vYboXI zUeSB(*9NCg*|62sRE$q!m%o2%;t-0}&#fC9wZ7rMp+QZ7cSt}nD9H~_e`Fp`MEH^}D9&*sC zFIzz}uEoSWJaWd9FD$iRIu*@WPZ!N|s)(^hn%T0`HFq`?!6m25+nO;5IDW0et|qo# zYDRWQ$ETPOTM$mfXDm)O#8x}pgZ5138ch-if?>Q#>-ky?`wh10Y31PR4JNMlbuU4t zNs~-BcbqEETsY`Hxo)+1K>QEt===qIQYbn?52|nB-PcQMOGhs_~ z!{yw~j~Biz_3pmP;TGkn`Y=;WQ}A(bW1dniyPIbp1+V>0KCh&AeG0Juh(Pcjenixt z|E~V$7f=X2c1+K~9~1&+EeEVEHWc(F7Cbqxyp7E_LjN-=UPO}UoC>yVNg-)?H|sUGljl7|K>Iox&+x6^qaAni& z&5)1?XA#cCl0ngPW*LK}a)4;xfLGeCt}Ig*_;g;D{AP3E5@sU8*T+jC$YpR~H!dh0 z^FA2od_1Nrr4l&dI_};EFO+reWi)D)8RFv^{_c4tH;l>&bicCsgvr7C&xTyv1--wp znti;Q&Rl$(>5gI$mp2c$$2zyqWs|h=lLOHWCjMx?r*6JmzT^*-xQ^NKT^dpn4{u?{ zbyptUsEfLK)&{;oOm=zYr3BemwxS_*u~IGmi!EX8o}EC#~7ZpmV*=)&ATk&}#zg_vWmD0e8YT;AVoyJZn0HHS_>wyk|Zy4WtA(su| z?=%{hIz4rf`l=!957KV1kNS}|xH=WeJuDv+ecgpQ7Gg1&H6(C>^a?^UPUSLSkIMU% zh~PVSk1bcgQukLCzKGE2lQSfRM?xcxw&tH0?x7UTc;vhD8J%wyuhY<38ab=sn%BaL zW@Y&t+G}FZ9$SYIx}6&fCqt5{8N}vPdFOK4%{$Z6%HUi}Z_!}OwO#W*PPRUun>jT^ zCmjg|M|sOx2<=lJK7o{Cp8LFyeQV8KQCPbD{H$fmcfBK z)3kj)sqTHsFOeA90@r(#>>#s|iLlw|(-2f;@Vv2M7q*3D^k*{9>t&HHLvOCJRpy_f z?X2&s1rzlb@^B>2Y#Do6cQd@pD85rwbXde{0_e1Os1UoOR2!dZ;kTb%^(Abv%os&s z-J7k7)I;yFkPTY5BVcQN92E5Wz0VxK(;Y752_haO22qrWiO>(n$xjmvinIdfnDJryoAM>@S@% zRf!2#TE5mWU$r{udTQr*NkS5-a;25h6fLW;3!GNq55WRSTWV=1_b|Vhtld3ZBP;j) zY5fQ_5wydKHR$`}r}9UM2cA)IGp}{f0StV?*kB=Lo`mMgxY(!}p*_WT!LfA^?PwEm zUa?(b5QS>CcJlE2bmZ(nerF%yg0{8(d8Io;bUYgKl_k%#5W1o_Q0yZ8&NwC4g>bZ4RVRw~_aNQ|)_Sv#r0}o)3+=FzuYF>TWU8}F-%|4~HE-){xzYNz zD}U&;fn-bwh1goMuT9Vh#D~vIcoW;D!_KoUml4a+QA&+sT+<2<;o*-FR;YdwjA+bq zZ11FpBkORt@+wiuH&pk2MRkag62Hz?$KLFH6?eku&`f7v3yTIGe}3Xv>DAzMkpxPr z1DK`c=Qg!4Tt{=<t#M>Yowh2O)}J{pM7@nPn>VEfqx&tPIk4qX7cs2N>991) zCEF-9W&V7qu6-`adKXGwW92Mx+5LTh%*y2vd*u?}PrtSZX4eb#wLm__b??4s+ zRFdwvvl^cB3qFrazd1P$&Qmndlslqoq;=-@*fXIdg!(AqCc}G%8T>UYInAVd7Wb5C zct5HygBF*za}+6|d8-BjPb|oC)lEjYOH966P`r{6@_u9>pVpoeFc!Vmp}I4G7P*^_ zN-~%wT1ZI+Yyh0kp8RsYGhOquW{(H~V!pg~*JKpdTrmd14EL>~ZBFtd4g{+iR*8H^ zcVklCi{1WYW6d?od1jw`bzrhP&Y{$TFU#yc%y2yKw!_eiMx(<^<*Sm+JYlb}Cxf@N zI4Y@=-ZOTrYFY2A-|bS0W|2(|TK45PSIv=1K`w-6L7bX~t0Ix`IHjLce;ZJE0+?;7 zr_^<~MD?0RP+w$-=wBfYcB+!hzpjm>$5Ix0laBEo@~H_s1;$mOmrMp+8lJaaHUFy9 zbds%{CJ)PJNaoNrwwpZqxUtTa)sPCVTIu&1Tx%hf4c|~}cQrh4zuIyVhhgTqUj_wX z{7O@8MU_mK-cF{Rv^sa5bxoIa7D?Ui2A7UUcXFN(Pqs7b!+8z>ImZV|cmqA0_q^>j28^H(y@ zA!T?YNjT`0wQe#f3w%&&r$NoijtF5{*Y?c`0dU2iqFM{FO;-ha|LyEr{=Mcwa2p<>Wz zU!*kiA&+}ZC(H7sM1;YRaBgqIqsTuZ3TzI%i$ot_x!qho7hEkX2X}3FiTcEPeJcF&yu#9AIxTFU)OX(eWqD1=y(_GAl)>88;4bx)gjYwlbx5 zyl3jL(Clo;tEET8{_$wCvx7Z)qi~gcwl=L=_}%5Oy?Ku5?>QZ1t%coRL*q0BBT|WT zAj^i?#nFc7RmsJUlh8GEE9L2&BW40W$K{-D?xSfM`IsY~EIRv>b*GgF${Q~boplKa zLQ^fu<)7`m8qiI@D0_PXe(2VGH3nU}{xaT7U(Rw6P_?w98^7H+bvdH@OVq7HN#I1Y zCiqMyJUl_pB1-~%@^XsShpgS;bFM?}^f0yysd4f&pkZL6a3g5~+d>)Liyz~nioh7R zFOG2#FuCmJS30a@ey97)t0{A46~&P?yIoDX%?+OC74DfI4AHWPcpML5;2L?+nxlW} zEkVCfA>j$m^u1+85^2e#$ni*wpaq6Y2pxY~*fif|JSao*)Gwq9CfR&uYs`Z@ zjS{%ngup0NbU`0lYt8f=()l)NW8d;{sBC+9;}8#nCd7-Z-kXiO71xlUP|vf{nop zMM`m_1HGT6-``&ueBKXPDMhN{ z`-rnCgzjp*LTrFXbsI}fJT$WiN~NSDPK5lSfb+BAn*nNWRWV&kVOmY)yWP>-atM!< z2-3%s{-q=XY-4;&!Q86;|HVCjppMy|4#eex$=7AEAH>p8GRLMhug2<5a*P=ly^eb& z#g(q$V8i(cYy|rE`Uh%VZ-RhNJbh)5PyYt8t)UN)z6I2ysn_B$PmUWW{)rlEjGFRL zVyBPqorqcf<2A7*GWKgn{#g7i#YI4uc1$ThdPOVNJMp2!zuoxqRtvN5tNhYff3bd$ z`>dT3WcY@R(SvTgR=Z9s1GNY1{a;R>oF7+_Isjd2QM>JySQ=auGUyrEL*`${OeWux z@rs4*is>^b_;@v4&h|HS05{wLB<|by@c$0x)+35$cut&Qf0#^3+SulJC2&INSQ1wD z^6PlT*s&9`lhEp?@+=G!Z#t|KPg n76&v-erFg!q1&N_&29S4N%TK7)1Pkxeg+pVpD)zC{^)-I(LY1_ literal 68170 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 Xw%TpMjh!zy0e{vO_Ghb4d&mC|$u6=? diff --git a/docs/op-stack/src/docs/build/explorer.md b/docs/op-stack/src/docs/build/explorer.md index 943aec92f10c8..02a54be5ffa5b 100644 --- a/docs/op-stack/src/docs/build/explorer.md +++ b/docs/op-stack/src/docs/build/explorer.md @@ -59,3 +59,66 @@ Download and install [Docker engine](https://docs.docker.com/engine/install/#ser After the docker containers start, browse to http:// < *computer running Blockscout* > :4000 to view the user interface. You can also use the [API](https://docs.blockscout.com/for-users/api) + +### GraphQL + +Blockscout's API includes [GraphQL](https://graphql.org/) support under `/graphiql`. +For example, this query looks at addresses. + +``` +query { + addresses(hashes:[ + "0xcB69A90Aa5311e0e9141a66212489bAfb48b9340", + "0xC2dfA7205088179A8644b9fDCecD6d9bED854Cfe"]) +``` + +GraphQL queries start with a top level entity (or entities). +In this case, our [top level query](https://docs.blockscout.com/for-users/api/graphql#queries) is for multiple addresses. + +Note that you can only query on fields that are indexed. +For example, here we query on the addresses. +However, we couldn't query on `contractCode` or `fetchedCoinBalance`. + +``` + { + hash + contractCode + fetchedCoinBalance +``` + +The fields above are fetched from the address table. + +``` + transactions(first:5) { +``` + +We can also fetch the transactions that include the address (either as source or destination). +The API does not let us fetch an unlimited number of transactions, so here we ask for the first 5. + + +``` + edges { + node { +``` + +Because this is a [graph](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)), the entities that connect two types, for example addresses and transactions, are called `edges`. +At the other end of each edge there is a transaction, which is a separate `node`. + +``` + hash + fromAddressHash + toAddressHash + input + } +``` + +These are the fields we read for each transaction. + +``` + } + } + } +} +``` + +Finally, close all the brackets. \ No newline at end of file diff --git a/docs/op-stack/src/docs/build/getting-started.md b/docs/op-stack/src/docs/build/getting-started.md index 24dd8fff089bf..46617b28080c1 100644 --- a/docs/op-stack/src/docs/build/getting-started.md +++ b/docs/op-stack/src/docs/build/getting-started.md @@ -39,12 +39,11 @@ This tutorial was checked on: | Software | Version | Installation command(s) | | -------- | ---------- | - | | Ubuntu | 20.04 LTS | | -| git | OS default | | -| make | 4.2.1-1.2 | `sudo apt install -y make` +| git, curl, and make | OS default | `sudo apt install -y git curl make` | | Go | 1.20 | `sudo apt update`
    `wget https://go.dev/dl/go1.20.linux-amd64.tar.gz`
    `tar xvzf go1.20.linux-amd64.tar.gz`
    `sudo cp go/bin/go /usr/bin/go`
    `sudo mv go /usr/lib`
    `echo export GOROOT=/usr/lib/go >> ~/.bashrc` -| Node | 16.19.0 | `curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -`
    `sudo apt-get install -y nodejs` +| Node | 16.19.0 | `curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -`
    `sudo apt-get install -y nodejs npm` | yarn | 1.22.19 | `sudo npm install -g yarn` -| Foundry | 0.2.0 | `curl -L https://foundry.paradigm.xyz | bash`
    `sudo bash`
    `foundryup` +| Foundry | 0.2.0 | `curl -L https://foundry.paradigm.xyz | bash`
    `. ~/.bashrc`
    `foundryup` ## Build the Source Code @@ -74,7 +73,8 @@ We’re going to be spinning up an EVM Rollup from the OP Stack source code. Yo 1. Build the various packages inside of the Optimism Monorepo. ```bash - make build + make op-node op-batcher + yarn build ``` ### Build op-geth @@ -440,20 +440,27 @@ Once you’ve connected your wallet, you’ll probably notice that you don’t h cd ~/optimism/packages/contracts-bedrock ``` -1. Grab the address of the `OptimismPortalProxy` contract: +1. Grab the address of the proxy to the L1 standard bridge contract: ```bash - cat deployments/getting-started/OptimismPortalProxy.json | grep \"address\": + cat deployments/getting-started/Proxy__OVM_L1StandardBridge.json.json | grep \"address\": ``` You should see a result like the following (**your address will be different**): ``` - "address": "0x264B5fde6B37fb6f1C92AaC17BA144cf9e3DcFE9", - "address": "0x264B5fde6B37fb6f1C92AaC17BA144cf9e3DcFE9", + "address": "0x874f2E16D803c044F10314A978322da3c9b075c7", + "internalType": "address", + "type": "address" + "internalType": "address", + "type": "address" + "internalType": "address", + "type": "address" + "internalType": "address", + "type": "address" ``` -1. Grab the `OptimismPortalProxy` address and, using the wallet that you want to have ETH on your Rollup, send that address a small amount of ETH on Goerli (0.1 or less is fine). It may take up to 5 minutes for that ETH to appear in your wallet on L2. +1. Grab the L1 bridge proxy contract address and, using the wallet that you want to have ETH on your Rollup, send that address a small amount of ETH on Goerli (0.1 or less is fine). It may take up to 5 minutes for that ETH to appear in your wallet on L2. ## Use your Rollup diff --git a/docs/op-stack/src/docs/build/sdk.md b/docs/op-stack/src/docs/build/sdk.md new file mode 100644 index 0000000000000..37d5a838ca44a --- /dev/null +++ b/docs/op-stack/src/docs/build/sdk.md @@ -0,0 +1,155 @@ +--- +title: Using the SDK with OP Stack +lang: en-US +--- + +When building applications for use with your OP Stack, you can continue to use [the Optimism JavaScript SDK](https://sdk.optimism.io/). +The main difference is you need to provide some contract addresses to the `CrossDomainMessenger` because they aren't preconfigured. + + +## Contract addresses + +### L1 contract addresses + +The contract addresses are in `.../optimism/packages/contracts-bedrock/deployments/getting-started`, which you created when you deployed the L1 contracts. + +| Contract name when creating `CrossDomainMessenger` | File with address | +| - | - | +| `AddressManager` | `Lib_AddressManager.json` +| `L1CrossDomainMessenger` | `Proxy__OVM_L1CrossDomainMessenger.json` +| `L1StandardBridge` | `Proxy__OVM_L1StandardBridge.json` +| `OptimismPortal` | `OptimismPortalProxy.json` +| `L2OutputOracle` | `L2OutputOracleProxy.json` + + +### Unneeded contract addresses + +Some contracts are required by the SDK, but not actually used. +For these contracts you can just specify the zero address: + +- `StateCommitmentChain` +- `CanonicalTransactionChain` +- `BondManager` + +In JavaScript you can create the zero address using the expression `"0x".padEnd(42, "0")`. + +## The CrossChainMessenger object + +These directions assume you are inside the [Hardhat console](https://hardhat.org/hardhat-runner/docs/guides/hardhat-console). +They further assume that your project already includes the Optimism SDK [`@eth-optimism/sdk`](https://www.npmjs.com/package/@eth-optimism/sdk). + +1. Import the SDK + + ```js + optimismSDK = require("@eth-optimism/sdk") + ``` + +1. Set the configuration parameters. + + | Variable name | Value | + | - | - | + | `l1Url` | URL to an RPC provider for L1, for example `https://eth-goerli.g.alchemy.com/v2/` + | `l2Url` | URL to your OP Stack. If running on the same computer, it is `http://localhost:8545` + | `privKey` | The private key for an account that has some ETH on the L1 + + +1. Create the [providers](https://docs.ethers.org/v5/api/providers/) and [signers](https://docs.ethers.org/v5/api/signer/). + + ```js + l1Provider = new ethers.providers.JsonRpcProvider(l1Url) + l2Provider = new ethers.providers.JsonRpcProvider(l2Url) + l1Signer = new ethers.Wallet(privKey).connect(l1Provider) + l2Signer = new ethers.Wallet(privKey).connect(l2Provider) + ``` + +1. Create the L1 contracts structure. + + ```js + zeroAddr = "0x".padEnd(42, "0") + l1Contracts = { + StateCommitmentChain: zeroAddr, + CanonicalTransactionChain: zeroAddr, + BondManager: zeroAddr, + // These contracts have the addresses you found out earlier. + AddressManager: "0x....", // Lib_AddressManager.json + L1CrossDomainMessenger: "0x....", // Proxy__OVM_L1CrossDomainMessenger.json + L1StandardBridge: "0x....", // Proxy__OVM_L1StandardBridge.json + OptimismPortal: "0x....", // OptimismPortalProxy.json + L2OutputOracle: "0x....", // L2OutputOracleProxy.json + } + ``` + +1. Create the data structure for the standard bridge. + + ```js + bridges = { + Standard: { + l1Bridge: l1Contracts.L1StandardBridge, + l2Bridge: "0x4200000000000000000000000000000000000010", + Adapter: optimismSDK.StandardBridgeAdapter + }, + ETH: { + l1Bridge: l1Contracts.L1StandardBridge, + l2Bridge: "0x4200000000000000000000000000000000000010", + Adapter: optimismSDK.ETHBridgeAdapter + } + } + ``` + + +1. Create the [`CrossChainMessenger`](https://sdk.optimism.io/classes/crosschainmessenger) object. + + ```js + crossChainMessenger = new optimismSDK.CrossChainMessenger({ + bedrock: true, + contracts: { + l1: l1Contracts + }, + bridges: bridges, + l1ChainId: await l1Signer.getChainId(), + l2ChainId: await l2Signer.getChainId(), + l1SignerOrProvider: l1Signer, + l2SignerOrProvider: l2Signer, + }) + ``` + +## Verify SDK functionality + +To verify the SDK's functionality, transfer some ETH from L1 to L2. + +1. Get the current balances. + + ```js + balances0 = [ + await l1Provider.getBalance(l1Signer.address), + await l2Provider.getBalance(l1Signer.address) + ] + ``` + +1. Transfer 1 gwei. + + ```js + tx = await crossChainMessenger.depositETH(1e9) + rcpt = await tx.wait() + ``` + +1. Get the balances after the transfer. + + ```js + balances1 = [ + await l1Provider.getBalance(l1Signer.address), + await l2Provider.getBalance(l1Signer.address) + ] + ``` + +1. See that the L1 balance changed (probably by a lot more than 1 gwei because of the cost of the transaction). + + ```js + (balances0[0]-balances1[0])/1e9 + ``` + +1. See that the L2 balance changed (it might take a few minutes). + + ```js + ((await l2Provider.getBalance(l1Signer.address))-balances0[1])/1e9 + ``` diff --git a/docs/op-stack/src/docs/contribute.md b/docs/op-stack/src/docs/contribute.md index 7516d4dd6c930..8a5d41a5a2cb9 100644 --- a/docs/op-stack/src/docs/contribute.md +++ b/docs/op-stack/src/docs/contribute.md @@ -1,5 +1,5 @@ --- -title: Contribute to OP Stack +title: Contribute to the OP Stack lang: en-US --- @@ -21,7 +21,7 @@ The OP Stack needs YOU (yes you!) to help review the codebase for bugs and vulne ## Documentation help -Spot a typo in these docs? See something that could be a little clearer? Head over to the Optimism Monorepo where the OP Stack docs are hosted and make a pull request. No contribution is too small! +Spot a typo in these docs? See something that could be a little clearer? Head over to the Optimism Monorepo where the OP Stack docs are hosted and make a pull request. No contribution is too small! ## Community contributions @@ -30,4 +30,4 @@ If you’re looking for other ways to get involved, here are a few options: - Grab an idea from the [project ideas list](https://github.com/ethereum-optimism/optimism-project-ideas) to and building - Suggest a new idea for the [project ideas list](https://github.com/ethereum-optimism/optimism-project-ideas) - Improve the [Optimism Community Hub](https://community.optimism.io/) [documentation](https://github.com/ethereum-optimism/community-hub) or [tutorials](https://github.com/ethereum-optimism/optimism-tutorial) -- Become an Optimism Ambassador, Support Nerd, and more in the [Optimism Discord](https://discord-gateway.optimism.io/) \ No newline at end of file +- Become an Optimism Ambassador, Support Nerd, and more in the [Optimism Discord](https://discord-gateway.optimism.io/) diff --git a/docs/op-stack/yarn.lock b/docs/op-stack/yarn.lock index 9f3f31524a6e1..5c8cf3d6fd79c 100644 --- a/docs/op-stack/yarn.lock +++ b/docs/op-stack/yarn.lock @@ -1292,6 +1292,46 @@ dependencies: "@hapi/hoek" "^8.3.0" +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@leancloud/adapter-types@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@leancloud/adapter-types/-/adapter-types-3.0.0.tgz#71c5e8e37065bea4914650848b55a6262d658577" @@ -2436,6 +2476,11 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== +acorn@^8.5.0: + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + agentkeepalive@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" @@ -2755,6 +2800,11 @@ autoprefixer@^9.5.1: postcss "^7.0.32" postcss-value-parser "^4.1.0" +autosize@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/autosize/-/autosize-4.0.4.tgz#924f13853a466b633b9309330833936d8bccce03" + integrity sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -2938,6 +2988,11 @@ bluebird@^3.1.1, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +blueimp-md5@^2.8.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0" + integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -3374,6 +3429,21 @@ check-md@^1.0.2: globby "^9.1.0" yargs-parser "^20.2.1" +"chokidar@>=3.0.0 <4.0.0": + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^2.0.3, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -3587,6 +3657,11 @@ commander@~2.19.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== +comment-regex@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/comment-regex/-/comment-regex-1.0.1.tgz#e070d2c4db33231955d0979d27c918fcb6f93565" + integrity sha512-IWlN//Yfby92tOIje7J18HkNmWRR7JESA/BK8W7wqY/akITpU5B0JQWnbTjCfdChSrDNb0DrdA9jfAxiiBXyiQ== + common-tags@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -3706,9 +3781,9 @@ cookie@0.4.0: integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== cookiejar@^2.1.0, cookiejar@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" - integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== copy-concurrently@^1.0.0: version "1.0.5" @@ -4626,9 +4701,9 @@ decamelize@^1.1.1, decamelize@^1.2.0: integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== decompress-response@^3.3.0: version "3.3.0" @@ -5855,6 +5930,13 @@ gray-matter@^4.0.1: section-matter "^1.0.0" strip-bom-string "^1.0.0" +hanabi@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/hanabi/-/hanabi-0.4.0.tgz#ebbb251358c1337db1eabda686c43ff777d30d82" + integrity sha512-ixJH94fwmmVzUSdxl7TMkVZJmsq4d2JKrxedpM5V1V+91iVHL0q6NnJi4xiDahK6Vo00xT17H8H6b4F6RVbsOg== + dependencies: + comment-regex "^1.0.0" + handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -6068,9 +6150,9 @@ htmlparser2@^6.1.0: entities "^2.0.0" http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-deceiver@^1.2.7: version "1.2.7" @@ -6219,6 +6301,11 @@ immediate@^3.2.3: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== +immutable@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be" + integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg== + import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -7228,9 +7315,9 @@ lru-cache@^6.0.0: yallist "^4.0.0" luxon@^1.3.3: - version "1.28.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" - integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + version "1.28.1" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.1.tgz#528cdf3624a54506d710290a2341aa8e6e6c61b0" + integrity sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw== magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.7" @@ -7309,6 +7396,11 @@ markdown-it@^8.4.1: mdurl "^1.0.1" uc.micro "^1.0.5" +marked@^4.0.8: + version "4.2.12" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.12.tgz#d69a64e21d71b06250da995dcd065c11083bebb5" + integrity sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -7510,16 +7602,16 @@ minimalistic-crypto-utils@^1.0.1: integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== miniprogram-api-typings@^2.10.2: version "2.12.0" @@ -8701,16 +8793,16 @@ qs@6.7.0: integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== qs@^6.5.1, qs@^6.6.0, qs@^6.9.4: - version "6.10.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.2.tgz#c1431bea37fc5b24c5bdbafa20f16bdf2a4b9ffe" - integrity sha512-mSIdjzqznWgfd4pMii7sHtaYF8rx8861hBO80SraY5GT0XQibWZWJSid0avzHGkDIZLImux2S5mXO0Hfct2QCw== + version "6.11.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" + integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== dependencies: side-channel "^1.0.4" qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== query-string@^5.0.1: version "5.1.1" @@ -9136,6 +9228,15 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass@^1.49.9: + version "1.59.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.59.3.tgz#a1ddf855d75c70c26b4555df4403e1bbf8e4403f" + integrity sha512-QCq98N3hX1jfTCoUAsF3eyGuXLsY7BCnCEg9qAact94Yc21npG2/mVOqoDvE0fCbWDqiM4WlcJQla0gWG2YlxQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -9455,6 +9556,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +"source-map-js@>=0.6.2 <2.0.0": + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -9502,7 +9608,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3, source-map@~0.7.2: +source-map@^0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== @@ -9960,21 +10066,22 @@ terser-webpack-plugin@^1.4.3: worker-farm "^1.7.0" terser@^4.1.2: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + version "4.8.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" + integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== dependencies: commander "^2.20.0" source-map "~0.6.1" source-map-support "~0.5.12" terser@^5.0.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" - integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== + version "5.16.6" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" + integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" commander "^2.20.0" - source-map "~0.7.2" source-map-support "~0.5.20" text-table@^0.2.0: @@ -10408,15 +10515,20 @@ uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== valine@^1.4.16: - version "1.4.16" - resolved "https://registry.yarnpkg.com/valine/-/valine-1.4.16.tgz#6b077734c94d75cb6b1cbfb88e9e634000c1bfef" - integrity sha512-gKmVzOAYrHNKt2Oswu6+UFiR+qJfTuVjPj6XrqtoPNZFcoJaITftGI6qw/n//YhWMd/75E4WTL6WeZ5CHhADdA== + version "1.5.1" + resolved "https://registry.yarnpkg.com/valine/-/valine-1.5.1.tgz#cb7aa97c07588646e7cd1494bd1a4d09093931c3" + integrity sha512-uT9tHuoKkFUqqVHHRyz4EPe8U+h1ZkCa2lVKH8SKZM9r1Whm8DZW9GfrK4USmLJ+QwpHWenPIKOH0s2xO0BVbQ== dependencies: + autosize "^4.0.2" balajs "^1.0.7" balalaika "^1.0.1" + blueimp-md5 "^2.8.0" element-closest "^3.0.2" + hanabi "^0.4.0" insane "^2.6.2" leancloud-storage "^3.0.4" + marked "^4.0.8" + sass "^1.49.9" storejs "^1.0.25" xss "^1.0.6" diff --git a/go.mod b/go.mod index 6ef566f31cb04..1ee69c817630b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ethereum-optimism/optimism -go 1.18 +go 1.19 require ( github.com/btcsuite/btcd v0.23.3 @@ -9,7 +9,7 @@ require ( github.com/docker/docker v20.10.21+incompatible github.com/docker/go-connections v0.4.0 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 - github.com/ethereum/go-ethereum v1.11.2 + github.com/ethereum/go-ethereum v1.11.4 github.com/fsnotify/fsnotify v1.6.0 github.com/golang/snappy v0.0.4 github.com/google/go-cmp v0.5.9 @@ -69,11 +69,11 @@ require ( github.com/francoispqt/gojay v1.2.13 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect - github.com/go-kit/kit v0.10.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect github.com/golang/mock v1.6.0 // indirect @@ -86,7 +86,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-bexpr v0.1.11 // indirect github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect - github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/huin/goupnp v1.1.0 // indirect github.com/influxdata/influxdb v1.8.3 // indirect @@ -147,7 +146,6 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/prometheus/tsdb v0.10.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-18 v0.2.0 // indirect github.com/quic-go/qtls-go1-19 v0.2.0 // indirect @@ -191,6 +189,6 @@ require ( nhooyr.io/websocket v1.8.7 // indirect ) -replace github.com/ethereum/go-ethereum v1.11.2 => github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230308025559-13ee9ab9153b +replace github.com/ethereum/go-ethereum v1.11.4 => github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230321002540-11f0554a4313 -//replace github.com/ethereum/go-ethereum v1.11.2 => ../go-ethereum +//replace github.com/ethereum/go-ethereum v1.11.4 => ../go-ethereum diff --git a/go.sum b/go.sum index 3b630af308eab..b01f9061f0a0c 100644 --- a/go.sum +++ b/go.sum @@ -36,40 +36,24 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VictoriaMetrics/fastcache v1.10.0 h1:5hDJnLsKLpnUEToub7ETuRu8RCkb40woBZAUiKonXzY= github.com/VictoriaMetrics/fastcache v1.10.0/go.mod h1:tjiYeEfYXCqacuvYw/7UoDIeJaNxq6132xHICNP77w8= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= @@ -78,7 +62,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= @@ -106,13 +89,10 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -120,10 +100,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= @@ -135,7 +113,6 @@ github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoG github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811/go.mod h1:Nb5lgvnQ2+oGlE/EyZy4+2/CxRh9KfvCXnag1vtpxVM= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= @@ -143,18 +120,15 @@ github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHq github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= @@ -181,7 +155,6 @@ github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -193,14 +166,9 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7 h1:kgvzE5wLsLa7XKfV85VZl40QXaMCaeFtHpPwJ8fhotY= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= @@ -209,7 +177,6 @@ github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/ github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= @@ -217,10 +184,9 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230308025559-13ee9ab9153b h1:7RNzqCwam//7PPieblo8GSIVukwrfoPO+0xT1yMp9Zw= -github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230308025559-13ee9ab9153b/go.mod h1:/tjlXxOaovIyuF0l6+wCzr6AtDb3lYWTymmpQAQcqu8= +github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230321002540-11f0554a4313 h1:dBPc4CEzqmHUeU/Awk7Lw2mAaTc59T5W8CvAr+4YuzU= +github.com/ethereum-optimism/op-geth v1.11.2-de8c5df46.0.20230321002540-11f0554a4313/go.mod h1:SGLXBOtu2JlKrNoUG76EatI2uJX/WZRY4nmEyvE9Q38= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ= github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= @@ -230,8 +196,6 @@ github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwU github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -262,13 +226,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= @@ -283,7 +242,6 @@ github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/j github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= @@ -301,13 +259,13 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -318,7 +276,6 @@ github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= @@ -343,7 +300,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= @@ -376,7 +332,6 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -386,41 +341,22 @@ github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE0 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= @@ -428,18 +364,11 @@ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:i github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnxuVTqZ6x4= github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e h1:pIYdhNkDh+YENVNi3gto8n9hAmRxKxoar0iE6BLucjw= -github.com/holiman/big v0.0.0-20221017200358-a027dc42d04e/go.mod h1:j9cQbcqHQujT0oKJ38PylVfqohClLr3CvDC+Qcg+lhU= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= @@ -454,7 +383,6 @@ github.com/influxdata/influxdb v1.8.3 h1:WEypI1BQFTT4teLM+1qkEcvUi0dAvopAI/ir0vA github.com/influxdata/influxdb v1.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= github.com/influxdata/influxdb-client-go/v2 v2.4.0 h1:HGBfZYStlx3Kqvsv1h2pJixbCl/jhnFtxpKFAv9Tu5k= github.com/influxdata/influxdb-client-go/v2 v2.4.0/go.mod h1:vLNHdxTJkIf2mSLvGrpj8TCcISApPoXkaxP8g9uRlW8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxql v1.1.1-0.20200828144457-65d3ef77d385/go.mod h1:gHp9y86a/pxhjJ+zMjNXiQAA197Xk9wLxaz+fGG+kWk= github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= @@ -495,12 +423,8 @@ github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZl github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= @@ -518,7 +442,6 @@ github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYb github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -553,6 +476,7 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= @@ -587,10 +511,7 @@ github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtI github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -605,7 +526,6 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -615,7 +535,6 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= @@ -629,7 +548,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= @@ -643,15 +561,9 @@ github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8Rv github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -699,21 +611,14 @@ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/n github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -734,40 +639,28 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= @@ -776,41 +669,29 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/prometheus/tsdb v0.10.0 h1:If5rVCMTp6W2SiRAQFlbpJNgVlgMEd+U2GZckwK38ic= -github.com/prometheus/tsdb v0.10.0/go.mod h1:oi49uRhEe9dPUTlS3JRZOwJuVi6tmh10QSgwXEyGCt4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-18 v0.2.0 h1:5ViXqBZ90wpUcZS0ge79rf029yx0dYB0McyPJwqqj7U= @@ -825,12 +706,10 @@ github.com/quic-go/webtransport-go v0.5.1 h1:1eVb7WDWCRoaeTtFHpFBJ6WDN1bSrPrRoW6 github.com/quic-go/webtransport-go v0.5.1/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= @@ -842,13 +721,10 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8= github.com/schollz/progressbar/v3 v3.13.0/go.mod h1:ZBYnSuLAX2LU8P8UiKN/KgF2DY58AJC8yfVYLPC8Ly4= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -878,14 +754,11 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -896,14 +769,10 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -930,7 +799,6 @@ github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYa github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= github.com/tklauser/numcpus v0.5.0 h1:ooe7gN0fg6myJ0EKoTAf5hebTZrH52px3New/D9iJ+A= github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= @@ -939,8 +807,6 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw= github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -958,7 +824,6 @@ github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPyS github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -972,16 +837,11 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= @@ -993,15 +853,12 @@ go.uber.org/fx v1.19.1/go.mod h1:bGK+AEy7XUwTBkqCsK/vDyFF0JJOA6X5KWpNC0e6qTA= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -1010,7 +867,6 @@ go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1 golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -1070,14 +926,11 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1089,7 +942,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1126,15 +978,11 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1143,7 +991,6 @@ golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1151,14 +998,12 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1220,7 +1065,6 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1252,7 +1096,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1279,7 +1122,6 @@ gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZ google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1304,7 +1146,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= @@ -1321,13 +1162,9 @@ google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmE google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1351,10 +1188,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= @@ -1362,11 +1197,8 @@ gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1394,7 +1226,5 @@ nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/integration-tests/package.json b/integration-tests/package.json index c53fa68f77769..1fb1408d41041 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -30,10 +30,10 @@ "devDependencies": { "@babel/eslint-parser": "^7.5.4", "@eth-optimism/contracts": "^0.5.40", - "@eth-optimism/contracts-bedrock": "0.13.0", + "@eth-optimism/contracts-bedrock": "0.13.1", "@eth-optimism/contracts-periphery": "^1.0.7", "@eth-optimism/core-utils": "0.12.0", - "@eth-optimism/sdk": "2.0.0", + "@eth-optimism/sdk": "2.0.1", "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/providers": "^5.7.0", "@ethersproject/transactions": "^5.7.0", diff --git a/op-batcher/Dockerfile b/op-batcher/Dockerfile index a4accc687dcaf..9ab3a2f348d45 100644 --- a/op-batcher/Dockerfile +++ b/op-batcher/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.18.0-alpine3.15 as builder +FROM --platform=$BUILDPLATFORM golang:1.19.0-alpine3.15 as builder ARG VERSION=v0.0.0 diff --git a/op-batcher/Makefile b/op-batcher/Makefile index 93a0bd587d01c..7ec5761e58163 100644 --- a/op-batcher/Makefile +++ b/op-batcher/Makefile @@ -19,8 +19,19 @@ test: lint: golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint -e "errors.As" -e "errors.Is" +fuzz: + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelConfig_CheckTimeout ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationZero ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutMaxChannelDuration ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDurationTimeoutZeroMaxChannelDuration ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelCloseTimeout ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzChannelZeroCloseTimeout ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowClose ./batcher + go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzSeqWindowZeroTimeoutClose ./batcher + .PHONY: \ op-batcher \ clean \ test \ - lint + lint \ + fuzz diff --git a/op-batcher/batcher/batch_submitter.go b/op-batcher/batcher/batch_submitter.go index 964b925500388..132660614b2de 100644 --- a/op-batcher/batcher/batch_submitter.go +++ b/op-batcher/batcher/batch_submitter.go @@ -12,9 +12,9 @@ import ( gethrpc "github.com/ethereum/go-ethereum/rpc" "github.com/urfave/cli" + "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-batcher/rpc" oplog "github.com/ethereum-optimism/optimism/op-service/log" - opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" oprpc "github.com/ethereum-optimism/optimism/op-service/rpc" ) @@ -36,9 +36,10 @@ func Main(version string, cliCtx *cli.Context) error { } l := oplog.NewLogger(cfg.LogConfig) + m := metrics.NewMetrics("default") l.Info("Initializing Batch Submitter") - batchSubmitter, err := NewBatchSubmitterFromCLIConfig(cfg, l) + batchSubmitter, err := NewBatchSubmitterFromCLIConfig(cfg, l, m) if err != nil { l.Error("Unable to create Batch Submitter", "error", err) return err @@ -64,16 +65,15 @@ func Main(version string, cliCtx *cli.Context) error { }() } - registry := opmetrics.NewRegistry() metricsCfg := cfg.MetricsConfig if metricsCfg.Enabled { l.Info("starting metrics server", "addr", metricsCfg.ListenAddr, "port", metricsCfg.ListenPort) go func() { - if err := opmetrics.ListenAndServe(ctx, registry, metricsCfg.ListenAddr, metricsCfg.ListenPort); err != nil { + if err := m.Serve(ctx, metricsCfg.ListenAddr, metricsCfg.ListenPort); err != nil { l.Error("error starting metrics server", err) } }() - opmetrics.LaunchBalanceMetrics(ctx, l, registry, "", batchSubmitter.L1Client, batchSubmitter.From) + m.StartBalanceMetrics(ctx, l, batchSubmitter.L1Client, batchSubmitter.From) } rpcCfg := cfg.RPCConfig @@ -95,6 +95,9 @@ func Main(version string, cliCtx *cli.Context) error { return fmt.Errorf("error starting RPC server: %w", err) } + m.RecordInfo(version) + m.RecordUp() + interruptChannel := make(chan os.Signal, 1) signal.Notify(interruptChannel, []os.Signal{ os.Interrupt, diff --git a/op-batcher/batcher/channel_builder.go b/op-batcher/batcher/channel_builder.go index 12660a6e3054d..9fdf0dfcba39d 100644 --- a/op-batcher/batcher/channel_builder.go +++ b/op-batcher/batcher/channel_builder.go @@ -11,6 +11,27 @@ import ( "github.com/ethereum/go-ethereum/core/types" ) +var ( + ErrInvalidChannelTimeout = errors.New("channel timeout is less than the safety margin") + ErrInputTargetReached = errors.New("target amount of input data reached") + ErrMaxFrameIndex = errors.New("max frame index reached (uint16)") + ErrMaxDurationReached = errors.New("max channel duration reached") + ErrChannelTimeoutClose = errors.New("close to channel timeout") + ErrSeqWindowClose = errors.New("close to sequencer window timeout") +) + +type ChannelFullError struct { + Err error +} + +func (e *ChannelFullError) Error() string { + return "channel full: " + e.Err.Error() +} + +func (e *ChannelFullError) Unwrap() error { + return e.Err +} + type ChannelConfig struct { // Number of epochs (L1 blocks) per sequencing window, including the epoch // L1 origin block itself @@ -48,6 +69,32 @@ type ChannelConfig struct { ApproxComprRatio float64 } +// Check validates the [ChannelConfig] parameters. +func (cc *ChannelConfig) Check() error { + // The [ChannelTimeout] must be larger than the [SubSafetyMargin]. + // Otherwise, new blocks would always be considered timed out. + if cc.ChannelTimeout < cc.SubSafetyMargin { + return ErrInvalidChannelTimeout + } + + // If the [MaxFrameSize] is set to 0, the channel builder + // will infinitely loop when trying to create frames in the + // [channelBuilder.OutputFrames] function. + if cc.MaxFrameSize == 0 { + return errors.New("max frame size cannot be zero") + } + + // If the [MaxFrameSize] is less than [FrameV0OverHeadSize], the channel + // out will underflow the maxSize variable in the [derive.ChannelOut]. + // Since it is of type uint64, it will wrap around to a very large + // number, making the frame size extremely large. + if cc.MaxFrameSize < derive.FrameV0OverHeadSize { + return fmt.Errorf("max frame size %d is less than the minimum 23", cc.MaxFrameSize) + } + + return nil +} + // InputThreshold calculates the input data threshold in bytes from the given // parameters. func (c ChannelConfig) InputThreshold() uint64 { @@ -87,8 +134,12 @@ type channelBuilder struct { blocks []*types.Block // frames data queue, to be send as txs frames []frameData + // total amount of output data of all frames created yet + outputBytes int } +// newChannelBuilder creates a new channel builder or returns an error if the +// channel out could not be created. func newChannelBuilder(cfg ChannelConfig) (*channelBuilder, error) { co, err := derive.NewChannelOut() if err != nil { @@ -105,11 +156,21 @@ func (c *channelBuilder) ID() derive.ChannelID { return c.co.ID() } -// InputBytes returns to total amount of input bytes added to the channel. +// InputBytes returns the total amount of input bytes added to the channel. func (c *channelBuilder) InputBytes() int { return c.co.InputBytes() } +// ReadyBytes returns the amount of bytes ready in the compression pipeline to +// output into a frame. +func (c *channelBuilder) ReadyBytes() int { + return c.co.ReadyBytes() +} + +func (c *channelBuilder) OutputBytes() int { + return c.outputBytes +} + // Blocks returns a backup list of all blocks that were added to the channel. It // can be used in case the channel needs to be rebuilt. func (c *channelBuilder) Blocks() []*types.Block { @@ -133,22 +194,25 @@ func (c *channelBuilder) Reset() error { // AddBlock returns a ChannelFullError if called even though the channel is // already full. See description of FullErr for details. // +// AddBlock also returns the L1BlockInfo that got extracted from the block's +// first transaction for subsequent use by the caller. +// // Call OutputFrames() afterwards to create frames. -func (c *channelBuilder) AddBlock(block *types.Block) error { +func (c *channelBuilder) AddBlock(block *types.Block) (derive.L1BlockInfo, error) { if c.IsFull() { - return c.FullErr() + return derive.L1BlockInfo{}, c.FullErr() } - batch, err := derive.BlockToBatch(block) + batch, l1info, err := derive.BlockToBatch(block) if err != nil { - return fmt.Errorf("converting block to batch: %w", err) + return l1info, fmt.Errorf("converting block to batch: %w", err) } if _, err = c.co.AddBatch(batch); errors.Is(err, derive.ErrTooManyRLPBytes) { c.setFullErr(err) - return c.FullErr() + return l1info, c.FullErr() } else if err != nil { - return fmt.Errorf("adding block to channel out: %w", err) + return l1info, fmt.Errorf("adding block to channel out: %w", err) } c.blocks = append(c.blocks, block) c.updateSwTimeout(batch) @@ -158,7 +222,7 @@ func (c *channelBuilder) AddBlock(block *types.Block) error { // Adding this block still worked, so don't return error, just mark as full } - return nil + return l1info, nil } // Timeout management @@ -215,7 +279,7 @@ func (c *channelBuilder) updateTimeout(timeoutBlockNum uint64, reason error) { } // checkTimeout checks if the channel is timed out at the given block number and -// in this case marks the channel as full, if it wasn't full alredy. +// in this case marks the channel as full, if it wasn't full already. func (c *channelBuilder) checkTimeout(blockNum uint64) { if !c.IsFull() && c.TimedOut(blockNum) { c.setFullErr(c.timeoutReason) @@ -330,10 +394,11 @@ func (c *channelBuilder) outputFrame() error { } frame := frameData{ - id: txID{chID: c.co.ID(), frameNumber: fn}, + id: frameID{chID: c.co.ID(), frameNumber: fn}, data: buf.Bytes(), } c.frames = append(c.frames, frame) + c.outputBytes += len(frame.data) return err // possibly io.EOF (last frame) } @@ -371,23 +436,3 @@ func (c *channelBuilder) PushFrame(frame frameData) { } c.frames = append(c.frames, frame) } - -var ( - ErrInputTargetReached = errors.New("target amount of input data reached") - ErrMaxFrameIndex = errors.New("max frame index reached (uint16)") - ErrMaxDurationReached = errors.New("max channel duration reached") - ErrChannelTimeoutClose = errors.New("close to channel timeout") - ErrSeqWindowClose = errors.New("close to sequencer window timeout") -) - -type ChannelFullError struct { - Err error -} - -func (e *ChannelFullError) Error() string { - return "channel full: " + e.Err.Error() -} - -func (e *ChannelFullError) Unwrap() error { - return e.Err -} diff --git a/op-batcher/batcher/channel_builder_test.go b/op-batcher/batcher/channel_builder_test.go new file mode 100644 index 0000000000000..7653964235294 --- /dev/null +++ b/op-batcher/batcher/channel_builder_test.go @@ -0,0 +1,750 @@ +package batcher + +import ( + "bytes" + "errors" + "fmt" + "math" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + dtest "github.com/ethereum-optimism/optimism/op-node/rollup/derive/test" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/trie" + + "github.com/stretchr/testify/require" +) + +var defaultTestChannelConfig = ChannelConfig{ + SeqWindowSize: 15, + ChannelTimeout: 40, + MaxChannelDuration: 1, + SubSafetyMargin: 4, + MaxFrameSize: 120000, + TargetFrameSize: 100000, + TargetNumFrames: 1, + ApproxComprRatio: 0.4, +} + +// TestChannelConfig_Check tests the [ChannelConfig] [Check] function. +func TestChannelConfig_Check(t *testing.T) { + type test struct { + input ChannelConfig + assertion func(error) + } + + // Construct test cases that test the boundary conditions + zeroChannelConfig := defaultTestChannelConfig + zeroChannelConfig.MaxFrameSize = 0 + timeoutChannelConfig := defaultTestChannelConfig + timeoutChannelConfig.ChannelTimeout = 0 + timeoutChannelConfig.SubSafetyMargin = 1 + tests := []test{ + { + input: defaultTestChannelConfig, + assertion: func(output error) { + require.NoError(t, output) + }, + }, + { + input: timeoutChannelConfig, + assertion: func(output error) { + require.ErrorIs(t, output, ErrInvalidChannelTimeout) + }, + }, + { + input: zeroChannelConfig, + assertion: func(output error) { + require.EqualError(t, output, "max frame size cannot be zero") + }, + }, + } + for i := 1; i < derive.FrameV0OverHeadSize; i++ { + smallChannelConfig := defaultTestChannelConfig + smallChannelConfig.MaxFrameSize = uint64(i) + expectedErr := fmt.Sprintf("max frame size %d is less than the minimum 23", i) + tests = append(tests, test{ + input: smallChannelConfig, + assertion: func(output error) { + require.EqualError(t, output, expectedErr) + }, + }) + } + + // Run the table tests + for _, test := range tests { + test.assertion(test.input.Check()) + } +} + +// FuzzChannelConfig_CheckTimeout tests the [ChannelConfig] [Check] function +// with fuzzing to make sure that a [ErrInvalidChannelTimeout] is thrown when +// the [ChannelTimeout] is less than the [SubSafetyMargin]. +func FuzzChannelConfig_CheckTimeout(f *testing.F) { + for i := range [10]int{} { + f.Add(uint64(i+1), uint64(i)) + } + f.Fuzz(func(t *testing.T, channelTimeout uint64, subSafetyMargin uint64) { + // We only test where [ChannelTimeout] is less than the [SubSafetyMargin] + // So we cannot have [ChannelTimeout] be [math.MaxUint64] + if channelTimeout == math.MaxUint64 { + channelTimeout = math.MaxUint64 - 1 + } + if subSafetyMargin <= channelTimeout { + subSafetyMargin = channelTimeout + 1 + } + + channelConfig := defaultTestChannelConfig + channelConfig.ChannelTimeout = channelTimeout + channelConfig.SubSafetyMargin = subSafetyMargin + require.ErrorIs(t, channelConfig.Check(), ErrInvalidChannelTimeout) + }) +} + +// addMiniBlock adds a minimal valid L2 block to the channel builder using the +// channelBuilder.AddBlock method. +func addMiniBlock(cb *channelBuilder) error { + a := newMiniL2Block(0) + _, err := cb.AddBlock(a) + return err +} + +// newMiniL2Block returns a minimal L2 block with a minimal valid L1InfoDeposit +// transaction as first transaction. Both blocks are minimal in the sense that +// most fields are left at defaults or are unset. +// +// If numTx > 0, that many empty DynamicFeeTxs will be added to the txs. +func newMiniL2Block(numTx int) *types.Block { + return newMiniL2BlockWithNumberParent(numTx, new(big.Int), (common.Hash{})) +} + +// newMiniL2Block returns a minimal L2 block with a minimal valid L1InfoDeposit +// transaction as first transaction. Both blocks are minimal in the sense that +// most fields are left at defaults or are unset. Block number and parent hash +// will be set to the given parameters number and parent. +// +// If numTx > 0, that many empty DynamicFeeTxs will be added to the txs. +func newMiniL2BlockWithNumberParent(numTx int, number *big.Int, parent common.Hash) *types.Block { + l1Block := types.NewBlock(&types.Header{ + BaseFee: big.NewInt(10), + Difficulty: common.Big0, + Number: big.NewInt(100), + }, nil, nil, nil, trie.NewStackTrie(nil)) + l1InfoTx, err := derive.L1InfoDeposit(0, l1Block, eth.SystemConfig{}, false) + if err != nil { + panic(err) + } + + txs := make([]*types.Transaction, 0, 1+numTx) + txs = append(txs, types.NewTx(l1InfoTx)) + for i := 0; i < numTx; i++ { + txs = append(txs, types.NewTx(&types.DynamicFeeTx{})) + } + + return types.NewBlock(&types.Header{ + Number: number, + ParentHash: parent, + }, txs, nil, nil, trie.NewStackTrie(nil)) +} + +// addTooManyBlocks adds blocks to the channel until it hits an error, +// which is presumably ErrTooManyRLPBytes. +func addTooManyBlocks(cb *channelBuilder) error { + for i := 0; i < 10_000; i++ { + block := newMiniL2Block(100) + _, err := cb.AddBlock(block) + if err != nil { + return err + } + } + + return nil +} + +// FuzzDurationTimeoutZeroMaxChannelDuration ensures that when whenever the MaxChannelDuration +// is set to 0, the channel builder cannot have a duration timeout. +func FuzzDurationTimeoutZeroMaxChannelDuration(f *testing.F) { + for i := range [10]int{} { + f.Add(uint64(i)) + } + f.Fuzz(func(t *testing.T, l1BlockNum uint64) { + channelConfig := defaultTestChannelConfig + channelConfig.MaxChannelDuration = 0 + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + cb.timeout = 0 + cb.updateDurationTimeout(l1BlockNum) + require.False(t, cb.TimedOut(l1BlockNum)) + }) +} + +// FuzzChannelBuilder_DurationZero ensures that when whenever the MaxChannelDuration +// is not set to 0, the channel builder will always have a duration timeout +// as long as the channel builder's timeout is set to 0. +func FuzzChannelBuilder_DurationZero(f *testing.F) { + for i := range [10]int{} { + f.Add(uint64(i), uint64(i)) + } + f.Fuzz(func(t *testing.T, l1BlockNum uint64, maxChannelDuration uint64) { + if maxChannelDuration == 0 { + t.Skip("Max channel duration cannot be 0") + } + + // Create the channel builder + channelConfig := defaultTestChannelConfig + channelConfig.MaxChannelDuration = maxChannelDuration + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Whenever the timeout is set to 0, the channel builder should have a duration timeout + cb.timeout = 0 + cb.updateDurationTimeout(l1BlockNum) + cb.checkTimeout(l1BlockNum + maxChannelDuration) + require.ErrorIs(t, cb.FullErr(), ErrMaxDurationReached) + }) +} + +// FuzzDurationTimeoutMaxChannelDuration ensures that when whenever the MaxChannelDuration +// is not set to 0, the channel builder will always have a duration timeout +// as long as the channel builder's timeout is greater than the target block number. +func FuzzDurationTimeoutMaxChannelDuration(f *testing.F) { + // Set multiple seeds in case fuzzing isn't explicitly used + for i := range [10]int{} { + f.Add(uint64(i), uint64(i), uint64(i)) + } + f.Fuzz(func(t *testing.T, l1BlockNum uint64, maxChannelDuration uint64, timeout uint64) { + if maxChannelDuration == 0 { + t.Skip("Max channel duration cannot be 0") + } + + // Create the channel builder + channelConfig := defaultTestChannelConfig + channelConfig.MaxChannelDuration = maxChannelDuration + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Whenever the timeout is greater than the l1BlockNum, + // the channel builder should have a duration timeout + cb.timeout = timeout + cb.updateDurationTimeout(l1BlockNum) + if timeout > l1BlockNum+maxChannelDuration { + // Notice: we cannot call this outside of the if statement + // because it would put the channel builder in an invalid state. + // That is, where the channel builder has a value set for the timeout + // with no timeoutReason. This subsequently causes a panic when + // a nil timeoutReason is used as an error (eg when calling FullErr). + cb.checkTimeout(l1BlockNum + maxChannelDuration) + require.ErrorIs(t, cb.FullErr(), ErrMaxDurationReached) + } else { + require.NoError(t, cb.FullErr()) + } + }) +} + +// FuzzChannelCloseTimeout ensures that the channel builder has a [ErrChannelTimeoutClose] +// as long as the timeout constraint is met and the builder's timeout is greater than +// the calculated timeout +func FuzzChannelCloseTimeout(f *testing.F) { + // Set multiple seeds in case fuzzing isn't explicitly used + for i := range [10]int{} { + f.Add(uint64(i), uint64(i), uint64(i), uint64(i*5)) + } + f.Fuzz(func(t *testing.T, l1BlockNum uint64, channelTimeout uint64, subSafetyMargin uint64, timeout uint64) { + // Create the channel builder + channelConfig := defaultTestChannelConfig + channelConfig.ChannelTimeout = channelTimeout + channelConfig.SubSafetyMargin = subSafetyMargin + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Check the timeout + cb.timeout = timeout + cb.FramePublished(l1BlockNum) + calculatedTimeout := l1BlockNum + channelTimeout - subSafetyMargin + if timeout > calculatedTimeout && calculatedTimeout != 0 { + cb.checkTimeout(calculatedTimeout) + require.ErrorIs(t, cb.FullErr(), ErrChannelTimeoutClose) + } else { + require.NoError(t, cb.FullErr()) + } + }) +} + +// FuzzChannelZeroCloseTimeout ensures that the channel builder has a [ErrChannelTimeoutClose] +// as long as the timeout constraint is met and the builder's timeout is set to zero. +func FuzzChannelZeroCloseTimeout(f *testing.F) { + // Set multiple seeds in case fuzzing isn't explicitly used + for i := range [10]int{} { + f.Add(uint64(i), uint64(i), uint64(i)) + } + f.Fuzz(func(t *testing.T, l1BlockNum uint64, channelTimeout uint64, subSafetyMargin uint64) { + // Create the channel builder + channelConfig := defaultTestChannelConfig + channelConfig.ChannelTimeout = channelTimeout + channelConfig.SubSafetyMargin = subSafetyMargin + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Check the timeout + cb.timeout = 0 + cb.FramePublished(l1BlockNum) + calculatedTimeout := l1BlockNum + channelTimeout - subSafetyMargin + cb.checkTimeout(calculatedTimeout) + if cb.timeout != 0 { + require.ErrorIs(t, cb.FullErr(), ErrChannelTimeoutClose) + } + }) +} + +// FuzzSeqWindowClose ensures that the channel builder has a [ErrSeqWindowClose] +// as long as the timeout constraint is met and the builder's timeout is greater than +// the calculated timeout +func FuzzSeqWindowClose(f *testing.F) { + // Set multiple seeds in case fuzzing isn't explicitly used + for i := range [10]int{} { + f.Add(uint64(i), uint64(i), uint64(i), uint64(i*5)) + } + f.Fuzz(func(t *testing.T, epochNum uint64, seqWindowSize uint64, subSafetyMargin uint64, timeout uint64) { + // Create the channel builder + channelConfig := defaultTestChannelConfig + channelConfig.SeqWindowSize = seqWindowSize + channelConfig.SubSafetyMargin = subSafetyMargin + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Check the timeout + cb.timeout = timeout + cb.updateSwTimeout(&derive.BatchData{ + BatchV1: derive.BatchV1{ + EpochNum: rollup.Epoch(epochNum), + }, + }) + calculatedTimeout := epochNum + seqWindowSize - subSafetyMargin + if timeout > calculatedTimeout && calculatedTimeout != 0 { + cb.checkTimeout(calculatedTimeout) + require.ErrorIs(t, cb.FullErr(), ErrSeqWindowClose) + } else { + require.NoError(t, cb.FullErr()) + } + }) +} + +// FuzzSeqWindowZeroTimeoutClose ensures that the channel builder has a [ErrSeqWindowClose] +// as long as the timeout constraint is met and the builder's timeout is set to zero. +func FuzzSeqWindowZeroTimeoutClose(f *testing.F) { + // Set multiple seeds in case fuzzing isn't explicitly used + for i := range [10]int{} { + f.Add(uint64(i), uint64(i), uint64(i)) + } + f.Fuzz(func(t *testing.T, epochNum uint64, seqWindowSize uint64, subSafetyMargin uint64) { + // Create the channel builder + channelConfig := defaultTestChannelConfig + channelConfig.SeqWindowSize = seqWindowSize + channelConfig.SubSafetyMargin = subSafetyMargin + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Check the timeout + cb.timeout = 0 + cb.updateSwTimeout(&derive.BatchData{ + BatchV1: derive.BatchV1{ + EpochNum: rollup.Epoch(epochNum), + }, + }) + calculatedTimeout := epochNum + seqWindowSize - subSafetyMargin + cb.checkTimeout(calculatedTimeout) + if cb.timeout != 0 { + require.ErrorIs(t, cb.FullErr(), ErrSeqWindowClose, "Sequence window close should be reached") + } + }) +} + +// TestChannelBuilder_NextFrame tests calling NextFrame on a ChannelBuilder with only one frame +func TestChannelBuilder_NextFrame(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Create a new channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Mock the internals of `channelBuilder.outputFrame` + // to construct a single frame + co := cb.co + var buf bytes.Buffer + fn, err := co.OutputFrame(&buf, channelConfig.MaxFrameSize) + require.NoError(t, err) + + // Push one frame into to the channel builder + expectedTx := txID{chID: co.ID(), frameNumber: fn} + expectedBytes := buf.Bytes() + frameData := frameData{ + id: frameID{ + chID: co.ID(), + frameNumber: fn, + }, + data: expectedBytes, + } + cb.PushFrame(frameData) + + // There should only be 1 frame in the channel builder + require.Equal(t, 1, cb.NumFrames()) + + // We should be able to increment to the next frame + constructedFrame := cb.NextFrame() + require.Equal(t, expectedTx, constructedFrame.id) + require.Equal(t, expectedBytes, constructedFrame.data) + require.Equal(t, 0, cb.NumFrames()) + + // The next call should panic since the length of frames is 0 + require.PanicsWithValue(t, "no next frame", func() { cb.NextFrame() }) +} + +// TestChannelBuilder_OutputWrongFramePanic tests that a panic is thrown when a frame is pushed with an invalid frame id +func TestChannelBuilder_OutputWrongFramePanic(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Construct a channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Mock the internals of `channelBuilder.outputFrame` + // to construct a single frame + co, err := derive.NewChannelOut() + require.NoError(t, err) + var buf bytes.Buffer + fn, err := co.OutputFrame(&buf, channelConfig.MaxFrameSize) + require.NoError(t, err) + + // The frame push should panic since we constructed a new channel out + // so the channel out id won't match + require.PanicsWithValue(t, "wrong channel", func() { + frame := frameData{ + id: frameID{ + chID: co.ID(), + frameNumber: fn, + }, + data: buf.Bytes(), + } + cb.PushFrame(frame) + }) +} + +// TestChannelBuilder_OutputFramesWorks tests the [ChannelBuilder] OutputFrames is successful. +func TestChannelBuilder_OutputFramesWorks(t *testing.T) { + channelConfig := defaultTestChannelConfig + channelConfig.MaxFrameSize = 24 + + // Construct the channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + require.False(t, cb.IsFull()) + require.Equal(t, 0, cb.NumFrames()) + + // Calling OutputFrames without having called [AddBlock] + // should return no error + require.NoError(t, cb.OutputFrames()) + + // There should be no ready bytes yet + require.Equal(t, 0, cb.co.ReadyBytes()) + + // Let's add a block + require.NoError(t, addMiniBlock(cb)) + require.NoError(t, cb.co.Flush()) + + // Check how many ready bytes + // There should be more than the max frame size ready + require.Greater(t, uint64(cb.co.ReadyBytes()), channelConfig.MaxFrameSize) + require.Equal(t, 0, cb.NumFrames()) + + // The channel should not be full + // but we want to output the frames for testing anyways + require.False(t, cb.IsFull()) + + // We should be able to output the frames + require.NoError(t, cb.OutputFrames()) + + // There should be many frames in the channel builder now + require.Greater(t, cb.NumFrames(), 1) + for _, frame := range cb.frames { + require.Len(t, frame.data, int(channelConfig.MaxFrameSize)) + } +} + +// TestChannelBuilder_MaxRLPBytesPerChannel tests the [channelBuilder.OutputFrames] +// function errors when the max RLP bytes per channel is reached. +func TestChannelBuilder_MaxRLPBytesPerChannel(t *testing.T) { + t.Parallel() + channelConfig := defaultTestChannelConfig + channelConfig.MaxFrameSize = derive.MaxRLPBytesPerChannel * 2 + channelConfig.TargetFrameSize = derive.MaxRLPBytesPerChannel * 2 + channelConfig.ApproxComprRatio = 1 + + // Construct the channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Add a block that overflows the [ChannelOut] + err = addTooManyBlocks(cb) + require.ErrorIs(t, err, derive.ErrTooManyRLPBytes) +} + +// TestChannelBuilder_OutputFramesMaxFrameIndex tests the [ChannelBuilder.OutputFrames] +// function errors when the max frame index is reached. +func TestChannelBuilder_OutputFramesMaxFrameIndex(t *testing.T) { + channelConfig := defaultTestChannelConfig + channelConfig.MaxFrameSize = 24 + channelConfig.TargetNumFrames = math.MaxInt + channelConfig.TargetFrameSize = 24 + channelConfig.ApproxComprRatio = 0 + + // Continuously add blocks until the max frame index is reached + // This should cause the [channelBuilder.OutputFrames] function + // to error + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + require.False(t, cb.IsFull()) + require.Equal(t, 0, cb.NumFrames()) + for { + lBlock := types.NewBlock(&types.Header{ + BaseFee: common.Big0, + Difficulty: common.Big0, + Number: common.Big0, + }, nil, nil, nil, trie.NewStackTrie(nil)) + l1InfoTx, _ := derive.L1InfoDeposit(0, lBlock, eth.SystemConfig{}, false) + txs := []*types.Transaction{types.NewTx(l1InfoTx)} + a := types.NewBlock(&types.Header{ + Number: big.NewInt(0), + }, txs, nil, nil, trie.NewStackTrie(nil)) + _, err = cb.AddBlock(a) + require.NoError(t, cb.co.Flush()) + if cb.IsFull() { + fullErr := cb.FullErr() + require.ErrorIs(t, fullErr, ErrMaxFrameIndex) + break + } + require.NoError(t, err) + _ = cb.OutputFrames() + // Flushing so we can construct new frames + _ = cb.co.Flush() + } +} + +// TestChannelBuilder_AddBlock tests the AddBlock function +func TestChannelBuilder_AddBlock(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Lower the max frame size so that we can batch + channelConfig.MaxFrameSize = 30 + + // Configure the Input Threshold params so we observe a full channel + channelConfig.TargetFrameSize = 30 + channelConfig.TargetNumFrames = 2 + channelConfig.ApproxComprRatio = 1 + + // Construct the channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Add a nonsense block to the channel builder + require.NoError(t, addMiniBlock(cb)) + require.NoError(t, cb.co.Flush()) + + // Check the fields reset in the AddBlock function + require.Equal(t, 74, cb.co.InputBytes()) + require.Equal(t, 1, len(cb.blocks)) + require.Equal(t, 0, len(cb.frames)) + require.True(t, cb.IsFull()) + + // Since the channel output is full, the next call to AddBlock + // should return the channel out full error + require.ErrorIs(t, addMiniBlock(cb), ErrInputTargetReached) +} + +// TestChannelBuilder_Reset tests the [Reset] function +func TestChannelBuilder_Reset(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Lower the max frame size so that we can batch + channelConfig.MaxFrameSize = 24 + + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Add a nonsense block to the channel builder + require.NoError(t, addMiniBlock(cb)) + require.NoError(t, cb.co.Flush()) + + // Check the fields reset in the Reset function + require.Equal(t, 1, len(cb.blocks)) + require.Equal(t, 0, len(cb.frames)) + // Timeout should be updated in the AddBlock internal call to `updateSwTimeout` + timeout := uint64(100) + cb.cfg.SeqWindowSize - cb.cfg.SubSafetyMargin + require.Equal(t, timeout, cb.timeout) + require.NoError(t, cb.fullErr) + + // Output frames so we can set the channel builder frames + require.NoError(t, cb.OutputFrames()) + + // Add another block to increment the block count + require.NoError(t, addMiniBlock(cb)) + require.NoError(t, cb.co.Flush()) + + // Check the fields reset in the Reset function + require.Equal(t, 2, len(cb.blocks)) + require.Greater(t, len(cb.frames), 1) + require.Equal(t, timeout, cb.timeout) + require.NoError(t, cb.fullErr) + + // Reset the channel builder + require.NoError(t, cb.Reset()) + + // Check the fields reset in the Reset function + require.Equal(t, 0, len(cb.blocks)) + require.Equal(t, 0, len(cb.frames)) + require.Equal(t, uint64(0), cb.timeout) + require.NoError(t, cb.fullErr) + require.Equal(t, 0, cb.co.InputBytes()) + require.Equal(t, 0, cb.co.ReadyBytes()) +} + +// TestBuilderRegisterL1Block tests the RegisterL1Block function +func TestBuilderRegisterL1Block(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Construct the channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Assert params modified in RegisterL1Block + require.Equal(t, uint64(1), channelConfig.MaxChannelDuration) + require.Equal(t, uint64(0), cb.timeout) + + // Register a new L1 block + cb.RegisterL1Block(uint64(100)) + + // Assert params modified in RegisterL1Block + require.Equal(t, uint64(1), channelConfig.MaxChannelDuration) + require.Equal(t, uint64(101), cb.timeout) +} + +// TestBuilderRegisterL1BlockZeroMaxChannelDuration tests the RegisterL1Block function +func TestBuilderRegisterL1BlockZeroMaxChannelDuration(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Set the max channel duration to 0 + channelConfig.MaxChannelDuration = 0 + + // Construct the channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Assert params modified in RegisterL1Block + require.Equal(t, uint64(0), channelConfig.MaxChannelDuration) + require.Equal(t, uint64(0), cb.timeout) + + // Register a new L1 block + cb.RegisterL1Block(uint64(100)) + + // Since the max channel duration is set to 0, + // the L1 block register should not update the timeout + require.Equal(t, uint64(0), channelConfig.MaxChannelDuration) + require.Equal(t, uint64(0), cb.timeout) +} + +// TestFramePublished tests the FramePublished function +func TestFramePublished(t *testing.T) { + channelConfig := defaultTestChannelConfig + + // Construct the channel builder + cb, err := newChannelBuilder(channelConfig) + require.NoError(t, err) + + // Let's say the block number is fed in as 100 + // and the channel timeout is 1000 + l1BlockNum := uint64(100) + cb.cfg.ChannelTimeout = uint64(1000) + cb.cfg.SubSafetyMargin = 100 + + // Then the frame published will update the timeout + cb.FramePublished(l1BlockNum) + + // Now the timeout will be 1000 + require.Equal(t, uint64(1000), cb.timeout) +} + +func TestChannelBuilder_InputBytes(t *testing.T) { + require := require.New(t) + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + cb, _ := defaultChannelBuilderSetup(t) + + require.Zero(cb.InputBytes()) + + var l int + for i := 0; i < 5; i++ { + block := newMiniL2Block(rng.Intn(32)) + l += blockBatchRlpSize(t, block) + + _, err := cb.AddBlock(block) + require.NoError(err) + require.Equal(cb.InputBytes(), l) + } +} + +func TestChannelBuilder_OutputBytes(t *testing.T) { + require := require.New(t) + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + cfg := defaultTestChannelConfig + cfg.TargetFrameSize = 1000 + cfg.MaxFrameSize = 1000 + cfg.TargetNumFrames = 16 + cfg.ApproxComprRatio = 1.0 + cb, err := newChannelBuilder(cfg) + require.NoError(err, "newChannelBuilder") + + require.Zero(cb.OutputBytes()) + + for { + block, _ := dtest.RandomL2Block(rng, rng.Intn(32)) + _, err := cb.AddBlock(block) + if errors.Is(err, ErrInputTargetReached) { + break + } + require.NoError(err) + } + + require.NoError(cb.OutputFrames()) + require.True(cb.IsFull()) + require.Greater(cb.NumFrames(), 1) + + var flen int + for cb.HasFrame() { + f := cb.NextFrame() + flen += len(f.data) + } + + require.Equal(cb.OutputBytes(), flen) +} + +func defaultChannelBuilderSetup(t *testing.T) (*channelBuilder, ChannelConfig) { + t.Helper() + cfg := defaultTestChannelConfig + cb, err := newChannelBuilder(cfg) + require.NoError(t, err, "newChannelBuilder") + return cb, cfg +} + +func blockBatchRlpSize(t *testing.T, b *types.Block) int { + t.Helper() + batch, _, err := derive.BlockToBatch(b) + require.NoError(t, err) + var buf bytes.Buffer + require.NoError(t, batch.EncodeRLP(&buf), "RLP-encoding batch") + return buf.Len() +} diff --git a/op-batcher/batcher/channel_config_test.go b/op-batcher/batcher/channel_config_test.go new file mode 100644 index 0000000000000..1b2cb202d7356 --- /dev/null +++ b/op-batcher/batcher/channel_config_test.go @@ -0,0 +1,129 @@ +package batcher_test + +import ( + "math" + "testing" + + "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/stretchr/testify/require" +) + +// TestInputThreshold tests the [ChannelConfig.InputThreshold] +// function using a table-driven testing approach. +func TestInputThreshold(t *testing.T) { + type testInput struct { + TargetFrameSize uint64 + TargetNumFrames int + ApproxComprRatio float64 + } + type test struct { + input testInput + assertion func(uint64) + } + + // Construct test cases that test the boundary conditions + tests := []test{ + { + input: testInput{ + TargetFrameSize: 1, + TargetNumFrames: 1, + ApproxComprRatio: 0.4, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(2), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 1, + TargetNumFrames: 100000, + ApproxComprRatio: 0.4, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(250_000), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 1, + TargetNumFrames: 1, + ApproxComprRatio: 1, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(1), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 1, + TargetNumFrames: 1, + ApproxComprRatio: 2, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(0), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 100000, + TargetNumFrames: 1, + ApproxComprRatio: 0.4, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(250_000), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 1, + TargetNumFrames: 100000, + ApproxComprRatio: 0.4, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(250_000), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 100000, + TargetNumFrames: 100000, + ApproxComprRatio: 0.4, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(25_000_000_000), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 1, + TargetNumFrames: 1, + ApproxComprRatio: 0.000001, + }, + assertion: func(output uint64) { + require.Equal(t, uint64(1_000_000), output) + }, + }, + { + input: testInput{ + TargetFrameSize: 0, + TargetNumFrames: 0, + ApproxComprRatio: 0, + }, + assertion: func(output uint64) { + // Need to allow for NaN depending on the machine architecture + require.True(t, output == uint64(0) || output == uint64(math.NaN())) + }, + }, + } + + // Validate each test case + for _, tt := range tests { + config := batcher.ChannelConfig{ + TargetFrameSize: tt.input.TargetFrameSize, + TargetNumFrames: tt.input.TargetNumFrames, + ApproxComprRatio: tt.input.ApproxComprRatio, + } + got := config.InputThreshold() + tt.assertion(got) + } +} diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index c2c64dcce2faa..270d603953464 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -6,7 +6,9 @@ import ( "io" "math" + "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" @@ -22,8 +24,9 @@ var ErrReorg = errors.New("block does not extend existing chain") // channel. // Functions on channelManager are not safe for concurrent access. type channelManager struct { - log log.Logger - cfg ChannelConfig + log log.Logger + metr metrics.Metricer + cfg ChannelConfig // All blocks since the last request for new tx data. blocks []*types.Block @@ -40,10 +43,12 @@ type channelManager struct { confirmedTransactions map[txID]eth.BlockID } -func NewChannelManager(log log.Logger, cfg ChannelConfig) *channelManager { +func NewChannelManager(log log.Logger, metr metrics.Metricer, cfg ChannelConfig) *channelManager { return &channelManager{ - log: log, - cfg: cfg, + log: log, + metr: metr, + cfg: cfg, + pendingTransactions: make(map[txID]txData), confirmedTransactions: make(map[txID]eth.BlockID), } @@ -71,6 +76,8 @@ func (s *channelManager) TxFailed(id txID) { } else { s.log.Warn("unknown transaction marked as failed", "id", id) } + + s.metr.RecordBatchTxFailed() } // TxConfirmed marks a transaction as confirmed on L1. Unfortunately even if all frames in @@ -78,7 +85,8 @@ func (s *channelManager) TxFailed(id txID) { // resubmitted. // This function may reset the pending channel if the pending channel has timed out. func (s *channelManager) TxConfirmed(id txID, inclusionBlock eth.BlockID) { - s.log.Trace("marked transaction as confirmed", "id", id, "block", inclusionBlock) + s.metr.RecordBatchTxSubmitted() + s.log.Debug("marked transaction as confirmed", "id", id, "block", inclusionBlock) if _, ok := s.pendingTransactions[id]; !ok { s.log.Warn("unknown transaction marked as confirmed", "id", id, "block", inclusionBlock) // TODO: This can occur if we clear the channel while there are still pending transactions @@ -92,13 +100,15 @@ func (s *channelManager) TxConfirmed(id txID, inclusionBlock eth.BlockID) { // If this channel timed out, put the pending blocks back into the local saved blocks // and then reset this state so it can try to build a new channel. if s.pendingChannelIsTimedOut() { - s.log.Warn("Channel timed out", "chID", s.pendingChannel.ID()) + s.metr.RecordChannelTimedOut(s.pendingChannel.ID()) + s.log.Warn("Channel timed out", "id", s.pendingChannel.ID()) s.blocks = append(s.pendingChannel.Blocks(), s.blocks...) s.clearPendingChannel() } // If we are done with this channel, record that. if s.pendingChannelIsFullySubmitted() { - s.log.Info("Channel is fully submitted", "chID", s.pendingChannel.ID()) + s.metr.RecordChannelFullySubmitted(s.pendingChannel.ID()) + s.log.Info("Channel is fully submitted", "id", s.pendingChannel.ID()) s.clearPendingChannel() } } @@ -194,8 +204,8 @@ func (s *channelManager) TxData(l1Head eth.BlockID) (txData, error) { // all pending blocks be included in this channel for submission. s.registerL1Block(l1Head) - if err := s.pendingChannel.OutputFrames(); err != nil { - return txData{}, fmt.Errorf("creating frames with channel builder: %w", err) + if err := s.outputFrames(); err != nil { + return txData{}, err } return s.nextTxData() @@ -211,7 +221,11 @@ func (s *channelManager) ensurePendingChannel(l1Head eth.BlockID) error { return fmt.Errorf("creating new channel: %w", err) } s.pendingChannel = cb - s.log.Info("Created channel", "chID", cb.ID(), "l1Head", l1Head) + s.log.Info("Created channel", + "id", cb.ID(), + "l1Head", l1Head, + "blocks_pending", len(s.blocks)) + s.metr.RecordChannelOpened(cb.ID(), len(s.blocks)) return nil } @@ -229,28 +243,27 @@ func (s *channelManager) registerL1Block(l1Head eth.BlockID) { // processBlocks adds blocks from the blocks queue to the pending channel until // either the queue got exhausted or the channel is full. func (s *channelManager) processBlocks() error { - var blocksAdded int - var _chFullErr *ChannelFullError // throw away, just for type checking + var ( + blocksAdded int + _chFullErr *ChannelFullError // throw away, just for type checking + latestL2ref eth.L2BlockRef + ) for i, block := range s.blocks { - if err := s.pendingChannel.AddBlock(block); errors.As(err, &_chFullErr) { + l1info, err := s.pendingChannel.AddBlock(block) + if errors.As(err, &_chFullErr) { // current block didn't get added because channel is already full break } else if err != nil { return fmt.Errorf("adding block[%d] to channel builder: %w", i, err) } blocksAdded += 1 + latestL2ref = l2BlockRefFromBlockAndL1Info(block, l1info) // current block got added but channel is now full if s.pendingChannel.IsFull() { break } } - s.log.Debug("Added blocks to channel", - "blocks_added", blocksAdded, - "channel_full", s.pendingChannel.IsFull(), - "blocks_pending", len(s.blocks)-blocksAdded, - "input_bytes", s.pendingChannel.InputBytes(), - ) if blocksAdded == len(s.blocks) { // all blocks processed, reuse slice s.blocks = s.blocks[:0] @@ -258,6 +271,53 @@ func (s *channelManager) processBlocks() error { // remove processed blocks s.blocks = s.blocks[blocksAdded:] } + + s.metr.RecordL2BlocksAdded(latestL2ref, + blocksAdded, + len(s.blocks), + s.pendingChannel.InputBytes(), + s.pendingChannel.ReadyBytes()) + s.log.Debug("Added blocks to channel", + "blocks_added", blocksAdded, + "blocks_pending", len(s.blocks), + "channel_full", s.pendingChannel.IsFull(), + "input_bytes", s.pendingChannel.InputBytes(), + "ready_bytes", s.pendingChannel.ReadyBytes(), + ) + return nil +} + +func (s *channelManager) outputFrames() error { + if err := s.pendingChannel.OutputFrames(); err != nil { + return fmt.Errorf("creating frames with channel builder: %w", err) + } + if !s.pendingChannel.IsFull() { + return nil + } + + inBytes, outBytes := s.pendingChannel.InputBytes(), s.pendingChannel.OutputBytes() + s.metr.RecordChannelClosed( + s.pendingChannel.ID(), + len(s.blocks), + s.pendingChannel.NumFrames(), + inBytes, + outBytes, + s.pendingChannel.FullErr(), + ) + + var comprRatio float64 + if inBytes > 0 { + comprRatio = float64(outBytes) / float64(inBytes) + } + s.log.Info("Channel closed", + "id", s.pendingChannel.ID(), + "blocks_pending", len(s.blocks), + "num_frames", s.pendingChannel.NumFrames(), + "input_bytes", inBytes, + "output_bytes", outBytes, + "full_reason", s.pendingChannel.FullErr(), + "compr_ratio", comprRatio, + ) return nil } @@ -273,3 +333,14 @@ func (s *channelManager) AddL2Block(block *types.Block) error { return nil } + +func l2BlockRefFromBlockAndL1Info(block *types.Block, l1info derive.L1BlockInfo) eth.L2BlockRef { + return eth.L2BlockRef{ + Hash: block.Hash(), + Number: block.NumberU64(), + ParentHash: block.ParentHash(), + Time: block.Time(), + L1Origin: eth.BlockID{Hash: l1info.BlockHash, Number: l1info.Number}, + SequenceNumber: l1info.SequenceNumber, + } +} diff --git a/op-batcher/batcher/channel_manager_test.go b/op-batcher/batcher/channel_manager_test.go index 41e1b52c3a7c5..ec04fb1f9dd00 100644 --- a/op-batcher/batcher/channel_manager_test.go +++ b/op-batcher/batcher/channel_manager_test.go @@ -1,4 +1,4 @@ -package batcher_test +package batcher import ( "io" @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" derivetest "github.com/ethereum-optimism/optimism/op-node/rollup/derive/test" @@ -15,15 +15,58 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie" "github.com/stretchr/testify/require" ) +// TestPendingChannelTimeout tests that the channel manager +// correctly identifies when a pending channel is timed out. +func TestPendingChannelTimeout(t *testing.T) { + // Create a new channel manager with a ChannelTimeout + log := testlog.Logger(t, log.LvlCrit) + m := NewChannelManager(log, metrics.NoopMetrics, ChannelConfig{ + ChannelTimeout: 100, + }) + + // Pending channel is nil so is cannot be timed out + timeout := m.pendingChannelIsTimedOut() + require.False(t, timeout) + + // Set the pending channel + require.NoError(t, m.ensurePendingChannel(eth.BlockID{})) + + // There are no confirmed transactions so + // the pending channel cannot be timed out + timeout = m.pendingChannelIsTimedOut() + require.False(t, timeout) + + // Manually set a confirmed transactions + // To avoid other methods clearing state + m.confirmedTransactions[frameID{frameNumber: 0}] = eth.BlockID{Number: 0} + m.confirmedTransactions[frameID{frameNumber: 1}] = eth.BlockID{Number: 99} + + // Since the ChannelTimeout is 100, the + // pending channel should not be timed out + timeout = m.pendingChannelIsTimedOut() + require.False(t, timeout) + + // Add a confirmed transaction with a higher number + // than the ChannelTimeout + m.confirmedTransactions[frameID{ + frameNumber: 2, + }] = eth.BlockID{ + Number: 101, + } + + // Now the pending channel should be timed out + timeout = m.pendingChannelIsTimedOut() + require.True(t, timeout) +} + // TestChannelManagerReturnsErrReorg ensures that the channel manager // detects a reorg when it has cached L1 blocks. func TestChannelManagerReturnsErrReorg(t *testing.T) { log := testlog.Logger(t, log.LvlCrit) - m := batcher.NewChannelManager(log, batcher.ChannelConfig{}) + m := NewChannelManager(log, metrics.NoopMetrics, ChannelConfig{}) a := types.NewBlock(&types.Header{ Number: big.NewInt(0), @@ -41,68 +84,261 @@ func TestChannelManagerReturnsErrReorg(t *testing.T) { ParentHash: common.Hash{0xff}, }, nil, nil, nil, nil) - err := m.AddL2Block(a) - require.NoError(t, err) - err = m.AddL2Block(b) - require.NoError(t, err) - err = m.AddL2Block(c) - require.NoError(t, err) - err = m.AddL2Block(x) - require.ErrorIs(t, err, batcher.ErrReorg) + require.NoError(t, m.AddL2Block(a)) + require.NoError(t, m.AddL2Block(b)) + require.NoError(t, m.AddL2Block(c)) + require.ErrorIs(t, m.AddL2Block(x), ErrReorg) + + require.Equal(t, []*types.Block{a, b, c}, m.blocks) } // TestChannelManagerReturnsErrReorgWhenDrained ensures that the channel manager // detects a reorg even if it does not have any blocks inside it. func TestChannelManagerReturnsErrReorgWhenDrained(t *testing.T) { log := testlog.Logger(t, log.LvlCrit) - m := batcher.NewChannelManager(log, batcher.ChannelConfig{ - TargetFrameSize: 0, - MaxFrameSize: 120_000, + m := NewChannelManager(log, metrics.NoopMetrics, + ChannelConfig{ + TargetFrameSize: 0, + MaxFrameSize: 120_000, + ApproxComprRatio: 1.0, + }) + + a := newMiniL2Block(0) + x := newMiniL2BlockWithNumberParent(0, big.NewInt(1), common.Hash{0xff}) + + require.NoError(t, m.AddL2Block(a)) + + _, err := m.TxData(eth.BlockID{}) + require.NoError(t, err) + _, err = m.TxData(eth.BlockID{}) + require.ErrorIs(t, err, io.EOF) + + require.ErrorIs(t, m.AddL2Block(x), ErrReorg) +} + +// TestChannelManagerNextTxData checks the nextTxData function. +func TestChannelManagerNextTxData(t *testing.T) { + log := testlog.Logger(t, log.LvlCrit) + m := NewChannelManager(log, metrics.NoopMetrics, ChannelConfig{}) + + // Nil pending channel should return EOF + returnedTxData, err := m.nextTxData() + require.ErrorIs(t, err, io.EOF) + require.Equal(t, txData{}, returnedTxData) + + // Set the pending channel + // The nextTxData function should still return EOF + // since the pending channel has no frames + require.NoError(t, m.ensurePendingChannel(eth.BlockID{})) + returnedTxData, err = m.nextTxData() + require.ErrorIs(t, err, io.EOF) + require.Equal(t, txData{}, returnedTxData) + + // Manually push a frame into the pending channel + channelID := m.pendingChannel.ID() + frame := frameData{ + data: []byte{}, + id: frameID{ + chID: channelID, + frameNumber: uint16(0), + }, + } + m.pendingChannel.PushFrame(frame) + require.Equal(t, 1, m.pendingChannel.NumFrames()) + + // Now the nextTxData function should return the frame + returnedTxData, err = m.nextTxData() + expectedTxData := txData{frame} + expectedChannelID := expectedTxData.ID() + require.NoError(t, err) + require.Equal(t, expectedTxData, returnedTxData) + require.Equal(t, 0, m.pendingChannel.NumFrames()) + require.Equal(t, expectedTxData, m.pendingTransactions[expectedChannelID]) +} + +// TestChannelManager_Clear tests clearing the channel manager. +func TestChannelManager_Clear(t *testing.T) { + require := require.New(t) + + // Create a channel manager + log := testlog.Logger(t, log.LvlCrit) + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + m := NewChannelManager(log, metrics.NoopMetrics, ChannelConfig{ + // Need to set the channel timeout here so we don't clear pending + // channels on confirmation. This would result in [TxConfirmed] + // clearing confirmed transactions, and reseting the pendingChannels map + ChannelTimeout: 10, + // Have to set the max frame size here otherwise the channel builder would not + // be able to output any frames + MaxFrameSize: 24, + TargetFrameSize: 24, ApproxComprRatio: 1.0, }) - l1Block := types.NewBlock(&types.Header{ - BaseFee: big.NewInt(10), - Difficulty: common.Big0, - Number: big.NewInt(100), - }, nil, nil, nil, trie.NewStackTrie(nil)) - l1InfoTx, err := derive.L1InfoDeposit(0, l1Block, eth.SystemConfig{}, false) - require.NoError(t, err) - txs := []*types.Transaction{types.NewTx(l1InfoTx)} - a := types.NewBlock(&types.Header{ - Number: big.NewInt(0), - }, txs, nil, nil, trie.NewStackTrie(nil)) - x := types.NewBlock(&types.Header{ + // Channel Manager state should be empty by default + require.Empty(m.blocks) + require.Equal(common.Hash{}, m.tip) + require.Nil(m.pendingChannel) + require.Empty(m.pendingTransactions) + require.Empty(m.confirmedTransactions) + + // Add a block to the channel manager + a, _ := derivetest.RandomL2Block(rng, 4) + newL1Tip := a.Hash() + l1BlockID := eth.BlockID{ + Hash: a.Hash(), + Number: a.NumberU64(), + } + require.NoError(m.AddL2Block(a)) + + // Make sure there is a channel builder + require.NoError(m.ensurePendingChannel(l1BlockID)) + require.NotNil(m.pendingChannel) + require.Len(m.confirmedTransactions, 0) + + // Process the blocks + // We should have a pending channel with 1 frame + // and no more blocks since processBlocks consumes + // the list + require.NoError(m.processBlocks()) + require.NoError(m.pendingChannel.co.Flush()) + require.NoError(m.pendingChannel.OutputFrames()) + _, err := m.nextTxData() + require.NoError(err) + require.Len(m.blocks, 0) + require.Equal(newL1Tip, m.tip) + require.Len(m.pendingTransactions, 1) + + // Add a new block so we can test clearing + // the channel manager with a full state + b := types.NewBlock(&types.Header{ Number: big.NewInt(1), - ParentHash: common.Hash{0xff}, - }, txs, nil, nil, trie.NewStackTrie(nil)) + ParentHash: a.Hash(), + }, nil, nil, nil, nil) + require.NoError(m.AddL2Block(b)) + require.Len(m.blocks, 1) + require.Equal(b.Hash(), m.tip) + + // Clear the channel manager + m.Clear() - err = m.AddL2Block(a) + // Check that the entire channel manager state cleared + require.Empty(m.blocks) + require.Equal(common.Hash{}, m.tip) + require.Nil(m.pendingChannel) + require.Empty(m.pendingTransactions) + require.Empty(m.confirmedTransactions) +} + +// TestChannelManagerTxConfirmed checks the [ChannelManager.TxConfirmed] function. +func TestChannelManagerTxConfirmed(t *testing.T) { + // Create a channel manager + log := testlog.Logger(t, log.LvlCrit) + m := NewChannelManager(log, metrics.NoopMetrics, ChannelConfig{ + // Need to set the channel timeout here so we don't clear pending + // channels on confirmation. This would result in [TxConfirmed] + // clearing confirmed transactions, and reseting the pendingChannels map + ChannelTimeout: 10, + }) + + // Let's add a valid pending transaction to the channel manager + // So we can demonstrate that TxConfirmed's correctness + require.NoError(t, m.ensurePendingChannel(eth.BlockID{})) + channelID := m.pendingChannel.ID() + frame := frameData{ + data: []byte{}, + id: frameID{ + chID: channelID, + frameNumber: uint16(0), + }, + } + m.pendingChannel.PushFrame(frame) + require.Equal(t, 1, m.pendingChannel.NumFrames()) + returnedTxData, err := m.nextTxData() + expectedTxData := txData{frame} + expectedChannelID := expectedTxData.ID() require.NoError(t, err) + require.Equal(t, expectedTxData, returnedTxData) + require.Equal(t, 0, m.pendingChannel.NumFrames()) + require.Equal(t, expectedTxData, m.pendingTransactions[expectedChannelID]) + require.Len(t, m.pendingTransactions, 1) - _, err = m.TxData(eth.BlockID{}) + // An unknown pending transaction should not be marked as confirmed + // and should not be removed from the pending transactions map + actualChannelID := m.pendingChannel.ID() + unknownChannelID := derive.ChannelID([derive.ChannelIDLength]byte{0x69}) + require.NotEqual(t, actualChannelID, unknownChannelID) + unknownTxID := frameID{chID: unknownChannelID, frameNumber: 0} + blockID := eth.BlockID{Number: 0, Hash: common.Hash{0x69}} + m.TxConfirmed(unknownTxID, blockID) + require.Empty(t, m.confirmedTransactions) + require.Len(t, m.pendingTransactions, 1) + + // Now let's mark the pending transaction as confirmed + // and check that it is removed from the pending transactions map + // and added to the confirmed transactions map + m.TxConfirmed(expectedChannelID, blockID) + require.Empty(t, m.pendingTransactions) + require.Len(t, m.confirmedTransactions, 1) + require.Equal(t, blockID, m.confirmedTransactions[expectedChannelID]) +} + +// TestChannelManagerTxFailed checks the [ChannelManager.TxFailed] function. +func TestChannelManagerTxFailed(t *testing.T) { + // Create a channel manager + log := testlog.Logger(t, log.LvlCrit) + m := NewChannelManager(log, metrics.NoopMetrics, ChannelConfig{}) + + // Let's add a valid pending transaction to the channel + // manager so we can demonstrate correctness + require.NoError(t, m.ensurePendingChannel(eth.BlockID{})) + channelID := m.pendingChannel.ID() + frame := frameData{ + data: []byte{}, + id: frameID{ + chID: channelID, + frameNumber: uint16(0), + }, + } + m.pendingChannel.PushFrame(frame) + require.Equal(t, 1, m.pendingChannel.NumFrames()) + returnedTxData, err := m.nextTxData() + expectedTxData := txData{frame} + expectedChannelID := expectedTxData.ID() require.NoError(t, err) - _, err = m.TxData(eth.BlockID{}) - require.ErrorIs(t, err, io.EOF) + require.Equal(t, expectedTxData, returnedTxData) + require.Equal(t, 0, m.pendingChannel.NumFrames()) + require.Equal(t, expectedTxData, m.pendingTransactions[expectedChannelID]) + require.Len(t, m.pendingTransactions, 1) - err = m.AddL2Block(x) - require.ErrorIs(t, err, batcher.ErrReorg) + // Trying to mark an unknown pending transaction as failed + // shouldn't modify state + m.TxFailed(frameID{}) + require.Equal(t, 0, m.pendingChannel.NumFrames()) + require.Equal(t, expectedTxData, m.pendingTransactions[expectedChannelID]) + + // Now we still have a pending transaction + // Let's mark it as failed + m.TxFailed(expectedChannelID) + require.Empty(t, m.pendingTransactions) + // There should be a frame in the pending channel now + require.Equal(t, 1, m.pendingChannel.NumFrames()) } func TestChannelManager_TxResend(t *testing.T) { require := require.New(t) rng := rand.New(rand.NewSource(time.Now().UnixNano())) log := testlog.Logger(t, log.LvlError) - m := batcher.NewChannelManager(log, batcher.ChannelConfig{ - TargetFrameSize: 0, - MaxFrameSize: 120_000, - ApproxComprRatio: 1.0, - }) + m := NewChannelManager(log, metrics.NoopMetrics, + ChannelConfig{ + TargetFrameSize: 0, + MaxFrameSize: 120_000, + ApproxComprRatio: 1.0, + }) a, _ := derivetest.RandomL2Block(rng, 4) - err := m.AddL2Block(a) - require.NoError(err) + require.NoError(m.AddL2Block(a)) txdata0, err := m.TxData(eth.BlockID{}) require.NoError(err) diff --git a/op-batcher/batcher/config.go b/op-batcher/batcher/config.go index bf9bfb87e00ff..1f26b4bdf8c67 100644 --- a/op-batcher/batcher/config.go +++ b/op-batcher/batcher/config.go @@ -9,6 +9,7 @@ import ( "github.com/urfave/cli" "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-batcher/rpc" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/sources" @@ -20,21 +21,35 @@ import ( ) type Config struct { - log log.Logger - L1Client *ethclient.Client - L2Client *ethclient.Client - RollupNode *sources.RollupClient - PollInterval time.Duration + log log.Logger + metr metrics.Metricer + L1Client *ethclient.Client + L2Client *ethclient.Client + RollupNode *sources.RollupClient + + PollInterval time.Duration + From common.Address + TxManagerConfig txmgr.Config - From common.Address // RollupConfig is queried at startup Rollup *rollup.Config - // Channel creation parameters + // Channel builder parameters Channel ChannelConfig } +// Check ensures that the [Config] is valid. +func (c *Config) Check() error { + if err := c.Rollup.Check(); err != nil { + return err + } + if err := c.Channel.Check(); err != nil { + return err + } + return nil +} + type CLIConfig struct { /* Required Params */ diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index 31031cc08dd33..4e7d8f5780bc8 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -10,7 +10,9 @@ import ( "sync" "time" + "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" "github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum/go-ethereum/core/types" @@ -34,13 +36,14 @@ type BatchSubmitter struct { // lastStoredBlock is the last block loaded into `state`. If it is empty it should be set to the l2 safe head. lastStoredBlock eth.BlockID + lastL1Tip eth.L1BlockRef state *channelManager } // NewBatchSubmitterFromCLIConfig initializes the BatchSubmitter, gathering any resources // that will be needed during operation. -func NewBatchSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger) (*BatchSubmitter, error) { +func NewBatchSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger, m metrics.Metricer) (*BatchSubmitter, error) { ctx := context.Background() signer, fromAddress, err := opcrypto.SignerFactoryFromConfig(l, cfg.PrivateKey, cfg.Mnemonic, cfg.SequencerHDPath, cfg.SignerConfig) @@ -99,12 +102,17 @@ func NewBatchSubmitterFromCLIConfig(cfg CLIConfig, l log.Logger) (*BatchSubmitte }, } - return NewBatchSubmitter(ctx, batcherCfg, l) + // Validate the batcher config + if err := batcherCfg.Check(); err != nil { + return nil, err + } + + return NewBatchSubmitter(ctx, batcherCfg, l, m) } // NewBatchSubmitter initializes the BatchSubmitter, gathering any resources // that will be needed during operation. -func NewBatchSubmitter(ctx context.Context, cfg Config, l log.Logger) (*BatchSubmitter, error) { +func NewBatchSubmitter(ctx context.Context, cfg Config, l log.Logger, m metrics.Metricer) (*BatchSubmitter, error) { balance, err := cfg.L1Client.BalanceAt(ctx, cfg.From, nil) if err != nil { return nil, err @@ -113,12 +121,14 @@ func NewBatchSubmitter(ctx context.Context, cfg Config, l log.Logger) (*BatchSub cfg.log = l cfg.log.Info("creating batch submitter", "submitter_addr", cfg.From, "submitter_bal", balance) + cfg.metr = m + return &BatchSubmitter{ Config: cfg, txMgr: NewTransactionManager(l, cfg.TxManagerConfig, cfg.Rollup.BatchInboxAddress, cfg.Rollup.L1ChainID, cfg.From, cfg.L1Client), - state: NewChannelManager(l, cfg.Channel), + state: NewChannelManager(l, m, cfg.Channel), }, nil } @@ -182,13 +192,16 @@ func (l *BatchSubmitter) Stop() error { func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) { start, end, err := l.calculateL2BlockRangeToStore(ctx) if err != nil { - l.log.Trace("was not able to calculate L2 block range", "err", err) + l.log.Warn("Error calculating L2 block range", "err", err) + return + } else if start.Number == end.Number { return } + var latestBlock *types.Block // Add all blocks to "state" for i := start.Number + 1; i < end.Number+1; i++ { - id, err := l.loadBlockIntoState(ctx, i) + block, err := l.loadBlockIntoState(ctx, i) if errors.Is(err, ErrReorg) { l.log.Warn("Found L2 reorg", "block_number", i) l.state.Clear() @@ -198,24 +211,34 @@ func (l *BatchSubmitter) loadBlocksIntoState(ctx context.Context) { l.log.Warn("failed to load block into state", "err", err) return } - l.lastStoredBlock = id + l.lastStoredBlock = eth.ToBlockID(block) + latestBlock = block + } + + l2ref, err := derive.L2BlockToBlockRef(latestBlock, &l.Rollup.Genesis) + if err != nil { + l.log.Warn("Invalid L2 block loaded into state", "err", err) + return } + + l.metr.RecordL2BlocksLoaded(l2ref) } // loadBlockIntoState fetches & stores a single block into `state`. It returns the block it loaded. -func (l *BatchSubmitter) loadBlockIntoState(ctx context.Context, blockNumber uint64) (eth.BlockID, error) { +func (l *BatchSubmitter) loadBlockIntoState(ctx context.Context, blockNumber uint64) (*types.Block, error) { ctx, cancel := context.WithTimeout(ctx, networkTimeout) + defer cancel() block, err := l.L2Client.BlockByNumber(ctx, new(big.Int).SetUint64(blockNumber)) - cancel() if err != nil { - return eth.BlockID{}, err + return nil, fmt.Errorf("getting L2 block: %w", err) } + if err := l.state.AddL2Block(block); err != nil { - return eth.BlockID{}, err + return nil, fmt.Errorf("adding L2 block to state: %w", err) } - id := eth.ToBlockID(block) - l.log.Info("added L2 block to local state", "block", id, "tx_count", len(block.Transactions()), "time", block.Time()) - return id, nil + + l.log.Info("added L2 block to local state", "block", eth.ToBlockID(block), "tx_count", len(block.Transactions()), "time", block.Time()) + return block, nil } // calculateL2BlockRangeToStore determines the range (start,end] that should be loaded into the local state. @@ -278,6 +301,7 @@ func (l *BatchSubmitter) loop() { l.log.Error("Failed to query L1 tip", "error", err) break } + l.recordL1Tip(l1tip) // Collect next transaction data txdata, err := l.state.TxData(l1tip.ID()) @@ -311,6 +335,14 @@ func (l *BatchSubmitter) loop() { } } +func (l *BatchSubmitter) recordL1Tip(l1tip eth.L1BlockRef) { + if l.lastL1Tip == l1tip { + return + } + l.lastL1Tip = l1tip + l.metr.RecordLatestL1Block(l1tip) +} + func (l *BatchSubmitter) recordFailedTx(id txID, err error) { l.log.Warn("Failed to send transaction", "err", err) l.state.TxFailed(id) diff --git a/op-batcher/batcher/txmgr.go b/op-batcher/batcher/txmgr.go index 25637a39377d2..bd00626a48c64 100644 --- a/op-batcher/batcher/txmgr.go +++ b/op-batcher/batcher/txmgr.go @@ -56,7 +56,7 @@ func (t *TransactionManager) SendTransaction(ctx context.Context, data []byte) ( return nil, fmt.Errorf("failed to create tx: %w", err) } - ctx, cancel := context.WithTimeout(ctx, 100*time.Second) // TODO: Select a timeout that makes sense here. + ctx, cancel := context.WithTimeout(ctx, 10*time.Minute) // TODO: Select a timeout that makes sense here. defer cancel() if receipt, err := t.txMgr.Send(ctx, tx); err != nil { t.log.Warn("unable to publish tx", "err", err, "data_size", len(data)) diff --git a/op-batcher/metrics/metrics.go b/op-batcher/metrics/metrics.go new file mode 100644 index 0000000000000..f7093a6e87f93 --- /dev/null +++ b/op-batcher/metrics/metrics.go @@ -0,0 +1,249 @@ +package metrics + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/prometheus/client_golang/prometheus" + + "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" +) + +const Namespace = "op_batcher" + +type Metricer interface { + RecordInfo(version string) + RecordUp() + + // Records all L1 and L2 block events + opmetrics.RefMetricer + + RecordLatestL1Block(l1ref eth.L1BlockRef) + RecordL2BlocksLoaded(l2ref eth.L2BlockRef) + RecordChannelOpened(id derive.ChannelID, numPendingBlocks int) + RecordL2BlocksAdded(l2ref eth.L2BlockRef, numBlocksAdded, numPendingBlocks, inputBytes, outputComprBytes int) + RecordChannelClosed(id derive.ChannelID, numPendingBlocks int, numFrames int, inputBytes int, outputComprBytes int, reason error) + RecordChannelFullySubmitted(id derive.ChannelID) + RecordChannelTimedOut(id derive.ChannelID) + + RecordBatchTxSubmitted() + RecordBatchTxSuccess() + RecordBatchTxFailed() + + Document() []opmetrics.DocumentedMetric +} + +type Metrics struct { + ns string + registry *prometheus.Registry + factory opmetrics.Factory + + opmetrics.RefMetrics + + Info prometheus.GaugeVec + Up prometheus.Gauge + + // label by openend, closed, fully_submitted, timed_out + ChannelEvs opmetrics.EventVec + + PendingBlocksCount prometheus.GaugeVec + BlocksAddedCount prometheus.Gauge + + ChannelInputBytes prometheus.GaugeVec + ChannelReadyBytes prometheus.Gauge + ChannelOutputBytes prometheus.Gauge + ChannelClosedReason prometheus.Gauge + ChannelNumFrames prometheus.Gauge + ChannelComprRatio prometheus.Histogram + + BatcherTxEvs opmetrics.EventVec +} + +var _ Metricer = (*Metrics)(nil) + +func NewMetrics(procName string) *Metrics { + if procName == "" { + procName = "default" + } + ns := Namespace + "_" + procName + + registry := opmetrics.NewRegistry() + factory := opmetrics.With(registry) + + return &Metrics{ + ns: ns, + registry: registry, + factory: factory, + + RefMetrics: opmetrics.MakeRefMetrics(ns, factory), + + Info: *factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "info", + Help: "Pseudo-metric tracking version and config info", + }, []string{ + "version", + }), + Up: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "up", + Help: "1 if the op-batcher has finished starting up", + }), + + ChannelEvs: opmetrics.NewEventVec(factory, ns, "channel", "Channel", []string{"stage"}), + + PendingBlocksCount: *factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "pending_blocks_count", + Help: "Number of pending blocks, not added to a channel yet.", + }, []string{"stage"}), + BlocksAddedCount: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "blocks_added_count", + Help: "Total number of blocks added to current channel.", + }), + + ChannelInputBytes: *factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "input_bytes", + Help: "Number of input bytes to a channel.", + }, []string{"stage"}), + ChannelReadyBytes: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "ready_bytes", + Help: "Number of bytes ready in the compression buffer.", + }), + ChannelOutputBytes: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "output_bytes", + Help: "Number of compressed output bytes from a channel.", + }), + ChannelClosedReason: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "channel_closed_reason", + Help: "Pseudo-metric to record the reason a channel got closed.", + }), + ChannelNumFrames: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: "channel_num_frames", + Help: "Total number of frames of closed channel.", + }), + ChannelComprRatio: factory.NewHistogram(prometheus.HistogramOpts{ + Namespace: ns, + Name: "channel_compr_ratio", + Help: "Compression ratios of closed channel.", + Buckets: append([]float64{0.1, 0.2}, prometheus.LinearBuckets(0.3, 0.05, 14)...), + }), + + BatcherTxEvs: opmetrics.NewEventVec(factory, ns, "batcher_tx", "BatcherTx", []string{"stage"}), + } +} + +func (m *Metrics) Serve(ctx context.Context, host string, port int) error { + return opmetrics.ListenAndServe(ctx, m.registry, host, port) +} + +func (m *Metrics) Document() []opmetrics.DocumentedMetric { + return m.factory.Document() +} + +func (m *Metrics) StartBalanceMetrics(ctx context.Context, + l log.Logger, client *ethclient.Client, account common.Address) { + opmetrics.LaunchBalanceMetrics(ctx, l, m.registry, m.ns, client, account) +} + +// RecordInfo sets a pseudo-metric that contains versioning and +// config info for the op-batcher. +func (m *Metrics) RecordInfo(version string) { + m.Info.WithLabelValues(version).Set(1) +} + +// RecordUp sets the up metric to 1. +func (m *Metrics) RecordUp() { + prometheus.MustRegister() + m.Up.Set(1) +} + +const ( + StageLoaded = "loaded" + StageOpened = "opened" + StageAdded = "added" + StageClosed = "closed" + StageFullySubmitted = "fully_submitted" + StageTimedOut = "timed_out" + + TxStageSubmitted = "submitted" + TxStageSuccess = "success" + TxStageFailed = "failed" +) + +func (m *Metrics) RecordLatestL1Block(l1ref eth.L1BlockRef) { + m.RecordL1Ref("latest", l1ref) +} + +// RecordL2BlockLoaded should be called when a new L2 block was loaded into the +// channel manager (but not processed yet). +func (m *Metrics) RecordL2BlocksLoaded(l2ref eth.L2BlockRef) { + m.RecordL2Ref(StageLoaded, l2ref) +} + +func (m *Metrics) RecordChannelOpened(id derive.ChannelID, numPendingBlocks int) { + m.ChannelEvs.Record(StageOpened) + m.BlocksAddedCount.Set(0) // reset + m.PendingBlocksCount.WithLabelValues(StageOpened).Set(float64(numPendingBlocks)) +} + +// RecordL2BlocksAdded should be called when L2 block were added to the channel +// builder, with the latest added block. +func (m *Metrics) RecordL2BlocksAdded(l2ref eth.L2BlockRef, numBlocksAdded, numPendingBlocks, inputBytes, outputComprBytes int) { + m.RecordL2Ref(StageAdded, l2ref) + m.BlocksAddedCount.Add(float64(numBlocksAdded)) + m.PendingBlocksCount.WithLabelValues(StageAdded).Set(float64(numPendingBlocks)) + m.ChannelInputBytes.WithLabelValues(StageAdded).Set(float64(inputBytes)) + m.ChannelReadyBytes.Set(float64(outputComprBytes)) +} + +func (m *Metrics) RecordChannelClosed(id derive.ChannelID, numPendingBlocks int, numFrames int, inputBytes int, outputComprBytes int, reason error) { + m.ChannelEvs.Record(StageClosed) + m.PendingBlocksCount.WithLabelValues(StageClosed).Set(float64(numPendingBlocks)) + m.ChannelNumFrames.Set(float64(numFrames)) + m.ChannelInputBytes.WithLabelValues(StageClosed).Set(float64(inputBytes)) + m.ChannelOutputBytes.Set(float64(outputComprBytes)) + + var comprRatio float64 + if inputBytes > 0 { + comprRatio = float64(outputComprBytes) / float64(inputBytes) + } + m.ChannelComprRatio.Observe(comprRatio) + + m.ChannelClosedReason.Set(float64(ClosedReasonToNum(reason))) +} + +func ClosedReasonToNum(reason error) int { + // CLI-3640 + return 0 +} + +func (m *Metrics) RecordChannelFullySubmitted(id derive.ChannelID) { + m.ChannelEvs.Record(StageFullySubmitted) +} + +func (m *Metrics) RecordChannelTimedOut(id derive.ChannelID) { + m.ChannelEvs.Record(StageTimedOut) +} + +func (m *Metrics) RecordBatchTxSubmitted() { + m.BatcherTxEvs.Record(TxStageSubmitted) +} + +func (m *Metrics) RecordBatchTxSuccess() { + m.BatcherTxEvs.Record(TxStageSuccess) +} + +func (m *Metrics) RecordBatchTxFailed() { + m.BatcherTxEvs.Record(TxStageFailed) +} diff --git a/op-batcher/metrics/noop.go b/op-batcher/metrics/noop.go new file mode 100644 index 0000000000000..80f993f0854a2 --- /dev/null +++ b/op-batcher/metrics/noop.go @@ -0,0 +1,30 @@ +package metrics + +import ( + "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" +) + +type noopMetrics struct{ opmetrics.NoopRefMetrics } + +var NoopMetrics Metricer = new(noopMetrics) + +func (*noopMetrics) Document() []opmetrics.DocumentedMetric { return nil } + +func (*noopMetrics) RecordInfo(version string) {} +func (*noopMetrics) RecordUp() {} + +func (*noopMetrics) RecordLatestL1Block(l1ref eth.L1BlockRef) {} +func (*noopMetrics) RecordL2BlocksLoaded(eth.L2BlockRef) {} +func (*noopMetrics) RecordChannelOpened(derive.ChannelID, int) {} +func (*noopMetrics) RecordL2BlocksAdded(eth.L2BlockRef, int, int, int, int) {} + +func (*noopMetrics) RecordChannelClosed(derive.ChannelID, int, int, int, int, error) {} + +func (*noopMetrics) RecordChannelFullySubmitted(derive.ChannelID) {} +func (*noopMetrics) RecordChannelTimedOut(derive.ChannelID) {} + +func (*noopMetrics) RecordBatchTxSubmitted() {} +func (*noopMetrics) RecordBatchTxSuccess() {} +func (*noopMetrics) RecordBatchTxFailed() {} diff --git a/op-chain-ops/Dockerfile b/op-chain-ops/Dockerfile index b59cd7f325b12..689e6cc496301 100644 --- a/op-chain-ops/Dockerfile +++ b/op-chain-ops/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18.0-alpine3.15 as builder +FROM golang:1.19.0-alpine3.15 as builder RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash diff --git a/op-chain-ops/cmd/check-migration/main.go b/op-chain-ops/cmd/check-migration/main.go new file mode 100644 index 0000000000000..eb08db43d3cf7 --- /dev/null +++ b/op-chain-ops/cmd/check-migration/main.go @@ -0,0 +1,210 @@ +package main + +import ( + "context" + "fmt" + "math/big" + "os" + "strings" + + "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" + + "github.com/ethereum-optimism/optimism/op-chain-ops/db" + "github.com/mattn/go-isatty" + + "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum/go-ethereum/log" + + "github.com/ethereum-optimism/optimism/op-bindings/hardhat" + + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum/go-ethereum/ethclient" + + "github.com/urfave/cli" +) + +func main() { + log.Root().SetHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(isatty.IsTerminal(os.Stderr.Fd())))) + + app := &cli.App{ + Name: "check-migration", + Usage: "Run sanity checks on a migrated database", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "l1-rpc-url", + Value: "http://127.0.0.1:8545", + Usage: "RPC URL for an L1 Node", + Required: true, + }, + &cli.StringFlag{ + Name: "ovm-addresses", + Usage: "Path to ovm-addresses.json", + Required: true, + }, + &cli.StringFlag{ + Name: "ovm-allowances", + Usage: "Path to ovm-allowances.json", + Required: true, + }, + &cli.StringFlag{ + Name: "ovm-messages", + Usage: "Path to ovm-messages.json", + Required: true, + }, + &cli.StringFlag{ + Name: "witness-file", + Usage: "Path to witness file", + Required: true, + }, + &cli.StringFlag{ + Name: "db-path", + Usage: "Path to database", + Required: true, + }, + cli.StringFlag{ + Name: "deploy-config", + Usage: "Path to hardhat deploy config file", + Required: true, + }, + cli.StringFlag{ + Name: "network", + Usage: "Name of hardhat deploy network", + Required: true, + }, + cli.StringFlag{ + Name: "hardhat-deployments", + Usage: "Comma separated list of hardhat deployment directories", + Required: true, + }, + cli.IntFlag{ + Name: "db-cache", + Usage: "LevelDB cache size in mb", + Value: 1024, + }, + cli.IntFlag{ + Name: "db-handles", + Usage: "LevelDB number of handles", + Value: 60, + }, + }, + Action: func(ctx *cli.Context) error { + deployConfig := ctx.String("deploy-config") + config, err := genesis.NewDeployConfig(deployConfig) + if err != nil { + return err + } + + ovmAddresses, err := crossdomain.NewAddresses(ctx.String("ovm-addresses")) + if err != nil { + return err + } + ovmAllowances, err := crossdomain.NewAllowances(ctx.String("ovm-allowances")) + if err != nil { + return err + } + ovmMessages, err := crossdomain.NewSentMessageFromJSON(ctx.String("ovm-messages")) + if err != nil { + return err + } + evmMessages, evmAddresses, err := crossdomain.ReadWitnessData(ctx.String("witness-file")) + if err != nil { + return err + } + + log.Info( + "Loaded witness data", + "ovmAddresses", len(ovmAddresses), + "evmAddresses", len(evmAddresses), + "ovmAllowances", len(ovmAllowances), + "ovmMessages", len(ovmMessages), + "evmMessages", len(evmMessages), + ) + + migrationData := crossdomain.MigrationData{ + OvmAddresses: ovmAddresses, + EvmAddresses: evmAddresses, + OvmAllowances: ovmAllowances, + OvmMessages: ovmMessages, + EvmMessages: evmMessages, + } + + network := ctx.String("network") + deployments := strings.Split(ctx.String("hardhat-deployments"), ",") + hh, err := hardhat.New(network, []string{}, deployments) + if err != nil { + return err + } + + l1RpcURL := ctx.String("l1-rpc-url") + l1Client, err := ethclient.Dial(l1RpcURL) + if err != nil { + return err + } + + var block *types.Block + tag := config.L1StartingBlockTag + if tag.BlockNumber != nil { + block, err = l1Client.BlockByNumber(context.Background(), big.NewInt(tag.BlockNumber.Int64())) + } else if tag.BlockHash != nil { + block, err = l1Client.BlockByHash(context.Background(), *tag.BlockHash) + } else { + return fmt.Errorf("invalid l1StartingBlockTag in deploy config: %v", tag) + } + if err != nil { + return err + } + + dbCache := ctx.Int("db-cache") + dbHandles := ctx.Int("db-handles") + + // Read the required deployment addresses from disk if required + if err := config.GetDeployedAddresses(hh); err != nil { + return err + } + + if err := config.Check(); err != nil { + return err + } + + postLDB, err := db.Open(ctx.String("db-path"), dbCache, dbHandles) + if err != nil { + return err + } + + if err := genesis.PostCheckMigratedDB( + postLDB, + migrationData, + &config.L1CrossDomainMessengerProxy, + config.L1ChainID, + config.FinalSystemOwner, + config.ProxyAdminOwner, + &derive.L1BlockInfo{ + Number: block.NumberU64(), + Time: block.Time(), + BaseFee: block.BaseFee(), + BlockHash: block.Hash(), + BatcherAddr: config.BatchSenderAddress, + L1FeeOverhead: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(config.GasPriceOracleOverhead))), + L1FeeScalar: eth.Bytes32(common.BigToHash(new(big.Int).SetUint64(config.GasPriceOracleScalar))), + }, + ); err != nil { + return err + } + + if err := postLDB.Close(); err != nil { + return err + } + + return nil + }, + } + + if err := app.Run(os.Args); err != nil { + log.Crit("error in migration", "err", err) + } +} diff --git a/op-chain-ops/cmd/op-migrate/main.go b/op-chain-ops/cmd/op-migrate/main.go index 634b70fdfe351..2d0db398e416d 100644 --- a/op-chain-ops/cmd/op-migrate/main.go +++ b/op-chain-ops/cmd/op-migrate/main.go @@ -106,6 +106,11 @@ func main() { Value: "rollup.json", Required: true, }, + cli.BoolFlag{ + Name: "post-check-only", + Usage: "Only perform sanity checks", + Required: false, + }, }, Action: func(ctx *cli.Context) error { deployConfig := ctx.String("deploy-config") diff --git a/op-chain-ops/ether/cli.go b/op-chain-ops/ether/cli.go index 6bea527321114..b345ad95082c3 100644 --- a/op-chain-ops/ether/cli.go +++ b/op-chain-ops/ether/cli.go @@ -20,3 +20,13 @@ func getOVMETHTotalSupplySlot() common.Hash { key := common.BytesToHash(common.LeftPadBytes(position.Bytes(), 32)) return key } + +func GetOVMETHTotalSupplySlot() common.Hash { + return getOVMETHTotalSupplySlot() +} + +// GetOVMETHBalance gets a user's OVM ETH balance from state by querying the +// appropriate storage slot directly. +func GetOVMETHBalance(db *state.StateDB, addr common.Address) *big.Int { + return db.GetState(OVMETHAddress, CalcOVMETHStorageKey(addr)).Big() +} diff --git a/op-chain-ops/ether/migrate.go b/op-chain-ops/ether/migrate.go index 85870c09ffc46..3a67f2740aa9a 100644 --- a/op-chain-ops/ether/migrate.go +++ b/op-chain-ops/ether/migrate.go @@ -13,11 +13,25 @@ import ( "github.com/ethereum/go-ethereum/log" ) +const ( + // checkJobs is the number of parallel workers to spawn + // when iterating the storage trie. + checkJobs = 64 + + // BalanceSlot is an ordinal used to represent slots corresponding to OVM_ETH + // balances in the state. + BalanceSlot = 1 + + // AllowanceSlot is an ordinal used to represent slots corresponding to OVM_ETH + // allowances in the state. + AllowanceSlot = 2 +) + var ( // OVMETHAddress is the address of the OVM ETH predeploy. OVMETHAddress = common.HexToAddress("0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000") - OVMETHIgnoredSlots = map[common.Hash]bool{ + ignoredSlots = map[common.Hash]bool{ // Total Supply common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000002"): true, // Name @@ -27,163 +41,204 @@ var ( common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000005"): true, common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000006"): true, } + + // sequencerEntrypointAddr is the address of the OVM sequencer entrypoint contract. + sequencerEntrypointAddr = common.HexToAddress("0x4200000000000000000000000000000000000005") ) -// MigrateLegacyETH checks that the given list of addresses and allowances represents all storage -// slots in the LegacyERC20ETH contract. We don't have to filter out extra addresses like we do for -// withdrawals because we'll simply carry the balance of a given address to the new system, if the -// account is extra then it won't have any balance and nothing will happen. For each valid balance, -// this method will migrate into state. This method does the checking as part of the migration loop -// in order to avoid having to iterate over state twice. This saves approximately 40 minutes during -// the mainnet migration. -func MigrateLegacyETH(db *state.StateDB, addresses []common.Address, allowances []*crossdomain.Allowance, chainID int, noCheck bool, commit bool) error { +// accountData is a wrapper struct that contains the balance and address of an account. +// It gets passed via channel to the collector process. +type accountData struct { + balance *big.Int + legacySlot common.Hash + address common.Address +} + +// MigrateBalances migrates all balances in the LegacyERC20ETH contract into state. It performs checks +// in parallel with mutations in order to reduce overall migration time. +func MigrateBalances(mutableDB *state.StateDB, dbFactory util.DBFactory, addresses []common.Address, allowances []*crossdomain.Allowance, chainID int, noCheck bool) error { // Chain params to use for integrity checking. params := crossdomain.ParamsByChainID[chainID] if params == nil { return fmt.Errorf("no chain params for %d", chainID) } - // Log the chain params for debugging purposes. - log.Info("Chain params", "chain-id", chainID, "supply-delta", params.ExpectedSupplyDelta) - - return doMigration(db, addresses, allowances, params.ExpectedSupplyDelta, noCheck, commit) + return doMigration(mutableDB, dbFactory, addresses, allowances, params.ExpectedSupplyDelta, noCheck) } -func doMigration(db *state.StateDB, addresses []common.Address, allowances []*crossdomain.Allowance, expSupplyDiff *big.Int, noCheck bool, commit bool) error { +func doMigration(mutableDB *state.StateDB, dbFactory util.DBFactory, addresses []common.Address, allowances []*crossdomain.Allowance, expDiff *big.Int, noCheck bool) error { // We'll need to maintain a list of all addresses that we've seen along with all of the storage // slots based on the witness data. slotsAddrs := make(map[common.Hash]common.Address) - slotTypes := make(map[common.Hash]int) + slotsInp := make(map[common.Hash]int) // For each known address, compute its balance key and add it to the list of addresses. // Mint events are instrumented as regular ETH events in the witness data, so we no longer // need to iterate over mint events during the migration. for _, addr := range addresses { sk := CalcOVMETHStorageKey(addr) - slotTypes[sk] = 1 slotsAddrs[sk] = addr + slotsInp[sk] = BalanceSlot } // For each known allowance, compute its storage key and add it to the list of addresses. for _, allowance := range allowances { - slotTypes[CalcAllowanceStorageKey(allowance.From, allowance.To)] = 2 + sk := CalcAllowanceStorageKey(allowance.From, allowance.To) + slotsAddrs[sk] = allowance.From + slotsInp[sk] = AllowanceSlot } // Add the old SequencerEntrypoint because someone sent it ETH a long time ago and it has a // balance but none of our instrumentation could easily find it. Special case. - sequencerEntrypointAddr := common.HexToAddress("0x4200000000000000000000000000000000000005") - slotTypes[CalcOVMETHStorageKey(sequencerEntrypointAddr)] = 1 + entrySK := CalcOVMETHStorageKey(sequencerEntrypointAddr) + slotsAddrs[entrySK] = sequencerEntrypointAddr + slotsInp[entrySK] = BalanceSlot + + // Channel to receive storage slot keys and values from each iteration job. + outCh := make(chan accountData) + + // Channel that gets closed when the collector is done. + doneCh := make(chan struct{}) + + // Create a map of accounts we've seen so that we can filter out duplicates. + seenAccounts := make(map[common.Address]bool) - // Migrate the OVM_ETH to ETH. - log.Info("Migrating legacy ETH to ETH", "num-accounts", len(addresses)) - totalMigrated := new(big.Int) - logAccountProgress := util.ProgressLogger(1000, "imported OVM_ETH storage slot") - var innerErr error - err := db.ForEachStorage(predeploys.LegacyERC20ETHAddr, func(key, value common.Hash) bool { - defer logAccountProgress() + // Keep track of the total migrated supply. + totalFound := new(big.Int) + // Kick off a background process to collect + // values from the channel and add them to the map. + var count int + progress := util.ProgressLogger(1000, "Migrated OVM_ETH storage slot") + go func() { + defer func() { doneCh <- struct{}{} }() + + for account := range outCh { + progress() + + // Filter out duplicate accounts. See the below note about keyspace iteration for + // why we may have to filter out duplicates. + if seenAccounts[account.address] { + log.Info("skipping duplicate account during iteration", "addr", account.address) + continue + } + + // Accumulate addresses and total supply. + totalFound = new(big.Int).Add(totalFound, account.balance) + + mutableDB.SetBalance(account.address, account.balance) + mutableDB.SetState(predeploys.LegacyERC20ETHAddr, account.legacySlot, common.Hash{}) + count++ + seenAccounts[account.address] = true + } + }() + + err := util.IterateState(dbFactory, predeploys.LegacyERC20ETHAddr, func(db *state.StateDB, key, value common.Hash) error { // We can safely ignore specific slots (totalSupply, name, symbol). - if OVMETHIgnoredSlots[key] { - return true + if ignoredSlots[key] { + return nil } - // Look up the slot type. - slotType, ok := slotTypes[key] + slotType, ok := slotsInp[key] if !ok { log.Error("unknown storage slot in state", "slot", key.String()) if !noCheck { - innerErr = fmt.Errorf("unknown storage slot in state: %s", key.String()) - return false + return fmt.Errorf("unknown storage slot in state: %s", key.String()) } } - switch slotType { - case 1: - // Balance slot. - bal := value.Big() - totalMigrated.Add(totalMigrated, bal) - addr := slotsAddrs[key] - - // There should never be any balances in state, so verify that here. - if db.GetBalance(addr).Sign() > 0 { - log.Error("account has non-zero balance in state - should never happen", "addr", addr) - if !noCheck { - innerErr = fmt.Errorf("account has non-zero balance in state - should never happen: %s", addr) - return false - } + // No accounts should have a balance in state. If they do, bail. + addr, ok := slotsAddrs[key] + if !ok { + log.Crit("could not find address in map - should never happen") + } + bal := db.GetBalance(addr) + if bal.Sign() != 0 { + log.Error( + "account has non-zero balance in state - should never happen", + "addr", addr, + "balance", bal.String(), + ) + if !noCheck { + return fmt.Errorf("account has non-zero balance in state - should never happen: %s", addr.String()) } + } - if !commit { - return true + // Add balances to the total found. + switch slotType { + case BalanceSlot: + // Send the data to the channel. + outCh <- accountData{ + balance: value.Big(), + legacySlot: key, + address: addr, } - - // Set the balance, and delete the legacy slot. - db.SetBalance(addr, bal) - db.SetState(predeploys.LegacyERC20ETHAddr, key, common.Hash{}) - case 2: - // Allowance slot. Nothing to do here. - return true + case AllowanceSlot: + // Allowance slot. Do nothing here. default: // Should never happen. - log.Error("unknown slot type", "slot", key.String(), "type", slotType) - if !noCheck { - innerErr = fmt.Errorf("unknown slot type: %d", slotType) - return false + if noCheck { + log.Error("unknown slot type", "slot", key, "type", slotType) + } else { + log.Crit("unknown slot type %d, should never happen", slotType) } } - return true - }) + return nil + }, checkJobs) + if err != nil { - return fmt.Errorf("failed to iterate over OVM_ETH storage: %w", err) + return err } - if innerErr != nil { - return fmt.Errorf("error in migration: %w", innerErr) + + // Close the outCh to cancel the collector. The collector will signal that it's done + // using doneCh. Any values waiting to be read from outCh will be read before the + // collector exits. + close(outCh) + <-doneCh + + // Log how many slots were iterated over. + log.Info("Iterated legacy balances", "count", count) + + // Verify the supply delta. Recorded total supply in the LegacyERC20ETH contract may be higher + // than the actual migrated amount because self-destructs will remove ETH supply in a way that + // cannot be reflected in the contract. This is fine because self-destructs just mean the L2 is + // actually *overcollateralized* by some tiny amount. + db, err := dbFactory() + if err != nil { + log.Crit("cannot get database", "err", err) } - // Make sure that the total supply delta matches the expected delta. This is equivalent to - // checking that the total migrated is equal to the total found, since we already performed the - // same check against the total found (a = b, b = c => a = c). totalSupply := getOVMETHTotalSupply(db) - delta := new(big.Int).Sub(totalSupply, totalMigrated) - if delta.Cmp(expSupplyDiff) != 0 { - if noCheck { - log.Error( - "supply mismatch", - "migrated", totalMigrated.String(), - "supply", totalSupply.String(), - "delta", delta.String(), - "exp_delta", expSupplyDiff.String(), - ) - } else { - log.Error( - "supply mismatch", - "migrated", totalMigrated.String(), - "supply", totalSupply.String(), - "delta", delta.String(), - "exp_delta", expSupplyDiff.String(), - ) - return fmt.Errorf("supply mismatch: exp delta %s != %s", expSupplyDiff.String(), delta.String()) + delta := new(big.Int).Sub(totalSupply, totalFound) + if delta.Cmp(expDiff) != 0 { + log.Error( + "supply mismatch", + "migrated", totalFound.String(), + "supply", totalSupply.String(), + "delta", delta.String(), + "exp_delta", expDiff.String(), + ) + if !noCheck { + return fmt.Errorf("supply mismatch: %s", delta.String()) } } // Supply is verified. log.Info( "supply verified OK", - "migrated", totalMigrated.String(), + "migrated", totalFound.String(), "supply", totalSupply.String(), "delta", delta.String(), - "exp_delta", expSupplyDiff.String(), + "exp_delta", expDiff.String(), ) // Set the total supply to 0. We do this because the total supply is necessarily going to be // different than the sum of all balances since we no longer track balances inside the contract // itself. The total supply is going to be weird no matter what, might as well set it to zero // so it's explicitly weird instead of implicitly weird. - if commit { - db.SetState(predeploys.LegacyERC20ETHAddr, getOVMETHTotalSupplySlot(), common.Hash{}) - log.Info("Set the totalSupply to 0") - } + mutableDB.SetState(predeploys.LegacyERC20ETHAddr, getOVMETHTotalSupplySlot(), common.Hash{}) + log.Info("Set the totalSupply to 0") return nil } diff --git a/op-chain-ops/ether/migrate_test.go b/op-chain-ops/ether/migrate_test.go index be20f8b4734a4..3c588fdc79c47 100644 --- a/op-chain-ops/ether/migrate_test.go +++ b/op-chain-ops/ether/migrate_test.go @@ -2,8 +2,13 @@ package ether import ( "math/big" + "math/rand" "testing" + "github.com/ethereum-optimism/optimism/op-chain-ops/util" + + "github.com/ethereum-optimism/optimism/op-bindings/predeploys" + "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -12,7 +17,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestMigrateLegacyETH(t *testing.T) { +func TestMigrateBalances(t *testing.T) { tests := []struct { name string totalSupply *big.Int @@ -46,11 +51,9 @@ func TestMigrateLegacyETH(t *testing.T) { }, check: func(t *testing.T, db *state.StateDB, err error) { require.NoError(t, err) - require.Equal(t, db.GetBalance(common.HexToAddress("0x123")), big.NewInt(1)) - require.Equal(t, db.GetBalance(common.HexToAddress("0x456")), big.NewInt(2)) - require.Equal(t, db.GetState(OVMETHAddress, CalcOVMETHStorageKey(common.HexToAddress("0x123"))), common.Hash{}) - require.Equal(t, db.GetState(OVMETHAddress, CalcOVMETHStorageKey(common.HexToAddress("0x456"))), common.Hash{}) - require.Equal(t, db.GetState(OVMETHAddress, getOVMETHTotalSupplySlot()), common.Hash{}) + require.EqualValues(t, common.Big1, db.GetBalance(common.HexToAddress("0x123"))) + require.EqualValues(t, common.Big2, db.GetBalance(common.HexToAddress("0x456"))) + require.EqualValues(t, common.Hash{}, db.GetState(predeploys.LegacyERC20ETHAddr, GetOVMETHTotalSupplySlot())) }, }, { @@ -66,9 +69,9 @@ func TestMigrateLegacyETH(t *testing.T) { }, check: func(t *testing.T, db *state.StateDB, err error) { require.NoError(t, err) - require.Equal(t, db.GetBalance(common.HexToAddress("0x123")), big.NewInt(1)) - require.Equal(t, db.GetState(OVMETHAddress, CalcOVMETHStorageKey(common.HexToAddress("0x123"))), common.Hash{}) - require.Equal(t, db.GetState(OVMETHAddress, getOVMETHTotalSupplySlot()), common.Hash{}) + require.EqualValues(t, common.Big1, db.GetBalance(common.HexToAddress("0x123"))) + require.EqualValues(t, common.Big0, db.GetBalance(common.HexToAddress("0x456"))) + require.EqualValues(t, common.Hash{}, db.GetState(predeploys.LegacyERC20ETHAddr, GetOVMETHTotalSupplySlot())) }, }, { @@ -97,9 +100,9 @@ func TestMigrateLegacyETH(t *testing.T) { }, check: func(t *testing.T, db *state.StateDB, err error) { require.NoError(t, err) - require.Equal(t, db.GetBalance(common.HexToAddress("0x123")), big.NewInt(1)) - require.Equal(t, db.GetState(OVMETHAddress, CalcOVMETHStorageKey(common.HexToAddress("0x123"))), common.Hash{}) - require.Equal(t, db.GetState(OVMETHAddress, getOVMETHTotalSupplySlot()), common.Hash{}) + require.EqualValues(t, common.Big1, db.GetBalance(common.HexToAddress("0x123"))) + require.EqualValues(t, common.Big0, db.GetBalance(common.HexToAddress("0x456"))) + require.EqualValues(t, common.Hash{}, db.GetState(predeploys.LegacyERC20ETHAddr, GetOVMETHTotalSupplySlot())) }, }, { @@ -174,25 +177,23 @@ func TestMigrateLegacyETH(t *testing.T) { }, check: func(t *testing.T, db *state.StateDB, err error) { require.NoError(t, err) - require.Equal(t, db.GetBalance(common.HexToAddress("0x123")), big.NewInt(1)) - require.Equal(t, db.GetBalance(common.HexToAddress("0x456")), big.NewInt(2)) - require.Equal(t, db.GetState(OVMETHAddress, CalcOVMETHStorageKey(common.HexToAddress("0x123"))), common.Hash{}) - require.Equal(t, db.GetState(OVMETHAddress, CalcOVMETHStorageKey(common.HexToAddress("0x456"))), common.Hash{}) - require.Equal(t, db.GetState(OVMETHAddress, getOVMETHTotalSupplySlot()), common.Hash{}) + require.EqualValues(t, common.Big1, db.GetBalance(common.HexToAddress("0x123"))) + require.EqualValues(t, common.Big2, db.GetBalance(common.HexToAddress("0x456"))) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - db := makeLegacyETH(t, tt.totalSupply, tt.stateBalances, tt.stateAllowances) - err := doMigration(db, tt.inputAddresses, tt.inputAllowances, tt.expDiff, false, true) + db, factory := makeLegacyETH(t, tt.totalSupply, tt.stateBalances, tt.stateAllowances) + err := doMigration(db, factory, tt.inputAddresses, tt.inputAllowances, tt.expDiff, false) tt.check(t, db, err) }) } } -func makeLegacyETH(t *testing.T, totalSupply *big.Int, balances map[common.Address]*big.Int, allowances map[common.Address]common.Address) *state.StateDB { - db, err := state.New(common.Hash{}, state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{ +func makeLegacyETH(t *testing.T, totalSupply *big.Int, balances map[common.Address]*big.Int, allowances map[common.Address]common.Address) (*state.StateDB, util.DBFactory) { + memDB := rawdb.NewMemoryDatabase() + db, err := state.New(common.Hash{}, state.NewDatabaseWithConfig(memDB, &trie.Config{ Preimages: true, Cache: 1024, }), nil) @@ -201,7 +202,7 @@ func makeLegacyETH(t *testing.T, totalSupply *big.Int, balances map[common.Addre db.CreateAccount(OVMETHAddress) db.SetState(OVMETHAddress, getOVMETHTotalSupplySlot(), common.BigToHash(totalSupply)) - for slot := range OVMETHIgnoredSlots { + for slot := range ignoredSlots { if slot == getOVMETHTotalSupplySlot() { continue } @@ -220,5 +221,101 @@ func makeLegacyETH(t *testing.T, totalSupply *big.Int, balances map[common.Addre err = db.Database().TrieDB().Commit(root, true) require.NoError(t, err) - return db + return db, func() (*state.StateDB, error) { + return state.New(root, state.NewDatabaseWithConfig(memDB, &trie.Config{ + Preimages: true, + Cache: 1024, + }), nil) + } +} + +// TestMigrateBalancesRandomOK tests that the pre-check balances function works +// with random addresses. This test makes sure that the partition logic doesn't +// miss anything, and helps detect concurrency errors. +func TestMigrateBalancesRandomOK(t *testing.T) { + for i := 0; i < 100; i++ { + addresses, stateBalances, allowances, stateAllowances, totalSupply := setupRandTest(t) + + db, factory := makeLegacyETH(t, totalSupply, stateBalances, stateAllowances) + err := doMigration(db, factory, addresses, allowances, big.NewInt(0), false) + require.NoError(t, err) + + for addr, expBal := range stateBalances { + actBal := db.GetBalance(addr) + require.EqualValues(t, expBal, actBal) + } + } +} + +// TestMigrateBalancesRandomMissing tests that the pre-check balances function works +// with random addresses when some of them are missing. This helps make sure that the +// partition logic doesn't miss anything, and helps detect concurrency errors. +func TestMigrateBalancesRandomMissing(t *testing.T) { + for i := 0; i < 100; i++ { + addresses, stateBalances, allowances, stateAllowances, totalSupply := setupRandTest(t) + + if len(addresses) == 0 { + continue + } + + // Remove a random address from the list of witnesses + idx := rand.Intn(len(addresses)) + addresses = append(addresses[:idx], addresses[idx+1:]...) + + db, factory := makeLegacyETH(t, totalSupply, stateBalances, stateAllowances) + err := doMigration(db, factory, addresses, allowances, big.NewInt(0), false) + require.ErrorContains(t, err, "unknown storage slot") + } + + for i := 0; i < 100; i++ { + addresses, stateBalances, allowances, stateAllowances, totalSupply := setupRandTest(t) + + if len(allowances) == 0 { + continue + } + + // Remove a random allowance from the list of witnesses + idx := rand.Intn(len(allowances)) + allowances = append(allowances[:idx], allowances[idx+1:]...) + + db, factory := makeLegacyETH(t, totalSupply, stateBalances, stateAllowances) + err := doMigration(db, factory, addresses, allowances, big.NewInt(0), false) + require.ErrorContains(t, err, "unknown storage slot") + } +} + +func randAddr(t *testing.T) common.Address { + var addr common.Address + _, err := rand.Read(addr[:]) + require.NoError(t, err) + return addr +} + +func setupRandTest(t *testing.T) ([]common.Address, map[common.Address]*big.Int, []*crossdomain.Allowance, map[common.Address]common.Address, *big.Int) { + addresses := make([]common.Address, 0) + stateBalances := make(map[common.Address]*big.Int) + + allowances := make([]*crossdomain.Allowance, 0) + stateAllowances := make(map[common.Address]common.Address) + + totalSupply := big.NewInt(0) + + for j := 0; j < rand.Intn(10000); j++ { + addr := randAddr(t) + addresses = append(addresses, addr) + stateBalances[addr] = big.NewInt(int64(rand.Intn(1_000_000))) + totalSupply = new(big.Int).Add(totalSupply, stateBalances[addr]) + } + + for j := 0; j < rand.Intn(1000); j++ { + addr := randAddr(t) + to := randAddr(t) + allowances = append(allowances, &crossdomain.Allowance{ + From: addr, + To: to, + }) + stateAllowances[addr] = to + } + + return addresses, stateBalances, allowances, stateAllowances, totalSupply } diff --git a/op-chain-ops/genesis/check.go b/op-chain-ops/genesis/check.go index 066fda6d41a18..ca8105052a155 100644 --- a/op-chain-ops/genesis/check.go +++ b/op-chain-ops/genesis/check.go @@ -6,6 +6,11 @@ import ( "errors" "fmt" "math/big" + "math/rand" + + "github.com/ethereum-optimism/optimism/op-chain-ops/util" + + "github.com/ethereum-optimism/optimism/op-chain-ops/ether" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/rawdb" @@ -22,15 +27,26 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup/derive" ) -// MaxSlotChecks is the maximum number of storage slots to check -// when validating the untouched predeploys. This limit is in place -// to bound execution time of the migration. We can parallelize this -// in the future. -const MaxSlotChecks = 1000 +const ( + // MaxPredeploySlotChecks is the maximum number of storage slots to check + // when validating the untouched predeploys. This limit is in place + // to bound execution time of the migration. We can parallelize this + // in the future. + MaxPredeploySlotChecks = 1000 + + // MaxOVMETHSlotChecks is the maximum number of OVM ETH storage slots to check + // when validating the OVM ETH migration. + MaxOVMETHSlotChecks = 5000 + + // OVMETHSampleLikelihood is the probability that a storage slot will be checked + // when validating the OVM ETH migration. + OVMETHSampleLikelihood = 0.1 +) type StorageCheckMap = map[common.Hash]common.Hash var ( + L2XDMOwnerSlot = common.Hash{31: 0x33} ProxyAdminOwnerSlot = common.Hash{} LegacyETHCheckSlots = map[common.Hash]common.Hash{ @@ -143,7 +159,7 @@ func PostCheckMigratedDB( } log.Info("checked L1Block") - if err := PostCheckLegacyETH(db); err != nil { + if err := PostCheckLegacyETH(prevDB, db, migrationData); err != nil { return err } log.Info("checked legacy eth") @@ -205,7 +221,7 @@ func PostCheckUntouchables(udb state.Database, currDB *state.StateDB, prevRoot c if err := prevDB.ForEachStorage(addr, func(key, value common.Hash) bool { count++ expSlots[key] = value - return count < MaxSlotChecks + return count < MaxPredeploySlotChecks }); err != nil { return fmt.Errorf("error iterating over storage: %w", err) } @@ -250,7 +266,7 @@ func PostCheckPredeploys(prevDB, currDB *state.StateDB) error { // Balances and nonces should match legacy oldNonce := prevDB.GetNonce(addr) - oldBalance := prevDB.GetBalance(addr) + oldBalance := ether.GetOVMETHBalance(prevDB, addr) newNonce := currDB.GetNonce(addr) newBalance := currDB.GetBalance(addr) if oldNonce != newNonce { @@ -360,14 +376,94 @@ func PostCheckPredeployStorage(db vm.StateDB, finalSystemOwner common.Address, p } // PostCheckLegacyETH checks that the legacy eth migration was successful. -// It currently only checks that the total supply was set to 0. -func PostCheckLegacyETH(db vm.StateDB) error { +// It checks that the total supply was set to 0, and randomly samples storage +// slots pre- and post-migration to ensure that balances were correctly migrated. +func PostCheckLegacyETH(prevDB, migratedDB *state.StateDB, migrationData crossdomain.MigrationData) error { + allowanceSlots := make(map[common.Hash]bool) + addresses := make(map[common.Hash]common.Address) + + log.Info("recomputing witness data") + for _, allowance := range migrationData.OvmAllowances { + key := ether.CalcAllowanceStorageKey(allowance.From, allowance.To) + allowanceSlots[key] = true + } + + for _, addr := range migrationData.Addresses() { + addresses[ether.CalcOVMETHStorageKey(addr)] = addr + } + + log.Info("checking legacy eth fixed storage slots") for slot, expValue := range LegacyETHCheckSlots { - actValue := db.GetState(predeploys.LegacyERC20ETHAddr, slot) + actValue := migratedDB.GetState(predeploys.LegacyERC20ETHAddr, slot) if actValue != expValue { return fmt.Errorf("expected slot %s on %s to be %s, but got %s", slot, predeploys.LegacyERC20ETHAddr, expValue, actValue) } } + + var count int + threshold := 100 - int(100*OVMETHSampleLikelihood) + progress := util.ProgressLogger(100, "checking legacy eth balance slots") + var innerErr error + err := prevDB.ForEachStorage(predeploys.LegacyERC20ETHAddr, func(key, value common.Hash) bool { + val := rand.Intn(100) + + // Randomly sample storage slots. + if val > threshold { + return true + } + + // Ignore fixed slots. + if _, ok := LegacyETHCheckSlots[key]; ok { + return true + } + + // Ignore allowances. + if allowanceSlots[key] { + return true + } + + // Grab the address, and bail if we can't find it. + addr, ok := addresses[key] + if !ok { + innerErr = fmt.Errorf("unknown OVM_ETH storage slot %s", key) + return false + } + + // Pull out the pre-migration OVM ETH balance, and the state balance. + ovmETHBalance := value.Big() + ovmETHStateBalance := prevDB.GetBalance(addr) + // Pre-migration state balance should be zero. + if ovmETHStateBalance.Cmp(common.Big0) != 0 { + innerErr = fmt.Errorf("expected OVM_ETH pre-migration state balance for %s to be 0, but got %s", addr, ovmETHStateBalance) + return false + } + + // Migrated state balance should equal the OVM ETH balance. + migratedStateBalance := migratedDB.GetBalance(addr) + if migratedStateBalance.Cmp(ovmETHBalance) != 0 { + innerErr = fmt.Errorf("expected OVM_ETH post-migration state balance for %s to be %s, but got %s", addr, ovmETHStateBalance, migratedStateBalance) + return false + } + // Migrated OVM ETH balance should be zero, since we wipe the slots. + migratedBalance := migratedDB.GetState(predeploys.LegacyERC20ETHAddr, key) + if migratedBalance.Big().Cmp(common.Big0) != 0 { + innerErr = fmt.Errorf("expected OVM_ETH post-migration ERC20 balance for %s to be 0, but got %s", addr, migratedBalance) + return false + } + + progress() + count++ + + // Stop iterating if we've checked enough slots. + return count < MaxOVMETHSlotChecks + }) + if err != nil { + return fmt.Errorf("error iterating over OVM_ETH storage: %w", err) + } + if innerErr != nil { + return innerErr + } + return nil } @@ -505,7 +601,9 @@ func CheckWithdrawalsAfter(db vm.StateDB, data crossdomain.MigrationData, l1Cros // Now, iterate over each legacy withdrawal and check if there is a corresponding // migrated withdrawal. var innerErr error + progress := util.ProgressLogger(1000, "checking withdrawals") err = db.ForEachStorage(predeploys.LegacyMessagePasserAddr, func(key, value common.Hash) bool { + progress() // The legacy message passer becomes a proxy during the migration, // so we need to ignore the implementation/admin slots. if key == ImplementationSlot || key == AdminSlot { @@ -542,7 +640,7 @@ func CheckWithdrawalsAfter(db vm.StateDB, data crossdomain.MigrationData, l1Cros // If the sender is _not_ the L2XDM, the value should not be migrated. wd := wdsByOldSlot[key] - if wd.XDomainSender == predeploys.L2CrossDomainMessengerAddr { + if wd.MessageSender == predeploys.L2CrossDomainMessengerAddr { // Make sure the value is abiTrue if this withdrawal should be migrated. if migratedValue != abiTrue { innerErr = fmt.Errorf("expected migrated value to be true, but got %s", migratedValue) @@ -551,7 +649,7 @@ func CheckWithdrawalsAfter(db vm.StateDB, data crossdomain.MigrationData, l1Cros } else { // Otherwise, ensure that withdrawals from senders other than the L2XDM are _not_ migrated. if migratedValue != abiFalse { - innerErr = fmt.Errorf("a migration from a sender other than the L2XDM was migrated") + innerErr = fmt.Errorf("a migration from a sender other than the L2XDM was migrated. sender: %s, migrated value: %s", wd.MessageSender, migratedValue) return false } } diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index b2182e7526336..7684f5cd23a7a 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -78,6 +78,8 @@ type DeployConfig struct { ProxyAdminOwner common.Address `json:"proxyAdminOwner"` // Owner of the system on L1 FinalSystemOwner common.Address `json:"finalSystemOwner"` + // GUARDIAN account in the OptimismPortal + PortalGuardian common.Address `json:"portalGuardian"` // L1 recipient of fees accumulated in the BaseFeeVault BaseFeeVaultRecipient common.Address `json:"baseFeeVaultRecipient"` // L1 recipient of fees accumulated in the L1FeeVault @@ -130,6 +132,9 @@ func (d *DeployConfig) Check() error { if d.FinalizationPeriodSeconds == 0 { return fmt.Errorf("%w: FinalizationPeriodSeconds cannot be 0", ErrInvalidDeployConfig) } + if d.PortalGuardian == (common.Address{}) { + return fmt.Errorf("%w: PortalGuardian cannot be address(0)", ErrInvalidDeployConfig) + } if d.MaxSequencerDrift == 0 { return fmt.Errorf("%w: MaxSequencerDrift cannot be 0", ErrInvalidDeployConfig) } diff --git a/op-chain-ops/genesis/db_migration.go b/op-chain-ops/genesis/db_migration.go index 1987955de682a..f91ce797711aa 100644 --- a/op-chain-ops/genesis/db_migration.go +++ b/op-chain-ops/genesis/db_migration.go @@ -82,16 +82,25 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m ) } - // Set up the backing store. - underlyingDB := state.NewDatabaseWithConfig(ldb, &trie.Config{ - Preimages: true, - Cache: 1024, - }) - - // Open up the state database. - db, err := state.New(header.Root, underlyingDB, nil) + dbFactory := func() (*state.StateDB, error) { + // Set up the backing store. + underlyingDB := state.NewDatabaseWithConfig(ldb, &trie.Config{ + Preimages: true, + Cache: 1024, + }) + + // Open up the state database. + db, err := state.New(header.Root, underlyingDB, nil) + if err != nil { + return nil, fmt.Errorf("cannot open StateDB: %w", err) + } + + return db, nil + } + + db, err := dbFactory() if err != nil { - return nil, fmt.Errorf("cannot open StateDB: %w", err) + return nil, fmt.Errorf("cannot create StateDB: %w", err) } // Before we do anything else, we need to ensure that all of the input configuration is correct @@ -182,17 +191,13 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m return nil, fmt.Errorf("cannot migrate withdrawals: %w", err) } - // We also need to verify that we have all of the storage slots for the LegacyERC20ETH contract - // that we expect to have. An error will be thrown if there are any missing storage slots. - // Unlike with withdrawals, we do not need to filter out extra addresses because their balances - // would necessarily be zero and therefore not affect the migration. - // - // Once verified, we migrate the balances held inside the LegacyERC20ETH contract into the state trie. - // We also delete the balances from the LegacyERC20ETH contract. + // Finally we migrate the balances held inside the LegacyERC20ETH contract into the state trie. + // We also delete the balances from the LegacyERC20ETH contract. Unlike the steps above, this step + // combines the check and mutation steps into one in order to reduce migration time. log.Info("Starting to migrate ERC20 ETH") - err = ether.MigrateLegacyETH(db, migrationData.Addresses(), migrationData.OvmAllowances, int(config.L1ChainID), noCheck, commit) + err = ether.MigrateBalances(db, dbFactory, migrationData.Addresses(), migrationData.OvmAllowances, int(config.L1ChainID), noCheck) if err != nil { - return nil, fmt.Errorf("cannot migrate legacy eth: %w", err) + return nil, fmt.Errorf("failed to migrate OVM_ETH: %w", err) } // We're done messing around with the database, so we can now commit the changes to the DB. diff --git a/op-chain-ops/genesis/layer_one.go b/op-chain-ops/genesis/layer_one.go index 317650c67bfd9..dd30f3556577e 100644 --- a/op-chain-ops/genesis/layer_one.go +++ b/op-chain-ops/genesis/layer_one.go @@ -295,7 +295,7 @@ func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend) Name: "OptimismPortal", Args: []interface{}{ predeploys.DevL2OutputOracleAddr, - config.FinalSystemOwner, + config.PortalGuardian, true, // _paused }, }, diff --git a/op-chain-ops/genesis/testdata/test-deploy-config-full.json b/op-chain-ops/genesis/testdata/test-deploy-config-full.json index 6d71ea6b08b13..af04f2e317976 100644 --- a/op-chain-ops/genesis/testdata/test-deploy-config-full.json +++ b/op-chain-ops/genesis/testdata/test-deploy-config-full.json @@ -19,6 +19,7 @@ "l1GenesisBlockGasLimit": "0xe4e1c0", "l1GenesisBlockDifficulty": "0x1", "finalSystemOwner": "0x0000000000000000000000000000000000000111", + "portalGuardian": "0x0000000000000000000000000000000000000112", "finalizationPeriodSeconds": 2, "l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "l1GenesisBlockCoinbase": "0x0000000000000000000000000000000000000000", diff --git a/op-chain-ops/util/state_iterator.go b/op-chain-ops/util/state_iterator.go new file mode 100644 index 0000000000000..06780951952c1 --- /dev/null +++ b/op-chain-ops/util/state_iterator.go @@ -0,0 +1,161 @@ +package util + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/trie" +) + +var ( + // maxSlot is the maximum possible storage slot. + maxSlot = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") +) + +type DBFactory func() (*state.StateDB, error) + +type StateCallback func(db *state.StateDB, key, value common.Hash) error + +func IterateState(dbFactory DBFactory, address common.Address, cb StateCallback, workers int) error { + if workers <= 0 { + panic("workers must be greater than 0") + } + + // WaitGroup to wait for all workers to finish. + var wg sync.WaitGroup + + // Channel to receive errors from each iteration job. + errCh := make(chan error, workers) + // Channel to cancel all iteration jobs. + cancelCh := make(chan struct{}) + + worker := func(start, end common.Hash) { + // Decrement the WaitGroup when the function returns. + defer wg.Done() + + db, err := dbFactory() + if err != nil { + // Should never happen, so explode if it does. + log.Crit("cannot create state db", "err", err) + } + st, err := db.StorageTrie(address) + if err != nil { + // Should never happen, so explode if it does. + log.Crit("cannot get storage trie", "address", address, "err", err) + } + // st can be nil if the account doesn't exist. + if st == nil { + errCh <- fmt.Errorf("account does not exist: %s", address.Hex()) + return + } + + it := trie.NewIterator(st.NodeIterator(start.Bytes())) + + // Below code is largely based on db.ForEachStorage. We can't use that + // because it doesn't allow us to specify a start and end key. + for it.Next() { + select { + case <-cancelCh: + // If one of the workers encounters an error, cancel all of them. + return + default: + break + } + + // Use the raw (i.e., secure hashed) key to check if we've reached + // the end of the partition. Use > rather than >= here to account for + // the fact that the values returned by PartitionKeys are inclusive. + // Duplicate addresses that may be returned by this iteration are + // filtered out in the collector. + if new(big.Int).SetBytes(it.Key).Cmp(end.Big()) > 0 { + return + } + + // Skip if the value is empty. + rawValue := it.Value + if len(rawValue) == 0 { + continue + } + + // Get the preimage. + rawKey := st.GetKey(it.Key) + if rawKey == nil { + // Should never happen, so explode if it does. + log.Crit("cannot get preimage for storage key", "key", it.Key) + } + key := common.BytesToHash(rawKey) + + // Parse the raw value. + _, content, _, err := rlp.Split(rawValue) + if err != nil { + // Should never happen, so explode if it does. + log.Crit("mal-formed data in state: %v", err) + } + + value := common.BytesToHash(content) + + // Call the callback with the DB, key, and value. Errors get + // bubbled up to the errCh. + if err := cb(db, key, value); err != nil { + errCh <- err + return + } + } + } + + for i := 0; i < workers; i++ { + wg.Add(1) + + // Partition the keyspace per worker. + start, end := PartitionKeyspace(i, workers) + + // Kick off our worker. + go worker(start, end) + } + + wg.Wait() + + for len(errCh) > 0 { + err := <-errCh + if err != nil { + return err + } + } + + return nil +} + +// PartitionKeyspace divides the key space into partitions by dividing the maximum keyspace +// by count then multiplying by i. This will leave some slots left over, which we handle below. It +// returns the start and end keys for the partition as a common.Hash. Note that the returned range +// of keys is inclusive, i.e., [start, end] NOT [start, end). +func PartitionKeyspace(i int, count int) (common.Hash, common.Hash) { + if i < 0 || count < 0 { + panic("i and count must be greater than 0") + } + + if i > count-1 { + panic("i must be less than count - 1") + } + + // Divide the key space into partitions by dividing the key space by the number + // of jobs. This will leave some slots left over, which we handle below. + partSize := new(big.Int).Div(maxSlot.Big(), big.NewInt(int64(count))) + + start := common.BigToHash(new(big.Int).Mul(big.NewInt(int64(i)), partSize)) + var end common.Hash + if i < count-1 { + // If this is not the last partition, use the next partition's start key as the end. + end = common.BigToHash(new(big.Int).Mul(big.NewInt(int64(i+1)), partSize)) + } else { + // If this is the last partition, use the max slot as the end. + end = maxSlot + } + + return start, end +} diff --git a/op-chain-ops/util/state_iterator_test.go b/op-chain-ops/util/state_iterator_test.go new file mode 100644 index 0000000000000..e4c3aed43eee3 --- /dev/null +++ b/op-chain-ops/util/state_iterator_test.go @@ -0,0 +1,211 @@ +package util + +import ( + crand "crypto/rand" + "fmt" + "math/rand" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/trie" + "github.com/stretchr/testify/require" +) + +var testAddr = common.Address{0: 0xff} + +func TestStateIteratorWorkers(t *testing.T) { + _, factory, _ := setupRandTest(t) + + for i := -1; i <= 0; i++ { + require.Panics(t, func() { + _ = IterateState(factory, testAddr, func(db *state.StateDB, key, value common.Hash) error { + return nil + }, i) + }) + } +} + +func TestStateIteratorNonexistentAccount(t *testing.T) { + _, factory, _ := setupRandTest(t) + + require.ErrorContains(t, IterateState(factory, common.Address{}, func(db *state.StateDB, key, value common.Hash) error { + return nil + }, 1), "account does not exist") +} + +func TestStateIteratorRandomOK(t *testing.T) { + for i := 0; i < 100; i++ { + hashes, factory, workerCount := setupRandTest(t) + + seenHashes := make(map[common.Hash]bool) + hashCh := make(chan common.Hash) + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + for hash := range hashCh { + seenHashes[hash] = true + } + }() + + require.NoError(t, IterateState(factory, testAddr, func(db *state.StateDB, key, value common.Hash) error { + hashCh <- key + return nil + }, workerCount)) + + close(hashCh) + <-doneCh + + // Perform a less or equal check here in case of duplicates. The map check below will assert + // that all of the hashes are accounted for. + require.LessOrEqual(t, len(seenHashes), len(hashes)) + + // Every hash we put into state should have been iterated over. + for _, hash := range hashes { + require.Contains(t, seenHashes, hash) + } + } +} + +func TestStateIteratorRandomError(t *testing.T) { + for i := 0; i < 100; i++ { + hashes, factory, workerCount := setupRandTest(t) + + failHash := hashes[rand.Intn(len(hashes))] + require.ErrorContains(t, IterateState(factory, testAddr, func(db *state.StateDB, key, value common.Hash) error { + if key == failHash { + return fmt.Errorf("test error") + } + return nil + }, workerCount), "test error") + } +} + +func TestPartitionKeyspace(t *testing.T) { + tests := []struct { + i int + count int + expected [2]common.Hash + }{ + { + i: 0, + count: 1, + expected: [2]common.Hash{ + common.HexToHash("0x00"), + common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, + }, + { + i: 0, + count: 2, + expected: [2]common.Hash{ + common.HexToHash("0x00"), + common.HexToHash("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, + }, + { + i: 1, + count: 2, + expected: [2]common.Hash{ + common.HexToHash("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, + }, + { + i: 0, + count: 3, + expected: [2]common.Hash{ + common.HexToHash("0x00"), + common.HexToHash("0x5555555555555555555555555555555555555555555555555555555555555555"), + }, + }, + { + i: 1, + count: 3, + expected: [2]common.Hash{ + common.HexToHash("0x5555555555555555555555555555555555555555555555555555555555555555"), + common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + }, + }, + { + i: 2, + count: 3, + expected: [2]common.Hash{ + common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + }, + }, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("i %d, count %d", tt.i, tt.count), func(t *testing.T) { + start, end := PartitionKeyspace(tt.i, tt.count) + require.Equal(t, tt.expected[0], start) + require.Equal(t, tt.expected[1], end) + }) + } + + t.Run("panics on invalid i or count", func(t *testing.T) { + require.Panics(t, func() { + PartitionKeyspace(1, 1) + }) + require.Panics(t, func() { + PartitionKeyspace(-1, 1) + }) + require.Panics(t, func() { + PartitionKeyspace(0, -1) + }) + require.Panics(t, func() { + PartitionKeyspace(-1, -1) + }) + }) +} + +func setupRandTest(t *testing.T) ([]common.Hash, DBFactory, int) { + memDB := rawdb.NewMemoryDatabase() + db, err := state.New(common.Hash{}, state.NewDatabaseWithConfig(memDB, &trie.Config{ + Preimages: true, + Cache: 1024, + }), nil) + require.NoError(t, err) + + hashCount := rand.Intn(100) + if hashCount == 0 { + hashCount = 1 + } + + hashes := make([]common.Hash, hashCount) + + db.CreateAccount(testAddr) + + for j := 0; j < hashCount; j++ { + hashes[j] = randHash(t) + db.SetState(testAddr, hashes[j], hashes[j]) + } + + root, err := db.Commit(false) + require.NoError(t, err) + + err = db.Database().TrieDB().Commit(root, true) + require.NoError(t, err) + + factory := func() (*state.StateDB, error) { + return state.New(root, state.NewDatabaseWithConfig(memDB, &trie.Config{ + Preimages: true, + Cache: 1024, + }), nil) + } + + workerCount := rand.Intn(64) + if workerCount == 0 { + workerCount = 1 + } + return hashes, factory, workerCount +} + +func randHash(t *testing.T) common.Hash { + var h common.Hash + _, err := crand.Read(h[:]) + require.NoError(t, err) + return h +} diff --git a/op-e2e/actions/blocktime_test.go b/op-e2e/actions/blocktime_test.go index 6e5fef0c021fd..6e160988dd2f6 100644 --- a/op-e2e/actions/blocktime_test.go +++ b/op-e2e/actions/blocktime_test.go @@ -35,7 +35,7 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) { ChainID: sd.L2Cfg.Config.ChainID, Nonce: n, GasTipCap: big.NewInt(2 * params.GWei), - GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee(), big.NewInt(2*params.GWei)), + GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee, big.NewInt(2*params.GWei)), Gas: params.TxGas, To: &dp.Addresses.Bob, Value: e2eutils.Ether(2), @@ -146,7 +146,7 @@ func TestLargeL1Gaps(gt *testing.T) { ChainID: sd.L2Cfg.Config.ChainID, Nonce: n, GasTipCap: big.NewInt(2 * params.GWei), - GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee(), big.NewInt(2*params.GWei)), + GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee, big.NewInt(2*params.GWei)), Gas: params.TxGas, To: &dp.Addresses.Bob, Value: e2eutils.Ether(2), diff --git a/op-e2e/actions/fork_test.go b/op-e2e/actions/fork_test.go new file mode 100644 index 0000000000000..02d9e3d23a56d --- /dev/null +++ b/op-e2e/actions/fork_test.go @@ -0,0 +1,53 @@ +package actions + +import ( + "testing" + + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/testlog" +) + +func TestShapellaL1Fork(gt *testing.T) { + t := NewDefaultTesting(gt) + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + + sd := e2eutils.Setup(t, dp, defaultAlloc) + activation := sd.L1Cfg.Timestamp + 24 + sd.L1Cfg.Config.ShanghaiTime = &activation + log := testlog.Logger(t, log.LvlDebug) + + _, _, miner, sequencer, _, verifier, _, batcher := setupReorgTestActors(t, dp, sd, log) + + require.False(t, sd.L1Cfg.Config.IsShanghai(miner.l1Chain.CurrentBlock().Time), "not active yet") + + // start op-nodes + sequencer.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + // build empty L1 blocks, crossing the fork boundary + miner.ActEmptyBlock(t) + miner.ActEmptyBlock(t) + miner.ActEmptyBlock(t) + + // verify Shanghai is active + l1Head := miner.l1Chain.CurrentBlock() + require.True(t, sd.L1Cfg.Config.IsShanghai(l1Head.Time)) + + // build L2 chain up to and including L2 blocks referencing shanghai L1 blocks + sequencer.ActL1HeadSignal(t) + sequencer.ActBuildToL1Head(t) + miner.ActL1StartBlock(12)(t) + batcher.ActSubmitAll(t) + miner.ActL1IncludeTx(batcher.batcherAddr)(t) + miner.ActL1EndBlock(t) + + // sync verifier + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + // verify verifier accepted shanghai L1 inputs + require.Equal(t, l1Head.Hash(), verifier.SyncStatus().SafeL2.L1Origin.Hash, "verifier synced L1 chain that includes shanghai headers") + require.Equal(t, sequencer.SyncStatus().UnsafeL2, verifier.SyncStatus().UnsafeL2, "verifier and sequencer agree") +} diff --git a/op-e2e/actions/l1_miner.go b/op-e2e/actions/l1_miner.go index b3074ee762931..8e3afe26b931f 100644 --- a/op-e2e/actions/l1_miner.go +++ b/op-e2e/actions/l1_miner.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/trie" + "github.com/stretchr/testify/require" ) // L1Miner wraps a L1Replica with instrumented block building ability. @@ -72,6 +73,9 @@ func (s *L1Miner) ActL1StartBlock(timeDelta uint64) Action { header.GasLimit = parent.GasLimit * s.l1Cfg.Config.ElasticityMultiplier() } } + if s.l1Cfg.Config.IsShanghai(header.Time) { + header.WithdrawalsHash = &types.EmptyWithdrawalsHash + } s.l1Building = true s.l1BuildingHeader = header @@ -97,26 +101,33 @@ func (s *L1Miner) ActL1IncludeTx(from common.Address) Action { t.Fatalf("no pending txs from %s, and have %d unprocessable queued txs from this account", from, len(q)) } tx := txs[i] - if tx.Gas() > s.l1BuildingHeader.GasLimit { - t.Fatalf("tx consumes %d gas, more than available in L1 block %d", tx.Gas(), s.l1BuildingHeader.GasLimit) - } - if tx.Gas() > uint64(*s.l1GasPool) { - t.InvalidAction("action takes too much gas: %d, only have %d", tx.Gas(), uint64(*s.l1GasPool)) - return - } + s.IncludeTx(t, tx) s.pendingIndices[from] = i + 1 // won't retry the tx - s.l1BuildingState.SetTxContext(tx.Hash(), len(s.l1Transactions)) - receipt, err := core.ApplyTransaction(s.l1Cfg.Config, s.l1Chain, &s.l1BuildingHeader.Coinbase, - s.l1GasPool, s.l1BuildingState, s.l1BuildingHeader, tx, &s.l1BuildingHeader.GasUsed, *s.l1Chain.GetVMConfig()) - if err != nil { - s.l1TxFailed = append(s.l1TxFailed, tx) - t.Fatalf("failed to apply transaction to L1 block (tx %d): %w", len(s.l1Transactions), err) - } - s.l1Receipts = append(s.l1Receipts, receipt) - s.l1Transactions = append(s.l1Transactions, tx) } } +func (s *L1Miner) IncludeTx(t Testing, tx *types.Transaction) { + from, err := s.l1Signer.Sender(tx) + require.NoError(t, err) + s.log.Info("including tx", "nonce", tx.Nonce(), "from", from) + if tx.Gas() > s.l1BuildingHeader.GasLimit { + t.Fatalf("tx consumes %d gas, more than available in L1 block %d", tx.Gas(), s.l1BuildingHeader.GasLimit) + } + if tx.Gas() > uint64(*s.l1GasPool) { + t.InvalidAction("action takes too much gas: %d, only have %d", tx.Gas(), uint64(*s.l1GasPool)) + return + } + s.l1BuildingState.SetTxContext(tx.Hash(), len(s.l1Transactions)) + receipt, err := core.ApplyTransaction(s.l1Cfg.Config, s.l1Chain, &s.l1BuildingHeader.Coinbase, + s.l1GasPool, s.l1BuildingState, s.l1BuildingHeader, tx, &s.l1BuildingHeader.GasUsed, *s.l1Chain.GetVMConfig()) + if err != nil { + s.l1TxFailed = append(s.l1TxFailed, tx) + t.Fatalf("failed to apply transaction to L1 block (tx %d): %v", len(s.l1Transactions), err) + } + s.l1Receipts = append(s.l1Receipts, receipt) + s.l1Transactions = append(s.l1Transactions, tx) +} + func (s *L1Miner) ActL1SetFeeRecipient(coinbase common.Address) { s.prefCoinbase = coinbase if s.l1Building { @@ -135,6 +146,9 @@ func (s *L1Miner) ActL1EndBlock(t Testing) { s.l1BuildingHeader.GasUsed = s.l1BuildingHeader.GasLimit - uint64(*s.l1GasPool) s.l1BuildingHeader.Root = s.l1BuildingState.IntermediateRoot(s.l1Cfg.Config.IsEIP158(s.l1BuildingHeader.Number)) block := types.NewBlock(s.l1BuildingHeader, s.l1Transactions, nil, s.l1Receipts, trie.NewStackTrie(nil)) + if s.l1Cfg.Config.IsShanghai(s.l1BuildingHeader.Time) { + block = block.WithWithdrawals(make([]*types.Withdrawal, 0)) + } // Write state changes to db root, err := s.l1BuildingState.Commit(s.l1Cfg.Config.IsEIP158(s.l1BuildingHeader.Number)) diff --git a/op-e2e/actions/l1_miner_test.go b/op-e2e/actions/l1_miner_test.go index c5074ea4d2cb5..2d2f259ed819c 100644 --- a/op-e2e/actions/l1_miner_test.go +++ b/op-e2e/actions/l1_miner_test.go @@ -31,7 +31,7 @@ func TestL1Miner_BuildBlock(gt *testing.T) { ChainID: sd.L1Cfg.Config.ChainID, Nonce: 0, GasTipCap: big.NewInt(2 * params.GWei), - GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee(), big.NewInt(2*params.GWei)), + GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee, big.NewInt(2*params.GWei)), Gas: params.TxGas, To: &dp.Addresses.Bob, Value: e2eutils.Ether(2), @@ -41,7 +41,8 @@ func TestL1Miner_BuildBlock(gt *testing.T) { // make an empty block, even though a tx may be waiting miner.ActL1StartBlock(10)(t) miner.ActL1EndBlock(t) - bl := miner.l1Chain.CurrentBlock() + header := miner.l1Chain.CurrentBlock() + bl := miner.l1Chain.GetBlockByHash(header.Hash()) require.Equal(t, uint64(1), bl.NumberU64()) require.Zero(gt, bl.Transactions().Len()) @@ -49,7 +50,8 @@ func TestL1Miner_BuildBlock(gt *testing.T) { miner.ActL1StartBlock(10)(t) miner.ActL1IncludeTx(dp.Addresses.Alice)(t) miner.ActL1EndBlock(t) - bl = miner.l1Chain.CurrentBlock() + header = miner.l1Chain.CurrentBlock() + bl = miner.l1Chain.GetBlockByHash(header.Hash()) require.Equal(t, uint64(2), bl.NumberU64()) require.Equal(t, 1, bl.Transactions().Len()) require.Equal(t, tx.Hash(), bl.Transactions()[0].Hash()) diff --git a/op-e2e/actions/l1_replica.go b/op-e2e/actions/l1_replica.go index fd1562c0b99fa..81c21768841e5 100644 --- a/op-e2e/actions/l1_replica.go +++ b/op-e2e/actions/l1_replica.go @@ -103,9 +103,9 @@ func (s *L1Replica) ActL1RewindDepth(depth uint64) Action { t.InvalidAction("cannot rewind L1 past genesis (current: %d, rewind depth: %d)", head, depth) return } - finalized := s.l1Chain.CurrentFinalizedBlock() - if finalized != nil && head < finalized.NumberU64()+depth { - t.InvalidAction("cannot rewind head of chain past finalized block %d with rewind depth %d", finalized.NumberU64(), depth) + finalized := s.l1Chain.CurrentFinalBlock() + if finalized != nil && head < finalized.Number.Uint64()+depth { + t.InvalidAction("cannot rewind head of chain past finalized block %d with rewind depth %d", finalized.Number.Uint64(), depth) return } if err := s.l1Chain.SetHead(head - depth); err != nil { @@ -188,7 +188,7 @@ func (s *L1Replica) UnsafeNum() uint64 { head := s.l1Chain.CurrentBlock() headNum := uint64(0) if head != nil { - headNum = head.NumberU64() + headNum = head.Number.Uint64() } return headNum } @@ -197,16 +197,16 @@ func (s *L1Replica) SafeNum() uint64 { safe := s.l1Chain.CurrentSafeBlock() safeNum := uint64(0) if safe != nil { - safeNum = safe.NumberU64() + safeNum = safe.Number.Uint64() } return safeNum } func (s *L1Replica) FinalizedNum() uint64 { - finalized := s.l1Chain.CurrentFinalizedBlock() + finalized := s.l1Chain.CurrentFinalBlock() finalizedNum := uint64(0) if finalized != nil { - finalizedNum = finalized.NumberU64() + finalizedNum = finalized.Number.Uint64() } return finalizedNum } @@ -219,7 +219,7 @@ func (s *L1Replica) ActL1Finalize(t Testing, num uint64) { t.InvalidAction("need to move forward safe block before moving finalized block") return } - newFinalized := s.l1Chain.GetBlockByNumber(num) + newFinalized := s.l1Chain.GetHeaderByNumber(num) if newFinalized == nil { t.Fatalf("expected block at %d after finalized L1 block %d, safe head is ahead", num, finalizedNum) } @@ -234,7 +234,7 @@ func (s *L1Replica) ActL1FinalizeNext(t Testing) { // ActL1Safe marks the given unsafe block as safe. func (s *L1Replica) ActL1Safe(t Testing, num uint64) { - newSafe := s.l1Chain.GetBlockByNumber(num) + newSafe := s.l1Chain.GetHeaderByNumber(num) if newSafe == nil { t.InvalidAction("could not find L1 block %d, cannot label it as safe", num) return diff --git a/op-e2e/actions/l1_replica_test.go b/op-e2e/actions/l1_replica_test.go index 6205d532676fb..b813e2748a4db 100644 --- a/op-e2e/actions/l1_replica_test.go +++ b/op-e2e/actions/l1_replica_test.go @@ -85,7 +85,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) { }) syncFromA := replica1.ActL1Sync(canonL1(chainA)) // sync canonical chain A - for replica1.l1Chain.CurrentBlock().NumberU64()+1 < uint64(len(chainA)) { + for replica1.l1Chain.CurrentBlock().Number.Uint64()+1 < uint64(len(chainA)) { syncFromA(t) } require.Equal(t, replica1.l1Chain.CurrentBlock().Hash(), chainA[len(chainA)-1].Hash(), "sync replica1 to head of chain A") @@ -94,7 +94,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) { // sync new canonical chain B syncFromB := replica1.ActL1Sync(canonL1(chainB)) - for replica1.l1Chain.CurrentBlock().NumberU64()+1 < uint64(len(chainB)) { + for replica1.l1Chain.CurrentBlock().Number.Uint64()+1 < uint64(len(chainB)) { syncFromB(t) } require.Equal(t, replica1.l1Chain.CurrentBlock().Hash(), chainB[len(chainB)-1].Hash(), "sync replica1 to head of chain B") @@ -105,7 +105,7 @@ func TestL1Replica_ActL1Sync(gt *testing.T) { _ = replica2.Close() }) syncFromOther := replica2.ActL1Sync(replica1.CanonL1Chain()) - for replica2.l1Chain.CurrentBlock().NumberU64()+1 < uint64(len(chainB)) { + for replica2.l1Chain.CurrentBlock().Number.Uint64()+1 < uint64(len(chainB)) { syncFromOther(t) } require.Equal(t, replica2.l1Chain.CurrentBlock().Hash(), chainB[len(chainB)-1].Hash(), "sync replica2 to head of chain B") diff --git a/op-e2e/actions/l2_batcher.go b/op-e2e/actions/l2_batcher.go index b5e353d99ece9..81909022df9cf 100644 --- a/op-e2e/actions/l2_batcher.go +++ b/op-e2e/actions/l2_batcher.go @@ -91,6 +91,10 @@ func (s *L2Batcher) SubmittingData() bool { // ActL2BatchBuffer adds the next L2 block to the batch buffer. // If the buffer is being submitted, the buffer is wiped. func (s *L2Batcher) ActL2BatchBuffer(t Testing) { + require.NoError(t, s.Buffer(t), "failed to add block to channel") +} + +func (s *L2Batcher) Buffer(t Testing) error { if s.l2Submitting { // break ongoing submitting work if necessary s.l2ChannelOut = nil s.l2Submitting = false @@ -120,7 +124,7 @@ func (s *L2Batcher) ActL2BatchBuffer(t Testing) { s.l2ChannelOut = nil } else { s.log.Info("nothing left to submit") - return + return nil } } // Create channel if we don't have one yet @@ -143,9 +147,10 @@ func (s *L2Batcher) ActL2BatchBuffer(t Testing) { s.l2ChannelOut = nil } if _, err := s.l2ChannelOut.AddBlock(block); err != nil { // should always succeed - t.Fatalf("failed to add block to channel: %v", err) + return err } s.l2BufferedBlock = eth.ToBlockID(block) + return nil } func (s *L2Batcher) ActL2ChannelClose(t Testing) { @@ -158,7 +163,7 @@ func (s *L2Batcher) ActL2ChannelClose(t Testing) { } // ActL2BatchSubmit constructs a batch tx from previous buffered L2 blocks, and submits it to L1 -func (s *L2Batcher) ActL2BatchSubmit(t Testing) { +func (s *L2Batcher) ActL2BatchSubmit(t Testing, txOpts ...func(tx *types.DynamicFeeTx)) { // Don't run this action if there's no data to submit if s.l2ChannelOut == nil { t.InvalidAction("need to buffer data first, cannot batch submit with empty buffer") @@ -192,6 +197,9 @@ func (s *L2Batcher) ActL2BatchSubmit(t Testing) { GasFeeCap: gasFeeCap, Data: data.Bytes(), } + for _, opt := range txOpts { + opt(rawTx) + } gas, err := core.IntrinsicGas(rawTx.Data, nil, false, true, true, false) require.NoError(t, err, "need to compute intrinsic gas") rawTx.Gas = gas diff --git a/op-e2e/actions/l2_batcher_test.go b/op-e2e/actions/l2_batcher_test.go index 1f075107a510c..91df669ed6823 100644 --- a/op-e2e/actions/l2_batcher_test.go +++ b/op-e2e/actions/l2_batcher_test.go @@ -1,10 +1,13 @@ package actions import ( + "crypto/rand" + "errors" "math/big" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -12,6 +15,7 @@ import ( "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/testlog" ) @@ -44,7 +48,7 @@ func TestBatcher(gt *testing.T) { ChainID: sd.L2Cfg.Config.ChainID, Nonce: n, GasTipCap: big.NewInt(2 * params.GWei), - GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee(), big.NewInt(2*params.GWei)), + GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee, big.NewInt(2*params.GWei)), Gas: params.TxGas, To: &dp.Addresses.Bob, Value: e2eutils.Ether(2), @@ -69,7 +73,7 @@ func TestBatcher(gt *testing.T) { miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) miner.ActL1EndBlock(t) bl := miner.l1Chain.CurrentBlock() - log.Info("bl", "txs", len(bl.Transactions())) + log.Info("bl", "txs", len(miner.l1Chain.GetBlockByHash(bl.Hash()).Transactions())) // Now make enough L1 blocks that the verifier will have to derive a L2 block // It will also eagerly derive the block from the batcher @@ -378,3 +382,131 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) { sequencer.ActL2PipelineFull(t) require.Equal(t, sequencer.L2Unsafe(), sequencer.L2Safe(), "same for sequencer") } + +// TestBigL2Txs tests a high-throughput case with constrained batcher: +// - Fill 100 L2 blocks to near max-capacity, with txs of 120 KB each +// - Buffer the L2 blocks into channels together as much as possible, submit data-txs only when necessary +// (just before crossing the max RLP channel size) +// - Limit the data-tx size to 40 KB, to force data to be split across multiple datat-txs +// - Defer all data-tx inclusion till the end +// - Fill L1 blocks with data-txs until we have processed them all +// - Run the verifier, and check if it derives the same L2 chain as was created by the sequencer. +// +// The goal of this test is to quickly run through an otherwise very slow process of submitting and including lots of data. +// This does not test the batcher code, but is really focused at testing the batcher utils +// and channel-decoding verifier code in the derive package. +func TestBigL2Txs(gt *testing.T) { + t := NewDefaultTesting(gt) + p := &e2eutils.TestParams{ + MaxSequencerDrift: 100, + SequencerWindowSize: 1000, + ChannelTimeout: 200, // give enough space to buffer large amounts of data before submitting it + } + dp := e2eutils.MakeDeployParams(t, p) + sd := e2eutils.Setup(t, dp, defaultAlloc) + log := testlog.Logger(t, log.LvlInfo) + miner, engine, sequencer := setupSequencerTest(t, sd, log) + + _, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg)) + + batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ + MinL1TxSize: 0, + MaxL1TxSize: 40_000, // try a small batch size, to force the data to be split between more frames + BatcherKey: dp.Secrets.Batcher, + }, sequencer.RollupClient(), miner.EthClient(), engine.EthClient()) + + sequencer.ActL2PipelineFull(t) + + verifier.ActL2PipelineFull(t) + cl := engine.EthClient() + + batcherNonce := uint64(0) // manually track batcher nonce. the "pending nonce" value in tx-pool is incorrect after we fill the pending-block gas limit and keep adding txs to the pool. + batcherTxOpts := func(tx *types.DynamicFeeTx) { + tx.Nonce = batcherNonce + batcherNonce++ + tx.GasFeeCap = e2eutils.Ether(1) // be very generous with basefee, since we're spamming L1 + } + + // build many L2 blocks filled to the brim with large txs of random data + for i := 0; i < 100; i++ { + aliceNonce, err := cl.PendingNonceAt(t.Ctx(), dp.Addresses.Alice) + status := sequencer.SyncStatus() + // build empty L1 blocks as necessary, so the L2 sequencer can continue to include txs while not drifting too far out + if status.UnsafeL2.Time >= status.HeadL1.Time+12 { + miner.ActEmptyBlock(t) + } + sequencer.ActL1HeadSignal(t) + sequencer.ActL2StartBlock(t) + baseFee := engine.l2Chain.CurrentBlock().BaseFee // this will go quite high, since so many consecutive blocks are filled at capacity. + // fill the block with large L2 txs from alice + for n := aliceNonce; ; n++ { + require.NoError(t, err) + signer := types.LatestSigner(sd.L2Cfg.Config) + data := make([]byte, 120_000) // very large L2 txs, as large as the tx-pool will accept + _, err := rand.Read(data[:]) // fill with random bytes, to make compression ineffective + require.NoError(t, err) + gas, err := core.IntrinsicGas(data, nil, false, true, true, false) + require.NoError(t, err) + if gas > engine.l2GasPool.Gas() { + break + } + tx := types.MustSignNewTx(dp.Secrets.Alice, signer, &types.DynamicFeeTx{ + ChainID: sd.L2Cfg.Config.ChainID, + Nonce: n, + GasTipCap: big.NewInt(2 * params.GWei), + GasFeeCap: new(big.Int).Add(new(big.Int).Mul(baseFee, big.NewInt(2)), big.NewInt(2*params.GWei)), + Gas: gas, + To: &dp.Addresses.Bob, + Value: big.NewInt(0), + Data: data, + }) + require.NoError(gt, cl.SendTransaction(t.Ctx(), tx)) + engine.ActL2IncludeTx(dp.Addresses.Alice)(t) + } + sequencer.ActL2EndBlock(t) + for batcher.l2BufferedBlock.Number < sequencer.SyncStatus().UnsafeL2.Number { + // if we run out of space, close the channel and submit all the txs + if err := batcher.Buffer(t); errors.Is(err, derive.ErrTooManyRLPBytes) { + log.Info("flushing filled channel to batch txs", "id", batcher.l2ChannelOut.ID()) + batcher.ActL2ChannelClose(t) + for batcher.l2ChannelOut != nil { + batcher.ActL2BatchSubmit(t, batcherTxOpts) + } + } + } + } + + // if anything is left in the channel, submit it + if batcher.l2ChannelOut != nil { + log.Info("flushing trailing channel to batch txs", "id", batcher.l2ChannelOut.ID()) + batcher.ActL2ChannelClose(t) + for batcher.l2ChannelOut != nil { + batcher.ActL2BatchSubmit(t, batcherTxOpts) + } + } + + // build L1 blocks until we're out of txs + txs, _ := miner.eth.TxPool().ContentFrom(dp.Addresses.Batcher) + for { + if len(txs) == 0 { + break + } + miner.ActL1StartBlock(12)(t) + for range txs { + if len(txs) == 0 { + break + } + tx := txs[0] + if miner.l1GasPool.Gas() < tx.Gas() { // fill the L1 block with batcher txs until we run out of gas + break + } + log.Info("including batcher tx", "nonce", tx) + miner.IncludeTx(t, tx) + txs = txs[1:] + } + miner.ActL1EndBlock(t) + } + verifier.ActL1HeadSignal(t) + verifier.ActL2PipelineFull(t) + require.Equal(t, sequencer.SyncStatus().UnsafeL2, verifier.SyncStatus().SafeL2, "verifier synced sequencer data even though of huge tx in block") +} diff --git a/op-e2e/actions/l2_engine_api.go b/op-e2e/actions/l2_engine_api.go index 96f21274557ed..1555f775edeab 100644 --- a/op-e2e/actions/l2_engine_api.go +++ b/op-e2e/actions/l2_engine_api.go @@ -202,30 +202,30 @@ func (ea *L2EngineAPI) ForkchoiceUpdatedV1(ctx context.Context, state *eth.Forkc // chain final and completely in PoS mode. if state.FinalizedBlockHash != (common.Hash{}) { // If the finalized block is not in our canonical tree, somethings wrong - finalBlock := ea.l2Chain.GetBlockByHash(state.FinalizedBlockHash) - if finalBlock == nil { + finalHeader := ea.l2Chain.GetHeaderByHash(state.FinalizedBlockHash) + if finalHeader == nil { ea.log.Warn("Final block not available in database", "hash", state.FinalizedBlockHash) return STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not available in database")) - } else if rawdb.ReadCanonicalHash(ea.l2Database, finalBlock.NumberU64()) != state.FinalizedBlockHash { + } else if rawdb.ReadCanonicalHash(ea.l2Database, finalHeader.Number.Uint64()) != state.FinalizedBlockHash { ea.log.Warn("Final block not in canonical chain", "number", block.NumberU64(), "hash", state.HeadBlockHash) return STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("final block not in canonical chain")) } // Set the finalized block - ea.l2Chain.SetFinalized(finalBlock) + ea.l2Chain.SetFinalized(finalHeader) } // Check if the safe block hash is in our canonical tree, if not somethings wrong if state.SafeBlockHash != (common.Hash{}) { - safeBlock := ea.l2Chain.GetBlockByHash(state.SafeBlockHash) - if safeBlock == nil { + safeHeader := ea.l2Chain.GetHeaderByHash(state.SafeBlockHash) + if safeHeader == nil { ea.log.Warn("Safe block not available in database") return STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("safe block not available in database")) } - if rawdb.ReadCanonicalHash(ea.l2Database, safeBlock.NumberU64()) != state.SafeBlockHash { + if rawdb.ReadCanonicalHash(ea.l2Database, safeHeader.Number.Uint64()) != state.SafeBlockHash { ea.log.Warn("Safe block not in canonical chain") return STATUS_INVALID, engine.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain")) } // Set the safe block - ea.l2Chain.SetSafe(safeBlock) + ea.l2Chain.SetSafe(safeHeader) } // If payload generation was requested, create a new block to be potentially // sealed by the beacon client. The payload will be requested later, and we diff --git a/op-e2e/actions/l2_engine_test.go b/op-e2e/actions/l2_engine_test.go index 3f60f60b780dc..5418d10b11e18 100644 --- a/op-e2e/actions/l2_engine_test.go +++ b/op-e2e/actions/l2_engine_test.go @@ -107,7 +107,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) { ChainID: sd.L2Cfg.Config.ChainID, Nonce: 0, GasTipCap: big.NewInt(2 * params.GWei), - GasFeeCap: new(big.Int).Add(engine.l2Chain.CurrentBlock().BaseFee(), big.NewInt(2*params.GWei)), + GasFeeCap: new(big.Int).Add(engine.l2Chain.CurrentBlock().BaseFee, big.NewInt(2*params.GWei)), Gas: params.TxGas, To: &dp.Addresses.Bob, Value: e2eutils.Ether(2), @@ -125,7 +125,7 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) { SafeBlockHash: genesisBlock.Hash(), FinalizedBlockHash: genesisBlock.Hash(), }, ð.PayloadAttributes{ - Timestamp: eth.Uint64Quantity(parent.Time()) + 2, + Timestamp: eth.Uint64Quantity(parent.Time) + 2, PrevRandao: eth.Bytes32{}, SuggestedFeeRecipient: common.Address{'C'}, Transactions: nil, @@ -161,12 +161,12 @@ func TestL2EngineAPIBlockBuilding(gt *testing.T) { require.Equal(t, payload.BlockHash, engine.l2Chain.CurrentBlock().Hash(), "now payload is canonical") } buildBlock(false) - require.Zero(t, engine.l2Chain.CurrentBlock().Transactions().Len(), "no tx included") + require.Zero(t, engine.l2Chain.GetBlockByHash(engine.l2Chain.CurrentBlock().Hash()).Transactions().Len(), "no tx included") buildBlock(true) - require.Equal(gt, 1, engine.l2Chain.CurrentBlock().Transactions().Len(), "tx from alice is included") + require.Equal(gt, 1, engine.l2Chain.GetBlockByHash(engine.l2Chain.CurrentBlock().Hash()).Transactions().Len(), "tx from alice is included") buildBlock(false) - require.Zero(t, engine.l2Chain.CurrentBlock().Transactions().Len(), "no tx included") - require.Equal(t, uint64(3), engine.l2Chain.CurrentBlock().NumberU64(), "built 3 blocks") + require.Zero(t, engine.l2Chain.GetBlockByHash(engine.l2Chain.CurrentBlock().Hash()).Transactions().Len(), "no tx included") + require.Equal(t, uint64(3), engine.l2Chain.CurrentBlock().Number.Uint64(), "built 3 blocks") } func TestL2EngineAPIFail(gt *testing.T) { diff --git a/op-e2e/actions/l2_sequencer_test.go b/op-e2e/actions/l2_sequencer_test.go index ed1c8d51c7cc4..5a62617d730d6 100644 --- a/op-e2e/actions/l2_sequencer_test.go +++ b/op-e2e/actions/l2_sequencer_test.go @@ -55,7 +55,7 @@ func TestL2Sequencer_SequencerDrift(gt *testing.T) { ChainID: sd.L2Cfg.Config.ChainID, Nonce: n, GasTipCap: big.NewInt(2 * params.GWei), - GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee(), big.NewInt(2*params.GWei)), + GasFeeCap: new(big.Int).Add(miner.l1Chain.CurrentBlock().BaseFee, big.NewInt(2*params.GWei)), Gas: params.TxGas, To: &dp.Addresses.Bob, Value: e2eutils.Ether(2), @@ -76,7 +76,7 @@ func TestL2Sequencer_SequencerDrift(gt *testing.T) { origin := miner.l1Chain.CurrentBlock() // L2 makes blocks to catch up - for sequencer.SyncStatus().UnsafeL2.Time+sd.RollupCfg.BlockTime < origin.Time() { + for sequencer.SyncStatus().UnsafeL2.Time+sd.RollupCfg.BlockTime < origin.Time { makeL2BlockWithAliceTx() require.Equal(t, uint64(0), sequencer.SyncStatus().UnsafeL2.L1Origin.Number, "no L1 origin change before time matches") } @@ -89,7 +89,7 @@ func TestL2Sequencer_SequencerDrift(gt *testing.T) { sequencer.ActL1HeadSignal(t) // Make blocks up till the sequencer drift is about to surpass, but keep the old L1 origin - for sequencer.SyncStatus().UnsafeL2.Time+sd.RollupCfg.BlockTime <= origin.Time()+sd.RollupCfg.MaxSequencerDrift { + for sequencer.SyncStatus().UnsafeL2.Time+sd.RollupCfg.BlockTime <= origin.Time+sd.RollupCfg.MaxSequencerDrift { sequencer.ActL2KeepL1Origin(t) makeL2BlockWithAliceTx() require.Equal(t, uint64(1), sequencer.SyncStatus().UnsafeL2.L1Origin.Number, "expected to keep old L1 origin") diff --git a/op-e2e/actions/l2_verifier_test.go b/op-e2e/actions/l2_verifier_test.go index 916e05c5b396e..c402558f8f823 100644 --- a/op-e2e/actions/l2_verifier_test.go +++ b/op-e2e/actions/l2_verifier_test.go @@ -41,20 +41,20 @@ func TestL2Verifier_SequenceWindow(gt *testing.T) { miner.ActL1SetFeeRecipient(common.Address{'A'}) // Make two sequence windows worth of empty L1 blocks. After we pass the first sequence window, the L2 chain should get blocks - for miner.l1Chain.CurrentBlock().NumberU64() < sd.RollupCfg.SeqWindowSize*2 { + for miner.l1Chain.CurrentBlock().Number.Uint64() < sd.RollupCfg.SeqWindowSize*2 { miner.ActL1StartBlock(10)(t) miner.ActL1EndBlock(t) verifier.ActL2PipelineFull(t) - l1Head := miner.l1Chain.CurrentBlock().NumberU64() + l1Head := miner.l1Chain.CurrentBlock().Number.Uint64() expectedL1Origin := uint64(0) // as soon as we complete the sequence window, we force-adopt the L1 origin if l1Head >= sd.RollupCfg.SeqWindowSize { expectedL1Origin = l1Head - sd.RollupCfg.SeqWindowSize } require.Equal(t, expectedL1Origin, verifier.SyncStatus().SafeL2.L1Origin.Number, "L1 origin is forced in, given enough L1 blocks pass by") - require.LessOrEqual(t, miner.l1Chain.GetBlockByNumber(expectedL1Origin).Time(), engine.l2Chain.CurrentBlock().Time(), "L2 time higher than L1 origin time") + require.LessOrEqual(t, miner.l1Chain.GetBlockByNumber(expectedL1Origin).Time(), engine.l2Chain.CurrentBlock().Time, "L2 time higher than L1 origin time") } tip2N := verifier.SyncStatus() @@ -75,7 +75,7 @@ func TestL2Verifier_SequenceWindow(gt *testing.T) { verifier.ActL2PipelineFull(t) require.Equal(t, tip2N.SafeL2, verifier.SyncStatus().SafeL2) - for miner.l1Chain.CurrentBlock().NumberU64() < sd.RollupCfg.SeqWindowSize*2 { + for miner.l1Chain.CurrentBlock().Number.Uint64() < sd.RollupCfg.SeqWindowSize*2 { miner.ActL1StartBlock(10)(t) miner.ActL1EndBlock(t) } diff --git a/op-e2e/actions/system_config_test.go b/op-e2e/actions/system_config_test.go index e4baf3d5c9131..018c4e0f698be 100644 --- a/op-e2e/actions/system_config_test.go +++ b/op-e2e/actions/system_config_test.go @@ -80,7 +80,7 @@ func TestBatcherKeyRotation(gt *testing.T) { miner.ActL1StartBlock(12)(t) miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t) miner.ActL1EndBlock(t) - cfgChangeL1BlockNum := miner.l1Chain.CurrentBlock().NumberU64() + cfgChangeL1BlockNum := miner.l1Chain.CurrentBlock().Number.Uint64() // sequence L2 blocks, and submit with new batcher sequencer.ActL1HeadSignal(t) @@ -200,7 +200,7 @@ func TestGPOParamsChange(gt *testing.T) { miner.ActEmptyBlock(t) sequencer.ActL1HeadSignal(t) sequencer.ActBuildToL1Head(t) - basefee := miner.l1Chain.CurrentBlock().BaseFee() + basefee := miner.l1Chain.CurrentBlock().BaseFee // alice makes a L2 tx, sequencer includes it alice.ActResetTxOpts(t) @@ -238,7 +238,7 @@ func TestGPOParamsChange(gt *testing.T) { miner.ActL1StartBlock(12)(t) miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t) miner.ActL1EndBlock(t) - basefeeGPOUpdate := miner.l1Chain.CurrentBlock().BaseFee() + basefeeGPOUpdate := miner.l1Chain.CurrentBlock().BaseFee // build empty L2 chain, up to but excluding the L2 block with the L1 origin that processes the GPO change sequencer.ActL1HeadSignal(t) @@ -274,7 +274,7 @@ func TestGPOParamsChange(gt *testing.T) { // build more L2 blocks, with new L1 origin miner.ActEmptyBlock(t) - basefee = miner.l1Chain.CurrentBlock().BaseFee() + basefee = miner.l1Chain.CurrentBlock().BaseFee sequencer.ActL1HeadSignal(t) sequencer.ActBuildToL1Head(t) // and Alice makes a tx again @@ -313,7 +313,7 @@ func TestGasLimitChange(gt *testing.T) { sequencer.ActL1HeadSignal(t) sequencer.ActBuildToL1Head(t) - oldGasLimit := seqEngine.l2Chain.CurrentBlock().GasLimit() + oldGasLimit := seqEngine.l2Chain.CurrentBlock().GasLimit require.Equal(t, oldGasLimit, uint64(dp.DeployConfig.L2GenesisBlockGasLimit)) // change gas limit on L1 to triple what it was @@ -335,12 +335,12 @@ func TestGasLimitChange(gt *testing.T) { sequencer.ActL1HeadSignal(t) sequencer.ActBuildToL1HeadExcl(t) - require.Equal(t, oldGasLimit, seqEngine.l2Chain.CurrentBlock().GasLimit()) + require.Equal(t, oldGasLimit, seqEngine.l2Chain.CurrentBlock().GasLimit) require.Equal(t, uint64(1), sequencer.SyncStatus().UnsafeL2.L1Origin.Number) // now include the L1 block with the gaslimit change, and see if it changes as expected sequencer.ActBuildToL1Head(t) - require.Equal(t, oldGasLimit*3, seqEngine.l2Chain.CurrentBlock().GasLimit()) + require.Equal(t, oldGasLimit*3, seqEngine.l2Chain.CurrentBlock().GasLimit) require.Equal(t, uint64(2), sequencer.SyncStatus().UnsafeL2.L1Origin.Number) // now submit all this to L1, and see if a verifier can sync and reproduce it diff --git a/op-e2e/actions/user_test.go b/op-e2e/actions/user_test.go index 0dd75be73a517..fd0385b117eae 100644 --- a/op-e2e/actions/user_test.go +++ b/op-e2e/actions/user_test.go @@ -132,7 +132,7 @@ func runCrossLayerUserTest(gt *testing.T, test regolithScheduledTest) { seq.ActL1HeadSignal(t) // sync sequencer build enough blocks to adopt latest L1 origin - for seq.SyncStatus().UnsafeL2.L1Origin.Number < miner.l1Chain.CurrentBlock().NumberU64() { + for seq.SyncStatus().UnsafeL2.L1Origin.Number < miner.l1Chain.CurrentBlock().Number.Uint64() { seq.ActL2StartBlock(t) seq.ActL2EndBlock(t) } diff --git a/op-e2e/bridge_test.go b/op-e2e/bridge_test.go new file mode 100644 index 0000000000000..d3d65a591b47a --- /dev/null +++ b/op-e2e/bridge_test.go @@ -0,0 +1,120 @@ +package op_e2e + +import ( + "math" + "math/big" + "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/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/testlog" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +// TestERC20BridgeDeposits tests the the L1StandardBridge bridge ERC20 +// functionality. +func TestERC20BridgeDeposits(t *testing.T) { + parallel(t) + if !verboseGethNodes { + log.Root().SetHandler(log.DiscardHandler()) + } + + cfg := DefaultSystemConfig(t) + + sys, err := cfg.Start() + require.Nil(t, err, "Error starting up system") + defer sys.Close() + + log := testlog.Logger(t, log.LvlInfo) + log.Info("genesis", "l2", sys.RollupConfig.Genesis.L2, "l1", sys.RollupConfig.Genesis.L1, "l2_time", sys.RollupConfig.Genesis.L2Time) + + l1Client := sys.Clients["l1"] + l2Client := sys.Clients["sequencer"] + + opts, err := bind.NewKeyedTransactorWithChainID(sys.cfg.Secrets.Alice, cfg.L1ChainIDBig()) + require.Nil(t, err) + + // Deploy WETH9 + weth9Address, tx, WETH9, err := bindings.DeployWETH9(opts, l1Client) + require.NoError(t, err) + _, err = waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err, "Waiting for deposit tx on L1") + + // Get some WETH + opts.Value = big.NewInt(params.Ether) + tx, err = WETH9.Fallback(opts, []byte{}) + require.NoError(t, err) + _, err = waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err) + opts.Value = nil + wethBalance, err := WETH9.BalanceOf(&bind.CallOpts{}, opts.From) + require.NoError(t, err) + require.Equal(t, big.NewInt(params.Ether), wethBalance) + + // Deploy L2 WETH9 + l2Opts, err := bind.NewKeyedTransactorWithChainID(sys.cfg.Secrets.Alice, cfg.L2ChainIDBig()) + require.NoError(t, err) + optimismMintableTokenFactory, err := bindings.NewOptimismMintableERC20Factory(predeploys.OptimismMintableERC20FactoryAddr, l2Client) + require.NoError(t, err) + tx, err = optimismMintableTokenFactory.CreateOptimismMintableERC20(l2Opts, weth9Address, "L2-WETH", "L2-WETH") + require.NoError(t, err) + _, err = waitForTransaction(tx.Hash(), l2Client, 3*time.Duration(cfg.DeployConfig.L2BlockTime)*time.Second) + require.NoError(t, err) + + // Get the deployment event to have access to the L2 WETH9 address + it, err := optimismMintableTokenFactory.FilterOptimismMintableERC20Created(&bind.FilterOpts{Start: 0}, nil, nil) + require.NoError(t, err) + var event *bindings.OptimismMintableERC20FactoryOptimismMintableERC20Created + for it.Next() { + event = it.Event + } + require.NotNil(t, event) + + // Approve WETH9 with the bridge + tx, err = WETH9.Approve(opts, predeploys.DevL1StandardBridgeAddr, new(big.Int).SetUint64(math.MaxUint64)) + require.NoError(t, err) + _, err = waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err) + + // Bridge the WETH9 + l1StandardBridge, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, l1Client) + require.NoError(t, err) + tx, err = l1StandardBridge.BridgeERC20(opts, weth9Address, event.LocalToken, big.NewInt(100), 100000, []byte{}) + require.NoError(t, err) + depositReceipt, err := waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) + require.NoError(t, err) + + t.Log("Deposit through L1StandardBridge", "gas used", depositReceipt.GasUsed) + + // compute the deposit transaction hash + poll for it + portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) + require.NoError(t, err) + + depIt, err := portal.FilterTransactionDeposited(&bind.FilterOpts{Start: 0}, nil, nil, nil) + require.NoError(t, err) + var depositEvent *bindings.OptimismPortalTransactionDeposited + for depIt.Next() { + depositEvent = depIt.Event + } + require.NotNil(t, depositEvent) + + depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) + require.NoError(t, err) + _, err = waitForTransaction(types.NewTx(depositTx).Hash(), l2Client, 3*time.Duration(cfg.DeployConfig.L2BlockTime)*time.Second) + require.NoError(t, err) + + // Ensure that the deposit went through + optimismMintableToken, err := bindings.NewOptimismMintableERC20(event.LocalToken, l2Client) + require.NoError(t, err) + + // Should have balance on L2 + l2Balance, err := optimismMintableToken.BalanceOf(&bind.CallOpts{}, opts.From) + require.NoError(t, err) + require.Equal(t, l2Balance, big.NewInt(100)) +} diff --git a/op-e2e/e2eutils/setup.go b/op-e2e/e2eutils/setup.go index 7fa6fe6bcc963..c86b47578cc12 100644 --- a/op-e2e/e2eutils/setup.go +++ b/op-e2e/e2eutils/setup.go @@ -169,7 +169,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * if alloc.PrefundTestUsers { for _, addr := range deployParams.Addresses.All() { l1Genesis.Alloc[addr] = core.GenesisAccount{ - Balance: Ether(1e6), + Balance: Ether(1e12), } } } @@ -184,7 +184,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * if alloc.PrefundTestUsers { for _, addr := range deployParams.Addresses.All() { l2Genesis.Alloc[addr] = core.GenesisAccount{ - Balance: Ether(1e6), + Balance: Ether(1e12), } } } diff --git a/op-e2e/e2eutils/setup_test.go b/op-e2e/e2eutils/setup_test.go index 78cbff32d9272..b53c41e645a98 100644 --- a/op-e2e/e2eutils/setup_test.go +++ b/op-e2e/e2eutils/setup_test.go @@ -27,10 +27,10 @@ func TestSetup(t *testing.T) { alloc := &AllocParams{PrefundTestUsers: true} sd := Setup(t, dp, alloc) require.Contains(t, sd.L1Cfg.Alloc, dp.Addresses.Alice) - require.Equal(t, sd.L1Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e6)) + require.Equal(t, sd.L1Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e12)) require.Contains(t, sd.L2Cfg.Alloc, dp.Addresses.Alice) - require.Equal(t, sd.L2Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e6)) + require.Equal(t, sd.L2Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e12)) require.Contains(t, sd.L1Cfg.Alloc, predeploys.DevOptimismPortalAddr) require.Contains(t, sd.L2Cfg.Alloc, predeploys.L1BlockAddr) diff --git a/op-e2e/geth.go b/op-e2e/geth.go index 1af7be4e49b56..966abafb1f81f 100644 --- a/op-e2e/geth.go +++ b/op-e2e/geth.go @@ -160,11 +160,11 @@ func (f *fakeSafeFinalizedL1) Start() error { case head := <-headChanges: num := head.Block.NumberU64() if num > f.finalizedDistance { - toFinalize := f.eth.BlockChain().GetBlockByNumber(num - f.finalizedDistance) + toFinalize := f.eth.BlockChain().GetHeaderByNumber(num - f.finalizedDistance) f.eth.BlockChain().SetFinalized(toFinalize) } if num > f.safeDistance { - toSafe := f.eth.BlockChain().GetBlockByNumber(num - f.safeDistance) + toSafe := f.eth.BlockChain().GetHeaderByNumber(num - f.safeDistance) f.eth.BlockChain().SetSafe(toSafe) } case <-quit: diff --git a/op-e2e/migration_test.go b/op-e2e/migration_test.go index 1d646a5808b69..69bf616e65065 100644 --- a/op-e2e/migration_test.go +++ b/op-e2e/migration_test.go @@ -12,6 +12,7 @@ import ( "time" bss "github.com/ethereum-optimism/optimism/op-batcher/batcher" + batchermetrics "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/sources" l2os "github.com/ethereum-optimism/optimism/op-proposer/proposer" @@ -341,7 +342,7 @@ func TestMigration(t *testing.T) { Format: "text", }, PrivateKey: hexPriv(secrets.Batcher), - }, lgr.New("module", "batcher")) + }, lgr.New("module", "batcher"), batchermetrics.NoopMetrics) require.NoError(t, err) t.Cleanup(func() { batcher.StopIfRunning() diff --git a/op-e2e/setup.go b/op-e2e/setup.go index 3c360f05a3736..fea710838265f 100644 --- a/op-e2e/setup.go +++ b/op-e2e/setup.go @@ -7,6 +7,7 @@ import ( "math/big" "os" "path" + "sort" "strings" "testing" "time" @@ -23,6 +24,7 @@ import ( "github.com/stretchr/testify/require" bss "github.com/ethereum-optimism/optimism/op-batcher/batcher" + batchermetrics "github.com/ethereum-optimism/optimism/op-batcher/metrics" "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" @@ -119,14 +121,6 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { JWTFilePath: writeDefaultJWT(t), JWTSecret: testingJWTSecret, Nodes: map[string]*rollupNode.Config{ - "verifier": { - Driver: driver.Config{ - VerifierConfDepth: 0, - SequencerConfDepth: 0, - SequencerEnabled: false, - }, - L1EpochPollInterval: time.Second * 4, - }, "sequencer": { Driver: driver.Config{ VerifierConfDepth: 0, @@ -141,6 +135,14 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { }, L1EpochPollInterval: time.Second * 4, }, + "verifier": { + Driver: driver.Config{ + VerifierConfDepth: 0, + SequencerConfDepth: 0, + SequencerEnabled: false, + }, + L1EpochPollInterval: time.Second * 4, + }, }, Loggers: map[string]log.Logger{ "verifier": testlog.Logger(t, log.LvlInfo).New("role", "verifier"), @@ -225,7 +227,43 @@ func (sys *System) Close() { sys.Mocknet.Close() } -func (cfg SystemConfig) Start() (*System, error) { +type systemConfigHook func(sCfg *SystemConfig, s *System) + +type SystemConfigOption struct { + key string + role string + action systemConfigHook +} + +type SystemConfigOptions struct { + opts map[string]systemConfigHook +} + +func NewSystemConfigOptions(_opts []SystemConfigOption) (SystemConfigOptions, error) { + opts := make(map[string]systemConfigHook) + for _, opt := range _opts { + if _, ok := opts[opt.key+":"+opt.role]; ok { + return SystemConfigOptions{}, fmt.Errorf("duplicate option for key %s and role %s", opt.key, opt.role) + } + opts[opt.key+":"+opt.role] = opt.action + } + + return SystemConfigOptions{ + opts: opts, + }, nil +} + +func (s *SystemConfigOptions) Get(key, role string) (systemConfigHook, bool) { + v, ok := s.opts[key+":"+role] + return v, ok +} + +func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { + opts, err := NewSystemConfigOptions(_opts) + if err != nil { + return nil, err + } + sys := &System{ cfg: cfg, Nodes: make(map[string]*node.Node), @@ -457,7 +495,17 @@ func (cfg SystemConfig) Start() (*System, error) { snapLog.SetHandler(log.DiscardHandler()) // Rollup nodes - for name, nodeConfig := range cfg.Nodes { + + // Ensure we are looping through the nodes in alphabetical order + ks := make([]string, 0, len(cfg.Nodes)) + for k := range cfg.Nodes { + ks = append(ks, k) + } + // Sort strings in ascending alphabetical order + sort.Strings(ks) + + for _, name := range ks { + nodeConfig := cfg.Nodes[name] c := *nodeConfig // copy c.Rollup = makeRollupConfig() @@ -482,6 +530,10 @@ func (cfg SystemConfig) Start() (*System, error) { return nil, err } sys.RollupNodes[name] = node + + if action, ok := opts.Get("afterRollupNodeStart", name); ok { + action(&cfg, sys) + } } if cfg.P2PTopology != nil { @@ -549,7 +601,7 @@ func (cfg SystemConfig) Start() (*System, error) { Format: "text", }, PrivateKey: hexPriv(cfg.Secrets.Batcher), - }, sys.cfg.Loggers["batcher"]) + }, sys.cfg.Loggers["batcher"], batchermetrics.NoopMetrics) if err != nil { return nil, fmt.Errorf("failed to setup batch submitter: %w", err) } diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index 07e1bdcb54502..f29d9aa88b98e 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -651,8 +651,94 @@ func TestSystemMockP2P(t *testing.T) { require.Contains(t, received, receiptVerif.BlockHash) } +// TestSystemMockP2P sets up a L1 Geth node, a rollup node, and a L2 geth node and then confirms that +// the nodes can sync L2 blocks before they are confirmed on L1. +// +// Test steps: +// 1. Spin up the nodes (P2P is disabled on the verifier) +// 2. Send a transaction to the sequencer. +// 3. Wait for the TX to be mined on the sequencer chain. +// 5. Wait for the verifier to detect a gap in the payload queue vs. the unsafe head +// 6. Wait for the RPC sync method to grab the block from the sequencer over RPC and insert it into the verifier's unsafe chain. +// 7. Wait for the verifier to sync the unsafe chain into the safe chain. +// 8. Verify that the TX is included in the verifier's safe chain. +func TestSystemMockAltSync(t *testing.T) { + parallel(t) + if !verboseGethNodes { + log.Root().SetHandler(log.DiscardHandler()) + } + + cfg := DefaultSystemConfig(t) + // slow down L1 blocks so we can see the L2 blocks arrive well before the L1 blocks do. + // Keep the seq window small so the L2 chain is started quick + cfg.DeployConfig.L1BlockTime = 10 + + var published, received []common.Hash + seqTracer, verifTracer := new(FnTracer), new(FnTracer) + seqTracer.OnPublishL2PayloadFn = func(ctx context.Context, payload *eth.ExecutionPayload) { + published = append(published, payload.BlockHash) + } + verifTracer.OnUnsafeL2PayloadFn = func(ctx context.Context, from peer.ID, payload *eth.ExecutionPayload) { + received = append(received, payload.BlockHash) + } + cfg.Nodes["sequencer"].Tracer = seqTracer + cfg.Nodes["verifier"].Tracer = verifTracer + + sys, err := cfg.Start(SystemConfigOption{ + key: "afterRollupNodeStart", + role: "sequencer", + action: func(sCfg *SystemConfig, system *System) { + rpc, _ := system.Nodes["sequencer"].Attach() // never errors + cfg.Nodes["verifier"].L2Sync = &rollupNode.L2SyncRPCConfig{ + Rpc: client.NewBaseRPCClient(rpc), + } + }, + }) + require.Nil(t, err, "Error starting up system") + defer sys.Close() + + l2Seq := sys.Clients["sequencer"] + l2Verif := sys.Clients["verifier"] + + // Transactor Account + ethPrivKey := cfg.Secrets.Alice + + // Submit a TX to L2 sequencer node + toAddr := common.Address{0xff, 0xff} + tx := types.MustSignNewTx(ethPrivKey, types.LatestSignerForChainID(cfg.L2ChainIDBig()), &types.DynamicFeeTx{ + ChainID: cfg.L2ChainIDBig(), + Nonce: 0, + To: &toAddr, + Value: big.NewInt(1_000_000_000), + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 21000, + }) + err = l2Seq.SendTransaction(context.Background(), tx) + require.Nil(t, err, "Sending L2 tx to sequencer") + + // Wait for tx to be mined on the L2 sequencer chain + receiptSeq, err := waitForTransaction(tx.Hash(), l2Seq, 6*time.Duration(sys.RollupConfig.BlockTime)*time.Second) + require.Nil(t, err, "Waiting for L2 tx on sequencer") + + // Wait for alt RPC sync to pick up the blocks on the sequencer chain + receiptVerif, err := waitForTransaction(tx.Hash(), l2Verif, 12*time.Duration(sys.RollupConfig.BlockTime)*time.Second) + require.Nil(t, err, "Waiting for L2 tx on verifier") + + require.Equal(t, receiptSeq, receiptVerif) + + // Verify that the tx was received via RPC sync (P2P is disabled) + require.Contains(t, received, receiptVerif.BlockHash) + + // Verify that everything that was received was published + require.GreaterOrEqual(t, len(published), len(received)) + require.ElementsMatch(t, received, published[:len(received)]) +} + // TestSystemDenseTopology sets up a dense p2p topology with 3 verifier nodes and 1 sequencer node. func TestSystemDenseTopology(t *testing.T) { + t.Skip("Skipping dense topology test to avoid flakiness. @refcell address in p2p scoring pr.") + parallel(t) if !verboseGethNodes { log.Root().SetHandler(log.DiscardHandler()) diff --git a/op-exporter/Dockerfile b/op-exporter/Dockerfile index 33586f7e8aca0..1861a08bb84a3 100644 --- a/op-exporter/Dockerfile +++ b/op-exporter/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18.0-alpine3.15 as builder +FROM golang:1.19.0-alpine3.15 as builder # build from root of repo COPY ./op-exporter /app diff --git a/op-node/Dockerfile b/op-node/Dockerfile index fda7cac0971aa..9bc1600bff1b9 100644 --- a/op-node/Dockerfile +++ b/op-node/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.18.0-alpine3.15 as builder +FROM --platform=$BUILDPLATFORM golang:1.19.0-alpine3.15 as builder ARG VERSION=v0.0.0 diff --git a/op-node/cmd/batch_decoder/README.md b/op-node/cmd/batch_decoder/README.md index 5c3b7639e2f1c..d2454ec3d68e4 100644 --- a/op-node/cmd/batch_decoder/README.md +++ b/op-node/cmd/batch_decoder/README.md @@ -26,6 +26,16 @@ the transaction hash. into channels. It then stores the channels with metadata on disk where the file name is the Channel ID. +### Force Close + +`batch_decoder force-close` will create a transaction data that can be sent from the batcher address to +the batch inbox address which will force close the given channels. This will allow future channels to +be read without waiting for the channel timeout. It uses uses the results from `batch_decoder fetch` to +create the close transaction because the transaction it creates for a specific channel requires information +about if the channel has been closed or not. If it has been closed already but is missing specific frames +those frames need to be generated differently than simply closing the channel. + + ## JQ Cheat Sheet `jq` is a really useful utility for manipulating JSON files. @@ -48,7 +58,6 @@ jq "select(.is_ready == false)|[.id, .frames[0].inclusion_block, .frames[0].tran ## Roadmap - Parallel transaction fetching (CLI-3563) -- Create force-close channel tx data from channel ID (CLI-3564) - Pull the batches out of channels & store that information inside the ChannelWithMetadata (CLI-3565) - Transaction Bytes used - Total uncompressed (different from tx bytes) + compressed bytes diff --git a/op-node/cmd/batch_decoder/main.go b/op-node/cmd/batch_decoder/main.go index 552f88aa38e94..d63b32de63c4a 100644 --- a/op-node/cmd/batch_decoder/main.go +++ b/op-node/cmd/batch_decoder/main.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/fetch" "github.com/ethereum-optimism/optimism/op-node/cmd/batch_decoder/reassemble" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/urfave/cli" @@ -113,6 +114,46 @@ func main() { return nil }, }, + { + Name: "force-close", + Usage: "Create the tx data which will force close a channel", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id", + Required: true, + Usage: "ID of the channel to close", + }, + cli.StringFlag{ + Name: "inbox", + Value: "0x0000000000000000000000000000000000000000", + Usage: "(Optional) Batch Inbox Address", + }, + cli.StringFlag{ + Name: "in", + Value: "/tmp/batch_decoder/transactions_cache", + Usage: "Cache directory for the found transactions", + }, + }, + Action: func(cliCtx *cli.Context) error { + var id derive.ChannelID + if err := (&id).UnmarshalText([]byte(cliCtx.String("id"))); err != nil { + log.Fatal(err) + } + frames := reassemble.LoadFrames(cliCtx.String("in"), common.HexToAddress(cliCtx.String("inbox"))) + var filteredFrames []derive.Frame + for _, frame := range frames { + if frame.Frame.ID == id { + filteredFrames = append(filteredFrames, frame.Frame) + } + } + data, err := derive.ForceCloseTxData(filteredFrames) + if err != nil { + log.Fatal(err) + } + fmt.Printf("%x\n", data) + return nil + }, + }, } if err := app.Run(os.Args); err != nil { diff --git a/op-node/cmd/batch_decoder/reassemble/reassemble.go b/op-node/cmd/batch_decoder/reassemble/reassemble.go index a92c0363dd899..79b67dc048df5 100644 --- a/op-node/cmd/batch_decoder/reassemble/reassemble.go +++ b/op-node/cmd/batch_decoder/reassemble/reassemble.go @@ -38,14 +38,8 @@ type Config struct { OutDirectory string } -// Channels loads all transactions from the given input directory that are submitted to the -// specified batch inbox and then re-assembles all channels & writes the re-assembled channels -// to the out directory. -func Channels(config Config) { - if err := os.MkdirAll(config.OutDirectory, 0750); err != nil { - log.Fatal(err) - } - txns := loadTransactions(config.InDirectory, config.BatchInbox) +func LoadFrames(directory string, inbox common.Address) []FrameWithMetadata { + txns := loadTransactions(directory, inbox) // Sort first by block number then by transaction index inside the block number range. // This is to match the order they are processed in derivation. sort.Slice(txns, func(i, j int) bool { @@ -56,7 +50,17 @@ func Channels(config Config) { } }) - frames := transactionsToFrames(txns) + return transactionsToFrames(txns) +} + +// Channels loads all transactions from the given input directory that are submitted to the +// specified batch inbox and then re-assembles all channels & writes the re-assembled channels +// to the out directory. +func Channels(config Config) { + if err := os.MkdirAll(config.OutDirectory, 0750); err != nil { + log.Fatal(err) + } + frames := LoadFrames(config.InDirectory, config.BatchInbox) framesByChannel := make(map[derive.ChannelID][]FrameWithMetadata) for _, frame := range frames { framesByChannel[frame.Frame.ID] = append(framesByChannel[frame.Frame.ID], frame) @@ -143,6 +147,7 @@ func transactionsToFrames(txns []fetch.TransactionWithMetadata) []FrameWithMetad return out } +// if inbox is the zero address, it will load all frames func loadTransactions(dir string, inbox common.Address) []fetch.TransactionWithMetadata { files, err := os.ReadDir(dir) if err != nil { @@ -152,7 +157,7 @@ func loadTransactions(dir string, inbox common.Address) []fetch.TransactionWithM for _, file := range files { f := path.Join(dir, file.Name()) txm := loadTransactionsFile(f) - if txm.InboxAddr == inbox && txm.ValidSender { + if (inbox == common.Address{} || txm.InboxAddr == inbox) && txm.ValidSender { out = append(out, txm) } } diff --git a/op-node/flags/flags.go b/op-node/flags/flags.go index 1f6c418510fee..ddb06925e0e8e 100644 --- a/op-node/flags/flags.go +++ b/op-node/flags/flags.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/sources" + oplog "github.com/ethereum-optimism/optimism/op-service/log" "github.com/urfave/cli" ) @@ -120,23 +121,6 @@ var ( Required: false, Value: time.Second * 12 * 32, } - LogLevelFlag = cli.StringFlag{ - Name: "log.level", - Usage: "The lowest log level that will be output", - Value: "info", - EnvVar: prefixEnvVar("LOG_LEVEL"), - } - LogFormatFlag = cli.StringFlag{ - Name: "log.format", - Usage: "Format the log output. Supported formats: 'text', 'json'", - Value: "text", - EnvVar: prefixEnvVar("LOG_FORMAT"), - } - LogColorFlag = cli.BoolFlag{ - Name: "log.color", - Usage: "Color the log output", - EnvVar: prefixEnvVar("LOG_COLOR"), - } MetricsEnabledFlag = cli.BoolFlag{ Name: "metrics.enabled", Usage: "Enable the metrics server", @@ -192,6 +176,12 @@ var ( EnvVar: prefixEnvVar("HEARTBEAT_URL"), Value: "https://heartbeat.optimism.io", } + BackupL2UnsafeSyncRPC = cli.StringFlag{ + Name: "l2.backup-unsafe-sync-rpc", + Usage: "Set the backup L2 unsafe sync RPC endpoint.", + EnvVar: prefixEnvVar("L2_BACKUP_UNSAFE_SYNC_RPC"), + Required: false, + } ) var requiredFlags = []cli.Flag{ @@ -201,7 +191,7 @@ var requiredFlags = []cli.Flag{ RPCListenPort, } -var optionalFlags = append([]cli.Flag{ +var optionalFlags = []cli.Flag{ RollupConfig, Network, L1TrustRPC, @@ -213,9 +203,6 @@ var optionalFlags = append([]cli.Flag{ SequencerMaxSafeLagFlag, SequencerL1Confs, L1EpochPollIntervalFlag, - LogLevelFlag, - LogFormatFlag, - LogColorFlag, RPCEnableAdmin, MetricsEnabledFlag, MetricsAddrFlag, @@ -227,10 +214,17 @@ var optionalFlags = append([]cli.Flag{ HeartbeatEnabledFlag, HeartbeatMonikerFlag, HeartbeatURLFlag, -}, p2pFlags...) + BackupL2UnsafeSyncRPC, +} // Flags contains the list of configuration options available to the binary. -var Flags = append(requiredFlags, optionalFlags...) +var Flags []cli.Flag + +func init() { + optionalFlags = append(optionalFlags, p2pFlags...) + optionalFlags = append(optionalFlags, oplog.CLIFlags(envVarPrefix)...) + Flags = append(requiredFlags, optionalFlags...) +} func CheckRequired(ctx *cli.Context) error { l1NodeAddr := ctx.GlobalString(L1NodeAddr.Name) diff --git a/op-node/node/api.go b/op-node/node/api.go index 753b575a6acee..46fef276251d8 100644 --- a/op-node/node/api.go +++ b/op-node/node/api.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -114,7 +115,16 @@ func (n *nodeAPI) OutputAtBlock(ctx context.Context, number hexutil.Uint64) (*et } var l2OutputRootVersion eth.Bytes32 // it's zero for now - l2OutputRoot := rollup.ComputeL2OutputRoot(l2OutputRootVersion, head.Hash(), head.Root(), proof.StorageHash) + l2OutputRoot, err := rollup.ComputeL2OutputRoot(&bindings.TypesOutputRootProof{ + Version: l2OutputRootVersion, + StateRoot: head.Root(), + MessagePasserStorageRoot: proof.StorageHash, + LatestBlockhash: head.Hash(), + }) + if err != nil { + n.log.Error("Error computing L2 output root, nil ptr passed to hashing function") + return nil, err + } return ð.OutputResponse{ Version: l2OutputRootVersion, diff --git a/op-node/node/client.go b/op-node/node/client.go index 7d4f7e128b1b1..d83a258fb1d29 100644 --- a/op-node/node/client.go +++ b/op-node/node/client.go @@ -19,6 +19,11 @@ type L2EndpointSetup interface { Check() error } +type L2SyncEndpointSetup interface { + Setup(ctx context.Context, log log.Logger) (cl client.RPC, err error) + Check() error +} + type L1EndpointSetup interface { // Setup a RPC client to a L1 node to pull rollup input-data from. // The results of the RPC client may be trusted for faster processing, or strictly validated. @@ -75,6 +80,50 @@ func (p *PreparedL2Endpoints) Setup(ctx context.Context, log log.Logger) (client return p.Client, nil } +// L2SyncEndpointConfig contains configuration for the fallback sync endpoint +type L2SyncEndpointConfig struct { + // Address of the L2 RPC to use for backup sync + L2NodeAddr string +} + +var _ L2SyncEndpointSetup = (*L2SyncEndpointConfig)(nil) + +func (cfg *L2SyncEndpointConfig) Setup(ctx context.Context, log log.Logger) (client.RPC, error) { + l2Node, err := client.NewRPC(ctx, log, cfg.L2NodeAddr) + if err != nil { + return nil, err + } + + return l2Node, nil +} + +func (cfg *L2SyncEndpointConfig) Check() error { + if cfg.L2NodeAddr == "" { + return errors.New("empty L2 Node Address") + } + + return nil +} + +type L2SyncRPCConfig struct { + // RPC endpoint to use for syncing + Rpc client.RPC +} + +var _ L2SyncEndpointSetup = (*L2SyncRPCConfig)(nil) + +func (cfg *L2SyncRPCConfig) Setup(ctx context.Context, log log.Logger) (client.RPC, error) { + return cfg.Rpc, nil +} + +func (cfg *L2SyncRPCConfig) Check() error { + if cfg.Rpc == nil { + return errors.New("rpc cannot be nil") + } + + return nil +} + type L1EndpointConfig struct { L1NodeAddr string // Address of L1 User JSON-RPC endpoint to use (eth namespace required) diff --git a/op-node/node/config.go b/op-node/node/config.go index 41f2e458f1c9b..8d95f79f360af 100644 --- a/op-node/node/config.go +++ b/op-node/node/config.go @@ -13,8 +13,9 @@ import ( ) type Config struct { - L1 L1EndpointSetup - L2 L2EndpointSetup + L1 L1EndpointSetup + L2 L2EndpointSetup + L2Sync L2SyncEndpointSetup Driver driver.Config diff --git a/op-node/node/node.go b/op-node/node/node.go index b44ce2fce6055..ad4d28f3802c4 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -197,7 +197,28 @@ func (n *OpNode) initL2(ctx context.Context, cfg *Config, snapshotLog log.Logger return err } - n.l2Driver = driver.NewDriver(&cfg.Driver, &cfg.Rollup, n.l2Source, n.l1Source, n, n.log, snapshotLog, n.metrics) + var syncClient *sources.SyncClient + // If the L2 sync config is present, use it to create a sync client + if cfg.L2Sync != nil { + if err := cfg.L2Sync.Check(); err != nil { + log.Info("L2 sync config is not present, skipping L2 sync client setup", "err", err) + } else { + rpcSyncClient, err := cfg.L2Sync.Setup(ctx, n.log) + if err != nil { + return fmt.Errorf("failed to setup L2 execution-engine RPC client for backup sync: %w", err) + } + + // The sync client's RPC is always trusted + config := sources.SyncClientDefaultConfig(&cfg.Rollup, true) + + syncClient, err = sources.NewSyncClient(n.OnUnsafeL2Payload, rpcSyncClient, n.log, n.metrics.L2SourceCache, config) + if err != nil { + return fmt.Errorf("failed to create sync client: %w", err) + } + } + } + + n.l2Driver = driver.NewDriver(&cfg.Driver, &cfg.Rollup, n.l2Source, n.l1Source, syncClient, n, n.log, snapshotLog, n.metrics) return nil } @@ -263,13 +284,21 @@ func (n *OpNode) initP2PSigner(ctx context.Context, cfg *Config) error { func (n *OpNode) Start(ctx context.Context) error { n.log.Info("Starting execution engine driver") + // start driving engine: sync blocks by deriving them from L1 and driving them into the engine - err := n.l2Driver.Start() - if err != nil { + if err := n.l2Driver.Start(); err != nil { n.log.Error("Could not start a rollup node", "err", err) return err } + // If the backup unsafe sync client is enabled, start its event loop + if n.l2Driver.L2SyncCl != nil { + if err := n.l2Driver.L2SyncCl.Start(); err != nil { + n.log.Error("Could not start the backup sync client", "err", err) + return err + } + } + return nil } @@ -382,6 +411,13 @@ func (n *OpNode) Close() error { if err := n.l2Driver.Close(); err != nil { result = multierror.Append(result, fmt.Errorf("failed to close L2 engine driver cleanly: %w", err)) } + + // If the L2 sync client is present & running, close it. + if n.l2Driver.L2SyncCl != nil { + if err := n.l2Driver.L2SyncCl.Close(); err != nil { + result = multierror.Append(result, fmt.Errorf("failed to close L2 engine backup sync client cleanly: %w", err)) + } + } } // close L2 engine RPC client diff --git a/op-node/rollup/derive/batch_queue.go b/op-node/rollup/derive/batch_queue.go index 789149443abb8..c5994b86a82ef 100644 --- a/op-node/rollup/derive/batch_queue.go +++ b/op-node/rollup/derive/batch_queue.go @@ -139,6 +139,7 @@ func (bq *BatchQueue) AddBatch(batch *BatchData, l2SafeHead eth.L2BlockRef) { if validity == BatchDrop { return // if we do drop the batch, CheckBatch will log the drop reason with WARN level. } + bq.log.Debug("Adding batch", "batch_timestamp", batch.Timestamp, "parent_hash", batch.ParentHash, "batch_epoch", batch.Epoch(), "txs", len(batch.Transactions)) bq.batches[batch.Timestamp] = append(bq.batches[batch.Timestamp], &data) } @@ -212,7 +213,7 @@ batchLoop: if nextBatch.Batch.EpochNum == rollup.Epoch(epoch.Number)+1 { bq.l1Blocks = bq.l1Blocks[1:] } - bq.log.Trace("Returning found batch", "epoch", epoch, "batch_epoch", nextBatch.Batch.EpochNum, "batch_timestamp", nextBatch.Batch.Timestamp) + bq.log.Info("Found next batch", "epoch", epoch, "batch_epoch", nextBatch.Batch.EpochNum, "batch_timestamp", nextBatch.Batch.Timestamp) return nextBatch.Batch, nil } @@ -241,7 +242,7 @@ batchLoop: // to preserve that L2 time >= L1 time. If this is the first block of the epoch, always generate a // batch to ensure that we at least have one batch per epoch. if nextTimestamp < nextEpoch.Time || firstOfEpoch { - bq.log.Trace("Generating next batch", "epoch", epoch, "timestamp", nextTimestamp) + bq.log.Info("Generating next batch", "epoch", epoch, "timestamp", nextTimestamp) return &BatchData{ BatchV1{ ParentHash: l2SafeHead.Hash, diff --git a/op-node/rollup/derive/channel_bank.go b/op-node/rollup/derive/channel_bank.go index b92266e9442ff..4243cc6272631 100644 --- a/op-node/rollup/derive/channel_bank.go +++ b/op-node/rollup/derive/channel_bank.go @@ -69,6 +69,7 @@ func (cb *ChannelBank) prune() { ch := cb.channels[id] cb.channelQueue = cb.channelQueue[1:] delete(cb.channels, id) + cb.log.Info("pruning channel", "channel", id, "totalSize", totalSize, "channel_size", ch.size, "remaining_channel_count", len(cb.channels)) totalSize -= ch.size } } @@ -77,7 +78,7 @@ func (cb *ChannelBank) prune() { // Read() should be called repeatedly first, until everything has been read, before adding new data. func (cb *ChannelBank) IngestFrame(f Frame) { origin := cb.Origin() - log := log.New("origin", origin, "channel", f.ID, "length", len(f.Data), "frame_number", f.FrameNumber, "is_last", f.IsLast) + log := cb.log.New("origin", origin, "channel", f.ID, "length", len(f.Data), "frame_number", f.FrameNumber, "is_last", f.IsLast) log.Debug("channel bank got new data") currentCh, ok := cb.channels[f.ID] @@ -86,6 +87,7 @@ func (cb *ChannelBank) IngestFrame(f Frame) { currentCh = NewChannel(f.ID, origin) cb.channels[f.ID] = currentCh cb.channelQueue = append(cb.channelQueue, f.ID) + log.Info("created new channel") } // check if the channel is not timed out @@ -114,7 +116,7 @@ func (cb *ChannelBank) Read() (data []byte, err error) { ch := cb.channels[first] timedOut := ch.OpenBlockNumber()+cb.cfg.ChannelTimeout < cb.Origin().Number if timedOut { - cb.log.Debug("channel timed out", "channel", first, "frames", len(ch.inputs)) + cb.log.Info("channel timed out", "channel", first, "frames", len(ch.inputs)) delete(cb.channels, first) cb.channelQueue = cb.channelQueue[1:] return nil, nil // multiple different channels may all be timed out @@ -137,7 +139,6 @@ func (cb *ChannelBank) Read() (data []byte, err error) { // consistency around channel bank pruning which depends upon the order // of operations. func (cb *ChannelBank) NextData(ctx context.Context) ([]byte, error) { - // Do the read from the channel bank first data, err := cb.Read() if err == io.EOF { diff --git a/op-node/rollup/derive/channel_out.go b/op-node/rollup/derive/channel_out.go index fd5657158cca7..90614a81d687b 100644 --- a/op-node/rollup/derive/channel_out.go +++ b/op-node/rollup/derive/channel_out.go @@ -14,9 +14,17 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) +var ErrMaxFrameSizeTooSmall = errors.New("maxSize is too small to fit the fixed frame overhead") var ErrNotDepositTx = errors.New("first transaction in block is not a deposit tx") var ErrTooManyRLPBytes = errors.New("batch would cause RLP bytes to go over limit") +// FrameV0OverHeadSize is the absolute minimum size of a frame. +// This is the fixed overhead frame size, calculated as specified +// in the [Frame Format] specs: 16 + 2 + 4 + 1 = 23 bytes. +// +// [Frame Format]: https://github.com/ethereum-optimism/optimism/blob/develop/specs/derivation.md#frame-format +const FrameV0OverHeadSize = 23 + type ChannelOut struct { id ChannelID // Frame ID of the next frame to emit. Increment after emitting @@ -76,7 +84,7 @@ func (co *ChannelOut) AddBlock(block *types.Block) (uint64, error) { return 0, errors.New("already closed") } - batch, err := BlockToBatch(block) + batch, _, err := BlockToBatch(block) if err != nil { return 0, err } @@ -141,19 +149,23 @@ func (co *ChannelOut) Close() error { // OutputFrame writes a frame to w with a given max size and returns the frame // number. // Use `ReadyBytes`, `Flush`, and `Close` to modify the ready buffer. -// Returns io.EOF when the channel is closed & there are no more frames +// Returns an error if the `maxSize` < FrameV0OverHeadSize. +// Returns io.EOF when the channel is closed & there are no more frames. // Returns nil if there is still more buffered data. -// Returns and error if it ran into an error during processing. +// Returns an error if it ran into an error during processing. func (co *ChannelOut) OutputFrame(w *bytes.Buffer, maxSize uint64) (uint16, error) { f := Frame{ ID: co.id, FrameNumber: uint16(co.frame), } + // Check that the maxSize is large enough for the frame overhead size. + if maxSize < FrameV0OverHeadSize { + return 0, ErrMaxFrameSizeTooSmall + } + // Copy data from the local buffer into the frame data buffer - // Don't go past the maxSize with the fixed frame overhead. - // Fixed overhead: 16 + 2 + 4 + 1 = 23 bytes. - maxDataSize := maxSize - 23 + maxDataSize := maxSize - FrameV0OverHeadSize if maxDataSize > uint64(co.buf.Len()) { maxDataSize = uint64(co.buf.Len()) // If we are closed & will not spill past the current frame @@ -182,7 +194,7 @@ func (co *ChannelOut) OutputFrame(w *bytes.Buffer, maxSize uint64) (uint16, erro } // BlockToBatch transforms a block into a batch object that can easily be RLP encoded. -func BlockToBatch(block *types.Block) (*BatchData, error) { +func BlockToBatch(block *types.Block) (*BatchData, L1BlockInfo, error) { opaqueTxs := make([]hexutil.Bytes, 0, len(block.Transactions())) for i, tx := range block.Transactions() { if tx.Type() == types.DepositTxType { @@ -190,17 +202,17 @@ func BlockToBatch(block *types.Block) (*BatchData, error) { } otx, err := tx.MarshalBinary() if err != nil { - return nil, fmt.Errorf("could not encode tx %v in block %v: %w", i, tx.Hash(), err) + return nil, L1BlockInfo{}, fmt.Errorf("could not encode tx %v in block %v: %w", i, tx.Hash(), err) } opaqueTxs = append(opaqueTxs, otx) } l1InfoTx := block.Transactions()[0] if l1InfoTx.Type() != types.DepositTxType { - return nil, ErrNotDepositTx + return nil, L1BlockInfo{}, ErrNotDepositTx } l1Info, err := L1InfoDepositTxData(l1InfoTx.Data()) if err != nil { - return nil, fmt.Errorf("could not parse the L1 Info deposit: %w", err) + return nil, l1Info, fmt.Errorf("could not parse the L1 Info deposit: %w", err) } return &BatchData{ @@ -211,5 +223,60 @@ func BlockToBatch(block *types.Block) (*BatchData, error) { Timestamp: block.Time(), Transactions: opaqueTxs, }, - }, nil + }, l1Info, nil +} + +// ForceCloseTxData generates the transaction data for a transaction which will force close +// a channel. It should be given every frame of that channel which has been submitted on +// chain. The frames should be given in order that they appear on L1. +func ForceCloseTxData(frames []Frame) ([]byte, error) { + if len(frames) == 0 { + return nil, errors.New("must provide at least one frame") + } + frameNumbers := make(map[uint16]struct{}) + id := frames[0].ID + closeNumber := uint16(0) + closed := false + for i, frame := range frames { + if !closed && frame.IsLast { + closeNumber = frame.FrameNumber + } + closed = closed || frame.IsLast + frameNumbers[frame.FrameNumber] = struct{}{} + if frame.ID != id { + return nil, fmt.Errorf("invalid ID in list: first ID: %v, %vth ID: %v", id, i, frame.ID) + } + } + + var out bytes.Buffer + out.WriteByte(DerivationVersion0) + + if !closed { + f := Frame{ + ID: id, + FrameNumber: 0, + Data: nil, + IsLast: true, + } + if err := f.MarshalBinary(&out); err != nil { + return nil, err + } + } else { + for i := uint16(0); i <= closeNumber; i++ { + if _, ok := frameNumbers[i]; ok { + continue + } + f := Frame{ + ID: id, + FrameNumber: i, + Data: nil, + IsLast: false, + } + if err := f.MarshalBinary(&out); err != nil { + return nil, err + } + } + } + + return out.Bytes(), nil } diff --git a/op-node/rollup/derive/channel_out_test.go b/op-node/rollup/derive/channel_out_test.go index 42e927039733f..09d6f15422380 100644 --- a/op-node/rollup/derive/channel_out_test.go +++ b/op-node/rollup/derive/channel_out_test.go @@ -5,6 +5,7 @@ import ( "math/big" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/require" @@ -28,6 +29,22 @@ func TestChannelOutAddBlock(t *testing.T) { }) } +// TestOutputFrameSmallMaxSize tests that calling [OutputFrame] with a small +// max size that is below the fixed frame size overhead of 23, will return +// an error. +func TestOutputFrameSmallMaxSize(t *testing.T) { + cout, err := NewChannelOut() + require.NoError(t, err) + + // Call OutputFrame with the range of small max size values that err + var w bytes.Buffer + for i := 0; i < 23; i++ { + fid, err := cout.OutputFrame(&w, uint64(i)) + require.ErrorIs(t, err, ErrMaxFrameSizeTooSmall) + require.Zero(t, fid) + } +} + // TestRLPByteLimit ensures that stream encoder is properly limiting the length. // It will decode the input if `len(input) <= inputLimit`. func TestRLPByteLimit(t *testing.T) { @@ -49,3 +66,69 @@ func TestRLPByteLimit(t *testing.T) { require.Equal(t, err, rlp.ErrValueTooLarge) require.Equal(t, out2, "") } + +func TestForceCloseTxData(t *testing.T) { + id := [16]byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef} + tests := []struct { + frames []Frame + errors bool + output string + }{ + { + frames: []Frame{}, + errors: true, + output: "", + }, + { + frames: []Frame{Frame{FrameNumber: 0, IsLast: false}, Frame{ID: id, FrameNumber: 1, IsLast: true}}, + errors: true, + output: "", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 0, IsLast: false}}, + errors: false, + output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000001", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 0, IsLast: true}}, + errors: false, + output: "00", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 1, IsLast: false}}, + errors: false, + output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000001", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 1, IsLast: true}}, + errors: false, + output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 2, IsLast: true}}, + errors: false, + output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000deadbeefdeadbeefdeadbeefdeadbeef00010000000000", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 1, IsLast: false}, Frame{ID: id, FrameNumber: 3, IsLast: true}}, + errors: false, + output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000deadbeefdeadbeefdeadbeefdeadbeef00020000000000", + }, + { + frames: []Frame{Frame{ID: id, FrameNumber: 1, IsLast: false}, Frame{ID: id, FrameNumber: 3, IsLast: true}, Frame{ID: id, FrameNumber: 5, IsLast: true}}, + errors: false, + output: "00deadbeefdeadbeefdeadbeefdeadbeef00000000000000deadbeefdeadbeefdeadbeefdeadbeef00020000000000", + }, + } + + for i, test := range tests { + out, err := ForceCloseTxData(test.frames) + if test.errors { + require.NotNil(t, err, "Should error on tc %v", i) + require.Nil(t, out, "Should return no value in tc %v", i) + } else { + require.NoError(t, err, "Should not error on tc %v", i) + require.Equal(t, common.FromHex(test.output), out, "Should match output tc %v", i) + } + } +} diff --git a/op-node/rollup/derive/engine_queue.go b/op-node/rollup/derive/engine_queue.go index 4f69d2621bad3..91a26cc46bdf7 100644 --- a/op-node/rollup/derive/engine_queue.go +++ b/op-node/rollup/derive/engine_queue.go @@ -104,8 +104,10 @@ type EngineQueue struct { finalizedL1 eth.L1BlockRef - safeAttributes *eth.PayloadAttributes - unsafePayloads PayloadsQueue // queue of unsafe payloads, ordered by ascending block number, may have gaps + // The queued-up attributes + safeAttributesParent eth.L2BlockRef + safeAttributes *eth.PayloadAttributes + unsafePayloads PayloadsQueue // queue of unsafe payloads, ordered by ascending block number, may have gaps // Tracks which L2 blocks where last derived from which L1 block. At most finalityLookback large. finalityData []FinalityData @@ -131,8 +133,9 @@ func NewEngineQueue(log log.Logger, cfg *rollup.Config, engine Engine, metrics M metrics: metrics, finalityData: make([]FinalityData, 0, finalityLookback), unsafePayloads: PayloadsQueue{ - MaxSize: maxUnsafePayloadsMemory, - SizeFn: payloadMemSize, + MaxSize: maxUnsafePayloadsMemory, + SizeFn: payloadMemSize, + blockNos: make(map[uint64]bool), }, prev: prev, l1Fetcher: l1Fetcher, @@ -224,6 +227,7 @@ func (eq *EngineQueue) Step(ctx context.Context) error { return err } else { eq.safeAttributes = next + eq.safeAttributesParent = eq.safeHead eq.log.Debug("Adding next safe attributes", "safe_head", eq.safeHead, "next", eq.safeAttributes) return NotEnoughData } @@ -426,6 +430,20 @@ func (eq *EngineQueue) tryNextUnsafePayload(ctx context.Context) error { } func (eq *EngineQueue) tryNextSafeAttributes(ctx context.Context) error { + if eq.safeAttributes == nil { // sanity check the attributes are there + return nil + } + // validate the safe attributes before processing them. The engine may have completed processing them through other means. + if eq.safeHead != eq.safeAttributesParent { + if eq.safeHead.ParentHash != eq.safeAttributesParent.Hash { + return NewResetError(fmt.Errorf("safe head changed to %s with parent %s, conflicting with queued safe attributes on top of %s", + eq.safeHead, eq.safeHead.ParentID(), eq.safeAttributesParent)) + } + eq.log.Warn("queued safe attributes are stale, safe-head progressed", + "safe_head", eq.safeHead, "safe_head_parent", eq.safeHead.ParentID(), "attributes_parent", eq.safeAttributesParent) + eq.safeAttributes = nil + return nil + } if eq.safeHead.Number < eq.unsafeHead.Number { return eq.consolidateNextSafeAttributes(ctx) } else if eq.safeHead.Number == eq.unsafeHead.Number { @@ -485,14 +503,15 @@ func (eq *EngineQueue) forceNextSafeAttributes(ctx context.Context) error { _, errType, err = eq.ConfirmPayload(ctx) } if err != nil { - _ = eq.CancelPayload(ctx, true) switch errType { case BlockInsertTemporaryErr: // RPC errors are recoverable, we can retry the buffered payload attributes later. return NewTemporaryError(fmt.Errorf("temporarily cannot insert new safe block: %w", err)) case BlockInsertPrestateErr: + _ = eq.CancelPayload(ctx, true) return NewResetError(fmt.Errorf("need reset to resolve pre-state problem: %w", err)) case BlockInsertPayloadErr: + _ = eq.CancelPayload(ctx, true) eq.log.Warn("could not process payload derived from L1 data, dropping batch", "err", err) // Count the number of deposits to see if the tx list is deposit only. depositCount := 0 @@ -662,3 +681,20 @@ func (eq *EngineQueue) Reset(ctx context.Context, _ eth.L1BlockRef, _ eth.System eq.logSyncProgress("reset derivation work") return io.EOF } + +// GetUnsafeQueueGap retrieves the current [start, end] range of the gap between the tip of the unsafe priority queue and the unsafe head. +// If there is no gap, the difference between end and start will be 0. +func (eq *EngineQueue) GetUnsafeQueueGap(expectedNumber uint64) (start uint64, end uint64) { + // The start of the gap is always the unsafe head + 1 + start = eq.unsafeHead.Number + 1 + + // If the priority queue is empty, the end is the first block number at the top of the priority queue + // Otherwise, the end is the expected block number + if first := eq.unsafePayloads.Peek(); first != nil { + end = first.ID().Number + } else { + end = expectedNumber + } + + return start, end +} diff --git a/op-node/rollup/derive/engine_queue_test.go b/op-node/rollup/derive/engine_queue_test.go index 440bb498e5675..be22db1285e76 100644 --- a/op-node/rollup/derive/engine_queue_test.go +++ b/op-node/rollup/derive/engine_queue_test.go @@ -2,10 +2,13 @@ package derive import ( "context" + "fmt" "io" + "math/big" "math/rand" "testing" + "github.com/holiman/uint256" "github.com/stretchr/testify/require" "github.com/ethereum/go-ethereum/common" @@ -19,6 +22,7 @@ import ( type fakeAttributesQueue struct { origin eth.L1BlockRef + attrs *eth.PayloadAttributes } func (f *fakeAttributesQueue) Origin() eth.L1BlockRef { @@ -26,7 +30,10 @@ func (f *fakeAttributesQueue) Origin() eth.L1BlockRef { } func (f *fakeAttributesQueue) NextAttributes(_ context.Context, _ eth.L2BlockRef) (*eth.PayloadAttributes, error) { - return nil, io.EOF + if f.attrs == nil { + return nil, io.EOF + } + return f.attrs, nil } var _ NextAttributesProvider = (*fakeAttributesQueue)(nil) @@ -209,17 +216,17 @@ func TestEngineQueue_Finalize(t *testing.T) { eng.ExpectL2BlockRefByHash(refF0.ParentHash, refE1, nil) // meet previous safe, counts 1/2 - l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) + l1F.ExpectL1BlockRefByHash(refE.Hash, refE, nil) eng.ExpectL2BlockRefByHash(refE1.ParentHash, refE0, nil) eng.ExpectL2BlockRefByHash(refE0.ParentHash, refD1, nil) // now full seq window, inclusive - l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + l1F.ExpectL1BlockRefByHash(refD.Hash, refD, nil) eng.ExpectL2BlockRefByHash(refD1.ParentHash, refD0, nil) eng.ExpectL2BlockRefByHash(refD0.ParentHash, refC1, nil) // now one more L1 origin - l1F.ExpectL1BlockRefByNumber(refC.Number, refC, nil) + l1F.ExpectL1BlockRefByHash(refC.Hash, refC, nil) eng.ExpectL2BlockRefByHash(refC1.ParentHash, refC0, nil) // parent of that origin will be considered safe eng.ExpectL2BlockRefByHash(refC0.ParentHash, refB1, nil) @@ -443,17 +450,17 @@ func TestEngineQueue_ResetWhenUnsafeOriginNotCanonical(t *testing.T) { eng.ExpectL2BlockRefByHash(refF0.ParentHash, refE1, nil) // meet previous safe, counts 1/2 - l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) + l1F.ExpectL1BlockRefByHash(refE.Hash, refE, nil) eng.ExpectL2BlockRefByHash(refE1.ParentHash, refE0, nil) eng.ExpectL2BlockRefByHash(refE0.ParentHash, refD1, nil) // now full seq window, inclusive - l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + l1F.ExpectL1BlockRefByHash(refD.Hash, refD, nil) eng.ExpectL2BlockRefByHash(refD1.ParentHash, refD0, nil) eng.ExpectL2BlockRefByHash(refD0.ParentHash, refC1, nil) // now one more L1 origin - l1F.ExpectL1BlockRefByNumber(refC.Number, refC, nil) + l1F.ExpectL1BlockRefByHash(refC.Hash, refC, nil) eng.ExpectL2BlockRefByHash(refC1.ParentHash, refC0, nil) // parent of that origin will be considered safe eng.ExpectL2BlockRefByHash(refC0.ParentHash, refB1, nil) @@ -775,17 +782,17 @@ func TestVerifyNewL1Origin(t *testing.T) { } // meet previous safe, counts 1/2 - l1F.ExpectL1BlockRefByNumber(refE.Number, refE, nil) + l1F.ExpectL1BlockRefByHash(refE.Hash, refE, nil) eng.ExpectL2BlockRefByHash(refE1.ParentHash, refE0, nil) eng.ExpectL2BlockRefByHash(refE0.ParentHash, refD1, nil) // now full seq window, inclusive - l1F.ExpectL1BlockRefByNumber(refD.Number, refD, nil) + l1F.ExpectL1BlockRefByHash(refD.Hash, refD, nil) eng.ExpectL2BlockRefByHash(refD1.ParentHash, refD0, nil) eng.ExpectL2BlockRefByHash(refD0.ParentHash, refC1, nil) // now one more L1 origin - l1F.ExpectL1BlockRefByNumber(refC.Number, refC, nil) + l1F.ExpectL1BlockRefByHash(refC.Hash, refC, nil) eng.ExpectL2BlockRefByHash(refC1.ParentHash, refC0, nil) // parent of that origin will be considered safe eng.ExpectL2BlockRefByHash(refC0.ParentHash, refB1, nil) @@ -837,3 +844,166 @@ func TestVerifyNewL1Origin(t *testing.T) { }) } } + +func TestBlockBuildingRace(t *testing.T) { + logger := testlog.Logger(t, log.LvlInfo) + eng := &testutils.MockEngine{} + + rng := rand.New(rand.NewSource(1234)) + + refA := testutils.RandomBlockRef(rng) + refA0 := eth.L2BlockRef{ + Hash: testutils.RandomHash(rng), + Number: 0, + ParentHash: common.Hash{}, + Time: refA.Time, + L1Origin: refA.ID(), + SequenceNumber: 0, + } + cfg := &rollup.Config{ + Genesis: rollup.Genesis{ + L1: refA.ID(), + L2: refA0.ID(), + L2Time: refA0.Time, + SystemConfig: eth.SystemConfig{ + BatcherAddr: common.Address{42}, + Overhead: [32]byte{123}, + Scalar: [32]byte{42}, + GasLimit: 20_000_000, + }, + }, + BlockTime: 1, + SeqWindowSize: 2, + } + refA1 := eth.L2BlockRef{ + Hash: testutils.RandomHash(rng), + Number: refA0.Number + 1, + ParentHash: refA0.Hash, + Time: refA0.Time + cfg.BlockTime, + L1Origin: refA.ID(), + SequenceNumber: 1, + } + + l1F := &testutils.MockL1Source{} + + eng.ExpectL2BlockRefByLabel(eth.Finalized, refA0, nil) + eng.ExpectL2BlockRefByLabel(eth.Safe, refA0, nil) + eng.ExpectL2BlockRefByLabel(eth.Unsafe, refA0, nil) + l1F.ExpectL1BlockRefByNumber(refA.Number, refA, nil) + l1F.ExpectL1BlockRefByHash(refA.Hash, refA, nil) + l1F.ExpectL1BlockRefByHash(refA.Hash, refA, nil) + + eng.ExpectSystemConfigByL2Hash(refA0.Hash, cfg.Genesis.SystemConfig, nil) + + metrics := &testutils.TestDerivationMetrics{} + + gasLimit := eth.Uint64Quantity(20_000_000) + attrs := ð.PayloadAttributes{ + Timestamp: eth.Uint64Quantity(refA1.Time), + PrevRandao: eth.Bytes32{}, + SuggestedFeeRecipient: common.Address{}, + Transactions: nil, + NoTxPool: false, + GasLimit: &gasLimit, + } + + prev := &fakeAttributesQueue{origin: refA, attrs: attrs} + eq := NewEngineQueue(logger, cfg, eng, metrics, prev, l1F) + require.ErrorIs(t, eq.Reset(context.Background(), eth.L1BlockRef{}, eth.SystemConfig{}), io.EOF) + + id := eth.PayloadID{0xff} + + preFc := ð.ForkchoiceState{ + HeadBlockHash: refA0.Hash, + SafeBlockHash: refA0.Hash, + FinalizedBlockHash: refA0.Hash, + } + preFcRes := ð.ForkchoiceUpdatedResult{ + PayloadStatus: eth.PayloadStatusV1{ + Status: eth.ExecutionValid, + LatestValidHash: &refA0.Hash, + ValidationError: nil, + }, + PayloadID: &id, + } + + // Expect initial forkchoice update + eng.ExpectForkchoiceUpdate(preFc, nil, preFcRes, nil) + require.NoError(t, eq.Step(context.Background()), "clean forkchoice state after reset") + + // Expect initial building update, to process the attributes we queued up + eng.ExpectForkchoiceUpdate(preFc, attrs, preFcRes, nil) + // Don't let the payload be confirmed straight away + mockErr := fmt.Errorf("mock error") + eng.ExpectGetPayload(id, nil, mockErr) + // The job will be not be cancelled, the untyped error is a temporary error + + require.ErrorIs(t, eq.Step(context.Background()), NotEnoughData, "queue up attributes") + require.ErrorIs(t, eq.Step(context.Background()), mockErr, "expecting to fail to process attributes") + require.NotNil(t, eq.safeAttributes, "still have attributes") + + // Now allow the building to complete + a1InfoTx, err := L1InfoDepositBytes(refA1.SequenceNumber, &testutils.MockBlockInfo{ + InfoHash: refA.Hash, + InfoParentHash: refA.ParentHash, + InfoCoinbase: common.Address{}, + InfoRoot: common.Hash{}, + InfoNum: refA.Number, + InfoTime: refA.Time, + InfoMixDigest: [32]byte{}, + InfoBaseFee: big.NewInt(7), + InfoReceiptRoot: common.Hash{}, + InfoGasUsed: 0, + }, cfg.Genesis.SystemConfig, false) + + require.NoError(t, err) + payloadA1 := ð.ExecutionPayload{ + ParentHash: refA1.ParentHash, + FeeRecipient: attrs.SuggestedFeeRecipient, + StateRoot: eth.Bytes32{}, + ReceiptsRoot: eth.Bytes32{}, + LogsBloom: eth.Bytes256{}, + PrevRandao: eth.Bytes32{}, + BlockNumber: eth.Uint64Quantity(refA1.Number), + GasLimit: gasLimit, + GasUsed: 0, + Timestamp: eth.Uint64Quantity(refA1.Time), + ExtraData: nil, + BaseFeePerGas: *uint256.NewInt(7), + BlockHash: refA1.Hash, + Transactions: []eth.Data{ + a1InfoTx, + }, + } + eng.ExpectGetPayload(id, payloadA1, nil) + eng.ExpectNewPayload(payloadA1, ð.PayloadStatusV1{ + Status: eth.ExecutionValid, + LatestValidHash: &refA1.Hash, + ValidationError: nil, + }, nil) + postFc := ð.ForkchoiceState{ + HeadBlockHash: refA1.Hash, + SafeBlockHash: refA1.Hash, + FinalizedBlockHash: refA0.Hash, + } + postFcRes := ð.ForkchoiceUpdatedResult{ + PayloadStatus: eth.PayloadStatusV1{ + Status: eth.ExecutionValid, + LatestValidHash: &refA1.Hash, + ValidationError: nil, + }, + PayloadID: &id, + } + eng.ExpectForkchoiceUpdate(postFc, nil, postFcRes, nil) + + // Now complete the job, as external user of the engine + _, _, err = eq.ConfirmPayload(context.Background()) + require.NoError(t, err) + require.Equal(t, refA1, eq.SafeL2Head(), "safe head should have changed") + + require.NoError(t, eq.Step(context.Background())) + require.Nil(t, eq.safeAttributes, "attributes should now be invalidated") + + l1F.AssertExpectations(t) + eng.AssertExpectations(t) +} diff --git a/op-node/rollup/derive/l2block_util.go b/op-node/rollup/derive/l2block_util.go new file mode 100644 index 0000000000000..179b1021669ce --- /dev/null +++ b/op-node/rollup/derive/l2block_util.go @@ -0,0 +1,64 @@ +package derive + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + + "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup" +) + +// L2BlockRefSource is a source for the generation of a L2BlockRef. E.g. a +// *types.Block is a L2BlockRefSource. +// +// L2BlockToBlockRef extracts L2BlockRef from a L2BlockRefSource. The first +// transaction of a source must be a Deposit transaction. +type L2BlockRefSource interface { + Hash() common.Hash + ParentHash() common.Hash + NumberU64() uint64 + Time() uint64 + Transactions() types.Transactions +} + +// PayloadToBlockRef extracts the essential L2BlockRef information from an L2 +// block ref source, falling back to genesis information if necessary. +func L2BlockToBlockRef(block L2BlockRefSource, genesis *rollup.Genesis) (eth.L2BlockRef, error) { + hash, number := block.Hash(), block.NumberU64() + + var l1Origin eth.BlockID + var sequenceNumber uint64 + if number == genesis.L2.Number { + if hash != genesis.L2.Hash { + return eth.L2BlockRef{}, fmt.Errorf("expected L2 genesis hash to match L2 block at genesis block number %d: %s <> %s", genesis.L2.Number, hash, genesis.L2.Hash) + } + l1Origin = genesis.L1 + sequenceNumber = 0 + } else { + txs := block.Transactions() + if txs.Len() == 0 { + return eth.L2BlockRef{}, fmt.Errorf("l2 block is missing L1 info deposit tx, block hash: %s", hash) + } + tx := txs[0] + if tx.Type() != types.DepositTxType { + return eth.L2BlockRef{}, fmt.Errorf("first payload tx has unexpected tx type: %d", tx.Type()) + } + info, err := L1InfoDepositTxData(tx.Data()) + if err != nil { + return eth.L2BlockRef{}, fmt.Errorf("failed to parse L1 info deposit tx from L2 block: %w", err) + } + l1Origin = eth.BlockID{Hash: info.BlockHash, Number: info.Number} + sequenceNumber = info.SequenceNumber + } + + return eth.L2BlockRef{ + Hash: hash, + Number: number, + ParentHash: block.ParentHash(), + Time: block.Time(), + L1Origin: l1Origin, + SequenceNumber: sequenceNumber, + }, nil +} diff --git a/op-node/rollup/derive/payloads_queue.go b/op-node/rollup/derive/payloads_queue.go index d71ccf8c90409..10f7215562e15 100644 --- a/op-node/rollup/derive/payloads_queue.go +++ b/op-node/rollup/derive/payloads_queue.go @@ -77,6 +77,7 @@ type PayloadsQueue struct { pq payloadsByNumber currentSize uint64 MaxSize uint64 + blockNos map[uint64]bool SizeFn func(p *eth.ExecutionPayload) uint64 } @@ -99,6 +100,9 @@ func (upq *PayloadsQueue) Push(p *eth.ExecutionPayload) error { if p == nil { return errors.New("cannot add nil payload") } + if upq.blockNos[p.ID().Number] { + return errors.New("cannot add duplicate payload") + } size := upq.SizeFn(p) if size > upq.MaxSize { return fmt.Errorf("cannot add payload %s, payload mem size %d is larger than max queue size %d", p.ID(), size, upq.MaxSize) @@ -111,6 +115,7 @@ func (upq *PayloadsQueue) Push(p *eth.ExecutionPayload) error { for upq.currentSize > upq.MaxSize { upq.Pop() } + upq.blockNos[p.ID().Number] = true return nil } @@ -132,5 +137,7 @@ func (upq *PayloadsQueue) Pop() *eth.ExecutionPayload { } ps := heap.Pop(&upq.pq).(payloadAndSize) // nosemgrep upq.currentSize -= ps.size + // remove the key from the blockNos map + delete(upq.blockNos, ps.payload.ID().Number) return ps.payload } diff --git a/op-node/rollup/derive/payloads_queue_test.go b/op-node/rollup/derive/payloads_queue_test.go index 49890228fe794..0b244b98f5e4f 100644 --- a/op-node/rollup/derive/payloads_queue_test.go +++ b/op-node/rollup/derive/payloads_queue_test.go @@ -75,8 +75,9 @@ func TestPayloadMemSize(t *testing.T) { func TestPayloadsQueue(t *testing.T) { pq := PayloadsQueue{ - MaxSize: payloadMemFixedCost * 3, - SizeFn: payloadMemSize, + MaxSize: payloadMemFixedCost * 3, + SizeFn: payloadMemSize, + blockNos: make(map[uint64]bool), } require.Equal(t, 0, pq.Len()) require.Equal(t, (*eth.ExecutionPayload)(nil), pq.Peek()) @@ -85,6 +86,7 @@ func TestPayloadsQueue(t *testing.T) { a := ð.ExecutionPayload{BlockNumber: 3} b := ð.ExecutionPayload{BlockNumber: 4} c := ð.ExecutionPayload{BlockNumber: 5} + d := ð.ExecutionPayload{BlockNumber: 6} bAlt := ð.ExecutionPayload{BlockNumber: 4} require.NoError(t, pq.Push(b)) require.Equal(t, pq.Len(), 1) @@ -105,28 +107,33 @@ func TestPayloadsQueue(t *testing.T) { require.Equal(t, pq.Pop(), a) require.Equal(t, pq.Len(), 2, "expecting to pop the lowest") - require.NoError(t, pq.Push(bAlt)) - require.Equal(t, pq.Len(), 3) - require.Equal(t, pq.Peek(), b, "expecting b to be lowest, compared to bAlt and c") + require.Equal(t, pq.Peek(), b, "expecting b to be lowest, compared to c") require.Equal(t, pq.Pop(), b) - require.Equal(t, pq.Len(), 2) - require.Equal(t, pq.MemSize(), 2*payloadMemFixedCost) - - require.Equal(t, pq.Pop(), bAlt) require.Equal(t, pq.Len(), 1) - require.Equal(t, pq.Peek(), c, "expecting c to only remain") + require.Equal(t, pq.MemSize(), payloadMemFixedCost) + + require.Equal(t, pq.Pop(), c) + require.Equal(t, pq.Len(), 0, "expecting no items to remain") - d := ð.ExecutionPayload{BlockNumber: 5, Transactions: []eth.Data{make([]byte, payloadMemFixedCost*3+1)}} - require.Error(t, pq.Push(d), "cannot add payloads that are too large") + e := ð.ExecutionPayload{BlockNumber: 5, Transactions: []eth.Data{make([]byte, payloadMemFixedCost*3+1)}} + require.Error(t, pq.Push(e), "cannot add payloads that are too large") require.NoError(t, pq.Push(b)) + require.Equal(t, pq.Len(), 1, "expecting b") + require.Equal(t, pq.Peek(), b) + require.NoError(t, pq.Push(c)) require.Equal(t, pq.Len(), 2, "expecting b, c") require.Equal(t, pq.Peek(), b) require.NoError(t, pq.Push(a)) require.Equal(t, pq.Len(), 3, "expecting a, b, c") require.Equal(t, pq.Peek(), a) - require.NoError(t, pq.Push(bAlt)) - require.Equal(t, pq.Len(), 3, "expecting b, bAlt, c") + + // No duplicates allowed + require.Error(t, pq.Push(bAlt)) + + require.NoError(t, pq.Push(d)) + require.Equal(t, pq.Len(), 3) + require.Equal(t, pq.Peek(), b, "expecting b, c, d") require.NotContainsf(t, pq.pq[:], a, "a should be dropped after 3 items already exist under max size constraint") } diff --git a/op-node/rollup/derive/pipeline.go b/op-node/rollup/derive/pipeline.go index da7b8ca0a8a62..5d53a1721fa79 100644 --- a/op-node/rollup/derive/pipeline.go +++ b/op-node/rollup/derive/pipeline.go @@ -51,6 +51,7 @@ type EngineQueueStage interface { Finalize(l1Origin eth.L1BlockRef) AddUnsafePayload(payload *eth.ExecutionPayload) + GetUnsafeQueueGap(expectedNumber uint64) (uint64, uint64) Step(context.Context) error } @@ -106,6 +107,12 @@ func NewDerivationPipeline(log log.Logger, cfg *rollup.Config, l1Fetcher L1Fetch } } +// EngineReady returns true if the engine is ready to be used. +// When it's being reset its state is inconsistent, and should not be used externally. +func (dp *DerivationPipeline) EngineReady() bool { + return dp.resetting > 0 +} + func (dp *DerivationPipeline) Reset() { dp.resetting = 0 } @@ -160,6 +167,12 @@ func (dp *DerivationPipeline) AddUnsafePayload(payload *eth.ExecutionPayload) { dp.eng.AddUnsafePayload(payload) } +// GetUnsafeQueueGap retrieves the current [start, end] range of the gap between the tip of the unsafe priority queue and the unsafe head. +// If there is no gap, the start and end will be 0. +func (dp *DerivationPipeline) GetUnsafeQueueGap(expectedNumber uint64) (uint64, uint64) { + return dp.eng.GetUnsafeQueueGap(expectedNumber) +} + // Step tries to progress the buffer. // An EOF is returned if there pipeline is blocked by waiting for new L1 data. // If ctx errors no error is returned, but the step may exit early in a state that can still be continued. diff --git a/op-node/rollup/driver/driver.go b/op-node/rollup/driver/driver.go index fc4a4ef925fc0..d94eada3cd347 100644 --- a/op-node/rollup/driver/driver.go +++ b/op-node/rollup/driver/driver.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/sources" ) type Metrics interface { @@ -48,12 +49,14 @@ type DerivationPipeline interface { Reset() Step(ctx context.Context) error AddUnsafePayload(payload *eth.ExecutionPayload) + GetUnsafeQueueGap(expectedNumber uint64) (uint64, uint64) Finalize(ref eth.L1BlockRef) FinalizedL1() eth.L1BlockRef Finalized() eth.L2BlockRef SafeL2Head() eth.L2BlockRef UnsafeL2Head() eth.L2BlockRef Origin() eth.L1BlockRef + EngineReady() bool } type L1StateIface interface { @@ -80,7 +83,7 @@ type Network interface { } // NewDriver composes an events handler that tracks L1 state, triggers L2 derivation, and optionally sequences new L2 blocks. -func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, network Network, log log.Logger, snapshotLog log.Logger, metrics Metrics) *Driver { +func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, syncClient *sources.SyncClient, network Network, log log.Logger, snapshotLog log.Logger, metrics Metrics) *Driver { l1State := NewL1State(log, metrics) sequencerConfDepth := NewConfDepth(driverCfg.SequencerConfDepth, l1State.L1Head, l1) findL1Origin := NewL1OriginSelector(log, cfg, sequencerConfDepth) @@ -112,5 +115,6 @@ func NewDriver(driverCfg *Config, cfg *rollup.Config, l2 L2Chain, l1 L1Chain, ne l1SafeSig: make(chan eth.L1BlockRef, 10), l1FinalizedSig: make(chan eth.L1BlockRef, 10), unsafeL2Payloads: make(chan *eth.ExecutionPayload, 10), + L2SyncCl: syncClient, } } diff --git a/op-node/rollup/driver/sequencer.go b/op-node/rollup/driver/sequencer.go index 4da401d284e19..af6d469cc19dc 100644 --- a/op-node/rollup/driver/sequencer.go +++ b/op-node/rollup/driver/sequencer.go @@ -123,6 +123,14 @@ func (d *Sequencer) CancelBuildingBlock(ctx context.Context) { // PlanNextSequencerAction returns a desired delay till the RunNextSequencerAction call. func (d *Sequencer) PlanNextSequencerAction() time.Duration { + // If the engine is busy building safe blocks (and thus changing the head that we would sync on top of), + // then give it time to sync up. + if onto, _, safe := d.engine.BuildingPayload(); safe { + d.log.Warn("delaying sequencing to not interrupt safe-head changes", "onto", onto, "onto_time", onto.Time) + // approximates the worst-case time it takes to build a block, to reattempt sequencing after. + return time.Second * time.Duration(d.config.BlockTime) + } + head := d.engine.UnsafeL2Head() now := d.timeNow() @@ -173,7 +181,7 @@ func (d *Sequencer) BuildingOnto() eth.L2BlockRef { // Only critical errors are bubbled up, other errors are handled internally. // Internally starting or sealing of a block may fail with a derivation-like error: // - If it is a critical error, the error is bubbled up to the caller. -// - If it is a reset error, the ResettableEngineControl used to build blocks is requested to reset, and a backoff aplies. +// - If it is a reset error, the ResettableEngineControl used to build blocks is requested to reset, and a backoff applies. // No attempt is made at completing the block building. // - If it is a temporary error, a backoff is applied to reattempt building later. // - If it is any other error, a backoff is applied and building is cancelled. @@ -187,8 +195,15 @@ func (d *Sequencer) BuildingOnto() eth.L2BlockRef { // since it can consolidate previously sequenced blocks by comparing sequenced inputs with derived inputs. // If the derivation pipeline does force a conflicting block, then an ongoing sequencer task might still finish, // but the derivation can continue to reset until the chain is correct. +// If the engine is currently building safe blocks, then that building is not interrupted, and sequencing is delayed. func (d *Sequencer) RunNextSequencerAction(ctx context.Context) (*eth.ExecutionPayload, error) { - if _, buildingID, _ := d.engine.BuildingPayload(); buildingID != (eth.PayloadID{}) { + if onto, buildingID, safe := d.engine.BuildingPayload(); buildingID != (eth.PayloadID{}) { + if safe { + d.log.Warn("avoiding sequencing to not interrupt safe-head changes", "onto", onto, "onto_time", onto.Time) + // approximates the worst-case time it takes to build a block, to reattempt sequencing after. + d.nextAction = d.timeNow().Add(time.Second * time.Duration(d.config.BlockTime)) + return nil, nil + } payload, err := d.CompleteBuildingBlock(ctx) if err != nil { if errors.Is(err, derive.ErrCritical) { diff --git a/op-node/rollup/driver/state.go b/op-node/rollup/driver/state.go index 72e939703190e..1387c9136b192 100644 --- a/op-node/rollup/driver/state.go +++ b/op-node/rollup/driver/state.go @@ -16,6 +16,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-service/backoff" ) @@ -63,7 +64,11 @@ type Driver struct { l1SafeSig chan eth.L1BlockRef l1FinalizedSig chan eth.L1BlockRef + // Backup unsafe sync client + L2SyncCl *sources.SyncClient + // L2 Signals: + unsafeL2Payloads chan *eth.ExecutionPayload l1 L1Chain @@ -195,10 +200,18 @@ func (s *Driver) eventLoop() { sequencerTimer.Reset(delay) } + // Create a ticker to check if there is a gap in the engine queue every 15 seconds + // If there is, we send requests to the backup RPC to retrieve the missing payloads + // and add them to the unsafe queue. + altSyncTicker := time.NewTicker(15 * time.Second) + defer altSyncTicker.Stop() + for { // If we are sequencing, and the L1 state is ready, update the trigger for the next sequencer action. // This may adjust at any time based on fork-choice changes or previous errors. - if s.driverConfig.SequencerEnabled && !s.driverConfig.SequencerStopped && s.l1State.L1Head() != (eth.L1BlockRef{}) { + // And avoid sequencing if the derivation pipeline indicates the engine is not ready. + if s.driverConfig.SequencerEnabled && !s.driverConfig.SequencerStopped && + s.l1State.L1Head() != (eth.L1BlockRef{}) && s.derivation.EngineReady() { // update sequencer time if the head changed if s.sequencer.BuildingOnto().ID() != s.derivation.UnsafeL2Head().ID() { planSequencerAction() @@ -223,6 +236,12 @@ func (s *Driver) eventLoop() { } } planSequencerAction() // schedule the next sequencer action to keep the sequencing looping + case <-altSyncTicker.C: + // Check if there is a gap in the current unsafe payload queue. If there is, attempt to fetch + // missing payloads from the backup RPC (if it is configured). + if s.L2SyncCl != nil { + s.checkForGapInUnsafeQueue(ctx) + } case payload := <-s.unsafeL2Payloads: s.snapshot("New unsafe payload") s.log.Info("Optimistically queueing unsafe L2 execution payload", "id", payload.ID()) @@ -442,3 +461,36 @@ type hashAndErrorChannel struct { hash common.Hash err chan error } + +// checkForGapInUnsafeQueue checks if there is a gap in the unsafe queue and attempts to retrieve the missing payloads from the backup RPC. +// WARNING: The sync client's attempt to retrieve the missing payloads is not guaranteed to succeed, and it will fail silently (besides +// emitting warning logs) if the requests fail. +func (s *Driver) checkForGapInUnsafeQueue(ctx context.Context) { + // subtract genesis time from wall clock to get the time elapsed since genesis, and then divide that + // difference by the block time to get the expected L2 block number at the current time. If the + // unsafe head does not have this block number, then there is a gap in the queue. + wallClock := uint64(time.Now().Unix()) + genesisTimestamp := s.config.Genesis.L2Time + wallClockGenesisDiff := wallClock - genesisTimestamp + expectedL2Block := wallClockGenesisDiff / s.config.BlockTime + + start, end := s.derivation.GetUnsafeQueueGap(expectedL2Block) + size := end - start + + // Check if there is a gap between the unsafe head and the expected L2 block number at the current time. + if size > 0 { + s.log.Warn("Gap in payload queue tip and expected unsafe chain detected", "start", start, "end", end, "size", size) + s.log.Info("Attempting to fetch missing payloads from backup RPC", "start", start, "end", end, "size", size) + + // Attempt to fetch the missing payloads from the backup unsafe sync RPC concurrently. + // Concurrent requests are safe here due to the engine queue being a priority queue. + for blockNumber := start; blockNumber <= end; blockNumber++ { + select { + case s.L2SyncCl.FetchUnsafeBlock <- blockNumber: + // Do nothing- the block number was successfully sent into the channel + default: + return // If the channel is full, return and wait for the next iteration of the event loop + } + } + } +} diff --git a/op-node/rollup/output_root.go b/op-node/rollup/output_root.go index 7f8b44e28c0ad..b27d4f7937d19 100644 --- a/op-node/rollup/output_root.go +++ b/op-node/rollup/output_root.go @@ -1,29 +1,26 @@ package rollup import ( + "errors" + "github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-node/eth" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) -// ComputeL2OutputRoot computes the L2 output root -func ComputeL2OutputRoot(l2OutputRootVersion eth.Bytes32, blockHash common.Hash, blockRoot common.Hash, storageRoot common.Hash) eth.Bytes32 { - digest := crypto.Keccak256Hash( - l2OutputRootVersion[:], - blockRoot.Bytes(), - storageRoot[:], - blockHash.Bytes(), - ) - return eth.Bytes32(digest) -} +var NilProof = errors.New("Output root proof is nil") + +// ComputeL2OutputRoot computes the L2 output root by hashing an output root proof. +func ComputeL2OutputRoot(proofElements *bindings.TypesOutputRootProof) (eth.Bytes32, error) { + if proofElements == nil { + return eth.Bytes32{}, NilProof + } -// HashOutputRootProof computes the hash of the output root proof -func HashOutputRootProof(proof *bindings.TypesOutputRootProof) eth.Bytes32 { - return ComputeL2OutputRoot( - proof.Version, - proof.StateRoot, - proof.MessagePasserStorageRoot, - proof.LatestBlockhash, + digest := crypto.Keccak256Hash( + proofElements.Version[:], + proofElements.StateRoot[:], + proofElements.MessagePasserStorageRoot[:], + proofElements.LatestBlockhash[:], ) + return eth.Bytes32(digest), nil } diff --git a/op-node/rollup/sync/start.go b/op-node/rollup/sync/start.go index 5ade51532aacf..de2def4550235 100644 --- a/op-node/rollup/sync/start.go +++ b/op-node/rollup/sync/start.go @@ -126,8 +126,17 @@ func FindL2Heads(ctx context.Context, cfg *rollup.Config, l1 L1Chain, l2 L2Chain // then we return the last L2 block of the epoch before that as safe head. // Each loop iteration we traverse a single L2 block, and we check if the L1 origins are consistent. for { - // Fetch L1 information if we never had it, or if we do not have it for the current origin - if l1Block == (eth.L1BlockRef{}) || n.L1Origin.Hash != l1Block.Hash { + // Fetch L1 information if we never had it, or if we do not have it for the current origin. + // Optimization: as soon as we have a previous L1 block, try to traverse L1 by hash instead of by number, to fill the cache. + if n.L1Origin.Hash == l1Block.ParentHash { + b, err := l1.L1BlockRefByHash(ctx, n.L1Origin.Hash) + if err != nil { + // Exit, find-sync start should start over, to move to an available L1 chain with block-by-number / not-found case. + return nil, fmt.Errorf("failed to retrieve L1 block: %w", err) + } + l1Block = b + ahead = false + } else if l1Block == (eth.L1BlockRef{}) || n.L1Origin.Hash != l1Block.Hash { b, err := l1.L1BlockRefByNumber(ctx, n.L1Origin.Number) // if L2 is ahead of L1 view, then consider it a "plausible" head notFound := errors.Is(err, ethereum.NotFound) diff --git a/op-node/rollup/types.go b/op-node/rollup/types.go index 6f51ccaa27e0e..59317ec66d2d9 100644 --- a/op-node/rollup/types.go +++ b/op-node/rollup/types.go @@ -157,7 +157,7 @@ func (cfg *Config) CheckL2ChainID(ctx context.Context, client L2Client) error { return err } if cfg.L2ChainID.Cmp(id) != 0 { - return fmt.Errorf("incorrect L2 RPC chain id %d, expected %d", cfg.L2ChainID, id) + return fmt.Errorf("incorrect L2 RPC chain id, expected from config %d, obtained from client %d", cfg.L2ChainID, id) } return nil } diff --git a/op-node/service.go b/op-node/service.go index ce737dcdefffa..3cd50deaedf9b 100644 --- a/op-node/service.go +++ b/op-node/service.go @@ -36,10 +36,7 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { return nil, err } - driverConfig, err := NewDriverConfig(ctx) - if err != nil { - return nil, err - } + driverConfig := NewDriverConfig(ctx) p2pSignerSetup, err := p2pcli.LoadSignerSetup(ctx) if err != nil { @@ -51,19 +48,19 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { return nil, fmt.Errorf("failed to load p2p config: %w", err) } - l1Endpoint, err := NewL1EndpointConfig(ctx) - if err != nil { - return nil, fmt.Errorf("failed to load l1 endpoint info: %w", err) - } + l1Endpoint := NewL1EndpointConfig(ctx) l2Endpoint, err := NewL2EndpointConfig(ctx, log) if err != nil { return nil, fmt.Errorf("failed to load l2 endpoints info: %w", err) } + l2SyncEndpoint := NewL2SyncEndpointConfig(ctx) + cfg := &node.Config{ L1: l1Endpoint, L2: l2Endpoint, + L2Sync: l2SyncEndpoint, Rollup: *rollupConfig, Driver: *driverConfig, RPC: node.RPCConfig{ @@ -96,12 +93,12 @@ func NewConfig(ctx *cli.Context, log log.Logger) (*node.Config, error) { return cfg, nil } -func NewL1EndpointConfig(ctx *cli.Context) (*node.L1EndpointConfig, error) { +func NewL1EndpointConfig(ctx *cli.Context) *node.L1EndpointConfig { return &node.L1EndpointConfig{ L1NodeAddr: ctx.GlobalString(flags.L1NodeAddr.Name), L1TrustRPC: ctx.GlobalBool(flags.L1TrustRPC.Name), L1RPCKind: sources.RPCProviderKind(strings.ToLower(ctx.GlobalString(flags.L1RPCProviderKind.Name))), - }, nil + } } func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConfig, error) { @@ -134,14 +131,22 @@ func NewL2EndpointConfig(ctx *cli.Context, log log.Logger) (*node.L2EndpointConf }, nil } -func NewDriverConfig(ctx *cli.Context) (*driver.Config, error) { +// NewL2SyncEndpointConfig returns a pointer to a L2SyncEndpointConfig if the +// flag is set, otherwise nil. +func NewL2SyncEndpointConfig(ctx *cli.Context) *node.L2SyncEndpointConfig { + return &node.L2SyncEndpointConfig{ + L2NodeAddr: ctx.GlobalString(flags.BackupL2UnsafeSyncRPC.Name), + } +} + +func NewDriverConfig(ctx *cli.Context) *driver.Config { return &driver.Config{ VerifierConfDepth: ctx.GlobalUint64(flags.VerifierL1Confs.Name), SequencerConfDepth: ctx.GlobalUint64(flags.SequencerL1Confs.Name), SequencerEnabled: ctx.GlobalBool(flags.SequencerEnabledFlag.Name), SequencerStopped: ctx.GlobalBool(flags.SequencerStoppedFlag.Name), SequencerMaxSafeLag: ctx.GlobalUint64(flags.SequencerMaxSafeLagFlag.Name), - }, nil + } } func NewRollupConfig(ctx *cli.Context) (*rollup.Config, error) { diff --git a/op-node/sources/l1_client.go b/op-node/sources/l1_client.go index af224c0c9dae4..d873e64e6f141 100644 --- a/op-node/sources/l1_client.go +++ b/op-node/sources/l1_client.go @@ -24,6 +24,7 @@ type L1ClientConfig struct { func L1ClientDefaultConfig(config *rollup.Config, trustRPC bool, kind RPCProviderKind) *L1ClientConfig { // Cache 3/2 worth of sequencing window of receipts and txs span := int(config.SeqWindowSize) * 3 / 2 + fullSpan := span if span > 1000 { // sanity cap. If a large sequencing window is configured, do not make the cache too large span = 1000 } @@ -40,7 +41,8 @@ func L1ClientDefaultConfig(config *rollup.Config, trustRPC bool, kind RPCProvide MustBePostMerge: false, RPCProviderKind: kind, }, - L1BlockRefsCacheSize: span, + // Not bounded by span, to cover find-sync-start range fully for speedy recovery after errors. + L1BlockRefsCacheSize: fullSpan, } } diff --git a/op-node/sources/l2_client.go b/op-node/sources/l2_client.go index e65d660c86c52..d2970fa813dda 100644 --- a/op-node/sources/l2_client.go +++ b/op-node/sources/l2_client.go @@ -34,6 +34,7 @@ func L2ClientDefaultConfig(config *rollup.Config, trustRPC bool) *L2ClientConfig span *= 12 span /= int(config.BlockTime) } + fullSpan := span if span > 1000 { // sanity cap. If a large sequencing window is configured, do not make the cache too large span = 1000 } @@ -50,7 +51,8 @@ func L2ClientDefaultConfig(config *rollup.Config, trustRPC bool) *L2ClientConfig MustBePostMerge: true, RPCProviderKind: RPCKindBasic, }, - L2BlockRefsCacheSize: span, + // Not bounded by span, to cover find-sync-start range fully for speedy recovery after errors. + L2BlockRefsCacheSize: fullSpan, L1ConfigsCacheSize: span, RollupCfg: config, } diff --git a/op-node/sources/sync_client.go b/op-node/sources/sync_client.go new file mode 100644 index 0000000000000..6c5aa6dccf9db --- /dev/null +++ b/op-node/sources/sync_client.go @@ -0,0 +1,122 @@ +package sources + +import ( + "context" + "errors" + "sync" + + "github.com/ethereum-optimism/optimism/op-node/client" + "github.com/ethereum-optimism/optimism/op-node/eth" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/sources/caching" + "github.com/ethereum/go-ethereum/log" + "github.com/libp2p/go-libp2p/core/peer" +) + +var ErrNoUnsafeL2PayloadChannel = errors.New("unsafeL2Payloads channel must not be nil") + +// RpcSyncPeer is a mock PeerID for the RPC sync client. +var RpcSyncPeer peer.ID = "ALT_RPC_SYNC" + +type receivePayload = func(ctx context.Context, from peer.ID, payload *eth.ExecutionPayload) error + +type SyncClientInterface interface { + Start() error + Close() error + fetchUnsafeBlockFromRpc(ctx context.Context, blockNumber uint64) +} + +type SyncClient struct { + *L2Client + FetchUnsafeBlock chan uint64 + done chan struct{} + receivePayload receivePayload + wg sync.WaitGroup +} + +var _ SyncClientInterface = (*SyncClient)(nil) + +type SyncClientConfig struct { + L2ClientConfig +} + +func SyncClientDefaultConfig(config *rollup.Config, trustRPC bool) *SyncClientConfig { + return &SyncClientConfig{ + *L2ClientDefaultConfig(config, trustRPC), + } +} + +func NewSyncClient(receiver receivePayload, client client.RPC, log log.Logger, metrics caching.Metrics, config *SyncClientConfig) (*SyncClient, error) { + l2Client, err := NewL2Client(client, log, metrics, &config.L2ClientConfig) + if err != nil { + return nil, err + } + + return &SyncClient{ + L2Client: l2Client, + FetchUnsafeBlock: make(chan uint64, 128), + done: make(chan struct{}), + receivePayload: receiver, + }, nil +} + +// Start starts up the state loop. +// The loop will have been started if err is not nil. +func (s *SyncClient) Start() error { + s.wg.Add(1) + go s.eventLoop() + return nil +} + +// Close sends a signal to the event loop to stop. +func (s *SyncClient) Close() error { + s.done <- struct{}{} + s.wg.Wait() + return nil +} + +// eventLoop is the main event loop for the sync client. +func (s *SyncClient) eventLoop() { + defer s.wg.Done() + s.log.Info("Starting sync client event loop") + + for { + select { + case <-s.done: + return + case blockNumber := <-s.FetchUnsafeBlock: + s.fetchUnsafeBlockFromRpc(context.Background(), blockNumber) + } + } +} + +// fetchUnsafeBlockFromRpc attempts to fetch an unsafe execution payload from the backup unsafe sync RPC. +// WARNING: This function fails silently (aside from warning logs). +// +// Post Shanghai hardfork, the engine API's `PayloadBodiesByRange` method will be much more efficient, but for now, +// the `eth_getBlockByNumber` method is more widely available. +func (s *SyncClient) fetchUnsafeBlockFromRpc(ctx context.Context, blockNumber uint64) { + s.log.Info("Requesting unsafe payload from backup RPC", "block number", blockNumber) + + payload, err := s.PayloadByNumber(ctx, blockNumber) + if err != nil { + s.log.Warn("Failed to convert block to execution payload", "block number", blockNumber, "err", err) + return + } + + // Signature validation is not necessary here since the backup RPC is trusted. + if _, ok := payload.CheckBlockHash(); !ok { + s.log.Warn("Received invalid payload from backup RPC; invalid block hash", "payload", payload.ID()) + return + } + + s.log.Info("Received unsafe payload from backup RPC", "payload", payload.ID()) + + // Send the retrieved payload to the `unsafeL2Payloads` channel. + if err = s.receivePayload(ctx, RpcSyncPeer, payload); err != nil { + s.log.Warn("Failed to send payload into the driver's unsafeL2Payloads channel", "payload", payload.ID(), "err", err) + return + } else { + s.log.Info("Sent received payload into the driver's unsafeL2Payloads channel", "payload", payload.ID()) + } +} diff --git a/op-node/sources/types.go b/op-node/sources/types.go index 991ed856f7a18..c812b036b595c 100644 --- a/op-node/sources/types.go +++ b/op-node/sources/types.go @@ -47,6 +47,9 @@ type HeaderInfo struct { txHash common.Hash receiptHash common.Hash gasUsed uint64 + + // withdrawalsRoot was added in Shapella and is thus optional + withdrawalsRoot *common.Hash } var _ eth.BlockInfo = (*HeaderInfo)(nil) @@ -113,7 +116,10 @@ type rpcHeader struct { Nonce types.BlockNonce `json:"nonce"` // BaseFee was added by EIP-1559 and is ignored in legacy headers. - BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` + BaseFee *hexutil.Big `json:"baseFeePerGas"` + + // WithdrawalsRoot was added by EIP-4895 and is ignored in legacy headers. + WithdrawalsRoot *common.Hash `json:"withdrawalsRoot"` // untrusted info included by RPC, may have to be checked Hash common.Hash `json:"hash"` @@ -144,22 +150,23 @@ func (hdr *rpcHeader) checkPostMerge() error { func (hdr *rpcHeader) computeBlockHash() common.Hash { gethHeader := types.Header{ - ParentHash: hdr.ParentHash, - UncleHash: hdr.UncleHash, - Coinbase: hdr.Coinbase, - Root: hdr.Root, - TxHash: hdr.TxHash, - ReceiptHash: hdr.ReceiptHash, - Bloom: types.Bloom(hdr.Bloom), - Difficulty: (*big.Int)(&hdr.Difficulty), - Number: new(big.Int).SetUint64(uint64(hdr.Number)), - GasLimit: uint64(hdr.GasLimit), - GasUsed: uint64(hdr.GasUsed), - Time: uint64(hdr.Time), - Extra: hdr.Extra, - MixDigest: hdr.MixDigest, - Nonce: hdr.Nonce, - BaseFee: (*big.Int)(hdr.BaseFee), + ParentHash: hdr.ParentHash, + UncleHash: hdr.UncleHash, + Coinbase: hdr.Coinbase, + Root: hdr.Root, + TxHash: hdr.TxHash, + ReceiptHash: hdr.ReceiptHash, + Bloom: types.Bloom(hdr.Bloom), + Difficulty: (*big.Int)(&hdr.Difficulty), + Number: new(big.Int).SetUint64(uint64(hdr.Number)), + GasLimit: uint64(hdr.GasLimit), + GasUsed: uint64(hdr.GasUsed), + Time: uint64(hdr.Time), + Extra: hdr.Extra, + MixDigest: hdr.MixDigest, + Nonce: hdr.Nonce, + BaseFee: (*big.Int)(hdr.BaseFee), + WithdrawalsHash: hdr.WithdrawalsRoot, } return gethHeader.Hash() } @@ -177,17 +184,18 @@ func (hdr *rpcHeader) Info(trustCache bool, mustBePostMerge bool) (*HeaderInfo, } info := HeaderInfo{ - hash: hdr.Hash, - parentHash: hdr.ParentHash, - coinbase: hdr.Coinbase, - root: hdr.Root, - number: uint64(hdr.Number), - time: uint64(hdr.Time), - mixDigest: hdr.MixDigest, - baseFee: (*big.Int)(hdr.BaseFee), - txHash: hdr.TxHash, - receiptHash: hdr.ReceiptHash, - gasUsed: uint64(hdr.GasUsed), + hash: hdr.Hash, + parentHash: hdr.ParentHash, + coinbase: hdr.Coinbase, + root: hdr.Root, + number: uint64(hdr.Number), + time: uint64(hdr.Time), + mixDigest: hdr.MixDigest, + baseFee: (*big.Int)(hdr.BaseFee), + txHash: hdr.TxHash, + receiptHash: hdr.ReceiptHash, + gasUsed: uint64(hdr.GasUsed), + withdrawalsRoot: hdr.WithdrawalsRoot, } return &info, nil } diff --git a/op-node/sources/types_test.go b/op-node/sources/types_test.go new file mode 100644 index 0000000000000..87bba2a98dd73 --- /dev/null +++ b/op-node/sources/types_test.go @@ -0,0 +1,35 @@ +package sources + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBlockJSON(t *testing.T) { + testCases := []struct { + Name string + OK bool + Data string + }{ + {Name: "pre-Shanghai good tx", OK: true, Data: `{"number":"0x840249","hash":"0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663","transactions":["0x39c666d9b5cec429accad7b0f94f789ca2ebeb5294b8b129c1b76f552daf57d3","0x2ca7289ab3738d17e0f5093bd96c97c06c9a2ea4c22fc84a6a7fbfda93ce55ee","0xb0085de1476530de3efc6928c4683e7c40f8fac18875f74cbcc47df159de17d9","0xe01c8631c86ded63af95b8dbc0c8aac5d31254c14d6ecb4cc51d98259d838e52","0x69414a126a6f07ab5e31ad2f9069fb986b7c490e096898473873e41ece6af783","0xa2fef1133ee726533c7f190f246fede123e3706a03933c1febc92618f90d2804","0x6585ec5c4c2bbf1f683f90f58e18f3b38d875e94457fe4cbb7bc5bf6581f83af","0x1db276b864fbf01dcf8cededf8d597553ecb0eb9438edfaf2f5bd0cc93297c66","0xcbe7ed31654af4e191ca53445b82de040ae2cd92459a3f951bdcce423d780f08","0x808ba5211f03cc78a732ff0f9383c6355e63c83ae8c6035ced2ba6f7c331dc63","0xdd66f1f26672849ef54c420210f479c9f0c46924d8e9f7b210981ffe8d3fac82","0x254abb2f8cdcffe9ef62ab924312a1e4142578db87e4f7c199fd35991e92f014","0xa7b7c654e7073b8043b680b7ffc95d3f2099abaa0b0578d6f954a2a7c99404e1","0x7ccdfa698c8acf47ab9316ed078eb40819ff575bcf612c6f59f29e7726df3f96","0xa0b035ef315824a6f6a6565fa8de27042ade3af9cf0583a36dea83d6e01bf2a8","0x1ebad7f3e8cb3543d4963686a94d99f61839f666831eab9c9c1b4711de11d3d9","0x501750278e91d8b5be1ccf60e793d4bbcd9b3bb3ccc518d3634a71caeac65f48","0xd80ff8af29ae163d5811ba511e60b3a87a279f677bb3872a0f1aa6d0a226e880","0x096acab3b3fe47b149d375782d1eb00b9fef7904076d60c54b3c197b04e6bf82","0xbe9d1738af74a22400591a9a808fb01a25ab41e2e56f202dd7251eb113e8ceeb","0x0834c720e55cccd97aaf4f8fb0cb66afb9881fb6a762c0f70473ec53f98a712e","0x51a0c33c9b37245b416575bdd2751c0d8a5d8bead49585ac427bfc873d4016af","0x531c25d51ccda59aa9ea82e85c99be9dd4e285af9b8973cbab9ac4a38e26e55a","0x93ac6c08d21cb1b61ff59e5e2d6fa3f9ad54008b0a66c669199050bef219f6e3","0x3792db6dd6285f409e4281951e9f78dad16c4a78072ff1c909dfadea5658d857","0xd2d51764c01e8c0a43fbe362704388df5bacf7e5e620c3864e242530ffb3e828","0x516b0227d9e64eb6e0de6862764d40f5376b5f12fec878436fea3479b4c36bb8","0x81b0abc78b82840adb666775b182a9e292f663b64bcd35004c04436ed3c8281c","0xd0287570d431d2baea96ecc81cb890e7f4f06ab5df02f9b4067768abca19acb5","0x76ddab2674369f34946c5fa2f05e2aa8566d86235b83e808e9b27bc106e04ac7","0x34a5c74011a2c8a00103bc91bfbfd94aa99cd569be69066e4bf64d188fe8714e","0x7b9730ead1b9f59b206d0ddea87be9383ba3fc7b496c7863b0cb847889b86617","0x77166ee0409ba86bd26e7c03ad1a927abaf5af8a8a37149e725cd37512091dd6","0x3c2b6c2ae505c5c36d5f316c1fcb5f54f7346ed35ae35c93462991ded7968a68","0xf99a792837e13827b5e0a8915fb59c760babc95d242feca99a5594e64ff6b6e2","0x522313f5d923f048ae5bd0b5595c1f4fc883bc0b3cf3cb0939d3fcf8b08c829c","0x471ceb0e85af594aa56deca54cb8198567b2afd8406722ea530077aaa6b641b3","0x3e9dca502e9039ae0c6d642f62e9562ff00010c6bfbb8234a6135712ba70dfda","0xc95cac67267f4accb9b5950316ac64772f7d082bed6b712c09cf2da0bdc237b7","0xfca28fdbd13fc16daf7aec7d4a2ad2c6b5f0b2a7b0fb1d9167c09b5e115ff26e","0xc73124ca798b2f7a5df2ea4d568efab2f41b135130ea5cc41d4bcb4b5c57d5bd","0x29abb76b5e7a5ce137bf9c22474d386eb58d249f43178d2b2e15c16dfdc5ca80","0x03e5ab25a58bd44fb9dd0c698b323eab8b8363479dfcbcbb16d0a0bd983880ae","0x3c8ee80ddea7fa2d2b75e44563c10c10756f598e8ad252a49c5d3e8a5c8e6cbf","0xaffa73b68bc7ab0c3f5e28377f5ca0a5df33c0a485f64dc094b7f6ae23353203","0xc66c9c66fbc8fe97fcc16506cde7a58689af1004a18c6171cfe763bcd94f50b2","0x80fec96707519172b53790610d5800cd09a4243aca9bacfa956c56337d06f820","0x61b33bfcf11214906dcdce7d7ed83ad82f38184c03ded07f7782059d02eeedea","0x5d4138d4e28a8327e506cb012346b1b38b65f615a2b991d35cf5d4de244b3e6d","0x875a142b6dfcf10ffb71a7afe0ce4672c047fc7e162ba0383390516d6334d45d","0x79b6df832bfbd04085d0b005a6e3ad8f00fc8717eed59280aa8107268b71e7e0","0xcb2fb25d268f65dc9312e89bd3c328c9847a3c9da282026793c54a745f825ab5","0xe483d4a36ad19fd5eacb7f6d9ad3ce080ad70ac673273e710f6e3d5acbc6559c","0x0564242c37d5013b671ef4864394cc0f3924c589f8aad64118223a9af2f164f6","0x48db358e80b278c3a46c2a166339797060a40f33984a5d974992cd9722139d5d","0x69d7758db91fae31fa35ecbed4d40897c5087f45dc796cd796b8ceead21f972e","0x2951478916ecd27a8e808d08f85be4bf2c0b0e0546f21f4e309145dd96eb8df1","0xaca9028cb5d55bbf71b7bff9884a9a3b0b38a575ffc8f8807ce345cf8bd298ef","0xc7f625a19ee41a1750eac9428b4394a9a2476b8ea2d31b4c2f9f5b4fcb86cae3","0x45499074aa521ac4151138f0aad969bcc2dfc1648d22ff8c42e51c74cb77414d","0x00b5b05c6d1a2eb8abe2c383da600516515e383fc8a29953bb6e6d167e9705b2","0x6fc411f24c7b4b8d821b45de32b9edc5ac998d1ac748a98abe8e983c6f39fc19"],"difficulty":"0x0","extraData":"0xd883010b02846765746888676f312e32302e31856c696e7578","gasLimit":"0x1c9c380","gasUsed":"0xa79638","logsBloom":"0xb034000008010014411408c080a0018440087220211154100005a1388807241142a2504080034a00111212a47f05008520200000280202a12800538cc06488486a0141989c7800c0c848011f02249661800e08449145b040a252d18082c009000641004052c80102000804ac10901c24032000980010438a01e50a90a0d8008c138c21204040000b20425000833041028000148124c2012d0aa8d1d0548301808228002015184090000224021040d68220100210220480420308455c382a40020130dc42502986080600000115034c0401c81828490410308005610048026b822e10b4228071ba00bdd20140621b2000c02012300808084181ac308200000011","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x31f0c0305fc07a93b1a33da339c79aadbe8d9811c78d2b514cd18d64e1328f25","nonce":"0x0000000000000000","parentHash":"0x2303b55af4add799b19275a491b150c1a03075395f87a7856a4e3327595ed7df","receiptsRoot":"0x99da71b17ae1929db912c3315ebe349d37f2bb600454616fdde0ee90d6dbc59e","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0xea6d","stateRoot":"0xd12bf4cf3941cf48be329a939b13d3403d326841c69cdcc9a9c13ab2f227e904","timestamp":"0x640fdeb0","totalDifficulty":"0xa4a470","transactionsRoot":"0x1ad3212eca045505cfc4cacf675b5fa2e7dc7b9f9cee88191464f97d1c9fbca4","uncles":[],"baseFeePerGas":"0x7ccf990f8"} +`}, + {Name: "pre-Shanghai bad receipts root", OK: false, Data: `{"number":"0x840249","hash":"0x9ef7cd2241202b919a0e51240818a8666c73f7ce4b908931e3ae6d26d30f7663","transactions":["0x39c666d9b5cec429accad7b0f94f789ca2ebeb5294b8b129c1b76f552daf57d3","0x2ca7289ab3738d17e0f5093bd96c97c06c9a2ea4c22fc84a6a7fbfda93ce55ee","0xb0085de1476530de3efc6928c4683e7c40f8fac18875f74cbcc47df159de17d9","0xe01c8631c86ded63af95b8dbc0c8aac5d31254c14d6ecb4cc51d98259d838e52","0x69414a126a6f07ab5e31ad2f9069fb986b7c490e096898473873e41ece6af783","0xa2fef1133ee726533c7f190f246fede123e3706a03933c1febc92618f90d2804","0x6585ec5c4c2bbf1f683f90f58e18f3b38d875e94457fe4cbb7bc5bf6581f83af","0x1db276b864fbf01dcf8cededf8d597553ecb0eb9438edfaf2f5bd0cc93297c66","0xcbe7ed31654af4e191ca53445b82de040ae2cd92459a3f951bdcce423d780f08","0x808ba5211f03cc78a732ff0f9383c6355e63c83ae8c6035ced2ba6f7c331dc63","0xdd66f1f26672849ef54c420210f479c9f0c46924d8e9f7b210981ffe8d3fac82","0x254abb2f8cdcffe9ef62ab924312a1e4142578db87e4f7c199fd35991e92f014","0xa7b7c654e7073b8043b680b7ffc95d3f2099abaa0b0578d6f954a2a7c99404e1","0x7ccdfa698c8acf47ab9316ed078eb40819ff575bcf612c6f59f29e7726df3f96","0xa0b035ef315824a6f6a6565fa8de27042ade3af9cf0583a36dea83d6e01bf2a8","0x1ebad7f3e8cb3543d4963686a94d99f61839f666831eab9c9c1b4711de11d3d9","0x501750278e91d8b5be1ccf60e793d4bbcd9b3bb3ccc518d3634a71caeac65f48","0xd80ff8af29ae163d5811ba511e60b3a87a279f677bb3872a0f1aa6d0a226e880","0x096acab3b3fe47b149d375782d1eb00b9fef7904076d60c54b3c197b04e6bf82","0xbe9d1738af74a22400591a9a808fb01a25ab41e2e56f202dd7251eb113e8ceeb","0x0834c720e55cccd97aaf4f8fb0cb66afb9881fb6a762c0f70473ec53f98a712e","0x51a0c33c9b37245b416575bdd2751c0d8a5d8bead49585ac427bfc873d4016af","0x531c25d51ccda59aa9ea82e85c99be9dd4e285af9b8973cbab9ac4a38e26e55a","0x93ac6c08d21cb1b61ff59e5e2d6fa3f9ad54008b0a66c669199050bef219f6e3","0x3792db6dd6285f409e4281951e9f78dad16c4a78072ff1c909dfadea5658d857","0xd2d51764c01e8c0a43fbe362704388df5bacf7e5e620c3864e242530ffb3e828","0x516b0227d9e64eb6e0de6862764d40f5376b5f12fec878436fea3479b4c36bb8","0x81b0abc78b82840adb666775b182a9e292f663b64bcd35004c04436ed3c8281c","0xd0287570d431d2baea96ecc81cb890e7f4f06ab5df02f9b4067768abca19acb5","0x76ddab2674369f34946c5fa2f05e2aa8566d86235b83e808e9b27bc106e04ac7","0x34a5c74011a2c8a00103bc91bfbfd94aa99cd569be69066e4bf64d188fe8714e","0x7b9730ead1b9f59b206d0ddea87be9383ba3fc7b496c7863b0cb847889b86617","0x77166ee0409ba86bd26e7c03ad1a927abaf5af8a8a37149e725cd37512091dd6","0x3c2b6c2ae505c5c36d5f316c1fcb5f54f7346ed35ae35c93462991ded7968a68","0xf99a792837e13827b5e0a8915fb59c760babc95d242feca99a5594e64ff6b6e2","0x522313f5d923f048ae5bd0b5595c1f4fc883bc0b3cf3cb0939d3fcf8b08c829c","0x471ceb0e85af594aa56deca54cb8198567b2afd8406722ea530077aaa6b641b3","0x3e9dca502e9039ae0c6d642f62e9562ff00010c6bfbb8234a6135712ba70dfda","0xc95cac67267f4accb9b5950316ac64772f7d082bed6b712c09cf2da0bdc237b7","0xfca28fdbd13fc16daf7aec7d4a2ad2c6b5f0b2a7b0fb1d9167c09b5e115ff26e","0xc73124ca798b2f7a5df2ea4d568efab2f41b135130ea5cc41d4bcb4b5c57d5bd","0x29abb76b5e7a5ce137bf9c22474d386eb58d249f43178d2b2e15c16dfdc5ca80","0x03e5ab25a58bd44fb9dd0c698b323eab8b8363479dfcbcbb16d0a0bd983880ae","0x3c8ee80ddea7fa2d2b75e44563c10c10756f598e8ad252a49c5d3e8a5c8e6cbf","0xaffa73b68bc7ab0c3f5e28377f5ca0a5df33c0a485f64dc094b7f6ae23353203","0xc66c9c66fbc8fe97fcc16506cde7a58689af1004a18c6171cfe763bcd94f50b2","0x80fec96707519172b53790610d5800cd09a4243aca9bacfa956c56337d06f820","0x61b33bfcf11214906dcdce7d7ed83ad82f38184c03ded07f7782059d02eeedea","0x5d4138d4e28a8327e506cb012346b1b38b65f615a2b991d35cf5d4de244b3e6d","0x875a142b6dfcf10ffb71a7afe0ce4672c047fc7e162ba0383390516d6334d45d","0x79b6df832bfbd04085d0b005a6e3ad8f00fc8717eed59280aa8107268b71e7e0","0xcb2fb25d268f65dc9312e89bd3c328c9847a3c9da282026793c54a745f825ab5","0xe483d4a36ad19fd5eacb7f6d9ad3ce080ad70ac673273e710f6e3d5acbc6559c","0x0564242c37d5013b671ef4864394cc0f3924c589f8aad64118223a9af2f164f6","0x48db358e80b278c3a46c2a166339797060a40f33984a5d974992cd9722139d5d","0x69d7758db91fae31fa35ecbed4d40897c5087f45dc796cd796b8ceead21f972e","0x2951478916ecd27a8e808d08f85be4bf2c0b0e0546f21f4e309145dd96eb8df1","0xaca9028cb5d55bbf71b7bff9884a9a3b0b38a575ffc8f8807ce345cf8bd298ef","0xc7f625a19ee41a1750eac9428b4394a9a2476b8ea2d31b4c2f9f5b4fcb86cae3","0x45499074aa521ac4151138f0aad969bcc2dfc1648d22ff8c42e51c74cb77414d","0x00b5b05c6d1a2eb8abe2c383da600516515e383fc8a29953bb6e6d167e9705b2","0x6fc411f24c7b4b8d821b45de32b9edc5ac998d1ac748a98abe8e983c6f39fc19"],"difficulty":"0x0","extraData":"0xd883010b02846765746888676f312e32302e31856c696e7578","gasLimit":"0x1c9c380","gasUsed":"0xa79638","logsBloom":"0xb034000008010014411408c080a0018440087220211154100005a1388807241142a2504080034a00111212a47f05008520200000280202a12800538cc06488486a0141989c7800c0c848011f02249661800e08449145b040a252d18082c009000641004052c80102000804ac10901c24032000980010438a01e50a90a0d8008c138c21204040000b20425000833041028000148124c2012d0aa8d1d0548301808228002015184090000224021040d68220100210220480420308455c382a40020130dc42502986080600000115034c0401c81828490410308005610048026b822e10b4228071ba00bdd20140621b2000c02012300808084181ac308200000011","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x31f0c0305fc07a93b1a33da339c79aadbe8d9811c78d2b514cd18d64e1328f25","nonce":"0x0000000000000000","parentHash":"0x2303b55af4add799b19275a491b150c1a03075395f87a7856a4e3327595ed7df","receiptsRoot":"0x99da71b17ae1929db912c3315ebe349d37f2bb600454616fdde0ee90d6dbc59f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0xea6d","stateRoot":"0xd12bf4cf3941cf48be329a939b13d3403d326841c69cdcc9a9c13ab2f227e904","timestamp":"0x640fdeb0","totalDifficulty":"0xa4a470","transactionsRoot":"0x1ad3212eca045505cfc4cacf675b5fa2e7dc7b9f9cee88191464f97d1c9fbca4","uncles":[],"baseFeePerGas":"0x7ccf990f8"} +`}, + {Name: "Shanghai good tx", OK: true, Data: `{"baseFeePerGas":"0x3fb7c357","difficulty":"0x0","extraData":"0x","gasLimit":"0x1c9c380","gasUsed":"0x18f759","hash":"0xa16c6bcda4fdca88b5761965c4d724f7afc6a6900d9051a204e544870adb3452","logsBloom":"0x020010404000001a0000021000000080001100410000100001000010040200980220400000008806200200000100000000000000000000008000000400042000000050000040000112080808800002044000040004042008800480002000000000000002020020000042002400000820000080040000000010200010020010100101212050000008000000008000001010200c80000112010000438040020400000000202400000000002002a0210402000622010000000001700144000040000000002204000000c000410105024010000808000000002004002000000261000000822200200800881000000012500400400000000000000040010000800000","miner":"0x000095e79eac4d76aab57cb2c1f091d553b36ca0","mixHash":"0x5b53dc49cbab268ef9950b1d81b5e36a1b2f1b97aee1b7ff6e4db0e06c29a8b0","nonce":"0x0000000000000000","number":"0x84161e","parentHash":"0x72d92c1498e05952988d4e79a695928a6bcbd37239f8a1734051263b4d3504b8","receiptsRoot":"0xaff90ae18dcc35924a4bddb68d403b8b7812c10c3ea2a114f34105c87d75bcdb","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x2a51","stateRoot":"0xc56738518b2c7854a640ae25996d2211c9ef0dd2e4dd9e59e9d9cacef39622da","timestamp":"0x64110a5c","totalDifficulty":"0xa4a470","transactions":["0x1e8f148a9aea7d8d16ea6e9446723b8f262e8bcd89c7c961d52046ebd43b4598","0xab5c870f4c367012bd763172afbfbe68fbf35336a66ae41aff3f2c9dbf4ea3f8","0xa81fd92b2d0f0bbd3cc355f869cca3243c98c5e2641db9ecf3eeabb3b13bff6a","0xa92c7b720c08c83f1a0ed7e4c163200e30a3a8c03fcc5a51e685ea20cd0cb577","0x6921b429ad2ec1e97d3457049ad2e893b5a0349beba47ca1c74a9540af75347a","0xf776b2da0b835dde05d0d8b76fd19385d61e7055036cf637f804b36dc94f2384","0x9a08d899cd14ebb930ed59fa774afdb88a22615b3a931e930931ea54d26dc0bc","0x0fe0d97e25d5eb11a33a3e8278584c3780941fc2675bdf8fc547cee3d1fd3b17","0xef47a60f57f177a683c723c658137efab66d311e1c5abbc4d74f653535144d03","0xe23a5b35faae5335adc5aca38c5d633b00438b798c2053104b8df48406c9b141","0xd8cea4ba619b317bc05d58534af73beec6c2548b31b24d4dc61c9bbd29cfa17a","0x79a4b9d90b02c768baaad305f266281213cc75062cbe99a13222cc0c4b509498","0x6790a3bbddbeb21fcb736a59b3775755051c3a6344d8390cf8ca27f2e8a814f0","0x87ec7ace5442db252b5751ffddd38dcb04b088d36b6b0e526ff25607a4293c81","0x40cb487ecffda94f97ce7fc0f7163f2f024235df2c8291169edc80dac063e6d0","0xb76bb3d88c9b30d927c45ccfcf8d5b0054411ac8501ad588822a7d04690cccf6","0x798ebe823209869347c08bd81e04fbf60e9bdfe44b1cc923215182d0cf3d4edb","0xbe68a7e02725f799a65ebb069ccc83a014ac7c40e4119bf7c220a2f6ddfee295","0xc90c3a72efe81331727fcce4b5bd4906066da314ca9a0b44023a6b09ea7e8114","0x619a6cbd43cde074d314c19623bd66d9fb1e13c158d7138775236f798dc1245e","0xca5a56cd77b9e5b0e79020cc6346edf205bc11e901984d805125f28c2e6686e6","0x999c9ddeed67c6ef6fbf02a6e977a6c1b68e18d24814e51643c7157b87a43e0a","0x47c8f5d0b3778e4c34eba7fcc356fa04a5afd954ccf484728e72c002764dd3c4","0x396797ae0ebcdb72ff1f96fd08b6128f78acc7417353f142f1a5facd425a33e6","0x454aa43d6546a6f62246826c16b7a49c6c704238c18802ef0d659922f23a573c","0x317ecb5bd19caa42a69f836d41556ebb0e0e00e1c6cd2dee230e6e6192612527","0xc879285db5ef0a6bce98021584d16f134c1dc0aed8cc988802c4f72ba6877ff6","0xecaa2d6f597608307e5084854854ba6dc1e69395e2abea14f2c6a2fa1d6faf9a","0x4dd69b69a568ff30ae439e2ded72fbd7f2e7aaa345836703663f155c749c5eed"],"transactionsRoot":"0x4a87d0cf5990b1c5bac631583e5965c2ba943858bebb2e07f74d0b697f73821a","uncles":[],"withdrawals":[{"index":"0x1170","validatorIndex":"0x38c2c","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x66edfd65"},{"index":"0x1171","validatorIndex":"0x38c2d","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6cd228e4"},{"index":"0x1172","validatorIndex":"0x38c2e","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x77f3431b"},{"index":"0x1173","validatorIndex":"0x38c2f","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6b61f268"},{"index":"0x1174","validatorIndex":"0x38c30","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6e10bb21"},{"index":"0x1175","validatorIndex":"0x38c31","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6eb115a5"},{"index":"0x1176","validatorIndex":"0x38c32","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x7caead1d"},{"index":"0x1177","validatorIndex":"0x38c33","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x772c0ddf"},{"index":"0x1178","validatorIndex":"0x38c34","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x75930a95"},{"index":"0x1179","validatorIndex":"0x38c35","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x76a4db09"},{"index":"0x117a","validatorIndex":"0x38c36","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x7e692b27"},{"index":"0x117b","validatorIndex":"0x38c37","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x72038ae6"},{"index":"0x117c","validatorIndex":"0x38c38","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6ccce352"},{"index":"0x117d","validatorIndex":"0x38c39","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x79ef6898"},{"index":"0x117e","validatorIndex":"0x38c3a","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6d58977d"},{"index":"0x117f","validatorIndex":"0x38c3b","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x76f7d208"}],"withdrawalsRoot":"0xbe712c930a0665264b025ced87cc7839eef95a3cbc26dadc93e9e185a350ad28"} +`}, + {Name: "Shanghai bad withdrawals root", OK: false, Data: `{"baseFeePerGas":"0x3fb7c357","difficulty":"0x0","extraData":"0x","gasLimit":"0x1c9c380","gasUsed":"0x18f759","hash":"0xa16c6bcda4fdca88b5761965c4d724f7afc6a6900d9051a204e544870adb3452","logsBloom":"0x020010404000001a0000021000000080001100410000100001000010040200980220400000008806200200000100000000000000000000008000000400042000000050000040000112080808800002044000040004042008800480002000000000000002020020000042002400000820000080040000000010200010020010100101212050000008000000008000001010200c80000112010000438040020400000000202400000000002002a0210402000622010000000001700144000040000000002204000000c000410105024010000808000000002004002000000261000000822200200800881000000012500400400000000000000040010000800000","miner":"0x000095e79eac4d76aab57cb2c1f091d553b36ca0","mixHash":"0x5b53dc49cbab268ef9950b1d81b5e36a1b2f1b97aee1b7ff6e4db0e06c29a8b0","nonce":"0x0000000000000000","number":"0x84161e","parentHash":"0x72d92c1498e05952988d4e79a695928a6bcbd37239f8a1734051263b4d3504b8","receiptsRoot":"0xaff90ae18dcc35924a4bddb68d403b8b7812c10c3ea2a114f34105c87d75bcdb","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x2a51","stateRoot":"0xc56738518b2c7854a640ae25996d2211c9ef0dd2e4dd9e59e9d9cacef39622da","timestamp":"0x64110a5c","totalDifficulty":"0xa4a470","transactions":["0x1e8f148a9aea7d8d16ea6e9446723b8f262e8bcd89c7c961d52046ebd43b4598","0xab5c870f4c367012bd763172afbfbe68fbf35336a66ae41aff3f2c9dbf4ea3f8","0xa81fd92b2d0f0bbd3cc355f869cca3243c98c5e2641db9ecf3eeabb3b13bff6a","0xa92c7b720c08c83f1a0ed7e4c163200e30a3a8c03fcc5a51e685ea20cd0cb577","0x6921b429ad2ec1e97d3457049ad2e893b5a0349beba47ca1c74a9540af75347a","0xf776b2da0b835dde05d0d8b76fd19385d61e7055036cf637f804b36dc94f2384","0x9a08d899cd14ebb930ed59fa774afdb88a22615b3a931e930931ea54d26dc0bc","0x0fe0d97e25d5eb11a33a3e8278584c3780941fc2675bdf8fc547cee3d1fd3b17","0xef47a60f57f177a683c723c658137efab66d311e1c5abbc4d74f653535144d03","0xe23a5b35faae5335adc5aca38c5d633b00438b798c2053104b8df48406c9b141","0xd8cea4ba619b317bc05d58534af73beec6c2548b31b24d4dc61c9bbd29cfa17a","0x79a4b9d90b02c768baaad305f266281213cc75062cbe99a13222cc0c4b509498","0x6790a3bbddbeb21fcb736a59b3775755051c3a6344d8390cf8ca27f2e8a814f0","0x87ec7ace5442db252b5751ffddd38dcb04b088d36b6b0e526ff25607a4293c81","0x40cb487ecffda94f97ce7fc0f7163f2f024235df2c8291169edc80dac063e6d0","0xb76bb3d88c9b30d927c45ccfcf8d5b0054411ac8501ad588822a7d04690cccf6","0x798ebe823209869347c08bd81e04fbf60e9bdfe44b1cc923215182d0cf3d4edb","0xbe68a7e02725f799a65ebb069ccc83a014ac7c40e4119bf7c220a2f6ddfee295","0xc90c3a72efe81331727fcce4b5bd4906066da314ca9a0b44023a6b09ea7e8114","0x619a6cbd43cde074d314c19623bd66d9fb1e13c158d7138775236f798dc1245e","0xca5a56cd77b9e5b0e79020cc6346edf205bc11e901984d805125f28c2e6686e6","0x999c9ddeed67c6ef6fbf02a6e977a6c1b68e18d24814e51643c7157b87a43e0a","0x47c8f5d0b3778e4c34eba7fcc356fa04a5afd954ccf484728e72c002764dd3c4","0x396797ae0ebcdb72ff1f96fd08b6128f78acc7417353f142f1a5facd425a33e6","0x454aa43d6546a6f62246826c16b7a49c6c704238c18802ef0d659922f23a573c","0x317ecb5bd19caa42a69f836d41556ebb0e0e00e1c6cd2dee230e6e6192612527","0xc879285db5ef0a6bce98021584d16f134c1dc0aed8cc988802c4f72ba6877ff6","0xecaa2d6f597608307e5084854854ba6dc1e69395e2abea14f2c6a2fa1d6faf9a","0x4dd69b69a568ff30ae439e2ded72fbd7f2e7aaa345836703663f155c749c5eed"],"transactionsRoot":"0x4a87d0cf5990b1c5bac631583e5965c2ba943858bebb2e07f74d0b697f73821a","uncles":[],"withdrawals":[{"index":"0x1170","validatorIndex":"0x38c2c","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x66edfd65"},{"index":"0x1171","validatorIndex":"0x38c2d","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6cd228e4"},{"index":"0x1172","validatorIndex":"0x38c2e","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x77f3431b"},{"index":"0x1173","validatorIndex":"0x38c2f","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6b61f268"},{"index":"0x1174","validatorIndex":"0x38c30","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6e10bb21"},{"index":"0x1175","validatorIndex":"0x38c31","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6eb115a5"},{"index":"0x1176","validatorIndex":"0x38c32","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x7caead1d"},{"index":"0x1177","validatorIndex":"0x38c33","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x772c0ddf"},{"index":"0x1178","validatorIndex":"0x38c34","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x75930a95"},{"index":"0x1179","validatorIndex":"0x38c35","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x76a4db09"},{"index":"0x117a","validatorIndex":"0x38c36","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x7e692b27"},{"index":"0x117b","validatorIndex":"0x38c37","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x72038ae6"},{"index":"0x117c","validatorIndex":"0x38c38","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6ccce352"},{"index":"0x117d","validatorIndex":"0x38c39","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x79ef6898"},{"index":"0x117e","validatorIndex":"0x38c3a","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x6d58977d"},{"index":"0x117f","validatorIndex":"0x38c3b","address":"0x8f0844fd51e31ff6bf5babe21dccf7328e19fd9f","amount":"0x76f7d208"}],"withdrawalsRoot":"0xbe712c930a0665264b025ced87cc7839eef95a3cbc26dadc93e9e185a350ad27"} +`}, + } + for _, testCase := range testCases { + var x rpcHeader + require.NoError(t, json.Unmarshal([]byte(testCase.Data), &x)) + h := x.computeBlockHash() + if testCase.OK { + require.Equal(t, h, x.Hash, "blockhash should verify ok") + } else { + require.NotEqual(t, h, x.Hash, "expecting verification error") + } + } +} diff --git a/op-proposer/Dockerfile b/op-proposer/Dockerfile index 444f0862992bc..368aad1e78cd9 100644 --- a/op-proposer/Dockerfile +++ b/op-proposer/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.18.0-alpine3.15 as builder +FROM --platform=$BUILDPLATFORM golang:1.19.0-alpine3.15 as builder ARG VERSION=v0.0.0 diff --git a/op-proposer/proposer/l2_output_submitter.go b/op-proposer/proposer/l2_output_submitter.go index 6a6000f447490..4bda756ac0193 100644 --- a/op-proposer/proposer/l2_output_submitter.go +++ b/op-proposer/proposer/l2_output_submitter.go @@ -210,6 +210,13 @@ func NewL2OutputSubmitter(cfg Config, l log.Logger) (*L2OutputSubmitter, error) return nil, err } + version, err := l2ooContract.Version(&bind.CallOpts{}) + if err != nil { + cancel() + return nil, err + } + log.Info("Connected to L2OutputOracle", "address", cfg.L2OutputOracleAddr, "version", version) + parsed, err := abi.JSON(strings.NewReader(bindings.L2OutputOracleMetaData.ABI)) if err != nil { cancel() @@ -427,30 +434,31 @@ func (l *L2OutputSubmitter) loop() { for { select { case <-ticker.C: - cCtx, cancel := context.WithTimeout(ctx, 3*time.Minute) + cCtx, cancel := context.WithTimeout(ctx, 30*time.Second) output, shouldPropose, err := l.FetchNextOutputInfo(cCtx) + cancel() if err != nil { - l.log.Error("Failed to fetch next output", "err", err) - cancel() break } if !shouldPropose { - cancel() break } + cCtx, cancel = context.WithTimeout(ctx, 30*time.Second) tx, err := l.CreateProposalTx(cCtx, output) + cancel() if err != nil { l.log.Error("Failed to create proposal transaction", "err", err) - cancel() break } + cCtx, cancel = context.WithTimeout(ctx, 10*time.Minute) if err := l.SendTransaction(cCtx, tx); err != nil { l.log.Error("Failed to send proposal transaction", "err", err) cancel() break + } else { + cancel() } - cancel() case <-l.done: return diff --git a/op-service/metrics/event.go b/op-service/metrics/event.go new file mode 100644 index 0000000000000..8589bf654d6ab --- /dev/null +++ b/op-service/metrics/event.go @@ -0,0 +1,58 @@ +package metrics + +import ( + "fmt" + + "github.com/prometheus/client_golang/prometheus" +) + +type Event struct { + Total prometheus.Counter + LastTime prometheus.Gauge +} + +func (e *Event) Record() { + e.Total.Inc() + e.LastTime.SetToCurrentTime() +} + +func NewEvent(factory Factory, ns string, name string, displayName string) Event { + return Event{ + Total: factory.NewCounter(prometheus.CounterOpts{ + Namespace: ns, + Name: fmt.Sprintf("%s_total", name), + Help: fmt.Sprintf("Count of %s events", displayName), + }), + LastTime: factory.NewGauge(prometheus.GaugeOpts{ + Namespace: ns, + Name: fmt.Sprintf("last_%s_unix", name), + Help: fmt.Sprintf("Timestamp of last %s event", displayName), + }), + } +} + +type EventVec struct { + Total prometheus.CounterVec + LastTime prometheus.GaugeVec +} + +func (e *EventVec) Record(lvs ...string) { + e.Total.WithLabelValues(lvs...).Inc() + e.LastTime.WithLabelValues(lvs...).SetToCurrentTime() +} + +func NewEventVec(factory Factory, ns string, name string, displayName string, labelNames []string) EventVec { + return EventVec{ + Total: *factory.NewCounterVec(prometheus.CounterOpts{ + Namespace: ns, + Name: fmt.Sprintf("%s_total", name), + Help: fmt.Sprintf("Count of %s events", displayName), + }, labelNames), + LastTime: *factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: fmt.Sprintf("last_%s_unix", name), + Help: fmt.Sprintf("Timestamp of last %s event", displayName), + }, + labelNames), + } +} diff --git a/op-service/metrics/ref_metrics.go b/op-service/metrics/ref_metrics.go new file mode 100644 index 0000000000000..159e788b2b611 --- /dev/null +++ b/op-service/metrics/ref_metrics.go @@ -0,0 +1,118 @@ +package metrics + +import ( + "encoding/binary" + "time" + + "github.com/ethereum-optimism/optimism/op-node/eth" + + "github.com/ethereum/go-ethereum/common" + "github.com/prometheus/client_golang/prometheus" +) + +type RefMetricer interface { + RecordRef(layer string, name string, num uint64, timestamp uint64, h common.Hash) + RecordL1Ref(name string, ref eth.L1BlockRef) + RecordL2Ref(name string, ref eth.L2BlockRef) +} + +// RefMetrics provides block reference metrics. It's a metrics module that's +// supposed to be embedded into a service metrics type. The service metrics type +// should set the full namespace and create the factory before calling +// NewRefMetrics. +type RefMetrics struct { + RefsNumber *prometheus.GaugeVec + RefsTime *prometheus.GaugeVec + RefsHash *prometheus.GaugeVec + RefsSeqNr *prometheus.GaugeVec + RefsLatency *prometheus.GaugeVec + // hash of the last seen block per name, so we don't reduce/increase latency on updates of the same data, + // and only count the first occurrence + LatencySeen map[string]common.Hash +} + +var _ RefMetricer = (*RefMetrics)(nil) + +// MakeRefMetrics returns a new RefMetrics, initializing its prometheus fields +// using factory. It is supposed to be used inside the construtors of metrics +// structs for any op service after the full namespace and factory have been +// setup. +// +// ns is the fully qualified namespace, e.g. "op_node_default". +func MakeRefMetrics(ns string, factory Factory) RefMetrics { + return RefMetrics{ + RefsNumber: factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "refs_number", + Help: "Gauge representing the different L1/L2 reference block numbers", + }, []string{ + "layer", + "type", + }), + RefsTime: factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "refs_time", + Help: "Gauge representing the different L1/L2 reference block timestamps", + }, []string{ + "layer", + "type", + }), + RefsHash: factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "refs_hash", + Help: "Gauge representing the different L1/L2 reference block hashes truncated to float values", + }, []string{ + "layer", + "type", + }), + RefsSeqNr: factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "refs_seqnr", + Help: "Gauge representing the different L2 reference sequence numbers", + }, []string{ + "type", + }), + RefsLatency: factory.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: ns, + Name: "refs_latency", + Help: "Gauge representing the different L1/L2 reference block timestamps minus current time, in seconds", + }, []string{ + "layer", + "type", + }), + LatencySeen: make(map[string]common.Hash), + } +} + +func (m *RefMetrics) RecordRef(layer string, name string, num uint64, timestamp uint64, h common.Hash) { + m.RefsNumber.WithLabelValues(layer, name).Set(float64(num)) + if timestamp != 0 { + m.RefsTime.WithLabelValues(layer, name).Set(float64(timestamp)) + // only meter the latency when we first see this hash for the given label name + if m.LatencySeen[name] != h { + m.LatencySeen[name] = h + m.RefsLatency.WithLabelValues(layer, name).Set(float64(timestamp) - (float64(time.Now().UnixNano()) / 1e9)) + } + } + // we map the first 8 bytes to a float64, so we can graph changes of the hash to find divergences visually. + // We don't do math.Float64frombits, just a regular conversion, to keep the value within a manageable range. + m.RefsHash.WithLabelValues(layer, name).Set(float64(binary.LittleEndian.Uint64(h[:]))) +} + +func (m *RefMetrics) RecordL1Ref(name string, ref eth.L1BlockRef) { + m.RecordRef("l1", name, ref.Number, ref.Time, ref.Hash) +} + +func (m *RefMetrics) RecordL2Ref(name string, ref eth.L2BlockRef) { + m.RecordRef("l2", name, ref.Number, ref.Time, ref.Hash) + m.RecordRef("l1_origin", name, ref.L1Origin.Number, 0, ref.L1Origin.Hash) + m.RefsSeqNr.WithLabelValues(name).Set(float64(ref.SequenceNumber)) +} + +// NoopRefMetrics can be embedded in a noop version of a metric implementation +// to have a noop RefMetricer. +type NoopRefMetrics struct{} + +func (*NoopRefMetrics) RecordRef(string, string, uint64, uint64, common.Hash) {} +func (*NoopRefMetrics) RecordL1Ref(string, eth.L1BlockRef) {} +func (*NoopRefMetrics) RecordL2Ref(string, eth.L2BlockRef) {} diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index 754b69aa3a5df..a09a5d9d1af46 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -154,11 +154,22 @@ func (m *SimpleTxManager) IncreaseGasPrice(ctx context.Context, tx *types.Transa thresholdFeeCap := new(big.Int).Mul(priceBumpPercent, tx.GasFeeCap()) thresholdFeeCap = thresholdFeeCap.Div(thresholdFeeCap, oneHundred) if tx.GasFeeCapIntCmp(gasFeeCap) >= 0 { - m.l.Debug("Reusing the previous fee cap", "previous", tx.GasFeeCap(), "suggested", gasFeeCap) - gasFeeCap = tx.GasFeeCap() - reusedFeeCap = true + if reusedTip { + m.l.Debug("Reusing the previous fee cap", "previous", tx.GasFeeCap(), "suggested", gasFeeCap) + gasFeeCap = tx.GasFeeCap() + reusedFeeCap = true + } else { + m.l.Debug("Overriding the fee cap to enforce a price bump because we increased the tip", "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap) + gasFeeCap = thresholdFeeCap + } } else if thresholdFeeCap.Cmp(gasFeeCap) > 0 { - m.l.Debug("Overriding the fee cap to enforce a price bump", "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap) + if reusedTip { + // TODO (CLI-3620): Increase the basefee then recompute the feecap + m.l.Warn("Overriding the fee cap to enforce a price bump without increasing the tip. Will likely result in ErrReplacementUnderpriced", + "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap) + } else { + m.l.Debug("Overriding the fee cap to enforce a price bump", "previous", tx.GasFeeCap(), "suggested", gasFeeCap, "new", thresholdFeeCap) + } gasFeeCap = thresholdFeeCap } diff --git a/op-service/txmgr/txmgr_test.go b/op-service/txmgr/txmgr_test.go index 4d60e245573e1..d107a13e89239 100644 --- a/op-service/txmgr/txmgr_test.go +++ b/op-service/txmgr/txmgr_test.go @@ -621,6 +621,84 @@ func TestIncreaseGasPriceEnforcesMinBump(t *testing.T) { baseFee: big.NewInt(460), } + mgr := &SimpleTxManager{ + Config: Config{ + ResubmissionTimeout: time.Second, + ReceiptQueryInterval: 50 * time.Millisecond, + NumConfirmations: 1, + SafeAbortNonceTooLowCount: 3, + Signer: func(ctx context.Context, from common.Address, tx *types.Transaction) (*types.Transaction, error) { + return tx, nil + }, + From: common.Address{}, + }, + name: "TEST", + backend: &borkedBackend, + l: testlog.Logger(t, log.LvlTrace), + } + + tx := types.NewTx(&types.DynamicFeeTx{ + GasTipCap: big.NewInt(100), + GasFeeCap: big.NewInt(1000), + }) + + ctx := context.Background() + newTx, err := mgr.IncreaseGasPrice(ctx, tx) + require.NoError(t, err) + require.True(t, newTx.GasFeeCap().Cmp(tx.GasFeeCap()) > 0, "new tx fee cap must be larger") + require.True(t, newTx.GasTipCap().Cmp(tx.GasTipCap()) > 0, "new tx tip must be larger") +} + +// TestIncreaseGasPriceEnforcesMinBumpForBothOnTipIncrease asserts that if the gasTip goes up, +// but the baseFee doesn't, both values are increased by 10% +func TestIncreaseGasPriceEnforcesMinBumpForBothOnTipIncrease(t *testing.T) { + t.Parallel() + + borkedBackend := failingBackend{ + gasTip: big.NewInt(101), + baseFee: big.NewInt(440), + } + + mgr := &SimpleTxManager{ + Config: Config{ + ResubmissionTimeout: time.Second, + ReceiptQueryInterval: 50 * time.Millisecond, + NumConfirmations: 1, + SafeAbortNonceTooLowCount: 3, + Signer: func(ctx context.Context, from common.Address, tx *types.Transaction) (*types.Transaction, error) { + return tx, nil + }, + From: common.Address{}, + }, + name: "TEST", + backend: &borkedBackend, + l: testlog.Logger(t, log.LvlCrit), + } + + tx := types.NewTx(&types.DynamicFeeTx{ + GasTipCap: big.NewInt(100), + GasFeeCap: big.NewInt(1000), + }) + + ctx := context.Background() + newTx, err := mgr.IncreaseGasPrice(ctx, tx) + require.NoError(t, err) + require.True(t, newTx.GasFeeCap().Cmp(tx.GasFeeCap()) > 0, "new tx fee cap must be larger") + require.True(t, newTx.GasTipCap().Cmp(tx.GasTipCap()) > 0, "new tx tip must be larger") +} + +// TestIncreaseGasPriceEnforcesMinBumpForBothOnBaseFeeIncrease asserts that if the baseFee goes up, +// but the tip doesn't, both values are increased by 10% +// TODO(CLI-3620): This test will fail until we implemented CLI-3620. +func TestIncreaseGasPriceEnforcesMinBumpForBothOnBaseFeeIncrease(t *testing.T) { + t.Skip("Failing until CLI-3620 is implemented") + t.Parallel() + + borkedBackend := failingBackend{ + gasTip: big.NewInt(99), + baseFee: big.NewInt(460), + } + mgr := &SimpleTxManager{ Config: Config{ ResubmissionTimeout: time.Second, diff --git a/op-wheel/Dockerfile b/op-wheel/Dockerfile index fb2d4f2a6735d..bdf7a19fba2a7 100644 --- a/op-wheel/Dockerfile +++ b/op-wheel/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18.0-alpine3.15 as builder +FROM golang:1.19.0-alpine3.15 as builder RUN apk add --no-cache make gcc musl-dev linux-headers diff --git a/op-wheel/cheat/cheat.go b/op-wheel/cheat/cheat.go index 835bc3ffee4b8..32312515fa08d 100644 --- a/op-wheel/cheat/cheat.go +++ b/op-wheel/cheat/cheat.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" + "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" @@ -80,11 +81,11 @@ type HeadFn func(headState *state.StateDB) error // and updates the blockchain headers indexes to reflect the new state-root, so geth will believe the cheat // (unless it ever re-applies the block). func (ch *Cheater) RunAndClose(fn HeadFn) error { - preBlock := ch.Blockchain.CurrentBlock() - if a, b := preBlock.NumberU64(), ch.Blockchain.Genesis().NumberU64(); a <= b { + preHeader := ch.Blockchain.CurrentBlock() + if a, b := preHeader.Number.Uint64(), ch.Blockchain.Genesis().NumberU64(); a <= b { return fmt.Errorf("cheating at genesis (head block %d <= genesis block %d) is not supported", a, b) } - state, err := ch.Blockchain.StateAt(preBlock.Root()) + state, err := ch.Blockchain.StateAt(preHeader.Root) if err != nil { _ = ch.Close() return fmt.Errorf("failed to look up head state: %w", err) @@ -103,7 +104,7 @@ func (ch *Cheater) RunAndClose(fn HeadFn) error { _ = ch.Close() return fmt.Errorf("failed to commit state change: %w", err) } - header := preBlock.Header() + header := preHeader // copy the header header.Root = stateRoot blockHash := header.Hash() @@ -115,14 +116,15 @@ func (ch *Cheater) RunAndClose(fn HeadFn) error { // based on core.BlockChain.writeHeadBlock: // Add the block to the canonical chain number scheme and mark as the head batch := ch.DB.NewBatch() - if ch.Blockchain.CurrentFinalizedBlock().Hash() == preBlock.Hash() { + preID := eth.BlockID{Hash: preHeader.Hash(), Number: preHeader.Number.Uint64()} + if ch.Blockchain.CurrentFinalBlock().Hash() == preID.Hash { rawdb.WriteFinalizedBlockHash(batch, blockHash) } - rawdb.DeleteHeaderNumber(batch, preBlock.Hash()) + rawdb.DeleteHeaderNumber(batch, preHeader.Hash()) rawdb.WriteHeadHeaderHash(batch, blockHash) rawdb.WriteHeadFastBlockHash(batch, blockHash) - rawdb.WriteCanonicalHash(batch, blockHash, preBlock.NumberU64()) - rawdb.WriteHeaderNumber(batch, blockHash, preBlock.NumberU64()) + rawdb.WriteCanonicalHash(batch, blockHash, preID.Number) + rawdb.WriteHeaderNumber(batch, blockHash, preID.Number) rawdb.WriteHeader(batch, header) // not keyed by blockhash, and we didn't remove any txs, so we just leave this one as-is. // rawdb.WriteTxLookupEntriesByBlock(batch, block) @@ -131,17 +133,17 @@ func (ch *Cheater) RunAndClose(fn HeadFn) error { // Geth stores the TD for each block separately from the block itself. We must update this // manually, otherwise Geth thinks we haven't reached TTD yet and tries to build a block // using Clique consensus, which causes a panic. - rawdb.WriteTd(batch, blockHash, preBlock.NumberU64(), ch.Blockchain.GetTd(preBlock.Hash(), preBlock.NumberU64())) + rawdb.WriteTd(batch, blockHash, preID.Number, ch.Blockchain.GetTd(preID.Hash, preID.Number)) // Need to copy over receipts since they are keyed by block hash. - receipts := rawdb.ReadReceipts(ch.DB, preBlock.Hash(), preBlock.NumberU64(), ch.Blockchain.Config()) - rawdb.WriteReceipts(batch, blockHash, preBlock.NumberU64(), receipts) + receipts := rawdb.ReadReceipts(ch.DB, preID.Hash, preID.Number, ch.Blockchain.Config()) + rawdb.WriteReceipts(batch, blockHash, preID.Number, receipts) // Geth maintains an internal mapping between block bodies and their hashes. None of the database // accessors above update this mapping, so we need to do it manually. - oldKey := blockBodyKey(preBlock.NumberU64(), preBlock.Hash()) - oldBody := rawdb.ReadBodyRLP(ch.DB, preBlock.Hash(), preBlock.NumberU64()) - newKey := blockBodyKey(preBlock.NumberU64(), blockHash) + oldKey := blockBodyKey(preID.Number, preID.Hash) + oldBody := rawdb.ReadBodyRLP(ch.DB, preID.Hash, preID.Number) + newKey := blockBodyKey(preID.Number, blockHash) if err := batch.Delete(oldKey); err != nil { return fmt.Errorf("error deleting old block body key") } diff --git a/packages/actor-tests/CHANGELOG.md b/packages/actor-tests/CHANGELOG.md index a3d0864f33fe6..d6cbbae6e86e0 100644 --- a/packages/actor-tests/CHANGELOG.md +++ b/packages/actor-tests/CHANGELOG.md @@ -1,5 +1,15 @@ # @eth-optimism/actor-tests +## 0.0.23 + +### Patch Changes + +- Updated dependencies [22c3885f5] +- Updated dependencies [66cafc00a] +- Updated dependencies [f52c07529] + - @eth-optimism/contracts-bedrock@0.13.1 + - @eth-optimism/sdk@2.0.1 + ## 0.0.22 ### Patch Changes diff --git a/packages/actor-tests/package.json b/packages/actor-tests/package.json index 161275f478c59..464c64edae0c7 100644 --- a/packages/actor-tests/package.json +++ b/packages/actor-tests/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/actor-tests", - "version": "0.0.22", + "version": "0.0.23", "description": "A library and suite of tests to stress test Optimism Bedrock.", "license": "MIT", "author": "", @@ -18,9 +18,9 @@ "test:coverage": "yarn test" }, "dependencies": { - "@eth-optimism/contracts-bedrock": "0.13.0", + "@eth-optimism/contracts-bedrock": "0.13.1", "@eth-optimism/core-utils": "^0.12.0", - "@eth-optimism/sdk": "^2.0.0", + "@eth-optimism/sdk": "^2.0.1", "@types/chai": "^4.2.18", "@types/chai-as-promised": "^7.1.4", "async-mutex": "^0.3.2", diff --git a/packages/atst/CHANGELOG.md b/packages/atst/CHANGELOG.md index 0840ee923d816..d0035d90bdcd8 100644 --- a/packages/atst/CHANGELOG.md +++ b/packages/atst/CHANGELOG.md @@ -1,5 +1,20 @@ # @eth-optimism/atst +## 0.2.0 + +### Minor Changes + +- dcd13eec1: Update readAttestations and prepareWriteAttestation to handle keys longer than 32 bytes +- 9fd5be8e2: Remove broken allowFailures as option +- 3f4a43542: Move react api to @eth-optimism/atst/react so react isn't required to run the core sdk +- 71727eae9: Fix main and module in atst package.json +- 3d5f26c49: Deprecate parseAttestationBytes and createRawKey in favor for createKey, createValue + +### Patch Changes + +- 68bbe48b6: Update docs +- 6fea2f2db: Fixed bug with atst not defaulting to currently connected chain + ## 0.1.0 ### Minor Changes diff --git a/packages/atst/package.json b/packages/atst/package.json index ecdb3fcb2d57c..5311437cba4d9 100644 --- a/packages/atst/package.json +++ b/packages/atst/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/atst", - "version": "0.1.0", + "version": "0.2.0", "type": "module", "main": "dist/index.cjs", "types": "src/index.ts", diff --git a/packages/chain-mon/CHANGELOG.md b/packages/chain-mon/CHANGELOG.md index e28bdb8eeacba..d9efe625eeb9a 100644 --- a/packages/chain-mon/CHANGELOG.md +++ b/packages/chain-mon/CHANGELOG.md @@ -1,5 +1,14 @@ # @eth-optimism/drippie-mon +## 0.2.1 + +### Patch Changes + +- Updated dependencies [fecd42d67] +- Updated dependencies [66cafc00a] + - @eth-optimism/common-ts@0.8.1 + - @eth-optimism/sdk@2.0.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/chain-mon/package.json b/packages/chain-mon/package.json index 37349ecc98b16..784706dff5594 100644 --- a/packages/chain-mon/package.json +++ b/packages/chain-mon/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@eth-optimism/chain-mon", - "version": "0.2.0", + "version": "0.2.1", "description": "[Optimism] Chain monitoring services", "main": "dist/index", "types": "dist/index", @@ -32,10 +32,10 @@ "url": "https://github.com/ethereum-optimism/optimism.git" }, "dependencies": { - "@eth-optimism/common-ts": "0.8.0", + "@eth-optimism/common-ts": "0.8.1", "@eth-optimism/contracts-periphery": "1.0.7", "@eth-optimism/core-utils": "0.12.0", - "@eth-optimism/sdk": "2.0.0", + "@eth-optimism/sdk": "2.0.1", "ethers": "^5.7.0", "@types/dateformat": "^5.0.0", "chai-as-promised": "^7.1.1", diff --git a/packages/two-step-monitor/.depcheckrc b/packages/common-ts/.depcheckrc similarity index 87% rename from packages/two-step-monitor/.depcheckrc rename to packages/common-ts/.depcheckrc index 3a691b778839b..b1352d5781d95 100644 --- a/packages/two-step-monitor/.depcheckrc +++ b/packages/common-ts/.depcheckrc @@ -8,6 +8,5 @@ ignores: [ "eslint-plugin-react", "@typescript-eslint/eslint-plugin", "eslint-config-prettier", - "eslint-plugin-prettier", - "chai" + "eslint-plugin-prettier" ] diff --git a/packages/common-ts/CHANGELOG.md b/packages/common-ts/CHANGELOG.md index 155b0e8e1a377..e5fb8b60a9157 100644 --- a/packages/common-ts/CHANGELOG.md +++ b/packages/common-ts/CHANGELOG.md @@ -1,5 +1,11 @@ # @eth-optimism/common-ts +## 0.8.1 + +### Patch Changes + +- fecd42d67: Fix BaseServiceV2 configuration for caseCase options + ## 0.8.0 ### Minor Changes diff --git a/packages/common-ts/package.json b/packages/common-ts/package.json index 79a35485032e5..362de6fa163e4 100644 --- a/packages/common-ts/package.json +++ b/packages/common-ts/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/common-ts", - "version": "0.8.0", + "version": "0.8.1", "description": "[Optimism] Advanced typescript tooling used by various services", "main": "dist/index", "types": "dist/index", @@ -17,7 +17,7 @@ "lint": "yarn lint:fix && yarn lint:check", "pre-commit": "lint-staged", "test": "ts-mocha test/*.spec.ts", - "test:coverage": "echo 'no coverage'" + "test:coverage": "nyc ts-mocha test/*.spec.ts && nyc merge .nyc_output coverage.json" }, "keywords": [ "optimism", @@ -48,8 +48,7 @@ "pino": "^6.11.3", "pino-multi-stream": "^5.3.0", "pino-sentry": "^0.7.0", - "prom-client": "^13.1.0", - "qs": "^6.10.5" + "prom-client": "^13.1.0" }, "devDependencies": { "@ethersproject/abstract-provider": "^5.7.0", diff --git a/packages/common-ts/test/metrics.spec.ts b/packages/common-ts/test/metrics.spec.ts index f0a46cdda292f..eb837d1da50e7 100644 --- a/packages/common-ts/test/metrics.spec.ts +++ b/packages/common-ts/test/metrics.spec.ts @@ -3,11 +3,11 @@ import request from 'supertest' import chai = require('chai') const expect = chai.expect -import { Logger, Metrics, createMetricsServer } from '../src' +import { Logger, LegacyMetrics, createMetricsServer } from '../src' describe('Metrics', () => { it('shoud serve metrics', async () => { - const metrics = new Metrics({ + const metrics = new LegacyMetrics({ prefix: 'test_metrics', }) const registry = metrics.registry diff --git a/packages/contracts-bedrock/.env.example b/packages/contracts-bedrock/.env.example index b2d1115479e77..b38594caa3209 100644 --- a/packages/contracts-bedrock/.env.example +++ b/packages/contracts-bedrock/.env.example @@ -7,3 +7,7 @@ PRIVATE_KEY_DEPLOYER= # Optional Tenderly details for a simulation link during deployment TENDERLY_PROJECT= TENDERLY_USERNAME= + +# Optional boolean to define if cast commands should be printed. +# Useful during migration testing +CAST_COMMANDS=1 diff --git a/packages/contracts-bedrock/.gas-snapshot b/packages/contracts-bedrock/.gas-snapshot index 64d88c91a669d..89013ebddebf8 100644 --- a/packages/contracts-bedrock/.gas-snapshot +++ b/packages/contracts-bedrock/.gas-snapshot @@ -141,6 +141,7 @@ L2ERC721Bridge_Test:test_bridgeERC721_succeeds() (gas: 144643) L2ERC721Bridge_Test:test_bridgeERC721_wrongOwner_reverts() (gas: 29258) L2ERC721Bridge_Test:test_constructor_succeeds() (gas: 10110) L2ERC721Bridge_Test:test_finalizeBridgeERC721_alreadyExists_reverts() (gas: 29128) +L2ERC721Bridge_Test:test_finalizeBridgeERC721_interfaceNotCompliant_reverts() (gas: 236012) L2ERC721Bridge_Test:test_finalizeBridgeERC721_notFromRemoteMessenger_reverts() (gas: 19874) L2ERC721Bridge_Test:test_finalizeBridgeERC721_notViaLocalMessenger_reverts() (gas: 16104) L2ERC721Bridge_Test:test_finalizeBridgeERC721_selfToken_reverts() (gas: 17659) @@ -266,9 +267,9 @@ OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutp OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() (gas: 207520) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() (gas: 41753) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() (gas: 199464) -OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() (gas: 206360) +OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() (gas: 205818) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() (gas: 180229) -OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReentrancy_reverts() (gas: 244377) +OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReentrancy_reverts() (gas: 243835) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReplay_reverts() (gas: 245528) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_paused_reverts() (gas: 53555) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_provenWithdrawalHash_succeeds() (gas: 234941) diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index 8130c8c12ed53..683f2f4b5a9b9 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -12,3 +12,4 @@ deployments/mainnet-forked deploy-config/mainnet-forked.json test-case-generator/fuzz .resource-metering.csv +scripts/differential-testing/differential-testing diff --git a/packages/contracts-bedrock/CHANGELOG.md b/packages/contracts-bedrock/CHANGELOG.md index c2a0248bb0d48..5a8aa9fad7ed0 100644 --- a/packages/contracts-bedrock/CHANGELOG.md +++ b/packages/contracts-bedrock/CHANGELOG.md @@ -1,5 +1,12 @@ # @eth-optimism/contracts-bedrock +## 0.13.1 + +### Patch Changes + +- 22c3885f5: Optionally print cast commands during migration +- f52c07529: Print tenderly simulation links during deployment + ## 0.13.0 ### Minor Changes diff --git a/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol b/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol index e195b5826f709..fe9521e0035fd 100644 --- a/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol +++ b/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol @@ -49,7 +49,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { L2OutputOracle public immutable L2_ORACLE; /** - * @notice Address that has the ability to pause and unpause deposits and withdrawals. + * @notice Address that has the ability to pause and unpause withdrawals. */ address public immutable GUARDIAN; diff --git a/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol b/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol index 1888175b5139c..f73632c886798 100644 --- a/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol +++ b/packages/contracts-bedrock/contracts/deployment/SystemDictator.sol @@ -155,6 +155,33 @@ contract SystemDictator is OwnableUpgradeable { currentStep++; } + /** + * @notice Constructor required to ensure that the implementation of the SystemDictator is + * initialized upon deployment. + */ + constructor() { + // Using this shorter variable as an alias for address(0) just prevents us from having to + // to use a new line for every single parameter. + address zero = address(0); + initialize( + DeployConfig( + GlobalConfig(AddressManager(zero), ProxyAdmin(zero), zero, zero), + ProxyAddressConfig(zero, zero, zero, zero, zero, zero, zero), + ImplementationAddressConfig( + L2OutputOracle(zero), + OptimismPortal(payable(zero)), + L1CrossDomainMessenger(zero), + L1StandardBridge(payable(zero)), + OptimismMintableERC20Factory(zero), + L1ERC721Bridge(zero), + PortalSender(zero), + SystemConfig(zero) + ), + SystemConfigConfig(zero, 0, 0, bytes32(0), 0, zero) + ) + ); + } + /** * @param _config System configuration. */ diff --git a/packages/contracts-bedrock/contracts/test/CommonTest.t.sol b/packages/contracts-bedrock/contracts/test/CommonTest.t.sol index 9cfa23241df8b..9fe1d55fc268b 100644 --- a/packages/contracts-bedrock/contracts/test/CommonTest.t.sol +++ b/packages/contracts-bedrock/contracts/test/CommonTest.t.sol @@ -477,16 +477,15 @@ contract FFIInterface is Test { bytes[] memory ) { - string[] memory cmds = new string[](9); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "getProveWithdrawalTransactionInputs"; - cmds[3] = vm.toString(_tx.nonce); - cmds[4] = vm.toString(_tx.sender); - cmds[5] = vm.toString(_tx.target); - cmds[6] = vm.toString(_tx.value); - cmds[7] = vm.toString(_tx.gasLimit); - cmds[8] = vm.toString(_tx.data); + string[] memory cmds = new string[](8); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "getProveWithdrawalTransactionInputs"; + cmds[2] = vm.toString(_tx.nonce); + cmds[3] = vm.toString(_tx.sender); + cmds[4] = vm.toString(_tx.target); + cmds[5] = vm.toString(_tx.value); + cmds[6] = vm.toString(_tx.gasLimit); + cmds[7] = vm.toString(_tx.data); bytes memory result = vm.ffi(cmds); ( @@ -508,16 +507,15 @@ contract FFIInterface is Test { uint256 _gasLimit, bytes memory _data ) external returns (bytes32) { - string[] memory cmds = new string[](9); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "hashCrossDomainMessage"; - cmds[3] = vm.toString(_nonce); - cmds[4] = vm.toString(_sender); - cmds[5] = vm.toString(_target); - cmds[6] = vm.toString(_value); - cmds[7] = vm.toString(_gasLimit); - cmds[8] = vm.toString(_data); + string[] memory cmds = new string[](8); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "hashCrossDomainMessage"; + cmds[2] = vm.toString(_nonce); + cmds[3] = vm.toString(_sender); + cmds[4] = vm.toString(_target); + cmds[5] = vm.toString(_value); + cmds[6] = vm.toString(_gasLimit); + cmds[7] = vm.toString(_data); bytes memory result = vm.ffi(cmds); return abi.decode(result, (bytes32)); @@ -531,16 +529,15 @@ contract FFIInterface is Test { uint256 _gasLimit, bytes memory _data ) external returns (bytes32) { - string[] memory cmds = new string[](9); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "hashWithdrawal"; - cmds[3] = vm.toString(_nonce); - cmds[4] = vm.toString(_sender); - cmds[5] = vm.toString(_target); - cmds[6] = vm.toString(_value); - cmds[7] = vm.toString(_gasLimit); - cmds[8] = vm.toString(_data); + string[] memory cmds = new string[](8); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "hashWithdrawal"; + cmds[2] = vm.toString(_nonce); + cmds[3] = vm.toString(_sender); + cmds[4] = vm.toString(_target); + cmds[5] = vm.toString(_value); + cmds[6] = vm.toString(_gasLimit); + cmds[7] = vm.toString(_data); bytes memory result = vm.ffi(cmds); return abi.decode(result, (bytes32)); @@ -552,14 +549,13 @@ contract FFIInterface is Test { bytes32 _messagePasserStorageRoot, bytes32 _latestBlockhash ) external returns (bytes32) { - string[] memory cmds = new string[](7); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "hashOutputRootProof"; - cmds[3] = Strings.toHexString(uint256(_version)); - cmds[4] = Strings.toHexString(uint256(_stateRoot)); - cmds[5] = Strings.toHexString(uint256(_messagePasserStorageRoot)); - cmds[6] = Strings.toHexString(uint256(_latestBlockhash)); + string[] memory cmds = new string[](6); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "hashOutputRootProof"; + cmds[2] = Strings.toHexString(uint256(_version)); + cmds[3] = Strings.toHexString(uint256(_stateRoot)); + cmds[4] = Strings.toHexString(uint256(_messagePasserStorageRoot)); + cmds[5] = Strings.toHexString(uint256(_latestBlockhash)); bytes memory result = vm.ffi(cmds); return abi.decode(result, (bytes32)); @@ -572,20 +568,19 @@ contract FFIInterface is Test { uint256 _value, uint64 _gas, bytes memory _data, - uint256 _logIndex + uint64 _logIndex ) external returns (bytes32) { - string[] memory cmds = new string[](11); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "hashDepositTransaction"; - cmds[3] = "0x0000000000000000000000000000000000000000000000000000000000000000"; - cmds[4] = vm.toString(_logIndex); - cmds[5] = vm.toString(_from); - cmds[6] = vm.toString(_to); - cmds[7] = vm.toString(_mint); - cmds[8] = vm.toString(_value); - cmds[9] = vm.toString(_gas); - cmds[10] = vm.toString(_data); + string[] memory cmds = new string[](10); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "hashDepositTransaction"; + cmds[2] = "0x0000000000000000000000000000000000000000000000000000000000000000"; + cmds[3] = vm.toString(_logIndex); + cmds[4] = vm.toString(_from); + cmds[5] = vm.toString(_to); + cmds[6] = vm.toString(_mint); + cmds[7] = vm.toString(_value); + cmds[8] = vm.toString(_gas); + cmds[9] = vm.toString(_data); bytes memory result = vm.ffi(cmds); return abi.decode(result, (bytes32)); @@ -595,19 +590,18 @@ contract FFIInterface is Test { external returns (bytes memory) { - string[] memory cmds = new string[](12); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "encodeDepositTransaction"; - cmds[3] = vm.toString(txn.from); - cmds[4] = vm.toString(txn.to); - cmds[5] = vm.toString(txn.value); - cmds[6] = vm.toString(txn.mint); - cmds[7] = vm.toString(txn.gasLimit); - cmds[8] = vm.toString(txn.isCreation); - cmds[9] = vm.toString(txn.data); - cmds[10] = vm.toString(txn.l1BlockHash); - cmds[11] = vm.toString(txn.logIndex); + string[] memory cmds = new string[](11); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "encodeDepositTransaction"; + cmds[2] = vm.toString(txn.from); + cmds[3] = vm.toString(txn.to); + cmds[4] = vm.toString(txn.value); + cmds[5] = vm.toString(txn.mint); + cmds[6] = vm.toString(txn.gasLimit); + cmds[7] = vm.toString(txn.isCreation); + cmds[8] = vm.toString(txn.data); + cmds[9] = vm.toString(txn.l1BlockHash); + cmds[10] = vm.toString(txn.logIndex); bytes memory result = vm.ffi(cmds); return abi.decode(result, (bytes)); @@ -621,27 +615,25 @@ contract FFIInterface is Test { uint256 _gasLimit, bytes memory _data ) external returns (bytes memory) { - string[] memory cmds = new string[](9); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "encodeCrossDomainMessage"; - cmds[3] = vm.toString(_nonce); - cmds[4] = vm.toString(_sender); - cmds[5] = vm.toString(_target); - cmds[6] = vm.toString(_value); - cmds[7] = vm.toString(_gasLimit); - cmds[8] = vm.toString(_data); + string[] memory cmds = new string[](8); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "encodeCrossDomainMessage"; + cmds[2] = vm.toString(_nonce); + cmds[3] = vm.toString(_sender); + cmds[4] = vm.toString(_target); + cmds[5] = vm.toString(_value); + cmds[6] = vm.toString(_gasLimit); + cmds[7] = vm.toString(_data); bytes memory result = vm.ffi(cmds); return abi.decode(result, (bytes)); } function decodeVersionedNonce(uint256 nonce) external returns (uint256, uint256) { - string[] memory cmds = new string[](4); - cmds[0] = "node"; - cmds[1] = "dist/scripts/differential-testing.js"; - cmds[2] = "decodeVersionedNonce"; - cmds[3] = vm.toString(nonce); + string[] memory cmds = new string[](3); + cmds[0] = "scripts/differential-testing/differential-testing"; + cmds[1] = "decodeVersionedNonce"; + cmds[2] = vm.toString(nonce); bytes memory result = vm.ffi(cmds); return abi.decode(result, (uint256, uint256)); diff --git a/packages/contracts-bedrock/contracts/test/Encoding.t.sol b/packages/contracts-bedrock/contracts/test/Encoding.t.sol index c30cfd736f502..778f00e366218 100644 --- a/packages/contracts-bedrock/contracts/test/Encoding.t.sol +++ b/packages/contracts-bedrock/contracts/test/Encoding.t.sol @@ -91,7 +91,7 @@ contract Encoding_Test is CommonTest { uint64 _gas, bool isCreate, bytes memory _data, - uint256 _logIndex + uint64 _logIndex ) external { Types.UserDepositTransaction memory t = Types.UserDepositTransaction( _from, diff --git a/packages/contracts-bedrock/contracts/test/Hashing.t.sol b/packages/contracts-bedrock/contracts/test/Hashing.t.sol index b7a7ccf9769a4..42287fd76d185 100644 --- a/packages/contracts-bedrock/contracts/test/Hashing.t.sol +++ b/packages/contracts-bedrock/contracts/test/Hashing.t.sol @@ -129,7 +129,7 @@ contract Hashing_hashDepositTransaction_Test is CommonTest { uint256 _value, uint64 _gas, bytes memory _data, - uint256 _logIndex + uint64 _logIndex ) external { assertEq( Hashing.hashDepositTransaction( diff --git a/packages/contracts-bedrock/contracts/test/L2ERC721Bridge.t.sol b/packages/contracts-bedrock/contracts/test/L2ERC721Bridge.t.sol index 49102b17e05cf..fabc6f58eac16 100644 --- a/packages/contracts-bedrock/contracts/test/L2ERC721Bridge.t.sol +++ b/packages/contracts-bedrock/contracts/test/L2ERC721Bridge.t.sol @@ -278,6 +278,33 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { assertEq(localToken.ownerOf(tokenId), alice); } + function test_finalizeBridgeERC721_interfaceNotCompliant_reverts() external { + // Create a non-compliant token + NonCompliantERC721 nonCompliantToken = new NonCompliantERC721(alice); + + // Bridge the non-compliant token. + vm.prank(alice); + bridge.bridgeERC721(address(nonCompliantToken), address(0x01), tokenId, 1234, hex"5678"); + + // Attempt to finalize the withdrawal. Should revert because the token does not claim + // to be compliant with the `IOptimismMintableERC721` interface. + vm.mockCall( + address(L2Messenger), + abi.encodeWithSelector(L2Messenger.xDomainMessageSender.selector), + abi.encode(otherBridge) + ); + vm.prank(address(L2Messenger)); + vm.expectRevert("L2ERC721Bridge: local token interface is not compliant"); + bridge.finalizeBridgeERC721( + address(address(nonCompliantToken)), + address(address(0x01)), + alice, + alice, + tokenId, + hex"5678" + ); + } + function test_finalizeBridgeERC721_notViaLocalMessenger_reverts() external { // Finalize a withdrawal. vm.prank(alice); @@ -349,3 +376,33 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ); } } + +/** + * @dev A non-compliant ERC721 token that does not implement the full ERC721 interface. + * + * This is used to test that the bridge will revert if the token does not claim to support + * the ERC721 interface. + */ +contract NonCompliantERC721 { + address internal immutable owner; + + constructor(address _owner) { + owner = _owner; + } + + function ownerOf(uint256) external view returns (address) { + return owner; + } + + function remoteToken() external pure returns (address) { + return address(0x01); + } + + function burn(address, uint256) external { + // Do nothing. + } + + function supportsInterface(bytes4) external pure returns (bool) { + return false; + } +} diff --git a/packages/contracts-bedrock/deploy-config/devnetL1.json b/packages/contracts-bedrock/deploy-config/devnetL1.json index 4ed542993ecb3..41aaf071c02b4 100644 --- a/packages/contracts-bedrock/deploy-config/devnetL1.json +++ b/packages/contracts-bedrock/deploy-config/devnetL1.json @@ -20,6 +20,7 @@ "sequencerFeeVaultRecipient": "0xfabb0ac9d68b0b445fb7357272ff202c5651694a", "proxyAdminOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "finalSystemOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", + "portalGuardian": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "controller": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "finalizationPeriodSeconds": 2, "deploymentWaitConfirmations": 1, diff --git a/packages/contracts-bedrock/deploy-config/final-migration-rehearsal.json b/packages/contracts-bedrock/deploy-config/final-migration-rehearsal.json index 3adf3d5c5dde6..60d3eaa2adcc8 100644 --- a/packages/contracts-bedrock/deploy-config/final-migration-rehearsal.json +++ b/packages/contracts-bedrock/deploy-config/final-migration-rehearsal.json @@ -1,5 +1,6 @@ { "finalSystemOwner": "0x62790eFcB3a5f3A5D398F95B47930A9Addd83807", + "portalGuardian": "0x62790eFcB3a5f3A5D398F95B47930A9Addd83807", "controller": "0x2d30335B0b807bBa1682C487BaAFD2Ad6da5D675", "l1StartingBlockTag": "0x4104895a540d87127ff11eef0d51d8f63ce00a6fc211db751a45a4b3a61a9c83", diff --git a/packages/contracts-bedrock/deploy-config/getting-started.json b/packages/contracts-bedrock/deploy-config/getting-started.json index ffe909d380469..5bf01a9d132a3 100644 --- a/packages/contracts-bedrock/deploy-config/getting-started.json +++ b/packages/contracts-bedrock/deploy-config/getting-started.json @@ -2,6 +2,7 @@ "numDeployConfirmations": 1, "finalSystemOwner": "ADMIN", + "portalGuardian": "ADMIN", "controller": "ADMIN", "l1StartingBlockTag": "BLOCKHASH", diff --git a/packages/contracts-bedrock/deploy-config/goerli-forked.json b/packages/contracts-bedrock/deploy-config/goerli-forked.json index b6469198cbb48..717efdc3fa4db 100644 --- a/packages/contracts-bedrock/deploy-config/goerli-forked.json +++ b/packages/contracts-bedrock/deploy-config/goerli-forked.json @@ -1,5 +1,6 @@ { "finalSystemOwner": "0x62790eFcB3a5f3A5D398F95B47930A9Addd83807", + "portalGuardian": "0x62790eFcB3a5f3A5D398F95B47930A9Addd83807", "controller": "0x2d30335B0b807bBa1682C487BaAFD2Ad6da5D675", "l1StartingBlockTag": "0x4104895a540d87127ff11eef0d51d8f63ce00a6fc211db751a45a4b3a61a9c83", diff --git a/packages/contracts-bedrock/deploy-config/goerli.json b/packages/contracts-bedrock/deploy-config/goerli.json index 8457fabe47d81..6967827a2601e 100644 --- a/packages/contracts-bedrock/deploy-config/goerli.json +++ b/packages/contracts-bedrock/deploy-config/goerli.json @@ -2,6 +2,7 @@ "numDeployConfirmations": 1, "finalSystemOwner": "0xBc1233d0C3e6B5d53Ab455cF65A6623F6dCd7e4f", + "portalGuardian": "0xBc1233d0C3e6B5d53Ab455cF65A6623F6dCd7e4f", "controller": "0xBc1233d0C3e6B5d53Ab455cF65A6623F6dCd7e4f", "l1StartingBlockTag": "0x6ffc1bf3754c01f6bb9fe057c1578b87a8571ce2e9be5ca14bace6eccfd336c7", diff --git a/packages/contracts-bedrock/deploy-config/hardhat.json b/packages/contracts-bedrock/deploy-config/hardhat.json index 7e4aaabaec188..cb40d0bbb13fa 100644 --- a/packages/contracts-bedrock/deploy-config/hardhat.json +++ b/packages/contracts-bedrock/deploy-config/hardhat.json @@ -1,5 +1,6 @@ { "finalSystemOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", + "portalGuardian": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "controller": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266", "l1StartingBlockTag": "earliest", diff --git a/packages/contracts-bedrock/deploy-config/internal-devnet.json b/packages/contracts-bedrock/deploy-config/internal-devnet.json index e4f919d1b3e3c..e352402ca9496 100644 --- a/packages/contracts-bedrock/deploy-config/internal-devnet.json +++ b/packages/contracts-bedrock/deploy-config/internal-devnet.json @@ -1,5 +1,6 @@ { "finalSystemOwner": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", + "portalGuardian": "0x858F0751ef8B4067f0d2668C076BDB50a8549fbF", "controller": "0x2d30335B0b807bBa1682C487BaAFD2Ad6da5D675", "l1StartingBlockTag": "0x19c7e6b18fe156e45f4cfef707294fd8f079fa9c30a7b7cd6ec1ce3682ec6a2e", diff --git a/packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts b/packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts index c148694b92d73..870c6d5538e3f 100644 --- a/packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts +++ b/packages/contracts-bedrock/deploy/013-OptimismPortalImpl.ts @@ -17,13 +17,11 @@ const deployFn: DeployFunction = async (hre) => { 'L2OutputOracleProxy' ) - const finalSystemOwner = hre.deployConfig.finalSystemOwner - const finalSystemOwnerCode = await hre.ethers.provider.getCode( - finalSystemOwner - ) - if (finalSystemOwnerCode === '0x') { + const portalGuardian = hre.deployConfig.portalGuardian + const portalGuardianCode = await hre.ethers.provider.getCode(portalGuardian) + if (portalGuardianCode === '0x') { console.log( - `WARNING: setting OptimismPortal.GUARDIAN to ${finalSystemOwner} and it has no code` + `WARNING: setting OptimismPortal.GUARDIAN to ${portalGuardian} and it has no code` ) if (!isLiveDeployer) { throw new Error( @@ -35,13 +33,13 @@ const deployFn: DeployFunction = async (hre) => { // Deploy the OptimismPortal implementation as paused to // ensure that users do not interact with it and instead // interact with the proxied contract. - // The `finalSystemOwner` is set at the GUARDIAN. + // The `portalGuardian` is set at the GUARDIAN. await deploy({ hre, name: 'OptimismPortal', args: [ L2OutputOracleProxy.address, - finalSystemOwner, + portalGuardian, true, // paused ], postDeployAction: async (contract) => { @@ -53,7 +51,7 @@ const deployFn: DeployFunction = async (hre) => { await assertContractVariable( contract, 'GUARDIAN', - hre.deployConfig.finalSystemOwner + hre.deployConfig.portalGuardian ) }, }) diff --git a/packages/contracts-bedrock/deploy/020-SystemDictatorSteps-1.ts b/packages/contracts-bedrock/deploy/020-SystemDictatorSteps-1.ts index 03ada183c6fc2..80a55d34f973c 100644 --- a/packages/contracts-bedrock/deploy/020-SystemDictatorSteps-1.ts +++ b/packages/contracts-bedrock/deploy/020-SystemDictatorSteps-1.ts @@ -14,6 +14,7 @@ import { doStep, jsonifyTransaction, getTenderlySimulationLink, + getCastCommand, } from '../src/deploy-utils' const deployFn: DeployFunction = async (hre) => { @@ -98,6 +99,7 @@ const deployFn: DeployFunction = async (hre) => { console.log(`MSD address: ${SystemDictator.address}`) console.log(`JSON:`) console.log(jsonifyTransaction(tx)) + console.log(getCastCommand(tx)) console.log(await getTenderlySimulationLink(SystemDictator.provider, tx)) } @@ -107,7 +109,7 @@ const deployFn: DeployFunction = async (hre) => { const owner = await AddressManager.owner() return owner === SystemDictator.address }, - 30000, + 5000, 1000 ) } else { @@ -135,6 +137,7 @@ const deployFn: DeployFunction = async (hre) => { console.log(`MSD address: ${SystemDictator.address}`) console.log(`JSON:`) console.log(jsonifyTransaction(tx)) + console.log(getCastCommand(tx)) console.log(await getTenderlySimulationLink(SystemDictator.provider, tx)) } @@ -146,7 +149,7 @@ const deployFn: DeployFunction = async (hre) => { }) return owner === SystemDictator.address }, - 30000, + 5000, 1000 ) } else { @@ -172,6 +175,7 @@ const deployFn: DeployFunction = async (hre) => { console.log(`MSD address: ${SystemDictator.address}`) console.log(`JSON:`) console.log(jsonifyTransaction(tx)) + console.log(getCastCommand(tx)) console.log(await getTenderlySimulationLink(SystemDictator.provider, tx)) } @@ -183,7 +187,7 @@ const deployFn: DeployFunction = async (hre) => { }) return owner === SystemDictator.address }, - 30000, + 5000, 1000 ) } else { diff --git a/packages/contracts-bedrock/deploy/021-SystemDictatorSteps-2.ts b/packages/contracts-bedrock/deploy/021-SystemDictatorSteps-2.ts index e4ba4db24ede1..906bd42537e82 100644 --- a/packages/contracts-bedrock/deploy/021-SystemDictatorSteps-2.ts +++ b/packages/contracts-bedrock/deploy/021-SystemDictatorSteps-2.ts @@ -14,6 +14,7 @@ import { isStep, doStep, getTenderlySimulationLink, + getCastCommand, } from '../src/deploy-utils' const deployFn: DeployFunction = async (hre) => { @@ -194,6 +195,7 @@ const deployFn: DeployFunction = async (hre) => { console.log(`MSD address: ${SystemDictator.address}`) console.log(`JSON:`) console.log(jsonifyTransaction(tx)) + console.log(getCastCommand(tx)) console.log(await getTenderlySimulationLink(SystemDictator.provider, tx)) } @@ -201,7 +203,7 @@ const deployFn: DeployFunction = async (hre) => { async () => { return SystemDictator.dynamicConfigSet() }, - 30000, + 5000, 1000 ) } @@ -305,6 +307,7 @@ const deployFn: DeployFunction = async (hre) => { console.log(`OptimismPortal address: ${OptimismPortal.address}`) console.log(`JSON:`) console.log(jsonifyTransaction(tx)) + console.log(getCastCommand(tx)) console.log(await getTenderlySimulationLink(SystemDictator.provider, tx)) } @@ -313,7 +316,7 @@ const deployFn: DeployFunction = async (hre) => { const paused = await OptimismPortal.paused() return !paused }, - 30000, + 5000, 1000 ) @@ -334,6 +337,7 @@ const deployFn: DeployFunction = async (hre) => { console.log(`MSD address: ${SystemDictator.address}`) console.log(`JSON:`) console.log(jsonifyTransaction(tx)) + console.log(getCastCommand(tx)) console.log(await getTenderlySimulationLink(SystemDictator.provider, tx)) } @@ -341,7 +345,7 @@ const deployFn: DeployFunction = async (hre) => { async () => { return SystemDictator.finalized() }, - 30000, + 5000, 1000 ) diff --git a/packages/contracts-bedrock/deployments/goerli/L1CrossDomainMessengerProxy.json b/packages/contracts-bedrock/deployments/goerli/L1CrossDomainMessengerProxy.json new file mode 100644 index 0000000000000..1ea3f212cbde3 --- /dev/null +++ b/packages/contracts-bedrock/deployments/goerli/L1CrossDomainMessengerProxy.json @@ -0,0 +1,119 @@ +{ + "address": "0x5086d1eEF304eb5284A0f6720f79403b4e9bE294", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_libAddressManager", + "type": "address" + }, + { + "internalType": "string", + "name": "_implementationName", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "stateMutability": "payable", + "type": "fallback" + } + ], + "transactionHash": "0xc547cd677c4bcb87deead498c827b1dfcfd5d14826f58a0f7416a46024a03e85", + "receipt": { + "to": null, + "from": "0x3a605B442055DF2898E18cF518feb2e2A6BD0D31", + "contractAddress": "0x5086d1eEF304eb5284A0f6720f79403b4e9bE294", + "transactionIndex": 13, + "gasUsed": "291461", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x3e18927a7be75d3ac2b6f54f8c40d781756e78327f9cedd8dff5e79dd49403ff", + "transactionHash": "0xc547cd677c4bcb87deead498c827b1dfcfd5d14826f58a0f7416a46024a03e85", + "logs": [], + "blockNumber": 7017129, + "cumulativeGasUsed": "1033610", + "status": 1, + "byzantium": true + }, + "args": [ + "0xa6f73589243a6A7a9023b1Fa0651b1d89c177111", + "OVM_L1CrossDomainMessenger" + ], + "numDeployments": 1, + "solcInputHash": "76c096070f4b72a86045eb6ab63709ed", + "metadata": "{\"compiler\":{\"version\":\"0.8.9+commit.e5eed63a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_libAddressManager\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_implementationName\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"_implementationName\":\"implementationName of the contract to proxy to.\",\"_libAddressManager\":\"Address of the Lib_AddressManager.\"}}},\"title\":\"Lib_ResolvedDelegateProxy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/libraries/resolver/Lib_ResolvedDelegateProxy.sol\":\"Lib_ResolvedDelegateProxy\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/access/Ownable.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\nimport \\\"../utils/Context.sol\\\";\\n\\n/**\\n * @dev Contract module which provides a basic access control mechanism, where\\n * there is an account (an owner) that can be granted exclusive access to\\n * specific functions.\\n *\\n * By default, the owner account will be the one that deploys the contract. This\\n * can later be changed with {transferOwnership}.\\n *\\n * This module is used through inheritance. It will make available the modifier\\n * `onlyOwner`, which can be applied to your functions to restrict their use to\\n * the owner.\\n */\\nabstract contract Ownable is Context {\\n address private _owner;\\n\\n event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\\n\\n /**\\n * @dev Initializes the contract setting the deployer as the initial owner.\\n */\\n constructor() {\\n _setOwner(_msgSender());\\n }\\n\\n /**\\n * @dev Returns the address of the current owner.\\n */\\n function owner() public view virtual returns (address) {\\n return _owner;\\n }\\n\\n /**\\n * @dev Throws if called by any account other than the owner.\\n */\\n modifier onlyOwner() {\\n require(owner() == _msgSender(), \\\"Ownable: caller is not the owner\\\");\\n _;\\n }\\n\\n /**\\n * @dev Leaves the contract without owner. It will not be possible to call\\n * `onlyOwner` functions anymore. Can only be called by the current owner.\\n *\\n * NOTE: Renouncing ownership will leave the contract without an owner,\\n * thereby removing any functionality that is only available to the owner.\\n */\\n function renounceOwnership() public virtual onlyOwner {\\n _setOwner(address(0));\\n }\\n\\n /**\\n * @dev Transfers ownership of the contract to a new account (`newOwner`).\\n * Can only be called by the current owner.\\n */\\n function transferOwnership(address newOwner) public virtual onlyOwner {\\n require(newOwner != address(0), \\\"Ownable: new owner is the zero address\\\");\\n _setOwner(newOwner);\\n }\\n\\n function _setOwner(address newOwner) private {\\n address oldOwner = _owner;\\n _owner = newOwner;\\n emit OwnershipTransferred(oldOwner, newOwner);\\n }\\n}\\n\",\"keccak256\":\"0x6bb804a310218875e89d12c053e94a13a4607cdf7cc2052f3e52bd32a0dc50a1\",\"license\":\"MIT\"},\"@openzeppelin/contracts/utils/Context.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\n\\npragma solidity ^0.8.0;\\n\\n/**\\n * @dev Provides information about the current execution context, including the\\n * sender of the transaction and its data. While these are generally available\\n * via msg.sender and msg.data, they should not be accessed in such a direct\\n * manner, since when dealing with meta-transactions the account sending and\\n * paying for execution may not be the actual sender (as far as an application\\n * is concerned).\\n *\\n * This contract is only required for intermediate, library-like contracts.\\n */\\nabstract contract Context {\\n function _msgSender() internal view virtual returns (address) {\\n return msg.sender;\\n }\\n\\n function _msgData() internal view virtual returns (bytes calldata) {\\n return msg.data;\\n }\\n}\\n\",\"keccak256\":\"0x90565a39ae45c80f0468dc96c7b20d0afc3055f344c8203a0c9258239f350b9f\",\"license\":\"MIT\"},\"contracts/libraries/resolver/Lib_AddressManager.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\n/* External Imports */\\nimport { Ownable } from \\\"@openzeppelin/contracts/access/Ownable.sol\\\";\\n\\n/**\\n * @title Lib_AddressManager\\n */\\ncontract Lib_AddressManager is Ownable {\\n /**********\\n * Events *\\n **********/\\n\\n event AddressSet(string indexed _name, address _newAddress, address _oldAddress);\\n\\n /*************\\n * Variables *\\n *************/\\n\\n mapping(bytes32 => address) private addresses;\\n\\n /********************\\n * Public Functions *\\n ********************/\\n\\n /**\\n * Changes the address associated with a particular name.\\n * @param _name String name to associate an address with.\\n * @param _address Address to associate with the name.\\n */\\n function setAddress(string memory _name, address _address) external onlyOwner {\\n bytes32 nameHash = _getNameHash(_name);\\n address oldAddress = addresses[nameHash];\\n addresses[nameHash] = _address;\\n\\n emit AddressSet(_name, _address, oldAddress);\\n }\\n\\n /**\\n * Retrieves the address associated with a given name.\\n * @param _name Name to retrieve an address for.\\n * @return Address associated with the given name.\\n */\\n function getAddress(string memory _name) external view returns (address) {\\n return addresses[_getNameHash(_name)];\\n }\\n\\n /**********************\\n * Internal Functions *\\n **********************/\\n\\n /**\\n * Computes the hash of a name.\\n * @param _name Name to compute a hash for.\\n * @return Hash of the given name.\\n */\\n function _getNameHash(string memory _name) internal pure returns (bytes32) {\\n return keccak256(abi.encodePacked(_name));\\n }\\n}\\n\",\"keccak256\":\"0xcde9b29429d512c549f7c1b8a033f161fa71c18cda08b241748663854196ae14\",\"license\":\"MIT\"},\"contracts/libraries/resolver/Lib_ResolvedDelegateProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\n/* Library Imports */\\nimport { Lib_AddressManager } from \\\"./Lib_AddressManager.sol\\\";\\n\\n/**\\n * @title Lib_ResolvedDelegateProxy\\n */\\ncontract Lib_ResolvedDelegateProxy {\\n /*************\\n * Variables *\\n *************/\\n\\n // Using mappings to store fields to avoid overwriting storage slots in the\\n // implementation contract. For example, instead of storing these fields at\\n // storage slot `0` & `1`, they are stored at `keccak256(key + slot)`.\\n // See: https://solidity.readthedocs.io/en/v0.7.0/internals/layout_in_storage.html\\n // NOTE: Do not use this code in your own contract system.\\n // There is a known flaw in this contract, and we will remove it from the repository\\n // in the near future. Due to the very limited way that we are using it, this flaw is\\n // not an issue in our system.\\n mapping(address => string) private implementationName;\\n mapping(address => Lib_AddressManager) private addressManager;\\n\\n /***************\\n * Constructor *\\n ***************/\\n\\n /**\\n * @param _libAddressManager Address of the Lib_AddressManager.\\n * @param _implementationName implementationName of the contract to proxy to.\\n */\\n constructor(address _libAddressManager, string memory _implementationName) {\\n addressManager[address(this)] = Lib_AddressManager(_libAddressManager);\\n implementationName[address(this)] = _implementationName;\\n }\\n\\n /*********************\\n * Fallback Function *\\n *********************/\\n\\n fallback() external payable {\\n address target = addressManager[address(this)].getAddress(\\n (implementationName[address(this)])\\n );\\n\\n require(target != address(0), \\\"Target address must be initialized.\\\");\\n\\n // slither-disable-next-line controlled-delegatecall\\n (bool success, bytes memory returndata) = target.delegatecall(msg.data);\\n\\n if (success == true) {\\n assembly {\\n return(add(returndata, 0x20), mload(returndata))\\n }\\n } else {\\n assembly {\\n revert(add(returndata, 0x20), mload(returndata))\\n }\\n }\\n }\\n}\\n\",\"keccak256\":\"0x987774d18365ed25f5be61198e8b241728db6f97c6f2496f4a35bf9dbe0bda2b\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b506040516105b53803806105b583398101604081905261002f91610125565b30600090815260016020908152604080832080546001600160a01b0319166001600160a01b038716179055828252909120825161006e92840190610076565b505050610252565b82805461008290610217565b90600052602060002090601f0160209004810192826100a457600085556100ea565b82601f106100bd57805160ff19168380011785556100ea565b828001600101855582156100ea579182015b828111156100ea5782518255916020019190600101906100cf565b506100f69291506100fa565b5090565b5b808211156100f657600081556001016100fb565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561013857600080fd5b82516001600160a01b038116811461014f57600080fd5b602084810151919350906001600160401b038082111561016e57600080fd5b818601915086601f83011261018257600080fd5b8151818111156101945761019461010f565b604051601f8201601f19908116603f011681019083821181831017156101bc576101bc61010f565b8160405282815289868487010111156101d457600080fd5b600093505b828410156101f657848401860151818501870152928501926101d9565b828411156102075760008684830101525b8096505050505050509250929050565b600181811c9082168061022b57607f821691505b6020821081141561024c57634e487b7160e01b600052602260045260246000fd5b50919050565b610354806102616000396000f3fe608060408181523060009081526001602090815282822054908290529181207fbf40fac1000000000000000000000000000000000000000000000000000000009093529173ffffffffffffffffffffffffffffffffffffffff9091169063bf40fac19061006d9060846101f2565b60206040518083038186803b15801561008557600080fd5b505afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906102d1565b905073ffffffffffffffffffffffffffffffffffffffff8116610166576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f5461726765742061646472657373206d75737420626520696e697469616c697a60448201527f65642e0000000000000000000000000000000000000000000000000000000000606482015260840160405180910390fd5b6000808273ffffffffffffffffffffffffffffffffffffffff1660003660405161019192919061030e565b600060405180830381855af49150503d80600081146101cc576040519150601f19603f3d011682016040523d82523d6000602084013e6101d1565b606091505b509092509050600182151514156101ea57805160208201f35b805160208201fd5b600060208083526000845481600182811c91508083168061021457607f831692505b85831081141561024b577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b8786018381526020018180156102685760018114610297576102c2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008616825287820196506102c2565b60008b81526020902060005b868110156102bc578154848201529085019089016102a3565b83019750505b50949998505050505050505050565b6000602082840312156102e357600080fd5b815173ffffffffffffffffffffffffffffffffffffffff8116811461030757600080fd5b9392505050565b818382376000910190815291905056fea2646970667358221220d66a7dad92a7f7528f41181719174e1d244423b8bb730d2884645c76cfa0944064736f6c63430008090033", + "deployedBytecode": "0x608060408181523060009081526001602090815282822054908290529181207fbf40fac1000000000000000000000000000000000000000000000000000000009093529173ffffffffffffffffffffffffffffffffffffffff9091169063bf40fac19061006d9060846101f2565b60206040518083038186803b15801561008557600080fd5b505afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906102d1565b905073ffffffffffffffffffffffffffffffffffffffff8116610166576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f5461726765742061646472657373206d75737420626520696e697469616c697a60448201527f65642e0000000000000000000000000000000000000000000000000000000000606482015260840160405180910390fd5b6000808273ffffffffffffffffffffffffffffffffffffffff1660003660405161019192919061030e565b600060405180830381855af49150503d80600081146101cc576040519150601f19603f3d011682016040523d82523d6000602084013e6101d1565b606091505b509092509050600182151514156101ea57805160208201f35b805160208201fd5b600060208083526000845481600182811c91508083168061021457607f831692505b85831081141561024b577f4e487b710000000000000000000000000000000000000000000000000000000085526022600452602485fd5b8786018381526020018180156102685760018114610297576102c2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008616825287820196506102c2565b60008b81526020902060005b868110156102bc578154848201529085019089016102a3565b83019750505b50949998505050505050505050565b6000602082840312156102e357600080fd5b815173ffffffffffffffffffffffffffffffffffffffff8116811461030757600080fd5b9392505050565b818382376000910190815291905056fea2646970667358221220d66a7dad92a7f7528f41181719174e1d244423b8bb730d2884645c76cfa0944064736f6c63430008090033", + "devdoc": { + "kind": "dev", + "methods": { + "constructor": { + "params": { + "_implementationName": "implementationName of the contract to proxy to.", + "_libAddressManager": "Address of the Lib_AddressManager." + } + } + }, + "title": "Lib_ResolvedDelegateProxy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": {}, + "version": 1 + }, + "storageLayout": { + "storage": [ + { + "astId": 7129, + "contract": "contracts/libraries/resolver/Lib_ResolvedDelegateProxy.sol:Lib_ResolvedDelegateProxy", + "label": "implementationName", + "offset": 0, + "slot": "0", + "type": "t_mapping(t_address,t_string_storage)" + }, + { + "astId": 7134, + "contract": "contracts/libraries/resolver/Lib_ResolvedDelegateProxy.sol:Lib_ResolvedDelegateProxy", + "label": "addressManager", + "offset": 0, + "slot": "1", + "type": "t_mapping(t_address,t_contract(Lib_AddressManager)7084)" + } + ], + "types": { + "t_address": { + "encoding": "inplace", + "label": "address", + "numberOfBytes": "20" + }, + "t_contract(Lib_AddressManager)7084": { + "encoding": "inplace", + "label": "contract Lib_AddressManager", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_contract(Lib_AddressManager)7084)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => contract Lib_AddressManager)", + "numberOfBytes": "32", + "value": "t_contract(Lib_AddressManager)7084" + }, + "t_mapping(t_address,t_string_storage)": { + "encoding": "mapping", + "key": "t_address", + "label": "mapping(address => string)", + "numberOfBytes": "32", + "value": "t_string_storage" + }, + "t_string_storage": { + "encoding": "bytes", + "label": "string", + "numberOfBytes": "32" + } + } + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/deployments/goerli/L1StandardBridgeProxy.json b/packages/contracts-bedrock/deployments/goerli/L1StandardBridgeProxy.json new file mode 100644 index 0000000000000..040726460d21d --- /dev/null +++ b/packages/contracts-bedrock/deployments/goerli/L1StandardBridgeProxy.json @@ -0,0 +1,178 @@ +{ + "address": "0x636Af16bf2f682dD3109e60102b8E1A089FedAa8", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "getImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getOwner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_code", + "type": "bytes" + } + ], + "name": "setCode", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_key", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_value", + "type": "bytes32" + } + ], + "name": "setStorage", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "transactionHash": "0xd57130025124776619229f980ae08c3097156ab127c897fa9ef17e29bf757a16", + "receipt": { + "to": null, + "from": "0x3a605B442055DF2898E18cF518feb2e2A6BD0D31", + "contractAddress": "0x636Af16bf2f682dD3109e60102b8E1A089FedAa8", + "transactionIndex": 8, + "gasUsed": "614417", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "blockHash": "0x85ce123b23be93051e83bc9d6dd2bf7817ee0fd338b936684c821020b8285393", + "transactionHash": "0xd57130025124776619229f980ae08c3097156ab127c897fa9ef17e29bf757a16", + "logs": [], + "blockNumber": 7017137, + "cumulativeGasUsed": "5543459", + "status": 1, + "byzantium": true + }, + "args": [ + "0x3a605B442055DF2898E18cF518feb2e2A6BD0D31" + ], + "numDeployments": 1, + "solcInputHash": "0a41276e1e61949b5de1e4f1cd89fb6c", + "metadata": "{\"compiler\":{\"version\":\"0.8.9+commit.e5eed63a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[],\"name\":\"getImplementation\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_code\",\"type\":\"bytes\"}],\"name\":\"setCode\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"_value\",\"type\":\"bytes32\"}],\"name\":\"setStorage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added functions `setCode` and `setStorage` for changing the code or storage of the contract. Nifty! Note for future developers: do NOT make anything in this contract 'public' unless you know what you're doing. Anything public can potentially have a function signature that conflicts with a signature attached to the implementation contract. Public functions SHOULD always have the 'proxyCallIfNotOwner' modifier unless there's some *really* good reason not to have that modifier. And there almost certainly is not a good reason to not have that modifier. Beware!\",\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"_owner\":\"Address of the initial contract owner.\"}},\"getImplementation()\":{\"returns\":{\"_0\":\"Implementation address.\"}},\"getOwner()\":{\"returns\":{\"_0\":\"Owner address.\"}},\"setCode(bytes)\":{\"params\":{\"_code\":\"New contract code to run inside this contract.\"}},\"setOwner(address)\":{\"params\":{\"_owner\":\"New owner of the proxy contract.\"}},\"setStorage(bytes32,bytes32)\":{\"params\":{\"_key\":\"Storage key to modify.\",\"_value\":\"New value for the storage key.\"}}},\"title\":\"L1ChugSplashProxy\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{\"getImplementation()\":{\"notice\":\"Queries the implementation address. Can only be called by the owner OR by making an eth_call and setting the \\\"from\\\" address to address(0).\"},\"getOwner()\":{\"notice\":\"Queries the owner of the proxy contract. Can only be called by the owner OR by making an eth_call and setting the \\\"from\\\" address to address(0).\"},\"setCode(bytes)\":{\"notice\":\"Sets the code that should be running behind this proxy. Note that this scheme is a bit different from the standard proxy scheme where one would typically deploy the code separately and then set the implementation address. We're doing it this way because it gives us a lot more freedom on the client side. Can only be triggered by the contract owner.\"},\"setOwner(address)\":{\"notice\":\"Changes the owner of the proxy contract. Only callable by the owner.\"},\"setStorage(bytes32,bytes32)\":{\"notice\":\"Modifies some storage slot within the proxy contract. Gives us a lot of power to perform upgrades in a more transparent way. Only callable by the owner.\"}},\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/chugsplash/L1ChugSplashProxy.sol\":\"L1ChugSplashProxy\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\",\"useLiteralContent\":true},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[]},\"sources\":{\"contracts/chugsplash/L1ChugSplashProxy.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\nimport { iL1ChugSplashDeployer } from \\\"./interfaces/iL1ChugSplashDeployer.sol\\\";\\n\\n/**\\n * @title L1ChugSplashProxy\\n * @dev Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added\\n * functions `setCode` and `setStorage` for changing the code or storage of the contract. Nifty!\\n *\\n * Note for future developers: do NOT make anything in this contract 'public' unless you know what\\n * you're doing. Anything public can potentially have a function signature that conflicts with a\\n * signature attached to the implementation contract. Public functions SHOULD always have the\\n * 'proxyCallIfNotOwner' modifier unless there's some *really* good reason not to have that\\n * modifier. And there almost certainly is not a good reason to not have that modifier. Beware!\\n */\\ncontract L1ChugSplashProxy {\\n /*************\\n * Constants *\\n *************/\\n\\n // \\\"Magic\\\" prefix. When prepended to some arbitrary bytecode and used to create a contract, the\\n // appended bytecode will be deployed as given.\\n bytes13 internal constant DEPLOY_CODE_PREFIX = 0x600D380380600D6000396000f3;\\n\\n // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)\\n bytes32 internal constant IMPLEMENTATION_KEY =\\n 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;\\n\\n // bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)\\n bytes32 internal constant OWNER_KEY =\\n 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;\\n\\n /***************\\n * Constructor *\\n ***************/\\n\\n /**\\n * @param _owner Address of the initial contract owner.\\n */\\n constructor(address _owner) {\\n _setOwner(_owner);\\n }\\n\\n /**********************\\n * Function Modifiers *\\n **********************/\\n\\n /**\\n * Blocks a function from being called when the parent signals that the system should be paused\\n * via an isUpgrading function.\\n */\\n modifier onlyWhenNotPaused() {\\n address owner = _getOwner();\\n\\n // We do a low-level call because there's no guarantee that the owner actually *is* an\\n // L1ChugSplashDeployer contract and Solidity will throw errors if we do a normal call and\\n // it turns out that it isn't the right type of contract.\\n (bool success, bytes memory returndata) = owner.staticcall(\\n abi.encodeWithSelector(iL1ChugSplashDeployer.isUpgrading.selector)\\n );\\n\\n // If the call was unsuccessful then we assume that there's no \\\"isUpgrading\\\" method and we\\n // can just continue as normal. We also expect that the return value is exactly 32 bytes\\n // long. If this isn't the case then we can safely ignore the result.\\n if (success && returndata.length == 32) {\\n // Although the expected value is a *boolean*, it's safer to decode as a uint256 in the\\n // case that the isUpgrading function returned something other than 0 or 1. But we only\\n // really care about the case where this value is 0 (= false).\\n uint256 ret = abi.decode(returndata, (uint256));\\n require(ret == 0, \\\"L1ChugSplashProxy: system is currently being upgraded\\\");\\n }\\n\\n _;\\n }\\n\\n /**\\n * Makes a proxy call instead of triggering the given function when the caller is either the\\n * owner or the zero address. Caller can only ever be the zero address if this function is\\n * being called off-chain via eth_call, which is totally fine and can be convenient for\\n * client-side tooling. Avoids situations where the proxy and implementation share a sighash\\n * and the proxy function ends up being called instead of the implementation one.\\n *\\n * Note: msg.sender == address(0) can ONLY be triggered off-chain via eth_call. If there's a\\n * way for someone to send a transaction with msg.sender == address(0) in any real context then\\n * we have much bigger problems. Primary reason to include this additional allowed sender is\\n * because the owner address can be changed dynamically and we do not want clients to have to\\n * keep track of the current owner in order to make an eth_call that doesn't trigger the\\n * proxied contract.\\n */\\n // slither-disable-next-line incorrect-modifier\\n modifier proxyCallIfNotOwner() {\\n if (msg.sender == _getOwner() || msg.sender == address(0)) {\\n _;\\n } else {\\n // This WILL halt the call frame on completion.\\n _doProxyCall();\\n }\\n }\\n\\n /*********************\\n * Fallback Function *\\n *********************/\\n\\n // slither-disable-next-line locked-ether\\n fallback() external payable {\\n // Proxy call by default.\\n _doProxyCall();\\n }\\n\\n /********************\\n * Public Functions *\\n ********************/\\n\\n /**\\n * Sets the code that should be running behind this proxy. Note that this scheme is a bit\\n * different from the standard proxy scheme where one would typically deploy the code\\n * separately and then set the implementation address. We're doing it this way because it gives\\n * us a lot more freedom on the client side. Can only be triggered by the contract owner.\\n * @param _code New contract code to run inside this contract.\\n */\\n // slither-disable-next-line external-function\\n function setCode(bytes memory _code) public proxyCallIfNotOwner {\\n // Get the code hash of the current implementation.\\n address implementation = _getImplementation();\\n\\n // If the code hash matches the new implementation then we return early.\\n if (keccak256(_code) == _getAccountCodeHash(implementation)) {\\n return;\\n }\\n\\n // Create the deploycode by appending the magic prefix.\\n bytes memory deploycode = abi.encodePacked(DEPLOY_CODE_PREFIX, _code);\\n\\n // Deploy the code and set the new implementation address.\\n address newImplementation;\\n assembly {\\n newImplementation := create(0x0, add(deploycode, 0x20), mload(deploycode))\\n }\\n\\n // Check that the code was actually deployed correctly. I'm not sure if you can ever\\n // actually fail this check. Should only happen if the contract creation from above runs\\n // out of gas but this parent execution thread does NOT run out of gas. Seems like we\\n // should be doing this check anyway though.\\n require(\\n _getAccountCodeHash(newImplementation) == keccak256(_code),\\n \\\"L1ChugSplashProxy: code was not correctly deployed.\\\"\\n );\\n\\n _setImplementation(newImplementation);\\n }\\n\\n /**\\n * Modifies some storage slot within the proxy contract. Gives us a lot of power to perform\\n * upgrades in a more transparent way. Only callable by the owner.\\n * @param _key Storage key to modify.\\n * @param _value New value for the storage key.\\n */\\n // slither-disable-next-line external-function\\n function setStorage(bytes32 _key, bytes32 _value) public proxyCallIfNotOwner {\\n assembly {\\n sstore(_key, _value)\\n }\\n }\\n\\n /**\\n * Changes the owner of the proxy contract. Only callable by the owner.\\n * @param _owner New owner of the proxy contract.\\n */\\n // slither-disable-next-line external-function\\n function setOwner(address _owner) public proxyCallIfNotOwner {\\n _setOwner(_owner);\\n }\\n\\n /**\\n * Queries the owner of the proxy contract. Can only be called by the owner OR by making an\\n * eth_call and setting the \\\"from\\\" address to address(0).\\n * @return Owner address.\\n */\\n // slither-disable-next-line external-function\\n function getOwner() public proxyCallIfNotOwner returns (address) {\\n return _getOwner();\\n }\\n\\n /**\\n * Queries the implementation address. Can only be called by the owner OR by making an\\n * eth_call and setting the \\\"from\\\" address to address(0).\\n * @return Implementation address.\\n */\\n // slither-disable-next-line external-function\\n function getImplementation() public proxyCallIfNotOwner returns (address) {\\n return _getImplementation();\\n }\\n\\n /**********************\\n * Internal Functions *\\n **********************/\\n\\n /**\\n * Sets the implementation address.\\n * @param _implementation New implementation address.\\n */\\n function _setImplementation(address _implementation) internal {\\n assembly {\\n sstore(IMPLEMENTATION_KEY, _implementation)\\n }\\n }\\n\\n /**\\n * Queries the implementation address.\\n * @return Implementation address.\\n */\\n function _getImplementation() internal view returns (address) {\\n address implementation;\\n assembly {\\n implementation := sload(IMPLEMENTATION_KEY)\\n }\\n return implementation;\\n }\\n\\n /**\\n * Changes the owner of the proxy contract.\\n * @param _owner New owner of the proxy contract.\\n */\\n function _setOwner(address _owner) internal {\\n assembly {\\n sstore(OWNER_KEY, _owner)\\n }\\n }\\n\\n /**\\n * Queries the owner of the proxy contract.\\n * @return Owner address.\\n */\\n function _getOwner() internal view returns (address) {\\n address owner;\\n assembly {\\n owner := sload(OWNER_KEY)\\n }\\n return owner;\\n }\\n\\n /**\\n * Gets the code hash for a given account.\\n * @param _account Address of the account to get a code hash for.\\n * @return Code hash for the account.\\n */\\n function _getAccountCodeHash(address _account) internal view returns (bytes32) {\\n bytes32 codeHash;\\n assembly {\\n codeHash := extcodehash(_account)\\n }\\n return codeHash;\\n }\\n\\n /**\\n * Performs the proxy call via a delegatecall.\\n */\\n function _doProxyCall() internal onlyWhenNotPaused {\\n address implementation = _getImplementation();\\n\\n require(implementation != address(0), \\\"L1ChugSplashProxy: implementation is not set yet\\\");\\n\\n assembly {\\n // Copy calldata into memory at 0x0....calldatasize.\\n calldatacopy(0x0, 0x0, calldatasize())\\n\\n // Perform the delegatecall, make sure to pass all available gas.\\n let success := delegatecall(gas(), implementation, 0x0, calldatasize(), 0x0, 0x0)\\n\\n // Copy returndata into memory at 0x0....returndatasize. Note that this *will*\\n // overwrite the calldata that we just copied into memory but that doesn't really\\n // matter because we'll be returning in a second anyway.\\n returndatacopy(0x0, 0x0, returndatasize())\\n\\n // Success == 0 means a revert. We'll revert too and pass the data up.\\n if iszero(success) {\\n revert(0x0, returndatasize())\\n }\\n\\n // Otherwise we'll just return and pass the data up.\\n return(0x0, returndatasize())\\n }\\n }\\n}\\n\",\"keccak256\":\"0xc3cb52dfdc2706992572dd5621ae89ba919fd20539b73488a455d564f16f1b8d\",\"license\":\"MIT\"},\"contracts/chugsplash/interfaces/iL1ChugSplashDeployer.sol\":{\"content\":\"// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.9;\\n\\n/**\\n * @title iL1ChugSplashDeployer\\n */\\ninterface iL1ChugSplashDeployer {\\n function isUpgrading() external view returns (bool);\\n}\\n\",\"keccak256\":\"0x9a496d99f111c1091f0c33d6bfc7802a522baa7235614b0014f35e4bbe280e57\",\"license\":\"MIT\"}},\"version\":1}", + "bytecode": "0x608060405234801561001057600080fd5b50604051610a5d380380610a5d83398101604081905261002f9161005d565b610057817fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355565b5061008d565b60006020828403121561006f57600080fd5b81516001600160a01b038116811461008657600080fd5b9392505050565b6109c18061009c6000396000f3fe60806040526004361061005a5760003560e01c8063893d20e811610043578063893d20e8146100a45780639b0b0fda146100e2578063aaf10f42146101025761005a565b806313af4035146100645780636c5d4ad014610084575b610062610117565b005b34801561007057600080fd5b5061006261007f366004610792565b6103ba565b34801561009057600080fd5b5061006261009f3660046107fe565b61044b565b3480156100b057600080fd5b506100b9610601565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100ee57600080fd5b506100626100fd3660046108cd565b610698565b34801561010e57600080fd5b506100b9610706565b60006101417fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb7947262000000000000000000000000000000000000000000000000000000001790529051919250600091829173ffffffffffffffffffffffffffffffffffffffff8516916101c3919061092a565b600060405180830381855afa9150503d80600081146101fe576040519150601f19603f3d011682016040523d82523d6000602084013e610203565b606091505b5091509150818015610216575080516020145b156102c8576000818060200190518101906102319190610936565b905080156102c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4c314368756753706c61736850726f78793a2073797374656d2069732063757260448201527f72656e746c79206265696e67207570677261646564000000000000000000000060648201526084015b60405180910390fd5b505b60006102f27f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b905073ffffffffffffffffffffffffffffffffffffffff8116610397576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4c314368756753706c61736850726f78793a20696d706c656d656e746174696f60448201527f6e206973206e6f7420736574207965740000000000000000000000000000000060648201526084016102bd565b3660008037600080366000845af43d6000803e806103b4573d6000fd5b503d6000f35b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610413575033155b1561044357610440817fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355565b50565b610440610117565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806104a4575033155b156104435760006104d37f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050803f8251602084012014156104e8575050565b60405160009061051e907f600d380380600d6000396000f30000000000000000000000000000000000000090859060200161094f565b604051602081830303815290604052905060008151602083016000f084516020860120909150813f146105d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f4c314368756753706c61736850726f78793a20636f646520776173206e6f742060448201527f636f72726563746c79206465706c6f7965642e0000000000000000000000000060648201526084016102bd565b6105fb817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50505050565b600061062b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610662575033155b1561068d57507fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b610695610117565b90565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806106f1575033155b156106fa579055565b610702610117565b5050565b60006107307fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610767575033155b1561068d57507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b6000602082840312156107a457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146107c857600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561081057600080fd5b813567ffffffffffffffff8082111561082857600080fd5b818401915084601f83011261083c57600080fd5b81358181111561084e5761084e6107cf565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610894576108946107cf565b816040528281528760208487010111156108ad57600080fd5b826020860160208301376000928101602001929092525095945050505050565b600080604083850312156108e057600080fd5b50508035926020909101359150565b6000815160005b8181101561091057602081850181015186830152016108f6565b8181111561091f576000828601525b509290920192915050565b60006107c882846108ef565b60006020828403121561094857600080fd5b5051919050565b7fffffffffffffffffffffffffff00000000000000000000000000000000000000831681526000610983600d8301846108ef565b94935050505056fea2646970667358221220aea34fd8cdcf3a9cced029d5f7b1e628f42ad1514501878e0040df2afddb6e7164736f6c63430008090033", + "deployedBytecode": "0x60806040526004361061005a5760003560e01c8063893d20e811610043578063893d20e8146100a45780639b0b0fda146100e2578063aaf10f42146101025761005a565b806313af4035146100645780636c5d4ad014610084575b610062610117565b005b34801561007057600080fd5b5061006261007f366004610792565b6103ba565b34801561009057600080fd5b5061006261009f3660046107fe565b61044b565b3480156100b057600080fd5b506100b9610601565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100ee57600080fd5b506100626100fd3660046108cd565b610698565b34801561010e57600080fd5b506100b9610706565b60006101417fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fb7947262000000000000000000000000000000000000000000000000000000001790529051919250600091829173ffffffffffffffffffffffffffffffffffffffff8516916101c3919061092a565b600060405180830381855afa9150503d80600081146101fe576040519150601f19603f3d011682016040523d82523d6000602084013e610203565b606091505b5091509150818015610216575080516020145b156102c8576000818060200190518101906102319190610936565b905080156102c6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4c314368756753706c61736850726f78793a2073797374656d2069732063757260448201527f72656e746c79206265696e67207570677261646564000000000000000000000060648201526084015b60405180910390fd5b505b60006102f27f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b905073ffffffffffffffffffffffffffffffffffffffff8116610397576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f4c314368756753706c61736850726f78793a20696d706c656d656e746174696f60448201527f6e206973206e6f7420736574207965740000000000000000000000000000000060648201526084016102bd565b3660008037600080366000845af43d6000803e806103b4573d6000fd5b503d6000f35b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610413575033155b1561044357610440817fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610355565b50565b610440610117565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806104a4575033155b156104435760006104d37f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b9050803f8251602084012014156104e8575050565b60405160009061051e907f600d380380600d6000396000f30000000000000000000000000000000000000090859060200161094f565b604051602081830303815290604052905060008151602083016000f084516020860120909150813f146105d3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f4c314368756753706c61736850726f78793a20636f646520776173206e6f742060448201527f636f72726563746c79206465706c6f7965642e0000000000000000000000000060648201526084016102bd565b6105fb817f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55565b50505050565b600061062b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610662575033155b1561068d57507fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b610695610117565b90565b7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806106f1575033155b156106fa579055565b610702610117565b5050565b60006107307fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035490565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610767575033155b1561068d57507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5490565b6000602082840312156107a457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146107c857600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561081057600080fd5b813567ffffffffffffffff8082111561082857600080fd5b818401915084601f83011261083c57600080fd5b81358181111561084e5761084e6107cf565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610894576108946107cf565b816040528281528760208487010111156108ad57600080fd5b826020860160208301376000928101602001929092525095945050505050565b600080604083850312156108e057600080fd5b50508035926020909101359150565b6000815160005b8181101561091057602081850181015186830152016108f6565b8181111561091f576000828601525b509290920192915050565b60006107c882846108ef565b60006020828403121561094857600080fd5b5051919050565b7fffffffffffffffffffffffffff00000000000000000000000000000000000000831681526000610983600d8301846108ef565b94935050505056fea2646970667358221220aea34fd8cdcf3a9cced029d5f7b1e628f42ad1514501878e0040df2afddb6e7164736f6c63430008090033", + "devdoc": { + "details": "Basic ChugSplash proxy contract for L1. Very close to being a normal proxy but has added functions `setCode` and `setStorage` for changing the code or storage of the contract. Nifty! Note for future developers: do NOT make anything in this contract 'public' unless you know what you're doing. Anything public can potentially have a function signature that conflicts with a signature attached to the implementation contract. Public functions SHOULD always have the 'proxyCallIfNotOwner' modifier unless there's some *really* good reason not to have that modifier. And there almost certainly is not a good reason to not have that modifier. Beware!", + "kind": "dev", + "methods": { + "constructor": { + "params": { + "_owner": "Address of the initial contract owner." + } + }, + "getImplementation()": { + "returns": { + "_0": "Implementation address." + } + }, + "getOwner()": { + "returns": { + "_0": "Owner address." + } + }, + "setCode(bytes)": { + "params": { + "_code": "New contract code to run inside this contract." + } + }, + "setOwner(address)": { + "params": { + "_owner": "New owner of the proxy contract." + } + }, + "setStorage(bytes32,bytes32)": { + "params": { + "_key": "Storage key to modify.", + "_value": "New value for the storage key." + } + } + }, + "title": "L1ChugSplashProxy", + "version": 1 + }, + "userdoc": { + "kind": "user", + "methods": { + "getImplementation()": { + "notice": "Queries the implementation address. Can only be called by the owner OR by making an eth_call and setting the \"from\" address to address(0)." + }, + "getOwner()": { + "notice": "Queries the owner of the proxy contract. Can only be called by the owner OR by making an eth_call and setting the \"from\" address to address(0)." + }, + "setCode(bytes)": { + "notice": "Sets the code that should be running behind this proxy. Note that this scheme is a bit different from the standard proxy scheme where one would typically deploy the code separately and then set the implementation address. We're doing it this way because it gives us a lot more freedom on the client side. Can only be triggered by the contract owner." + }, + "setOwner(address)": { + "notice": "Changes the owner of the proxy contract. Only callable by the owner." + }, + "setStorage(bytes32,bytes32)": { + "notice": "Modifies some storage slot within the proxy contract. Gives us a lot of power to perform upgrades in a more transparent way. Only callable by the owner." + } + }, + "version": 1 + }, + "storageLayout": { + "storage": [], + "types": null + } +} \ No newline at end of file diff --git a/packages/contracts-bedrock/hardhat.config.ts b/packages/contracts-bedrock/hardhat.config.ts index 16ddca9420cd4..c8a09bd7681e6 100644 --- a/packages/contracts-bedrock/hardhat.config.ts +++ b/packages/contracts-bedrock/hardhat.config.ts @@ -23,7 +23,7 @@ const config: HardhatUserConfig = { live: false, }, mainnet: { - url: process.env.RPC_URL || 'http://localhost:8545', + url: process.env.L1_RPC || 'http://localhost:8545', }, devnetL1: { live: false, diff --git a/packages/contracts-bedrock/package.json b/packages/contracts-bedrock/package.json index f58a8fb196aa6..e9db0cad59ca1 100644 --- a/packages/contracts-bedrock/package.json +++ b/packages/contracts-bedrock/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/contracts-bedrock", - "version": "0.13.0", + "version": "0.13.1", "description": "Contracts for Optimism Specs", "main": "dist/index", "types": "dist/index", @@ -17,7 +17,7 @@ "bindings": "cd ../../op-bindings && make", "build:forge": "forge build", "build:with-metadata": "FOUNDRY_PROFILE=echidna yarn build:forge", - "build:differential": "tsc scripts/differential-testing.ts --outDir dist --moduleResolution node --esModuleInterop", + "build:differential": "go build -o ./scripts/differential-testing/differential-testing ./scripts/differential-testing", "build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)", "prebuild": "yarn ts-node scripts/verify-foundry-install.ts", "build": "hardhat compile && yarn autogen:artifacts && yarn build:ts && yarn typechain", @@ -59,8 +59,6 @@ }, "devDependencies": { "@eth-optimism/hardhat-deploy-config": "^0.2.5", - "@ethereumjs/trie": "^5.0.0-beta.1", - "@ethereumjs/util": "^8.0.0-beta.1", "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", "ethereumjs-wallet": "^1.0.2", diff --git a/packages/contracts-bedrock/scripts/differential-testing.ts b/packages/contracts-bedrock/scripts/differential-testing.ts deleted file mode 100644 index 360bec2ba075f..0000000000000 --- a/packages/contracts-bedrock/scripts/differential-testing.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { BigNumber, utils, constants } from 'ethers' -import { - decodeVersionedNonce, - hashCrossDomainMessage, - DepositTx, - SourceHashDomain, - encodeCrossDomainMessage, - hashWithdrawal, - hashOutputRootProof, -} from '@eth-optimism/core-utils' -import { SecureTrie } from '@ethereumjs/trie' -import { Account, Address, toBuffer, bufferToHex } from '@ethereumjs/util' - -import { predeploys } from '../src' - -const { hexZeroPad, keccak256 } = utils - -const args = process.argv.slice(2) -const command = args[0] - -;(async () => { - switch (command) { - case 'decodeVersionedNonce': { - const input = BigNumber.from(args[1]) - const { nonce, version } = decodeVersionedNonce(input) - - const output = utils.defaultAbiCoder.encode( - ['uint256', 'uint256'], - [nonce.toHexString(), version.toHexString()] - ) - process.stdout.write(output) - break - } - case 'encodeCrossDomainMessage': { - const nonce = BigNumber.from(args[1]) - const sender = args[2] - const target = args[3] - const value = BigNumber.from(args[4]) - const gasLimit = BigNumber.from(args[5]) - const data = args[6] - - const encoding = encodeCrossDomainMessage( - nonce, - sender, - target, - value, - gasLimit, - data - ) - - const output = utils.defaultAbiCoder.encode(['bytes'], [encoding]) - process.stdout.write(output) - break - } - case 'hashCrossDomainMessage': { - const nonce = BigNumber.from(args[1]) - const sender = args[2] - const target = args[3] - const value = BigNumber.from(args[4]) - const gasLimit = BigNumber.from(args[5]) - const data = args[6] - - const hash = hashCrossDomainMessage( - nonce, - sender, - target, - value, - gasLimit, - data - ) - const output = utils.defaultAbiCoder.encode(['bytes32'], [hash]) - process.stdout.write(output) - break - } - case 'hashDepositTransaction': { - // The solidity transaction hash computation currently only works with - // user deposits. System deposit transaction hashing is not supported. - const l1BlockHash = args[1] - const logIndex = BigNumber.from(args[2]) - const from = args[3] - const to = args[4] - const mint = BigNumber.from(args[5]) - const value = BigNumber.from(args[6]) - const gas = BigNumber.from(args[7]) - const data = args[8] - - const tx = new DepositTx({ - l1BlockHash, - logIndex, - from, - to, - mint, - value, - gas, - data, - isSystemTransaction: false, - domain: SourceHashDomain.UserDeposit, - }) - - const digest = tx.hash() - const output = utils.defaultAbiCoder.encode(['bytes32'], [digest]) - process.stdout.write(output) - break - } - case 'encodeDepositTransaction': { - const from = args[1] - const to = args[2] - const value = BigNumber.from(args[3]) - const mint = BigNumber.from(args[4]) - const gasLimit = BigNumber.from(args[5]) - const isCreate = args[6] === 'true' ? true : false - const data = args[7] - const l1BlockHash = args[8] - const logIndex = BigNumber.from(args[9]) - - const tx = new DepositTx({ - from, - to: isCreate ? null : to, - value, - mint, - gas: gasLimit, - data, - l1BlockHash, - logIndex, - domain: SourceHashDomain.UserDeposit, - }) - - const raw = tx.encode() - const output = utils.defaultAbiCoder.encode(['bytes'], [raw]) - process.stdout.write(output) - break - } - case 'hashWithdrawal': { - const nonce = BigNumber.from(args[1]) - const sender = args[2] - const target = args[3] - const value = BigNumber.from(args[4]) - const gas = BigNumber.from(args[5]) - const data = args[6] - - const hash = hashWithdrawal(nonce, sender, target, value, gas, data) - const output = utils.defaultAbiCoder.encode(['bytes32'], [hash]) - process.stdout.write(output) - break - } - case 'hashOutputRootProof': { - const version = hexZeroPad(BigNumber.from(args[1]).toHexString(), 32) - const stateRoot = hexZeroPad(BigNumber.from(args[2]).toHexString(), 32) - const messagePasserStorageRoot = hexZeroPad( - BigNumber.from(args[3]).toHexString(), - 32 - ) - const latestBlockhash = hexZeroPad( - BigNumber.from(args[4]).toHexString(), - 32 - ) - - const hash = hashOutputRootProof({ - version, - stateRoot, - messagePasserStorageRoot, - latestBlockhash, - }) - const output = utils.defaultAbiCoder.encode(['bytes32'], [hash]) - process.stdout.write(output) - break - } - case 'getProveWithdrawalTransactionInputs': { - const nonce = BigNumber.from(args[1]) - const sender = args[2] - const target = args[3] - const value = BigNumber.from(args[4]) - const gas = BigNumber.from(args[5]) - const data = args[6] - - // Compute the withdrawalHash - const withdrawalHash = hashWithdrawal( - nonce, - sender, - target, - value, - gas, - data - ) - - // Compute the storage slot the withdrawalHash will be stored in - const slot = utils.defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [withdrawalHash, utils.hexZeroPad('0x', 32)] - ) - const key = keccak256(slot) - - // Create the account storage trie - const storage = new SecureTrie() - // Put a bool "true" into storage - await storage.put(toBuffer(key), toBuffer('0x01')) - - // Put the storage root into the L2ToL1MessagePasser storage - const address = Address.fromString(predeploys.L2ToL1MessagePasser) - const account = Account.fromAccountData({ - nonce: 0, - balance: 0, - stateRoot: storage.root, - }) - - const world = new SecureTrie() - await world.put(address.toBuffer(), account.serialize()) - - const proof = await SecureTrie.createProof(storage, toBuffer(key)) - - const outputRoot = hashOutputRootProof({ - version: constants.HashZero, - stateRoot: bufferToHex(world.root), - messagePasserStorageRoot: bufferToHex(storage.root), - latestBlockhash: constants.HashZero, - }) - - const output = utils.defaultAbiCoder.encode( - ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'bytes[]'], - [world.root, storage.root, outputRoot, withdrawalHash, proof] - ) - process.stdout.write(output) - break - } - } -})().catch((err: Error) => { - console.error(err) - process.stdout.write('') -}) diff --git a/packages/contracts-bedrock/scripts/differential-testing/differential-testing.go b/packages/contracts-bedrock/scripts/differential-testing/differential-testing.go new file mode 100644 index 0000000000000..c40171abadd3f --- /dev/null +++ b/packages/contracts-bedrock/scripts/differential-testing/differential-testing.go @@ -0,0 +1,318 @@ +package main + +import ( + "bytes" + "fmt" + "math/big" + "os" + + "github.com/ethereum-optimism/optimism/op-bindings/predeploys" + "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/trie" +) + +// ABI types +var ( + // Plain dynamic dynBytes type + dynBytes, _ = abi.NewType("bytes", "", nil) + bytesArgs = abi.Arguments{ + {Type: dynBytes}, + } + + // Plain fixed bytes32 type + fixedBytes, _ = abi.NewType("bytes32", "", nil) + fixedBytesArgs = abi.Arguments{ + {Type: fixedBytes}, + } + + // Decoded nonce tuple (nonce, version) + decodedNonce, _ = abi.NewType("tuple", "DecodedNonce", []abi.ArgumentMarshaling{ + {Name: "nonce", Type: "uint256"}, + {Name: "version", Type: "uint256"}, + }) + decodedNonceArgs = abi.Arguments{ + {Name: "encodedNonce", Type: decodedNonce}, + } + + // WithdrawalHash slot tuple (bytes32, bytes32) + withdrawalSlot, _ = abi.NewType("tuple", "SlotHash", []abi.ArgumentMarshaling{ + {Name: "withdrawalHash", Type: "bytes32"}, + {Name: "zeroPadding", Type: "bytes32"}, + }) + withdrawalSlotArgs = abi.Arguments{ + {Name: "slotHash", Type: withdrawalSlot}, + } + + // Prove withdrawal inputs tuple (bytes32, bytes32, bytes32, bytes32, bytes[]) + proveWithdrawalInputs, _ = abi.NewType("tuple", "ProveWithdrawalInputs", []abi.ArgumentMarshaling{ + {Name: "worldRoot", Type: "bytes32"}, + {Name: "stateRoot", Type: "bytes32"}, + {Name: "outputRoot", Type: "bytes32"}, + {Name: "withdrawalHash", Type: "bytes32"}, + {Name: "proof", Type: "bytes[]"}, + }) + proveWithdrawalInputsArgs = abi.Arguments{ + {Name: "inputs", Type: proveWithdrawalInputs}, + } +) + +func main() { + args := os.Args[1:] + + // This command requires arguments + if len(args) == 0 { + panic("Error: No arguments provided") + } + + switch args[0] { + case "decodeVersionedNonce": + // Parse input arguments + input, ok := new(big.Int).SetString(args[1], 10) + checkOk(ok) + + // Decode versioned nonce + nonce, version := crossdomain.DecodeVersionedNonce(input) + + // ABI encode output + packArgs := struct { + Nonce *big.Int + Version *big.Int + }{ + nonce, + version, + } + packed, err := decodedNonceArgs.Pack(&packArgs) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "encodeCrossDomainMessage": + // Parse input arguments + nonce, ok := new(big.Int).SetString(args[1], 10) + checkOk(ok) + sender := common.HexToAddress(args[2]) + target := common.HexToAddress(args[3]) + value, ok := new(big.Int).SetString(args[4], 10) + checkOk(ok) + gasLimit, ok := new(big.Int).SetString(args[5], 10) + checkOk(ok) + data := common.FromHex(args[6]) + + // Encode cross domain message + encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data) + checkErr(err, "Error encoding cross domain message") + + // Pack encoded cross domain message + packed, err := bytesArgs.Pack(&encoded) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "hashCrossDomainMessage": + // Parse input arguments + nonce, ok := new(big.Int).SetString(args[1], 10) + checkOk(ok) + sender := common.HexToAddress(args[2]) + target := common.HexToAddress(args[3]) + value, ok := new(big.Int).SetString(args[4], 10) + checkOk(ok) + gasLimit, ok := new(big.Int).SetString(args[5], 10) + checkOk(ok) + data := common.FromHex(args[6]) + + // Encode cross domain message + encoded, err := encodeCrossDomainMessage(nonce, sender, target, value, gasLimit, data) + checkErr(err, "Error encoding cross domain message") + + // Hash encoded cross domain message + hash := crypto.Keccak256Hash(encoded) + + // Pack hash + packed, err := fixedBytesArgs.Pack(&hash) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "hashDepositTransaction": + // Parse input arguments + l1BlockHash := common.HexToHash(args[1]) + logIndex, ok := new(big.Int).SetString(args[2], 10) + checkOk(ok) + from := common.HexToAddress(args[3]) + to := common.HexToAddress(args[4]) + mint, ok := new(big.Int).SetString(args[5], 10) + checkOk(ok) + value, ok := new(big.Int).SetString(args[6], 10) + checkOk(ok) + gasLimit, ok := new(big.Int).SetString(args[7], 10) + checkOk(ok) + data := common.FromHex(args[8]) + + // Create deposit transaction + depositTx := makeDepositTx(from, to, value, mint, gasLimit, false, data, l1BlockHash, logIndex) + + // RLP encode deposit transaction + encoded, err := types.NewTx(&depositTx).MarshalBinary() + checkErr(err, "Error encoding deposit transaction") + + // Hash encoded deposit transaction + hash := crypto.Keccak256Hash(encoded) + + // Pack hash + packed, err := fixedBytesArgs.Pack(&hash) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "encodeDepositTransaction": + // Parse input arguments + from := common.HexToAddress(args[1]) + to := common.HexToAddress(args[2]) + value, ok := new(big.Int).SetString(args[3], 10) + checkOk(ok) + mint, ok := new(big.Int).SetString(args[4], 10) + checkOk(ok) + gasLimit, ok := new(big.Int).SetString(args[5], 10) + checkOk(ok) + isCreate := args[6] == "true" + data := common.FromHex(args[7]) + l1BlockHash := common.HexToHash(args[8]) + logIndex, ok := new(big.Int).SetString(args[9], 10) + checkOk(ok) + + depositTx := makeDepositTx(from, to, value, mint, gasLimit, isCreate, data, l1BlockHash, logIndex) + + // RLP encode deposit transaction + encoded, err := types.NewTx(&depositTx).MarshalBinary() + checkErr(err, "Failed to RLP encode deposit transaction") + // Pack rlp encoded deposit transaction + packed, err := bytesArgs.Pack(&encoded) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "hashWithdrawal": + // Parse input arguments + nonce, ok := new(big.Int).SetString(args[1], 10) + checkOk(ok) + sender := common.HexToAddress(args[2]) + target := common.HexToAddress(args[3]) + value, ok := new(big.Int).SetString(args[4], 10) + checkOk(ok) + gasLimit, ok := new(big.Int).SetString(args[5], 10) + checkOk(ok) + data := common.FromHex(args[6]) + + // Hash withdrawal + hash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data) + checkErr(err, "Error hashing withdrawal") + + // Pack hash + packed, err := fixedBytesArgs.Pack(&hash) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "hashOutputRootProof": + // Parse input arguments + version := common.HexToHash(args[1]) + stateRoot := common.HexToHash(args[2]) + messagePasserStorageRoot := common.HexToHash(args[3]) + latestBlockHash := common.HexToHash(args[4]) + + // Hash the output root proof + hash, err := hashOutputRootProof(version, stateRoot, messagePasserStorageRoot, latestBlockHash) + checkErr(err, "Error hashing output root proof") + + // Pack hash + packed, err := fixedBytesArgs.Pack(&hash) + checkErr(err, "Error encoding output") + + fmt.Print(hexutil.Encode(packed)) + case "getProveWithdrawalTransactionInputs": + // Parse input arguments + nonce, ok := new(big.Int).SetString(args[1], 10) + checkOk(ok) + sender := common.HexToAddress(args[2]) + target := common.HexToAddress(args[3]) + value, ok := new(big.Int).SetString(args[4], 10) + checkOk(ok) + gasLimit, ok := new(big.Int).SetString(args[5], 10) + checkOk(ok) + data := common.FromHex(args[6]) + + wdHash, err := hashWithdrawal(nonce, sender, target, value, gasLimit, data) + checkErr(err, "Error hashing withdrawal") + + // Compute the storage slot the withdrawalHash will be stored in + slot := struct { + WithdrawalHash common.Hash + ZeroPadding common.Hash + }{ + WithdrawalHash: wdHash, + ZeroPadding: common.Hash{}, + } + packed, err := withdrawalSlotArgs.Pack(&slot) + checkErr(err, "Error packing withdrawal slot") + + // Compute the storage slot the withdrawalHash will be stored in + hash := crypto.Keccak256Hash(packed) + + // Create a secure trie for state + state, err := trie.NewStateTrie( + trie.TrieID(types.EmptyRootHash), + trie.NewDatabase(rawdb.NewMemoryDatabase()), + ) + checkErr(err, "Error creating secure trie") + + // Put a "true" bool in the storage slot + state.Update(hash.Bytes(), []byte{0x01}) + + // Create a secure trie for the world state + world, err := trie.NewStateTrie( + trie.TrieID(types.EmptyRootHash), + trie.NewDatabase(rawdb.NewMemoryDatabase()), + ) + checkErr(err, "Error creating secure trie") + + // Put the put the rlp encoded account in the world trie + account := types.StateAccount{ + Nonce: 0, + Balance: big.NewInt(0), + Root: state.Hash(), + } + writer := new(bytes.Buffer) + checkErr(account.EncodeRLP(writer), "Error encoding account") + world.Update(predeploys.L2ToL1MessagePasserAddr.Bytes(), writer.Bytes()) + + // Get the proof + var proof proofList + checkErr(state.Prove(predeploys.L2ToL1MessagePasserAddr.Bytes(), 0, &proof), "Error getting proof") + + // Get the output root + outputRoot, err := hashOutputRootProof(common.Hash{}, world.Hash(), state.Hash(), common.Hash{}) + checkErr(err, "Error hashing output root proof") + + // Pack the output + output := struct { + WorldRoot common.Hash + StateRoot common.Hash + OutputRoot common.Hash + WithdrawalHash common.Hash + Proof proofList + }{ + WorldRoot: world.Hash(), + StateRoot: state.Hash(), + OutputRoot: outputRoot, + WithdrawalHash: wdHash, + Proof: proof, + } + packed, err = proveWithdrawalInputsArgs.Pack(&output) + checkErr(err, "Error encoding output") + + // Print the output + fmt.Print(hexutil.Encode(packed[32:])) + default: + panic(fmt.Errorf("Unknown command: %s", args[0])) + } +} diff --git a/packages/contracts-bedrock/scripts/differential-testing/utils.go b/packages/contracts-bedrock/scripts/differential-testing/utils.go new file mode 100644 index 0000000000000..f7e866fc5e5b9 --- /dev/null +++ b/packages/contracts-bedrock/scripts/differential-testing/utils.go @@ -0,0 +1,129 @@ +package main + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum-optimism/optimism/op-bindings/bindings" + "github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +var UnknownNonceVersion = errors.New("Unknown nonce version") + +// checkOk checks if ok is false, and panics if so. +// Shorthand to ease go's god awful error handling +func checkOk(ok bool) { + if !ok { + panic(fmt.Errorf("checkOk failed")) + } +} + +// checkErr checks if err is not nil, and throws if so. +// Shorthand to ease go's god awful error handling +func checkErr(err error, failReason string) { + if err != nil { + panic(fmt.Errorf("%s: %w", failReason, err)) + } +} + +// encodeCrossDomainMessage encodes a versioned cross domain message into a byte array. +func encodeCrossDomainMessage(nonce *big.Int, sender common.Address, target common.Address, value *big.Int, gasLimit *big.Int, data []byte) ([]byte, error) { + _, version := crossdomain.DecodeVersionedNonce(nonce) + + var encoded []byte + var err error + if version.Cmp(big.NewInt(0)) == 0 { + // Encode cross domain message V0 + encoded, err = crossdomain.EncodeCrossDomainMessageV0(target, sender, data, nonce) + } else if version.Cmp(big.NewInt(1)) == 0 { + // Encode cross domain message V1 + encoded, err = crossdomain.EncodeCrossDomainMessageV1(nonce, sender, target, value, gasLimit, data) + } else { + return nil, UnknownNonceVersion + } + + return encoded, err +} + +// hashWithdrawal hashes a withdrawal transaction. +func hashWithdrawal(nonce *big.Int, sender common.Address, target common.Address, value *big.Int, gasLimit *big.Int, data []byte) (common.Hash, error) { + wd := crossdomain.Withdrawal{ + Nonce: nonce, + Sender: &sender, + Target: &target, + Value: value, + GasLimit: gasLimit, + Data: data, + } + return wd.Hash() +} + +// hashOutputRootProof hashes an output root proof. +func hashOutputRootProof(version common.Hash, stateRoot common.Hash, messagePasserStorageRoot common.Hash, latestBlockHash common.Hash) (common.Hash, error) { + hash, err := rollup.ComputeL2OutputRoot(&bindings.TypesOutputRootProof{ + Version: version, + StateRoot: stateRoot, + MessagePasserStorageRoot: messagePasserStorageRoot, + LatestBlockhash: latestBlockHash, + }) + if err != nil { + return common.Hash{}, err + } + return common.Hash(hash), nil +} + +// makeDepositTx creates a deposit transaction type. +func makeDepositTx( + from common.Address, + to common.Address, + value *big.Int, + mint *big.Int, + gasLimit *big.Int, + isCreate bool, + data []byte, + l1BlockHash common.Hash, + logIndex *big.Int, +) types.DepositTx { + // Create deposit transaction source + udp := derive.UserDepositSource{ + L1BlockHash: l1BlockHash, + LogIndex: logIndex.Uint64(), + } + + // Create deposit transaction + depositTx := types.DepositTx{ + SourceHash: udp.SourceHash(), + From: from, + Value: value, + Gas: gasLimit.Uint64(), + IsSystemTransaction: false, // This will never be a system transaction in the tests. + Data: data, + } + + // Fill optional fields + if mint.Cmp(big.NewInt(0)) == 1 { + depositTx.Mint = mint + } + if !isCreate { + depositTx.To = &to + } + + return depositTx +} + +// Custom type to write the generated proof to +type proofList [][]byte + +func (n *proofList) Put(key []byte, value []byte) error { + *n = append(*n, value) + return nil +} + +func (n *proofList) Delete(key []byte) error { + panic("not supported") +} diff --git a/packages/contracts-bedrock/src/deploy-config.ts b/packages/contracts-bedrock/src/deploy-config.ts index 3495cafe779aa..2cf2eb73cbac1 100644 --- a/packages/contracts-bedrock/src/deploy-config.ts +++ b/packages/contracts-bedrock/src/deploy-config.ts @@ -14,6 +14,12 @@ interface RequiredDeployConfig { */ finalSystemOwner?: string + /** + * Address that is deployed as the GUARDIAN in the OptimismPortal. Has the + * ability to pause withdrawals. + */ + portalGuardian: string + /** * Address that will own the entire system on L1 during the deployment process. This address will * not own the system after the deployment is complete, ownership will be transferred to the @@ -181,6 +187,9 @@ export const deployConfigSpec: { finalSystemOwner: { type: 'address', }, + portalGuardian: { + type: 'address', + }, controller: { type: 'address', }, diff --git a/packages/contracts-bedrock/src/deploy-utils.ts b/packages/contracts-bedrock/src/deploy-utils.ts index f3736d8fc19bb..375951899f459 100644 --- a/packages/contracts-bedrock/src/deploy-utils.ts +++ b/packages/contracts-bedrock/src/deploy-utils.ts @@ -395,3 +395,15 @@ export const getTenderlySimulationLink = async ( }).toString()}` } } + +/** + * Returns a cast commmand for submitting a given transaction. + * + * @param tx Ethers transaction object. + * @returns the cast command + */ +export const getCastCommand = (tx: ethers.PopulatedTransaction): string => { + if (process.env.CAST_COMMANDS) { + return `cast send ${tx.to} ${tx.data} --from ${tx.from} --value ${tx.value}` + } +} diff --git a/packages/contracts-periphery/package.json b/packages/contracts-periphery/package.json index ec533898021fc..86f4624375ac1 100644 --- a/packages/contracts-periphery/package.json +++ b/packages/contracts-periphery/package.json @@ -53,7 +53,7 @@ "url": "https://github.com/ethereum-optimism/optimism.git" }, "devDependencies": { - "@eth-optimism/contracts-bedrock": "0.13.0", + "@eth-optimism/contracts-bedrock": "0.13.1", "@eth-optimism/core-utils": "^0.12.0", "@eth-optimism/hardhat-deploy-config": "^0.2.5", "@ethersproject/hardware-wallets": "^5.7.0", diff --git a/packages/data-transport-layer/CHANGELOG.md b/packages/data-transport-layer/CHANGELOG.md index a2744a83db2ef..e9dc4094b0911 100644 --- a/packages/data-transport-layer/CHANGELOG.md +++ b/packages/data-transport-layer/CHANGELOG.md @@ -1,5 +1,12 @@ # data transport layer +## 0.5.54 + +### Patch Changes + +- Updated dependencies [fecd42d67] + - @eth-optimism/common-ts@0.8.1 + ## 0.5.53 ### Patch Changes diff --git a/packages/data-transport-layer/package.json b/packages/data-transport-layer/package.json index d1f664470364d..3049eb945395f 100644 --- a/packages/data-transport-layer/package.json +++ b/packages/data-transport-layer/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@eth-optimism/data-transport-layer", - "version": "0.5.53", + "version": "0.5.54", "description": "[Optimism] Service for shuttling data from L1 into L2", "main": "dist/index", "types": "dist/index", @@ -36,7 +36,7 @@ "url": "https://github.com/ethereum-optimism/optimism.git" }, "dependencies": { - "@eth-optimism/common-ts": "0.8.0", + "@eth-optimism/common-ts": "0.8.1", "@eth-optimism/contracts": "0.5.40", "@eth-optimism/core-utils": "0.12.0", "@ethersproject/providers": "^5.7.0", diff --git a/packages/fault-detector/CHANGELOG.md b/packages/fault-detector/CHANGELOG.md index 9cdf476bb3319..a4cfb68b4329d 100644 --- a/packages/fault-detector/CHANGELOG.md +++ b/packages/fault-detector/CHANGELOG.md @@ -1,5 +1,15 @@ # @eth-optimism/fault-detector +## 0.6.2 + +### Patch Changes + +- f9b579d55: Fixes a bug that would cause the fault detector to error out if no outputs had been proposed yet. +- Updated dependencies [fecd42d67] +- Updated dependencies [66cafc00a] + - @eth-optimism/common-ts@0.8.1 + - @eth-optimism/sdk@2.0.1 + ## 0.6.1 ### Patch Changes diff --git a/packages/fault-detector/package.json b/packages/fault-detector/package.json index 3bdb7c6c1f9b2..b51bdcc55ff18 100644 --- a/packages/fault-detector/package.json +++ b/packages/fault-detector/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@eth-optimism/fault-detector", - "version": "0.6.1", + "version": "0.6.2", "description": "[Optimism] Service for detecting faulty L2 output proposals", "main": "dist/index", "types": "dist/index", @@ -47,10 +47,10 @@ "ts-node": "^10.9.1" }, "dependencies": { - "@eth-optimism/common-ts": "^0.8.0", + "@eth-optimism/common-ts": "^0.8.1", "@eth-optimism/contracts": "^0.5.40", "@eth-optimism/core-utils": "^0.12.0", - "@eth-optimism/sdk": "^2.0.0", + "@eth-optimism/sdk": "^2.0.1", "@ethersproject/abstract-provider": "^5.7.0" } } diff --git a/packages/message-relayer/CHANGELOG.md b/packages/message-relayer/CHANGELOG.md index 1c1d366909032..e74f4783e1d03 100644 --- a/packages/message-relayer/CHANGELOG.md +++ b/packages/message-relayer/CHANGELOG.md @@ -1,5 +1,14 @@ # @eth-optimism/message-relayer +## 0.5.32 + +### Patch Changes + +- Updated dependencies [fecd42d67] +- Updated dependencies [66cafc00a] + - @eth-optimism/common-ts@0.8.1 + - @eth-optimism/sdk@2.0.1 + ## 0.5.31 ### Patch Changes diff --git a/packages/message-relayer/package.json b/packages/message-relayer/package.json index 4ec268ecaccd7..1043b0deefd3e 100644 --- a/packages/message-relayer/package.json +++ b/packages/message-relayer/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@eth-optimism/message-relayer", - "version": "0.5.31", + "version": "0.5.32", "description": "[Optimism] Service for automatically relaying L2 to L1 transactions", "main": "dist/index", "types": "dist/index", @@ -31,9 +31,9 @@ "url": "https://github.com/ethereum-optimism/optimism.git" }, "dependencies": { - "@eth-optimism/common-ts": "0.8.0", + "@eth-optimism/common-ts": "0.8.1", "@eth-optimism/core-utils": "0.12.0", - "@eth-optimism/sdk": "2.0.0", + "@eth-optimism/sdk": "2.0.1", "ethers": "^5.7.0" }, "devDependencies": { diff --git a/packages/replica-healthcheck/CHANGELOG.md b/packages/replica-healthcheck/CHANGELOG.md index c1ac743c60118..a484eaf74fde0 100644 --- a/packages/replica-healthcheck/CHANGELOG.md +++ b/packages/replica-healthcheck/CHANGELOG.md @@ -1,5 +1,12 @@ # @eth-optimism/replica-healthcheck +## 1.2.3 + +### Patch Changes + +- Updated dependencies [fecd42d67] + - @eth-optimism/common-ts@0.8.1 + ## 1.2.2 ### Patch Changes diff --git a/packages/replica-healthcheck/package.json b/packages/replica-healthcheck/package.json index f6947f80c00bf..a571236ae250a 100644 --- a/packages/replica-healthcheck/package.json +++ b/packages/replica-healthcheck/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@eth-optimism/replica-healthcheck", - "version": "1.2.2", + "version": "1.2.3", "description": "[Optimism] Service for monitoring the health of replica nodes", "main": "dist/index", "types": "dist/index", @@ -32,7 +32,7 @@ "url": "https://github.com/ethereum-optimism/optimism.git" }, "dependencies": { - "@eth-optimism/common-ts": "0.8.0", + "@eth-optimism/common-ts": "0.8.1", "@eth-optimism/core-utils": "0.12.0", "@ethersproject/abstract-provider": "^5.7.0" }, diff --git a/packages/sdk/CHANGELOG.md b/packages/sdk/CHANGELOG.md index 49bb728627329..3c7f0a9abf0af 100644 --- a/packages/sdk/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,5 +1,14 @@ # @eth-optimism/sdk +## 2.0.1 + +### Patch Changes + +- 66cafc00a: Update migrated withdrawal gaslimit calculation +- Updated dependencies [22c3885f5] +- Updated dependencies [f52c07529] + - @eth-optimism/contracts-bedrock@0.13.1 + ## 2.0.0 ### Major Changes diff --git a/packages/sdk/package.json b/packages/sdk/package.json index c72313a73a532..9ac2ce1b84a55 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@eth-optimism/sdk", - "version": "2.0.0", + "version": "2.0.1", "description": "[Optimism] Tools for working with Optimism", "main": "dist/index", "types": "dist/index", @@ -50,7 +50,7 @@ "dependencies": { "@eth-optimism/contracts": "0.5.40", "@eth-optimism/core-utils": "0.12.0", - "@eth-optimism/contracts-bedrock": "0.13.0", + "@eth-optimism/contracts-bedrock": "0.13.1", "lodash": "^4.17.21", "merkletreejs": "^0.2.27", "rlp": "^2.2.7" diff --git a/packages/sdk/src/utils/contracts.ts b/packages/sdk/src/utils/contracts.ts index 83882736b66cf..877a3fe07a3a5 100644 --- a/packages/sdk/src/utils/contracts.ts +++ b/packages/sdk/src/utils/contracts.ts @@ -165,19 +165,19 @@ export const getBridgeAdapters = ( } ): BridgeAdapters => { const adapterData: BridgeAdapterData = { - ...(CONTRACT_ADDRESSES[l2ChainId] + ...(CONTRACT_ADDRESSES[l2ChainId] || opts?.contracts?.l1?.L1StandardBridge ? { Standard: { Adapter: StandardBridgeAdapter, l1Bridge: - opts.contracts?.l1?.L1StandardBridge || + opts?.contracts?.l1?.L1StandardBridge || CONTRACT_ADDRESSES[l2ChainId].l1.L1StandardBridge, l2Bridge: predeploys.L2StandardBridge, }, ETH: { Adapter: ETHBridgeAdapter, l1Bridge: - opts.contracts?.l1?.L1StandardBridge || + opts?.contracts?.l1?.L1StandardBridge || CONTRACT_ADDRESSES[l2ChainId].l1.L1StandardBridge, l2Bridge: predeploys.L2StandardBridge, }, diff --git a/packages/two-step-monitor/.env.example b/packages/two-step-monitor/.env.example deleted file mode 100644 index 224ae9786f31c..0000000000000 --- a/packages/two-step-monitor/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# URL for an L1 RPC provider, used to query L2 output proposals -TWO_STEP_MONITOR__L1_RPC_PROVIDER= - -# URL for an L2 RPC provider, used to query canonical L2 state -TWO_STEP_MONITOR__L2_RPC_PROVIDER= - -TWO_STEP_MONITOR__HOSTNAME= -TWO_STEP_MONITOR__PORT= - -TWO_STEP_MONITOR__START_BATCH_INDEX= -TWO_STEP_MONITOR__LOOP_INTERVAL_MS= diff --git a/packages/two-step-monitor/.eslintrc.js b/packages/two-step-monitor/.eslintrc.js deleted file mode 100644 index bfd2057be80bd..0000000000000 --- a/packages/two-step-monitor/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: '../../.eslintrc.js', -} diff --git a/packages/two-step-monitor/.gitignore b/packages/two-step-monitor/.gitignore deleted file mode 100644 index b38db2f296ff3..0000000000000 --- a/packages/two-step-monitor/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/ -build/ diff --git a/packages/two-step-monitor/.lintstagedrc.yml b/packages/two-step-monitor/.lintstagedrc.yml deleted file mode 100644 index a3035a2299b26..0000000000000 --- a/packages/two-step-monitor/.lintstagedrc.yml +++ /dev/null @@ -1,2 +0,0 @@ -"*.{ts,js}": - - eslint diff --git a/packages/two-step-monitor/.prettierrc.js b/packages/two-step-monitor/.prettierrc.js deleted file mode 100644 index 90ef435957fc5..0000000000000 --- a/packages/two-step-monitor/.prettierrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('../../.prettierrc.js'), -}; diff --git a/packages/two-step-monitor/LICENSE b/packages/two-step-monitor/LICENSE deleted file mode 100644 index 6a7da5218bb25..0000000000000 --- a/packages/two-step-monitor/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright 2020-2021 Optimism - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/two-step-monitor/README.md b/packages/two-step-monitor/README.md deleted file mode 100644 index d424a033ba0c0..0000000000000 --- a/packages/two-step-monitor/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# @eth-optimism/two-step-monitor - -[![codecov](https://codecov.io/gh/ethereum-optimism/optimism/branch/develop/graph/badge.svg?token=0VTG7PG7YR&flag=two-step-monitor-tests)](https://codecov.io/gh/ethereum-optimism/optimism) - -The `two-step-monitor` is a simple service for detecting discrepancies between withdrawals created on L2, and -withdrawals proven on L1. - -## Installation - -Clone, install, and build the Optimism monorepo: - -``` -git clone https://github.com/ethereum-optimism/optimism.git -yarn install -yarn build -``` - -## Running the service - -Copy `.env.example` into a new file named `.env`, then set the environment variables listed there. -Once your environment variables have been set, run the service via: - -``` -yarn start -``` - -## Ports - -- API is exposed at `$TWO_STEP_MONITOR__HOSTNAME:$TWO_STEP_MONITOR__PORT/api` -- Metrics are exposed at `$TWO_STEP_MONITOR__HOSTNAME:$TWO_STEP_MONITOR__PORT/metrics` -- `$TWO_STEP_MONITOR__HOSTNAME` defaults to `0.0.0.0` -- `$TWO_STEP_MONITOR__PORT` defaults to `7300` - -## What this service does - -The `two-step-monitor` detects when a withdrawal is proven on L1, and verifies that a corresponding withdrawal -has been created on L2. - -We export a series of Prometheus metrics that you can use to trigger alerting when issues are detected. -Check the list of available metrics via `yarn start --help`: - -```sh -> yarn start --help -yarn run v1.22.19 -$ ts-node ./src/service.ts --help -Usage: service [options] - -Options: - --l1rpcprovider Provider for interacting with L1 (env: TWO_STEP_MONITOR__L1_RPC_PROVIDER) - --l2rpcprovider Provider for interacting with L2 (env: TWO_STEP_MONITOR__L2_RPC_PROVIDER) - --port Port for the app server (env: TWO_STEP_MONITOR__PORT) - --hostname Hostname for the app server (env: TWO_STEP_MONITOR__HOSTNAME) - -h, --help display help for command - -Metrics: - l1_node_connection_failures Number of times L1 node connection has failed (type: Gauge) - l2_node_connection_failures Number of times L2 node connection has failed (type: Gauge) - metadata Service metadata (type: Gauge) - unhandled_errors Unhandled errors (type: Counter) - -Done in 2.19s. -``` diff --git a/packages/two-step-monitor/hardhat.config.ts b/packages/two-step-monitor/hardhat.config.ts deleted file mode 100644 index 51ebadf6ceec6..0000000000000 --- a/packages/two-step-monitor/hardhat.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { HardhatUserConfig } from 'hardhat/types' - -// Hardhat plugins -import '@nomiclabs/hardhat-ethers' -import '@nomiclabs/hardhat-waffle' - -const config: HardhatUserConfig = { - mocha: { - timeout: 50000, - }, -} - -export default config diff --git a/packages/two-step-monitor/package.json b/packages/two-step-monitor/package.json deleted file mode 100644 index fc1b09db27bfa..0000000000000 --- a/packages/two-step-monitor/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "private": true, - "name": "@eth-optimism/two-step-monitor", - "version": "0.5.0", - "description": "[Optimism] Service for detecting faulty L2 output proposals", - "main": "dist/index", - "types": "dist/index", - "files": [ - "dist/*" - ], - "scripts": { - "start": "ts-node ./src/service.ts", - "test": "hardhat test", - "test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json", - "build": "tsc -p tsconfig.json", - "clean": "rimraf dist/ ./tsconfig.tsbuildinfo", - "lint": "yarn lint:fix && yarn lint:check", - "pre-commit": "lint-staged", - "lint:fix": "yarn lint:check --fix", - "lint:check": "eslint . --max-warnings=0" - }, - "keywords": [ - "optimism", - "ethereum", - "fault", - "detector" - ], - "homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/two-step-monitor#readme", - "license": "MIT", - "author": "Optimism PBC", - "repository": { - "type": "git", - "url": "https://github.com/ethereum-optimism/optimism.git" - }, - "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.6", - "@nomiclabs/hardhat-waffle": "^2.0.3", - "@types/chai": "^4.3.1", - "chai-as-promised": "^7.1.1", - "ethers": "^5.7.0", - "hardhat": "^2.9.6", - "ts-node": "^10.9.1" - } -} diff --git a/packages/two-step-monitor/src/index.ts b/packages/two-step-monitor/src/index.ts deleted file mode 100644 index caf7fffa10172..0000000000000 --- a/packages/two-step-monitor/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './service' diff --git a/packages/two-step-monitor/src/service.ts b/packages/two-step-monitor/src/service.ts deleted file mode 100644 index e842adabb96f5..0000000000000 --- a/packages/two-step-monitor/src/service.ts +++ /dev/null @@ -1 +0,0 @@ -export const todo = 'implement me' diff --git a/packages/two-step-monitor/test/setup.ts b/packages/two-step-monitor/test/setup.ts deleted file mode 100644 index c20ad0f59006b..0000000000000 --- a/packages/two-step-monitor/test/setup.ts +++ /dev/null @@ -1,10 +0,0 @@ -import chai = require('chai') -import chaiAsPromised from 'chai-as-promised' - -// Chai plugins go here. -chai.use(chaiAsPromised) - -const should = chai.should() -const expect = chai.expect - -export { should, expect } diff --git a/packages/two-step-monitor/tsconfig.json b/packages/two-step-monitor/tsconfig.json deleted file mode 100644 index 12b6b742d745a..0000000000000 --- a/packages/two-step-monitor/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist" - }, - "include": [ - "package.json", - "src/**/*" - ] -} diff --git a/patches/@changesets+cli+2.26.0.patch b/patches/@changesets+cli+2.26.0.patch new file mode 100644 index 0000000000000..9421a834c8238 --- /dev/null +++ b/patches/@changesets+cli+2.26.0.patch @@ -0,0 +1,66 @@ +diff --git a/node_modules/@changesets/cli/dist/cli.cjs.dev.js b/node_modules/@changesets/cli/dist/cli.cjs.dev.js +index b158219..6fdfb6e 100644 +--- a/node_modules/@changesets/cli/dist/cli.cjs.dev.js ++++ b/node_modules/@changesets/cli/dist/cli.cjs.dev.js +@@ -937,7 +937,7 @@ async function publishPackages({ + }) { + const packagesByName = new Map(packages.map(x => [x.packageJson.name, x])); + const publicPackages = packages.filter(pkg => !pkg.packageJson.private); +- const unpublishedPackagesInfo = await getUnpublishedPackages(publicPackages, preState); ++ const unpublishedPackagesInfo = await getUnpublishedPackages(packages, preState); + + if (unpublishedPackagesInfo.length === 0) { + return []; +@@ -957,20 +957,27 @@ async function publishAPackage(pkg, access, twoFactorState, tag) { + const { + name, + version, +- publishConfig ++ publishConfig, ++ private: isPrivate + } = pkg.packageJson; + const localAccess = publishConfig === null || publishConfig === void 0 ? void 0 : publishConfig.access; +- logger.info(`Publishing ${chalk__default['default'].cyan(`"${name}"`)} at ${chalk__default['default'].green(`"${version}"`)}`); +- const publishDir = publishConfig !== null && publishConfig !== void 0 && publishConfig.directory ? path.join(pkg.dir, publishConfig.directory) : pkg.dir; +- const publishConfirmation = await publish(name, { +- cwd: publishDir, +- access: localAccess || access, +- tag +- }, twoFactorState); ++ let published; ++ if (!isPrivate) { ++ logger.info(`Publishing ${chalk__default['default'].cyan(`"${name}"`)} at ${chalk__default['default'].green(`"${version}"`)}`); ++ const publishDir = publishConfig !== null && publishConfig !== void 0 && publishConfig.directory ? path.join(pkg.dir, publishConfig.directory) : pkg.dir; ++ const publishConfirmation = await publish(name, { ++ cwd: publishDir, ++ access: localAccess || access, ++ tag ++ }, twoFactorState); ++ published = publishConfirmation.published; ++ } else { ++ published = true; ++ } + return { + name, + newVersion: version, +- published: publishConfirmation.published ++ published + }; + } + +@@ -1140,8 +1147,13 @@ async function tagPublish(tool, packageReleases, cwd) { + if (tool !== "root") { + for (const pkg of packageReleases) { + const tag = `${pkg.name}@${pkg.newVersion}`; +- logger.log("New tag: ", tag); +- await git.tag(tag, cwd); ++ const allTags = await git.getAllTags(cwd); ++ if (allTags.has(tag)) { ++ logger.log("Skipping existing tag: ", tag); ++ } else { ++ logger.log("New tag: ", tag); ++ await git.tag(tag, cwd); ++ } + } + } else { + const tag = `v${packageReleases[0].newVersion}`; diff --git a/specs/assets/components.svg b/specs/assets/components.svg index e2526006c6389..410204e1896ce 100644 --- a/specs/assets/components.svg +++ b/specs/assets/components.svg @@ -1,4 +1,4 @@ -
    DepositFeed
    DepositFeed
    L2OutputOracle
    L2OutputOracle
    Rollup
    Node
    Rollup...
    Batch Submitter
    Batch Submitter
    L2 Execution Engine
    (L2 Geth)
    L2 Execution Engine...
    BatchInbox
    BatchInbox
    L1
    L1
    L2
    L2
    Output Submitter
    Output Submitter
    Legend
    Legend
    Sequencer
    Sequencer
    Verifier
    Verifier
    Text is not SVG - cannot display
    \ No newline at end of file +
    OptimismPortal
    OptimismPortal
    L2OutputOracle
    L2OutputOracle
    Rollup
    Node
    Rollup...
    Batch Submitter
    Batch Submitter
    L2 Execution Engine
    (L2 Geth)
    L2 Execution Engine...
    BatchInbox
    BatchInbox
    L1
    L1
    L2
    L2
    Output Submitter
    Output Submitter
    Legend
    Legend
    Sequencer
    Sequencer
    Verifier
    Verifier
    Text is not SVG - cannot display
    \ No newline at end of file diff --git a/specs/assets/propagation.svg b/specs/assets/propagation.svg index d95494eac5da0..7410bfd96914e 100644 --- a/specs/assets/propagation.svg +++ b/specs/assets/propagation.svg @@ -1,4 +1,4 @@ -
    Engine API
    Engine API
    Rollup
    Node
    Rollup...
    EE
    EE
    L1
    L1
    L2
    L2
    Batch Submitter
    Batch Submitter
    Output Submitter
    Output Submitter
    BatchInbox
    BatchInbox
    DepositFeed
    DepositFeed
    Rollup
    Node
    Rollup...
    EE
    EE
    Unsafe
    Block
    Propagation
    Unsafe...
    TX Sync
    TX Sync
    State Sync
    State Sync
    Legend
    Legend
    Sequencer
    Sequencer
    Verifier
    Verifier
    Text is not SVG - cannot display
    \ No newline at end of file +
    Engine API
    Engine API
    Rollup
    Node
    Rollup...
    EE
    EE
    L1
    L1
    L2
    L2
    Batch Submitter
    Batch Submitter
    Output Submitter
    Output Submitter
    BatchInbox
    BatchInbox
    OptimismPortal
    OptimismPortal
    Rollup
    Node
    Rollup...
    EE
    EE
    Unsafe
    Block
    Propagation
    Unsafe...
    TX Sync
    TX Sync
    State Sync
    State Sync
    Legend
    Legend
    Sequencer
    Sequencer
    Verifier
    Verifier
    Text is not SVG - cannot display
    \ No newline at end of file diff --git a/specs/derivation.md b/specs/derivation.md index 4dcd01ee2ba66..2fc7e55e9230e 100644 --- a/specs/derivation.md +++ b/specs/derivation.md @@ -517,12 +517,14 @@ New frames for timed-out channels are dropped instead of buffered. The channel-bank can only output data from the first opened channel. -Upon reading, first all timed-out channels are dropped. +Upon reading, while the first opened channel is timed-out, remove it from the channel-bank. -After pruning timed-out channels, the first remaining channel, if any, is read if it is ready: +Once the first opened channel, if any, is not timed-out and is ready, then it is read and removed from the channel-bank. -- The channel must be closed -- The channel must have a contiguous sequence of frames until the closing frame +A channel is ready if: + +- The channel is closed +- The channel has a contiguous sequence of frames until the closing frame If no channel is ready, the next frame is read and ingested into the channel bank. @@ -533,12 +535,16 @@ a new channel is opened, tagged with the current L1 block, and appended to the c Frame insertion conditions: -- New frames matching existing timed-out channels are dropped. -- Duplicate frames (by frame number) are dropped. -- Duplicate closes (new frame `is_last == 1`, but the channel has already seen a closing frame) are dropped. +- New frames matching timed-out channels that have not yet been pruned from the channel-bank are dropped. +- Duplicate frames (by frame number) for frames that have not yet been pruned from the channel-bank are dropped. +- Duplicate closes (new frame `is_last == 1`, but the channel has already seen a closing frame and has not yet been + pruned from the channel-bank) are dropped. If a frame is closing (`is_last == 1`) any existing higher-numbered frames are removed from the channel. +Note that while this allows channel IDs to be reused once they have been pruned from the channel-bank, it is recommended +that batcher implementations use unique channel IDs. + ### Channel Reader (Batch Decoding) In this stage, we decompress the channel we pull from the last stage, and then parse diff --git a/specs/overview.md b/specs/overview.md index 00fcfd4e5e8c5..7a5cdd95248d8 100644 --- a/specs/overview.md +++ b/specs/overview.md @@ -48,8 +48,8 @@ mechanisms. ### L1 Components -- **DepositFeed**: A feed of L2 transactions which originated as smart contract calls in the L1 state. - - The `DepositFeed` contract emits `TransactionDeposited` events, which the rollup driver reads in order to process +- **OptimismPortal**: A feed of L2 transactions which originated as smart contract calls in the L1 state. + - The `OptimismPortal` contract emits `TransactionDeposited` events, which the rollup driver reads in order to process deposits. - Deposits are guaranteed to be reflected in the L2 state within the _sequencing window_. - Beware that _transactions_ are deposited, not tokens. However deposited transactions are a key part of implementing @@ -106,7 +106,7 @@ The below diagram illustrates how the sequencer and verifiers fit together: - [Deposits](./deposits.md) Optimism supports two types of deposits: user deposits, and L1 attributes deposits. To perform a user deposit, users -call the `depositTransaction` method on the `DepositFeed` contract. This in turn emits `TransactionDeposited` events, +call the `depositTransaction` method on the `OptimismPortal` contract. This in turn emits `TransactionDeposited` events, which the rollup node reads during block derivation. L1 attributes deposits are used to register L1 block attributes (number, timestamp, etc.) on L2 via a call to the L1 @@ -141,8 +141,8 @@ worth of blocks has passed, i.e. after L1 block number `n + SEQUENCING_WINDOW_SI Each epoch contains at least one block. Every block in the epoch contains an L1 info transaction which contains contextual information about L1 such as the block hash and timestamp. The first block in the epoch also contains all -deposits initiated via the `DepositFeed` contract on L1. All L2 blocks can also contain _sequenced transactions_, i.e. -transactions submitted directly to the sequencer. +deposits initiated via the `OptimismPortal` contract on L1. All L2 blocks can also contain _sequenced transactions_, +i.e. transactions submitted directly to the sequencer. Whenever the sequencer creates a new L2 block for a given epoch, it must submit it to L1 as part of a _batch_, within the epoch's sequencing window (i.e. the batch must land before L1 block `n + SEQUENCING_WINDOW_SIZE`). These batches are diff --git a/specs/withdrawals.md b/specs/withdrawals.md index 84bf4dc0950cd..61bd8e45e1475 100644 --- a/specs/withdrawals.md +++ b/specs/withdrawals.md @@ -130,7 +130,7 @@ recognize that having the same address does not imply that a contract on L2 will ## The Optimism Portal Contract The Optimism Portal serves as both the entry and exit point to the Optimism L2. It is a contract which inherits from -the [DepositFeed](./deposits.md#deposit-contract) contract, and in addition provides the following interface for +the [OptimismPortal](./deposits.md#deposit-contract) contract, and in addition provides the following interface for withdrawals: - [`WithdrawalTransaction` type] diff --git a/yarn.lock b/yarn.lock index e557a3fd95f04..21435f664d9c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1105,20 +1105,6 @@ ethereumjs-util "^7.1.1" miller-rabin "^4.0.0" -"@ethereumjs/trie@^5.0.0-beta.1": - version "5.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@ethereumjs/trie/-/trie-5.0.0-beta.1.tgz#79d1108222b45bc3576d62583364c96626ce4175" - integrity sha512-OjTzt9fK5aMzm84GRSe+C7bO2zorbEWRueLbxOMlS7lHCiXA7akIQ3mzz9VBSMjT7m01hZ1r3fZIOGHzQVCHtw== - dependencies: - "@ethereumjs/util" "8.0.0-beta.1" - abstract-level "^1.0.3" - ethereum-cryptography "^1.0.3" - level "^8.0.0" - memory-level "^1.0.0" - readable-stream "^3.6.0" - rlp "4.0.0-beta.1" - semaphore-async-await "^1.5.1" - "@ethereumjs/tx@^3.2.1": version "3.3.0" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.0.tgz#14ed1b7fa0f28e1cd61e3ecbdab824205f6a4378" @@ -1143,14 +1129,6 @@ "@ethereumjs/common" "^2.6.3" ethereumjs-util "^7.1.4" -"@ethereumjs/util@8.0.0-beta.1", "@ethereumjs/util@^8.0.0-beta.1": - version "8.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.0.0-beta.1.tgz#369526faf6e9f1cadfd39c7741cc07cf33d128f8" - integrity sha512-yUg3TdJm25HiamAXbNuOagXQPmgdSrV3oEH0h+Adsxt6D7qHw8HyHLA8C+tNrLP2YwcjF1dGJ+F7WtOibzEp9g== - dependencies: - ethereum-cryptography "^1.0.3" - rlp "4.0.0-beta.1" - "@ethereumjs/vm@^5.9.0": version "5.9.0" resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.9.0.tgz#54e485097c6dbb42554d541ef8d84d06b7ddf12f" @@ -5534,19 +5512,6 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -abstract-level@^1.0.0, abstract-level@^1.0.2, abstract-level@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/abstract-level/-/abstract-level-1.0.3.tgz#78a67d3d84da55ee15201486ab44c09560070741" - integrity sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA== - dependencies: - buffer "^6.0.3" - catering "^2.1.0" - is-buffer "^2.0.5" - level-supports "^4.0.0" - level-transcoder "^1.0.1" - module-error "^1.0.1" - queue-microtask "^1.2.3" - abstract-leveldown@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-3.0.0.tgz#5cb89f958a44f526779d740d1440e743e0c30a57" @@ -7086,16 +7051,6 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-level@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browser-level/-/browser-level-1.0.1.tgz#36e8c3183d0fe1c405239792faaab5f315871011" - integrity sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ== - dependencies: - abstract-level "^1.0.2" - catering "^2.1.1" - module-error "^1.0.2" - run-parallel-limit "^1.1.0" - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -7528,11 +7483,6 @@ caseless@^0.12.0, caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -catering@^2.1.0, catering@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" - integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== - cbor@^5.0.2: version "5.2.0" resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" @@ -7803,17 +7753,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -classic-level@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/classic-level/-/classic-level-1.2.0.tgz#2d52bdec8e7a27f534e67fdeb890abef3e643c27" - integrity sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg== - dependencies: - abstract-level "^1.0.2" - catering "^2.1.0" - module-error "^1.0.1" - napi-macros "~2.0.0" - node-gyp-build "^4.3.0" - clean-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" @@ -13089,7 +13028,7 @@ is-buffer@^1.1.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.0, is-buffer@^2.0.5, is-buffer@~2.0.3: +is-buffer@^2.0.0, is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== @@ -14256,11 +14195,6 @@ level-sublevel@6.6.4: typewiselite "~1.0.0" xtend "~4.0.0" -level-supports@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-4.0.1.tgz#431546f9d81f10ff0fea0e74533a0e875c08c66a" - integrity sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA== - level-supports@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" @@ -14268,14 +14202,6 @@ level-supports@~1.0.0: dependencies: xtend "^4.0.2" -level-transcoder@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/level-transcoder/-/level-transcoder-1.0.1.tgz#f8cef5990c4f1283d4c86d949e73631b0bc8ba9c" - integrity sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w== - dependencies: - buffer "^6.0.3" - module-error "^1.0.1" - level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" @@ -14311,14 +14237,6 @@ level-ws@^2.0.0: level-packager "^5.1.0" leveldown "^5.4.0" -level@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/level/-/level-8.0.0.tgz#41b4c515dabe28212a3e881b61c161ffead14394" - integrity sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ== - dependencies: - browser-level "^1.0.1" - classic-level "^1.2.0" - leveldown@^5.4.0: version "5.6.0" resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" @@ -15176,15 +15094,6 @@ memdown@~3.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" -memory-level@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/memory-level/-/memory-level-1.0.0.tgz#7323c3fd368f9af2f71c3cd76ba403a17ac41692" - integrity sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og== - dependencies: - abstract-level "^1.0.0" - functional-red-black-tree "^1.0.1" - module-error "^1.0.1" - memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" @@ -15849,11 +15758,6 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -module-error@^1.0.1, module-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86" - integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA== - morgan@^1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" @@ -16164,11 +16068,6 @@ node-gyp-build@^4.2.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== -node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== - node-gyp-build@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" @@ -17972,13 +17871,6 @@ qs@^6.10.3, qs@^6.9.4: dependencies: side-channel "^1.0.4" -qs@^6.10.5: - version "6.10.5" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" - integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== - dependencies: - side-channel "^1.0.4" - qs@^6.4.0, qs@^6.7.0: version "6.10.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" @@ -18046,7 +17938,7 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== -queue-microtask@^1.2.2, queue-microtask@^1.2.3: +queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== @@ -18818,11 +18710,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rlp@4.0.0-beta.1: - version "4.0.0-beta.1" - resolved "https://registry.yarnpkg.com/rlp/-/rlp-4.0.0-beta.1.tgz#46983ee758344e5eee48f135129407434cfea2b6" - integrity sha512-UVIENF7Rw+nX5cpfzw6X3/oXNQKsSZ8HbDJUeU9RoIs1LLyMjcPZR1o26i1vFbpuVN8GRmcdopEYOMjVsLRsQQ== - rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" @@ -18862,13 +18749,6 @@ run-async@^2.2.0, run-async@^2.4.0: resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== -run-parallel-limit@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz#be80e936f5768623a38a963262d6bef8ff11e7ba" - integrity sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw== - dependencies: - queue-microtask "^1.2.2" - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" From 372ec73b277f5165f1dc7bafa1948caf01a6f2e7 Mon Sep 17 00:00:00 2001 From: keroro Date: Tue, 11 Apr 2023 10:22:14 +0800 Subject: [PATCH 15/17] feat(.github): add docker-build-push.yml --- .github/workflows/docker-build-push.yml | 48 +++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/docker-build-push.yml diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000000000..329043656362a --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,48 @@ +name: Build and Push Docker Images + +on: + push: + tags: + - '*' + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push op-node image + uses: docker/build-push-action@v4 + with: + context: . + file: ./op-node/Dockerfile + push: true + tags: ghcr.io/node-real/optimism/op-node:${{ github.ref_name }} + + - name: Build and push op-batcher image + uses: docker/build-push-action@v4 + with: + context: . + file: ./op-batcher/Dockerfile + push: true + tags: ghcr.io/node-real/optimism/op-batcher:${{ github.ref_name }} + + - name: Build and push op-proposer image + uses: docker/build-push-action@v4 + with: + context: . + file: ./op-proposer/Dockerfile + push: true + tags: ghcr.io/node-real/optimism/op-proposer:${{ github.ref_name }} From 19788c8e6fbed0c985d606bd32ef0747404d74fc Mon Sep 17 00:00:00 2001 From: keroro Date: Fri, 28 Apr 2023 16:34:10 +0800 Subject: [PATCH 16/17] feat: add API admin_sequencerStopped --- op-node/node/api.go | 5 +++++ op-node/rollup/driver/state.go | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/op-node/node/api.go b/op-node/node/api.go index 46fef276251d8..ede83051fa879 100644 --- a/op-node/node/api.go +++ b/op-node/node/api.go @@ -27,6 +27,7 @@ type driverClient interface { SyncStatus(ctx context.Context) (*eth.SyncStatus, error) BlockRefWithStatus(ctx context.Context, num uint64) (eth.L2BlockRef, *eth.SyncStatus, error) ResetDerivationPipeline(context.Context) error + SequencerStopped(ctx context.Context) bool StartSequencer(ctx context.Context, blockHash common.Hash) error StopSequencer(context.Context) (common.Hash, error) } @@ -54,6 +55,10 @@ func (n *adminAPI) ResetDerivationPipeline(ctx context.Context) error { return n.dr.ResetDerivationPipeline(ctx) } +func (n *adminAPI) SequencerStopped(ctx context.Context) bool { + return n.dr.SequencerStopped(ctx) +} + func (n *adminAPI) StartSequencer(ctx context.Context, blockHash common.Hash) error { recordDur := n.m.RecordRPCServerRequest("admin_startSequencer") defer recordDur() diff --git a/op-node/rollup/driver/state.go b/op-node/rollup/driver/state.go index 1387c9136b192..32284783d0874 100644 --- a/op-node/rollup/driver/state.go +++ b/op-node/rollup/driver/state.go @@ -348,6 +348,10 @@ func (s *Driver) ResetDerivationPipeline(ctx context.Context) error { } } +func (s *Driver) SequencerStopped(_ctx context.Context) bool { + return s.driverConfig.SequencerStopped +} + func (s *Driver) StartSequencer(ctx context.Context, blockHash common.Hash) error { if !s.driverConfig.SequencerEnabled { return errors.New("sequencer is not enabled") From 1be46d30cbfb1d7c93348f41d5668886c693e694 Mon Sep 17 00:00:00 2001 From: s7v7nislands Date: Mon, 24 Apr 2023 09:31:16 +0800 Subject: [PATCH 17/17] Squashed commit of the following: commit 6695f82783417b9f355da32ca0cc0c6068091d36 Merge: e07e80ca2 a582e6f9a Author: Kero Date: Wed Apr 19 14:25:18 2023 +0800 Merge pull request #17 from node-real/gastrack average base fee commit e07e80ca2d7432974d2217725f77876fb5b5fdd7 Merge: 250fbcb2a 372ec73b2 Author: Kero Date: Mon Apr 17 18:29:41 2023 +0800 Merge pull request #14 from keroro520/docker-build-push-workflow feat(.github): add docker-build-push.yml commit 372ec73b277f5165f1dc7bafa1948caf01a6f2e7 Author: keroro Date: Tue Apr 11 10:22:14 2023 +0800 feat(.github): add docker-build-push.yml commit 250fbcb2a22507f973dececb33fe49fe2469b87c Merge: f7e1641b1 8fa4ea66c Author: Kero Date: Thu Apr 13 15:49:56 2023 +0800 Merge pull request #11 from node-real/update_to_for_bsc_0.5 update to for_bsc 0.5 commit 8fa4ea66cd201c9cfd2a7b7e4b24023b64cef3c0 Author: s7v7nislands Date: Thu Apr 13 13:22:43 2023 +0800 update to for_bsc 0.5 commit a582e6f9a7c40c7e1b92950f9a1b37f728b21662 Author: s7v7nislands Date: Wed Apr 12 18:24:56 2023 +0800 filter system trxs which gas price is zero commit f7e1641b12a598c1959e5d7f45c9b2e28ecf7307 Merge: 018857554 25954a2f4 Author: Kero Date: Wed Apr 12 15:29:02 2023 +0800 Merge pull request #9 from keroro520/cherry-pick-ci-with-tag Cherry pick ci with tag commit 25954a2f428549ebc31a2c4391f39d243e3bc25d Author: keroro Date: Tue Apr 11 21:15:56 2023 +0800 fix(.github): change create trigger to push trigger commit 4173294881509bf52e842f9212bcdc06c159dbc4 Author: ArthurMa <4406arthur@gmail.com> Date: Tue Apr 11 19:30:23 2023 +0800 Update ci-with-tag.yml commit a7672a549deede2da3b5f1befb7388895f0ee542 Author: ArthurMa <4406arthur@gmail.com> Date: Tue Apr 11 18:44:26 2023 +0800 Update ci-with-tag.yml commit 071c9d80652b1e8b5e9a4d8abe570545417eb535 Author: ArthurMa <4406arthur@gmail.com> Date: Tue Apr 11 17:51:44 2023 +0800 Update ci-with-tag.yml commit 1d83273d3ca077a939d3e37727f72741b3eb0ed7 Author: ArthurMa <4406arthur@gmail.com> Date: Tue Apr 11 17:44:58 2023 +0800 Update ci-with-tag.yml commit 023aa14ab2346e9ed83d7fac226150800b7253fe Author: ArthurMa <4406arthur@gmail.com> Date: Tue Apr 11 17:12:30 2023 +0800 Update ci-with-tag.yml commit 40a7234bb94ad561310e51b2c1ccbd950ac98cd9 Author: ArthurMa <4406arthur@gmail.com> Date: Tue Apr 11 17:10:56 2023 +0800 Update and rename ci-with-pr.yml to ci-with-tag.yml commit 2f7128c47a95ec55a93197af1b8d2b9ecd4c573d Author: arthurma Date: Tue Apr 11 14:24:56 2023 +0800 [beta] new a CI flow to export image to nr ecr commit 307353a3b952c25082bb347372b18a3e859e914f Author: s7v7nislands Date: Mon Apr 10 16:16:52 2023 +0800 add avarage gasprice to l1 block info commit 018857554fe497949d40552cd08204a0370a83b6 Merge: 9b95617e0 2bf1e92ad Author: s7v7nislands Date: Thu Apr 6 17:59:17 2023 +0800 Merge pull request #4 from node-real/fix_gasprice fix gasprice commit 2bf1e92adad9462d2659cbccee778d5fd62a1ada Author: s7v7nislands Date: Thu Apr 6 16:47:03 2023 +0800 fix gasprice commit 9b95617e0cb7c55e8efe55767a79b5b377731fa9 Merge: fbba7ba49 d75fb80c5 Author: Kero Date: Tue Mar 28 16:53:01 2023 +0800 Merge pull request #2 from node-real/fix_basefee fix basefee commit d75fb80c5db39cbb10c222dd55e02fb6c181783b Author: s7v7nislands Date: Tue Mar 28 16:51:12 2023 +0800 fix basefee commit fbba7ba495636eac7d92ded3db917fe5b3ea043c Merge: 7bdcb0ff1 22779e4b3 Author: s7v7nislands Date: Mon Mar 27 16:19:31 2023 +0800 Merge pull request #1 from node-real/fix_bsc fix optimims port to bsc commit 22779e4b340430675e00dbca2cfa13744e9df749 Author: s7v7nislands Date: Sun Mar 26 21:44:48 2023 +0800 fix optimims port to bsc fix trx manager feat: add API admin_sequencerStopped --- .github/workflows/ci-with-tag.yml | 77 ++++++++++++ .github/workflows/docker-build-push.yml | 48 +++++++ op-bindings/bindings/erc20.go | 2 +- op-bindings/bindings/optimismportal.go | 2 +- op-bindings/bindings/optimismportal_more.go | 2 +- op-chain-ops/genesis/config.go | 15 ++- op-e2e/actions/action.go | 2 + op-e2e/system_test.go | 2 + op-node/eth/heads.go | 27 +++- op-node/node/api.go | 5 + op-node/rollup/derive/attributes.go | 58 ++++++++- .../rollup/derive/attributes_queue_test.go | 2 + op-node/rollup/derive/l1_block_info.go | 16 ++- op-node/rollup/derive/l1_block_info_test.go | 2 + op-node/rollup/driver/state.go | 4 + op-proposer/proposer/abi_test.go | 3 +- op-proposer/proposer/l2_output_submitter.go | 3 +- op-service/txmgr/txmgr.go | 118 ++++++++++++------ op-service/txmgr/txmgr_test.go | 2 + op-service/util.go | 2 + op-signer/client/transaction_args.go | 36 +++--- .../contracts/L1/ResourceMetering.sol | 3 +- 22 files changed, 366 insertions(+), 65 deletions(-) create mode 100644 .github/workflows/ci-with-tag.yml create mode 100644 .github/workflows/docker-build-push.yml diff --git a/.github/workflows/ci-with-tag.yml b/.github/workflows/ci-with-tag.yml new file mode 100644 index 0000000000000..56ad0e69abab6 --- /dev/null +++ b/.github/workflows/ci-with-tag.yml @@ -0,0 +1,77 @@ +name: CI-trigger-by-tag + +on: + push: + tags: + - '*' +env: + REGION: us-east-1 + ECR_REGISTRY_ID: 553885929720 + +jobs: + ci-lint: + runs-on: [self-hosted,qa-infra-k8s] + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: static check + id: static-check + # TODO: developers need to add static check scripts + run: | + echo "static check" + ci-tests: + runs-on: [self-hosted,qa-infra-k8s] + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: unit tests + id: unit-tests + # TODO: developers need to add unit test scripts + run: | + echo "unit tests" + ci-build: + runs-on: [self-hosted,qa-infra-k8s] + needs: [ci-lint, ci-tests] + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 # More information on this action can be found below in the 'AWS Credentials' section + with: + role-to-assume: arn:aws:iam::553885929720:role/tf_nodereal_prod_ecr_cicd_deployment_assume_role + aws-region: us-east-1 + role-duration-seconds: 1800 + - name: Build, tag, and push image to Amazon ECR + env: + OP_NODE_IMAGE_REPO: op-node + OP_BATCHER_IMAGE_REPO: op-batcher + OP_PROPOSER_IMAGE_REPO: op-proposer + run: | + + OP_NODE_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_NODE_IMAGE_REPO:${GITHUB_REF#refs/*/}" + OP_BATCHER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_BATCHER_IMAGE_REPO:${GITHUB_REF#refs/*/}" + OP_PROPOSER_IMAGE_NAME="${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com/$OP_PROPOSER_IMAGE_REPO:${GITHUB_REF#refs/*/}" + + aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ECR_REGISTRY_ID}.dkr.ecr.${REGION}.amazonaws.com + + aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_NODE_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_NODE_IMAGE_REPO + aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_NODE_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json + + aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_BATCHER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_BATCHER_IMAGE_REPO + aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_BATCHER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json + + aws ecr --region $REGION describe-repositories --registry-id $ECR_REGISTRY_ID --repository-names $OP_PROPOSER_IMAGE_REPO || aws ecr --region $REGION create-repository --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO + aws ecr --region $REGION set-repository-policy --registry-id $ECR_REGISTRY_ID --repository-name $OP_PROPOSER_IMAGE_REPO --policy-text file:///home/runner/repo-access-permissions.json + + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_NODE_IMAGE_NAME -f op-node/Dockerfile . + docker push $OP_NODE_IMAGE_NAME + + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_BATCHER_IMAGE_NAME -f op-batcher/Dockerfile . + docker push $OP_BATCHER_IMAGE_NAME + + docker build --build-arg GIT_TOKEN=${{ secrets.GO_MODULES_TOKEN }} -t $OP_PROPOSER_IMAGE_NAME -f op-proposer/Dockerfile . + docker push $OP_PROPOSER_IMAGE_NAME + diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000000000..329043656362a --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,48 @@ +name: Build and Push Docker Images + +on: + push: + tags: + - '*' + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push op-node image + uses: docker/build-push-action@v4 + with: + context: . + file: ./op-node/Dockerfile + push: true + tags: ghcr.io/node-real/optimism/op-node:${{ github.ref_name }} + + - name: Build and push op-batcher image + uses: docker/build-push-action@v4 + with: + context: . + file: ./op-batcher/Dockerfile + push: true + tags: ghcr.io/node-real/optimism/op-batcher:${{ github.ref_name }} + + - name: Build and push op-proposer image + uses: docker/build-push-action@v4 + with: + context: . + file: ./op-proposer/Dockerfile + push: true + tags: ghcr.io/node-real/optimism/op-proposer:${{ github.ref_name }} diff --git a/op-bindings/bindings/erc20.go b/op-bindings/bindings/erc20.go index 766f6eb69b117..917d43b11428e 100644 --- a/op-bindings/bindings/erc20.go +++ b/op-bindings/bindings/erc20.go @@ -31,7 +31,7 @@ var ( // ERC20MetaData contains all meta data concerning the ERC20 contract. var ERC20MetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name_\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"symbol_\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x60806040523480156200001157600080fd5b5060405162000e3c38038062000e3c833981016040819052620000349162000127565b600362000042838262000220565b50600462000051828262000220565b505050620002ec565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200008257600080fd5b81516001600160401b03808211156200009f576200009f6200005a565b604051601f8301601f19908116603f01168101908282118183101715620000ca57620000ca6200005a565b81604052838152602092508683858801011115620000e757600080fd5b600091505b838210156200010b5785820183015181830184015290820190620000ec565b838211156200011d5760008385830101525b9695505050505050565b600080604083850312156200013b57600080fd5b82516001600160401b03808211156200015357600080fd5b620001618683870162000070565b935060208501519150808211156200017857600080fd5b50620001878582860162000070565b9150509250929050565b600181811c90821680620001a657607f821691505b602082108103620001c757634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200021b57600081815260208120601f850160051c81016020861015620001f65750805b601f850160051c820191505b81811015620002175782815560010162000202565b5050505b505050565b81516001600160401b038111156200023c576200023c6200005a565b62000254816200024d845462000191565b84620001cd565b602080601f8311600181146200028c5760008415620002735750858301515b600019600386901b1c1916600185901b17855562000217565b600085815260208120601f198616915b82811015620002bd578886015182559484019460019091019084016200029c565b5085821015620002dc5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610b4080620002fc6000396000f3fe608060405234801561001057600080fd5b50600436106100c95760003560e01c80633950935111610081578063a457c2d71161005b578063a457c2d714610194578063a9059cbb146101a7578063dd62ed3e146101ba57600080fd5b8063395093511461014357806370a082311461015657806395d89b411461018c57600080fd5b806318160ddd116100b257806318160ddd1461010f57806323b872dd14610121578063313ce5671461013457600080fd5b806306fdde03146100ce578063095ea7b3146100ec575b600080fd5b6100d6610200565b6040516100e3919061094a565b60405180910390f35b6100ff6100fa3660046109e6565b610292565b60405190151581526020016100e3565b6002545b6040519081526020016100e3565b6100ff61012f366004610a10565b6102aa565b604051601281526020016100e3565b6100ff6101513660046109e6565b6102ce565b610113610164366004610a4c565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6100d661031a565b6100ff6101a23660046109e6565b610329565b6100ff6101b53660046109e6565b6103ff565b6101136101c8366004610a6e565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b60606003805461020f90610aa1565b80601f016020809104026020016040519081016040528092919081815260200182805461023b90610aa1565b80156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b5050505050905090565b6000336102a081858561040d565b5060019392505050565b6000336102b88582856105c0565b6102c3858585610697565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906102a09082908690610315908790610af4565b61040d565b60606004805461020f90610aa1565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152812054909190838110156103f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6102c3828686840361040d565b6000336102a0818585610697565b73ffffffffffffffffffffffffffffffffffffffff83166104af576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016103e9565b73ffffffffffffffffffffffffffffffffffffffff8216610552576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016103e9565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106915781811015610684576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016103e9565b610691848484840361040d565b50505050565b73ffffffffffffffffffffffffffffffffffffffff831661073a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016103e9565b73ffffffffffffffffffffffffffffffffffffffff82166107dd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016103e9565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610893576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016103e9565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152602081905260408082208585039055918516815290812080548492906108d7908490610af4565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161093d91815260200190565b60405180910390a3610691565b600060208083528351808285015260005b818110156109775785810183015185820160400152820161095b565b81811115610989576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146109e157600080fd5b919050565b600080604083850312156109f957600080fd5b610a02836109bd565b946020939093013593505050565b600080600060608486031215610a2557600080fd5b610a2e846109bd565b9250610a3c602085016109bd565b9150604084013590509250925092565b600060208284031215610a5e57600080fd5b610a67826109bd565b9392505050565b60008060408385031215610a8157600080fd5b610a8a836109bd565b9150610a98602084016109bd565b90509250929050565b600181811c90821680610ab557607f821691505b602082108103610aee577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b60008219821115610b2e577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b50019056fea164736f6c634300080f000a", + Bin: "0x60806040523480156200001157600080fd5b5060405162000e2a38038062000e2a83398101604081905262000034916200011f565b600362000042838262000218565b50600462000051828262000218565b505050620002e4565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200008257600080fd5b81516001600160401b03808211156200009f576200009f6200005a565b604051601f8301601f19908116603f01168101908282118183101715620000ca57620000ca6200005a565b81604052838152602092508683858801011115620000e757600080fd5b600091505b838210156200010b5785820183015181830184015290820190620000ec565b600093810190920192909252949350505050565b600080604083850312156200013357600080fd5b82516001600160401b03808211156200014b57600080fd5b620001598683870162000070565b935060208501519150808211156200017057600080fd5b506200017f8582860162000070565b9150509250929050565b600181811c908216806200019e57607f821691505b602082108103620001bf57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200021357600081815260208120601f850160051c81016020861015620001ee5750805b601f850160051c820191505b818110156200020f57828155600101620001fa565b5050505b505050565b81516001600160401b038111156200023457620002346200005a565b6200024c8162000245845462000189565b84620001c5565b602080601f8311600181146200028457600084156200026b5750858301515b600019600386901b1c1916600185901b1785556200020f565b600085815260208120601f198616915b82811015620002b55788860151825594840194600190910190840162000294565b5085821015620002d45787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b610b3680620002f46000396000f3fe608060405234801561001057600080fd5b50600436106100c95760003560e01c80633950935111610081578063a457c2d71161005b578063a457c2d714610194578063a9059cbb146101a7578063dd62ed3e146101ba57600080fd5b8063395093511461014357806370a082311461015657806395d89b411461018c57600080fd5b806318160ddd116100b257806318160ddd1461010f57806323b872dd14610121578063313ce5671461013457600080fd5b806306fdde03146100ce578063095ea7b3146100ec575b600080fd5b6100d6610200565b6040516100e3919061094c565b60405180910390f35b6100ff6100fa3660046109e1565b610292565b60405190151581526020016100e3565b6002545b6040519081526020016100e3565b6100ff61012f366004610a0b565b6102ac565b604051601281526020016100e3565b6100ff6101513660046109e1565b6102d0565b610113610164366004610a47565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6100d661031c565b6100ff6101a23660046109e1565b61032b565b6100ff6101b53660046109e1565b610401565b6101136101c8366004610a69565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b60606003805461020f90610a9c565b80601f016020809104026020016040519081016040528092919081815260200182805461023b90610a9c565b80156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b5050505050905090565b6000336102a081858561040f565b60019150505b92915050565b6000336102ba8582856105c2565b6102c5858585610699565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906102a09082908690610317908790610aef565b61040f565b60606004805461020f90610a9c565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff87168452909152812054909190838110156103f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6102c5828686840361040f565b6000336102a0818585610699565b73ffffffffffffffffffffffffffffffffffffffff83166104b1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f726573730000000000000000000000000000000000000000000000000000000060648201526084016103eb565b73ffffffffffffffffffffffffffffffffffffffff8216610554576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f737300000000000000000000000000000000000000000000000000000000000060648201526084016103eb565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106935781811015610686576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e636500000060448201526064016103eb565b610693848484840361040f565b50505050565b73ffffffffffffffffffffffffffffffffffffffff831661073c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f647265737300000000000000000000000000000000000000000000000000000060648201526084016103eb565b73ffffffffffffffffffffffffffffffffffffffff82166107df576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f657373000000000000000000000000000000000000000000000000000000000060648201526084016103eb565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610895576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e6365000000000000000000000000000000000000000000000000000060648201526084016103eb565b73ffffffffffffffffffffffffffffffffffffffff8085166000908152602081905260408082208585039055918516815290812080548492906108d9908490610aef565b925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161093f91815260200190565b60405180910390a3610693565b600060208083528351808285015260005b818110156109795785810183015185820160400152820161095d565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff811681146109dc57600080fd5b919050565b600080604083850312156109f457600080fd5b6109fd836109b8565b946020939093013593505050565b600080600060608486031215610a2057600080fd5b610a29846109b8565b9250610a37602085016109b8565b9150604084013590509250925092565b600060208284031215610a5957600080fd5b610a62826109b8565b9392505050565b60008060408385031215610a7c57600080fd5b610a85836109b8565b9150610a93602084016109b8565b90509250929050565b600181811c90821680610ab057607f821691505b602082108103610ae9577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b808201808211156102a6577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fdfea164736f6c6343000810000a", } // ERC20ABI is the input ABI used to generate the binding from. diff --git a/op-bindings/bindings/optimismportal.go b/op-bindings/bindings/optimismportal.go index 0cce4b93fac9c..ad5f4ec319618 100644 --- a/op-bindings/bindings/optimismportal.go +++ b/op-bindings/bindings/optimismportal.go @@ -49,7 +49,7 @@ type TypesWithdrawalTransaction struct { // OptimismPortalMetaData contains all meta data concerning the OptimismPortal contract. var OptimismPortalMetaData = &bind.MetaData{ ABI: "[{\"inputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"_l2Oracle\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"_guardian\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_paused\",\"type\":\"bool\"},{\"internalType\":\"contractSystemConfig\",\"name\":\"_config\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"opaqueData\",\"type\":\"bytes\"}],\"name\":\"TransactionDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"withdrawalHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"WithdrawalFinalized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"withdrawalHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"WithdrawalProven\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GUARDIAN\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"L2_ORACLE\",\"outputs\":[{\"internalType\":\"contractL2OutputOracle\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"SYSTEM_CONFIG\",\"outputs\":[{\"internalType\":\"contractSystemConfig\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_value\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"_isCreation\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"depositTransaction\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"donateETH\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.WithdrawalTransaction\",\"name\":\"_tx\",\"type\":\"tuple\"}],\"name\":\"finalizeWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"finalizedWithdrawals\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"_paused\",\"type\":\"bool\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_l2OutputIndex\",\"type\":\"uint256\"}],\"name\":\"isOutputFinalized\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"l2Sender\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"params\",\"outputs\":[{\"internalType\":\"uint128\",\"name\":\"prevBaseFee\",\"type\":\"uint128\"},{\"internalType\":\"uint64\",\"name\":\"prevBoughtGas\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"prevBlockNum\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.WithdrawalTransaction\",\"name\":\"_tx\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"_l2OutputIndex\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"version\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"stateRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"messagePasserStorageRoot\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"latestBlockhash\",\"type\":\"bytes32\"}],\"internalType\":\"structTypes.OutputRootProof\",\"name\":\"_outputRootProof\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"_withdrawalProof\",\"type\":\"bytes[]\"}],\"name\":\"proveWithdrawalTransaction\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"provenWithdrawals\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"outputRoot\",\"type\":\"bytes32\"},{\"internalType\":\"uint128\",\"name\":\"timestamp\",\"type\":\"uint128\"},{\"internalType\":\"uint128\",\"name\":\"l2OutputIndex\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", - Bin: "0x6101406040523480156200001257600080fd5b506040516200599b3803806200599b833981016040819052620000359162000296565b60016080819052600360a05260c0526001600160a01b0380851660e052838116610120528116610100526200006a8262000074565b5050505062000302565b600054610100900460ff1615808015620000955750600054600160ff909116105b80620000c55750620000b230620001cb60201b62001b9d1760201c565b158015620000c5575060005460ff166001145b6200012e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000152576000805461ff0019166101001790555b603280546001600160a01b03191661dead1790556035805483151560ff1990911617905562000180620001da565b8015620001c7576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620002475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000125565b60408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b0217600155565b6001600160a01b03811681146200029357600080fd5b50565b60008060008060808587031215620002ad57600080fd5b8451620002ba816200027d565b6020860151909450620002cd816200027d565b60408601519093508015158114620002e457600080fd5b6060860151909250620002f7816200027d565b939692955090935050565b60805160a05160c05160e051610100516101205161560a620003916000396000818161024e015281816106ca0152610fcd01526000818161047401526122e701526000818161014f0152818161093301528181610b1401528181610f29015281816112e30152818161155501526120d701526000610e9401526000610e6b01526000610e42015261560a6000f3fe6080604052600436106101115760003560e01c80638b4c40b0116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c146103c3578063e9e05c421461044f578063f04987501461046257600080fd5b8063cff0ab9614610302578063d53a822f146103a357600080fd5b80638b4c40b0146101365780638c3152e9146102855780639bf62d82146102a5578063a14238e7146102d257600080fd5b80635c975abb116100e15780635c975abb146101f25780636dbffb781461021c578063724c184c1461023c5780638456cb591461027057600080fd5b80621c2ff61461013d5780633f4ba83a1461019b5780634870496f146101b057806354fd4d50146101d057600080fd5b36610138576101363334620186a0600060405180602001604052806000815250610496565b005b600080fd5b34801561014957600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101a757600080fd5b506101366106b2565b3480156101bc57600080fd5b506101366101cb366004614c13565b6107d5565b3480156101dc57600080fd5b506101e5610e3b565b6040516101929190614d69565b3480156101fe57600080fd5b5060355461020c9060ff1681565b6040519015158152602001610192565b34801561022857600080fd5b5061020c610237366004614d7c565b610ede565b34801561024857600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b34801561027c57600080fd5b50610136610fb5565b34801561029157600080fd5b506101366102a0366004614d95565b6110d5565b3480156102b157600080fd5b506032546101719073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102de57600080fd5b5061020c6102ed366004614d7c565b60336020526000908152604090205460ff1681565b34801561030e57600080fd5b5060015461036a906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610192565b3480156103af57600080fd5b506101366103be366004614dda565b6119b0565b3480156103cf57600080fd5b506104216103de366004614d7c565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff9283166020850152911690820152606001610192565b61013661045d366004614df5565b610496565b34801561046e57600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b8260005a9050831561054d5773ffffffffffffffffffffffffffffffffffffffff87161561054d57604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6152088567ffffffffffffffff1610156105e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4f7074696d69736d506f7274616c3a20676173206c696d6974206d757374206360448201527f6f76657220696e737472696e7369632067617320636f737400000000000000006064820152608401610544565b3332811461060a575033731111000000000000000000000000000000001111015b60003488888888604051602001610625959493929190614e7a565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516106959190614d69565b60405180910390a450506106a98282611bb9565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610777576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610901576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610544565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa15801561098f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b39190614eff565b5190506109cd6109c836869003860186614f64565b611ee6565b8114610a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610544565b6000610a6687611f42565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610b985750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b949190614eff565b5114155b610c24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610544565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610ced9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610ce3888a614fca565b8a60400135611f72565b610d79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610544565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610e667f0000000000000000000000000000000000000000000000000000000000000000611f96565b610e8f7f0000000000000000000000000000000000000000000000000000000000000000611f96565b610eb87f0000000000000000000000000000000000000000000000000000000000000000611f96565b604051602001610eca9392919061504e565b604051602081830303815290604052905090565b6040517fa25ae55700000000000000000000000000000000000000000000000000000000815260048101829052600090610faf9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015610f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f949190614eff565b602001516fffffffffffffffffffffffffffffffff166120d3565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461107a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016107cb565b60355460ff1615611142576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146111eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610544565b60006111f682611f42565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036112e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610544565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561134c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137091906150c4565b81602001516fffffffffffffffffffffffffffffffff16101561143b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610544565b61145a81602001516fffffffffffffffffffffffffffffffff166120d3565b61150c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610544565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa1580156115b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d59190614eff565b825181519192501461168f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610544565b6116ae81602001516fffffffffffffffffffffffffffffffff166120d3565b611760576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610544565b60008381526033602052604090205460ff16156117ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610544565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a08801516118a193929190612176565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b9061190690841515815260200190565b60405180910390a28015801561191c5750326001145b156119a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610544565b5050505050565b600054610100900460ff16158080156119d05750600054600160ff909116105b806119ea5750303b1580156119ea575060005460ff166001145b611a76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610544565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611ad457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611b366121d0565b8015611b9957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611bef907801000000000000000000000000000000000000000000000000900467ffffffffffffffff164361510c565b90506000611bfb6122b3565b90506000816020015160ff16826000015163ffffffff16611c1c9190615152565b90508215611d5357600154600090611c53908390700100000000000000000000000000000000900467ffffffffffffffff166151ba565b90506000836040015160ff1683611c6a919061522e565b600154611c8a9084906fffffffffffffffffffffffffffffffff1661522e565b611c949190615152565b600154909150600090611ce590611cbe9084906fffffffffffffffffffffffffffffffff166152ea565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612379565b90506001861115611d1457611d11611cbe82876040015160ff1660018a611d0c919061510c565b612398565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611d86908490700100000000000000000000000000000000900467ffffffffffffffff1661535e565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611e69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610544565b600154600090611e95906fffffffffffffffffffffffffffffffff1667ffffffffffffffff881661538a565b90506000611ea748633b9aca006123ed565b611eb190836153c7565b905060005a611ec0908861510c565b905080821115611edc57611edc611ed7828461510c565b612404565b5050505050505050565b60008160000151826020015183604001518460600151604051602001611f25949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097611f259790969591016153db565b600080611f7e86612432565b9050611f8c81868686612464565b9695505050505050565b606081600003611fd957505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156120035780611fed81615432565b9150611ffc9050600a836153c7565b9150611fdd565b60008167ffffffffffffffff81111561201e5761201e614a39565b6040519080825280601f01601f191660200182016040528015612048576020820181803683370190505b5090505b84156120cb5761205d60018361510c565b915061206a600a8661546a565b61207590603061547e565b60f81b81838151811061208a5761208a615496565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506120c4600a866153c7565b945061204c565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa158015612140573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216491906150c4565b61216e908361547e565b421192915050565b600080603f60c88601604002045a10156121b9576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080845160208601878a5af19695505050505050565b600054610100900460ff16612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610544565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612350573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237491906154ea565b905090565b600061238e6123888585612494565b836124a4565b90505b9392505050565b6000670de0b6b3a76400006123d96123b08583615152565b6123c290670de0b6b3a76400006151ba565b6123d485670de0b6b3a764000061522e565b6124b3565b6123e3908661522e565b61238e9190615152565b6000818310156123fd5781612391565b5090919050565b6000805a90505b825a612417908361510c565b101561242d5761242682615432565b915061240b565b505050565b6060818051906020012060405160200161244e91815260200190565b6040516020818303038152906040529050919050565b600061248b846124758786866124e4565b8051602091820120825192909101919091201490565b95945050505050565b6000818312156123fd5781612391565b60008183126123fd5781612391565b6000612391670de0b6b3a7640000836124cb86612f6c565b6124d5919061522e565b6124df9190615152565b6131b0565b60606000845111612551576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610544565b600061255c846133ef565b90506000612569866134de565b905060008460405160200161258091815260200190565b60405160208183030381529060405290506000805b8451811015612ee35760008582815181106125b2576125b2615496565b60200260200101519050845183111561264d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610544565b82600003612706578051805160209182012060405161269b9261267592910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b612701576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610544565b61285d565b8051516020116127bc57805180516020918201206040516127309261267592910190815260200190565b612701576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610544565b80518451602080870191909120825191909201201461285d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610544565b6128696010600161547e565b81602001515103612a4a57845183036129e25760006128a5826020015160108151811061289857612898615496565b6020026020010151613679565b90506000815111612938576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610544565b60018751612946919061510c565b83146129d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610544565b965061239195505050505050565b60008584815181106129f6576129f6615496565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612a2157612a21615496565b60200260200101519050612a34816137d9565b9550612a4160018661547e565b94505050612ed0565b600281602001515103612e48576000612a62826137fe565b9050600081600081518110612a7957612a79615496565b016020015160f81c90506000612a90600283615589565b612a9b9060026155ab565b90506000612aac848360ff16613822565b90506000612aba8a89613822565b90506000612ac88383613858565b905080835114612b5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610544565b60ff851660021480612b6f575060ff85166003145b15612d635780825114612c04576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610544565b6000612c20886020015160018151811061289857612898615496565b90506000815111612cb3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610544565b60018d51612cc1919061510c565b8914612d4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610544565b9c506123919b505050505050505050505050565b60ff85161580612d76575060ff85166001145b15612db557612da28760200151600181518110612d9557612d95615496565b60200260200101516137d9565b9950612dae818a61547e565b9850612e3d565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610544565b505050505050612ed0565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610544565b5080612edb81615432565b915050612595565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610544565b6000808213612fd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b60006060612fe484613907565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136131e157506000919050565b680755bf798b4a1bf1e58212613253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610544565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff81111561340f5761340f614a39565b60405190808252806020026020018201604052801561345457816020015b604080518082019091526060808252602082015281526020019060019003908161342d5790505b50905060005b828110156134d657604051806040016040528086838151811061347f5761347f615496565b602002602001015181526020016134ae8784815181106134a1576134a1615496565b60200260200101516139dd565b8152508282815181106134c3576134c3615496565b602090810291909101015260010161345a565b509392505050565b805160609060006134f082600261538a565b67ffffffffffffffff81111561350857613508614a39565b6040519080825280601f01601f191660200182016040528015613532576020820181803683370190505b5090506000805b8381101561366f5785818151811061355357613553615496565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff000000000000000000000000000000000000000000000000000000000000016836135af83600261538a565b815181106135bf576135bf615496565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f0000000000000000000000000000000000000000000000000000000000000082168361361d83600261538a565b61362890600161547e565b8151811061363857613638615496565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101613539565b5090949350505050565b60606000806000613689856139f0565b9194509250905060008160018111156136a4576136a46155ce565b14613731576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610544565b61373b828461547e565b8551146137ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610544565b61248b8560200151848461445d565b606060208260000151106137f5576137f082613679565b610faf565b610faf826144fe565b6060610faf61381d836020015160008151811061289857612898615496565b6134de565b6060825182106138415750604080516020810190915260008152610faf565b6123918383848651613853919061510c565b614514565b6000806000835185511061386d578351613870565b84515b90505b80821080156138f7575083828151811061388f5761388f615496565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106138ce576138ce615496565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156134d657816001019150613873565b6000808211613972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b6060610faf6139eb836146ec565b6147d5565b600080600080846000015111613aae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b6020840151805160001a607f8111613ad3576000600160009450945094505050614456565b60b78111613ce1576000613ae860808361510c565b905080876000015111613ba3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610544565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613c1c57507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610544565b5060019550935060009250614456915050565b60bf811161402f576000613cf660b78361510c565b905080876000015111613db1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610544565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613e8f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c60378111613f53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610544565b613f5d818461547e565b895111614012576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610544565b61401d83600161547e565b97509550600094506144569350505050565b60f7811161411057600061404460c08361510c565b9050808760000151116140ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b600195509350849250614456915050565b600061411d60f78361510c565b9050808760000151116141d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610544565b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036142b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c6037811161437a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610544565b614384818461547e565b895111614439576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b61444483600161547e565b97509550600194506144569350505050565b9193909250565b606060008267ffffffffffffffff81111561447a5761447a614a39565b6040519080825280601f01601f1916602001820160405280156144a4576020820181803683370190505b509050826000036144b6579050612391565b60006144c2858761547e565b90506020820160005b858110156144e35782810151828201526020016144cb565b858111156144f2576000868301525b50919695505050505050565b6060610faf82602001516000846000015161445d565b60608182601f011015614583576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b8282840110156145ef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b8183018451101561465c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610544565b60608215801561467b57604051915060008252602082016040526146e3565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156146b457805183526020928301920161469c565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b604080518082019091526000808252602082015260008251116147b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b50604080518082019091528151815260209182019181019190915290565b606060008060006147e5856139f0565b919450925090506001816001811115614800576148006155ce565b1461488d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610544565b8451614899838561547e565b14614926576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610544565b6040805160208082526104208201909252600091816020015b604080518082019091526000808252602082015281526020019060019003908161493f5790505090506000845b8751811015614a2d576000806149b26040518060400160405280858d60000151614996919061510c565b8152602001858d602001516149ab919061547e565b90526139f0565b5091509150604051806040016040528083836149ce919061547e565b8152602001848c602001516149e3919061547e565b8152508585815181106149f8576149f8615496565b6020908102919091010152614a0e60018561547e565b9350614a1a818361547e565b614a24908461547e565b9250505061496c565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614aaf57614aaf614a39565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614adb57600080fd5b919050565b600082601f830112614af157600080fd5b813567ffffffffffffffff811115614b0b57614b0b614a39565b614b3c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614a68565b818152846020838601011115614b5157600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614b8057600080fd5b60405160c0810167ffffffffffffffff8282108183111715614ba457614ba4614a39565b8160405282935084358352614bbb60208601614ab7565b6020840152614bcc60408601614ab7565b6040840152606085013560608401526080850135608084015260a0850135915080821115614bf957600080fd5b50614c0685828601614ae0565b60a0830152505092915050565b600080600080600085870360e0811215614c2c57600080fd5b863567ffffffffffffffff80821115614c4457600080fd5b614c508a838b01614b6e565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614c8957600080fd5b60408901955060c0890135925080831115614ca357600080fd5b828901925089601f840112614cb757600080fd5b8235915080821115614cc857600080fd5b508860208260051b8401011115614cde57600080fd5b959894975092955050506020019190565b60005b83811015614d0a578181015183820152602001614cf2565b83811115614d19576000848401525b50505050565b60008151808452614d37816020860160208601614cef565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006123916020830184614d1f565b600060208284031215614d8e57600080fd5b5035919050565b600060208284031215614da757600080fd5b813567ffffffffffffffff811115614dbe57600080fd5b6120cb84828501614b6e565b80358015158114614adb57600080fd5b600060208284031215614dec57600080fd5b61239182614dca565b600080600080600060a08688031215614e0d57600080fd5b614e1686614ab7565b945060208601359350604086013567ffffffffffffffff8082168214614e3b57600080fd5b819450614e4a60608901614dca565b93506080880135915080821115614e6057600080fd5b50614e6d88828901614ae0565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614ece816049850160208701614cef565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614adb57600080fd5b600060608284031215614f1157600080fd5b6040516060810181811067ffffffffffffffff82111715614f3457614f34614a39565b60405282518152614f4760208401614edf565b6020820152614f5860408401614edf565b60408201529392505050565b600060808284031215614f7657600080fd5b6040516080810181811067ffffffffffffffff82111715614f9957614f99614a39565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614fe557614fe5614a39565b8360051b6020614ff6818301614a68565b86815291850191818101903684111561500e57600080fd5b865b84811015615042578035868111156150285760008081fd5b61503436828b01614ae0565b845250918301918301615010565b50979650505050505050565b60008451615060818460208901614cef565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161509c816001850160208a01614cef565b600192019182015283516150b7816002840160208801614cef565b0160020195945050505050565b6000602082840312156150d657600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561511e5761511e6150dd565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261516157615161615123565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156151b5576151b56150dd565b500590565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156151f4576151f46150dd565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615615228576152286150dd565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561526f5761526f6150dd565b7f800000000000000000000000000000000000000000000000000000000000000060008712868205881281841616156152aa576152aa6150dd565b600087129250878205871284841616156152c6576152c66150dd565b878505871281841616156152dc576152dc6150dd565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615615324576153246150dd565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615615358576153586150dd565b50500190565b600067ffffffffffffffff808316818516808303821115615381576153816150dd565b01949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153c2576153c26150dd565b500290565b6000826153d6576153d6615123565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261542660c0830184614d1f565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615463576154636150dd565b5060010190565b60008261547957615479615123565b500690565b60008219821115615491576154916150dd565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614adb57600080fd5b805160ff81168114614adb57600080fd5b600060c082840312156154fc57600080fd5b60405160c0810181811067ffffffffffffffff8211171561551f5761551f614a39565b60405261552b836154c5565b8152615539602084016154d9565b602082015261554a604084016154d9565b604082015261555b606084016154c5565b606082015261556c608084016154c5565b608082015261557d60a08401614edf565b60a08201529392505050565b600060ff83168061559c5761559c615123565b8060ff84160691505092915050565b600060ff821660ff8416808210156155c5576155c56150dd565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a", + Bin: "0x6101406040523480156200001257600080fd5b506040516200598238038062005982833981016040819052620000359162000296565b60016080819052600360a05260c0526001600160a01b0380851660e052838116610120528116610100526200006a8262000074565b5050505062000302565b600054610100900460ff1615808015620000955750600054600160ff909116105b80620000c55750620000b230620001cb60201b62001b9d1760201c565b158015620000c5575060005460ff166001145b6200012e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000152576000805461ff0019166101001790555b603280546001600160a01b03191661dead1790556035805483151560ff1990911617905562000180620001da565b8015620001c7576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6001600160a01b03163b151590565b600054610100900460ff16620002475760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000125565b60408051606081018252633b9aca0080825260006020830152436001600160401b031691909201819052600160c01b0217600155565b6001600160a01b03811681146200029357600080fd5b50565b60008060008060808587031215620002ad57600080fd5b8451620002ba816200027d565b6020860151909450620002cd816200027d565b60408601519093508015158114620002e457600080fd5b6060860151909250620002f7816200027d565b939692955090935050565b60805160a05160c05160e05161010051610120516155f1620003916000396000818161024e015281816106ca0152610fcd01526000818161047401526122de01526000818161014f0152818161093301528181610b1401528181610f29015281816112e30152818161155501526120ce01526000610e9401526000610e6b01526000610e4201526155f16000f3fe6080604052600436106101115760003560e01c80638b4c40b0116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c146103c3578063e9e05c421461044f578063f04987501461046257600080fd5b8063cff0ab9614610302578063d53a822f146103a357600080fd5b80638b4c40b0146101365780638c3152e9146102855780639bf62d82146102a5578063a14238e7146102d257600080fd5b80635c975abb116100e15780635c975abb146101f25780636dbffb781461021c578063724c184c1461023c5780638456cb591461027057600080fd5b80621c2ff61461013d5780633f4ba83a1461019b5780634870496f146101b057806354fd4d50146101d057600080fd5b36610138576101363334620186a0600060405180602001604052806000815250610496565b005b600080fd5b34801561014957600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101a757600080fd5b506101366106b2565b3480156101bc57600080fd5b506101366101cb366004614bfa565b6107d5565b3480156101dc57600080fd5b506101e5610e3b565b6040516101929190614d50565b3480156101fe57600080fd5b5060355461020c9060ff1681565b6040519015158152602001610192565b34801561022857600080fd5b5061020c610237366004614d63565b610ede565b34801561024857600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b34801561027c57600080fd5b50610136610fb5565b34801561029157600080fd5b506101366102a0366004614d7c565b6110d5565b3480156102b157600080fd5b506032546101719073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102de57600080fd5b5061020c6102ed366004614d63565b60336020526000908152604090205460ff1681565b34801561030e57600080fd5b5060015461036a906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610192565b3480156103af57600080fd5b506101366103be366004614dc1565b6119b0565b3480156103cf57600080fd5b506104216103de366004614d63565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff9283166020850152911690820152606001610192565b61013661045d366004614ddc565b610496565b34801561046e57600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b8260005a9050831561054d5773ffffffffffffffffffffffffffffffffffffffff87161561054d57604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6152088567ffffffffffffffff1610156105e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4f7074696d69736d506f7274616c3a20676173206c696d6974206d757374206360448201527f6f76657220696e737472696e7369632067617320636f737400000000000000006064820152608401610544565b3332811461060a575033731111000000000000000000000000000000001111015b60003488888888604051602001610625959493929190614e61565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516106959190614d50565b60405180910390a450506106a98282611bb9565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610777576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610901576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610544565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa15801561098f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b39190614ee6565b5190506109cd6109c836869003860186614f4b565b611edd565b8114610a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610544565b6000610a6687611f39565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610b985750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b949190614ee6565b5114155b610c24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610544565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610ced9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610ce3888a614fb1565b8a60400135611f69565b610d79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610544565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610e667f0000000000000000000000000000000000000000000000000000000000000000611f8d565b610e8f7f0000000000000000000000000000000000000000000000000000000000000000611f8d565b610eb87f0000000000000000000000000000000000000000000000000000000000000000611f8d565b604051602001610eca93929190615035565b604051602081830303815290604052905090565b6040517fa25ae55700000000000000000000000000000000000000000000000000000000815260048101829052600090610faf9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015610f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f949190614ee6565b602001516fffffffffffffffffffffffffffffffff166120ca565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461107a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016107cb565b60355460ff1615611142576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146111eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610544565b60006111f682611f39565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036112e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610544565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561134c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137091906150ab565b81602001516fffffffffffffffffffffffffffffffff16101561143b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610544565b61145a81602001516fffffffffffffffffffffffffffffffff166120ca565b61150c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610544565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa1580156115b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d59190614ee6565b825181519192501461168f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610544565b6116ae81602001516fffffffffffffffffffffffffffffffff166120ca565b611760576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610544565b60008381526033602052604090205460ff16156117ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610544565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a08801516118a19392919061216d565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b9061190690841515815260200190565b60405180910390a28015801561191c5750326001145b156119a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610544565b5050505050565b600054610100900460ff16158080156119d05750600054600160ff909116105b806119ea5750303b1580156119ea575060005460ff166001145b611a76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610544565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611ad457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611b366121c7565b8015611b9957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611bef907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16436150f3565b90506000611bfb6122aa565b90506000816020015160ff16826000015163ffffffff16611c1c9190615139565b90508215611d5357600154600090611c53908390700100000000000000000000000000000000900467ffffffffffffffff166151a1565b90506000836040015160ff1683611c6a9190615215565b600154611c8a9084906fffffffffffffffffffffffffffffffff16615215565b611c949190615139565b600154909150600090611ce590611cbe9084906fffffffffffffffffffffffffffffffff166152d1565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612370565b90506001861115611d1457611d11611cbe82876040015160ff1660018a611d0c91906150f3565b61238f565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611d86908490700100000000000000000000000000000000900467ffffffffffffffff16615345565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611e69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610544565b600154600090611e95906fffffffffffffffffffffffffffffffff1667ffffffffffffffff8816615371565b90506000611ea864012a05f200836153ae565b905060005a611eb790886150f3565b905080821115611ed357611ed3611ece82846150f3565b6123e4565b5050505050505050565b60008160000151826020015183604001518460600151604051602001611f1c949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097611f1c9790969591016153c2565b600080611f7586612412565b9050611f8381868686612444565b9695505050505050565b606081600003611fd057505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115611ffa5780611fe481615419565b9150611ff39050600a836153ae565b9150611fd4565b60008167ffffffffffffffff81111561201557612015614a20565b6040519080825280601f01601f19166020018201604052801561203f576020820181803683370190505b5090505b84156120c2576120546001836150f3565b9150612061600a86615451565b61206c906030615465565b60f81b8183815181106120815761208161547d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506120bb600a866153ae565b9450612043565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa158015612137573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215b91906150ab565b6121659083615465565b421192915050565b600080603f60c88601604002045a10156121b0576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080845160208601878a5af19695505050505050565b600054610100900460ff1661225e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610544565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612347573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236b91906154d1565b905090565b600061238561237f8585612474565b8361248b565b90505b9392505050565b6000670de0b6b3a76400006123d06123a78583615139565b6123b990670de0b6b3a76400006151a1565b6123cb85670de0b6b3a7640000615215565b61249a565b6123da9086615215565b6123859190615139565b6000805a90505b825a6123f790836150f3565b101561240d5761240682615419565b91506123eb565b505050565b6060818051906020012060405160200161242e91815260200190565b6040516020818303038152906040529050919050565b600061246b846124558786866124cb565b8051602091820120825192909101919091201490565b95945050505050565b6000818312156124845781612388565b5090919050565b60008183126124845781612388565b6000612388670de0b6b3a7640000836124b286612f53565b6124bc9190615215565b6124c69190615139565b613197565b60606000845111612538576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610544565b6000612543846133d6565b90506000612550866134c5565b905060008460405160200161256791815260200190565b60405160208183030381529060405290506000805b8451811015612eca5760008582815181106125995761259961547d565b602002602001015190508451831115612634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610544565b826000036126ed57805180516020918201206040516126829261265c92910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b6126e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610544565b612844565b8051516020116127a357805180516020918201206040516127179261265c92910190815260200190565b6126e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610544565b805184516020808701919091208251919092012014612844576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610544565b61285060106001615465565b81602001515103612a3157845183036129c957600061288c826020015160108151811061287f5761287f61547d565b6020026020010151613660565b9050600081511161291f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610544565b6001875161292d91906150f3565b83146129bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610544565b965061238895505050505050565b60008584815181106129dd576129dd61547d565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612a0857612a0861547d565b60200260200101519050612a1b816137c0565b9550612a28600186615465565b94505050612eb7565b600281602001515103612e2f576000612a49826137e5565b9050600081600081518110612a6057612a6061547d565b016020015160f81c90506000612a77600283615570565b612a82906002615592565b90506000612a93848360ff16613809565b90506000612aa18a89613809565b90506000612aaf838361383f565b905080835114612b41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610544565b60ff851660021480612b56575060ff85166003145b15612d4a5780825114612beb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610544565b6000612c07886020015160018151811061287f5761287f61547d565b90506000815111612c9a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610544565b60018d51612ca891906150f3565b8914612d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610544565b9c506123889b505050505050505050505050565b60ff85161580612d5d575060ff85166001145b15612d9c57612d898760200151600181518110612d7c57612d7c61547d565b60200260200101516137c0565b9950612d95818a615465565b9850612e24565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610544565b505050505050612eb7565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610544565b5080612ec281615419565b91505061257c565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610544565b6000808213612fbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b60006060612fcb846138ee565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136131c857506000919050565b680755bf798b4a1bf1e5821261323a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610544565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff8111156133f6576133f6614a20565b60405190808252806020026020018201604052801561343b57816020015b60408051808201909152606080825260208201528152602001906001900390816134145790505b50905060005b828110156134bd5760405180604001604052808683815181106134665761346661547d565b602002602001015181526020016134958784815181106134885761348861547d565b60200260200101516139c4565b8152508282815181106134aa576134aa61547d565b6020908102919091010152600101613441565b509392505050565b805160609060006134d7826002615371565b67ffffffffffffffff8111156134ef576134ef614a20565b6040519080825280601f01601f191660200182016040528015613519576020820181803683370190505b5090506000805b838110156136565785818151811061353a5761353a61547d565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff00000000000000000000000000000000000000000000000000000000000001683613596836002615371565b815181106135a6576135a661547d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f00000000000000000000000000000000000000000000000000000000000000821683613604836002615371565b61360f906001615465565b8151811061361f5761361f61547d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101613520565b5090949350505050565b60606000806000613670856139d7565b91945092509050600081600181111561368b5761368b6155b5565b14613718576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610544565b6137228284615465565b8551146137b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610544565b61246b85602001518484614444565b606060208260000151106137dc576137d782613660565b610faf565b610faf826144e5565b6060610faf613804836020015160008151811061287f5761287f61547d565b6134c5565b6060825182106138285750604080516020810190915260008152610faf565b612388838384865161383a91906150f3565b6144fb565b60008060008351855110613854578351613857565b84515b90505b80821080156138de57508382815181106138765761387661547d565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106138b5576138b561547d565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156134bd5781600101915061385a565b6000808211613959576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b6060610faf6139d2836146d3565b6147bc565b600080600080846000015111613a95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b6020840151805160001a607f8111613aba57600060016000945094509450505061443d565b60b78111613cc8576000613acf6080836150f3565b905080876000015111613b8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610544565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613c0357507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613cb5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610544565b506001955093506000925061443d915050565b60bf8111614016576000613cdd60b7836150f3565b905080876000015111613d98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610544565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613e76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c60378111613f3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610544565b613f448184615465565b895111613ff9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610544565b614004836001615465565b975095506000945061443d9350505050565b60f781116140f757600061402b60c0836150f3565b9050808760000151116140e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b60019550935084925061443d915050565b600061410460f7836150f3565b9050808760000151116141bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610544565b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361429d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c60378111614361576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610544565b61436b8184615465565b895111614420576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b61442b836001615465565b975095506001945061443d9350505050565b9193909250565b606060008267ffffffffffffffff81111561446157614461614a20565b6040519080825280601f01601f19166020018201604052801561448b576020820181803683370190505b5090508260000361449d579050612388565b60006144a98587615465565b90506020820160005b858110156144ca5782810151828201526020016144b2565b858111156144d9576000868301525b50919695505050505050565b6060610faf826020015160008460000151614444565b60608182601f01101561456a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b8282840110156145d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b81830184511015614643576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610544565b60608215801561466257604051915060008252602082016040526146ca565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561469b578051835260209283019201614683565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6040805180820190915260008082526020820152600082511161479e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b50604080518082019091528151815260209182019181019190915290565b606060008060006147cc856139d7565b9194509250905060018160018111156147e7576147e76155b5565b14614874576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610544565b84516148808385615465565b1461490d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610544565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816149265790505090506000845b8751811015614a14576000806149996040518060400160405280858d6000015161497d91906150f3565b8152602001858d602001516149929190615465565b90526139d7565b5091509150604051806040016040528083836149b59190615465565b8152602001848c602001516149ca9190615465565b8152508585815181106149df576149df61547d565b60209081029190910101526149f5600185615465565b9350614a018183615465565b614a0b9084615465565b92505050614953565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614a9657614a96614a20565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614ac257600080fd5b919050565b600082601f830112614ad857600080fd5b813567ffffffffffffffff811115614af257614af2614a20565b614b2360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614a4f565b818152846020838601011115614b3857600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614b6757600080fd5b60405160c0810167ffffffffffffffff8282108183111715614b8b57614b8b614a20565b8160405282935084358352614ba260208601614a9e565b6020840152614bb360408601614a9e565b6040840152606085013560608401526080850135608084015260a0850135915080821115614be057600080fd5b50614bed85828601614ac7565b60a0830152505092915050565b600080600080600085870360e0811215614c1357600080fd5b863567ffffffffffffffff80821115614c2b57600080fd5b614c378a838b01614b55565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614c7057600080fd5b60408901955060c0890135925080831115614c8a57600080fd5b828901925089601f840112614c9e57600080fd5b8235915080821115614caf57600080fd5b508860208260051b8401011115614cc557600080fd5b959894975092955050506020019190565b60005b83811015614cf1578181015183820152602001614cd9565b83811115614d00576000848401525b50505050565b60008151808452614d1e816020860160208601614cd6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006123886020830184614d06565b600060208284031215614d7557600080fd5b5035919050565b600060208284031215614d8e57600080fd5b813567ffffffffffffffff811115614da557600080fd5b6120c284828501614b55565b80358015158114614ac257600080fd5b600060208284031215614dd357600080fd5b61238882614db1565b600080600080600060a08688031215614df457600080fd5b614dfd86614a9e565b945060208601359350604086013567ffffffffffffffff8082168214614e2257600080fd5b819450614e3160608901614db1565b93506080880135915080821115614e4757600080fd5b50614e5488828901614ac7565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614eb5816049850160208701614cd6565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614ac257600080fd5b600060608284031215614ef857600080fd5b6040516060810181811067ffffffffffffffff82111715614f1b57614f1b614a20565b60405282518152614f2e60208401614ec6565b6020820152614f3f60408401614ec6565b60408201529392505050565b600060808284031215614f5d57600080fd5b6040516080810181811067ffffffffffffffff82111715614f8057614f80614a20565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614fcc57614fcc614a20565b8360051b6020614fdd818301614a4f565b868152918501918181019036841115614ff557600080fd5b865b848110156150295780358681111561500f5760008081fd5b61501b36828b01614ac7565b845250918301918301614ff7565b50979650505050505050565b60008451615047818460208901614cd6565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551615083816001850160208a01614cd6565b6001920191820152835161509e816002840160208801614cd6565b0160020195945050505050565b6000602082840312156150bd57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015615105576151056150c4565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826151485761514861510a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561519c5761519c6150c4565b500590565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156151db576151db6150c4565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561520f5761520f6150c4565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615615256576152566150c4565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615615291576152916150c4565b600087129250878205871284841616156152ad576152ad6150c4565b878505871281841616156152c3576152c36150c4565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561530b5761530b6150c4565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561533f5761533f6150c4565b50500190565b600067ffffffffffffffff808316818516808303821115615368576153686150c4565b01949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153a9576153a96150c4565b500290565b6000826153bd576153bd61510a565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261540d60c0830184614d06565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361544a5761544a6150c4565b5060010190565b6000826154605761546061510a565b500690565b60008219821115615478576154786150c4565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614ac257600080fd5b805160ff81168114614ac257600080fd5b600060c082840312156154e357600080fd5b60405160c0810181811067ffffffffffffffff8211171561550657615506614a20565b604052615512836154ac565b8152615520602084016154c0565b6020820152615531604084016154c0565b6040820152615542606084016154ac565b6060820152615553608084016154ac565b608082015261556460a08401614ec6565b60a08201529392505050565b600060ff8316806155835761558361510a565b8060ff84160691505092915050565b600060ff821660ff8416808210156155ac576155ac6150c4565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a", } // OptimismPortalABI is the input ABI used to generate the binding from. diff --git a/op-bindings/bindings/optimismportal_more.go b/op-bindings/bindings/optimismportal_more.go index c61f3f173d3ce..0940acffc8052 100644 --- a/op-bindings/bindings/optimismportal_more.go +++ b/op-bindings/bindings/optimismportal_more.go @@ -13,7 +13,7 @@ const OptimismPortalStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contrac var OptimismPortalStorageLayout = new(solc.StorageLayout) -var OptimismPortalDeployedBin = "0x6080604052600436106101115760003560e01c80638b4c40b0116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c146103c3578063e9e05c421461044f578063f04987501461046257600080fd5b8063cff0ab9614610302578063d53a822f146103a357600080fd5b80638b4c40b0146101365780638c3152e9146102855780639bf62d82146102a5578063a14238e7146102d257600080fd5b80635c975abb116100e15780635c975abb146101f25780636dbffb781461021c578063724c184c1461023c5780638456cb591461027057600080fd5b80621c2ff61461013d5780633f4ba83a1461019b5780634870496f146101b057806354fd4d50146101d057600080fd5b36610138576101363334620186a0600060405180602001604052806000815250610496565b005b600080fd5b34801561014957600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101a757600080fd5b506101366106b2565b3480156101bc57600080fd5b506101366101cb366004614c13565b6107d5565b3480156101dc57600080fd5b506101e5610e3b565b6040516101929190614d69565b3480156101fe57600080fd5b5060355461020c9060ff1681565b6040519015158152602001610192565b34801561022857600080fd5b5061020c610237366004614d7c565b610ede565b34801561024857600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b34801561027c57600080fd5b50610136610fb5565b34801561029157600080fd5b506101366102a0366004614d95565b6110d5565b3480156102b157600080fd5b506032546101719073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102de57600080fd5b5061020c6102ed366004614d7c565b60336020526000908152604090205460ff1681565b34801561030e57600080fd5b5060015461036a906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610192565b3480156103af57600080fd5b506101366103be366004614dda565b6119b0565b3480156103cf57600080fd5b506104216103de366004614d7c565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff9283166020850152911690820152606001610192565b61013661045d366004614df5565b610496565b34801561046e57600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b8260005a9050831561054d5773ffffffffffffffffffffffffffffffffffffffff87161561054d57604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6152088567ffffffffffffffff1610156105e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4f7074696d69736d506f7274616c3a20676173206c696d6974206d757374206360448201527f6f76657220696e737472696e7369632067617320636f737400000000000000006064820152608401610544565b3332811461060a575033731111000000000000000000000000000000001111015b60003488888888604051602001610625959493929190614e7a565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516106959190614d69565b60405180910390a450506106a98282611bb9565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610777576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610901576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610544565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa15801561098f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b39190614eff565b5190506109cd6109c836869003860186614f64565b611ee6565b8114610a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610544565b6000610a6687611f42565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610b985750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b949190614eff565b5114155b610c24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610544565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610ced9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610ce3888a614fca565b8a60400135611f72565b610d79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610544565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610e667f0000000000000000000000000000000000000000000000000000000000000000611f96565b610e8f7f0000000000000000000000000000000000000000000000000000000000000000611f96565b610eb87f0000000000000000000000000000000000000000000000000000000000000000611f96565b604051602001610eca9392919061504e565b604051602081830303815290604052905090565b6040517fa25ae55700000000000000000000000000000000000000000000000000000000815260048101829052600090610faf9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015610f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f949190614eff565b602001516fffffffffffffffffffffffffffffffff166120d3565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461107a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016107cb565b60355460ff1615611142576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146111eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610544565b60006111f682611f42565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036112e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610544565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561134c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137091906150c4565b81602001516fffffffffffffffffffffffffffffffff16101561143b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610544565b61145a81602001516fffffffffffffffffffffffffffffffff166120d3565b61150c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610544565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa1580156115b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d59190614eff565b825181519192501461168f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610544565b6116ae81602001516fffffffffffffffffffffffffffffffff166120d3565b611760576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610544565b60008381526033602052604090205460ff16156117ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610544565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a08801516118a193929190612176565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b9061190690841515815260200190565b60405180910390a28015801561191c5750326001145b156119a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610544565b5050505050565b600054610100900460ff16158080156119d05750600054600160ff909116105b806119ea5750303b1580156119ea575060005460ff166001145b611a76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610544565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611ad457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611b366121d0565b8015611b9957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611bef907801000000000000000000000000000000000000000000000000900467ffffffffffffffff164361510c565b90506000611bfb6122b3565b90506000816020015160ff16826000015163ffffffff16611c1c9190615152565b90508215611d5357600154600090611c53908390700100000000000000000000000000000000900467ffffffffffffffff166151ba565b90506000836040015160ff1683611c6a919061522e565b600154611c8a9084906fffffffffffffffffffffffffffffffff1661522e565b611c949190615152565b600154909150600090611ce590611cbe9084906fffffffffffffffffffffffffffffffff166152ea565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612379565b90506001861115611d1457611d11611cbe82876040015160ff1660018a611d0c919061510c565b612398565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611d86908490700100000000000000000000000000000000900467ffffffffffffffff1661535e565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611e69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610544565b600154600090611e95906fffffffffffffffffffffffffffffffff1667ffffffffffffffff881661538a565b90506000611ea748633b9aca006123ed565b611eb190836153c7565b905060005a611ec0908861510c565b905080821115611edc57611edc611ed7828461510c565b612404565b5050505050505050565b60008160000151826020015183604001518460600151604051602001611f25949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097611f259790969591016153db565b600080611f7e86612432565b9050611f8c81868686612464565b9695505050505050565b606081600003611fd957505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156120035780611fed81615432565b9150611ffc9050600a836153c7565b9150611fdd565b60008167ffffffffffffffff81111561201e5761201e614a39565b6040519080825280601f01601f191660200182016040528015612048576020820181803683370190505b5090505b84156120cb5761205d60018361510c565b915061206a600a8661546a565b61207590603061547e565b60f81b81838151811061208a5761208a615496565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506120c4600a866153c7565b945061204c565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa158015612140573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061216491906150c4565b61216e908361547e565b421192915050565b600080603f60c88601604002045a10156121b9576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080845160208601878a5af19695505050505050565b600054610100900460ff16612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610544565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612350573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061237491906154ea565b905090565b600061238e6123888585612494565b836124a4565b90505b9392505050565b6000670de0b6b3a76400006123d96123b08583615152565b6123c290670de0b6b3a76400006151ba565b6123d485670de0b6b3a764000061522e565b6124b3565b6123e3908661522e565b61238e9190615152565b6000818310156123fd5781612391565b5090919050565b6000805a90505b825a612417908361510c565b101561242d5761242682615432565b915061240b565b505050565b6060818051906020012060405160200161244e91815260200190565b6040516020818303038152906040529050919050565b600061248b846124758786866124e4565b8051602091820120825192909101919091201490565b95945050505050565b6000818312156123fd5781612391565b60008183126123fd5781612391565b6000612391670de0b6b3a7640000836124cb86612f6c565b6124d5919061522e565b6124df9190615152565b6131b0565b60606000845111612551576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610544565b600061255c846133ef565b90506000612569866134de565b905060008460405160200161258091815260200190565b60405160208183030381529060405290506000805b8451811015612ee35760008582815181106125b2576125b2615496565b60200260200101519050845183111561264d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610544565b82600003612706578051805160209182012060405161269b9261267592910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b612701576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610544565b61285d565b8051516020116127bc57805180516020918201206040516127309261267592910190815260200190565b612701576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610544565b80518451602080870191909120825191909201201461285d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610544565b6128696010600161547e565b81602001515103612a4a57845183036129e25760006128a5826020015160108151811061289857612898615496565b6020026020010151613679565b90506000815111612938576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610544565b60018751612946919061510c565b83146129d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610544565b965061239195505050505050565b60008584815181106129f6576129f6615496565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612a2157612a21615496565b60200260200101519050612a34816137d9565b9550612a4160018661547e565b94505050612ed0565b600281602001515103612e48576000612a62826137fe565b9050600081600081518110612a7957612a79615496565b016020015160f81c90506000612a90600283615589565b612a9b9060026155ab565b90506000612aac848360ff16613822565b90506000612aba8a89613822565b90506000612ac88383613858565b905080835114612b5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610544565b60ff851660021480612b6f575060ff85166003145b15612d635780825114612c04576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610544565b6000612c20886020015160018151811061289857612898615496565b90506000815111612cb3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610544565b60018d51612cc1919061510c565b8914612d4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610544565b9c506123919b505050505050505050505050565b60ff85161580612d76575060ff85166001145b15612db557612da28760200151600181518110612d9557612d95615496565b60200260200101516137d9565b9950612dae818a61547e565b9850612e3d565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610544565b505050505050612ed0565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610544565b5080612edb81615432565b915050612595565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610544565b6000808213612fd7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b60006060612fe484613907565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136131e157506000919050565b680755bf798b4a1bf1e58212613253576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610544565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff81111561340f5761340f614a39565b60405190808252806020026020018201604052801561345457816020015b604080518082019091526060808252602082015281526020019060019003908161342d5790505b50905060005b828110156134d657604051806040016040528086838151811061347f5761347f615496565b602002602001015181526020016134ae8784815181106134a1576134a1615496565b60200260200101516139dd565b8152508282815181106134c3576134c3615496565b602090810291909101015260010161345a565b509392505050565b805160609060006134f082600261538a565b67ffffffffffffffff81111561350857613508614a39565b6040519080825280601f01601f191660200182016040528015613532576020820181803683370190505b5090506000805b8381101561366f5785818151811061355357613553615496565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff000000000000000000000000000000000000000000000000000000000000016836135af83600261538a565b815181106135bf576135bf615496565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f0000000000000000000000000000000000000000000000000000000000000082168361361d83600261538a565b61362890600161547e565b8151811061363857613638615496565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101613539565b5090949350505050565b60606000806000613689856139f0565b9194509250905060008160018111156136a4576136a46155ce565b14613731576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610544565b61373b828461547e565b8551146137ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610544565b61248b8560200151848461445d565b606060208260000151106137f5576137f082613679565b610faf565b610faf826144fe565b6060610faf61381d836020015160008151811061289857612898615496565b6134de565b6060825182106138415750604080516020810190915260008152610faf565b6123918383848651613853919061510c565b614514565b6000806000835185511061386d578351613870565b84515b90505b80821080156138f7575083828151811061388f5761388f615496565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106138ce576138ce615496565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156134d657816001019150613873565b6000808211613972576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b6060610faf6139eb836146ec565b6147d5565b600080600080846000015111613aae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b6020840151805160001a607f8111613ad3576000600160009450945094505050614456565b60b78111613ce1576000613ae860808361510c565b905080876000015111613ba3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610544565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613c1c57507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613cce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610544565b5060019550935060009250614456915050565b60bf811161402f576000613cf660b78361510c565b905080876000015111613db1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610544565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613e8f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c60378111613f53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610544565b613f5d818461547e565b895111614012576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610544565b61401d83600161547e565b97509550600094506144569350505050565b60f7811161411057600061404460c08361510c565b9050808760000151116140ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b600195509350849250614456915050565b600061411d60f78361510c565b9050808760000151116141d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610544565b60018301517fff000000000000000000000000000000000000000000000000000000000000001660008190036142b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c6037811161437a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610544565b614384818461547e565b895111614439576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b61444483600161547e565b97509550600194506144569350505050565b9193909250565b606060008267ffffffffffffffff81111561447a5761447a614a39565b6040519080825280601f01601f1916602001820160405280156144a4576020820181803683370190505b509050826000036144b6579050612391565b60006144c2858761547e565b90506020820160005b858110156144e35782810151828201526020016144cb565b858111156144f2576000868301525b50919695505050505050565b6060610faf82602001516000846000015161445d565b60608182601f011015614583576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b8282840110156145ef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b8183018451101561465c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610544565b60608215801561467b57604051915060008252602082016040526146e3565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156146b457805183526020928301920161469c565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b604080518082019091526000808252602082015260008251116147b7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b50604080518082019091528151815260209182019181019190915290565b606060008060006147e5856139f0565b919450925090506001816001811115614800576148006155ce565b1461488d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610544565b8451614899838561547e565b14614926576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610544565b6040805160208082526104208201909252600091816020015b604080518082019091526000808252602082015281526020019060019003908161493f5790505090506000845b8751811015614a2d576000806149b26040518060400160405280858d60000151614996919061510c565b8152602001858d602001516149ab919061547e565b90526139f0565b5091509150604051806040016040528083836149ce919061547e565b8152602001848c602001516149e3919061547e565b8152508585815181106149f8576149f8615496565b6020908102919091010152614a0e60018561547e565b9350614a1a818361547e565b614a24908461547e565b9250505061496c565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614aaf57614aaf614a39565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614adb57600080fd5b919050565b600082601f830112614af157600080fd5b813567ffffffffffffffff811115614b0b57614b0b614a39565b614b3c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614a68565b818152846020838601011115614b5157600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614b8057600080fd5b60405160c0810167ffffffffffffffff8282108183111715614ba457614ba4614a39565b8160405282935084358352614bbb60208601614ab7565b6020840152614bcc60408601614ab7565b6040840152606085013560608401526080850135608084015260a0850135915080821115614bf957600080fd5b50614c0685828601614ae0565b60a0830152505092915050565b600080600080600085870360e0811215614c2c57600080fd5b863567ffffffffffffffff80821115614c4457600080fd5b614c508a838b01614b6e565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614c8957600080fd5b60408901955060c0890135925080831115614ca357600080fd5b828901925089601f840112614cb757600080fd5b8235915080821115614cc857600080fd5b508860208260051b8401011115614cde57600080fd5b959894975092955050506020019190565b60005b83811015614d0a578181015183820152602001614cf2565b83811115614d19576000848401525b50505050565b60008151808452614d37816020860160208601614cef565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006123916020830184614d1f565b600060208284031215614d8e57600080fd5b5035919050565b600060208284031215614da757600080fd5b813567ffffffffffffffff811115614dbe57600080fd5b6120cb84828501614b6e565b80358015158114614adb57600080fd5b600060208284031215614dec57600080fd5b61239182614dca565b600080600080600060a08688031215614e0d57600080fd5b614e1686614ab7565b945060208601359350604086013567ffffffffffffffff8082168214614e3b57600080fd5b819450614e4a60608901614dca565b93506080880135915080821115614e6057600080fd5b50614e6d88828901614ae0565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614ece816049850160208701614cef565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614adb57600080fd5b600060608284031215614f1157600080fd5b6040516060810181811067ffffffffffffffff82111715614f3457614f34614a39565b60405282518152614f4760208401614edf565b6020820152614f5860408401614edf565b60408201529392505050565b600060808284031215614f7657600080fd5b6040516080810181811067ffffffffffffffff82111715614f9957614f99614a39565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614fe557614fe5614a39565b8360051b6020614ff6818301614a68565b86815291850191818101903684111561500e57600080fd5b865b84811015615042578035868111156150285760008081fd5b61503436828b01614ae0565b845250918301918301615010565b50979650505050505050565b60008451615060818460208901614cef565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161509c816001850160208a01614cef565b600192019182015283516150b7816002840160208801614cef565b0160020195945050505050565b6000602082840312156150d657600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008282101561511e5761511e6150dd565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261516157615161615123565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f8000000000000000000000000000000000000000000000000000000000000000831416156151b5576151b56150dd565b500590565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156151f4576151f46150dd565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018313811615615228576152286150dd565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561526f5761526f6150dd565b7f800000000000000000000000000000000000000000000000000000000000000060008712868205881281841616156152aa576152aa6150dd565b600087129250878205871284841616156152c6576152c66150dd565b878505871281841616156152dc576152dc6150dd565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03841381151615615324576153246150dd565b827f8000000000000000000000000000000000000000000000000000000000000000038412811615615358576153586150dd565b50500190565b600067ffffffffffffffff808316818516808303821115615381576153816150dd565b01949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153c2576153c26150dd565b500290565b6000826153d6576153d6615123565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261542660c0830184614d1f565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203615463576154636150dd565b5060010190565b60008261547957615479615123565b500690565b60008219821115615491576154916150dd565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614adb57600080fd5b805160ff81168114614adb57600080fd5b600060c082840312156154fc57600080fd5b60405160c0810181811067ffffffffffffffff8211171561551f5761551f614a39565b60405261552b836154c5565b8152615539602084016154d9565b602082015261554a604084016154d9565b604082015261555b606084016154c5565b606082015261556c608084016154c5565b608082015261557d60a08401614edf565b60a08201529392505050565b600060ff83168061559c5761559c615123565b8060ff84160691505092915050565b600060ff821660ff8416808210156155c5576155c56150dd565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a" +var OptimismPortalDeployedBin = "0x6080604052600436106101115760003560e01c80638b4c40b0116100a5578063cff0ab9611610074578063e965084c11610059578063e965084c146103c3578063e9e05c421461044f578063f04987501461046257600080fd5b8063cff0ab9614610302578063d53a822f146103a357600080fd5b80638b4c40b0146101365780638c3152e9146102855780639bf62d82146102a5578063a14238e7146102d257600080fd5b80635c975abb116100e15780635c975abb146101f25780636dbffb781461021c578063724c184c1461023c5780638456cb591461027057600080fd5b80621c2ff61461013d5780633f4ba83a1461019b5780634870496f146101b057806354fd4d50146101d057600080fd5b36610138576101363334620186a0600060405180602001604052806000815250610496565b005b600080fd5b34801561014957600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101a757600080fd5b506101366106b2565b3480156101bc57600080fd5b506101366101cb366004614bfa565b6107d5565b3480156101dc57600080fd5b506101e5610e3b565b6040516101929190614d50565b3480156101fe57600080fd5b5060355461020c9060ff1681565b6040519015158152602001610192565b34801561022857600080fd5b5061020c610237366004614d63565b610ede565b34801561024857600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b34801561027c57600080fd5b50610136610fb5565b34801561029157600080fd5b506101366102a0366004614d7c565b6110d5565b3480156102b157600080fd5b506032546101719073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102de57600080fd5b5061020c6102ed366004614d63565b60336020526000908152604090205460ff1681565b34801561030e57600080fd5b5060015461036a906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610192565b3480156103af57600080fd5b506101366103be366004614dc1565b6119b0565b3480156103cf57600080fd5b506104216103de366004614d63565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff9283166020850152911690820152606001610192565b61013661045d366004614ddc565b610496565b34801561046e57600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b8260005a9050831561054d5773ffffffffffffffffffffffffffffffffffffffff87161561054d57604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b6152088567ffffffffffffffff1610156105e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4f7074696d69736d506f7274616c3a20676173206c696d6974206d757374206360448201527f6f76657220696e737472696e7369632067617320636f737400000000000000006064820152608401610544565b3332811461060a575033731111000000000000000000000000000000001111015b60003488888888604051602001610625959493929190614e61565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c32846040516106959190614d50565b60405180910390a450506106a98282611bb9565b50505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610777576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e20756e706175736500000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60355460ff1615610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610901576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e7472616374006064820152608401610544565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa15801561098f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109b39190614ee6565b5190506109cd6109c836869003860186614f4b565b611edd565b8114610a5b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f6600000000000000000000000000000000000000000000006064820152608401610544565b6000610a6687611f39565b6000818152603460209081526040918290208251606081018452815481526001909101546fffffffffffffffffffffffffffffffff8082169383018490527001000000000000000000000000000000009091041692810192909252919250901580610b985750805160408083015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa158015610b70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b949190614ee6565b5114155b610c24576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173682060448201527f68617320616c7265616479206265656e2070726f76656e0000000000000000006064820152608401610544565b60408051602081018490526000918101829052606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250610ced9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f0100000000000000000000000000000000000000000000000000000000000000602083015290610ce3888a614fb1565b8a60400135611f69565b610d79576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f6600000000000000000000000000006064820152608401610544565b604080516060810182528581526fffffffffffffffffffffffffffffffff42811660208084019182528c831684860190815260008981526034835286812095518655925190518416700100000000000000000000000000000000029316929092176001909301929092558b830151908c0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169186917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a4505050505050505050565b6060610e667f0000000000000000000000000000000000000000000000000000000000000000611f8d565b610e8f7f0000000000000000000000000000000000000000000000000000000000000000611f8d565b610eb87f0000000000000000000000000000000000000000000000000000000000000000611f8d565b604051602001610eca93929190615035565b604051602081830303815290604052905090565b6040517fa25ae55700000000000000000000000000000000000000000000000000000000815260048101829052600090610faf9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae55790602401606060405180830381865afa158015610f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f949190614ee6565b602001516fffffffffffffffffffffffffffffffff166120ca565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461107a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4f7074696d69736d506f7274616c3a206f6e6c7920677561726469616e20636160448201527f6e207061757365000000000000000000000000000000000000000000000000006064820152608401610544565b603580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258906020016107cb565b60355460ff1615611142576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f7074696d69736d506f7274616c3a20706175736564000000000000000000006044820152606401610544565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead146111eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e006064820152608401610544565b60006111f682611f39565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff808216948301859052700100000000000000000000000000000000909104169181019190915292935090036112e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e2079657400000000000000000000000000006064820152608401610544565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663887862726040518163ffffffff1660e01b8152600401602060405180830381865afa15801561134c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061137091906150ab565b81602001516fffffffffffffffffffffffffffffffff16101561143b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a401610544565b61145a81602001516fffffffffffffffffffffffffffffffff166120ca565b61150c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a401610544565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae55790602401606060405180830381865afa1580156115b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115d59190614ee6565b825181519192501461168f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a401610544565b6116ae81602001516fffffffffffffffffffffffffffffffff166120ca565b611760576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a401610544565b60008381526033602052604090205460ff16156117ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a656400000000000000000000006064820152608401610544565b600083815260336020908152604080832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055908601516032805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffffff00000000000000000000000000000000000000009092169190911790558501516080860151606087015160a08801516118a19392919061216d565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b9061190690841515815260200190565b60405180910390a28015801561191c5750326001145b156119a9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4f7074696d69736d506f7274616c3a207769746864726177616c206661696c6560448201527f64000000000000000000000000000000000000000000000000000000000000006064820152608401610544565b5050505050565b600054610100900460ff16158080156119d05750600054600160ff909116105b806119ea5750303b1580156119ea575060005460ff166001145b611a76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610544565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015611ad457600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055603580548315157fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116179055611b366121c7565b8015611b9957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b600154600090611bef907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16436150f3565b90506000611bfb6122aa565b90506000816020015160ff16826000015163ffffffff16611c1c9190615139565b90508215611d5357600154600090611c53908390700100000000000000000000000000000000900467ffffffffffffffff166151a1565b90506000836040015160ff1683611c6a9190615215565b600154611c8a9084906fffffffffffffffffffffffffffffffff16615215565b611c949190615139565b600154909150600090611ce590611cbe9084906fffffffffffffffffffffffffffffffff166152d1565b866060015163ffffffff168760a001516fffffffffffffffffffffffffffffffff16612370565b90506001861115611d1457611d11611cbe82876040015160ff1660018a611d0c91906150f3565b61238f565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054869190601090611d86908490700100000000000000000000000000000000900467ffffffffffffffff16615345565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550816000015163ffffffff16600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315611e69576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d697400006064820152608401610544565b600154600090611e95906fffffffffffffffffffffffffffffffff1667ffffffffffffffff8816615371565b90506000611ea864012a05f200836153ae565b905060005a611eb790886150f3565b905080821115611ed357611ed3611ece82846150f3565b6123e4565b5050505050505050565b60008160000151826020015183604001518460600151604051602001611f1c949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a08801519351600097611f1c9790969591016153c2565b600080611f7586612412565b9050611f8381868686612444565b9695505050505050565b606081600003611fd057505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115611ffa5780611fe481615419565b9150611ff39050600a836153ae565b9150611fd4565b60008167ffffffffffffffff81111561201557612015614a20565b6040519080825280601f01601f19166020018201604052801561203f576020820181803683370190505b5090505b84156120c2576120546001836150f3565b9150612061600a86615451565b61206c906030615465565b60f81b8183815181106120815761208161547d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506120bb600a866153ae565b9450612043565b949350505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663f4daa2916040518163ffffffff1660e01b8152600401602060405180830381865afa158015612137573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061215b91906150ab565b6121659083615465565b421192915050565b600080603f60c88601604002045a10156121b0576308c379a06000526020805278185361666543616c6c3a204e6f7420656e6f756768206761736058526064601cfd5b600080845160208601878a5af19695505050505050565b600054610100900460ff1661225e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610544565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663cc731b026040518163ffffffff1660e01b815260040160c060405180830381865afa158015612347573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061236b91906154d1565b905090565b600061238561237f8585612474565b8361248b565b90505b9392505050565b6000670de0b6b3a76400006123d06123a78583615139565b6123b990670de0b6b3a76400006151a1565b6123cb85670de0b6b3a7640000615215565b61249a565b6123da9086615215565b6123859190615139565b6000805a90505b825a6123f790836150f3565b101561240d5761240682615419565b91506123eb565b505050565b6060818051906020012060405160200161242e91815260200190565b6040516020818303038152906040529050919050565b600061246b846124558786866124cb565b8051602091820120825192909101919091201490565b95945050505050565b6000818312156124845781612388565b5090919050565b60008183126124845781612388565b6000612388670de0b6b3a7640000836124b286612f53565b6124bc9190615215565b6124c69190615139565b613197565b60606000845111612538576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d65726b6c65547269653a20656d707479206b657900000000000000000000006044820152606401610544565b6000612543846133d6565b90506000612550866134c5565b905060008460405160200161256791815260200190565b60405160208183030381529060405290506000805b8451811015612eca5760008582815181106125995761259961547d565b602002602001015190508451831115612634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4d65726b6c65547269653a206b657920696e646578206578636565647320746f60448201527f74616c206b6579206c656e6774680000000000000000000000000000000000006064820152608401610544565b826000036126ed57805180516020918201206040516126829261265c92910190815260200190565b604051602081830303815290604052858051602091820120825192909101919091201490565b6126e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f7420686173680000006044820152606401610544565b612844565b8051516020116127a357805180516020918201206040516127179261265c92910190815260200190565b6126e8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c2068617368000000000000000000000000000000000000000000000000006064820152608401610544565b805184516020808701919091208251919092012014612844576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f65206861736800000000000000000000000000000000000000000000000000006064820152608401610544565b61285060106001615465565b81602001515103612a3157845183036129c957600061288c826020015160108151811061287f5761287f61547d565b6020026020010151613660565b9050600081511161291f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603b60248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286272616e63682900000000006064820152608401610544565b6001875161292d91906150f3565b83146129bb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286272616e6368290000000000006064820152608401610544565b965061238895505050505050565b60008584815181106129dd576129dd61547d565b602001015160f81c60f81b60f81c9050600082602001518260ff1681518110612a0857612a0861547d565b60200260200101519050612a1b816137c0565b9550612a28600186615465565b94505050612eb7565b600281602001515103612e2f576000612a49826137e5565b9050600081600081518110612a6057612a6061547d565b016020015160f81c90506000612a77600283615570565b612a82906002615592565b90506000612a93848360ff16613809565b90506000612aa18a89613809565b90506000612aaf838361383f565b905080835114612b41576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f4d65726b6c65547269653a20706174682072656d61696e646572206d7573742060448201527f736861726520616c6c206e6962626c65732077697468206b65790000000000006064820152608401610544565b60ff851660021480612b56575060ff85166003145b15612d4a5780825114612beb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603d60248201527f4d65726b6c65547269653a206b65792072656d61696e646572206d757374206260448201527f65206964656e746963616c20746f20706174682072656d61696e6465720000006064820152608401610544565b6000612c07886020015160018151811061287f5761287f61547d565b90506000815111612c9a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a2076616c7565206c656e677468206d75737420626560448201527f2067726561746572207468616e207a65726f20286c65616629000000000000006064820152608401610544565b60018d51612ca891906150f3565b8914612d36576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f4d65726b6c65547269653a2076616c7565206e6f6465206d757374206265206c60448201527f617374206e6f646520696e2070726f6f6620286c6561662900000000000000006064820152608401610544565b9c506123889b505050505050505050505050565b60ff85161580612d5d575060ff85166001145b15612d9c57612d898760200151600181518110612d7c57612d7c61547d565b60200260200101516137c0565b9950612d95818a615465565b9850612e24565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e2070726566697800000000000000000000000000006064820152608401610544565b505050505050612eb7565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f64650000000000000000000000000000000000000000000000006064820152608401610544565b5080612ec281615419565b91505061257c565b506040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2072616e206f7574206f662070726f6f6620656c6560448201527f6d656e74730000000000000000000000000000000000000000000000000000006064820152608401610544565b6000808213612fbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b60006060612fcb846138ee565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c182136131c857506000919050565b680755bf798b4a1bf1e5821261323a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f5700000000000000000000000000000000000000006044820152606401610544565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b805160609060008167ffffffffffffffff8111156133f6576133f6614a20565b60405190808252806020026020018201604052801561343b57816020015b60408051808201909152606080825260208201528152602001906001900390816134145790505b50905060005b828110156134bd5760405180604001604052808683815181106134665761346661547d565b602002602001015181526020016134958784815181106134885761348861547d565b60200260200101516139c4565b8152508282815181106134aa576134aa61547d565b6020908102919091010152600101613441565b509392505050565b805160609060006134d7826002615371565b67ffffffffffffffff8111156134ef576134ef614a20565b6040519080825280601f01601f191660200182016040528015613519576020820181803683370190505b5090506000805b838110156136565785818151811061353a5761353a61547d565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff00000000000000000000000000000000000000000000000000000000000001683613596836002615371565b815181106135a6576135a661547d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f00000000000000000000000000000000000000000000000000000000000000821683613604836002615371565b61360f906001615465565b8151811061361f5761361f61547d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101613520565b5090949350505050565b60606000806000613670856139d7565b91945092509050600081600181111561368b5761368b6155b5565b14613718576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d000000000000006064820152608401610544565b6137228284615465565b8551146137b1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e6465720000000000000000000000006064820152608401610544565b61246b85602001518484614444565b606060208260000151106137dc576137d782613660565b610faf565b610faf826144e5565b6060610faf613804836020015160008151811061287f5761287f61547d565b6134c5565b6060825182106138285750604080516020810190915260008152610faf565b612388838384865161383a91906150f3565b6144fb565b60008060008351855110613854578351613857565b84515b90505b80821080156138de57508382815181106138765761387661547d565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106138b5576138b561547d565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b156134bd5781600101915061385a565b6000808211613959576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e454400000000000000000000000000000000000000000000006044820152606401610544565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b6060610faf6139d2836146d3565b6147bc565b600080600080846000015111613a95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b6020840151805160001a607f8111613aba57600060016000945094509450505061443d565b60b78111613cc8576000613acf6080836150f3565b905080876000015111613b8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a401610544565b6001838101517fff00000000000000000000000000000000000000000000000000000000000000169082141580613c0357507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b613cb5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a401610544565b506001955093506000925061443d915050565b60bf8111614016576000613cdd60b7836150f3565b905080876000015111613d98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a401610544565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613e76576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c60378111613f3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a401610544565b613f448184615465565b895111613ff9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a401610544565b614004836001615465565b975095506000945061443d9350505050565b60f781116140f757600061402b60c0836150f3565b9050808760000151116140e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b60019550935084925061443d915050565b600061410460f7836150f3565b9050808760000151116141bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a401610544565b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361429d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a401610544565b600184015160088302610100031c60378111614361576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a401610544565b61436b8184615465565b895111614420576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a401610544565b61442b836001615465565b975095506001945061443d9350505050565b9193909250565b606060008267ffffffffffffffff81111561446157614461614a20565b6040519080825280601f01601f19166020018201604052801561448b576020820181803683370190505b5090508260000361449d579050612388565b60006144a98587615465565b90506020820160005b858110156144ca5782810151828201526020016144b2565b858111156144d9576000868301525b50919695505050505050565b6060610faf826020015160008460000151614444565b60608182601f01101561456a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b8282840110156145d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401610544565b81830184511015614643576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401610544565b60608215801561466257604051915060008252602082016040526146ca565b6040519150601f8416801560200281840101858101878315602002848b0101015b8183101561469b578051835260209283019201614683565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6040805180820190915260008082526020820152600082511161479e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a401610544565b50604080518082019091528151815260209182019181019190915290565b606060008060006147cc856139d7565b9194509250905060018160018111156147e7576147e76155b5565b14614874576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d00000000000000006064820152608401610544565b84516148808385615465565b1461490d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e64657200000000000000000000000000006064820152608401610544565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816149265790505090506000845b8751811015614a14576000806149996040518060400160405280858d6000015161497d91906150f3565b8152602001858d602001516149929190615465565b90526139d7565b5091509150604051806040016040528083836149b59190615465565b8152602001848c602001516149ca9190615465565b8152508585815181106149df576149df61547d565b60209081029190910101526149f5600185615465565b9350614a018183615465565b614a0b9084615465565b92505050614953565b50815295945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614a9657614a96614a20565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114614ac257600080fd5b919050565b600082601f830112614ad857600080fd5b813567ffffffffffffffff811115614af257614af2614a20565b614b2360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614a4f565b818152846020838601011115614b3857600080fd5b816020850160208301376000918101602001919091529392505050565b600060c08284031215614b6757600080fd5b60405160c0810167ffffffffffffffff8282108183111715614b8b57614b8b614a20565b8160405282935084358352614ba260208601614a9e565b6020840152614bb360408601614a9e565b6040840152606085013560608401526080850135608084015260a0850135915080821115614be057600080fd5b50614bed85828601614ac7565b60a0830152505092915050565b600080600080600085870360e0811215614c1357600080fd5b863567ffffffffffffffff80821115614c2b57600080fd5b614c378a838b01614b55565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc084011215614c7057600080fd5b60408901955060c0890135925080831115614c8a57600080fd5b828901925089601f840112614c9e57600080fd5b8235915080821115614caf57600080fd5b508860208260051b8401011115614cc557600080fd5b959894975092955050506020019190565b60005b83811015614cf1578181015183820152602001614cd9565b83811115614d00576000848401525b50505050565b60008151808452614d1e816020860160208601614cd6565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006123886020830184614d06565b600060208284031215614d7557600080fd5b5035919050565b600060208284031215614d8e57600080fd5b813567ffffffffffffffff811115614da557600080fd5b6120c284828501614b55565b80358015158114614ac257600080fd5b600060208284031215614dd357600080fd5b61238882614db1565b600080600080600060a08688031215614df457600080fd5b614dfd86614a9e565b945060208601359350604086013567ffffffffffffffff8082168214614e2257600080fd5b819450614e3160608901614db1565b93506080880135915080821115614e4757600080fd5b50614e5488828901614ac7565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251614eb5816049850160208701614cd6565b919091016049019695505050505050565b80516fffffffffffffffffffffffffffffffff81168114614ac257600080fd5b600060608284031215614ef857600080fd5b6040516060810181811067ffffffffffffffff82111715614f1b57614f1b614a20565b60405282518152614f2e60208401614ec6565b6020820152614f3f60408401614ec6565b60408201529392505050565b600060808284031215614f5d57600080fd5b6040516080810181811067ffffffffffffffff82111715614f8057614f80614a20565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff80841115614fcc57614fcc614a20565b8360051b6020614fdd818301614a4f565b868152918501918181019036841115614ff557600080fd5b865b848110156150295780358681111561500f5760008081fd5b61501b36828b01614ac7565b845250918301918301614ff7565b50979650505050505050565b60008451615047818460208901614cd6565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551615083816001850160208a01614cd6565b6001920191820152835161509e816002840160208801614cd6565b0160020195945050505050565b6000602082840312156150bd57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015615105576151056150c4565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000826151485761514861510a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561519c5761519c6150c4565b500590565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156151db576151db6150c4565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561520f5761520f6150c4565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600084136000841385830485118282161615615256576152566150c4565b7f80000000000000000000000000000000000000000000000000000000000000006000871286820588128184161615615291576152916150c4565b600087129250878205871284841616156152ad576152ad6150c4565b878505871281841616156152c3576152c36150c4565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561530b5761530b6150c4565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561533f5761533f6150c4565b50500190565b600067ffffffffffffffff808316818516808303821115615368576153686150c4565b01949350505050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156153a9576153a96150c4565b500290565b6000826153bd576153bd61510a565b500490565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a083015261540d60c0830184614d06565b98975050505050505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361544a5761544a6150c4565b5060010190565b6000826154605761546061510a565b500690565b60008219821115615478576154786150c4565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b805163ffffffff81168114614ac257600080fd5b805160ff81168114614ac257600080fd5b600060c082840312156154e357600080fd5b60405160c0810181811067ffffffffffffffff8211171561550657615506614a20565b604052615512836154ac565b8152615520602084016154c0565b6020820152615531604084016154c0565b6040820152615542606084016154ac565b6060820152615553608084016154ac565b608082015261556460a08401614ec6565b60a08201529392505050565b600060ff8316806155835761558361510a565b8060ff84160691505092915050565b600060ff821660ff8416808210156155ac576155ac6150c4565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a" func init() { if err := json.Unmarshal([]byte(OptimismPortalStorageLayoutJSON), OptimismPortalStorageLayout); err != nil { diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index e0915880bdfe0..ff1c016a9ce2a 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -20,6 +20,8 @@ import ( "github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var ( @@ -430,8 +432,10 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage if block.Number() == nil { return storage, errors.New("block number not set") } - if block.BaseFee() == nil { - return storage, errors.New("block base fee not set") + if !opservice.ForBSC { + if block.BaseFee() == nil { + return storage, errors.New("block base fee not set") + } } storage["L2ToL1MessagePasser"] = state.StorageValues{ @@ -444,8 +448,8 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage "msgNonce": 0, } storage["L1Block"] = state.StorageValues{ - "number": block.Number(), - "timestamp": block.Time(), + "number": block.Number(), + "timestamp": block.Time(), "basefee": block.BaseFee(), "hash": block.Hash(), "sequenceNumber": 0, @@ -453,6 +457,9 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage "l1FeeOverhead": config.GasPriceOracleOverhead, "l1FeeScalar": config.GasPriceOracleScalar, } + if opservice.ForBSC { + storage["L1Block"]["basefee"] = derive.BSCFakeBaseFee + } storage["LegacyERC20ETH"] = state.StorageValues{ "_name": "Ether", "_symbol": "ETH", diff --git a/op-e2e/actions/action.go b/op-e2e/actions/action.go index 53b710a44e210..50ffadb35147d 100644 --- a/op-e2e/actions/action.go +++ b/op-e2e/actions/action.go @@ -5,6 +5,7 @@ import ( "os" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var enableParallelTesting bool = true @@ -13,6 +14,7 @@ func init() { if os.Getenv("OP_E2E_DISABLE_PARALLEL") == "true" { enableParallelTesting = false } + opservice.ForBSC = false } func parallel(t e2eutils.TestingBase) { diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index 96bb3b794e91b..a60b1b5cf5d02 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -36,6 +36,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/sources" "github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/withdrawals" + opservice "github.com/ethereum-optimism/optimism/op-service" oppprof "github.com/ethereum-optimism/optimism/op-service/pprof" ) @@ -55,6 +56,7 @@ func init() { if os.Getenv("OP_E2E_DISABLE_PARALLEL") == "true" { enableParallelTesting = false } + opservice.ForBSC = false } func parallel(t *testing.T) { diff --git a/op-node/eth/heads.go b/op-node/eth/heads.go index af92990f0f1d6..2bd70c406220b 100644 --- a/op-node/eth/heads.go +++ b/op-node/eth/heads.go @@ -8,6 +8,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" + + opservice "github.com/ethereum-optimism/optimism/op-service" ) // HeadSignalFn is used as callback function to accept head-signals @@ -48,8 +50,11 @@ func WatchHeadChanges(ctx context.Context, src NewHeadSource, fn HeadSignalFn) ( type L1BlockRefsSource interface { L1BlockRefByLabel(ctx context.Context, label BlockLabel) (L1BlockRef, error) + L1BlockRefByNumber(ctx context.Context, num uint64) (L1BlockRef, error) } +var finalizedBlockNumberForBSC uint64 = 15 + // PollBlockChanges opens a polling loop to fetch the L1 block reference with the given label, // on provided interval and with request timeout. Results are returned with provided callback fn, // which may block to pause/back-pressure polling. @@ -67,12 +72,32 @@ func PollBlockChanges(ctx context.Context, log log.Logger, src L1BlockRefsSource select { case <-ticker.C: reqCtx, reqCancel := context.WithTimeout(ctx, timeout) + if opservice.ForBSC { + label = Unsafe + } ref, err := src.L1BlockRefByLabel(reqCtx, label) reqCancel() if err != nil { log.Warn("failed to poll L1 block", "label", label, "err", err) } else { - fn(ctx, ref) + if opservice.ForBSC { + reqCtx, reqCancel := context.WithTimeout(ctx, timeout) + number := ref.Number + if number < finalizedBlockNumberForBSC { + number = 0 + } else { + number -= finalizedBlockNumberForBSC + } + ref, err := src.L1BlockRefByNumber(reqCtx, number) + reqCancel() + if err != nil { + log.Warn("failed to poll L1 block", "number", number, "err", err) + } else { + fn(ctx, ref) + } + } else { + fn(ctx, ref) + } } case <-ctx.Done(): return ctx.Err() diff --git a/op-node/node/api.go b/op-node/node/api.go index 46fef276251d8..ede83051fa879 100644 --- a/op-node/node/api.go +++ b/op-node/node/api.go @@ -27,6 +27,7 @@ type driverClient interface { SyncStatus(ctx context.Context) (*eth.SyncStatus, error) BlockRefWithStatus(ctx context.Context, num uint64) (eth.L2BlockRef, *eth.SyncStatus, error) ResetDerivationPipeline(context.Context) error + SequencerStopped(ctx context.Context) bool StartSequencer(ctx context.Context, blockHash common.Hash) error StopSequencer(context.Context) (common.Hash, error) } @@ -54,6 +55,10 @@ func (n *adminAPI) ResetDerivationPipeline(ctx context.Context) error { return n.dr.ResetDerivationPipeline(ctx) } +func (n *adminAPI) SequencerStopped(ctx context.Context) bool { + return n.dr.SequencerStopped(ctx) +} + func (n *adminAPI) StartSequencer(ctx context.Context, blockHash common.Hash) error { recordDur := n.m.RecordRPCServerRequest("admin_startSequencer") defer recordDur() diff --git a/op-node/rollup/derive/attributes.go b/op-node/rollup/derive/attributes.go index 9383c642c901a..361321186aa7e 100644 --- a/op-node/rollup/derive/attributes.go +++ b/op-node/rollup/derive/attributes.go @@ -3,6 +3,7 @@ package derive import ( "context" "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -11,12 +12,14 @@ import ( "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/rollup" + opservice "github.com/ethereum-optimism/optimism/op-service" ) // L1ReceiptsFetcher fetches L1 header info and receipts for the payload attributes derivation (the info tx and deposits) type L1ReceiptsFetcher interface { InfoByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, error) FetchReceipts(ctx context.Context, blockHash common.Hash) (eth.BlockInfo, types.Receipts, error) + InfoAndTxsByHash(ctx context.Context, hash common.Hash) (eth.BlockInfo, types.Transactions, error) } type SystemConfigL2Fetcher interface { @@ -53,6 +56,14 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex return nil, NewTemporaryError(fmt.Errorf("failed to retrieve L2 parent block: %w", err)) } + var gasPrice *big.Int + if opservice.ForBSC { + gasPrice, err = ba.prepareAverageGasPrice(ctx, epoch) + if err != nil { + return nil, NewTemporaryError(fmt.Errorf("failed to prepare average gas price: %w", err)) + } + } + // If the L1 origin changed this block, then we are in the first block of the epoch. In this // case we need to fetch all transaction receipts from the L1 origin block so we can scan for // user deposits. @@ -76,7 +87,9 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex if err := UpdateSystemConfigWithL1Receipts(&sysConfig, receipts, ba.cfg); err != nil { return nil, NewCriticalError(fmt.Errorf("failed to apply derived L1 sysCfg updates: %w", err)) } - + if opservice.ForBSC { + info = newGasPriceWrapper(info, gasPrice) + } l1Info = info depositTxs = deposits seqNumber = 0 @@ -88,6 +101,9 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex if err != nil { return nil, NewTemporaryError(fmt.Errorf("failed to fetch L1 block info: %w", err)) } + if opservice.ForBSC { + info = newGasPriceWrapper(info, gasPrice) + } l1Info = info depositTxs = nil seqNumber = l2Parent.SequenceNumber + 1 @@ -118,3 +134,43 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex GasLimit: (*eth.Uint64Quantity)(&sysConfig.GasLimit), }, nil } + +var bscDefaultGasPrice = big.NewInt(5000000000) + +func (ba *FetchingAttributesBuilder) prepareAverageGasPrice(ctx context.Context, epoch eth.BlockID) (*big.Int, error) { + _, txs, err := ba.l1.InfoAndTxsByHash(ctx, epoch.Hash) + if err != nil { + return nil, err + } + count := 0 + var sum big.Int + for _, tx := range txs { + if tx.GasPrice().Cmp(common.Big0) <= 0 { + continue + } + sum.Add(&sum, tx.GasPrice()) + count += 1 + } + if count == 0 { + return bscDefaultGasPrice, nil + } + return sum.Div(&sum, big.NewInt(int64(count))), nil +} + +type gasPriceWrapper struct { + eth.BlockInfo + gasprice *big.Int +} + +var _ (eth.BlockInfo) = (*gasPriceWrapper)(nil) + +func newGasPriceWrapper(info eth.BlockInfo, gasprice *big.Int) *gasPriceWrapper { + return &gasPriceWrapper{ + BlockInfo: info, + gasprice: gasprice, + } +} + +func (w *gasPriceWrapper) BaseFee() *big.Int { + return w.gasprice +} diff --git a/op-node/rollup/derive/attributes_queue_test.go b/op-node/rollup/derive/attributes_queue_test.go index d6f2f5b3449f0..3b93a164412d7 100644 --- a/op-node/rollup/derive/attributes_queue_test.go +++ b/op-node/rollup/derive/attributes_queue_test.go @@ -16,12 +16,14 @@ import ( "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testutils" + opservice "github.com/ethereum-optimism/optimism/op-service" ) // TestAttributesQueue checks that it properly uses the PreparePayloadAttributes function // (which is well tested) and that it properly sets NoTxPool and adds in the candidate // transactions. func TestAttributesQueue(t *testing.T) { + opservice.ForBSC = false // test config, only init the necessary fields cfg := &rollup.Config{ BlockTime: 2, diff --git a/op-node/rollup/derive/l1_block_info.go b/op-node/rollup/derive/l1_block_info.go index 0e4733d710692..7c45ac08571db 100644 --- a/op-node/rollup/derive/l1_block_info.go +++ b/op-node/rollup/derive/l1_block_info.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-node/eth" + opservice "github.com/ethereum-optimism/optimism/op-service" ) const ( @@ -54,11 +55,13 @@ func (info *L1BlockInfo) MarshalBinary() ([]byte, error) { offset += 32 binary.BigEndian.PutUint64(data[offset+24:offset+32], info.Time) offset += 32 - // Ensure that the baseFee is not too large. - if info.BaseFee.BitLen() > 256 { - return nil, fmt.Errorf("base fee exceeds 256 bits: %d", info.BaseFee) + if info.BaseFee != nil { + // Ensure that the baseFee is not too large. + if info.BaseFee.BitLen() > 256 { + return nil, fmt.Errorf("base fee exceeds 256 bits: %d", info.BaseFee) + } + info.BaseFee.FillBytes(data[offset : offset+32]) } - info.BaseFee.FillBytes(data[offset : offset+32]) offset += 32 copy(data[offset:offset+32], info.BlockHash.Bytes()) offset += 32 @@ -117,6 +120,8 @@ func L1InfoDepositTxData(data []byte) (L1BlockInfo, error) { return info, err } +var BSCFakeBaseFee = big.NewInt(5000000000) + // L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block, // and the L2 block-height difference with the start of the epoch. func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfig, regolith bool) (*types.DepositTx, error) { @@ -130,6 +135,9 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi L1FeeOverhead: sysCfg.Overhead, L1FeeScalar: sysCfg.Scalar, } + if opservice.ForBSC { + infoDat.BaseFee = BSCFakeBaseFee + } data, err := infoDat.MarshalBinary() if err != nil { return nil, err diff --git a/op-node/rollup/derive/l1_block_info_test.go b/op-node/rollup/derive/l1_block_info_test.go index 721046178eeed..7a841e5b5f414 100644 --- a/op-node/rollup/derive/l1_block_info_test.go +++ b/op-node/rollup/derive/l1_block_info_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/testutils" + opservice "github.com/ethereum-optimism/optimism/op-service" ) var _ eth.BlockInfo = (*testutils.MockBlockInfo)(nil) @@ -35,6 +36,7 @@ func randomL1Cfg(rng *rand.Rand, l1Info eth.BlockInfo) eth.SystemConfig { var MockDepositContractAddr = common.HexToAddress("0xdeadbeefdeadbeefdeadbeefdeadbeef00000000") func TestParseL1InfoDepositTxData(t *testing.T) { + opservice.ForBSC = false randomSeqNr := func(rng *rand.Rand) uint64 { return rng.Uint64() } diff --git a/op-node/rollup/driver/state.go b/op-node/rollup/driver/state.go index 79fd55f2af9a2..51a8923651b74 100644 --- a/op-node/rollup/driver/state.go +++ b/op-node/rollup/driver/state.go @@ -371,6 +371,10 @@ func (s *Driver) ResetDerivationPipeline(ctx context.Context) error { } } +func (s *Driver) SequencerStopped(_ctx context.Context) bool { + return s.driverConfig.SequencerStopped +} + func (s *Driver) StartSequencer(ctx context.Context, blockHash common.Hash) error { if !s.driverConfig.SequencerEnabled { return errors.New("sequencer is not enabled") diff --git a/op-proposer/proposer/abi_test.go b/op-proposer/proposer/abi_test.go index 75b7d49950389..b091536c2264d 100644 --- a/op-proposer/proposer/abi_test.go +++ b/op-proposer/proposer/abi_test.go @@ -67,7 +67,8 @@ func TestManualABIPacking(t *testing.T) { opts, output.OutputRoot, new(big.Int).SetUint64(output.BlockRef.Number), - output.Status.CurrentL1.Hash, + // output.Status.CurrentL1.Hash, + [32]byte{}, new(big.Int).SetUint64(output.Status.CurrentL1.Number)) require.NoError(t, err) diff --git a/op-proposer/proposer/l2_output_submitter.go b/op-proposer/proposer/l2_output_submitter.go index bcf34e5a09699..d2fb39e262471 100644 --- a/op-proposer/proposer/l2_output_submitter.go +++ b/op-proposer/proposer/l2_output_submitter.go @@ -316,7 +316,8 @@ func proposeL2OutputTxData(abi *abi.ABI, output *eth.OutputResponse) ([]byte, er "proposeL2Output", output.OutputRoot, new(big.Int).SetUint64(output.BlockRef.Number), - output.Status.CurrentL1.Hash, + // output.Status.CurrentL1.Hash, + [32]byte{}, new(big.Int).SetUint64(output.Status.CurrentL1.Number)) } diff --git a/op-service/txmgr/txmgr.go b/op-service/txmgr/txmgr.go index 37711506cbbfe..ccea1ae6663f2 100644 --- a/op-service/txmgr/txmgr.go +++ b/op-service/txmgr/txmgr.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" + opservice "github.com/ethereum-optimism/optimism/op-service" "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" ) @@ -163,38 +164,68 @@ func (m *SimpleTxManager) craftTx(ctx context.Context, candidate TxCandidate) (* } m.metr.RecordNonce(nonce) - rawTx := &types.DynamicFeeTx{ - ChainID: m.chainID, - Nonce: nonce, - To: candidate.To, - GasTipCap: gasTipCap, - GasFeeCap: gasFeeCap, - Data: candidate.TxData, - } - - m.l.Info("creating tx", "to", rawTx.To, "from", m.cfg.From) - - // If the gas limit is set, we can use that as the gas - if candidate.GasLimit != 0 { - rawTx.Gas = candidate.GasLimit + var tx *types.Transaction + if opservice.ForBSC { + rawTx := &types.LegacyTx{ + Nonce: nonce, + To: candidate.To, + GasPrice: gasFeeCap, + Data: candidate.TxData, + } + m.l.Info("creating tx", "to", rawTx.To, "from", m.cfg.From) + + // If the gas limit is set, we can use that as the gas + if candidate.GasLimit != 0 { + rawTx.Gas = candidate.GasLimit + } else { + // Calculate the intrinsic gas for the transaction + gas, err := m.backend.EstimateGas(ctx, ethereum.CallMsg{ + From: m.cfg.From, + To: candidate.To, + GasPrice: gasFeeCap, + Data: rawTx.Data, + }) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas: %w", err) + } + rawTx.Gas = gas + } + tx = types.NewTx(rawTx) } else { - // Calculate the intrinsic gas for the transaction - gas, err := m.backend.EstimateGas(ctx, ethereum.CallMsg{ - From: m.cfg.From, + rawTx := &types.DynamicFeeTx{ + ChainID: m.chainID, + Nonce: nonce, To: candidate.To, - GasFeeCap: gasFeeCap, GasTipCap: gasTipCap, - Data: rawTx.Data, - }) - if err != nil { - return nil, fmt.Errorf("failed to estimate gas: %w", err) + GasFeeCap: gasFeeCap, + Data: candidate.TxData, } - rawTx.Gas = gas + + m.l.Info("creating tx", "to", rawTx.To, "from", m.cfg.From) + + // If the gas limit is set, we can use that as the gas + if candidate.GasLimit != 0 { + rawTx.Gas = candidate.GasLimit + } else { + // Calculate the intrinsic gas for the transaction + gas, err := m.backend.EstimateGas(ctx, ethereum.CallMsg{ + From: m.cfg.From, + To: candidate.To, + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Data: rawTx.Data, + }) + if err != nil { + return nil, fmt.Errorf("failed to estimate gas: %w", err) + } + rawTx.Gas = gas + } + tx = types.NewTx(rawTx) } ctx, cancel = context.WithTimeout(ctx, m.cfg.NetworkTimeout) defer cancel() - return m.cfg.Signer(ctx, m.cfg.From, types.NewTx(rawTx)) + return m.cfg.Signer(ctx, m.cfg.From, tx) } // send submits the same transaction several times with increasing gas prices as necessary. @@ -392,20 +423,34 @@ func (m *SimpleTxManager) increaseGasPrice(ctx context.Context, tx *types.Transa return tx } - rawTx := &types.DynamicFeeTx{ - ChainID: tx.ChainId(), - Nonce: tx.Nonce(), - GasTipCap: gasTipCap, - GasFeeCap: gasFeeCap, - Gas: tx.Gas(), - To: tx.To(), - Value: tx.Value(), - Data: tx.Data(), - AccessList: tx.AccessList(), + var rtx *types.Transaction + if opservice.ForBSC { + rawTx := &types.LegacyTx{ + Nonce: tx.Nonce(), + Gas: tx.Gas(), + GasPrice: gasFeeCap, + To: tx.To(), + Value: tx.Value(), + Data: tx.Data(), + } + rtx = types.NewTx(rawTx) + } else { + rawTx := &types.DynamicFeeTx{ + ChainID: tx.ChainId(), + Nonce: tx.Nonce(), + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: tx.Gas(), + To: tx.To(), + Value: tx.Value(), + Data: tx.Data(), + AccessList: tx.AccessList(), + } + rtx = types.NewTx(rawTx) } ctx, cancel := context.WithTimeout(ctx, m.cfg.NetworkTimeout) defer cancel() - newTx, err := m.cfg.Signer(ctx, m.cfg.From, types.NewTx(rawTx)) + newTx, err := m.cfg.Signer(ctx, m.cfg.From, rtx) if err != nil { m.l.Warn("failed to sign new transaction", "err", err) return tx @@ -431,6 +476,9 @@ func (m *SimpleTxManager) suggestGasPriceCaps(ctx context.Context) (*big.Int, *b m.metr.RPCError() return nil, nil, fmt.Errorf("failed to fetch the suggested basefee: %w", err) } else if head.BaseFee == nil { + if opservice.ForBSC { + return tip, big.NewInt(0), nil + } return nil, nil, errors.New("txmgr does not support pre-london blocks that do not have a basefee") } return tip, head.BaseFee, nil diff --git a/op-service/txmgr/txmgr_test.go b/op-service/txmgr/txmgr_test.go index f170b5c08cc96..22978c53c85d3 100644 --- a/op-service/txmgr/txmgr_test.go +++ b/op-service/txmgr/txmgr_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "github.com/ethereum-optimism/optimism/op-node/testlog" + opservice "github.com/ethereum-optimism/optimism/op-service" "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" "github.com/ethereum/go-ethereum" @@ -739,6 +740,7 @@ func doGasPriceIncrease(t *testing.T, txTipCap, txFeeCap, newTip, newBaseFee int } func TestIncreaseGasPrice(t *testing.T) { + opservice.ForBSC = false // t.Parallel() tests := []struct { name string diff --git a/op-service/util.go b/op-service/util.go index 2f282dccffa0b..41e1497c6a34c 100644 --- a/op-service/util.go +++ b/op-service/util.go @@ -9,6 +9,8 @@ import ( "time" ) +var ForBSC = true + func PrefixEnvVar(prefix, suffix string) string { return prefix + "_" + suffix } diff --git a/op-signer/client/transaction_args.go b/op-signer/client/transaction_args.go index 480d46a15e103..7cfec5069fdb3 100644 --- a/op-signer/client/transaction_args.go +++ b/op-signer/client/transaction_args.go @@ -65,20 +65,28 @@ func (args *TransactionArgs) data() []byte { // ToTransaction converts the arguments to a transaction. func (args *TransactionArgs) ToTransaction() *types.Transaction { var data types.TxData - al := types.AccessList{} - if args.AccessList != nil { - al = *args.AccessList - } - data = &types.DynamicFeeTx{ - To: args.To, - ChainID: (*big.Int)(args.ChainID), - Nonce: uint64(*args.Nonce), - Gas: uint64(*args.Gas), - GasFeeCap: (*big.Int)(args.MaxFeePerGas), - GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas), - Value: (*big.Int)(args.Value), - Data: args.data(), - AccessList: al, + // al := types.AccessList{} + // if args.AccessList != nil { + // al = *args.AccessList + // } + // data = &types.DynamicFeeTx{ + // To: args.To, + // ChainID: (*big.Int)(args.ChainID), + // Nonce: uint64(*args.Nonce), + // Gas: uint64(*args.Gas), + // GasFeeCap: (*big.Int)(args.MaxFeePerGas), + // GasTipCap: (*big.Int)(args.MaxPriorityFeePerGas), + // Value: (*big.Int)(args.Value), + // Data: args.data(), + // AccessList: al, + // } + data = &types.LegacyTx{ + To: args.To, + Nonce: uint64(*args.Nonce), + Gas: uint64(*args.Gas), + GasPrice: args.GasPrice.ToInt(), + Value: (*big.Int)(args.Value), + Data: args.data(), } return types.NewTx(data) } diff --git a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol index 8a50079a662c5..31021b905c6bf 100644 --- a/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol +++ b/packages/contracts-bedrock/contracts/L1/ResourceMetering.sol @@ -152,7 +152,8 @@ abstract contract ResourceMetering is Initializable { // division by zero for L1s that don't support 1559 or to avoid excessive gas burns during // periods of extremely low L1 demand. One-day average gas fee hasn't dipped below 1 gwei // during any 1 day period in the last 5 years, so should be fine. - uint256 gasCost = resourceCost / Math.max(block.basefee, 1 gwei); + // uint256 gasCost = resourceCost / Math.max(block.basefee, 1 gwei); + uint256 gasCost = resourceCost / 5 gwei; // Give the user a refund based on the amount of gas they used to do all of the work up to // this point. Since we're at the end of the modifier, this should be pretty accurate. Acts