From fdffaf31c438bcb9d5293fe129694bdcf995f6be Mon Sep 17 00:00:00 2001 From: rjl493456442 Date: Fri, 17 Nov 2017 11:30:38 +0800 Subject: [PATCH] consensus, core: move CalcDifficulty to Engine interface --- consensus/clique/clique.go | 23 ++++++++--- consensus/consensus.go | 13 ++----- consensus/ethash/consensus.go | 16 +++++--- consensus/ethash/consensus_test.go | 3 +- core/chain_makers.go | 62 ++++++++++++++---------------- tests/difficulty_test_util.go | 2 +- 6 files changed, 64 insertions(+), 55 deletions(-) diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 6892d83906..4db2a27bcb 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -510,7 +510,6 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro header.Nonce = types.BlockNonce{} number := header.Number.Uint64() - // Assemble the voting snapshot to check which votes make sense snap, err := c.snapshot(chain, number-1, header.ParentHash, nil) if err != nil { @@ -538,10 +537,8 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro c.lock.RUnlock() } // Set the correct difficulty - header.Difficulty = diffNoTurn - if snap.inturn(header.Number.Uint64(), c.signer) { - header.Difficulty = diffInTurn - } + header.Difficulty = CalcDifficulty(snap, c.signer) + // Ensure the extra data has all it's components if len(header.Extra) < extraVanity { header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...) @@ -655,6 +652,22 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, stop <-ch return block.WithSeal(header), nil } +// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty that a new block should have. +func (c *Clique) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { + snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil) + if err != nil { + return nil + } + return CalcDifficulty(snap, c.signer) +} + +func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int { + if snap.inturn(snap.Number+1, signer) { + return new(big.Int).Set(diffInTurn) + } + return new(big.Int).Set(diffNoTurn) +} + // APIs implements consensus.Engine, returning the user facing RPC API to allow // controlling the signer voting. func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API { diff --git a/consensus/consensus.go b/consensus/consensus.go index 937bf2e632..be5e661c12 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -89,6 +89,10 @@ type Engine interface { // seal place on top. Seal(chain ChainReader, block *types.Block, stop <-chan struct{}) (*types.Block, error) + // CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty + // that a new block should have. + CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int + // APIs returns the RPC APIs this consensus engine provides. APIs(chain ChainReader) []rpc.API } @@ -99,13 +103,4 @@ type PoW interface { // Hashrate returns the current mining hashrate of a PoW consensus engine. Hashrate() float64 - - // CalcDifficulty returns the difficulty that a new block should have when created at time - // given the parent block's time and difficulty. - CalcDifficulty(*params.ChainConfig, uint64, *types.Header) *big.Int - - // AccumulateRewards credits the coinbase of the given block with the mining - // reward. The total reward consists of the static block reward and rewards for - // included uncles. The coinbase of each uncle block is also rewarded. - AccumulateRewards(*params.ChainConfig, *state.StateDB, *types.Header, []*types.Header) } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 0a2614d7bc..eef6fb0df2 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -239,7 +239,8 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * return errZeroBlockTime } // Verify the block's difficulty based in it's timestamp and parent's difficulty - expected := ethash.CalcDifficulty(chain.Config(), header.Time.Uint64(), parent) + expected := ethash.CalcDifficulty(chain, header.Time.Uint64(), parent) + if expected.Cmp(header.Difficulty) != 0 { return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected) } @@ -286,7 +287,11 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * // CalcDifficulty is the difficulty adjustment algorithm. It returns // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. -func (ethash *Ethash) CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { +func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int { + return CalcDifficulty(chain.Config(), time, parent) +} + +func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int { next := new(big.Int).Add(parent.Number, big1) switch { case config.IsByzantium(next): @@ -500,8 +505,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) if parent == nil { return consensus.ErrUnknownAncestor } - header.Difficulty = ethash.CalcDifficulty(chain.Config(), header.Time.Uint64(), parent) - + header.Difficulty = ethash.CalcDifficulty(chain, header.Time.Uint64(), parent) return nil } @@ -509,7 +513,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) // setting the final state and assembling the block. func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { // Accumulate any block and uncle rewards and commit the final state root - ethash.AccumulateRewards(chain.Config(), state, header, uncles) + accumulateRewards(chain.Config(), state, header, uncles) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) // Header seems complete, assemble into a block and return @@ -525,7 +529,7 @@ var ( // AccumulateRewards credits the coinbase of the given block with the mining // reward. The total reward consists of the static block reward and rewards for // included uncles. The coinbase of each uncle block is also rewarded. -func (ethash *Ethash) AccumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { +func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) { // Select the correct block reward based on chain progression blockReward := frontierBlockReward if config.IsByzantium(header.Number) { diff --git a/consensus/ethash/consensus_test.go b/consensus/ethash/consensus_test.go index 3d14c2136b..438a99dd66 100644 --- a/consensus/ethash/consensus_test.go +++ b/consensus/ethash/consensus_test.go @@ -71,9 +71,10 @@ func TestCalcDifficulty(t *testing.T) { } config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)} + for name, test := range tests { number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1)) - diff := NewFaker().CalcDifficulty(config, test.CurrentTimestamp, &types.Header{ + diff := CalcDifficulty(config, test.CurrentTimestamp, &types.Header{ Number: number, Time: new(big.Int).SetUint64(test.ParentTimestamp), Difficulty: test.ParentDifficulty, diff --git a/core/chain_makers.go b/core/chain_makers.go index 27ecb8ce03..d107add59c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -39,11 +39,12 @@ var ( // BlockGen creates blocks for testing. // See GenerateChain for a detailed explanation. type BlockGen struct { - i int - parent *types.Block - chain []*types.Block - header *types.Header - statedb *state.StateDB + i int + parent *types.Block + chain []*types.Block + chainReader consensus.ChainReader + header *types.Header + statedb *state.StateDB gasPool *GasPool txs []*types.Transaction @@ -142,9 +143,7 @@ func (b *BlockGen) OffsetTime(seconds int64) { if b.header.Time.Cmp(b.parent.Header().Time) <= 0 { panic("block time out of range") } - if pow, ok := b.engine.(consensus.PoW); ok { - b.header.Difficulty = pow.CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Header()) - } + b.header.Difficulty = b.engine.CalcDifficulty(b.chainReader, b.header.Time.Uint64(), b.parent.Header()) } // GenerateChain creates a chain of n blocks. The first block's @@ -165,8 +164,10 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse } blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n) genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { - b := &BlockGen{i: i, parent: parent, chain: blocks, statedb: statedb, config: config, engine: engine} - b.header = makeHeader(config, parent, statedb, b.engine) + blockchain, _ := NewBlockChain(db, config, engine, vm.Config{}) + defer blockchain.Stop() + b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine} + b.header = makeHeader(b.chainReader, parent, statedb, b.engine) // Mutate the state and block according to any hard-fork specs if daoBlock := config.DAOForkBlock; daoBlock != nil { @@ -185,16 +186,16 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse gen(i, b) } - if pow, ok := b.engine.(consensus.PoW); ok { - pow.AccumulateRewards(config, statedb, b.header, b.uncles) + if b.engine != nil { + block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts) + // Write state changes to db + _, err := statedb.CommitTo(db, config.IsEIP158(b.header.Number)) + if err != nil { + panic(fmt.Sprintf("state write error: %v", err)) + } + return block, b.receipts } - - root, err := statedb.CommitTo(db, config.IsEIP158(b.header.Number)) - if err != nil { - panic(fmt.Sprintf("state write error: %v", err)) - } - b.header.Root = root - return types.NewBlock(b.header, b.txs, b.uncles, b.receipts), b.receipts + return nil, nil } for i := 0; i < n; i++ { statedb, err := state.New(parent.Root(), state.NewDatabase(db)) @@ -209,7 +210,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse return blocks, receipts } -func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { +func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header { var time *big.Int if parent.Time() == nil { time = big.NewInt(10) @@ -217,26 +218,21 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds } - header := &types.Header{ - Root: state.IntermediateRoot(config.IsEIP158(parent.Number())), + return &types.Header{ + Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), - GasLimit: CalcGasLimit(parent), - GasUsed: new(big.Int), - Number: new(big.Int).Add(parent.Number(), common.Big1), - Time: time, - } - - if pow, ok := engine.(consensus.PoW); ok { - header.Difficulty = pow.CalcDifficulty(config, time.Uint64(), &types.Header{ + Difficulty: engine.CalcDifficulty(chain, time.Uint64(), &types.Header{ Number: parent.Number(), Time: new(big.Int).Sub(time, big.NewInt(10)), Difficulty: parent.Difficulty(), UncleHash: parent.UncleHash(), - }) + }), + GasLimit: CalcGasLimit(parent), + GasUsed: new(big.Int), + Number: new(big.Int).Add(parent.Number(), common.Big1), + Time: time, } - - return header } // newCanonical creates a chain database, and injects a deterministic canonical diff --git a/tests/difficulty_test_util.go b/tests/difficulty_test_util.go index 4eb6cbc0e4..7541477933 100644 --- a/tests/difficulty_test_util.go +++ b/tests/difficulty_test_util.go @@ -57,7 +57,7 @@ func (test *DifficultyTest) Run(config *params.ChainConfig) error { UncleHash: test.UncleHash, } - actual := ethash.NewFaker().CalcDifficulty(config, test.CurrentTimestamp.Uint64(), parent) + actual := ethash.CalcDifficulty(config, test.CurrentTimestamp.Uint64(), parent) exp := test.CurrentDifficulty if actual.Cmp(exp) != 0 {