mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-24 05:36:46 +00:00
* feat: address system contract consensus issues and comments * fix Euclid header chain verification
218 lines
8.1 KiB
Go
218 lines
8.1 KiB
Go
package wrapper
|
|
|
|
import (
|
|
"math/big"
|
|
"time"
|
|
|
|
"github.com/scroll-tech/go-ethereum/common"
|
|
"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/state"
|
|
"github.com/scroll-tech/go-ethereum/core/types"
|
|
"github.com/scroll-tech/go-ethereum/log"
|
|
"github.com/scroll-tech/go-ethereum/rpc"
|
|
)
|
|
|
|
// UpgradableEngine implements consensus.Engine and acts as a middleware to dispatch
|
|
// calls to either Clique or SystemContract consensus.
|
|
type UpgradableEngine struct {
|
|
// isUpgraded takes a block timestamp, and returns true once the engine should be upgraded to SystemContract.
|
|
isUpgraded func(uint64) bool
|
|
|
|
// clique is the original Clique consensus engine.
|
|
clique consensus.Engine
|
|
|
|
// system is the new SystemContract consensus engine.
|
|
system consensus.Engine
|
|
}
|
|
|
|
// NewUpgradableEngine constructs a new upgradable consensus middleware.
|
|
func NewUpgradableEngine(isUpgraded func(uint64) bool, clique consensus.Engine, system consensus.Engine) *UpgradableEngine {
|
|
log.Info("Initializing upgradable consensus engine")
|
|
|
|
return &UpgradableEngine{
|
|
isUpgraded: isUpgraded,
|
|
clique: clique,
|
|
system: system,
|
|
}
|
|
}
|
|
|
|
// chooseEngine returns the appropriate consensus engine based on the header's timestamp.
|
|
func (ue *UpgradableEngine) chooseEngine(header *types.Header) consensus.Engine {
|
|
if ue.isUpgraded(header.Time) {
|
|
return ue.system
|
|
}
|
|
return ue.clique
|
|
}
|
|
|
|
// --------------------
|
|
// Methods to implement consensus.Engine
|
|
|
|
// Author returns the author of the block based on the header.
|
|
func (ue *UpgradableEngine) Author(header *types.Header) (common.Address, error) {
|
|
return ue.chooseEngine(header).Author(header)
|
|
}
|
|
|
|
// VerifyHeader checks whether a header conforms to the consensus rules of the engine.
|
|
func (ue *UpgradableEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
|
|
return ue.chooseEngine(header).VerifyHeader(chain, header, seal)
|
|
}
|
|
|
|
// VerifyHeaders verifies a batch of headers concurrently. In our use-case,
|
|
// headers can only be all system, all clique, or start with clique and then switch once to system.
|
|
func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
|
abort := make(chan struct{})
|
|
results := make(chan error, len(headers))
|
|
|
|
// If there are no headers, return a closed error channel.
|
|
if len(headers) == 0 {
|
|
close(results)
|
|
return nil, results
|
|
}
|
|
|
|
// Choose engine for the first and last header.
|
|
firstEngine := ue.chooseEngine(headers[0])
|
|
lastEngine := ue.chooseEngine(headers[len(headers)-1])
|
|
|
|
// If the first header is system, then all headers must be system.
|
|
if firstEngine == ue.system {
|
|
return firstEngine.VerifyHeaders(chain, headers, seals)
|
|
}
|
|
|
|
// If first and last headers are both clique, then all headers are clique.
|
|
if firstEngine == lastEngine {
|
|
return firstEngine.VerifyHeaders(chain, headers, seals)
|
|
}
|
|
|
|
// Otherwise, headers start as clique then switch to system. Since we assume
|
|
// a single switchover, find the first header that uses system.
|
|
splitIndex := 0
|
|
for i, header := range headers {
|
|
if ue.chooseEngine(header) == ue.system {
|
|
splitIndex = i
|
|
break
|
|
}
|
|
}
|
|
// It's expected that splitIndex is > 0.
|
|
cliqueHeaders := headers[:splitIndex]
|
|
cliqueSeals := seals[:splitIndex]
|
|
systemHeaders := headers[splitIndex:]
|
|
systemSeals := seals[splitIndex:]
|
|
|
|
log.Info("Verifying EuclidV2 transition header chain")
|
|
|
|
// Do verification concurrently,
|
|
// but make sure to run Clique first, then SystemContract,
|
|
// so that the results are sent in the correct order.
|
|
go func() {
|
|
defer close(results)
|
|
|
|
// Verify clique headers.
|
|
log.Info("Start EuclidV2 transition verification in Clique section", "startBlockNumber", cliqueHeaders[0].Number, "endBlockNumber", cliqueHeaders[len(cliqueHeaders)-1].Number)
|
|
abortClique, cliqueResults := ue.clique.VerifyHeaders(chain, cliqueHeaders, cliqueSeals)
|
|
|
|
// Note: cliqueResults is not closed so we cannot directly iterate over it
|
|
for i := 0; i < len(cliqueHeaders); i++ {
|
|
select {
|
|
case <-abort:
|
|
close(abortClique)
|
|
log.Warn("Aborted EuclidV2 transition verification in Clique section")
|
|
return
|
|
case err := <-cliqueResults:
|
|
results <- err
|
|
}
|
|
}
|
|
|
|
// Not sure why we need this here, but without this we get err="unknown ancestor"
|
|
// at the 1st Euclid block. It seems that `VerifyHeaders` start processing the next
|
|
// header before the previous one was written into `chain`.
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// Verify system contract headers.
|
|
log.Info("Start EuclidV2 transition verification in SystemContract section", "startBlockNumber", systemHeaders[0].Number, "endBlockNumber", systemHeaders[len(systemHeaders)-1].Number)
|
|
abortSystem, systemResults := ue.system.VerifyHeaders(chain, systemHeaders, systemSeals)
|
|
|
|
// Note: systemResults is not closed so we cannot directly iterate over it
|
|
for i := 0; i < len(systemHeaders); i++ {
|
|
select {
|
|
case <-abort:
|
|
close(abortSystem)
|
|
log.Info("Aborted EuclidV2 transition verification in SystemContract section")
|
|
return
|
|
case err := <-systemResults:
|
|
results <- err
|
|
}
|
|
}
|
|
|
|
log.Info("Completed EuclidV2 transition verification")
|
|
}()
|
|
|
|
return abort, results
|
|
}
|
|
|
|
// Prepare prepares a block header for sealing.
|
|
func (ue *UpgradableEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
|
return ue.chooseEngine(header).Prepare(chain, header)
|
|
}
|
|
|
|
// Seal instructs the engine to start sealing a block.
|
|
func (ue *UpgradableEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
|
|
return ue.chooseEngine(block.Header()).Seal(chain, block, results, stop)
|
|
}
|
|
|
|
// CalcDifficulty calculates the block difficulty if applicable.
|
|
func (ue *UpgradableEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
|
return ue.chooseEngine(parent).CalcDifficulty(chain, time, parent)
|
|
}
|
|
|
|
// Finalize finalizes the block, applying any post-transaction rules.
|
|
func (ue *UpgradableEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
|
|
ue.chooseEngine(header).Finalize(chain, header, state, txs, uncles)
|
|
}
|
|
|
|
// FinalizeAndAssemble finalizes and assembles a new block.
|
|
func (ue *UpgradableEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
|
|
return ue.chooseEngine(header).FinalizeAndAssemble(chain, header, state, txs, uncles, receipts)
|
|
}
|
|
|
|
// VerifyUncles verifies that no uncles are attached to the block.
|
|
func (ue *UpgradableEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
|
return ue.chooseEngine(block.Header()).VerifyUncles(chain, block)
|
|
}
|
|
|
|
// APIs returns any RPC APIs exposed by the consensus engine.
|
|
func (ue *UpgradableEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
|
|
return append(ue.clique.APIs(chain), ue.system.APIs(chain)...)
|
|
}
|
|
|
|
// Close terminates the consensus engine.
|
|
func (ue *UpgradableEngine) Close() error {
|
|
// Always close both engines.
|
|
err1 := ue.clique.Close()
|
|
err2 := ue.system.Close()
|
|
|
|
if err1 != nil || err2 != nil {
|
|
log.Error("Error while closing upgradable engine", "cliqueError", err1, "systemContractError", err2)
|
|
}
|
|
if err1 != nil {
|
|
return err1
|
|
}
|
|
return err2
|
|
}
|
|
|
|
// SealHash returns the hash of a block prior to it being sealed.
|
|
func (ue *UpgradableEngine) SealHash(header *types.Header) common.Hash {
|
|
return ue.chooseEngine(header).SealHash(header)
|
|
}
|
|
|
|
// Authorize injects a private key into the consensus engine to mint new blocks
|
|
// with.
|
|
func (ue *UpgradableEngine) Authorize(signer common.Address, signFn clique.SignerFn, signFn2 system_contract.SignerFn) {
|
|
if cliqueEngine, ok := ue.clique.(*clique.Clique); ok {
|
|
cliqueEngine.Authorize(signer, signFn)
|
|
}
|
|
if sysContractEngine, ok := ue.system.(*system_contract.SystemContract); ok {
|
|
sysContractEngine.Authorize(signer, signFn2)
|
|
}
|
|
}
|