mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
Merge f1c8bdc760 into 7bb95a9a64
This commit is contained in:
commit
e6225a41a3
3 changed files with 196 additions and 27 deletions
|
|
@ -20,6 +20,7 @@ package clique
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -166,19 +167,12 @@ func sigHash(header *types.Header) (hash common.Hash) {
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// ecrecover extracts the Ethereum account address from a signed header.
|
func RecoverSealer(header *types.Header) (common.Address, error) {
|
||||||
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
|
||||||
// If the signature's already cached, return that
|
|
||||||
hash := header.Hash()
|
|
||||||
if address, known := sigcache.Get(hash); known {
|
|
||||||
return address.(common.Address), nil
|
|
||||||
}
|
|
||||||
// Retrieve the signature from the header extra-data
|
// Retrieve the signature from the header extra-data
|
||||||
if len(header.Extra) < extraSeal {
|
if len(header.Extra) < extraSeal {
|
||||||
return common.Address{}, errMissingSignature
|
return common.Address{}, errMissingSignature
|
||||||
}
|
}
|
||||||
signature := header.Extra[len(header.Extra)-extraSeal:]
|
signature := header.Extra[len(header.Extra)-extraSeal:]
|
||||||
|
|
||||||
// Recover the public key and the Ethereum address
|
// Recover the public key and the Ethereum address
|
||||||
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature)
|
pubkey, err := crypto.Ecrecover(sigHash(header).Bytes(), signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -186,7 +180,20 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er
|
||||||
}
|
}
|
||||||
var signer common.Address
|
var signer common.Address
|
||||||
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
copy(signer[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||||
|
return signer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ecrecover extracts the Ethereum account address from a signed header.
|
||||||
|
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
|
||||||
|
// If the signature's already cached, return that
|
||||||
|
hash := header.Hash()
|
||||||
|
if address, known := sigcache.Get(hash); known {
|
||||||
|
return address.(common.Address), nil
|
||||||
|
}
|
||||||
|
signer, err := RecoverSealer(header)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
sigcache.Add(hash, signer)
|
sigcache.Add(hash, signer)
|
||||||
return signer, nil
|
return signer, nil
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +348,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
|
||||||
parent = chain.GetHeader(header.ParentHash, number-1)
|
parent = chain.GetHeader(header.ParentHash, number-1)
|
||||||
}
|
}
|
||||||
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
|
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
|
||||||
return consensus.ErrUnknownAncestor
|
return fmt.Errorf("unknown ancestor [0x%x] for block %d", header.ParentHash, number)
|
||||||
}
|
}
|
||||||
if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
|
if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
|
||||||
return ErrInvalidTimestamp
|
return ErrInvalidTimestamp
|
||||||
|
|
@ -411,14 +418,14 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
|
||||||
// If we have explicit parents, pick from there (enforced)
|
// If we have explicit parents, pick from there (enforced)
|
||||||
header = parents[len(parents)-1]
|
header = parents[len(parents)-1]
|
||||||
if header.Hash() != hash || header.Number.Uint64() != number {
|
if header.Hash() != hash || header.Number.Uint64() != number {
|
||||||
return nil, consensus.ErrUnknownAncestor
|
return nil, fmt.Errorf("unknown ancestor [0x%x] for block %d", header.ParentHash, number)
|
||||||
}
|
}
|
||||||
parents = parents[:len(parents)-1]
|
parents = parents[:len(parents)-1]
|
||||||
} else {
|
} else {
|
||||||
// No explicit parents (or no more left), reach out to the database
|
// No explicit parents (or no more left), reach out to the database
|
||||||
header = chain.GetHeader(hash, number)
|
header = chain.GetHeader(hash, number)
|
||||||
if header == nil {
|
if header == nil {
|
||||||
return nil, consensus.ErrUnknownAncestor
|
return nil, fmt.Errorf("unknown ancestor [0x%x] for block %d", hash, number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
headers = append(headers, header)
|
headers = append(headers, header)
|
||||||
|
|
@ -481,23 +488,23 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, ok := snap.Signers[signer]; !ok {
|
if _, ok := snap.Signers[signer]; !ok {
|
||||||
return errUnauthorized
|
return fmt.Errorf("unauthorised[2]: signer 0x%x is not one of the signers", signer)
|
||||||
}
|
}
|
||||||
for seen, recent := range snap.Recents {
|
for seen, recent := range snap.Recents {
|
||||||
if recent == signer {
|
if recent == signer {
|
||||||
// Signer is among recents, only fail if the current block doesn't shift it out
|
// Signer is among recents, only fail if the current block doesn't shift it out
|
||||||
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
|
if limit := uint64(len(snap.Signers)/2 + 1); seen > number-limit {
|
||||||
return errUnauthorized
|
return fmt.Errorf("unauthorised: signer 0x%x recently signed", signer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
// Ensure that the difficulty corresponds to the turn-ness of the signer
|
||||||
inturn := snap.inturn(header.Number.Uint64(), signer)
|
inturn := snap.inturn(header.Number.Uint64(), signer)
|
||||||
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
|
if inturn && header.Difficulty.Cmp(diffInTurn) != 0 {
|
||||||
return errInvalidDifficulty
|
return fmt.Errorf("Invalid difficulty [block %d], was %d expected %d (in-turn)", header.Number, header.Difficulty, diffInTurn)
|
||||||
}
|
}
|
||||||
if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 {
|
if !inturn && header.Difficulty.Cmp(diffNoTurn) != 0 {
|
||||||
return errInvalidDifficulty
|
return fmt.Errorf("Invalid difficulty [block %d], was %d expected %d (out-of-turn)", header.Number, header.Difficulty, diffNoTurn)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -558,7 +565,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
|
||||||
// Ensure the timestamp has the correct delay
|
// Ensure the timestamp has the correct delay
|
||||||
parent := chain.GetHeader(header.ParentHash, number-1)
|
parent := chain.GetHeader(header.ParentHash, number-1)
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return consensus.ErrUnknownAncestor
|
return fmt.Errorf("unknown ancestor [0x%x] for block %d", header.ParentHash, number)
|
||||||
}
|
}
|
||||||
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
|
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
|
||||||
if header.Time.Int64() < time.Now().Unix() {
|
if header.Time.Int64() < time.Now().Unix() {
|
||||||
|
|
@ -613,7 +620,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, authorized := snap.Signers[signer]; !authorized {
|
if _, authorized := snap.Signers[signer]; !authorized {
|
||||||
return errUnauthorized
|
return fmt.Errorf("unauthorised[1]: signer 0x%x is not one of the signers", signer)
|
||||||
}
|
}
|
||||||
// If we're amongst the recent signers, wait for the next block
|
// If we're amongst the recent signers, wait for the next block
|
||||||
for seen, recent := range snap.Recents {
|
for seen, recent := range snap.Recents {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@ package clique
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -42,6 +45,8 @@ type testerVote struct {
|
||||||
// keys capable of signing transactions.
|
// keys capable of signing transactions.
|
||||||
type testerAccountPool struct {
|
type testerAccountPool struct {
|
||||||
accounts map[string]*ecdsa.PrivateKey
|
accounts map[string]*ecdsa.PrivateKey
|
||||||
|
// can be used to always yield the same accounts
|
||||||
|
fakePRNG io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTesterAccountPool() *testerAccountPool {
|
func newTesterAccountPool() *testerAccountPool {
|
||||||
|
|
@ -49,21 +54,52 @@ func newTesterAccountPool() *testerAccountPool {
|
||||||
accounts: make(map[string]*ecdsa.PrivateKey),
|
accounts: make(map[string]*ecdsa.PrivateKey),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func newDeterministicTesterAccountPool() *testerAccountPool {
|
||||||
|
return &testerAccountPool{
|
||||||
|
accounts: make(map[string]*ecdsa.PrivateKey),
|
||||||
|
fakePRNG: &fakeRand{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (ap *testerAccountPool) sign(header *types.Header, signer string) {
|
type fakeRand struct {
|
||||||
|
counter uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeRand) Read(p []byte) (n int, err error) {
|
||||||
|
for i := 0; i < len(p); i++ {
|
||||||
|
p[i] = byte(f.counter)
|
||||||
|
}
|
||||||
|
f.counter++
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ap *testerAccountPool) getSignature(header *types.Header, signer string) ([]byte, error) {
|
||||||
// Ensure we have a persistent key for the signer
|
// Ensure we have a persistent key for the signer
|
||||||
if ap.accounts[signer] == nil {
|
if ap.accounts[signer] == nil {
|
||||||
ap.accounts[signer], _ = crypto.GenerateKey()
|
if ap.fakePRNG != nil {
|
||||||
|
ap.accounts[signer], _ = ecdsa.GenerateKey(crypto.S256(), ap.fakePRNG)
|
||||||
|
} else {
|
||||||
|
ap.accounts[signer], _ = crypto.GenerateKey()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Sign the header and embed the signature in extra data
|
// Sign the header and embed the signature in extra data
|
||||||
sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer])
|
return crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ap *testerAccountPool) sign(header *types.Header, signer string) {
|
||||||
|
// Sign the header and embed the signature in extra data
|
||||||
|
sig, _ := ap.getSignature(header, signer)
|
||||||
copy(header.Extra[len(header.Extra)-65:], sig)
|
copy(header.Extra[len(header.Extra)-65:], sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ap *testerAccountPool) address(account string) common.Address {
|
func (ap *testerAccountPool) address(account string) common.Address {
|
||||||
// Ensure we have a persistent key for the account
|
// Ensure we have a persistent key for the account
|
||||||
if ap.accounts[account] == nil {
|
if ap.accounts[account] == nil {
|
||||||
ap.accounts[account], _ = crypto.GenerateKey()
|
if ap.fakePRNG != nil {
|
||||||
|
ap.accounts[account], _ = ecdsa.GenerateKey(crypto.S256(), ap.fakePRNG)
|
||||||
|
} else {
|
||||||
|
ap.accounts[account], _ = crypto.GenerateKey()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Resolve and return the Ethereum address
|
// Resolve and return the Ethereum address
|
||||||
return crypto.PubkeyToAddress(ap.accounts[account].PublicKey)
|
return crypto.PubkeyToAddress(ap.accounts[account].PublicKey)
|
||||||
|
|
@ -75,16 +111,129 @@ type testerChainReader struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges }
|
func (r *testerChainReader) Config() *params.ChainConfig { return params.AllCliqueProtocolChanges }
|
||||||
func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") }
|
func (r *testerChainReader) CurrentHeader() *types.Header { panic("not supported") }
|
||||||
func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") }
|
func (r *testerChainReader) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") }
|
return rawdb.ReadHeader(r.db, hash, number)
|
||||||
func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") }
|
}
|
||||||
|
func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block { panic("not supported") }
|
||||||
|
func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header { panic("not supported") }
|
||||||
func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header {
|
func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header {
|
||||||
if number == 0 {
|
if number == 0 {
|
||||||
return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0)
|
return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0)
|
||||||
}
|
}
|
||||||
return nil
|
return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, number), number)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCliqueReorgAtEpoch(t *testing.T) {
|
||||||
|
|
||||||
|
// Create the account pool and generate the initial set of signers
|
||||||
|
accounts := newDeterministicTesterAccountPool()
|
||||||
|
|
||||||
|
// 7 signers
|
||||||
|
signerlist := []string{"A", "B", "C", "D", "E", "F", "G"}
|
||||||
|
|
||||||
|
signers := make([]common.Address, len(signerlist))
|
||||||
|
for j, signer := range signerlist {
|
||||||
|
signers[j] = accounts.address(signer)
|
||||||
|
}
|
||||||
|
// Sort the signers
|
||||||
|
for j := 0; j < len(signers); j++ {
|
||||||
|
for k := j + 1; k < len(signers); k++ {
|
||||||
|
if bytes.Compare(signers[j][:], signers[k][:]) > 0 {
|
||||||
|
signers[j], signers[k] = signers[k], signers[j]
|
||||||
|
signerlist[j], signerlist[k] = signerlist[k], signerlist[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the genesis block with the initial set of signers
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal),
|
||||||
|
}
|
||||||
|
for j, signer := range signers {
|
||||||
|
copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
|
||||||
|
fmt.Printf("adding signer 0x%x\n", signer)
|
||||||
|
}
|
||||||
|
sealerListBytes := genesis.ExtraData
|
||||||
|
// Create a pristine blockchain with the genesis injected
|
||||||
|
db := ethdb.NewMemDatabase()
|
||||||
|
genesis.Commit(db)
|
||||||
|
|
||||||
|
engine := New(¶ms.CliqueConfig{Epoch: 5}, db)
|
||||||
|
|
||||||
|
// Initialize a fresh chain with only a genesis block
|
||||||
|
blockchain, _ := core.NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
|
||||||
|
snap, err := engine.snapshot(&testerChainReader{db: db}, blockchain.CurrentBlock().NumberU64(), blockchain.CurrentBlock().Hash(), nil)
|
||||||
|
for addy, _ := range snap.Signers {
|
||||||
|
fmt.Printf("signer: 0x%x\n", addy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate blocks, up to the checkpoint block
|
||||||
|
// This segment should with the last block being of higher difficulty: in-turn
|
||||||
|
blocks2, _ := core.GenerateChainSeal(params.TestChainConfig, blockchain.CurrentBlock(), engine, db, 6,
|
||||||
|
func(i int, b *core.BlockGen) {
|
||||||
|
b.SetDifficulty(diffNoTurn)
|
||||||
|
extraData := make([]byte, extraVanity+extraSeal)
|
||||||
|
b.SetExtra(extraData)
|
||||||
|
if i+1 == 5 {
|
||||||
|
// Checkpoint block
|
||||||
|
b.SetExtra(sealerListBytes)
|
||||||
|
b.SetDifficulty(diffInTurn)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
func(b *types.Block) *types.Header {
|
||||||
|
// Seal it
|
||||||
|
hdr := b.Header()
|
||||||
|
if hdr.Number.Uint64() >= 5 {
|
||||||
|
k := 5 % len(signerlist)
|
||||||
|
sig, _ := accounts.getSignature(hdr, signerlist[k])
|
||||||
|
extradata := b.Extra()
|
||||||
|
copy(extradata[len(extradata)-65:], sig)
|
||||||
|
hdr.Extra = extradata
|
||||||
|
} else {
|
||||||
|
k := int(b.NumberU64()+5) % len(signerlist)
|
||||||
|
sig, _ := accounts.getSignature(hdr, signerlist[k])
|
||||||
|
extradata := b.Extra()
|
||||||
|
copy(extradata[len(extradata)-65:], sig)
|
||||||
|
hdr.Extra = extradata
|
||||||
|
|
||||||
|
}
|
||||||
|
return hdr
|
||||||
|
})
|
||||||
|
|
||||||
|
addBlocks := func(blocks []*types.Block) {
|
||||||
|
fmt.Printf("Importing %d blocks\n", len(blocks))
|
||||||
|
for _, block := range blocks {
|
||||||
|
author, _ := engine.Author(block.Header())
|
||||||
|
fmt.Printf(" Adding block %d: 0x%x, sealer: 0x%x, len(extra): %v, difficulty %d\n", block.Number(), block.Header().Hash(), author, len(block.Extra()), block.Difficulty())
|
||||||
|
}
|
||||||
|
if _, err := blockchain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("failed to import blocks: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showRecents := func() {
|
||||||
|
snap, err = engine.snapshot(&testerChainReader{db: db}, blockchain.CurrentBlock().NumberU64(), blockchain.CurrentBlock().Hash(), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to generate snapshot: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Recents:\n")
|
||||||
|
for block, addy := range snap.Recents {
|
||||||
|
fmt.Printf("%v 0x%x\n", block, addy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addBlocks(blocks2[0:4])
|
||||||
|
showRecents()
|
||||||
|
addBlocks(blocks2[4:5])
|
||||||
|
showRecents()
|
||||||
|
addBlocks(blocks2[5:6])
|
||||||
|
showRecents()
|
||||||
|
fmt.Printf("Current head: 0x%x\n", blockchain.CurrentBlock().Header().Hash())
|
||||||
|
for block, addy := range snap.Recents {
|
||||||
|
fmt.Printf("%v 0x%x\n", block, addy)
|
||||||
|
}
|
||||||
|
if blockchain.CurrentBlock().Number().Uint64() == 6 {
|
||||||
|
t.Error("Block should have been rejected")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that voting is evaluated correctly for various simple and complex scenarios.
|
// Tests that voting is evaluated correctly for various simple and complex scenarios.
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,11 @@ func (b *BlockGen) AddUncle(h *types.Header) {
|
||||||
b.uncles = append(b.uncles, h)
|
b.uncles = append(b.uncles, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDifficulty sets difficulty of the block
|
||||||
|
func (b *BlockGen) SetDifficulty(d *big.Int) {
|
||||||
|
b.header.Difficulty = d
|
||||||
|
}
|
||||||
|
|
||||||
// PrevBlock returns a previously generated block by number. It panics if
|
// PrevBlock returns a previously generated block by number. It panics if
|
||||||
// num is greater or equal to the number of the block being generated.
|
// num is greater or equal to the number of the block being generated.
|
||||||
// For index -1, PrevBlock returns the parent block given to GenerateChain.
|
// For index -1, PrevBlock returns the parent block given to GenerateChain.
|
||||||
|
|
@ -165,6 +170,9 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||||
// values. Inserting them into BlockChain requires use of FakePow or
|
// values. Inserting them into BlockChain requires use of FakePow or
|
||||||
// a similar non-validating proof of work implementation.
|
// a similar non-validating proof of work implementation.
|
||||||
func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
func GenerateChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||||
|
return GenerateChainSeal(config, parent, engine, db, n, gen, nil)
|
||||||
|
}
|
||||||
|
func GenerateChainSeal(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen), sealer func(block *types.Block) *types.Header) ([]*types.Block, []types.Receipts) {
|
||||||
if config == nil {
|
if config == nil {
|
||||||
config = params.TestChainConfig
|
config = params.TestChainConfig
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +213,11 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
|
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
|
||||||
panic(fmt.Sprintf("trie write error: %v", err))
|
panic(fmt.Sprintf("trie write error: %v", err))
|
||||||
}
|
}
|
||||||
|
if sealer != nil {
|
||||||
|
header := sealer(block)
|
||||||
|
return types.NewBlock(header, b.txs, b.uncles, b.receipts), nil
|
||||||
|
}
|
||||||
|
|
||||||
return block, b.receipts
|
return block, b.receipts
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue