[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
}
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
func (bs blockSet) add(block *blockNode) {
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.
err = dag.db.Update(func(dbTx database.Tx) error {
// Update best block state.
err := dbPutDAGTipHashes(dbTx, dag.virtual.TipHashes())
err := dbPutDAGTipHashes(dbTx, dag.TipHashes())
if err != nil {
return err
}
@ -930,6 +930,17 @@ func (dag *BlockDAG) Height() int32 {
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
// error if it doesn't exist.
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.
err = dbPutDAGTipHashes(dbTx, dag.virtual.TipHashes())
err = dbPutDAGTipHashes(dbTx, dag.TipHashes())
if err != nil {
return err
}

View File

@ -272,14 +272,13 @@ func TestFullBlocks(t *testing.T) {
item.Name, block.Hash(), blockHeight)
// Ensure hash and height match.
virtualBlock := dag.VirtualBlock()
if virtualBlock.SelectedTipHash() != item.Block.BlockHash() ||
dag.Height() != blockHeight { //TODO: (Ori) the use of dag.Height() and virtualBlock.SelectedTipHash() is wrong, and was done only for compilation
if dag.HighestTipHash() != item.Block.BlockHash() ||
dag.Height() != blockHeight { //TODO: (Ori) the use of dag.Height() and virtualBlock.HighestTipHash() is wrong, and was done only for compilation
t.Fatalf("block %q (hash %s, height %d) should be "+
"the current tip -- got (hash %s, height %d)",
item.Name, block.Hash(), blockHeight, virtualBlock.SelectedTipHash(),
dag.Height()) //TODO: (Ori) the use of dag.Height() and virtualBlock.SelectedTipHash() is wrong, and was done only for compilation
item.Name, block.Hash(), blockHeight, dag.HighestTipHash(),
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 (
"sync"
"github.com/daglabs/btcd/dagconfig/daghash"
"github.com/daglabs/btcd/wire"
)
@ -101,16 +100,6 @@ func (v *VirtualBlock) SelectedTip() *blockNode {
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
// 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
// candidates at the last checkpoint that is already hard coded since there
// 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.
block, err := dag.BlockByHash(selectedTipHash)
block, err := dag.BlockByHash(highestTipHash)
if err != nil {
return nil, err
}
@ -162,12 +162,11 @@ func main() {
// Get the latest block hash and height from the database and report
// status.
virtualBlock := dag.VirtualBlock()
fmt.Printf("Block database loaded with block height %d\n", dag.Height())
// Find checkpoint candidates.
selectedTipHash := virtualBlock.SelectedTipHash()
candidates, err := findCandidates(dag, &selectedTipHash)
highestTipHash := dag.HighestTipHash()
candidates, err := findCandidates(dag, &highestTipHash)
if err != nil {
fmt.Fprintln(os.Stderr, "Unable to identify candidates:", err)
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
// possible a block was found and submitted in between.
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 "+
"blocks %s is stale", msgBlock.Header.PrevBlocks)
return false
@ -247,8 +247,7 @@ func (m *CPUMiner) solveBlock(msgBlock *wire.MsgBlock, blockHeight int32,
hashesCompleted = 0
// The current block is stale if the DAG has changed.
virtualBlock := m.g.VirtualBlock()
if !daghash.AreEqual(header.PrevBlocks, virtualBlock.TipHashes()) {
if !daghash.AreEqual(header.PrevBlocks, m.g.TipHashes()) {
return false
}

View File

@ -717,8 +717,8 @@ mempoolLoop:
var msgBlock wire.MsgBlock
msgBlock.Header = wire.BlockHeader{
Version: nextBlockVersion,
NumPrevBlocks: byte(len(virtualBlock.TipHashes())),
PrevBlocks: virtualBlock.TipHashes(),
NumPrevBlocks: byte(len(g.dag.TipHashes())),
PrevBlocks: g.dag.TipHashes(),
MerkleRoot: *merkles[len(merkles)-1],
Timestamp: ts,
Bits: reqDifficulty,
@ -820,6 +820,11 @@ func (g *BlkTmplGenerator) DAGHeight() int32 {
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.
//
// This function is safe for concurrent access.

View File

@ -392,9 +392,8 @@ func (sm *SyncManager) handleDonePeerMsg(peer *peerpkg.Peer) {
if sm.syncPeer == peer {
sm.syncPeer = nil
if sm.headersFirstMode {
virtualBlock := sm.dag.VirtualBlock()
selectedTipHash := virtualBlock.SelectedTipHash()
sm.resetHeaderState(&selectedTipHash, sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation
highestTipHash := sm.dag.HighestTipHash()
sm.resetHeaderState(&highestTipHash, sm.dag.Height()) //TODO: (Ori) This is probably wrong. Done only for compilation
}
sm.startSync()
}
@ -616,10 +615,9 @@ func (sm *SyncManager) handleBlockMsg(bmsg *blockMsg) {
// Update this peer's latest block height, for future
// potential sync node candidacy.
virtualBlock := sm.dag.VirtualBlock()
selectedTipHash := virtualBlock.SelectedTipHash()
highestTipHash := sm.dag.HighestTipHash()
heightUpdate = sm.dag.Height() //TODO: (Ori) This is probably wrong. Done only for compilation
blkHashUpdate = &selectedTipHash
blkHashUpdate = &highestTipHash
// Clear the rejected transactions.
sm.rejectedTxns = make(map[daghash.Hash]struct{})
@ -1389,13 +1387,12 @@ func New(config *Config) (*SyncManager, error) {
feeEstimator: config.FeeEstimator,
}
virtualBlock := sm.dag.VirtualBlock()
selectedTipHash := virtualBlock.SelectedTipHash()
highestTipHash := sm.dag.HighestTipHash()
if !config.DisableCheckpoints {
// 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
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 {
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
// required by the configuration for the peer package.
func (sp *Peer) newestBlock() (*daghash.Hash, int32, error) {
virtualBlock := sp.server.DAG.VirtualBlock()
selectedTipHash := virtualBlock.SelectedTipHash()
return &selectedTipHash, sp.server.DAG.Height(), nil //TODO: (Ori) This is probably wrong. Done only for compilation
highestTipHash := sp.server.DAG.HighestTipHash()
return &highestTipHash, 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
@ -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
// batch of inventory.
if sendInv {
selectedTipHash := sp.server.DAG.VirtualBlock().SelectedTipHash()
highestTipHash := sp.server.DAG.HighestTipHash()
invMsg := wire.NewMsgInvSizeHint(1)
iv := wire.NewInvVect(wire.InvTypeBlock, &selectedTipHash)
iv := wire.NewInvVect(wire.InvTypeBlock, &highestTipHash)
invMsg.AddInvVect(iv)
sp.QueueMessage(invMsg, doneChan)
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
// hash, or both but require the block SHA. This gets both for
// the best block.
virtualBlock := s.cfg.DAG.VirtualBlock()
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
}
return result, nil
@ -1018,8 +1017,7 @@ func handleGetBestBlock(s *Server, cmd interface{}, closeChan <-chan struct{}) (
// handleGetBestBlockHash implements the getbestblockhash command.
func handleGetBestBlockHash(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
virtualBlock := s.cfg.DAG.VirtualBlock()
return virtualBlock.SelectedTipHash().String(), nil
return s.cfg.DAG.HighestTipHash().String(), nil
}
// 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,
Blocks: 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),
MedianTime: virtualBlock.SelectedTip().CalcPastMedianTime().Unix(),
Pruned: false,
@ -1490,7 +1488,7 @@ func (state *gbtWorkState) updateBlockTemplate(s *Server, useCoinbaseValue bool)
var msgBlock *wire.MsgBlock
var targetDifficulty string
virtualBlock := s.cfg.DAG.VirtualBlock()
tipHashes := virtualBlock.TipHashes()
tipHashes := s.cfg.DAG.TipHashes()
template := state.template
if template == nil || state.tipHashes == nil ||
!daghash.AreEqual(state.tipHashes, tipHashes) ||
@ -2050,7 +2048,7 @@ func handleGetBlockTemplateProposal(s *Server, request *btcjson.TemplateRequest)
block := util.NewBlock(&msgBlock)
// 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
if !daghash.AreEqual(expectedPrevHashes, prevHashes) {
return "bad-prevblk", nil
@ -2283,8 +2281,8 @@ func handleGetMiningInfo(s *Server, cmd interface{}, closeChan <-chan struct{})
}
virtualBlock := s.cfg.DAG.VirtualBlock()
selectedTipHash := virtualBlock.SelectedTipHash()
selectedBlock, err := s.cfg.DAG.BlockByHash(&selectedTipHash)
highestTipHash := s.cfg.DAG.HighestTipHash()
selectedBlock, err := s.cfg.DAG.BlockByHash(&highestTipHash)
if err != nil {
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCInternal.Code,
@ -2548,8 +2546,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
return nil, internalRPCError(errStr, "")
}
virtualBlock := s.cfg.DAG.VirtualBlock()
bestBlockHash = virtualBlock.SelectedTipHash().String()
bestBlockHash = s.cfg.DAG.HighestTipHash().String()
confirmations = 0
value = txOut.Value
pkScript = txOut.PkScript
@ -2570,8 +2567,7 @@ func handleGetTxOut(s *Server, cmd interface{}, closeChan <-chan struct{}) (inte
return nil, nil
}
virtualBlock := s.cfg.DAG.VirtualBlock()
bestBlockHash = virtualBlock.SelectedTipHash().String()
bestBlockHash = s.cfg.DAG.HighestTipHash().String()
confirmations = 1 + s.cfg.DAG.Height() - entry.BlockHeight() //TODO: (Ori) This is probably wrong. Done only for compilation
value = entry.Amount()
pkScript = entry.PkScript()
@ -3322,7 +3318,6 @@ func handleValidateAddress(s *Server, cmd interface{}, closeChan <-chan struct{}
}
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
if 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",
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
// Level 0 just looks up the block.
block, err := s.cfg.DAG.BlockByHash(&currentHash)