go-ethereum/consensus/system_contract/system_contract.go
Péter Garamvölgyi 478940e796
feat: Euclid release on Scroll Sepolia (#1122)
* feat: Euclid release on Scroll Sepolia (wip)

* update system contract consensus params

* update timestamp

* improve logs

* fix

* adjust logs

* undo some changes
2025-03-05 16:10:38 +01:00

142 lines
3.7 KiB
Go

package system_contract
import (
"context"
"fmt"
"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 * time.Second
)
// SystemContract is the proof-of-authority consensus engine that fetches
// the authorized signer from the SystemConfig contract, starting from EuclidV2.
type SystemContract struct {
config *params.SystemContractConfig // Consensus engine configuration parameters
client sync_service.EthClient // RPC client to fetch info from L1
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
// authorized signer fetched from L1 (if available).
func New(ctx context.Context, config *params.SystemContractConfig, client sync_service.EthClient) *SystemContract {
log.Info("Initializing system_contract consensus engine", "config", config)
ctx, cancel := context.WithCancel(ctx)
s := &SystemContract{
config: config,
client: client,
ctx: ctx,
cancel: cancel,
}
if err := s.fetchAddressFromL1(); err != nil {
log.Error("Failed to fetch signer address from L1", "err", err)
}
return s
}
// Authorize injects a private key into the consensus engine to mint new blocks
// with.
func (s *SystemContract) Authorize(signer common.Address, signFn SignerFn) {
log.Info("Authorizing system contract consensus", "signer", signer.Hex())
s.lock.Lock()
defer s.lock.Unlock()
s.signer = signer
s.signFn = signFn
}
func (s *SystemContract) Start() {
go func() {
log.Info("starting SystemContract")
syncTicker := time.NewTicker(defaultSyncInterval)
defer syncTicker.Stop()
for {
select {
case <-s.ctx.Done():
return
default:
}
select {
case <-s.ctx.Done():
return
case <-syncTicker.C:
if err := s.fetchAddressFromL1(); err != nil {
log.Error("Failed to fetch signer address from L1", "err", err)
}
}
}
}()
}
func (s *SystemContract) fetchAddressFromL1() error {
address, err := s.client.StorageAt(s.ctx, s.config.SystemContractAddress, s.config.SystemContractSlot, nil)
if err != nil {
return fmt.Errorf("failed to get signer address from L1 System Contract: %w", err)
}
bAddress := common.BytesToAddress(address)
s.lock.Lock()
defer s.lock.Unlock()
// Validate the address is not empty
if bAddress == (common.Address{}) {
log.Debug("Retrieved empty signer address from L1 System Contract", "contract", s.config.SystemContractAddress.Hex(), "slot", s.config.SystemContractSlot.Hex())
// Not initialized yet -- we don't consider this an error
if s.signerAddressL1 == (common.Address{}) {
log.Warn("System Contract signer address not initialized")
return nil
}
return fmt.Errorf("retrieved empty signer address from L1 System Contract")
}
log.Debug("Read address from system contract", "address", bAddress.Hex())
if s.signerAddressL1 != bAddress {
s.signerAddressL1 = bAddress
log.Info("Updated new signer from L1 system contract", "signer", bAddress.Hex())
}
return nil
}
// Close implements consensus.Engine.
func (s *SystemContract) Close() error {
s.cancel()
return nil
}
func (s *SystemContract) currentSignerAddressL1() common.Address {
s.lock.RLock()
defer s.lock.RUnlock()
return s.signerAddressL1
}
func (s *SystemContract) localSignerAddress() common.Address {
s.lock.RLock()
defer s.lock.RUnlock()
return s.signer
}