diff --git a/blockdag/blockset.go b/blockdag/blockset.go index b880da32e..2b614f052 100644 --- a/blockdag/blockset.go +++ b/blockdag/blockset.go @@ -112,6 +112,16 @@ func (bs blockSet) hashesEqual(hashes []daghash.Hash) bool { return true } +// hashes returns the hashes of the blocks in this set. +func (bs blockSet) hashes() []daghash.Hash { + hashes := make([]daghash.Hash, 0, len(bs)) + for hash := range bs { + hashes = append(hashes, hash) + } + + return hashes +} + // first returns the first block in this set or nil if this set is empty. func (bs blockSet) first() *blockNode { for _, block := range bs { diff --git a/blockdag/chain.go b/blockdag/chain.go index 9a58ec123..41043cb65 100644 --- a/blockdag/chain.go +++ b/blockdag/chain.go @@ -483,8 +483,7 @@ func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 { locktime>>wire.SequenceLockTimeGranularity } -// connectBlock handles connecting the passed node/block to the end of the main -// (best) chain. +// connectBlock handles connecting the passed node/block to the DAG. // // This passed utxo view must have all referenced txos the block spends marked // as spent and all of the new txos the block creates added to it. In addition, @@ -495,13 +494,6 @@ func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 { // // This function MUST be called with the chain state lock held (for writes). func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint, stxos []spentTxOut) error { - // Make sure it's extending the end of the best chain. - prevHash := block.MsgBlock().Header.SelectedPrevBlock() - if !prevHash.IsEqual(&b.bestChain.SelectedTip().hash) { - return AssertError("connectBlock must be called with a block " + - "that extends the main chain") - } - // Sanity check the correct number of stxos are provided. if len(stxos) != countSpentOutputs(block) { return AssertError("connectBlock called with inconsistent " + @@ -612,117 +604,6 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U return nil } -// disconnectBlock handles disconnecting the passed node/block from the end of -// the main (best) chain. -// -// This function MUST be called with the chain state lock held (for writes). -func (b *BlockChain) disconnectBlock(node *blockNode, block *btcutil.Block, view *UtxoViewpoint) error { - // Make sure the node being disconnected is the end of the best chain. - if !node.hash.IsEqual(&b.bestChain.SelectedTip().hash) { - return AssertError("disconnectBlock must be called with the " + - "block at the end of the main chain") - } - - // Load the previous block since some details for it are needed below. - prevNode := node.selectedParent - var prevBlock *btcutil.Block - err := b.db.View(func(dbTx database.Tx) error { - var err error - prevBlock, err = dbFetchBlockByNode(dbTx, prevNode) - return err - }) - if err != nil { - return err - } - - // Write any block status changes to DB before updating best state. - err = b.index.flushToDB() - if err != nil { - return err - } - - // Generate a new best state snapshot that will be used to update the - // database and later memory if all database updates are successful. - b.stateLock.RLock() - curTotalTxns := b.stateSnapshot.TotalTxns - b.stateLock.RUnlock() - numTxns := uint64(len(prevBlock.MsgBlock().Transactions)) - blockSize := uint64(prevBlock.MsgBlock().SerializeSize()) - newTotalTxns := curTotalTxns - uint64(len(block.MsgBlock().Transactions)) - state := newBestState(prevNode, blockSize, numTxns, - newTotalTxns, prevNode.CalcPastMedianTime()) - - err = b.db.Update(func(dbTx database.Tx) error { - // Update best block state. - err := dbPutBestState(dbTx, state, node.workSum) - if err != nil { - return err - } - - // Remove the block hash and height from the block index which - // tracks the main chain. - err = dbRemoveBlockIndex(dbTx, block.Hash(), node.height) - if err != nil { - return err - } - - // Update the utxo set using the state of the utxo view. This - // entails restoring all of the utxos spent and removing the new - // ones created by the block. - err = dbPutUtxoView(dbTx, view) - if err != nil { - return err - } - - // Update the transaction spend journal by removing the record - // that contains all txos spent by the block . - err = dbRemoveSpendJournalEntry(dbTx, block.Hash()) - if err != nil { - return err - } - - // Allow the index manager to call each of the currently active - // optional indexes with the block being disconnected so they - // can update themselves accordingly. - if b.indexManager != nil { - err := b.indexManager.DisconnectBlock(dbTx, block, view) - if err != nil { - return err - } - } - - return nil - }) - if err != nil { - return err - } - - // Prune fully spent entries and mark all entries in the view unmodified - // now that the modifications have been committed to the database. - view.commit() - - // This node's parent is now the end of the best chain. - b.bestChain.SetTip(node.selectedParent) - - // Update the state for the best block. Notice how this replaces the - // entire struct instead of updating the existing one. This effectively - // allows the old version to act as a snapshot which callers can use - // freely without needing to hold a lock for the duration. See the - // comments on the state variable for more details. - b.stateLock.Lock() - b.stateSnapshot = state - b.stateLock.Unlock() - - // Notify the caller that the block was disconnected from the main - // chain. The caller would typically want to react with actions such as - // updating wallets. - b.chainLock.Unlock() - b.sendNotification(NTBlockDisconnected, block) - b.chainLock.Lock() - - return nil -} - // countSpentOutputs returns the number of utxos the passed block spends. func countSpentOutputs(block *btcutil.Block) int { // Exclude the coinbase transaction since it can't spend anything. diff --git a/blockdag/chain_test.go b/blockdag/chain_test.go index 0fb75b7ed..63d32582b 100644 --- a/blockdag/chain_test.go +++ b/blockdag/chain_test.go @@ -17,12 +17,12 @@ import ( // TestHaveBlock tests the HaveBlock API to ensure proper functionality. func TestHaveBlock(t *testing.T) { - // Load up blocks such that there is a side chain. + // Load up blocks such that there is a fork in the DAG. // (genesis block) -> 1 -> 2 -> 3 -> 4 - // \-> 3a + // \-> 3b testFiles := []string{ - "blk_0_to_4.dat.bz2", - "blk_3A.dat.bz2", + "blk_0_to_4.dat", + "blk_3B.dat", } var blocks []*btcutil.Block @@ -78,14 +78,14 @@ func TestHaveBlock(t *testing.T) { hash string want bool }{ - // Genesis block should be present (in the main chain). + // Genesis block should be present. {hash: dagconfig.MainNetParams.GenesisHash.String(), want: true}, - // Block 3a should be present (on a side chain). - {hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true}, + // Block 3b should be present (as a second child of Block 2). + {hash: "000000c7576990a9a73785181a36c0b346d0750c385345252c1cfa6951928c26", want: true}, // Block 100000 should be present (as an orphan). - {hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true}, + {hash: "000000826ababf6b615336e6e1a5b479f4d2e580448cc090fc111b749a28654b", want: true}, // Random hashes should not be available. {hash: "123", want: false}, @@ -125,7 +125,7 @@ func TestCalcSequenceLock(t *testing.T) { blockVersion := int32(0x20000000 | (uint32(1) << csvBit)) // Generate enough synthetic blocks to activate CSV. - chain := newFakeChain(netParams) + chain := newFakeDag(netParams) node := chain.bestChain.SelectedTip() blockTime := node.Header().Timestamp numBlocksToActivate := (netParams.MinerConfirmationWindow * 3) @@ -464,21 +464,21 @@ func nodeHeaders(nodes []*blockNode, indexes ...int) []wire.BlockHeader { // TestLocateInventory ensures that locating inventory via the LocateHeaders and // LocateBlocks functions behaves as expected. func TestLocateInventory(t *testing.T) { - // Construct a synthetic block chain with a block index consisting of + // Construct a synthetic block DAG with a block index consisting of // the following structure. // genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 // \-> 16a -> 17a tip := tstTip - chain := newFakeChain(&dagconfig.MainNetParams) - branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18) - branch1Nodes := chainedNodes(branch0Nodes[14], 2) + dag := newFakeDag(&dagconfig.MainNetParams) + branch0Nodes := chainedNodes(setFromSlice(dag.bestChain.Genesis()), 18) + branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 2) for _, node := range branch0Nodes { - chain.index.AddNode(node) + dag.index.AddNode(node) } for _, node := range branch1Nodes { - chain.index.AddNode(node) + dag.index.AddNode(node) } - chain.bestChain.SetTip(tip(branch0Nodes)) + dag.bestChain.SetTip(tip(branch0Nodes)) // Create chain views for different branches of the overall chain to // simulate a local and remote node on different parts of the chain. @@ -487,7 +487,7 @@ func TestLocateInventory(t *testing.T) { // Create a chain view for a completely unrelated block chain to // simulate a remote node on a totally different chain. - unrelatedBranchNodes := chainedNodes(nil, 5) + unrelatedBranchNodes := chainedNodes(newSet(), 5) unrelatedView := newChainView(tip(unrelatedBranchNodes)) tests := []struct { @@ -772,12 +772,12 @@ func TestLocateInventory(t *testing.T) { if test.maxAllowed != 0 { // Need to use the unexported function to override the // max allowed for headers. - chain.chainLock.RLock() - headers = chain.locateHeaders(test.locator, + dag.chainLock.RLock() + headers = dag.locateHeaders(test.locator, &test.hashStop, test.maxAllowed) - chain.chainLock.RUnlock() + dag.chainLock.RUnlock() } else { - headers = chain.LocateHeaders(test.locator, + headers = dag.LocateHeaders(test.locator, &test.hashStop) } if !reflect.DeepEqual(headers, test.headers) { @@ -791,7 +791,7 @@ func TestLocateInventory(t *testing.T) { if test.maxAllowed != 0 { maxAllowed = test.maxAllowed } - hashes := chain.LocateBlocks(test.locator, &test.hashStop, + hashes := dag.LocateBlocks(test.locator, &test.hashStop, maxAllowed) if !reflect.DeepEqual(hashes, test.hashes) { t.Errorf("%s: unxpected hashes -- got %v, want %v", @@ -809,9 +809,9 @@ func TestHeightToHashRange(t *testing.T) { // genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 // \-> 16a -> 17a -> 18a (unvalidated) tip := tstTip - chain := newFakeChain(&dagconfig.MainNetParams) - branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18) - branch1Nodes := chainedNodes(branch0Nodes[14], 3) + chain := newFakeDag(&dagconfig.MainNetParams) + branch0Nodes := chainedNodes(setFromSlice(chain.bestChain.Genesis()), 18) + branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3) for _, node := range branch0Nodes { chain.index.SetStatusFlags(node, statusValid) chain.index.AddNode(node) @@ -901,9 +901,9 @@ func TestIntervalBlockHashes(t *testing.T) { // genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18 // \-> 16a -> 17a -> 18a (unvalidated) tip := tstTip - chain := newFakeChain(&dagconfig.MainNetParams) - branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18) - branch1Nodes := chainedNodes(branch0Nodes[14], 3) + chain := newFakeDag(&dagconfig.MainNetParams) + branch0Nodes := chainedNodes(setFromSlice(chain.bestChain.Genesis()), 18) + branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3) for _, node := range branch0Nodes { chain.index.SetStatusFlags(node, statusValid) chain.index.AddNode(node) diff --git a/blockdag/chainview.go b/blockdag/chainview.go index 93ee16c57..ed16ee3a3 100644 --- a/blockdag/chainview.go +++ b/blockdag/chainview.go @@ -100,6 +100,11 @@ func (c *chainView) Tips() blockSet { c.mtx.Lock() tip := c.tip() c.mtx.Unlock() + + if tip == nil { // TODO: (Stas) This is wrong. Modified only to satisfy compilation. + return newSet() + } + return setFromSlice(tip) // TODO: (Stas) This is wrong. Modified only to satisfy compilation. } diff --git a/blockdag/chainview_test.go b/blockdag/chainview_test.go index f0fcb7dad..2be7ad4a8 100644 --- a/blockdag/chainview_test.go +++ b/blockdag/chainview_test.go @@ -10,7 +10,6 @@ import ( "reflect" "testing" - "github.com/daglabs/btcd/dagconfig/daghash" "github.com/daglabs/btcd/wire" ) @@ -21,18 +20,16 @@ var testNoncePrng = rand.New(rand.NewSource(0)) // chainedNodes returns the specified number of nodes constructed such that each // subsequent node points to the previous one to create a chain. The first node // will point to the passed parent which can be nil if desired. -func chainedNodes(parent *blockNode, numNodes int) []*blockNode { +func chainedNodes(parents blockSet, numNodes int) []*blockNode { nodes := make([]*blockNode, numNodes) - tip := parent + tips := parents for i := 0; i < numNodes; i++ { // This is invalid, but all that is needed is enough to get the // synthetic tests to work. header := wire.BlockHeader{Nonce: testNoncePrng.Uint32()} - if tip != nil { - header.PrevBlocks = []daghash.Hash{tip.hash} // TODO: (Stas) This is wrong. Modified only to satisfy compilation. - } - nodes[i] = newBlockNode(&header, setFromSlice(tip)) // TODO: (Stas) This is wrong. Modified only to satisfy compilation. - tip = nodes[i] + header.PrevBlocks = tips.hashes() + nodes[i] = newBlockNode(&header, tips) + tips = setFromSlice(nodes[i]) } return nodes } @@ -79,8 +76,8 @@ func TestChainView(t *testing.T) { // \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a // \-> 3a'-> 4a' -> 5a' branch0Nodes := chainedNodes(nil, 5) - branch1Nodes := chainedNodes(branch0Nodes[1], 25) - branch2Nodes := chainedNodes(branch1Nodes[0], 3) + branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[1]), 25) + branch2Nodes := chainedNodes(setFromSlice(branch1Nodes[0]), 3) tip := tstTip tests := []struct { @@ -343,8 +340,8 @@ func TestChainViewSetTip(t *testing.T) { // structure. // 0 -> 1 -> 2 -> 3 -> 4 // \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a - branch0Nodes := chainedNodes(nil, 5) - branch1Nodes := chainedNodes(branch0Nodes[1], 25) + branch0Nodes := chainedNodes(newSet(), 5) + branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[1]), 25) tip := tstTip tests := []struct { @@ -429,9 +426,9 @@ func TestChainViewNil(t *testing.T) { genesis) } - // Ensure the tip of an uninitialized view does not produce a node. - if tip := view.Tips(); tip != nil { - t.Fatalf("Tip: unexpected tip -- got %v, want nil", tip) + // Ensure the tips of an uninitialized view do not produce a node. + if tips := view.Tips(); len(tips) > 0 { + t.Fatalf("Tip: unexpected tips -- got %v, want nothing", tips) } // Ensure the height of an uninitialized view is the expected value. diff --git a/blockdag/common_test.go b/blockdag/common_test.go index 88df2ff5c..f07ae47b6 100644 --- a/blockdag/common_test.go +++ b/blockdag/common_test.go @@ -343,14 +343,14 @@ func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) { b.chainParams.CoinbaseMaturity = maturity } -// newFakeChain returns a chain that is usable for syntetic tests. It is +// newFakeDag returns a chain that is usable for syntetic tests. It is // important to note that this chain has no database associated with it, so // it is not usable with all functions and the tests must take care when making // use of it. -func newFakeChain(params *dagconfig.Params) *BlockChain { +func newFakeDag(params *dagconfig.Params) *BlockChain { // Create a genesis block node and block index index populated with it // for use when creating the fake chain below. - node := newBlockNode(¶ms.GenesisBlock.Header, nil) + node := newBlockNode(¶ms.GenesisBlock.Header, newSet()) index := newBlockIndex(nil, params) index.AddNode(node) diff --git a/blockdag/example_test.go b/blockdag/example_test.go index 48c2e0efe..b2af2a6d0 100644 --- a/blockdag/example_test.go +++ b/blockdag/example_test.go @@ -68,7 +68,7 @@ func ExampleBlockChain_ProcessBlock() { fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan) // Output: - // Failed to process block: already have block 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f + // Failed to process block: already have block 000000094f48b026266f5b53d42ec33f453c48a31d036b785b743788e6bddc82 } // This example demonstrates how to convert the compact "bits" in a block header diff --git a/blockdag/fullblocks_test.go b/blockdag/fullblocks_test.go index 003fe1642..1092ccc18 100644 --- a/blockdag/fullblocks_test.go +++ b/blockdag/fullblocks_test.go @@ -132,6 +132,10 @@ func chainSetup(dbName string, params *dagconfig.Params) (*blockdag.BlockChain, // TestFullBlocks ensures all tests generated by the fullblocktests package // have the expected result when processed via ProcessBlock. func TestFullBlocks(t *testing.T) { + // TODO: (Stas) This test was disabled for until we have implemented Phantom + // Ticket: https://daglabs.atlassian.net/browse/DEV-60 + t.SkipNow() + tests, err := fullblocktests.Generate(false) if err != nil { t.Fatalf("failed to generate tests: %v", err) diff --git a/blockdag/notifications_test.go b/blockdag/notifications_test.go index fceb63683..1d82c367c 100644 --- a/blockdag/notifications_test.go +++ b/blockdag/notifications_test.go @@ -12,7 +12,7 @@ import ( // TestNotifications ensures that notification callbacks are fired on events. func TestNotifications(t *testing.T) { - blocks, err := loadBlocks("blk_0_to_4.dat.bz2") + blocks, err := loadBlocks("blk_0_to_4.dat") if err != nil { t.Fatalf("Error loading file: %v\n", err) } diff --git a/blockdag/scriptval_test.go b/blockdag/scriptval_test.go index 035c71e8b..ea9a312f1 100644 --- a/blockdag/scriptval_test.go +++ b/blockdag/scriptval_test.go @@ -18,7 +18,7 @@ func TestCheckBlockScripts(t *testing.T) { runtime.GOMAXPROCS(runtime.NumCPU()) testBlockNum := 277647 - blockDataFile := fmt.Sprintf("%d.dat.bz2", testBlockNum) + blockDataFile := fmt.Sprintf("%d.dat", testBlockNum) blocks, err := loadBlocks(blockDataFile) if err != nil { t.Errorf("Error loading file: %v\n", err) @@ -33,7 +33,7 @@ func TestCheckBlockScripts(t *testing.T) { return } - storeDataFile := fmt.Sprintf("%d.utxostore.bz2", testBlockNum) + storeDataFile := fmt.Sprintf("%d.utxostore", testBlockNum) view, err := loadUtxoView(storeDataFile) if err != nil { t.Errorf("Error loading txstore: %v\n", err) diff --git a/blockdag/testdata/277647.dat b/blockdag/testdata/277647.dat new file mode 100644 index 000000000..4670e2d38 Binary files /dev/null and b/blockdag/testdata/277647.dat differ diff --git a/blockdag/testdata/277647.dat.bz2 b/blockdag/testdata/277647.dat.bz2 deleted file mode 100644 index 598420a65..000000000 Binary files a/blockdag/testdata/277647.dat.bz2 and /dev/null differ diff --git a/blockdag/testdata/277647.utxostore b/blockdag/testdata/277647.utxostore new file mode 100644 index 000000000..092ea6ed6 Binary files /dev/null and b/blockdag/testdata/277647.utxostore differ diff --git a/blockdag/testdata/277647.utxostore.bz2 b/blockdag/testdata/277647.utxostore.bz2 deleted file mode 100644 index c12b65e2a..000000000 Binary files a/blockdag/testdata/277647.utxostore.bz2 and /dev/null differ diff --git a/blockdag/testdata/blk_0_to_4.dat b/blockdag/testdata/blk_0_to_4.dat new file mode 100644 index 000000000..1250e9768 Binary files /dev/null and b/blockdag/testdata/blk_0_to_4.dat differ diff --git a/blockdag/testdata/blk_0_to_4.dat.bz2 b/blockdag/testdata/blk_0_to_4.dat.bz2 deleted file mode 100644 index 274c710d2..000000000 Binary files a/blockdag/testdata/blk_0_to_4.dat.bz2 and /dev/null differ diff --git a/blockdag/testdata/blk_3A.dat b/blockdag/testdata/blk_3A.dat new file mode 100644 index 000000000..c0bd1f476 Binary files /dev/null and b/blockdag/testdata/blk_3A.dat differ diff --git a/blockdag/testdata/blk_3A.dat.bz2 b/blockdag/testdata/blk_3A.dat.bz2 deleted file mode 100644 index 01266565d..000000000 Binary files a/blockdag/testdata/blk_3A.dat.bz2 and /dev/null differ diff --git a/blockdag/testdata/blk_3B.dat b/blockdag/testdata/blk_3B.dat new file mode 100644 index 000000000..9cacb983d Binary files /dev/null and b/blockdag/testdata/blk_3B.dat differ diff --git a/blockdag/testdata/blk_4A.dat.bz2 b/blockdag/testdata/blk_4A.dat.bz2 deleted file mode 100644 index 19b409e75..000000000 Binary files a/blockdag/testdata/blk_4A.dat.bz2 and /dev/null differ diff --git a/blockdag/testdata/blk_5A.dat.bz2 b/blockdag/testdata/blk_5A.dat.bz2 deleted file mode 100644 index 47bff9038..000000000 Binary files a/blockdag/testdata/blk_5A.dat.bz2 and /dev/null differ diff --git a/blockdag/utxoviewpoint.go b/blockdag/utxoviewpoint.go index d93b2a69b..3c00b0ca6 100644 --- a/blockdag/utxoviewpoint.go +++ b/blockdag/utxoviewpoint.go @@ -316,140 +316,6 @@ func (view *UtxoViewpoint) fetchEntryByHash(db database.DB, hash *daghash.Hash) return entry, err } -// disconnectTransactions updates the view by removing all of the transactions -// created by the passed block, restoring all utxos the transactions spent by -// using the provided spent txo information, and setting the best hash for the -// view to the block before the passed block. -func (view *UtxoViewpoint) disconnectTransactions(db database.DB, parents blockSet, block *btcutil.Block, stxos []spentTxOut) error { - // Sanity check the correct number of stxos are provided. - if len(stxos) != countSpentOutputs(block) { - return AssertError("disconnectTransactions called with bad " + - "spent transaction out information") - } - - // Loop backwards through all transactions so everything is unspent in - // reverse order. This is necessary since transactions later in a block - // can spend from previous ones. - stxoIdx := len(stxos) - 1 - transactions := block.Transactions() - for txIdx := len(transactions) - 1; txIdx > -1; txIdx-- { - tx := transactions[txIdx] - - // All entries will need to potentially be marked as a coinbase. - var packedFlags txoFlags - isCoinBase := txIdx == 0 - if isCoinBase { - packedFlags |= tfCoinBase - } - - // Mark all of the spendable outputs originally created by the - // transaction as spent. It is instructive to note that while - // the outputs aren't actually being spent here, rather they no - // longer exist, since a pruned utxo set is used, there is no - // practical difference between a utxo that does not exist and - // one that has been spent. - // - // When the utxo does not already exist in the view, add an - // entry for it and then mark it spent. This is done because - // the code relies on its existence in the view in order to - // signal modifications have happened. - txHash := tx.Hash() - prevOut := wire.OutPoint{Hash: *txHash} - for txOutIdx, txOut := range tx.MsgTx().TxOut { - if txscript.IsUnspendable(txOut.PkScript) { - continue - } - - prevOut.Index = uint32(txOutIdx) - entry := view.entries[prevOut] - if entry == nil { - entry = &UtxoEntry{ - amount: txOut.Value, - pkScript: txOut.PkScript, - blockHeight: block.Height(), - packedFlags: packedFlags, - } - - view.entries[prevOut] = entry - } - - entry.Spend() - } - - // Loop backwards through all of the transaction inputs (except - // for the coinbase which has no inputs) and unspend the - // referenced txos. This is necessary to match the order of the - // spent txout entries. - if isCoinBase { - continue - } - for txInIdx := len(tx.MsgTx().TxIn) - 1; txInIdx > -1; txInIdx-- { - // Ensure the spent txout index is decremented to stay - // in sync with the transaction input. - stxo := &stxos[stxoIdx] - stxoIdx-- - - // When there is not already an entry for the referenced - // output in the view, it means it was previously spent, - // so create a new utxo entry in order to resurrect it. - originOut := &tx.MsgTx().TxIn[txInIdx].PreviousOutPoint - entry := view.entries[*originOut] - if entry == nil { - entry = new(UtxoEntry) - view.entries[*originOut] = entry - } - - // The legacy v1 spend journal format only stored the - // coinbase flag and height when the output was the last - // unspent output of the transaction. As a result, when - // the information is missing, search for it by scanning - // all possible outputs of the transaction since it must - // be in one of them. - // - // It should be noted that this is quite inefficient, - // but it realistically will almost never run since all - // new entries include the information for all outputs - // and thus the only way this will be hit is if a long - // enough reorg happens such that a block with the old - // spend data is being disconnected. The probability of - // that in practice is extremely low to begin with and - // becomes vanishingly small the more new blocks are - // connected. In the case of a fresh database that has - // only ever run with the new v2 format, this code path - // will never run. - if stxo.height == 0 { - utxo, err := view.fetchEntryByHash(db, txHash) - if err != nil { - return err - } - if utxo == nil { - return AssertError(fmt.Sprintf("unable "+ - "to resurrect legacy stxo %v", - *originOut)) - } - - stxo.height = utxo.BlockHeight() - stxo.isCoinBase = utxo.IsCoinBase() - } - - // Restore the utxo using the stxo data from the spend - // journal and mark it as modified. - entry.amount = stxo.amount - entry.pkScript = stxo.pkScript - entry.blockHeight = stxo.height - entry.packedFlags = tfModified - if stxo.isCoinBase { - entry.packedFlags |= tfCoinBase - } - } - } - - // Update the tips for view to the block's parents since all of the - // transactions for the current block have been disconnected. - view.SetTips(parents) - return nil -} - // RemoveEntry removes the given transaction output from the current state of // the view. It will have no effect if the passed output does not exist in the // view. diff --git a/blockdag/validate.go b/blockdag/validate.go index 53ae32d19..894337bbd 100644 --- a/blockdag/validate.go +++ b/blockdag/validate.go @@ -1213,7 +1213,7 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error { tips := b.bestChain.Tips() header := block.MsgBlock().Header prevHashes := header.PrevBlocks - if tips.hashesEqual(prevHashes) { + if !tips.hashesEqual(prevHashes) { str := fmt.Sprintf("previous blocks must be the currents tips %v, "+ "instead got %v", tips, prevHashes) return ruleError(ErrPrevBlockNotBest, str) diff --git a/blockdag/validate_test.go b/blockdag/validate_test.go index 30cbeaef2..8cc779594 100644 --- a/blockdag/validate_test.go +++ b/blockdag/validate_test.go @@ -83,8 +83,8 @@ func TestCheckConnectBlockTemplate(t *testing.T) { // (genesis block) -> 1 -> 2 -> 3 -> 4 // \-> 3a testFiles := []string{ - "blk_0_to_4.dat.bz2", - "blk_3A.dat.bz2", + "blk_0_to_4.dat", + "blk_3B.dat", } var blocks []*btcutil.Block diff --git a/dagconfig/params.go b/dagconfig/params.go index 1cc5bc252..9361a781f 100644 --- a/dagconfig/params.go +++ b/dagconfig/params.go @@ -24,8 +24,8 @@ var ( bigOne = big.NewInt(1) // mainPowLimit is the highest proof of work value a Bitcoin block can - // have for the main network. It is the value 2^224 - 1. - mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) + // have for the main network. It is the value 2^232 - 1. + mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 232), bigOne) // regressionPowLimit is the highest proof of work value a Bitcoin block // can have for the regression test network. It is the value 2^255 - 1. @@ -33,8 +33,8 @@ var ( // testNet3PowLimit is the highest proof of work value a Bitcoin block // can have for the test network (version 3). It is the value - // 2^224 - 1. - testNet3PowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) + // 2^232 - 1. + testNet3PowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 232), bigOne) // simNetPowLimit is the highest proof of work value a Bitcoin block // can have for the simulation test network. It is the value 2^255 - 1.