core: split out TD from database and all internals

This commit is contained in:
Péter Szilágyi
2015-09-07 20:43:01 +03:00
parent 2b339cbbd8
commit cdc2662c40
19 changed files with 595 additions and 299 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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