diff --git a/bindings/bindings/l1sequencer.go b/bindings/bindings/l1sequencer.go new file mode 100644 index 000000000..80110f035 --- /dev/null +++ b/bindings/bindings/l1sequencer.go @@ -0,0 +1,820 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/morph-l2/go-ethereum" + "github.com/morph-l2/go-ethereum/accounts/abi" + "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/core/types" + "github.com/morph-l2/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// L1SequencerMetaData contains all meta data concerning the L1Sequencer contract. +var L1SequencerMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"getSequencer\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"sequencer\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"updateSequencer\",\"inputs\":[{\"name\":\"newSequencer\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SequencerUpdated\",\"inputs\":[{\"name\":\"oldSequencer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newSequencer\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", + Bin: "0x608060405234801561000f575f80fd5b5061081a8061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061007a575f3560e01c8063715018a611610058578063715018a6146100f65780638da5cb5b146100fe578063c4d66de81461011c578063f2fde38b1461012f575f80fd5b806343ae20a31461007e5780634d96a90a146100935780635c1bba38146100d6575b5f80fd5b61009161008c3660046107d3565b610142565b005b60655473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b6065546100ad9073ffffffffffffffffffffffffffffffffffffffff1681565b6100916102c7565b60335473ffffffffffffffffffffffffffffffffffffffff166100ad565b61009161012a3660046107d3565b6102da565b61009161013d3660046107d3565b6104ed565b61014a6105a4565b73ffffffffffffffffffffffffffffffffffffffff81166101cc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f696e76616c69642073657175656e63657200000000000000000000000000000060448201526064015b60405180910390fd5b60655473ffffffffffffffffffffffffffffffffffffffff90811690821603610251576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f73616d652073657175656e63657200000000000000000000000000000000000060448201526064016101c3565b6065805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907fcd58b762453bd126b48db83f2cecd464f5281dd7e5e6824b528c09d0482984d6905f90a35050565b6102cf6105a4565b6102d85f610625565b565b5f54610100900460ff16158080156102f857505f54600160ff909116105b806103115750303b15801561031157505f5460ff166001145b61039d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016101c3565b5f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156103f9575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b73ffffffffffffffffffffffffffffffffffffffff8216610476576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600d60248201527f696e76616c6964206f776e65720000000000000000000000000000000000000060448201526064016101c3565b61047e61069b565b61048782610625565b80156104e9575f80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050565b6104f56105a4565b73ffffffffffffffffffffffffffffffffffffffff8116610598576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016101c3565b6105a181610625565b50565b60335473ffffffffffffffffffffffffffffffffffffffff1633146102d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016101c3565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a35050565b5f54610100900460ff16610731576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016101c3565b6102d85f54610100900460ff166107ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016101c3565b6102d833610625565b5f602082840312156107e3575f80fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610806575f80fd5b939250505056fea164736f6c6343000818000a", +} + +// L1SequencerABI is the input ABI used to generate the binding from. +// Deprecated: Use L1SequencerMetaData.ABI instead. +var L1SequencerABI = L1SequencerMetaData.ABI + +// L1SequencerBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use L1SequencerMetaData.Bin instead. +var L1SequencerBin = L1SequencerMetaData.Bin + +// DeployL1Sequencer deploys a new Ethereum contract, binding an instance of L1Sequencer to it. +func DeployL1Sequencer(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *L1Sequencer, error) { + parsed, err := L1SequencerMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(L1SequencerBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &L1Sequencer{L1SequencerCaller: L1SequencerCaller{contract: contract}, L1SequencerTransactor: L1SequencerTransactor{contract: contract}, L1SequencerFilterer: L1SequencerFilterer{contract: contract}}, nil +} + +// L1Sequencer is an auto generated Go binding around an Ethereum contract. +type L1Sequencer struct { + L1SequencerCaller // Read-only binding to the contract + L1SequencerTransactor // Write-only binding to the contract + L1SequencerFilterer // Log filterer for contract events +} + +// L1SequencerCaller is an auto generated read-only Go binding around an Ethereum contract. +type L1SequencerCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// L1SequencerTransactor is an auto generated write-only Go binding around an Ethereum contract. +type L1SequencerTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// L1SequencerFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type L1SequencerFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// L1SequencerSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type L1SequencerSession struct { + Contract *L1Sequencer // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// L1SequencerCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type L1SequencerCallerSession struct { + Contract *L1SequencerCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// L1SequencerTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type L1SequencerTransactorSession struct { + Contract *L1SequencerTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// L1SequencerRaw is an auto generated low-level Go binding around an Ethereum contract. +type L1SequencerRaw struct { + Contract *L1Sequencer // Generic contract binding to access the raw methods on +} + +// L1SequencerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type L1SequencerCallerRaw struct { + Contract *L1SequencerCaller // Generic read-only contract binding to access the raw methods on +} + +// L1SequencerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type L1SequencerTransactorRaw struct { + Contract *L1SequencerTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewL1Sequencer creates a new instance of L1Sequencer, bound to a specific deployed contract. +func NewL1Sequencer(address common.Address, backend bind.ContractBackend) (*L1Sequencer, error) { + contract, err := bindL1Sequencer(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &L1Sequencer{L1SequencerCaller: L1SequencerCaller{contract: contract}, L1SequencerTransactor: L1SequencerTransactor{contract: contract}, L1SequencerFilterer: L1SequencerFilterer{contract: contract}}, nil +} + +// NewL1SequencerCaller creates a new read-only instance of L1Sequencer, bound to a specific deployed contract. +func NewL1SequencerCaller(address common.Address, caller bind.ContractCaller) (*L1SequencerCaller, error) { + contract, err := bindL1Sequencer(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &L1SequencerCaller{contract: contract}, nil +} + +// NewL1SequencerTransactor creates a new write-only instance of L1Sequencer, bound to a specific deployed contract. +func NewL1SequencerTransactor(address common.Address, transactor bind.ContractTransactor) (*L1SequencerTransactor, error) { + contract, err := bindL1Sequencer(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &L1SequencerTransactor{contract: contract}, nil +} + +// NewL1SequencerFilterer creates a new log filterer instance of L1Sequencer, bound to a specific deployed contract. +func NewL1SequencerFilterer(address common.Address, filterer bind.ContractFilterer) (*L1SequencerFilterer, error) { + contract, err := bindL1Sequencer(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &L1SequencerFilterer{contract: contract}, nil +} + +// bindL1Sequencer binds a generic wrapper to an already deployed contract. +func bindL1Sequencer(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := L1SequencerMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_L1Sequencer *L1SequencerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _L1Sequencer.Contract.L1SequencerCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_L1Sequencer *L1SequencerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _L1Sequencer.Contract.L1SequencerTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_L1Sequencer *L1SequencerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _L1Sequencer.Contract.L1SequencerTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_L1Sequencer *L1SequencerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _L1Sequencer.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_L1Sequencer *L1SequencerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _L1Sequencer.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_L1Sequencer *L1SequencerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _L1Sequencer.Contract.contract.Transact(opts, method, params...) +} + +// GetSequencer is a free data retrieval call binding the contract method 0x4d96a90a. +// +// Solidity: function getSequencer() view returns(address) +func (_L1Sequencer *L1SequencerCaller) GetSequencer(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _L1Sequencer.contract.Call(opts, &out, "getSequencer") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// GetSequencer is a free data retrieval call binding the contract method 0x4d96a90a. +// +// Solidity: function getSequencer() view returns(address) +func (_L1Sequencer *L1SequencerSession) GetSequencer() (common.Address, error) { + return _L1Sequencer.Contract.GetSequencer(&_L1Sequencer.CallOpts) +} + +// GetSequencer is a free data retrieval call binding the contract method 0x4d96a90a. +// +// Solidity: function getSequencer() view returns(address) +func (_L1Sequencer *L1SequencerCallerSession) GetSequencer() (common.Address, error) { + return _L1Sequencer.Contract.GetSequencer(&_L1Sequencer.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_L1Sequencer *L1SequencerCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _L1Sequencer.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_L1Sequencer *L1SequencerSession) Owner() (common.Address, error) { + return _L1Sequencer.Contract.Owner(&_L1Sequencer.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_L1Sequencer *L1SequencerCallerSession) Owner() (common.Address, error) { + return _L1Sequencer.Contract.Owner(&_L1Sequencer.CallOpts) +} + +// Sequencer is a free data retrieval call binding the contract method 0x5c1bba38. +// +// Solidity: function sequencer() view returns(address) +func (_L1Sequencer *L1SequencerCaller) Sequencer(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _L1Sequencer.contract.Call(opts, &out, "sequencer") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Sequencer is a free data retrieval call binding the contract method 0x5c1bba38. +// +// Solidity: function sequencer() view returns(address) +func (_L1Sequencer *L1SequencerSession) Sequencer() (common.Address, error) { + return _L1Sequencer.Contract.Sequencer(&_L1Sequencer.CallOpts) +} + +// Sequencer is a free data retrieval call binding the contract method 0x5c1bba38. +// +// Solidity: function sequencer() view returns(address) +func (_L1Sequencer *L1SequencerCallerSession) Sequencer() (common.Address, error) { + return _L1Sequencer.Contract.Sequencer(&_L1Sequencer.CallOpts) +} + +// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8. +// +// Solidity: function initialize(address _owner) returns() +func (_L1Sequencer *L1SequencerTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address) (*types.Transaction, error) { + return _L1Sequencer.contract.Transact(opts, "initialize", _owner) +} + +// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8. +// +// Solidity: function initialize(address _owner) returns() +func (_L1Sequencer *L1SequencerSession) Initialize(_owner common.Address) (*types.Transaction, error) { + return _L1Sequencer.Contract.Initialize(&_L1Sequencer.TransactOpts, _owner) +} + +// Initialize is a paid mutator transaction binding the contract method 0xc4d66de8. +// +// Solidity: function initialize(address _owner) returns() +func (_L1Sequencer *L1SequencerTransactorSession) Initialize(_owner common.Address) (*types.Transaction, error) { + return _L1Sequencer.Contract.Initialize(&_L1Sequencer.TransactOpts, _owner) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_L1Sequencer *L1SequencerTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _L1Sequencer.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_L1Sequencer *L1SequencerSession) RenounceOwnership() (*types.Transaction, error) { + return _L1Sequencer.Contract.RenounceOwnership(&_L1Sequencer.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_L1Sequencer *L1SequencerTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _L1Sequencer.Contract.RenounceOwnership(&_L1Sequencer.TransactOpts) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_L1Sequencer *L1SequencerTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _L1Sequencer.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_L1Sequencer *L1SequencerSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _L1Sequencer.Contract.TransferOwnership(&_L1Sequencer.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_L1Sequencer *L1SequencerTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _L1Sequencer.Contract.TransferOwnership(&_L1Sequencer.TransactOpts, newOwner) +} + +// UpdateSequencer is a paid mutator transaction binding the contract method 0x43ae20a3. +// +// Solidity: function updateSequencer(address newSequencer) returns() +func (_L1Sequencer *L1SequencerTransactor) UpdateSequencer(opts *bind.TransactOpts, newSequencer common.Address) (*types.Transaction, error) { + return _L1Sequencer.contract.Transact(opts, "updateSequencer", newSequencer) +} + +// UpdateSequencer is a paid mutator transaction binding the contract method 0x43ae20a3. +// +// Solidity: function updateSequencer(address newSequencer) returns() +func (_L1Sequencer *L1SequencerSession) UpdateSequencer(newSequencer common.Address) (*types.Transaction, error) { + return _L1Sequencer.Contract.UpdateSequencer(&_L1Sequencer.TransactOpts, newSequencer) +} + +// UpdateSequencer is a paid mutator transaction binding the contract method 0x43ae20a3. +// +// Solidity: function updateSequencer(address newSequencer) returns() +func (_L1Sequencer *L1SequencerTransactorSession) UpdateSequencer(newSequencer common.Address) (*types.Transaction, error) { + return _L1Sequencer.Contract.UpdateSequencer(&_L1Sequencer.TransactOpts, newSequencer) +} + +// L1SequencerInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the L1Sequencer contract. +type L1SequencerInitializedIterator struct { + Event *L1SequencerInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *L1SequencerInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(L1SequencerInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(L1SequencerInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *L1SequencerInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *L1SequencerInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// L1SequencerInitialized represents a Initialized event raised by the L1Sequencer contract. +type L1SequencerInitialized struct { + Version uint8 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_L1Sequencer *L1SequencerFilterer) FilterInitialized(opts *bind.FilterOpts) (*L1SequencerInitializedIterator, error) { + + logs, sub, err := _L1Sequencer.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &L1SequencerInitializedIterator{contract: _L1Sequencer.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_L1Sequencer *L1SequencerFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *L1SequencerInitialized) (event.Subscription, error) { + + logs, sub, err := _L1Sequencer.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(L1SequencerInitialized) + if err := _L1Sequencer.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_L1Sequencer *L1SequencerFilterer) ParseInitialized(log types.Log) (*L1SequencerInitialized, error) { + event := new(L1SequencerInitialized) + if err := _L1Sequencer.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// L1SequencerOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the L1Sequencer contract. +type L1SequencerOwnershipTransferredIterator struct { + Event *L1SequencerOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *L1SequencerOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(L1SequencerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(L1SequencerOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *L1SequencerOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *L1SequencerOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// L1SequencerOwnershipTransferred represents a OwnershipTransferred event raised by the L1Sequencer contract. +type L1SequencerOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_L1Sequencer *L1SequencerFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*L1SequencerOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _L1Sequencer.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &L1SequencerOwnershipTransferredIterator{contract: _L1Sequencer.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_L1Sequencer *L1SequencerFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *L1SequencerOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _L1Sequencer.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(L1SequencerOwnershipTransferred) + if err := _L1Sequencer.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_L1Sequencer *L1SequencerFilterer) ParseOwnershipTransferred(log types.Log) (*L1SequencerOwnershipTransferred, error) { + event := new(L1SequencerOwnershipTransferred) + if err := _L1Sequencer.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// L1SequencerSequencerUpdatedIterator is returned from FilterSequencerUpdated and is used to iterate over the raw logs and unpacked data for SequencerUpdated events raised by the L1Sequencer contract. +type L1SequencerSequencerUpdatedIterator struct { + Event *L1SequencerSequencerUpdated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *L1SequencerSequencerUpdatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(L1SequencerSequencerUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(L1SequencerSequencerUpdated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *L1SequencerSequencerUpdatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *L1SequencerSequencerUpdatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// L1SequencerSequencerUpdated represents a SequencerUpdated event raised by the L1Sequencer contract. +type L1SequencerSequencerUpdated struct { + OldSequencer common.Address + NewSequencer common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSequencerUpdated is a free log retrieval operation binding the contract event 0xcd58b762453bd126b48db83f2cecd464f5281dd7e5e6824b528c09d0482984d6. +// +// Solidity: event SequencerUpdated(address indexed oldSequencer, address indexed newSequencer) +func (_L1Sequencer *L1SequencerFilterer) FilterSequencerUpdated(opts *bind.FilterOpts, oldSequencer []common.Address, newSequencer []common.Address) (*L1SequencerSequencerUpdatedIterator, error) { + + var oldSequencerRule []interface{} + for _, oldSequencerItem := range oldSequencer { + oldSequencerRule = append(oldSequencerRule, oldSequencerItem) + } + var newSequencerRule []interface{} + for _, newSequencerItem := range newSequencer { + newSequencerRule = append(newSequencerRule, newSequencerItem) + } + + logs, sub, err := _L1Sequencer.contract.FilterLogs(opts, "SequencerUpdated", oldSequencerRule, newSequencerRule) + if err != nil { + return nil, err + } + return &L1SequencerSequencerUpdatedIterator{contract: _L1Sequencer.contract, event: "SequencerUpdated", logs: logs, sub: sub}, nil +} + +// WatchSequencerUpdated is a free log subscription operation binding the contract event 0xcd58b762453bd126b48db83f2cecd464f5281dd7e5e6824b528c09d0482984d6. +// +// Solidity: event SequencerUpdated(address indexed oldSequencer, address indexed newSequencer) +func (_L1Sequencer *L1SequencerFilterer) WatchSequencerUpdated(opts *bind.WatchOpts, sink chan<- *L1SequencerSequencerUpdated, oldSequencer []common.Address, newSequencer []common.Address) (event.Subscription, error) { + + var oldSequencerRule []interface{} + for _, oldSequencerItem := range oldSequencer { + oldSequencerRule = append(oldSequencerRule, oldSequencerItem) + } + var newSequencerRule []interface{} + for _, newSequencerItem := range newSequencer { + newSequencerRule = append(newSequencerRule, newSequencerItem) + } + + logs, sub, err := _L1Sequencer.contract.WatchLogs(opts, "SequencerUpdated", oldSequencerRule, newSequencerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(L1SequencerSequencerUpdated) + if err := _L1Sequencer.contract.UnpackLog(event, "SequencerUpdated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSequencerUpdated is a log parse operation binding the contract event 0xcd58b762453bd126b48db83f2cecd464f5281dd7e5e6824b528c09d0482984d6. +// +// Solidity: event SequencerUpdated(address indexed oldSequencer, address indexed newSequencer) +func (_L1Sequencer *L1SequencerFilterer) ParseSequencerUpdated(log types.Log) (*L1SequencerSequencerUpdated, error) { + event := new(L1SequencerSequencerUpdated) + if err := _L1Sequencer.contract.UnpackLog(event, "SequencerUpdated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/contracts/contracts/l1/L1Sequencer.sol b/contracts/contracts/l1/L1Sequencer.sol new file mode 100644 index 000000000..3a46768bd --- /dev/null +++ b/contracts/contracts/l1/L1Sequencer.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.24; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +/// @title L1Sequencer +/// @notice L1 contract for managing the sequencer address. +/// The sequencer address can be updated by the owner (multisig recommended). +contract L1Sequencer is OwnableUpgradeable { + // ============ Storage ============ + + /// @notice Current sequencer address + address public sequencer; + + // ============ Events ============ + + /// @notice Emitted when sequencer is updated + event SequencerUpdated(address indexed oldSequencer, address indexed newSequencer); + + // ============ Initializer ============ + + /// @notice Initialize the contract + /// @param _owner Contract owner (multisig recommended) + /// @param _initialSequencer Initial sequencer address (can be address(0) to set later) + function initialize(address _owner, address _initialSequencer) external initializer { + require(_owner != address(0), "invalid owner"); + + __Ownable_init(); + _transferOwnership(_owner); + + // Set initial sequencer if provided + if (_initialSequencer != address(0)) { + sequencer = _initialSequencer; + emit SequencerUpdated(address(0), _initialSequencer); + } + } + + // ============ Admin Functions ============ + + /// @notice Update sequencer address (takes effect immediately) + /// @param newSequencer New sequencer address + function updateSequencer(address newSequencer) external onlyOwner { + require(newSequencer != address(0), "invalid sequencer"); + require(newSequencer != sequencer, "same sequencer"); + + address oldSequencer = sequencer; + sequencer = newSequencer; + + emit SequencerUpdated(oldSequencer, newSequencer); + } + + // ============ View Functions ============ + + /// @notice Get current sequencer address + function getSequencer() external view returns (address) { + return sequencer; + } +} diff --git a/contracts/deploy/013-DeployProxys.ts b/contracts/deploy/013-DeployProxys.ts index 24545613a..6b3a4a5ce 100644 --- a/contracts/deploy/013-DeployProxys.ts +++ b/contracts/deploy/013-DeployProxys.ts @@ -48,6 +48,7 @@ export const deployContractProxies = async ( const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName + const L1SequencerProxyStorageName = ProxyStorageName.L1SequencerProxyStorageName const L1GatewayRouterProxyStorageName = ProxyStorageName.L1GatewayRouterProxyStorageName const L1ETHGatewayProxyStorageName = ProxyStorageName.L1ETHGatewayProxyStorageName @@ -112,6 +113,13 @@ export const deployContractProxies = async ( return err } + // ************************ sequencer contracts deploy ************************ + // L1SequencerProxy deploy + err = await deployContractProxyByStorageName(hre, path, deployer, L1SequencerProxyStorageName) + if (err != "") { + return err + } + // ************************ rollup contracts deploy ************************ // RollupProxy deploy err = await deployContractProxyByStorageName(hre, path, deployer, RollupProxyStorageName) @@ -274,6 +282,7 @@ export const deployContractProxiesConcurrently = async ( ProxyStorageName.L1CrossDomainMessengerProxyStorageName, ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName, ProxyStorageName.L1StakingProxyStorageName, + ProxyStorageName.L1SequencerProxyStorageName, ProxyStorageName.RollupProxyStorageName, ProxyStorageName.L1GatewayRouterProxyStorageName, ProxyStorageName.L1ETHGatewayProxyStorageName, diff --git a/contracts/deploy/014-DeployImpls.ts b/contracts/deploy/014-DeployImpls.ts index ed0653706..b32613018 100644 --- a/contracts/deploy/014-DeployImpls.ts +++ b/contracts/deploy/014-DeployImpls.ts @@ -122,6 +122,11 @@ export const deployContractImplsConcurrently = async ( deployPromises.push(deployContract(L1StakingFactoryName, StakingImplStorageName, [L1CrossDomainMessengerProxyAddress])) + // L1Sequencer deploy (no constructor args) + const L1SequencerFactoryName = ContractFactoryName.L1Sequencer + const L1SequencerImplStorageName = ImplStorageName.L1SequencerStorageName + deployPromises.push(deployContract(L1SequencerFactoryName, L1SequencerImplStorageName)) + const results = await Promise.all(deployPromises) for (const result of results) { @@ -382,6 +387,21 @@ export const deployContractImpls = async ( return err } + // ************************ sequencer contracts deploy ************************ + // L1Sequencer deploy + const L1SequencerFactoryName = ContractFactoryName.L1Sequencer + const L1SequencerImplStorageName = ImplStorageName.L1SequencerStorageName + Factory = await hre.ethers.getContractFactory(L1SequencerFactoryName) + contract = await Factory.deploy() + await contract.deployed() + console.log("%s=%s ; TX_HASH: %s", L1SequencerImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash) + blockNumber = await hre.ethers.provider.getBlockNumber() + console.log("BLOCK_NUMBER: %s", blockNumber) + err = await storage(path, L1SequencerImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0) + if (err != '') { + return err + } + // return return '' } diff --git a/contracts/deploy/019-AdminTransfer.ts b/contracts/deploy/019-AdminTransfer.ts index 566396b90..a0cd1b58a 100644 --- a/contracts/deploy/019-AdminTransfer.ts +++ b/contracts/deploy/019-AdminTransfer.ts @@ -109,6 +109,7 @@ export const AdminTransferConcurrently = async ( ProxyStorageName.L1CrossDomainMessengerProxyStorageName, ProxyStorageName.L1MessageQueueWithGasPriceOracleProxyStorageName, ProxyStorageName.L1StakingProxyStorageName, + ProxyStorageName.L1SequencerProxyStorageName, // Added L1Sequencer ProxyStorageName.RollupProxyStorageName, ProxyStorageName.L1GatewayRouterProxyStorageName, ProxyStorageName.L1ETHGatewayProxyStorageName, @@ -159,6 +160,7 @@ export const AdminTransfer = async ( const RollupProxyStorageName = ProxyStorageName.RollupProxyStorageName const L1StakingProxyStorageName = ProxyStorageName.L1StakingProxyStorageName + const L1SequencerProxyStorageName = ProxyStorageName.L1SequencerProxyStorageName const L1GatewayRouterProxyStorageName = ProxyStorageName.L1GatewayRouterProxyStorageName const L1ETHGatewayProxyStorageName = ProxyStorageName.L1ETHGatewayProxyStorageName @@ -192,6 +194,13 @@ export const AdminTransfer = async ( return err } + // ************************ sequencer contracts admin change ************************ + // L1SequencerProxy admin change + err = await AdminTransferByProxyStorageName(hre, path, deployer, L1SequencerProxyStorageName) + if (err != '') { + return err + } + // ************************ rollup contracts admin change ************************ // RollupProxy admin change err = await AdminTransferByProxyStorageName(hre, path, deployer, RollupProxyStorageName) diff --git a/contracts/deploy/022-SequencerInit.ts b/contracts/deploy/022-SequencerInit.ts new file mode 100644 index 000000000..e8de25116 --- /dev/null +++ b/contracts/deploy/022-SequencerInit.ts @@ -0,0 +1,89 @@ +import "@nomiclabs/hardhat-web3"; +import "@nomiclabs/hardhat-ethers"; +import "@nomiclabs/hardhat-waffle"; + +import { + HardhatRuntimeEnvironment +} from 'hardhat/types'; +import { assertContractVariable, getContractAddressByName, awaitCondition } from "../src/deploy-utils"; +import { ethers } from 'ethers' + +import { + ImplStorageName, + ProxyStorageName, + ContractFactoryName, +} from "../src/types" + +export const SequencerInit = async ( + hre: HardhatRuntimeEnvironment, + path: string, + deployer: any, + configTmp: any +): Promise => { + // L1Sequencer addresses + const L1SequencerProxyAddress = getContractAddressByName(path, ProxyStorageName.L1SequencerProxyStorageName) + const L1SequencerImplAddress = getContractAddressByName(path, ImplStorageName.L1SequencerStorageName) + const L1SequencerFactory = await hre.ethers.getContractFactory(ContractFactoryName.L1Sequencer) + + const IL1SequencerProxy = await hre.ethers.getContractAt(ContractFactoryName.DefaultProxyInterface, L1SequencerProxyAddress, deployer) + + if ( + (await IL1SequencerProxy.implementation()).toLocaleLowerCase() !== L1SequencerImplAddress.toLocaleLowerCase() + ) { + console.log('Upgrading the L1Sequencer proxy...') + + // Owner is the deployer (will be transferred to multisig in production) + const owner = await deployer.getAddress() + + // Get initial sequencer address from config (first sequencer address) + // Note: l2SequencerAddresses is defined in contracts/src/deploy-config/l1.ts + const initialSequencer = (configTmp.l2SequencerAddresses && configTmp.l2SequencerAddresses.length > 0) + ? configTmp.l2SequencerAddresses[0] + : ethers.constants.AddressZero + + console.log('Initial sequencer address:', initialSequencer) + + // Upgrade and initialize the proxy with owner and initial sequencer + // Note: We set sequencer in initialize() to avoid TransparentUpgradeableProxy admin restriction + await IL1SequencerProxy.upgradeToAndCall( + L1SequencerImplAddress, + L1SequencerFactory.interface.encodeFunctionData('initialize', [owner, initialSequencer]) + ) + + await awaitCondition( + async () => { + return ( + (await IL1SequencerProxy.implementation()).toLocaleLowerCase() === L1SequencerImplAddress.toLocaleLowerCase() + ) + }, + 3000, + 1000 + ) + + const contractTmp = new ethers.Contract( + L1SequencerProxyAddress, + L1SequencerFactory.interface, + deployer, + ) + + await assertContractVariable( + contractTmp, + 'owner', + owner, + ) + + if (initialSequencer !== ethers.constants.AddressZero) { + await assertContractVariable( + contractTmp, + 'sequencer', + initialSequencer, + ) + console.log('L1SequencerProxy upgrade success, initial sequencer set:', initialSequencer) + } else { + console.log('L1SequencerProxy upgrade success (no initial sequencer set)') + } + } + return '' +} + +export default SequencerInit diff --git a/contracts/deploy/index.ts b/contracts/deploy/index.ts index 16f69f20b..a30ab90fb 100644 --- a/contracts/deploy/index.ts +++ b/contracts/deploy/index.ts @@ -10,6 +10,7 @@ import StakingInit from './018-StakingInit' import {AdminTransfer,AdminTransferByProxyStorageName, AdminTransferConcurrently} from './019-AdminTransfer' import ContractInit from './020-ContractInit' import StakingRegister from './021-StakingRegister' +import SequencerInit from './022-SequencerInit' export { @@ -28,5 +29,6 @@ export { AdminTransferByProxyStorageName, AdminTransferConcurrently, ContractInit, - StakingRegister + StakingRegister, + SequencerInit } \ No newline at end of file diff --git a/contracts/src/types.ts b/contracts/src/types.ts index ea2577c89..7e518addd 100644 --- a/contracts/src/types.ts +++ b/contracts/src/types.ts @@ -18,6 +18,8 @@ const ContractFactoryName = { MultipleVersionRollupVerifier: 'MultipleVersionRollupVerifier', // staking L1Staking: 'L1Staking', + // L1 sequencer + L1Sequencer: 'L1Sequencer', // gateway L1GatewayRouter: 'L1GatewayRouter', L1StandardERC20Gateway: 'L1StandardERC20Gateway', @@ -40,6 +42,8 @@ const ProxyStorageName = { RollupProxyStorageName: 'Proxy__Rollup', // staking L1StakingProxyStorageName: 'Proxy__L1Staking', + // L1 sequencer + L1SequencerProxyStorageName: 'Proxy__L1Sequencer', // gateway L1GatewayRouterProxyStorageName: 'Proxy__L1GatewayRouter', L1StandardERC20GatewayProxyStorageName: 'Proxy__L1StandardERC20Gateway', @@ -71,6 +75,8 @@ const ImplStorageName = { MultipleVersionRollupVerifierStorageName: 'Impl__MultipleVersionRollupVerifier', // staking L1StakingStorageName: 'Impl__L1Staking', + // L1 sequencer + L1SequencerStorageName: 'Impl__L1Sequencer', // gateway L1GatewayRouterStorageName: 'Impl__L1GatewayRouter', L1StandardERC20GatewayStorageName: 'Impl__L1StandardERC20Gateway', diff --git a/contracts/tasks/deploy.ts b/contracts/tasks/deploy.ts index 9ee6739a9..a7ea6bb6a 100644 --- a/contracts/tasks/deploy.ts +++ b/contracts/tasks/deploy.ts @@ -21,6 +21,7 @@ import { AdminTransferConcurrently, ContractInit, StakingRegister, + SequencerInit, } from '../deploy/index' import { ethers } from "ethers"; @@ -120,6 +121,12 @@ task("initialize") console.log('Staking init failed, err: ', err) return } + console.log('\n---------------------------------- Sequencer init ----------------------------------') + err = await SequencerInit(hre, storagePath, deployer, config) + if (err != '') { + console.log('Sequencer init failed, err: ', err) + return + } console.log('\n---------------------------------- Admin Transfer ----------------------------------') if (concurrent === 'true') { err = await AdminTransferConcurrently(hre, storagePath, deployer, config) diff --git a/node/blocktag/config.go b/node/blocktag/config.go index 6c392ce0f..43c282800 100644 --- a/node/blocktag/config.go +++ b/node/blocktag/config.go @@ -20,7 +20,6 @@ const ( // Config holds the configuration for BlockTagService type Config struct { - L1Addr string RollupAddress common.Address SafeConfirmations uint64 PollInterval time.Duration @@ -36,8 +35,6 @@ func DefaultConfig() *Config { // SetCliContext sets the configuration from CLI context func (c *Config) SetCliContext(ctx *cli.Context) error { - c.L1Addr = ctx.GlobalString(flags.L1NodeAddr.Name) - // Determine RollupAddress: use explicit flag, or mainnet default, or error if ctx.GlobalBool(flags.MainnetFlag.Name) { c.RollupAddress = node.MainnetRollupContractAddress diff --git a/node/blocktag/service.go b/node/blocktag/service.go index 86f9b8d70..45f7ecda3 100644 --- a/node/blocktag/service.go +++ b/node/blocktag/service.go @@ -63,22 +63,18 @@ type BlockTagService struct { // NewBlockTagService creates a new BlockTagService func NewBlockTagService( ctx context.Context, + l1Client *ethclient.Client, l2Client *types.RetryableClient, config *Config, logger tmlog.Logger, ) (*BlockTagService, error) { - if config.L1Addr == "" { - return nil, fmt.Errorf("L1 RPC address is required") + if l1Client == nil { + return nil, fmt.Errorf("L1 client is required") } if config.RollupAddress == (common.Address{}) { return nil, fmt.Errorf("Rollup contract address is required") } - l1Client, err := ethclient.Dial(config.L1Addr) - if err != nil { - return nil, fmt.Errorf("failed to connect to L1: %w", err) - } - rollup, err := bindings.NewRollup(config.RollupAddress, l1Client) if err != nil { return nil, fmt.Errorf("failed to create rollup binding: %w", err) @@ -122,7 +118,6 @@ func (s *BlockTagService) Stop() { s.logger.Info("Stopping BlockTagService") s.cancel() <-s.stop - s.l1Client.Close() s.logger.Info("BlockTagService stopped") } diff --git a/node/cmd/node/main.go b/node/cmd/node/main.go index 2a71f2a28..3a3f949d0 100644 --- a/node/cmd/node/main.go +++ b/node/cmd/node/main.go @@ -6,9 +6,14 @@ import ( "os" "os/signal" "path/filepath" + "strings" "syscall" + "time" + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/crypto" "github.com/morph-l2/go-ethereum/ethclient" + tmlog "github.com/tendermint/tendermint/libs/log" tmnode "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/privval" "github.com/urfave/cli" @@ -20,6 +25,7 @@ import ( "morph-l2/node/db" "morph-l2/node/derivation" "morph-l2/node/flags" + "morph-l2/node/l1sequencer" "morph-l2/node/sequencer" "morph-l2/node/sequencer/mock" "morph-l2/node/sync" @@ -60,6 +66,9 @@ func L2NodeMain(ctx *cli.Context) error { tmNode *tmnode.Node dvNode *derivation.Derivation blockTagSvc *blocktag.BlockTagService + tracker *l1sequencer.L1Tracker + verifier *l1sequencer.SequencerVerifier + signer l1sequencer.Signer nodeConfig = node.DefaultConfig() ) @@ -118,14 +127,27 @@ func L2NodeMain(ctx *cli.Context) error { dvNode.Start() nodeConfig.Logger.Info("derivation node starting") } else { - // launch tendermint node + // ========== Create Syncer and L1 Sequencer Components ========== + syncer, err = node.NewSyncer(ctx, home, nodeConfig) + if err != nil { + return fmt.Errorf("failed to create syncer: %w", err) + } + + tracker, verifier, signer, err = initL1SequencerComponents(ctx, syncer.L1Client(), nodeConfig.Logger) + if err != nil { + return fmt.Errorf("failed to init L1 sequencer components: %w", err) + } + + // ========== Launch Tendermint Node ========== tmCfg, err := sequencer.LoadTmConfig(ctx, home) if err != nil { return err } tmVal := privval.LoadOrGenFilePV(tmCfg.PrivValidatorKeyFile(), tmCfg.PrivValidatorStateFile()) pubKey, _ := tmVal.GetPubKey() - newSyncerFunc := func() (*sync.Syncer, error) { return node.NewSyncer(ctx, home, nodeConfig) } + + // Create executor with syncer + newSyncerFunc := func() (*sync.Syncer, error) { return syncer, nil } // Reuse existing syncer executor, err = node.NewExecutor(newSyncerFunc, nodeConfig, pubKey) if err != nil { return err @@ -137,25 +159,19 @@ func L2NodeMain(ctx *cli.Context) error { } go ms.Start() } else { - if tmNode, err = sequencer.SetupNode(tmCfg, tmVal, executor, nodeConfig.Logger); err != nil { - return fmt.Errorf("failed to setup consensus node, error: %v", err) + tmNode, err = sequencer.SetupNode(tmCfg, tmVal, executor, nodeConfig.Logger, verifier, signer) + if err != nil { + return fmt.Errorf("failed to setup consensus node: %v", err) } if err = tmNode.Start(); err != nil { return fmt.Errorf("failed to start consensus node, error: %v", err) } } - // Start BlockTagService for sequencer mode - blockTagConfig := blocktag.DefaultConfig() - if err := blockTagConfig.SetCliContext(ctx); err != nil { - return fmt.Errorf("blocktag config set cli context error: %w", err) - } - blockTagSvc, err = blocktag.NewBlockTagService(context.Background(), executor.L2Client(), blockTagConfig, nodeConfig.Logger) + // ========== Initialize BlockTagService ========== + blockTagSvc, err = initBlockTagService(ctx, syncer.L1Client(), executor, nodeConfig.Logger) if err != nil { - return fmt.Errorf("failed to create BlockTagService: %w", err) - } - if err := blockTagSvc.Start(); err != nil { - return fmt.Errorf("failed to start BlockTagService: %w", err) + return fmt.Errorf("failed to init BlockTagService: %w", err) } } @@ -186,10 +202,102 @@ func L2NodeMain(ctx *cli.Context) error { if blockTagSvc != nil { blockTagSvc.Stop() } + if tracker != nil { + tracker.Stop() + } return nil } +// initL1SequencerComponents initializes all L1 sequencer related components: +// - L1Tracker: monitors L1 sync status +// - SequencerCache: caches L1 sequencer address (nil if contract not configured) +// - Signer: signs blocks (nil if private key not configured) +func initL1SequencerComponents( + ctx *cli.Context, + l1Client *ethclient.Client, + logger tmlog.Logger, +) (*l1sequencer.L1Tracker, *l1sequencer.SequencerVerifier, l1sequencer.Signer, error) { + if l1Client == nil { + return nil, nil, nil, fmt.Errorf("L1 client is required, check l1.rpc configuration") + } + + // Get config from flags + lagThreshold := ctx.GlobalDuration(flags.L1SyncLagThreshold.Name) + if lagThreshold == 0 { + lagThreshold = 5 * time.Minute // default + } + contractAddr := common.HexToAddress(ctx.GlobalString(flags.L1SequencerContractAddr.Name)) + seqPrivKeyHex := ctx.GlobalString(flags.SequencerPrivateKey.Name) + + // Initialize L1 Tracker + tracker := l1sequencer.NewL1Tracker(context.Background(), l1Client, lagThreshold, logger) + if err := tracker.Start(); err != nil { + return nil, nil, nil, fmt.Errorf("failed to start L1 tracker: %w", err) + } + logger.Info("L1 Tracker started", "lagThreshold", lagThreshold) + + // Initialize Sequencer Verifier (optional) + var verifier *l1sequencer.SequencerVerifier + if contractAddr != (common.Address{}) { + caller, err := bindings.NewL1SequencerCaller(contractAddr, l1Client) + if err != nil { + tracker.Stop() + return nil, nil, nil, fmt.Errorf("failed to create L1Sequencer caller: %w", err) + } + verifier = l1sequencer.NewSequencerVerifier(caller, logger) + logger.Info("Sequencer verifier initialized", "contract", contractAddr.Hex()) + } else { + logger.Info("L1 Sequencer contract not configured, verifier disabled") + } + + // Initialize Signer (optional) + var signer l1sequencer.Signer + if seqPrivKeyHex != "" { + seqPrivKeyHex = strings.TrimPrefix(seqPrivKeyHex, "0x") + privKey, err := crypto.HexToECDSA(seqPrivKeyHex) + if err != nil { + tracker.Stop() + return nil, nil, nil, fmt.Errorf("invalid sequencer private key: %w", err) + } + signer, err = l1sequencer.NewLocalSigner(privKey, verifier, logger) + if err != nil { + tracker.Stop() + return nil, nil, nil, err + } + logger.Info("Sequencer signer initialized", "address", signer.Address().Hex()) + } else { + logger.Info("Sequencer private key not configured, signer disabled") + } + + return tracker, verifier, signer, nil +} + +// initBlockTagService initializes the block tag service +func initBlockTagService( + ctx *cli.Context, + l1Client *ethclient.Client, + executor *node.Executor, + logger tmlog.Logger, +) (*blocktag.BlockTagService, error) { + config := blocktag.DefaultConfig() + if err := config.SetCliContext(ctx); err != nil { + return nil, err + } + + svc, err := blocktag.NewBlockTagService(context.Background(), l1Client, executor.L2Client(), config, logger) + if err != nil { + return nil, err + } + + if err := svc.Start(); err != nil { + return nil, err + } + + logger.Info("BlockTagService started") + return svc, nil +} + func homeDir(ctx *cli.Context) (string, error) { home := ctx.GlobalString(flags.Home.Name) if home == "" { diff --git a/node/flags/flags.go b/node/flags/flags.go index d344c62ce..3cfcfd87a 100644 --- a/node/flags/flags.go +++ b/node/flags/flags.go @@ -1,6 +1,9 @@ package flags -import "github.com/urfave/cli" +import ( + "github.com/urfave/cli" + "time" +) const envVarPrefix = "MORPH_NODE_" @@ -228,6 +231,27 @@ var ( Value: 10, } + // L1 Sequencer options + L1SequencerContractAddr = cli.StringFlag{ + Name: "l1.sequencerContract", + Usage: "L1 Sequencer contract address for signature verification", + EnvVar: prefixEnvVar("L1_SEQUENCER_CONTRACT"), + } + + L1SyncLagThreshold = cli.DurationFlag{ + Name: "l1.syncLagThreshold", + Usage: "L1 sync lag threshold for warning logs", + EnvVar: prefixEnvVar("L1_SYNC_LAG_THRESHOLD"), + Value: 5 * time.Minute, + } + + // Sequencer private key for block signing (hex encoded, without 0x prefix) + SequencerPrivateKey = cli.StringFlag{ + Name: "sequencer.privateKey", + Usage: "Sequencer private key for block signing (hex encoded)", + EnvVar: prefixEnvVar("SEQUENCER_PRIVATE_KEY"), + } + // Batch rules UpgradeBatchTime = cli.Uint64Flag{ Name: "upgrade.batchTime", @@ -349,6 +373,11 @@ var Flags = []cli.Flag{ // blocktag options BlockTagSafeConfirmations, + // L1 Sequencer options + L1SequencerContractAddr, + L1SyncLagThreshold, + SequencerPrivateKey, + // batch rules UpgradeBatchTime, MainnetFlag, diff --git a/node/l1sequencer/signer.go b/node/l1sequencer/signer.go new file mode 100644 index 000000000..f03901ae3 --- /dev/null +++ b/node/l1sequencer/signer.go @@ -0,0 +1,71 @@ +package l1sequencer + +import ( + "context" + "crypto/ecdsa" + "fmt" + + "github.com/morph-l2/go-ethereum/common" + "github.com/morph-l2/go-ethereum/crypto" + tmlog "github.com/tendermint/tendermint/libs/log" +) + +// Signer manages sequencer identity and signing capabilities. +// It abstracts the private key management, allowing for local key storage +// or remote signing services (e.g., HSM, KMS) in the future. +type Signer interface { + // Sign signs data with the sequencer's private key + Sign(data []byte) ([]byte, error) + + // Address returns the sequencer's address + Address() common.Address + + // IsActiveSequencer checks if this signer is the current L1 sequencer + IsActiveSequencer(ctx context.Context) (bool, error) +} + +// LocalSigner implements Signer with a local private key +type LocalSigner struct { + privKey *ecdsa.PrivateKey + address common.Address + verifier *SequencerVerifier + logger tmlog.Logger +} + +// NewLocalSigner creates a new LocalSigner with a local private key +func NewLocalSigner(privKey *ecdsa.PrivateKey, verifier *SequencerVerifier, logger tmlog.Logger) (*LocalSigner, error) { + if privKey == nil { + return nil, fmt.Errorf("private key is required") + } + + address := crypto.PubkeyToAddress(privKey.PublicKey) + + return &LocalSigner{ + privKey: privKey, + address: address, + verifier: verifier, + logger: logger.With("module", "signer"), + }, nil +} + +// Sign signs data with the sequencer's private key +func (s *LocalSigner) Sign(data []byte) ([]byte, error) { + signature, err := crypto.Sign(data, s.privKey) + if err != nil { + return nil, fmt.Errorf("failed to sign: %w", err) + } + return signature, nil +} + +// Address returns the sequencer's address +func (s *LocalSigner) Address() common.Address { + return s.address +} + +// IsActiveSequencer checks if this signer is the current L1 sequencer +func (s *LocalSigner) IsActiveSequencer(ctx context.Context) (bool, error) { + if s.verifier == nil { + return false, fmt.Errorf("sequencer verifier not set") + } + return s.verifier.IsSequencer(ctx, s.address) +} diff --git a/node/l1sequencer/tracker.go b/node/l1sequencer/tracker.go new file mode 100644 index 000000000..d8ea3b8c0 --- /dev/null +++ b/node/l1sequencer/tracker.go @@ -0,0 +1,86 @@ +package l1sequencer + +import ( + "context" + "time" + + "github.com/morph-l2/go-ethereum/ethclient" + tmlog "github.com/tendermint/tendermint/libs/log" +) + +// L1Tracker monitors L1 RPC sync status and logs warnings if behind. +// It runs as an independent service. +type L1Tracker struct { + ctx context.Context + cancel context.CancelFunc + l1Client *ethclient.Client + lagThreshold time.Duration + logger tmlog.Logger + stop chan struct{} +} + +// NewL1Tracker creates a new L1Tracker +func NewL1Tracker( + ctx context.Context, + l1Client *ethclient.Client, + lagThreshold time.Duration, + logger tmlog.Logger, +) *L1Tracker { + ctx, cancel := context.WithCancel(ctx) + return &L1Tracker{ + ctx: ctx, + cancel: cancel, + l1Client: l1Client, + lagThreshold: lagThreshold, + logger: logger.With("module", "l1tracker"), + stop: make(chan struct{}), + } +} + +// Start starts the L1Tracker +func (t *L1Tracker) Start() error { + t.logger.Info("Starting L1Tracker", "lagThreshold", t.lagThreshold) + go t.loop() + return nil +} + +// Stop stops the L1Tracker +func (t *L1Tracker) Stop() { + t.logger.Info("Stopping L1Tracker") + t.cancel() + <-t.stop +} + +func (t *L1Tracker) loop() { + defer close(t.stop) + + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-t.ctx.Done(): + return + case <-ticker.C: + t.checkL1SyncLag() + } + } +} + +func (t *L1Tracker) checkL1SyncLag() { + header, err := t.l1Client.HeaderByNumber(t.ctx, nil) + if err != nil { + t.logger.Error("Failed to get L1 header", "error", err) + return + } + + blockTime := time.Unix(int64(header.Time), 0) + lag := time.Since(blockTime) + if lag > t.lagThreshold { + t.logger.Error("L1 RPC is behind", + "latestBlock", header.Number.Uint64(), + "blockTime", blockTime.Format(time.RFC3339), + "lag", lag.Round(time.Second), + ) + } +} diff --git a/node/l1sequencer/verifier.go b/node/l1sequencer/verifier.go new file mode 100644 index 000000000..1cbf8517a --- /dev/null +++ b/node/l1sequencer/verifier.go @@ -0,0 +1,97 @@ +package l1sequencer + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/morph-l2/go-ethereum/accounts/abi/bind" + "github.com/morph-l2/go-ethereum/common" + tmlog "github.com/tendermint/tendermint/libs/log" + + "morph-l2/bindings/bindings" +) + +const ( + // CacheTTL is the time-to-live for the sequencer verifier cache + //CacheTTL = 30 * time.Minute + CacheTTL = 10 * time.Second +) + +// SequencerVerifier verifies L1 sequencer status with caching. +// It provides IsSequencer() for checking if an address is the current sequencer. +type SequencerVerifier struct { + mutex sync.Mutex + sequencer common.Address + cacheExpiry time.Time + + caller *bindings.L1SequencerCaller + logger tmlog.Logger +} + +// NewSequencerVerifier creates a new SequencerVerifier +func NewSequencerVerifier(caller *bindings.L1SequencerCaller, logger tmlog.Logger) *SequencerVerifier { + return &SequencerVerifier{ + caller: caller, + logger: logger.With("module", "l1sequencer_verifier"), + } +} + +// flushCache refreshes the cache (caller must hold the lock) +func (c *SequencerVerifier) flushCache(ctx context.Context) error { + newSeq, err := c.caller.GetSequencer(&bind.CallOpts{Context: ctx}) + if err != nil { + return fmt.Errorf("failed to get sequencer from L1: %w", err) + } + + if c.sequencer != newSeq { + c.logger.Info("Sequencer address updated", + "old", c.sequencer.Hex(), + "new", newSeq.Hex()) + } + + c.sequencer = newSeq + c.cacheExpiry = time.Now().Add(CacheTTL) + return nil +} + +// IsSequencer checks if the given address is the current sequencer. +// It uses lazy loading: refreshes cache if expired, and retries on miss. +func (c *SequencerVerifier) IsSequencer(ctx context.Context, addr common.Address) (bool, error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + // Cache expired, refresh + if time.Now().After(c.cacheExpiry) { + if err := c.flushCache(ctx); err != nil { + return false, err + } + } + + // Cache hit + if c.sequencer == addr { + return true, nil + } + + // Cache miss - maybe sequencer just updated, force refresh once + if err := c.flushCache(ctx); err != nil { + return false, err + } + + return c.sequencer == addr, nil +} + +// GetSequencer returns the cached sequencer address (refreshes if expired) +func (c *SequencerVerifier) GetSequencer(ctx context.Context) (common.Address, error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + if time.Now().After(c.cacheExpiry) { + if err := c.flushCache(ctx); err != nil { + return common.Address{}, err + } + } + + return c.sequencer, nil +} diff --git a/node/sequencer/tm_node.go b/node/sequencer/tm_node.go index 6f6bf994c..b9cbffe38 100644 --- a/node/sequencer/tm_node.go +++ b/node/sequencer/tm_node.go @@ -14,11 +14,13 @@ import ( tmnode "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" + tmsequencer "github.com/tendermint/tendermint/sequencer" "github.com/tendermint/tendermint/types" "github.com/urfave/cli" node "morph-l2/node/core" "morph-l2/node/flags" + "morph-l2/node/l1sequencer" nodetypes "morph-l2/node/types" ) @@ -51,7 +53,17 @@ func LoadTmConfig(ctx *cli.Context, home string) (*config.Config, error) { return tmCfg, nil } -func SetupNode(tmCfg *config.Config, privValidator types.PrivValidator, executor *node.Executor, logger tmlog.Logger) (*tmnode.Node, error) { +// SetupNode creates a tendermint node with the given configuration. +// verifier: L1 sequencer verifier for signature verification (optional, can be nil) +// signer: sequencer signer for block signing (optional, can be nil) +func SetupNode( + tmCfg *config.Config, + privValidator types.PrivValidator, + executor *node.Executor, + logger tmlog.Logger, + verifier *l1sequencer.SequencerVerifier, + signer l1sequencer.Signer, +) (*tmnode.Node, error) { nodeLogger := logger.With("module", "main") nodeKey, err := p2p.LoadOrGenNodeKey(tmCfg.NodeKeyFile()) @@ -67,7 +79,12 @@ func SetupNode(tmCfg *config.Config, privValidator types.PrivValidator, executor return nil, fmt.Errorf("failed to load bls priv key") } - //var app types.Application + // Build verifier (SequencerVerifier implements tmsequencer.SequencerVerifier interface) + var tmVerifier tmsequencer.SequencerVerifier + if verifier != nil { + tmVerifier = verifier + } + n, err := tmnode.NewNode( tmCfg, executor, @@ -79,6 +96,8 @@ func SetupNode(tmCfg *config.Config, privValidator types.PrivValidator, executor tmnode.DefaultDBProvider, tmnode.DefaultMetricsProvider(tmCfg.Instrumentation), nodeLogger, + tmVerifier, + signer, ) return n, err } diff --git a/node/sync/bridge_client.go b/node/sync/bridge_client.go index bab51c45e..e8a73abf3 100644 --- a/node/sync/bridge_client.go +++ b/node/sync/bridge_client.go @@ -24,6 +24,11 @@ type BridgeClient struct { logger tmlog.Logger } +// L1Client returns the underlying L1 client (for sharing with other services) +func (c *BridgeClient) L1Client() *ethclient.Client { + return c.l1Client +} + func NewBridgeClient(l1Client *ethclient.Client, l1MessageQueueAddress common.Address, confirmations rpc.BlockNumber, logger tmlog.Logger) (*BridgeClient, error) { logger = logger.With("module", "bridge") filter, err := bindings.NewL1MessageQueueWithGasPriceOracleFilterer(l1MessageQueueAddress, l1Client) diff --git a/node/sync/syncer.go b/node/sync/syncer.go index 9c109ac06..c9948983a 100644 --- a/node/sync/syncer.go +++ b/node/sync/syncer.go @@ -196,3 +196,11 @@ func (s *Syncer) ReadL1MessagesInRange(start, end uint64) []types.L1Message { func (s *Syncer) LatestSynced() uint64 { return s.latestSynced } + +// L1Client returns the underlying L1 client (for sharing with other services) +func (s *Syncer) L1Client() *ethclient.Client { + if s.bridgeClient != nil { + return s.bridgeClient.L1Client() + } + return nil +} diff --git a/ops/devnet-morph/devnet/__init__.py b/ops/devnet-morph/devnet/__init__.py index 4ec81c897..385a7a2a3 100644 --- a/ops/devnet-morph/devnet/__init__.py +++ b/ops/devnet-morph/devnet/__init__.py @@ -248,6 +248,7 @@ def devnet_deploy(paths, args): env_data['MORPH_ROLLUP'] = addresses['Proxy__Rollup'] env_data['RUST_LOG'] = rust_log_level env_data['Proxy__L1Staking'] = addresses['Proxy__L1Staking'] + env_data['L1_SEQUENCER_CONTRACT'] = addresses.get('Proxy__L1Sequencer', '') envfile.seek(0) for key, value in env_data.items(): envfile.write(f'{key}={value}\n') diff --git a/ops/docker-sequencer-test/docker-compose.override.yml b/ops/docker-sequencer-test/docker-compose.override.yml index 50d960e27..ac4efdd4f 100644 --- a/ops/docker-sequencer-test/docker-compose.override.yml +++ b/ops/docker-sequencer-test/docker-compose.override.yml @@ -15,8 +15,8 @@ services: morph-geth-2: image: morph-geth-test:latest - # morph-geth-3: - # image: morph-geth-test:latest + morph-geth-3: + image: morph-geth-test:latest node-0: image: morph-node-test:latest @@ -24,16 +24,32 @@ services: context: ../.. dockerfile: ops/docker-sequencer-test/Dockerfile.l2-node-test environment: - - SEQUENCER_PRIVATE_KEY=${SEQUENCER_PRIVATE_KEY:-0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80} + - MORPH_NODE_SEQUENCER_PRIVATE_KEY=0xd99870855d97327d20c666abc78588f1449b1fac76ed0c86c1afb9ce2db85f32 + - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_ROLLUP_ADDRESS=${MORPH_ROLLUP:-0x6900000000000000000000000000000000000010} + node-1: image: morph-node-test:latest + environment: + - MORPH_NODE_SEQUENCER_PRIVATE_KEY=0x0890c388c3bf5e04fee1d8f3c117e5f44f435ced7baf7bfd66c10e1f3a3f4b10 + - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_ROLLUP_ADDRESS=${MORPH_ROLLUP:-0x6900000000000000000000000000000000000010} + node-2: image: morph-node-test:latest + environment: + - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_ROLLUP_ADDRESS=${MORPH_ROLLUP:-0x6900000000000000000000000000000000000010} + + + node-3: + image: morph-node-test:latest + environment: + - MORPH_NODE_L1_SEQUENCER_CONTRACT=${L1_SEQUENCER_CONTRACT} + - MORPH_NODE_ROLLUP_ADDRESS=${MORPH_ROLLUP:-0x6900000000000000000000000000000000000010} - # node-3: - # image: morph-node-test:latest sentry-geth-0: image: morph-geth-test:latest diff --git a/ops/docker-sequencer-test/run-test.sh b/ops/docker-sequencer-test/run-test.sh index 823ba5273..3399f2433 100755 --- a/ops/docker-sequencer-test/run-test.sh +++ b/ops/docker-sequencer-test/run-test.sh @@ -234,6 +234,7 @@ with open(env_file, 'r+') as envfile: env_data['MORPH_PORTAL'] = addresses['Proxy__L1MessageQueueWithGasPriceOracle'] env_data['MORPH_ROLLUP'] = addresses['Proxy__Rollup'] env_data['MORPH_L1STAKING'] = addresses['Proxy__L1Staking'] + env_data['L1_SEQUENCER_CONTRACT'] = addresses.get('Proxy__L1Sequencer', '') envfile.seek(0) for key, value in env_data.items(): envfile.write(f'{key}={value}\n')