mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
fix: provide parent header during EuclidV2 transition verification (#1187)
* fix: provide parent header during EuclidV2 transition verification * add test
This commit is contained in:
parent
aba2ddda86
commit
ad6cced99d
12 changed files with 199 additions and 108 deletions
|
|
@ -226,7 +226,7 @@ func (c *Clique) VerifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
|
||||||
// method returns a quit channel to abort the operations and a results channel to
|
// 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).
|
// retrieve the async verifications (the order is that of the input slice).
|
||||||
func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, parent *types.Header) (chan<- struct{}, <-chan error) {
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
results := make(chan error, len(headers))
|
results := make(chan error, len(headers))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ type Engine interface {
|
||||||
// concurrently. The method returns a quit channel to abort the operations and
|
// 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
|
// a results channel to retrieve the async verifications (the order is that of
|
||||||
// the input slice).
|
// the input slice).
|
||||||
VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
|
VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool, parent *types.Header) (chan<- struct{}, <-chan error)
|
||||||
|
|
||||||
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
||||||
// rules of a given engine.
|
// rules of a given engine.
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *ty
|
||||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||||
// concurrently. The method returns a quit channel to abort the operations and
|
// concurrently. The method returns a quit channel to abort the operations and
|
||||||
// a results channel to retrieve the async verifications.
|
// a results channel to retrieve the async verifications.
|
||||||
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, parent *types.Header) (chan<- struct{}, <-chan error) {
|
||||||
// If we're running a full engine faking, accept any input as valid
|
// If we're running a full engine faking, accept any input as valid
|
||||||
if ethash.config.PowMode == ModeFullFake || len(headers) == 0 {
|
if ethash.config.PowMode == ModeFullFake || len(headers) == 0 {
|
||||||
abort, results := make(chan struct{}), make(chan error, len(headers))
|
abort, results := make(chan struct{}), make(chan error, len(headers))
|
||||||
|
|
|
||||||
|
|
@ -86,13 +86,18 @@ func (s *SystemContract) VerifyHeader(chain consensus.ChainHeaderReader, header
|
||||||
// concurrently. The method returns a quit channel to abort the operations and
|
// 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
|
// a results channel to retrieve the async verifications (the order is that of
|
||||||
// the input slice).
|
// the input slice).
|
||||||
func (s *SystemContract) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
|
func (s *SystemContract) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, parent *types.Header) (chan<- struct{}, <-chan error) {
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
results := make(chan error, len(headers))
|
results := make(chan error, len(headers))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for i, header := range headers {
|
for i, header := range headers {
|
||||||
err := s.verifyHeader(chain, header, headers[:i])
|
parents := headers[:i]
|
||||||
|
if len(parents) == 0 && parent != nil {
|
||||||
|
parents = []*types.Header{parent}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := s.verifyHeader(chain, header, parents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error verifying headers", "err", err)
|
log.Error("Error verifying headers", "err", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,13 @@ package system_contract
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/scroll-tech/go-ethereum"
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
"github.com/scroll-tech/go-ethereum/log"
|
"github.com/scroll-tech/go-ethereum/log"
|
||||||
"github.com/scroll-tech/go-ethereum/params"
|
"github.com/scroll-tech/go-ethereum/params"
|
||||||
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
|
"github.com/scroll-tech/go-ethereum/rollup/sync_service"
|
||||||
|
|
@ -140,3 +143,53 @@ func (s *SystemContract) localSignerAddress() common.Address {
|
||||||
|
|
||||||
return s.signer
|
return s.signer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FakeEthClient implements a minimal version of sync_service.EthClient for testing purposes.
|
||||||
|
type FakeEthClient struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
// Value is the fixed Value to return from StorageAt.
|
||||||
|
// We'll assume StorageAt returns a 32-byte Value representing an Ethereum address.
|
||||||
|
Value common.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockNumber returns 0.
|
||||||
|
func (f *FakeEthClient) BlockNumber(ctx context.Context) (uint64, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainID returns a zero-value chain ID.
|
||||||
|
func (f *FakeEthClient) ChainID(ctx context.Context) (*big.Int, error) {
|
||||||
|
return big.NewInt(0), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterLogs returns an empty slice of logs.
|
||||||
|
func (f *FakeEthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
||||||
|
return []types.Log{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByNumber returns nil.
|
||||||
|
func (f *FakeEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeFilterLogs returns a nil subscription.
|
||||||
|
func (f *FakeEthClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionByHash returns (nil, false, nil).
|
||||||
|
func (f *FakeEthClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
||||||
|
return nil, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockByHash returns nil.
|
||||||
|
func (f *FakeEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageAt returns the byte representation of f.value.
|
||||||
|
func (f *FakeEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
f.mu.Lock()
|
||||||
|
defer f.mu.Unlock()
|
||||||
|
return f.Value.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,11 @@ package system_contract
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum"
|
|
||||||
"github.com/scroll-tech/go-ethereum/accounts"
|
"github.com/scroll-tech/go-ethereum/accounts"
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
|
@ -19,14 +17,14 @@ import (
|
||||||
"github.com/scroll-tech/go-ethereum/trie"
|
"github.com/scroll-tech/go-ethereum/trie"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ sync_service.EthClient = &fakeEthClient{}
|
var _ sync_service.EthClient = &FakeEthClient{}
|
||||||
|
|
||||||
func TestSystemContract_FetchSigner(t *testing.T) {
|
func TestSystemContract_FetchSigner(t *testing.T) {
|
||||||
log.Root().SetHandler(log.DiscardHandler())
|
log.Root().SetHandler(log.DiscardHandler())
|
||||||
|
|
||||||
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
||||||
|
|
||||||
fakeClient := &fakeEthClient{value: expectedSigner}
|
fakeClient := &FakeEthClient{Value: expectedSigner}
|
||||||
|
|
||||||
config := ¶ms.SystemContractConfig{
|
config := ¶ms.SystemContractConfig{
|
||||||
SystemContractAddress: common.HexToAddress("0xFAKE"),
|
SystemContractAddress: common.HexToAddress("0xFAKE"),
|
||||||
|
|
@ -55,7 +53,7 @@ func TestSystemContract_AuthorizeCheck(t *testing.T) {
|
||||||
|
|
||||||
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
expectedSigner := common.HexToAddress("0x1234567890abcdef1234567890abcdef12345678")
|
||||||
|
|
||||||
fakeClient := &fakeEthClient{value: expectedSigner}
|
fakeClient := &FakeEthClient{Value: expectedSigner}
|
||||||
config := ¶ms.SystemContractConfig{
|
config := ¶ms.SystemContractConfig{
|
||||||
SystemContractAddress: common.HexToAddress("0xFAKE"),
|
SystemContractAddress: common.HexToAddress("0xFAKE"),
|
||||||
Period: 10,
|
Period: 10,
|
||||||
|
|
@ -106,8 +104,8 @@ func TestSystemContract_SignsAfterUpdate(t *testing.T) {
|
||||||
updatedSigner := common.HexToAddress("0x2222222222222222222222222222222222222222")
|
updatedSigner := common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||||
|
|
||||||
// Create a fake client that starts by returning the wrong signer.
|
// Create a fake client that starts by returning the wrong signer.
|
||||||
fakeClient := &fakeEthClient{
|
fakeClient := &FakeEthClient{
|
||||||
value: oldSigner,
|
Value: oldSigner,
|
||||||
}
|
}
|
||||||
|
|
||||||
config := ¶ms.SystemContractConfig{
|
config := ¶ms.SystemContractConfig{
|
||||||
|
|
@ -130,7 +128,7 @@ func TestSystemContract_SignsAfterUpdate(t *testing.T) {
|
||||||
|
|
||||||
// Now, simulate an update: change the fake client's returned value to updatedSigner.
|
// Now, simulate an update: change the fake client's returned value to updatedSigner.
|
||||||
fakeClient.mu.Lock()
|
fakeClient.mu.Lock()
|
||||||
fakeClient.value = updatedSigner
|
fakeClient.Value = updatedSigner
|
||||||
fakeClient.mu.Unlock()
|
fakeClient.mu.Unlock()
|
||||||
|
|
||||||
// fetch new value from L1 (simulating a background poll)
|
// fetch new value from L1 (simulating a background poll)
|
||||||
|
|
@ -171,53 +169,3 @@ func TestSystemContract_SignsAfterUpdate(t *testing.T) {
|
||||||
t.Fatal("Timed out waiting for Seal to return a sealed block")
|
t.Fatal("Timed out waiting for Seal to return a sealed block")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fakeEthClient implements a minimal version of sync_service.EthClient for testing purposes.
|
|
||||||
type fakeEthClient struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
// value is the fixed value to return from StorageAt.
|
|
||||||
// We'll assume StorageAt returns a 32-byte value representing an Ethereum address.
|
|
||||||
value common.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockNumber returns 0.
|
|
||||||
func (f *fakeEthClient) BlockNumber(ctx context.Context) (uint64, error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChainID returns a zero-value chain ID.
|
|
||||||
func (f *fakeEthClient) ChainID(ctx context.Context) (*big.Int, error) {
|
|
||||||
return big.NewInt(0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterLogs returns an empty slice of logs.
|
|
||||||
func (f *fakeEthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
|
|
||||||
return []types.Log{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderByNumber returns nil.
|
|
||||||
func (f *fakeEthClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeFilterLogs returns a nil subscription.
|
|
||||||
func (f *fakeEthClient) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TransactionByHash returns (nil, false, nil).
|
|
||||||
func (f *fakeEthClient) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
|
||||||
return nil, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BlockByHash returns nil.
|
|
||||||
func (f *fakeEthClient) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// StorageAt returns the byte representation of f.value.
|
|
||||||
func (f *fakeEthClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
|
||||||
f.mu.Lock()
|
|
||||||
defer f.mu.Unlock()
|
|
||||||
return f.value.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package wrapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/scroll-tech/go-ethereum/common"
|
"github.com/scroll-tech/go-ethereum/common"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus"
|
"github.com/scroll-tech/go-ethereum/consensus"
|
||||||
|
|
@ -59,34 +58,9 @@ func (ue *UpgradableEngine) VerifyHeader(chain consensus.ChainHeaderReader, head
|
||||||
return ue.chooseEngine(header.Time).VerifyHeader(chain, header, seal)
|
return ue.chooseEngine(header.Time).VerifyHeader(chain, header, seal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForHeader(chain consensus.ChainHeaderReader, header *types.Header) {
|
|
||||||
hash, number := header.Hash(), header.Number.Uint64()
|
|
||||||
|
|
||||||
// poll every 2 seconds, should succeed after a few tries
|
|
||||||
ticker := time.NewTicker(2 * time.Second)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
// we give up after 2 minutes
|
|
||||||
timeout := time.After(120 * time.Second)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
// try reading from chain, if the header is present then we are ready
|
|
||||||
if h := chain.GetHeader(hash, number); h != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-timeout:
|
|
||||||
log.Warn("Unable to find last pre-EuclidV2 header in chain", "hash", hash.Hex(), "number", number)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyHeaders verifies a batch of headers concurrently. In our use-case,
|
// 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.
|
// 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) {
|
func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, parent *types.Header) (chan<- struct{}, <-chan error) {
|
||||||
abort := make(chan struct{})
|
abort := make(chan struct{})
|
||||||
results := make(chan error, len(headers))
|
results := make(chan error, len(headers))
|
||||||
|
|
||||||
|
|
@ -102,12 +76,12 @@ func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, hea
|
||||||
|
|
||||||
// If the first header is system, then all headers must be system.
|
// If the first header is system, then all headers must be system.
|
||||||
if firstEngine == ue.system {
|
if firstEngine == ue.system {
|
||||||
return firstEngine.VerifyHeaders(chain, headers, seals)
|
return firstEngine.VerifyHeaders(chain, headers, seals, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If first and last headers are both clique, then all headers are clique.
|
// If first and last headers are both clique, then all headers are clique.
|
||||||
if firstEngine == lastEngine {
|
if firstEngine == lastEngine {
|
||||||
return firstEngine.VerifyHeaders(chain, headers, seals)
|
return firstEngine.VerifyHeaders(chain, headers, seals, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, headers start as clique then switch to system. Since we assume
|
// Otherwise, headers start as clique then switch to system. Since we assume
|
||||||
|
|
@ -135,7 +109,7 @@ func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, hea
|
||||||
|
|
||||||
// Verify clique headers.
|
// Verify clique headers.
|
||||||
log.Info("Start EuclidV2 transition verification in Clique section", "startBlockNumber", cliqueHeaders[0].Number, "endBlockNumber", cliqueHeaders[len(cliqueHeaders)-1].Number)
|
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)
|
abortClique, cliqueResults := ue.clique.VerifyHeaders(chain, cliqueHeaders, cliqueSeals, nil)
|
||||||
|
|
||||||
// Note: cliqueResults is not closed so we cannot directly iterate over it
|
// Note: cliqueResults is not closed so we cannot directly iterate over it
|
||||||
for i := 0; i < len(cliqueHeaders); i++ {
|
for i := 0; i < len(cliqueHeaders); i++ {
|
||||||
|
|
@ -149,14 +123,13 @@ func (ue *UpgradableEngine) VerifyHeaders(chain consensus.ChainHeaderReader, hea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// `VerifyHeader` will try to call `chain.GetHeader`, which will race with `InsertHeaderChain`.
|
// Since the Clique part of the header chain might not yet be stored in the local chain,
|
||||||
// This might result in "unknown ancestor" header validation error.
|
// provide a hint to the SystemContract consensus engine.
|
||||||
// This should be temporary, so we solve this by waiting here.
|
lastCliqueHeader := cliqueHeaders[len(cliqueHeaders)-1]
|
||||||
waitForHeader(chain, cliqueHeaders[len(cliqueHeaders)-1])
|
|
||||||
|
|
||||||
// Verify system contract headers.
|
// Verify system contract headers.
|
||||||
log.Info("Start EuclidV2 transition verification in SystemContract section", "startBlockNumber", systemHeaders[0].Number, "endBlockNumber", systemHeaders[len(systemHeaders)-1].Number)
|
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)
|
abortSystem, systemResults := ue.system.VerifyHeaders(chain, systemHeaders, systemSeals, lastCliqueHeader)
|
||||||
|
|
||||||
// Note: systemResults is not closed so we cannot directly iterate over it
|
// Note: systemResults is not closed so we cannot directly iterate over it
|
||||||
for i := 0; i < len(systemHeaders); i++ {
|
for i := 0; i < len(systemHeaders); i++ {
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,10 @@ func TestHeaderVerification(t *testing.T) {
|
||||||
|
|
||||||
if valid {
|
if valid {
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
|
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}, nil)
|
||||||
} else {
|
} else {
|
||||||
engine := ethash.NewFakeFailer(headers[i].Number.Uint64())
|
engine := ethash.NewFakeFailer(headers[i].Number.Uint64())
|
||||||
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true})
|
_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}, nil)
|
||||||
}
|
}
|
||||||
// Wait for the verification result
|
// Wait for the verification result
|
||||||
select {
|
select {
|
||||||
|
|
@ -107,11 +107,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
|
||||||
|
|
||||||
if valid {
|
if valid {
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
_, results = chain.engine.VerifyHeaders(chain, headers, seals, nil)
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
} else {
|
} else {
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
|
||||||
_, results = chain.engine.VerifyHeaders(chain, headers, seals)
|
_, results = chain.engine.VerifyHeaders(chain, headers, seals, nil)
|
||||||
chain.Stop()
|
chain.Stop()
|
||||||
}
|
}
|
||||||
// Wait for all the verification results
|
// Wait for all the verification results
|
||||||
|
|
@ -176,7 +176,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
|
||||||
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
|
chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
|
abort, results := chain.engine.VerifyHeaders(chain, headers, seals, nil)
|
||||||
close(abort)
|
close(abort)
|
||||||
|
|
||||||
// Deplete the results channel
|
// Deplete the results channel
|
||||||
|
|
|
||||||
|
|
@ -1546,7 +1546,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
|
||||||
headers[i] = block.Header()
|
headers[i] = block.Header()
|
||||||
seals[i] = verifySeals
|
seals[i] = verifySeals
|
||||||
}
|
}
|
||||||
abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
|
abort, results := bc.engine.VerifyHeaders(bc, headers, seals, nil)
|
||||||
defer close(abort)
|
defer close(abort)
|
||||||
|
|
||||||
// Peek the error for the first block to decide the directing import logic
|
// Peek the error for the first block to decide the directing import logic
|
||||||
|
|
|
||||||
|
|
@ -339,7 +339,7 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
seals[len(seals)-1] = true
|
seals[len(seals)-1] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
|
abort, results := hc.engine.VerifyHeaders(hc, chain, seals, nil)
|
||||||
defer close(abort)
|
defer close(abort)
|
||||||
|
|
||||||
// Iterate over the headers and ensure they all check out
|
// Iterate over the headers and ensure they all check out
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package miner
|
package miner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -33,6 +34,8 @@ import (
|
||||||
"github.com/scroll-tech/go-ethereum/consensus"
|
"github.com/scroll-tech/go-ethereum/consensus"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus/clique"
|
"github.com/scroll-tech/go-ethereum/consensus/clique"
|
||||||
"github.com/scroll-tech/go-ethereum/consensus/ethash"
|
"github.com/scroll-tech/go-ethereum/consensus/ethash"
|
||||||
|
"github.com/scroll-tech/go-ethereum/consensus/system_contract"
|
||||||
|
"github.com/scroll-tech/go-ethereum/consensus/wrapper"
|
||||||
"github.com/scroll-tech/go-ethereum/core"
|
"github.com/scroll-tech/go-ethereum/core"
|
||||||
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
"github.com/scroll-tech/go-ethereum/core/rawdb"
|
||||||
"github.com/scroll-tech/go-ethereum/core/types"
|
"github.com/scroll-tech/go-ethereum/core/types"
|
||||||
|
|
@ -77,6 +80,14 @@ var (
|
||||||
MaxAccountsNum: math.MaxInt,
|
MaxAccountsNum: math.MaxInt,
|
||||||
CCCMaxWorkers: 2,
|
CCCMaxWorkers: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testConfigAllowEmpty = &Config{
|
||||||
|
Recommit: time.Second,
|
||||||
|
GasCeil: params.GenesisGasLimit,
|
||||||
|
MaxAccountsNum: math.MaxInt,
|
||||||
|
CCCMaxWorkers: 2,
|
||||||
|
AllowEmpty: true,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -138,6 +149,15 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
|
||||||
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
||||||
return crypto.Sign(crypto.Keccak256(data), testBankKey)
|
return crypto.Sign(crypto.Keccak256(data), testBankKey)
|
||||||
})
|
})
|
||||||
|
case *wrapper.UpgradableEngine:
|
||||||
|
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
|
||||||
|
gspec.Timestamp = uint64(time.Now().Unix())
|
||||||
|
copy(gspec.ExtraData[32:32+common.AddressLength], testBankAddress.Bytes())
|
||||||
|
e.Authorize(testBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
||||||
|
return crypto.Sign(crypto.Keccak256(data), testBankKey)
|
||||||
|
}, func(account accounts.Account, s string, data []byte) ([]byte, error) {
|
||||||
|
return crypto.Sign(crypto.Keccak256(data), testBankKey)
|
||||||
|
})
|
||||||
case *ethash.Ethash:
|
case *ethash.Ethash:
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unexpected consensus engine type: %T", engine)
|
t.Fatalf("unexpected consensus engine type: %T", engine)
|
||||||
|
|
@ -207,14 +227,26 @@ func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
|
func testWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, allowEmpty bool) (*worker, *testWorkerBackend) {
|
||||||
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
|
backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
|
||||||
backend.txPool.AddLocals(pendingTxs)
|
backend.txPool.AddLocals(pendingTxs)
|
||||||
w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false, false)
|
config := testConfig
|
||||||
|
if allowEmpty {
|
||||||
|
config = testConfigAllowEmpty
|
||||||
|
}
|
||||||
|
w := newWorker(config, chainConfig, engine, backend, new(event.TypeMux), nil, false, false)
|
||||||
w.setEtherbase(testBankAddress)
|
w.setEtherbase(testBankAddress)
|
||||||
return w, backend
|
return w, backend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
|
||||||
|
return testWorker(t, chainConfig, engine, db, blocks, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestWorkerWithEmptyBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
|
||||||
|
return testWorker(t, chainConfig, engine, db, blocks, true)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGenerateBlockAndImportClique(t *testing.T) {
|
func TestGenerateBlockAndImportClique(t *testing.T) {
|
||||||
testGenerateBlockAndImport(t, true)
|
testGenerateBlockAndImport(t, true)
|
||||||
}
|
}
|
||||||
|
|
@ -1355,3 +1387,83 @@ func TestEuclidV2HardForkMessageQueue(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEuclidV2TransitionVerification tests that the upgradable consensus engine
|
||||||
|
// can successfully verify the EuclidV2 transition chain.
|
||||||
|
func TestEuclidV2TransitionVerification(t *testing.T) {
|
||||||
|
// patch time.Now() to be able to simulate hard fork time
|
||||||
|
patches := gomonkey.NewPatches()
|
||||||
|
defer patches.Reset()
|
||||||
|
var timeCount int64
|
||||||
|
patches.ApplyFunc(time.Now, func() time.Time {
|
||||||
|
timeCount++
|
||||||
|
return time.Unix(timeCount, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// init chain config
|
||||||
|
chainConfig := params.AllCliqueProtocolChanges.Clone()
|
||||||
|
chainConfig.EuclidTime = newUint64(0)
|
||||||
|
chainConfig.EuclidV2Time = newUint64(10000)
|
||||||
|
chainConfig.Clique = ¶ms.CliqueConfig{Period: 1, Epoch: 30000}
|
||||||
|
chainConfig.SystemContract = ¶ms.SystemContractConfig{Period: 1}
|
||||||
|
|
||||||
|
// init worker
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
cliqueEngine := clique.New(chainConfig.Clique, db)
|
||||||
|
sysEngine := system_contract.New(context.Background(), chainConfig.SystemContract, &system_contract.FakeEthClient{Value: testBankAddress})
|
||||||
|
engine := wrapper.NewUpgradableEngine(chainConfig.IsEuclidV2, cliqueEngine, sysEngine)
|
||||||
|
w, b := newTestWorkerWithEmptyBlock(t, chainConfig, engine, db, 0)
|
||||||
|
defer w.close()
|
||||||
|
b.genesis.MustCommit(db)
|
||||||
|
|
||||||
|
// collect mined blocks
|
||||||
|
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
|
||||||
|
defer sub.Unsubscribe()
|
||||||
|
w.start()
|
||||||
|
|
||||||
|
blocks := []*types.Block{}
|
||||||
|
headers := []*types.Header{}
|
||||||
|
|
||||||
|
for i := 0; i < 6; i++ {
|
||||||
|
select {
|
||||||
|
case ev := <-sub.Chan():
|
||||||
|
// activate EuclidV2 at next block
|
||||||
|
if i == 2 {
|
||||||
|
timeCount = int64(*chainConfig.EuclidV2Time)
|
||||||
|
}
|
||||||
|
|
||||||
|
block := ev.Data.(core.NewMinedBlockEvent).Block
|
||||||
|
blocks = append(blocks, block)
|
||||||
|
headers = append(headers, block.Header())
|
||||||
|
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatalf("timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check: we generated the EuclidV2 transition block
|
||||||
|
assert.False(t, chainConfig.IsEuclidV2(headers[0].Time))
|
||||||
|
assert.True(t, chainConfig.IsEuclidV2(headers[len(headers)-1].Time))
|
||||||
|
|
||||||
|
// import headers into new chain
|
||||||
|
chainDb := rawdb.NewMemoryDatabase()
|
||||||
|
b.genesis.MustCommit(chainDb)
|
||||||
|
chain, err := core.NewBlockChain(chainDb, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
// previously this would fail with `unknown ancestor`
|
||||||
|
_, err = chain.InsertHeaderChain(headers, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// import headers into new chain
|
||||||
|
chainDb = rawdb.NewMemoryDatabase()
|
||||||
|
b.genesis.MustCommit(chainDb)
|
||||||
|
chain, err = core.NewBlockChain(chainDb, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer chain.Stop()
|
||||||
|
|
||||||
|
// previously this would fail with `unknown ancestor`
|
||||||
|
_, err = chain.InsertChain(blocks)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import (
|
||||||
const (
|
const (
|
||||||
VersionMajor = 5 // Major version component of the current release
|
VersionMajor = 5 // Major version component of the current release
|
||||||
VersionMinor = 8 // Minor version component of the current release
|
VersionMinor = 8 // Minor version component of the current release
|
||||||
VersionPatch = 46 // Patch version component of the current release
|
VersionPatch = 47 // Patch version component of the current release
|
||||||
VersionMeta = "mainnet" // Version metadata to append to the version string
|
VersionMeta = "mainnet" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue