From 3c88184b386bf28b38588f79c09144439467ed1a Mon Sep 17 00:00:00 2001 From: Ori Newman Date: Sun, 7 Oct 2018 13:07:30 +0300 Subject: [PATCH] [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 --- blockdag/blockset.go | 10 ++++++++++ blockdag/dag.go | 13 ++++++++++++- blockdag/dagio.go | 2 +- blockdag/fullblocks_test.go | 9 ++++----- blockdag/virtualblock.go | 11 ----------- cmd/findcheckpoint/findcheckpoint.go | 9 ++++----- mining/cpuminer/cpuminer.go | 5 ++--- mining/mining.go | 9 +++++++-- netsync/manager.go | 15 ++++++--------- server/p2p/p2p.go | 9 ++++----- server/rpc/rpcserver.go | 25 ++++++++++--------------- 11 files changed, 60 insertions(+), 57 deletions(-) diff --git a/blockdag/blockset.go b/blockdag/blockset.go index 9d48f2a6b..b6602e023 100644 --- a/blockdag/blockset.go +++ b/blockdag/blockset.go @@ -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 diff --git a/blockdag/dag.go b/blockdag/dag.go index e74e53ef8..3bd2d7d94 100644 --- a/blockdag/dag.go +++ b/blockdag/dag.go @@ -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) { diff --git a/blockdag/dagio.go b/blockdag/dagio.go index 474237fe3..552471bce 100644 --- a/blockdag/dagio.go +++ b/blockdag/dagio.go @@ -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 } diff --git a/blockdag/fullblocks_test.go b/blockdag/fullblocks_test.go index 6e3a50df2..19bfe310a 100644 --- a/blockdag/fullblocks_test.go +++ b/blockdag/fullblocks_test.go @@ -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 } } diff --git a/blockdag/virtualblock.go b/blockdag/virtualblock.go index 03b77873a..3db1f99f8 100644 --- a/blockdag/virtualblock.go +++ b/blockdag/virtualblock.go @@ -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. // diff --git a/cmd/findcheckpoint/findcheckpoint.go b/cmd/findcheckpoint/findcheckpoint.go index 7eff25b24..6bff31184 100644 --- a/cmd/findcheckpoint/findcheckpoint.go +++ b/cmd/findcheckpoint/findcheckpoint.go @@ -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 diff --git a/mining/cpuminer/cpuminer.go b/mining/cpuminer/cpuminer.go index fe1b43983..30677c78c 100644 --- a/mining/cpuminer/cpuminer.go +++ b/mining/cpuminer/cpuminer.go @@ -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 } diff --git a/mining/mining.go b/mining/mining.go index be32f19e7..47246cdf5 100644 --- a/mining/mining.go +++ b/mining/mining.go @@ -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. diff --git a/netsync/manager.go b/netsync/manager.go index c4a420865..99b8113d9 100644 --- a/netsync/manager.go +++ b/netsync/manager.go @@ -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") diff --git a/server/p2p/p2p.go b/server/p2p/p2p.go index ea3be7528..93bb2e2ca 100644 --- a/server/p2p/p2p.go +++ b/server/p2p/p2p.go @@ -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 diff --git a/server/rpc/rpcserver.go b/server/rpc/rpcserver.go index 76979e61d..1283d3d1a 100644 --- a/server/rpc/rpcserver.go +++ b/server/rpc/rpcserver.go @@ -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(¤tHash)