[DEV-169] add to BlockDAG TipHashes() and HighestTipHash() and remove Tiphashes() and SelectedTipHash from VirtualBlock (#82)

* [DEV-169] add to BlockDAG TipHashes() and HighestTipHash() and remove Tiphashes() and SelectedTipHash from VirtualBlock

* [DEV-169] move highest node logic to separate method of blockset
This commit is contained in:
Ori Newman 2018-10-07 13:07:30 +03:00 committed by stasatdaglabs
parent 2f3e0a609c
commit 3c88184b38
11 changed files with 60 additions and 57 deletions

View File

@ -35,6 +35,16 @@ func (bs blockSet) maxHeight() int32 {
return maxHeight return maxHeight
} }
func (bs blockSet) highest() *blockNode {
var highest *blockNode
for _, node := range bs {
if highest.height < node.height || daghash.Less(&highest.hash, &node.hash) {
highest = node
}
}
return highest
}
// add adds a block to this BlockSet // add adds a block to this BlockSet
func (bs blockSet) add(block *blockNode) { func (bs blockSet) add(block *blockNode) {
bs[block.hash] = block bs[block.hash] = block

View File

@ -516,7 +516,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block) error {
// Atomically insert info into the database. // Atomically insert info into the database.
err = dag.db.Update(func(dbTx database.Tx) error { err = dag.db.Update(func(dbTx database.Tx) error {
// Update best block state. // Update best block state.
err := dbPutDAGTipHashes(dbTx, dag.virtual.TipHashes()) err := dbPutDAGTipHashes(dbTx, dag.TipHashes())
if err != nil { if err != nil {
return err return err
} }
@ -930,6 +930,17 @@ func (dag *BlockDAG) Height() int32 {
return dag.virtual.tips().maxHeight() return dag.virtual.tips().maxHeight()
} }
// TipHashes returns the hashes of the DAG's tips
func (dag *BlockDAG) TipHashes() []daghash.Hash {
return dag.virtual.tips().hashes()
}
// HighestTipHash returns the hash of the highest tip.
// This function is a placeholder for places that aren't DAG-compatible, and it's needed to be removed in the future
func (dag *BlockDAG) HighestTipHash() daghash.Hash {
return dag.virtual.tips().highest().hash
}
// HeaderByHash returns the block header identified by the given hash or an // HeaderByHash returns the block header identified by the given hash or an
// error if it doesn't exist. // error if it doesn't exist.
func (dag *BlockDAG) HeaderByHash(hash *daghash.Hash) (wire.BlockHeader, error) { func (dag *BlockDAG) HeaderByHash(hash *daghash.Hash) (wire.BlockHeader, error) {

View File

@ -811,7 +811,7 @@ func (dag *BlockDAG) createDAGState() error {
} }
// Store the current DAG tip hashes into the database. // Store the current DAG tip hashes into the database.
err = dbPutDAGTipHashes(dbTx, dag.virtual.TipHashes()) err = dbPutDAGTipHashes(dbTx, dag.TipHashes())
if err != nil { if err != nil {
return err return err
} }

View File

@ -272,14 +272,13 @@ func TestFullBlocks(t *testing.T) {
item.Name, block.Hash(), blockHeight) item.Name, block.Hash(), blockHeight)
// Ensure hash and height match. // Ensure hash and height match.
virtualBlock := dag.VirtualBlock() if dag.HighestTipHash() != item.Block.BlockHash() ||
if virtualBlock.SelectedTipHash() != item.Block.BlockHash() || dag.Height() != blockHeight { //TODO: (Ori) the use of dag.Height() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation
dag.Height() != blockHeight { //TODO: (Ori) the use of dag.Height() and virtualBlock.SelectedTipHash() is wrong, and was done only for compilation
t.Fatalf("block %q (hash %s, height %d) should be "+ t.Fatalf("block %q (hash %s, height %d) should be "+
"the current tip -- got (hash %s, height %d)", "the current tip -- got (hash %s, height %d)",
item.Name, block.Hash(), blockHeight, virtualBlock.SelectedTipHash(), item.Name, block.Hash(), blockHeight, dag.HighestTipHash(),
dag.Height()) //TODO: (Ori) the use of dag.Height() and virtualBlock.SelectedTipHash() is wrong, and was done only for compilation dag.Height()) //TODO: (Ori) the use of dag.Height() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation
} }
} }

View File

@ -7,7 +7,6 @@ package blockdag
import ( import (
"sync" "sync"
"github.com/daglabs/btcd/dagconfig/daghash"
"github.com/daglabs/btcd/wire" "github.com/daglabs/btcd/wire"
) )
@ -101,16 +100,6 @@ func (v *VirtualBlock) SelectedTip() *blockNode {
return v.selectedParent return v.selectedParent
} }
// TipHashes returns the hashes of the tips of the virtual block.
func (v *VirtualBlock) TipHashes() []daghash.Hash {
return v.tips().hashes()
}
// SelectedTipHash returns the hash of the selected tip of the virtual block.
func (v *VirtualBlock) SelectedTipHash() daghash.Hash {
return v.SelectedTip().hash
}
// GetUTXOEntry returns the requested unspent transaction output. The returned // GetUTXOEntry returns the requested unspent transaction output. The returned
// instance must be treated as immutable since it is shared by all callers. // instance must be treated as immutable since it is shared by all callers.
// //

View File

@ -38,9 +38,9 @@ func loadBlockDB() (database.DB, error) {
// returns a slice of found candidates, if any. It also stops searching for // returns a slice of found candidates, if any. It also stops searching for
// candidates at the last checkpoint that is already hard coded since there // candidates at the last checkpoint that is already hard coded since there
// is no point in finding candidates before already existing checkpoints. // is no point in finding candidates before already existing checkpoints.
func findCandidates(dag *blockdag.BlockDAG, selectedTipHash *daghash.Hash) ([]*dagconfig.Checkpoint, error) { func findCandidates(dag *blockdag.BlockDAG, highestTipHash *daghash.Hash) ([]*dagconfig.Checkpoint, error) {
// Start with the selected tip. // Start with the selected tip.
block, err := dag.BlockByHash(selectedTipHash) block, err := dag.BlockByHash(highestTipHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -162,12 +162,11 @@ func main() {
// Get the latest block hash and height from the database and report // Get the latest block hash and height from the database and report
// status. // status.
virtualBlock := dag.VirtualBlock()
fmt.Printf("Block database loaded with block height %d\n", dag.Height()) fmt.Printf("Block database loaded with block height %d\n", dag.Height())
// Find checkpoint candidates. // Find checkpoint candidates.
selectedTipHash := virtualBlock.SelectedTipHash() highestTipHash := dag.HighestTipHash()
candidates, err := findCandidates(dag, &selectedTipHash) candidates, err := findCandidates(dag, &highestTipHash)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err) fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err)
return return

View File

@ -162,7 +162,7 @@ func (m *CPUMiner) submitBlock(block *util.Block) bool {
// a new block, but the check only happens periodically, so it is // a new block, but the check only happens periodically, so it is
// possible a block was found and submitted in between. // possible a block was found and submitted in between.
msgBlock := block.MsgBlock() msgBlock := block.MsgBlock()
if !daghash.AreEqual(msgBlock.Header.PrevBlocks, m.g.VirtualBlock().TipHashes()) { if !daghash.AreEqual(msgBlock.Header.PrevBlocks, m.g.TipHashes()) {
log.Debugf("Block submitted via CPU miner with previous "+ log.Debugf("Block submitted via CPU miner with previous "+
"blocks %s is stale", msgBlock.Header.PrevBlocks) "blocks %s is stale", msgBlock.Header.PrevBlocks)
return false return false
@ -247,8 +247,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32,
hashesCompleted = 0 hashesCompleted = 0
// The current block is stale if the DAG has changed. // The current block is stale if the DAG has changed.
virtualBlock := m.g.VirtualBlock() if !daghash.AreEqual(header.PrevBlocks, m.g.TipHashes()) {
if !daghash.AreEqual(header.PrevBlocks, virtualBlock.TipHashes()) {
return false return false
} }

View File

@ -717,8 +717,8 @@ mempoolLoop:
var msgBlock wire.MsgBlock var msgBlock wire.MsgBlock
msgBlock.Header = wire.BlockHeader{ msgBlock.Header = wire.BlockHeader{
Version: nextBlockVersion, Version: nextBlockVersion,
NumPrevBlocks: byte(len(virtualBlock.TipHashes())), NumPrevBlocks: byte(len(g.dag.TipHashes())),
PrevBlocks: virtualBlock.TipHashes(), PrevBlocks: g.dag.TipHashes(),
MerkleRoot: *merkles[len(merkles)-1], MerkleRoot: *merkles[len(merkles)-1],
Timestamp: ts, Timestamp: ts,
Bits: reqDifficulty, Bits: reqDifficulty,
@ -820,6 +820,11 @@ func (g *BlkTmplGenerator) DAGHeight() int32 {
return g.dag.Height() return g.dag.Height()
} }
// TipHashes returns the hashes of the DAG's tips
func (g *BlkTmplGenerator) TipHashes() []daghash.Hash {
return g.dag.TipHashes()
}
// TxSource returns the associated transaction source. // TxSource returns the associated transaction source.
// //
// This function is safe for concurrent access. // This function is safe for concurrent access.

View File

@ -392,9 +392,8 @@ func (sm *SyncManager) handleDonePeerMsg(peer *peerpkg.Peer) {
if sm.syncPeer == peer { if sm.syncPeer == peer {
sm.syncPeer = nil sm.syncPeer = nil
if sm.headersFirstMode { if sm.headersFirstMode {
virtualBlock := sm.dag.VirtualBlock() highestTipHash := sm.dag.HighestTipHash()
selectedTipHash := virtualBlock.SelectedTipHash() sm.resetHeaderState(&highestTipHash, sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation
sm.resetHeaderState(&selectedTipHash, sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation
} }
sm.startSync() sm.startSync()
} }
@ -616,10 +615,9 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
// Update this peer's latest block height, for future // Update this peer's latest block height, for future
// potential sync node candidacy. // potential sync node candidacy.
virtualBlock := sm.dag.VirtualBlock() highestTipHash := sm.dag.HighestTipHash()
selectedTipHash := virtualBlock.SelectedTipHash()
heightUpdate = sm.dag.Height() //TODO: (Ori) This is probably wrong. Done only for compilation heightUpdate = sm.dag.Height() //TODO: (Ori) This is probably wrong. Done only for compilation
blkHashUpdate = &selectedTipHash blkHashUpdate = &highestTipHash
// Clear the rejected transactions. // Clear the rejected transactions.
sm.rejectedTxns = make(map[daghash.Hash]struct{}) sm.rejectedTxns = make(map[daghash.Hash]struct{})
@ -1389,13 +1387,12 @@ func New(config *Config) (*SyncManager, error) {
feeEstimator: config.FeeEstimator, feeEstimator: config.FeeEstimator,
} }
virtualBlock := sm.dag.VirtualBlock() highestTipHash := sm.dag.HighestTipHash()
selectedTipHash := virtualBlock.SelectedTipHash()
if !config.DisableCheckpoints { if !config.DisableCheckpoints {
// Initialize the next checkpoint based on the current height. // Initialize the next checkpoint based on the current height.
sm.nextCheckpoint = sm.findNextHeaderCheckpoint(sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation sm.nextCheckpoint = sm.findNextHeaderCheckpoint(sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation
if sm.nextCheckpoint != nil { if sm.nextCheckpoint != nil {
sm.resetHeaderState(&selectedTipHash, sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation) sm.resetHeaderState(&highestTipHash, sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation)
} }
} else { } else {
log.Info("Checkpoints are disabled") log.Info("Checkpoints are disabled")

View File

@ -290,9 +290,8 @@ func newServerPeer(s *Server, isPersistent bool) *Peer {
// newestBlock returns the current best block hash and height using the format // newestBlock returns the current best block hash and height using the format
// required by the configuration for the peer package. // required by the configuration for the peer package.
func (sp *Peer) newestBlock() (*daghash.Hash, int32, error) { func (sp *Peer) newestBlock() (*daghash.Hash, int32, error) {
virtualBlock := sp.server.DAG.VirtualBlock() highestTipHash := sp.server.DAG.HighestTipHash()
selectedTipHash := virtualBlock.SelectedTipHash() return &highestTipHash, sp.server.DAG.Height(), nil //TODO: (Ori) This is probably wrong. Done only for compilation
return &selectedTipHash, sp.server.DAG.Height(), nil //TODO: (Ori) This is probably wrong. Done only for compilation
} }
// addKnownAddresses adds the given addresses to the set of known addresses to // addKnownAddresses adds the given addresses to the set of known addresses to
@ -1308,9 +1307,9 @@ func (s *Server) pushBlockMsg(sp *Peer, hash *daghash.Hash, doneChan chan<- stru
// to trigger it to issue another getblocks message for the next // to trigger it to issue another getblocks message for the next
// batch of inventory. // batch of inventory.
if sendInv { if sendInv {
selectedTipHash := sp.server.DAG.VirtualBlock().SelectedTipHash() highestTipHash := sp.server.DAG.HighestTipHash()
invMsg := wire.NewMsgInvSizeHint(1) invMsg := wire.NewMsgInvSizeHint(1)
iv := wire.NewInvVect(wire.InvTypeBlock, &selectedTipHash) iv := wire.NewInvVect(wire.InvTypeBlock, &highestTipHash)
invMsg.AddInvVect(iv) invMsg.AddInvVect(iv)
sp.QueueMessage(invMsg, doneChan) sp.QueueMessage(invMsg, doneChan)
sp.continueHash = nil sp.continueHash = nil

View File

@ -1008,9 +1008,8 @@ func handleGetBestBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (
// All other "get block" commands give either the height, the // All other "get block" commands give either the height, the
// hash, or both but require the block SHA. This gets both for // hash, or both but require the block SHA. This gets both for
// the best block. // the best block.
virtualBlock := s.cfg.DAG.VirtualBlock()
result := &btcjson.GetBestBlockResult{ result := &btcjson.GetBestBlockResult{
Hash: virtualBlock.SelectedTipHash().String(), Hash: s.cfg.DAG.HighestTipHash().String(),
Height: s.cfg.DAG.Height(), //TODO: (Ori) This is probably wrong. Done only for compilation Height: s.cfg.DAG.Height(), //TODO: (Ori) This is probably wrong. Done only for compilation
} }
return result, nil return result, nil
@ -1018,8 +1017,7 @@ func handleGetBestBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (
// handleGetBestBlockHash implements the getbestblockhash command. // handleGetBestBlockHash implements the getbestblockhash command.
func handleGetBestBlockHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) { func handleGetBestBlockHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
virtualBlock := s.cfg.DAG.VirtualBlock() return s.cfg.DAG.HighestTipHash().String(), nil
return virtualBlock.SelectedTipHash().String(), nil
} }
// getDifficultyRatio returns the proof-of-work difficulty as a multiple of the // getDifficultyRatio returns the proof-of-work difficulty as a multiple of the
@ -1173,7 +1171,7 @@ func handleGetBlockDAGInfo(s *Server, cmd interface{}, closeChan <-chan struct{}
DAG: params.Name, DAG: params.Name,
Blocks: dag.Height(), //TODO: (Ori) This is wrong. Done only for compilation Blocks: dag.Height(), //TODO: (Ori) This is wrong. Done only for compilation
Headers: dag.Height(), //TODO: (Ori) This is wrong. Done only for compilation Headers: dag.Height(), //TODO: (Ori) This is wrong. Done only for compilation
TipHashes: daghash.Strings(virtualBlock.TipHashes()), TipHashes: daghash.Strings(dag.TipHashes()),
Difficulty: getDifficultyRatio(virtualBlock.SelectedTip().Header().Bits, params), Difficulty: getDifficultyRatio(virtualBlock.SelectedTip().Header().Bits, params),
MedianTime: virtualBlock.SelectedTip().CalcPastMedianTime().Unix(), MedianTime: virtualBlock.SelectedTip().CalcPastMedianTime().Unix(),
Pruned: false, Pruned: false,
@ -1490,7 +1488,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool)
var msgBlock *wire.MsgBlock var msgBlock *wire.MsgBlock
var targetDifficulty string var targetDifficulty string
virtualBlock := s.cfg.DAG.VirtualBlock() virtualBlock := s.cfg.DAG.VirtualBlock()
tipHashes := virtualBlock.TipHashes() tipHashes := s.cfg.DAG.TipHashes()
template := state.template template := state.template
if template == nil || state.tipHashes == nil || if template == nil || state.tipHashes == nil ||
!daghash.AreEqual(state.tipHashes, tipHashes) || !daghash.AreEqual(state.tipHashes, tipHashes) ||
@ -2050,7 +2048,7 @@ func handleGetBlockTemplateProposal(s *Server, request *btcjson.TemplateRequest)
block := util.NewBlock(&msgBlock) block := util.NewBlock(&msgBlock)
// Ensure the block is building from the expected previous blocks. // Ensure the block is building from the expected previous blocks.
expectedPrevHashes := s.cfg.DAG.VirtualBlock().TipHashes() expectedPrevHashes := s.cfg.DAG.TipHashes()
prevHashes := block.MsgBlock().Header.PrevBlocks prevHashes := block.MsgBlock().Header.PrevBlocks
if !daghash.AreEqual(expectedPrevHashes, prevHashes) { if !daghash.AreEqual(expectedPrevHashes, prevHashes) {
return "bad-prevblk", nil return "bad-prevblk", nil
@ -2283,8 +2281,8 @@ func handleGetMiningInfo(s *Server, cmd interface{}, closeChan <-chan struct{})
} }
virtualBlock := s.cfg.DAG.VirtualBlock() virtualBlock := s.cfg.DAG.VirtualBlock()
selectedTipHash := virtualBlock.SelectedTipHash() highestTipHash := s.cfg.DAG.HighestTipHash()
selectedBlock, err := s.cfg.DAG.BlockByHash(&selectedTipHash) selectedBlock, err := s.cfg.DAG.BlockByHash(&highestTipHash)
if err != nil { if err != nil {
return nil, &btcjson.RPCError{ return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInternal.Code, Code: btcjson.ErrRPCInternal.Code,
@ -2548,8 +2546,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
return nil, internalRPCError(errStr, "") return nil, internalRPCError(errStr, "")
} }
virtualBlock := s.cfg.DAG.VirtualBlock() bestBlockHash = s.cfg.DAG.HighestTipHash().String()
bestBlockHash = virtualBlock.SelectedTipHash().String()
confirmations = 0 confirmations = 0
value = txOut.Value value = txOut.Value
pkScript = txOut.PkScript pkScript = txOut.PkScript
@ -2570,8 +2567,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
return nil, nil return nil, nil
} }
virtualBlock := s.cfg.DAG.VirtualBlock() bestBlockHash = s.cfg.DAG.HighestTipHash().String()
bestBlockHash = virtualBlock.SelectedTipHash().String()
confirmations = 1 + s.cfg.DAG.Height() - entry.BlockHeight() //TODO: (Ori) This is probably wrong. Done only for compilation confirmations = 1 + s.cfg.DAG.Height() - entry.BlockHeight() //TODO: (Ori) This is probably wrong. Done only for compilation
value = entry.Amount() value = entry.Amount()
pkScript = entry.PkScript() pkScript = entry.PkScript()
@ -3322,7 +3318,6 @@ func handleValidateAddress(s *Server, cmd interface{}, closeChan <-chan struct{}
} }
func verifyDAG(s *Server, level, depth int32) error { func verifyDAG(s *Server, level, depth int32) error {
virtualBlock := s.cfg.DAG.VirtualBlock()
finishHeight := s.cfg.DAG.Height() - depth //TODO: (Ori) This is probably wrong. Done only for compilation finishHeight := s.cfg.DAG.Height() - depth //TODO: (Ori) This is probably wrong. Done only for compilation
if finishHeight < 0 { if finishHeight < 0 {
finishHeight = 0 finishHeight = 0
@ -3330,7 +3325,7 @@ func verifyDAG(s *Server, level, depth int32) error {
log.Infof("Verifying chain for %d blocks at level %d", log.Infof("Verifying chain for %d blocks at level %d",
s.cfg.DAG.Height()-finishHeight, level) //TODO: (Ori) This is probably wrong. Done only for compilation s.cfg.DAG.Height()-finishHeight, level) //TODO: (Ori) This is probably wrong. Done only for compilation
currentHash := virtualBlock.SelectedTipHash() currentHash := s.cfg.DAG.HighestTipHash()
for height := s.cfg.DAG.Height(); height > finishHeight; { //TODO: (Ori) This is probably wrong. Done only for compilation for height := s.cfg.DAG.Height(); height > finishHeight; { //TODO: (Ori) This is probably wrong. Done only for compilation
// Level 0 just looks up the block. // Level 0 just looks up the block.
block, err := s.cfg.DAG.BlockByHash(&currentHash) block, err := s.cfg.DAG.BlockByHash(&currentHash)