mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 22:26:42 +00:00
core: basic fork tests
This commit is contained in:
parent
25fddfb76f
commit
7d5e2443e2
3 changed files with 32 additions and 45 deletions
53
core/fork.go
53
core/fork.go
|
|
@ -28,6 +28,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var errUnboundedParent = errors.New("core/fork: parent hash does not match last block") // unbounded parent
|
||||||
|
|
||||||
// ChainResolver should implement chain resolving and should be capable
|
// ChainResolver should implement chain resolving and should be capable
|
||||||
// handling reorganisations.
|
// handling reorganisations.
|
||||||
type ChainResolver interface {
|
type ChainResolver interface {
|
||||||
|
|
@ -57,41 +59,11 @@ type receipt struct {
|
||||||
|
|
||||||
// ChainFork is a temporary fork of the blockchain to which all block changes
|
// ChainFork is a temporary fork of the blockchain to which all block changes
|
||||||
// must be applied before being written out to the database.
|
// must be applied before being written out to the database.
|
||||||
//
|
|
||||||
// Example chain processing
|
|
||||||
//
|
|
||||||
// fork := Fork(chain, blocks[0].ParentHash)
|
|
||||||
// for _, block := range blocks {
|
|
||||||
// // State is an in-memory StateDB used throughout the fork
|
|
||||||
// err := ValidateBlock(block, fork.State())
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// fork.CommitBlock(block)
|
|
||||||
// }
|
|
||||||
// fork.ApplyTo(blockchain)
|
|
||||||
// fork.CommitToDb()
|
|
||||||
//
|
|
||||||
// Example block generation
|
|
||||||
//
|
|
||||||
// fork := Fork(chain, hash)
|
|
||||||
//
|
|
||||||
// // Create a new unsealed block
|
|
||||||
// block := fork.NewUnsealedBlock()
|
|
||||||
// // Apply transactions and uncles
|
|
||||||
// appliedTxs := block.ApplyTransactions(txpool.Transactions())
|
|
||||||
// appliedUncles := block.ApplyUncles(someUncles)
|
|
||||||
//
|
|
||||||
// // Seal the block, making it a valid sealed and signed block.
|
|
||||||
// block := Seal(powSealer, block)
|
|
||||||
//
|
|
||||||
// // Commit the block back to the fork.
|
|
||||||
// fork.CommitBlock(block)
|
|
||||||
//
|
|
||||||
type ChainFork struct {
|
type ChainFork struct {
|
||||||
db ethdb.Database // backing database
|
db ethdb.Database // backing database
|
||||||
reader BlockReader // block reader utility interface
|
reader BlockReader // block reader utility interface
|
||||||
state *state.StateDB // the current state database
|
state *state.StateDB // the current state database
|
||||||
|
config *ChainConfig // the chain configuration
|
||||||
|
|
||||||
originN uint64 // the origin block number
|
originN uint64 // the origin block number
|
||||||
origin *types.Block // origin notes the start of the fork
|
origin *types.Block // origin notes the start of the fork
|
||||||
|
|
@ -102,10 +74,11 @@ type ChainFork struct {
|
||||||
|
|
||||||
// Fork returns a new blockchain with the given database as backing layer
|
// Fork returns a new blockchain with the given database as backing layer
|
||||||
// for the localised blockchain transaction.
|
// for the localised blockchain transaction.
|
||||||
func Fork(blockReader BlockReader, origin common.Hash) (*ChainFork, error) {
|
func Fork(config *ChainConfig, blockReader BlockReader, origin common.Hash) (*ChainFork, error) {
|
||||||
fork := &ChainFork{
|
fork := &ChainFork{
|
||||||
db: blockReader.Db(),
|
db: blockReader.Db(),
|
||||||
reader: blockReader,
|
reader: blockReader,
|
||||||
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the origin block from which this fork originates
|
// get the origin block from which this fork originates
|
||||||
|
|
@ -131,17 +104,19 @@ func (fork *ChainFork) GetNumHash(n uint64) common.Hash {
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we should have it cached
|
// Check whether we should have it cached and retrieve it
|
||||||
|
// if present.
|
||||||
if n > fork.originN {
|
if n > fork.originN {
|
||||||
return fork.changes[n-fork.originN].block.Hash()
|
return fork.changes[n-(fork.originN+1)].block.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise search in the database
|
// Otherwise search in the database and retrieve it.
|
||||||
for block := fork.reader.GetBlock(fork.origin.Hash()); block != nil; block = fork.reader.GetBlock(block.ParentHash()) {
|
for block := fork.reader.GetBlock(fork.origin.Hash()); block != nil; block = fork.reader.GetBlock(block.ParentHash()) {
|
||||||
if block.NumberU64() == n {
|
if block.NumberU64() == n {
|
||||||
return block.Hash()
|
return block.Hash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Returns empty hash indicating "not-found".
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,8 +124,6 @@ func (fork *ChainFork) State() *state.StateDB {
|
||||||
return fork.state
|
return fork.state
|
||||||
}
|
}
|
||||||
|
|
||||||
var errUnboundedParent = errors.New("core/fork: parent hash does not match last block")
|
|
||||||
|
|
||||||
// CommitBlock commits a new block to the fork. The block that's being commited their parent hash must
|
// CommitBlock commits a new block to the fork. The block that's being commited their parent hash must
|
||||||
// match the previously committed block or the origin if the fork is empty.
|
// match the previously committed block or the origin if the fork is empty.
|
||||||
func (fork *ChainFork) CommitBlock(td *big.Int, block *types.Block, receipts types.Receipts) error {
|
func (fork *ChainFork) CommitBlock(td *big.Int, block *types.Block, receipts types.Receipts) error {
|
||||||
|
|
@ -257,17 +230,17 @@ func (fork *ChainFork) NewUnsealedBlock(coinbase common.Address, extra []byte) *
|
||||||
tstamp = parent.Time.Int64() + 1
|
tstamp = parent.Time.Int64() + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
unsealedBlock.header = types.Header{
|
unsealedBlock.Block = types.NewBlockWithHeader(&types.Header{
|
||||||
Root: fork.state.IntermediateRoot(),
|
Root: fork.state.IntermediateRoot(),
|
||||||
ParentHash: parent.Hash(),
|
ParentHash: parent.Hash(),
|
||||||
Number: new(big.Int).Add(parent.Number, common.Big1),
|
Number: new(big.Int).Add(parent.Number, common.Big1),
|
||||||
Difficulty: CalcDifficulty(nil, uint64(tstamp), parent.Time.Uint64(), parent.Number, parent.Difficulty),
|
Difficulty: CalcDifficulty(fork.config, uint64(tstamp), parent.Time.Uint64(), parent.Number, parent.Difficulty),
|
||||||
GasLimit: CalcGasLimit(types.NewBlockWithHeader(parent)),
|
GasLimit: CalcGasLimit(types.NewBlockWithHeader(parent)),
|
||||||
GasUsed: new(big.Int),
|
GasUsed: new(big.Int),
|
||||||
Coinbase: coinbase,
|
Coinbase: coinbase,
|
||||||
Extra: extra,
|
Extra: extra,
|
||||||
Time: big.NewInt(tstamp),
|
Time: big.NewInt(tstamp),
|
||||||
}
|
})
|
||||||
|
|
||||||
return unsealedBlock
|
return unsealedBlock
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -30,8 +31,9 @@ func (c fakeChain) GetBlock(hash common.Hash) *types.Block {
|
||||||
func TestGetNumHash(t *testing.T) {
|
func TestGetNumHash(t *testing.T) {
|
||||||
chain := newFakeChain()
|
chain := newFakeChain()
|
||||||
genesis := WriteGenesisBlockForTesting(chain.db)
|
genesis := WriteGenesisBlockForTesting(chain.db)
|
||||||
|
config := &ChainConfig{HomesteadBlock: new(big.Int)}
|
||||||
|
|
||||||
fork, err := Fork(chain, genesis.Hash())
|
fork, err := Fork(config, chain, genesis.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -44,4 +46,18 @@ func TestGetNumHash(t *testing.T) {
|
||||||
if fork.GetNumHash(1) != (common.Hash{}) {
|
if fork.GetNumHash(1) != (common.Hash{}) {
|
||||||
t.Error("expected exmpty hash to be returned")
|
t.Error("expected exmpty hash to be returned")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const chainLength = 5
|
||||||
|
blocks := make([]*UnsealedBlock, chainLength)
|
||||||
|
for i := 0; i < chainLength; i++ {
|
||||||
|
unsealedBlock := fork.NewUnsealedBlock(common.Address{}, nil)
|
||||||
|
fork.CommitBlock(new(big.Int), unsealedBlock.Block, unsealedBlock.receipts)
|
||||||
|
blocks[i] = unsealedBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := uint64(0); i < chainLength; i++ {
|
||||||
|
if fork.GetNumHash(fork.originN+i+1) != blocks[i].Block.Hash() {
|
||||||
|
t.Errorf("%d failed: expected %x got %x", i, fork.GetNumHash(fork.originN+i+1), blocks[i].Block.Hash())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,8 @@ import "github.com/ethereum/go-ethereum/core/types"
|
||||||
// UnsealedBlock is a not-yet finalised block to which you can
|
// UnsealedBlock is a not-yet finalised block to which you can
|
||||||
// keep applying transactions until the gas-limit is met.
|
// keep applying transactions until the gas-limit is met.
|
||||||
type UnsealedBlock struct {
|
type UnsealedBlock struct {
|
||||||
header types.Header // block header
|
*types.Block // embedded block
|
||||||
transactions types.Transactions // transactions previously applied
|
receipts types.Receipts // receipts generated by applying transactions
|
||||||
receipts types.Receipts // receipts generated by applying transactions
|
|
||||||
uncles []types.Header // uncles previously applied
|
|
||||||
|
|
||||||
gasPool *GasPool // gas pool for this block
|
gasPool *GasPool // gas pool for this block
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue