diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go index 6290347b4e3e..970919215d09 100644 --- a/cmd/faucet/faucet.go +++ b/cmd/faucet/faucet.go @@ -254,7 +254,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network ui cfg.Genesis = genesis utils.SetDNSDiscoveryDefaults(&cfg, genesis.ToBlock(nil).Hash()) - lesBackend, err := les.New(stack, &cfg) + lesBackend, err := les.New(stack, &cfg, nil) if err != nil { return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err) } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index bccd6017b36e..5b9d814879f8 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -1981,16 +1981,6 @@ func SetDNSDiscoveryDefaults(cfg *ethconfig.Config, genesis common.Hash) { // The second return value is the full node instance, which may be nil if the // node is running as a light client. func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend, *eth.Ethereum) { - if cfg.SyncMode == downloader.LightSync { - backend, err := les.New(stack, cfg) - if err != nil { - Fatalf("Failed to register the Ethereum service: %v", err) - } - scrollTracerWrapper := tracing.NewTracerWrapper() - stack.RegisterAPIs(tracers.APIs(backend.ApiBackend, scrollTracerWrapper)) - return backend.ApiBackend, nil - } - // initialize L1 client for sync service // note: we need to do this here to avoid circular dependency l1EndpointUrl := stack.Config().L1Endpoint @@ -2006,6 +1996,16 @@ func RegisterEthService(stack *node.Node, cfg *ethconfig.Config) (ethapi.Backend log.Info("Initialized L1 client", "endpoint", l1EndpointUrl) } + if cfg.SyncMode == downloader.LightSync { + backend, err := les.New(stack, cfg, l1Client) + if err != nil { + Fatalf("Failed to register the Ethereum service: %v", err) + } + scrollTracerWrapper := tracing.NewTracerWrapper() + stack.RegisterAPIs(tracers.APIs(backend.ApiBackend, scrollTracerWrapper)) + return backend.ApiBackend, nil + } + backend, err := eth.New(stack, cfg, l1Client) if err != nil { Fatalf("Failed to register the Ethereum service: %v", err) diff --git a/consensus/system_contract/api.go b/consensus/system_contract/api.go new file mode 100644 index 000000000000..9e201766ebbe --- /dev/null +++ b/consensus/system_contract/api.go @@ -0,0 +1,18 @@ +package system_contract + +import ( + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/consensus" + "github.com/scroll-tech/go-ethereum/rpc" +) + +// API is a user facing RPC API to allow controlling the signer and voting +// mechanisms of the proof-of-authority scheme. +type API struct { + chain consensus.ChainHeaderReader +} + +// GetSigners retrieves the list of authorized signers at the specified block. +func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { + return nil, nil +} \ No newline at end of file diff --git a/consensus/system_contract/consensus.go b/consensus/system_contract/consensus.go new file mode 100644 index 000000000000..878012f42827 --- /dev/null +++ b/consensus/system_contract/consensus.go @@ -0,0 +1,371 @@ +package system_contract + +import ( + "bytes" + "errors" + "fmt" + "io" + "math/big" + "time" + + "github.com/scroll-tech/go-ethereum/accounts" + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/consensus" + "github.com/scroll-tech/go-ethereum/consensus/misc" + "github.com/scroll-tech/go-ethereum/core/state" + "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/crypto" + "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rlp" + "github.com/scroll-tech/go-ethereum/rpc" + "github.com/scroll-tech/go-ethereum/trie" + "golang.org/x/crypto/sha3" +) + +var ( + extraSeal = crypto.SignatureLength // Fixed number of extra-data suffix bytes reserved for signer seal + uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW. +) + +// Various error messages to mark blocks invalid. These should be private to +// prevent engine specific errors from being referenced in the remainder of the +// codebase, inherently breaking if the engine is swapped out. Please put common +// error types into the consensus package. +var ( + // errUnknownBlock is returned when the list of signers is requested for a block + // that is not part of the local blockchain. + errUnknownBlock = errors.New("unknown block") + // errCoinbaseNotEmpty is returned if a coinbase value is non-zero + errInvalidCoinbase = errors.New("coinbase not empty nor zero") + // errNonceNotEmpty is returned if a nonce value is non-zero + errInvalidNonce = errors.New("nonce not empty nor zero") + // errMissingSignature is returned if a block's extra-data section doesn't seem + // to contain a 65 byte secp256k1 signature. + errMissingSignature = errors.New("extra-data 65 byte signature missing") + // errInvalidMixDigest is returned if a block's mix digest is non-zero. + errInvalidMixDigest = errors.New("non-zero mix digest") + // errInvalidUncleHash is returned if a block contains an non-empty uncle list. + errInvalidUncleHash = errors.New("non empty uncle hash") + // errInvalidDifficulty is returned if a difficulty value is non-zero + errInvalidDifficulty = errors.New("non-one difficulty") + // errInvalidTimestamp is returned if the timestamp of a block is lower than + // the previous block's timestamp + the minimum block period. + errInvalidTimestamp = errors.New("invalid timestamp") + // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity. + errUnauthorizedSigner = errors.New("unauthorized signer") +) + +// SignerFn hashes and signs the data to be signed by a backing account. +type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error) + +// Author implements consensus.Engine, returning the Ethereum address recovered +// from the signature in the header's extra-data section. +func (s *SystemContract) Author(header *types.Header) (common.Address, error) { + return ecrecover(header) +} + +// VerifyHeader checks whether a header conforms to the consensus rules of a +// given engine. +func (s *SystemContract) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { + return s.verifyHeader(chain, header, nil) +} + +// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers +// concurrently. The method returns a quit channel to abort the operations and +// a results channel to retrieve the async verifications (the order is that of +// the input slice). +func (s *SystemContract) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { + abort := make(chan struct{}) + results := make(chan error, len(headers)) + + go func() { + for i, header := range headers { + err := s.verifyHeader(chain, header, headers[:i]) + + select { + case <-abort: + return + case results <- err: + } + } + }() + return abort, results +} + +// verifyHeader checks whether a header conforms to the consensus rules.The +// caller may optionally pass in a batch of parents (ascending order) to avoid +// looking those up from the database. This is useful for concurrently verifying +// a batch of new headers. +func (s *SystemContract) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + if header.Number == nil { + return errUnknownBlock + } + + // Don't waste time checking blocks from the future + if header.Time > uint64(time.Now().Unix()) { + return consensus.ErrFutureBlock + } + // Ensure that the nonce is zero + if header.Nonce != (types.BlockNonce{}) { + return errInvalidNonce + } + // Check that the extra-data contains signature + if len(header.Extra) != extraSeal { + return errMissingSignature + } + // Ensure that the mix digest is zero + if header.MixDigest != (common.Hash{}) { + return errInvalidMixDigest + } + // Ensure that the block doesn't contain any uncles which are meaningless in PoA + if header.UncleHash != uncleHash { + return errInvalidUncleHash + } + // Ensure that the difficulty is zero + if header.Difficulty.Cmp(common.Big0) != 1 { + return errInvalidDifficulty + } + // Verify that the gas limit is <= 2^63-1 + cap := uint64(0x7fffffffffffffff) + if header.GasLimit > cap { + return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap) + } + // All basic checks passed, verify cascading fields + return s.verifyCascadingFields(chain, header, parents) +} + +// verifyCascadingFields verifies all the header fields that are not standalone, +// rather depend on a batch of previous headers. The caller may optionally pass +// in a batch of parents (ascending order) to avoid looking those up from the +// database. This is useful for concurrently verifying a batch of new headers. +func (s *SystemContract) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error { + // The genesis block is the always valid dead-end + number := header.Number.Uint64() + if number == 0 { + return nil + } + // Ensure that the block's timestamp isn't too close to its parent + var parent *types.Header + if len(parents) > 0 { + parent = parents[len(parents)-1] + } else { + parent = chain.GetHeader(header.ParentHash, number-1) + } + if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash { + return consensus.ErrUnknownAncestor + } + if header.Time < parent.Time { + return errInvalidTimestamp + } + // Verify that the gasUsed is <= gasLimit + if header.GasUsed > header.GasLimit { + return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) + } + if !chain.Config().IsCurie(header.Number) { + // Verify BaseFee not present before EIP-1559 fork. + if header.BaseFee != nil { + return fmt.Errorf("invalid baseFee before fork: have %d, want ", header.BaseFee) + } + if err := misc.VerifyGaslimit(parent.GasLimit, header.GasLimit); err != nil { + return err + } + } else if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil { + // Verify the header's EIP-1559 attributes. + return err + } + + signer, err := ecrecover(header) + if err != nil { + return err + } + + s.lock.Lock() + defer s.lock.Unlock() + + if signer != s.signerAddressL1 { + return errUnauthorizedSigner + } + return nil +} + +// VerifyUncles implements consensus.Engine, always returning an error for any +// uncles as this consensus mechanism doesn't permit uncles. +func (s *SystemContract) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { + if len(block.Uncles()) > 0 { + return errors.New("uncles not allowed") + } + return nil +} + +// Prepare initializes the consensus fields of a block header according to the +// rules of a particular engine. Update only timestamp and prepare ExtraData for Signature +func (s *SystemContract) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { + header.Extra = make([]byte, extraSeal) + // Ensure the timestamp has the correct delay + parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) + if parent == nil { + return consensus.ErrUnknownAncestor + } + header.Time = parent.Time + s.config.Period + // If RelaxedPeriod is enabled, always set the header timestamp to now (ie the time we start building it) as + // we don't know when it will be sealed + if s.config.RelaxedPeriod || header.Time < uint64(time.Now().Unix()) { + header.Time = uint64(time.Now().Unix()) + } + header.Difficulty = big.NewInt(1) + return nil +} + +// Finalize implements consensus.Engine. There is no post-transaction +// consensus rules in clique, therefore no rules here +func (s *SystemContract) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) { + // No block rewards in PoA, so the state remains as is +} + +// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, +// nor block rewards given, and returns the final block. +func (s *SystemContract) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + // Finalize block + s.Finalize(chain, header, state, txs, uncles) + + // Assign the final state root to header. + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + + // Assemble and return the final block for sealing. + return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil +} + +// Seal implements consensus.Engine, attempting to create a sealed block using +// the local signing credentials. +func (s *SystemContract) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { + header := block.Header() + + // Sealing the genesis block is not supported + number := header.Number.Uint64() + if number == 0 { + return errUnknownBlock + } + // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing) + if s.config.Period == 0 && len(block.Transactions()) == 0 { + return errors.New("sealing paused while waiting for transactions") + } + // Don't hold the signer fields for the entire sealing procedure + s.lock.RLock() + signer, signFn := s.signer, s.signFn + s.lock.RUnlock() + + // Bail out if we're unauthorized to sign a block + // todo + + // Sweet, the protocol permits us to sign the block, wait for our time + delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple + + // Sign all the things! + sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeClique, SystemContractRLP(header)) + if err != nil { + return err + } + copy(header.Extra[0:], sighash) + // Wait until sealing is terminated or delay timeout. + log.Trace("Waiting for slot to sign and propagate", "delay", common.PrettyDuration(delay)) + go func() { + defer close(results) + + select { + case <-stop: + return + case <-time.After(delay): + } + + select { + case results <- block.WithSeal(header): + case <-time.After(time.Second): + log.Warn("Sealing result is not read by miner", "sealhash", SealHash(header)) + } + }() + + return nil +} + +// SealHash returns the hash of a block prior to it being sealed. +func (s *SystemContract) SealHash(header *types.Header) (hash common.Hash) { + return SealHash(header) +} + +// SealHash returns the hash of a block prior to it being sealed. +func SealHash(header *types.Header) (hash common.Hash) { + hasher := sha3.NewLegacyKeccak256() + encodeSigHeader(hasher, header) + hasher.(crypto.KeccakState).Read(hash[:]) + return hash +} + + +// ecrecover extracts the Ethereum account address from a signed header. +func ecrecover(header *types.Header) (common.Address, error) { + signature := header.Extra[0:] + + // Recover the public key and the Ethereum address + pubkey, err := crypto.Ecrecover(SealHash(header).Bytes(), signature) + if err != nil { + return common.Address{}, err + } + var signer common.Address + copy(signer[:], crypto.Keccak256(pubkey[1:])[12:]) + + return signer, nil +} + +// SystemContractRLP returns the rlp bytes which needs to be signed for the system contract +// sealing. The RLP to sign consists of the entire header apart from the ExtraData +// +// Note, the method requires the extra data to be at least 65 bytes, otherwise it +// panics. This is done to avoid accidentally using both forms (signature present +// or not), which could be abused to produce different hashes for the same header. +func SystemContractRLP(header *types.Header) []byte { + b := new(bytes.Buffer) + encodeSigHeader(b, header) + return b.Bytes() +} + +// CalcDifficulty implements consensus.Engine. There is no difficulty rules here +func (s *SystemContract) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { + return nil +} + +// APIs implements consensus.Engine, returning the user facing RPC API to allow +// controlling the signer voting. +func (s *SystemContract) APIs(chain consensus.ChainHeaderReader) []rpc.API { + return []rpc.API{{ + Namespace: "system_contract", + Service: &API{}, + }} +} + +func encodeSigHeader(w io.Writer, header *types.Header) { + enc := []interface{}{ + header.ParentHash, + header.UncleHash, + header.Coinbase, + header.Root, + header.TxHash, + header.ReceiptHash, + header.Bloom, + header.Difficulty, + header.Number, + header.GasLimit, + header.GasUsed, + header.Time, + header.MixDigest, + header.Nonce, + } + if header.BaseFee != nil { + enc = append(enc, header.BaseFee) + } + if header.WithdrawalsHash != nil { + panic("unexpected withdrawal hash value in clique") + } + if err := rlp.Encode(w, enc); err != nil { + panic("can't encode: " + err.Error()) + } +} \ No newline at end of file diff --git a/consensus/system_contract/system_contract.go b/consensus/system_contract/system_contract.go new file mode 100644 index 000000000000..f77a80eb2d4c --- /dev/null +++ b/consensus/system_contract/system_contract.go @@ -0,0 +1,98 @@ +package system_contract + +import ( + "context" + "math/big" + "sync" + "time" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/params" + "github.com/scroll-tech/go-ethereum/rollup/sync_service" +) + +const ( + defaultSyncInterval = 10 +) + +// SystemContract +type SystemContract struct { + config *params.SystemContractConfig // Consensus engine configuration parameters + client sync_service.EthClient + + signerAddressL1 common.Address // Address of the signer stored in L1 System Contract + + signer common.Address // Ethereum address of the signing key + signFn SignerFn // Signer function to authorize hashes with + lock sync.RWMutex // Protects the signer and proposals fields + + ctx context.Context + cancel context.CancelFunc +} + +// New creates a SystemContract consensus engine with the initial +// signers set to the ones provided by the user. +func New(ctx context.Context, config *params.SystemContractConfig, client sync_service.EthClient) *SystemContract { + blockNumber := big.NewInt(-1) // todo: get block number from L1BlocksContract (l1 block hash relay) or other source (depending on exact design) + ctx, cancel := context.WithCancel(ctx) + address, err := client.StorageAt(ctx, config.SystemContractAddress, config.SystemContractSlot, blockNumber) + if err != nil { + log.Error("failed to get signer address from L1 System Contract", "err", err) + } + systemContract := &SystemContract{ + config: config, + client: client, + signerAddressL1: common.BytesToAddress(address), + + ctx: ctx, + cancel: cancel, + } + systemContract.Start() + return systemContract +} + +// Authorize injects a private key into the consensus engine to mint new blocks +// with. +func (s *SystemContract) Authorize(signer common.Address, signFn SignerFn) { + s.lock.Lock() + defer s.lock.Unlock() + + s.signer = signer + s.signFn = signFn +} + +func (s *SystemContract) Start() { + log.Info("starting SystemContract") + go func() { + syncTicker := time.NewTicker(defaultSyncInterval) + defer syncTicker.Stop() + for { + select { + case <-s.ctx.Done(): + return + default: + } + select { + case <-s.ctx.Done(): + return + case <-syncTicker.C: + blockNumber := big.NewInt(-1) // todo: get block number from L1BlocksContract (l1 block hash relay) or other source (depending on exact design) + + address, err := s.client.StorageAt(s.ctx, s.config.SystemContractAddress, s.config.SystemContractSlot, blockNumber) + if err != nil { + log.Error("failed to get signer address from L1 System Contract", "err", err) + } + s.lock.Lock() + s.signerAddressL1 = common.BytesToAddress(address) + s.lock.Unlock() + } + } + }() +} + +// Close implements consensus.Engine. +func (s *SystemContract) Close() error { + s.cancel() + return nil +} \ No newline at end of file diff --git a/eth/backend.go b/eth/backend.go index 2b6c663d2744..acbec39418af 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -32,6 +32,7 @@ import ( "github.com/scroll-tech/go-ethereum/common/hexutil" "github.com/scroll-tech/go-ethereum/consensus" "github.com/scroll-tech/go-ethereum/consensus/clique" + "github.com/scroll-tech/go-ethereum/consensus/system_contract" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/core/bloombits" "github.com/scroll-tech/go-ethereum/core/rawdb" @@ -155,7 +156,7 @@ func New(stack *node.Node, config *ethconfig.Config, l1Client sync_service.EthCl chainDb: chainDb, eventMux: stack.EventMux(), accountManager: stack.AccountManager(), - engine: ethconfig.CreateConsensusEngine(stack, chainConfig, ðashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, ðashConfig, config.Miner.Notify, config.Miner.Noverify, chainDb, l1Client), closeBloomHandler: make(chan struct{}), networkID: config.NetworkId, gasPrice: config.Miner.GasPrice, @@ -529,6 +530,13 @@ func (s *Ethereum) StartMining(threads int) error { return fmt.Errorf("signer missing: %v", err) } clique.Authorize(eb, wallet.SignData) + } else if systemContract, ok := s.engine.(*system_contract.SystemContract); ok { + wallet, err := s.accountManager.Find(accounts.Account{Address: eb}) + if wallet == nil || err != nil { + log.Error("Etherbase account unavailable locally", "err", err) + return fmt.Errorf("signer missing: %v", err) + } + systemContract.Authorize(eb, wallet.SignData) } // If mining is started, we can disable the transaction rejection mechanism // introduced to speed sync times. diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index e8c7a5aa178c..129118e0df8e 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -18,6 +18,7 @@ package ethconfig import ( + "context" "math/big" "os" "os/user" @@ -29,6 +30,7 @@ import ( "github.com/scroll-tech/go-ethereum/consensus" "github.com/scroll-tech/go-ethereum/consensus/clique" "github.com/scroll-tech/go-ethereum/consensus/ethash" + "github.com/scroll-tech/go-ethereum/consensus/system_contract" "github.com/scroll-tech/go-ethereum/core" "github.com/scroll-tech/go-ethereum/eth/downloader" "github.com/scroll-tech/go-ethereum/eth/gasprice" @@ -38,6 +40,7 @@ import ( "github.com/scroll-tech/go-ethereum/node" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/rollup/da_syncer" + "github.com/scroll-tech/go-ethereum/rollup/sync_service" ) // FullNodeGPO contains default gasprice oracle settings for full node. @@ -228,11 +231,14 @@ type Config struct { } // CreateConsensusEngine creates a consensus engine for the given chain configuration. -func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine { +func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database, l1Client sync_service.EthClient) consensus.Engine { // If proof-of-authority is requested, set it up if chainConfig.Clique != nil { return clique.New(chainConfig.Clique, db) } + if chainConfig.SystemContract != nil { + return system_contract.New(context.Background(), chainConfig.SystemContract, l1Client) + } // Otherwise assume proof-of-work switch config.PowMode { case ethash.ModeFake: diff --git a/les/api_test.go b/les/api_test.go index ad47ff02d6a1..28634c7c86d0 100644 --- a/les/api_test.go +++ b/les/api_test.go @@ -498,7 +498,7 @@ func newLesClientService(ctx *adapters.ServiceContext, stack *node.Node) (node.L config := ethconfig.Defaults config.SyncMode = (ethdownloader.SyncMode)(downloader.LightSync) config.Ethash.PowMode = ethash.ModeFake - return New(stack, &config) + return New(stack, &config, nil) } func newLesServerService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { diff --git a/les/client.go b/les/client.go index 311f78a658a5..2918d4d68f9d 100644 --- a/les/client.go +++ b/les/client.go @@ -46,6 +46,7 @@ import ( "github.com/scroll-tech/go-ethereum/p2p/enr" "github.com/scroll-tech/go-ethereum/params" "github.com/scroll-tech/go-ethereum/rlp" + "github.com/scroll-tech/go-ethereum/rollup/sync_service" "github.com/scroll-tech/go-ethereum/rpc" ) @@ -79,7 +80,7 @@ type LightEthereum struct { } // New creates an instance of the light client. -func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { +func New(stack *node.Node, config *ethconfig.Config, l1Client sync_service.EthClient) (*LightEthereum, error) { chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/", false) if err != nil { return nil, err @@ -109,7 +110,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) { eventMux: stack.EventMux(), reqDist: newRequestDistributor(peers, &mclock.System{}), accountManager: stack.AccountManager(), - engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb), + engine: ethconfig.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb, l1Client), bloomRequests: make(chan chan *bloombits.Retrieval), bloomIndexer: core.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations), p2pServer: stack.Server(), diff --git a/mobile/geth.go b/mobile/geth.go index 6b1dfebb1bf8..d1c252adfc4b 100644 --- a/mobile/geth.go +++ b/mobile/geth.go @@ -194,7 +194,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { ethConf.SyncMode = downloader.LightSync ethConf.NetworkId = uint64(config.EthereumNetworkID) ethConf.DatabaseCache = config.EthereumDatabaseCache - lesBackend, err := les.New(rawStack, ðConf) + lesBackend, err := les.New(rawStack, ðConf, nil) if err != nil { return nil, fmt.Errorf("ethereum init: %v", err) } diff --git a/params/config.go b/params/config.go index eb2213fdd360..dde4856781c3 100644 --- a/params/config.go +++ b/params/config.go @@ -639,8 +639,9 @@ type ChainConfig struct { TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` // Various consensus engines - Ethash *EthashConfig `json:"ethash,omitempty"` - Clique *CliqueConfig `json:"clique,omitempty"` + Ethash *EthashConfig `json:"ethash,omitempty"` + Clique *CliqueConfig `json:"clique,omitempty"` + SystemContract *SystemContractConfig `json:"systemContract,omitempty"` // Scroll genesis extension: enable scroll rollup-related traces & state transition Scroll ScrollConfig `json:"scroll,omitempty"` @@ -745,6 +746,21 @@ func (c *CliqueConfig) String() string { return "clique" } +// SystemContractConfig is the consensus engine configs for rollup sequencer sealing. +type SystemContractConfig struct { + Period uint64 `json:"period"` // Number of seconds between blocks to enforce + + SystemContractAddress common.Address `json:"system_contract_address"` // address of system contract on L1 + SystemContractSlot common.Hash `json:"system_contract_slot"` // slot of signer address in system contract on L1 + + RelaxedPeriod bool `json:"relaxed_period"` // Relaxes the period to be just an upper bound +} + +// String implements the stringer interface, returning the consensus engine details. +func (c *SystemContractConfig) String() string { + return "system_contract" +} + // String implements the fmt.Stringer interface. func (c *ChainConfig) String() string { var engine interface{} diff --git a/rollup/rollup_sync_service/l1client_test.go b/rollup/rollup_sync_service/l1client_test.go index 394f455b80c5..8a42bfb3068e 100644 --- a/rollup/rollup_sync_service/l1client_test.go +++ b/rollup/rollup_sync_service/l1client_test.go @@ -72,3 +72,8 @@ func (m *mockEthClient) TransactionByHash(ctx context.Context, txHash common.Has func (m *mockEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { return nil, nil } + +func (m *mockEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { + return nil, nil +} + diff --git a/rollup/sync_service/types.go b/rollup/sync_service/types.go index 3429ec1bb778..08b4dda2ecf0 100644 --- a/rollup/sync_service/types.go +++ b/rollup/sync_service/types.go @@ -19,4 +19,5 @@ type EthClient interface { SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) }