core: split out TD from database and all internals
This commit is contained in:
@ -316,9 +316,13 @@ func New(config *Config) (*Ethereum, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case config.GenesisBlock != nil: // This is for testing only.
|
||||
}
|
||||
// This is for testing only.
|
||||
if config.GenesisBlock != nil {
|
||||
core.WriteTd(chainDb, config.GenesisBlock.Hash(), config.GenesisBlock.Difficulty())
|
||||
core.WriteBlock(chainDb, config.GenesisBlock)
|
||||
core.WriteHead(chainDb, config.GenesisBlock)
|
||||
core.WriteCanonicalHash(chainDb, config.GenesisBlock.Hash(), config.GenesisBlock.NumberU64())
|
||||
core.WriteHeadBlockHash(chainDb, config.GenesisBlock.Hash())
|
||||
}
|
||||
|
||||
if !config.SkipBcVersionCheck {
|
||||
@ -752,7 +756,10 @@ func upgradeChainDatabase(db common.Database) error {
|
||||
// Load the block, split and serialize (order!)
|
||||
block := core.GetBlockByHashOld(db, common.BytesToHash(bytes.TrimPrefix(it.Key(), blockPrefix)))
|
||||
|
||||
if err := core.WriteBody(db, block); err != nil {
|
||||
if err := core.WriteTd(db, block.Hash(), block.DeprecatedTd()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := core.WriteBody(db, block.Hash(), &types.Body{block.Transactions(), block.Uncles()}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := core.WriteHeader(db, block.Header()); err != nil {
|
||||
@ -765,7 +772,10 @@ func upgradeChainDatabase(db common.Database) error {
|
||||
// Lastly, upgrade the head block, disabling the upgrade mechanism
|
||||
current := core.GetBlockByHashOld(db, head)
|
||||
|
||||
if err := core.WriteBody(db, current); err != nil {
|
||||
if err := core.WriteTd(db, current.Hash(), current.DeprecatedTd()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := core.WriteBody(db, current.Hash(), &types.Body{current.Transactions(), current.Uncles()}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := core.WriteHeader(db, current.Header()); err != nil {
|
||||
|
@ -87,6 +87,9 @@ type blockRetrievalFn func(common.Hash) *types.Block
|
||||
// headRetrievalFn is a callback type for retrieving the head block from the local chain.
|
||||
type headRetrievalFn func() *types.Block
|
||||
|
||||
// tdRetrievalFn is a callback type for retrieving the total difficulty of a local block.
|
||||
type tdRetrievalFn func(common.Hash) *big.Int
|
||||
|
||||
// chainInsertFn is a callback type to insert a batch of blocks into the local chain.
|
||||
type chainInsertFn func(types.Blocks) (int, error)
|
||||
|
||||
@ -136,6 +139,7 @@ type Downloader struct {
|
||||
hasBlock hashCheckFn // Checks if a block is present in the chain
|
||||
getBlock blockRetrievalFn // Retrieves a block from the chain
|
||||
headBlock headRetrievalFn // Retrieves the head block from the chain
|
||||
getTd tdRetrievalFn // Retrieves the TD of a block from the chain
|
||||
insertChain chainInsertFn // Injects a batch of blocks into the chain
|
||||
dropPeer peerDropFn // Drops a peer for misbehaving
|
||||
|
||||
@ -168,7 +172,7 @@ type Block struct {
|
||||
}
|
||||
|
||||
// New creates a new downloader to fetch hashes and blocks from remote peers.
|
||||
func New(mux *event.TypeMux, hasBlock hashCheckFn, getBlock blockRetrievalFn, headBlock headRetrievalFn, insertChain chainInsertFn, dropPeer peerDropFn) *Downloader {
|
||||
func New(mux *event.TypeMux, hasBlock hashCheckFn, getBlock blockRetrievalFn, headBlock headRetrievalFn, getTd tdRetrievalFn, insertChain chainInsertFn, dropPeer peerDropFn) *Downloader {
|
||||
return &Downloader{
|
||||
mux: mux,
|
||||
queue: newQueue(),
|
||||
@ -176,6 +180,7 @@ func New(mux *event.TypeMux, hasBlock hashCheckFn, getBlock blockRetrievalFn, he
|
||||
hasBlock: hasBlock,
|
||||
getBlock: getBlock,
|
||||
headBlock: headBlock,
|
||||
getTd: getTd,
|
||||
insertChain: insertChain,
|
||||
dropPeer: dropPeer,
|
||||
newPeerCh: make(chan *peer, 1),
|
||||
@ -582,7 +587,7 @@ func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
|
||||
// L: Sync begins, and finds common ancestor at 11
|
||||
// L: Request new hashes up from 11 (R's TD was higher, it must have something)
|
||||
// R: Nothing to give
|
||||
if !gotHashes && td.Cmp(d.headBlock().Td) > 0 {
|
||||
if !gotHashes && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 {
|
||||
return errStallingPeer
|
||||
}
|
||||
return nil
|
||||
@ -958,7 +963,7 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
|
||||
// L: Sync begins, and finds common ancestor at 11
|
||||
// L: Request new headers up from 11 (R's TD was higher, it must have something)
|
||||
// R: Nothing to give
|
||||
if !gotHeaders && td.Cmp(d.headBlock().Td) > 0 {
|
||||
if !gotHeaders && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 {
|
||||
return errStallingPeer
|
||||
}
|
||||
return nil
|
||||
|
@ -93,21 +93,25 @@ func makeChainFork(n, f int, parent *types.Block) (h1, h2 []common.Hash, b1, b2
|
||||
type downloadTester struct {
|
||||
downloader *Downloader
|
||||
|
||||
ownHashes []common.Hash // Hash chain belonging to the tester
|
||||
ownBlocks map[common.Hash]*types.Block // Blocks belonging to the tester
|
||||
peerHashes map[string][]common.Hash // Hash chain belonging to different test peers
|
||||
peerBlocks map[string]map[common.Hash]*types.Block // Blocks belonging to different test peers
|
||||
ownHashes []common.Hash // Hash chain belonging to the tester
|
||||
ownBlocks map[common.Hash]*types.Block // Blocks belonging to the tester
|
||||
ownChainTd map[common.Hash]*big.Int // Total difficulties of the blocks in the local chain
|
||||
peerHashes map[string][]common.Hash // Hash chain belonging to different test peers
|
||||
peerBlocks map[string]map[common.Hash]*types.Block // Blocks belonging to different test peers
|
||||
peerChainTds map[string]map[common.Hash]*big.Int // Total difficulties of the blocks in the peer chains
|
||||
}
|
||||
|
||||
// newTester creates a new downloader test mocker.
|
||||
func newTester() *downloadTester {
|
||||
tester := &downloadTester{
|
||||
ownHashes: []common.Hash{genesis.Hash()},
|
||||
ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
||||
peerHashes: make(map[string][]common.Hash),
|
||||
peerBlocks: make(map[string]map[common.Hash]*types.Block),
|
||||
ownHashes: []common.Hash{genesis.Hash()},
|
||||
ownBlocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
|
||||
ownChainTd: map[common.Hash]*big.Int{genesis.Hash(): genesis.Difficulty()},
|
||||
peerHashes: make(map[string][]common.Hash),
|
||||
peerBlocks: make(map[string]map[common.Hash]*types.Block),
|
||||
peerChainTds: make(map[string]map[common.Hash]*big.Int),
|
||||
}
|
||||
tester.downloader = New(new(event.TypeMux), tester.hasBlock, tester.getBlock, tester.headBlock, tester.insertChain, tester.dropPeer)
|
||||
tester.downloader = New(new(event.TypeMux), tester.hasBlock, tester.getBlock, tester.headBlock, tester.getTd, tester.insertChain, tester.dropPeer)
|
||||
|
||||
return tester
|
||||
}
|
||||
@ -119,8 +123,8 @@ func (dl *downloadTester) sync(id string, td *big.Int) error {
|
||||
// If no particular TD was requested, load from the peer's blockchain
|
||||
if td == nil {
|
||||
td = big.NewInt(1)
|
||||
if block, ok := dl.peerBlocks[id][hash]; ok {
|
||||
td = block.Td
|
||||
if diff, ok := dl.peerChainTds[id][hash]; ok {
|
||||
td = diff
|
||||
}
|
||||
}
|
||||
err := dl.downloader.synchronise(id, hash, td)
|
||||
@ -152,6 +156,11 @@ func (dl *downloadTester) headBlock() *types.Block {
|
||||
return dl.getBlock(dl.ownHashes[len(dl.ownHashes)-1])
|
||||
}
|
||||
|
||||
// getTd retrieves the block's total difficulty from the canonical chain.
|
||||
func (dl *downloadTester) getTd(hash common.Hash) *big.Int {
|
||||
return dl.ownChainTd[hash]
|
||||
}
|
||||
|
||||
// insertChain injects a new batch of blocks into the simulated chain.
|
||||
func (dl *downloadTester) insertChain(blocks types.Blocks) (int, error) {
|
||||
for i, block := range blocks {
|
||||
@ -160,6 +169,7 @@ func (dl *downloadTester) insertChain(blocks types.Blocks) (int, error) {
|
||||
}
|
||||
dl.ownHashes = append(dl.ownHashes, block.Hash())
|
||||
dl.ownBlocks[block.Hash()] = block
|
||||
dl.ownChainTd[block.Hash()] = dl.ownChainTd[block.ParentHash()]
|
||||
}
|
||||
return len(blocks), nil
|
||||
}
|
||||
@ -180,9 +190,16 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
|
||||
// Assign the owned hashes and blocks to the peer (deep copy)
|
||||
dl.peerHashes[id] = make([]common.Hash, len(hashes))
|
||||
copy(dl.peerHashes[id], hashes)
|
||||
|
||||
dl.peerBlocks[id] = make(map[common.Hash]*types.Block)
|
||||
for hash, block := range blocks {
|
||||
dl.peerBlocks[id][hash] = block
|
||||
dl.peerChainTds[id] = make(map[common.Hash]*big.Int)
|
||||
for _, hash := range hashes {
|
||||
if block, ok := blocks[hash]; ok {
|
||||
dl.peerBlocks[id][hash] = block
|
||||
if parent, ok := dl.peerBlocks[id][block.ParentHash()]; ok {
|
||||
dl.peerChainTds[id][hash] = new(big.Int).Add(block.Difficulty(), dl.peerChainTds[id][parent.Hash()])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
@ -192,6 +209,7 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
|
||||
func (dl *downloadTester) dropPeer(id string) {
|
||||
delete(dl.peerHashes, id)
|
||||
delete(dl.peerBlocks, id)
|
||||
delete(dl.peerChainTds, id)
|
||||
|
||||
dl.downloader.UnregisterPeer(id)
|
||||
}
|
||||
|
@ -36,8 +36,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// This is the target maximum size of returned blocks, headers or node data.
|
||||
const softResponseLimit = 2 * 1024 * 1024
|
||||
const (
|
||||
softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data.
|
||||
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
|
||||
)
|
||||
|
||||
func errResp(code errCode, format string, v ...interface{}) error {
|
||||
return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...))
|
||||
@ -113,7 +115,7 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
|
||||
}
|
||||
}
|
||||
// Construct the different synchronisation mechanisms
|
||||
manager.downloader = downloader.New(manager.eventMux, manager.chainman.HasBlock, manager.chainman.GetBlock, manager.chainman.CurrentBlock, manager.chainman.InsertChain, manager.removePeer)
|
||||
manager.downloader = downloader.New(manager.eventMux, manager.chainman.HasBlock, manager.chainman.GetBlock, manager.chainman.CurrentBlock, manager.chainman.GetTd, manager.chainman.InsertChain, manager.removePeer)
|
||||
|
||||
validator := func(block *types.Block, parent *types.Block) error {
|
||||
return core.ValidateHeader(pow, block.Header(), parent, true, false)
|
||||
@ -363,7 +365,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||
break
|
||||
}
|
||||
headers = append(headers, origin)
|
||||
bytes += 500 // Approximate, should be good enough estimate
|
||||
bytes += estHeaderRlpSize
|
||||
|
||||
// Advance to the next header of the query
|
||||
switch {
|
||||
@ -453,7 +455,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||
var (
|
||||
hash common.Hash
|
||||
bytes int
|
||||
bodies []*blockBodyRLP
|
||||
bodies []rlp.RawValue
|
||||
)
|
||||
for bytes < softResponseLimit && len(bodies) < downloader.MaxBlockFetch {
|
||||
// Retrieve the hash of the next block
|
||||
@ -464,9 +466,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||
}
|
||||
// Retrieve the requested block body, stopping if enough was found
|
||||
if data := pm.chainman.GetBodyRLP(hash); len(data) != 0 {
|
||||
body := blockBodyRLP(data)
|
||||
bodies = append(bodies, &body)
|
||||
bytes += len(body)
|
||||
bodies = append(bodies, data)
|
||||
bytes += len(data)
|
||||
}
|
||||
}
|
||||
return p.SendBlockBodiesRLP(bodies)
|
||||
@ -644,7 +645,7 @@ func (pm *ProtocolManager) BroadcastBlock(block *types.Block, propagate bool) {
|
||||
// Calculate the TD of the block (it's not imported yet, so block.Td is not valid)
|
||||
var td *big.Int
|
||||
if parent := pm.chainman.GetBlock(block.ParentHash()); parent != nil {
|
||||
td = new(big.Int).Add(parent.Td, block.Difficulty())
|
||||
td = new(big.Int).Add(block.Difficulty(), pm.chainman.GetTd(block.ParentHash()))
|
||||
} else {
|
||||
glog.V(logger.Error).Infof("propagating dangling block #%d [%x]", block.NumberU64(), hash[:4])
|
||||
return
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
@ -186,8 +187,8 @@ func (p *peer) SendBlockBodies(bodies []*blockBody) error {
|
||||
|
||||
// SendBlockBodiesRLP sends a batch of block contents to the remote peer from
|
||||
// an already RLP encoded format.
|
||||
func (p *peer) SendBlockBodiesRLP(bodies []*blockBodyRLP) error {
|
||||
return p2p.Send(p.rw, BlockBodiesMsg, blockBodiesRLPData(bodies))
|
||||
func (p *peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error {
|
||||
return p2p.Send(p.rw, BlockBodiesMsg, bodies)
|
||||
}
|
||||
|
||||
// SendNodeData sends a batch of arbitrary internal data, corresponding to the
|
||||
|
@ -213,22 +213,6 @@ type blockBody struct {
|
||||
// blockBodiesData is the network packet for block content distribution.
|
||||
type blockBodiesData []*blockBody
|
||||
|
||||
// blockBodyRLP represents the RLP encoded data content of a single block.
|
||||
type blockBodyRLP []byte
|
||||
|
||||
// EncodeRLP is a specialized encoder for a block body to pass the already
|
||||
// encoded body RLPs from the database on, without double encoding.
|
||||
func (b *blockBodyRLP) EncodeRLP(w io.Writer) error {
|
||||
if _, err := w.Write([]byte(*b)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// blockBodiesRLPData is the network packet for block content distribution
|
||||
// based on original RLP formatting (i.e. skip the db-decode/proto-encode).
|
||||
type blockBodiesRLPData []*blockBodyRLP
|
||||
|
||||
// nodeDataData is the network response packet for a node data retrieval.
|
||||
type nodeDataData []struct {
|
||||
Value []byte
|
||||
|
Reference in New Issue
Block a user