Commit d4bb3798 authored by gary rong's avatar gary rong Committed by Felix Lange

miner: add generate and import unit test (#20111)

This PR adds a new unit test in miner package which will create some blocks from miner and then import into another chain. In this way, we can ensure all blocks generated by Geth miner obey consensus rules.
parent 08953e42
...@@ -18,9 +18,11 @@ package miner ...@@ -18,9 +18,11 @@ package miner
import ( import (
"math/big" "math/big"
"math/rand"
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique" "github.com/ethereum/go-ethereum/consensus/clique"
...@@ -35,6 +37,15 @@ import ( ...@@ -35,6 +37,15 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
const (
// testCode is the testing contract binary code which will initialises some
// variables in constructor
testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032"
// testGas is the gas required for contract deployment.
testGas = 144109
)
var ( var (
// Test chain configurations // Test chain configurations
testTxPoolConfig core.TxPoolConfig testTxPoolConfig core.TxPoolConfig
...@@ -81,29 +92,30 @@ type testWorkerBackend struct { ...@@ -81,29 +92,30 @@ type testWorkerBackend struct {
txPool *core.TxPool txPool *core.TxPool
chain *core.BlockChain chain *core.BlockChain
testTxFeed event.Feed testTxFeed event.Feed
genesis *core.Genesis
uncleBlock *types.Block uncleBlock *types.Block
} }
func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, n int) *testWorkerBackend { func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend {
var ( var gspec = core.Genesis{
db = rawdb.NewMemoryDatabase() Config: chainConfig,
gspec = core.Genesis{ Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
Config: chainConfig, }
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}},
}
)
switch engine.(type) { switch e := engine.(type) {
case *clique.Clique: case *clique.Clique:
gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength) gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
copy(gspec.ExtraData[32:], testBankAddress[:]) 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)
})
case *ethash.Ethash: case *ethash.Ethash:
default: default:
t.Fatalf("unexpected consensus engine type: %T", engine) t.Fatalf("unexpected consensus engine type: %T", engine)
} }
genesis := gspec.MustCommit(db) genesis := gspec.MustCommit(db)
chain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil) chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, gspec.Config, engine, vm.Config{}, nil)
txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain) txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain)
// Generate a small n-block chain and an uncle block for it // Generate a small n-block chain and an uncle block for it
...@@ -127,6 +139,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine ...@@ -127,6 +139,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
db: db, db: db,
chain: chain, chain: chain,
txPool: txpool, txPool: txpool,
genesis: &gspec,
uncleBlock: blocks[0], uncleBlock: blocks[0],
} }
} }
...@@ -137,14 +150,123 @@ func (b *testWorkerBackend) PostChainEvents(events []interface{}) { ...@@ -137,14 +150,123 @@ func (b *testWorkerBackend) PostChainEvents(events []interface{}) {
b.chain.PostChainEvents(events, nil) b.chain.PostChainEvents(events, nil)
} }
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, blocks int) (*worker, *testWorkerBackend) { func (b *testWorkerBackend) newRandomUncle() *types.Block {
backend := newTestWorkerBackend(t, chainConfig, engine, blocks) var parent *types.Block
cur := b.chain.CurrentBlock()
if cur.NumberU64() == 0 {
parent = b.chain.Genesis()
} else {
parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash())
}
blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.db, 1, func(i int, gen *core.BlockGen) {
var addr common.Address
rand.Read(addr.Bytes())
gen.SetCoinbase(addr)
})
return blocks[0]
}
func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
var tx *types.Transaction
if creation {
tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(testBankAddress), big.NewInt(0), testGas, nil, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey)
} else {
tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(testBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey)
}
return tx
}
func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int) (*worker, *testWorkerBackend) {
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) w := newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil)
w.setEtherbase(testBankAddress) w.setEtherbase(testBankAddress)
return w, backend return w, backend
} }
func TestGenerateBlockAndImportEthash(t *testing.T) {
testGenerateBlockAndImport(t, false)
}
func TestGenerateBlockAndImportClique(t *testing.T) {
testGenerateBlockAndImport(t, true)
}
func testGenerateBlockAndImport(t *testing.T, isClique bool) {
var (
engine consensus.Engine
chainConfig *params.ChainConfig
db = rawdb.NewMemoryDatabase()
)
if isClique {
chainConfig = params.AllCliqueProtocolChanges
engine = clique.New(chainConfig.Clique, db)
} else {
chainConfig = params.AllEthashProtocolChanges
engine = ethash.NewFaker()
}
w, b := newTestWorker(t, chainConfig, engine, db, 0)
defer w.close()
db2 := rawdb.NewMemoryDatabase()
b.genesis.MustCommit(db2)
chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil)
defer chain.Stop()
newBlock := make(chan struct{})
listenNewBlock := func() {
sub := w.mux.Subscribe(core.NewMinedBlockEvent{})
defer sub.Unsubscribe()
for item := range sub.Chan() {
block := item.Data.(core.NewMinedBlockEvent).Block
_, err := chain.InsertChain([]*types.Block{block})
if err != nil {
t.Fatalf("Failed to insert new mined block:%d, error:%v", block.NumberU64(), err)
}
newBlock <- struct{}{}
}
}
// Ensure worker has finished initialization
for {
b := w.pendingBlock()
if b != nil && b.NumberU64() == 1 {
break
}
}
w.start() // Start mining!
// Ignore first 2 commits caused by start operation
ignored := make(chan struct{}, 2)
w.skipSealHook = func(task *task) bool {
ignored <- struct{}{}
return true
}
for i := 0; i < 2; i++ {
<-ignored
}
go listenNewBlock()
// Ignore empty commit here for less noise
w.skipSealHook = func(task *task) bool {
return len(task.receipts) == 0
}
for i := 0; i < 50; i++ {
b.txPool.AddLocal(b.newRandomTx(true))
b.txPool.AddLocal(b.newRandomTx(false))
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.newRandomUncle()}})
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.newRandomUncle()}})
select {
case <-newBlock:
case <-time.NewTimer(time.Millisecond * 1500).C: // Worker needs 1s to include new changes.
t.Fatalf("timeout")
}
}
}
func TestPendingStateAndBlockEthash(t *testing.T) { func TestPendingStateAndBlockEthash(t *testing.T) {
testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker()) testPendingStateAndBlock(t, ethashChainConfig, ethash.NewFaker())
} }
...@@ -155,7 +277,7 @@ func TestPendingStateAndBlockClique(t *testing.T) { ...@@ -155,7 +277,7 @@ func TestPendingStateAndBlockClique(t *testing.T) {
func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, b := newTestWorker(t, chainConfig, engine, 0) w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
defer w.close() defer w.close()
// Ensure snapshot has been updated. // Ensure snapshot has been updated.
...@@ -187,7 +309,7 @@ func TestEmptyWorkClique(t *testing.T) { ...@@ -187,7 +309,7 @@ func TestEmptyWorkClique(t *testing.T) {
func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, _ := newTestWorker(t, chainConfig, engine, 0) w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
defer w.close() defer w.close()
var ( var (
...@@ -241,7 +363,7 @@ func TestStreamUncleBlock(t *testing.T) { ...@@ -241,7 +363,7 @@ func TestStreamUncleBlock(t *testing.T) {
ethash := ethash.NewFaker() ethash := ethash.NewFaker()
defer ethash.Close() defer ethash.Close()
w, b := newTestWorker(t, ethashChainConfig, ethash, 1) w, b := newTestWorker(t, ethashChainConfig, ethash, rawdb.NewMemoryDatabase(), 1)
defer w.close() defer w.close()
var taskCh = make(chan struct{}) var taskCh = make(chan struct{})
...@@ -304,7 +426,7 @@ func TestRegenerateMiningBlockClique(t *testing.T) { ...@@ -304,7 +426,7 @@ func TestRegenerateMiningBlockClique(t *testing.T) {
func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testRegenerateMiningBlock(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, b := newTestWorker(t, chainConfig, engine, 0) w, b := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
defer w.close() defer w.close()
var taskCh = make(chan struct{}) var taskCh = make(chan struct{})
...@@ -369,7 +491,7 @@ func TestAdjustIntervalClique(t *testing.T) { ...@@ -369,7 +491,7 @@ func TestAdjustIntervalClique(t *testing.T) {
func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) { func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine consensus.Engine) {
defer engine.Close() defer engine.Close()
w, _ := newTestWorker(t, chainConfig, engine, 0) w, _ := newTestWorker(t, chainConfig, engine, rawdb.NewMemoryDatabase(), 0)
defer w.close() defer w.close()
w.skipSealHook = func(task *task) bool { w.skipSealHook = func(task *task) bool {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment