mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-07-06 04:42:31 +00:00
[DEV-45] Update blockdag package tests for primitive blockDAG (#24)
* [DEV-45] Updated the BlockDB files to contain blocks that could be deserialized. * [DEV-45] Fixed TestHaveBlock (finally) * [DEV-45] Fixed TestLocateInventory and everything that's reliant on chainview_test::chainedNodes. * [DEV-45] Fixed TestChainViewNil. * [DEV-45] Fixed TestNotifications. * [DEV-45] Fixed ExampleBlockChain_ProcessBlock. * [DEV-45] Fixed TestCheckBlockScripts. * [DEV-45] Fixed TestCheckConnectBlockTemplate. * [DEV-45] Renamed the BlockDBs to their original names. * [DEV-45] Skipping TestFullBlocks for until we have implemented Phantom. * [DEV-45] Deleted a couple of methods that are no longer used. (They were previously used for reorganization)
This commit is contained in:
parent
c9c81e1a82
commit
f4a0ec175d
@ -112,6 +112,16 @@ func (bs blockSet) hashesEqual(hashes []daghash.Hash) bool {
|
|||||||
return true
|
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.
|
// first returns the first block in this set or nil if this set is empty.
|
||||||
func (bs blockSet) first() *blockNode {
|
func (bs blockSet) first() *blockNode {
|
||||||
for _, block := range bs {
|
for _, block := range bs {
|
||||||
|
@ -483,8 +483,7 @@ func LockTimeToSequence(isSeconds bool, locktime uint32) uint32 {
|
|||||||
locktime>>wire.SequenceLockTimeGranularity
|
locktime>>wire.SequenceLockTimeGranularity
|
||||||
}
|
}
|
||||||
|
|
||||||
// connectBlock handles connecting the passed node/block to the end of the main
|
// connectBlock handles connecting the passed node/block to the DAG.
|
||||||
// (best) chain.
|
|
||||||
//
|
//
|
||||||
// This passed utxo view must have all referenced txos the block spends marked
|
// 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,
|
// 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).
|
// 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 {
|
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.
|
// Sanity check the correct number of stxos are provided.
|
||||||
if len(stxos) != countSpentOutputs(block) {
|
if len(stxos) != countSpentOutputs(block) {
|
||||||
return AssertError("connectBlock called with inconsistent " +
|
return AssertError("connectBlock called with inconsistent " +
|
||||||
@ -612,117 +604,6 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
|
|||||||
return nil
|
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.
|
// countSpentOutputs returns the number of utxos the passed block spends.
|
||||||
func countSpentOutputs(block *btcutil.Block) int {
|
func countSpentOutputs(block *btcutil.Block) int {
|
||||||
// Exclude the coinbase transaction since it can't spend anything.
|
// Exclude the coinbase transaction since it can't spend anything.
|
||||||
|
@ -17,12 +17,12 @@ import (
|
|||||||
|
|
||||||
// TestHaveBlock tests the HaveBlock API to ensure proper functionality.
|
// TestHaveBlock tests the HaveBlock API to ensure proper functionality.
|
||||||
func TestHaveBlock(t *testing.T) {
|
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
|
// (genesis block) -> 1 -> 2 -> 3 -> 4
|
||||||
// \-> 3a
|
// \-> 3b
|
||||||
testFiles := []string{
|
testFiles := []string{
|
||||||
"blk_0_to_4.dat.bz2",
|
"blk_0_to_4.dat",
|
||||||
"blk_3A.dat.bz2",
|
"blk_3B.dat",
|
||||||
}
|
}
|
||||||
|
|
||||||
var blocks []*btcutil.Block
|
var blocks []*btcutil.Block
|
||||||
@ -78,14 +78,14 @@ func TestHaveBlock(t *testing.T) {
|
|||||||
hash string
|
hash string
|
||||||
want bool
|
want bool
|
||||||
}{
|
}{
|
||||||
// Genesis block should be present (in the main chain).
|
// Genesis block should be present.
|
||||||
{hash: dagconfig.MainNetParams.GenesisHash.String(), want: true},
|
{hash: dagconfig.MainNetParams.GenesisHash.String(), want: true},
|
||||||
|
|
||||||
// Block 3a should be present (on a side chain).
|
// Block 3b should be present (as a second child of Block 2).
|
||||||
{hash: "00000000474284d20067a4d33f6a02284e6ef70764a3a26d6a5b9df52ef663dd", want: true},
|
{hash: "000000c7576990a9a73785181a36c0b346d0750c385345252c1cfa6951928c26", want: true},
|
||||||
|
|
||||||
// Block 100000 should be present (as an orphan).
|
// Block 100000 should be present (as an orphan).
|
||||||
{hash: "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506", want: true},
|
{hash: "000000826ababf6b615336e6e1a5b479f4d2e580448cc090fc111b749a28654b", want: true},
|
||||||
|
|
||||||
// Random hashes should not be available.
|
// Random hashes should not be available.
|
||||||
{hash: "123", want: false},
|
{hash: "123", want: false},
|
||||||
@ -125,7 +125,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
|||||||
blockVersion := int32(0x20000000 | (uint32(1) << csvBit))
|
blockVersion := int32(0x20000000 | (uint32(1) << csvBit))
|
||||||
|
|
||||||
// Generate enough synthetic blocks to activate CSV.
|
// Generate enough synthetic blocks to activate CSV.
|
||||||
chain := newFakeChain(netParams)
|
chain := newFakeDag(netParams)
|
||||||
node := chain.bestChain.SelectedTip()
|
node := chain.bestChain.SelectedTip()
|
||||||
blockTime := node.Header().Timestamp
|
blockTime := node.Header().Timestamp
|
||||||
numBlocksToActivate := (netParams.MinerConfirmationWindow * 3)
|
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
|
// TestLocateInventory ensures that locating inventory via the LocateHeaders and
|
||||||
// LocateBlocks functions behaves as expected.
|
// LocateBlocks functions behaves as expected.
|
||||||
func TestLocateInventory(t *testing.T) {
|
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.
|
// the following structure.
|
||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
// \-> 16a -> 17a
|
// \-> 16a -> 17a
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
chain := newFakeChain(&dagconfig.MainNetParams)
|
dag := newFakeDag(&dagconfig.MainNetParams)
|
||||||
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
branch0Nodes := chainedNodes(setFromSlice(dag.bestChain.Genesis()), 18)
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[14], 2)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 2)
|
||||||
for _, node := range branch0Nodes {
|
for _, node := range branch0Nodes {
|
||||||
chain.index.AddNode(node)
|
dag.index.AddNode(node)
|
||||||
}
|
}
|
||||||
for _, node := range branch1Nodes {
|
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
|
// Create chain views for different branches of the overall chain to
|
||||||
// simulate a local and remote node on different parts of the chain.
|
// 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
|
// Create a chain view for a completely unrelated block chain to
|
||||||
// simulate a remote node on a totally different chain.
|
// simulate a remote node on a totally different chain.
|
||||||
unrelatedBranchNodes := chainedNodes(nil, 5)
|
unrelatedBranchNodes := chainedNodes(newSet(), 5)
|
||||||
unrelatedView := newChainView(tip(unrelatedBranchNodes))
|
unrelatedView := newChainView(tip(unrelatedBranchNodes))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -772,12 +772,12 @@ func TestLocateInventory(t *testing.T) {
|
|||||||
if test.maxAllowed != 0 {
|
if test.maxAllowed != 0 {
|
||||||
// Need to use the unexported function to override the
|
// Need to use the unexported function to override the
|
||||||
// max allowed for headers.
|
// max allowed for headers.
|
||||||
chain.chainLock.RLock()
|
dag.chainLock.RLock()
|
||||||
headers = chain.locateHeaders(test.locator,
|
headers = dag.locateHeaders(test.locator,
|
||||||
&test.hashStop, test.maxAllowed)
|
&test.hashStop, test.maxAllowed)
|
||||||
chain.chainLock.RUnlock()
|
dag.chainLock.RUnlock()
|
||||||
} else {
|
} else {
|
||||||
headers = chain.LocateHeaders(test.locator,
|
headers = dag.LocateHeaders(test.locator,
|
||||||
&test.hashStop)
|
&test.hashStop)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(headers, test.headers) {
|
if !reflect.DeepEqual(headers, test.headers) {
|
||||||
@ -791,7 +791,7 @@ func TestLocateInventory(t *testing.T) {
|
|||||||
if test.maxAllowed != 0 {
|
if test.maxAllowed != 0 {
|
||||||
maxAllowed = test.maxAllowed
|
maxAllowed = test.maxAllowed
|
||||||
}
|
}
|
||||||
hashes := chain.LocateBlocks(test.locator, &test.hashStop,
|
hashes := dag.LocateBlocks(test.locator, &test.hashStop,
|
||||||
maxAllowed)
|
maxAllowed)
|
||||||
if !reflect.DeepEqual(hashes, test.hashes) {
|
if !reflect.DeepEqual(hashes, test.hashes) {
|
||||||
t.Errorf("%s: unxpected hashes -- got %v, want %v",
|
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
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
chain := newFakeChain(&dagconfig.MainNetParams)
|
chain := newFakeDag(&dagconfig.MainNetParams)
|
||||||
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
branch0Nodes := chainedNodes(setFromSlice(chain.bestChain.Genesis()), 18)
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[14], 3)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3)
|
||||||
for _, node := range branch0Nodes {
|
for _, node := range branch0Nodes {
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
chain.index.SetStatusFlags(node, statusValid)
|
||||||
chain.index.AddNode(node)
|
chain.index.AddNode(node)
|
||||||
@ -901,9 +901,9 @@ func TestIntervalBlockHashes(t *testing.T) {
|
|||||||
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
// genesis -> 1 -> 2 -> ... -> 15 -> 16 -> 17 -> 18
|
||||||
// \-> 16a -> 17a -> 18a (unvalidated)
|
// \-> 16a -> 17a -> 18a (unvalidated)
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
chain := newFakeChain(&dagconfig.MainNetParams)
|
chain := newFakeDag(&dagconfig.MainNetParams)
|
||||||
branch0Nodes := chainedNodes(chain.bestChain.Genesis(), 18)
|
branch0Nodes := chainedNodes(setFromSlice(chain.bestChain.Genesis()), 18)
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[14], 3)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[14]), 3)
|
||||||
for _, node := range branch0Nodes {
|
for _, node := range branch0Nodes {
|
||||||
chain.index.SetStatusFlags(node, statusValid)
|
chain.index.SetStatusFlags(node, statusValid)
|
||||||
chain.index.AddNode(node)
|
chain.index.AddNode(node)
|
||||||
|
@ -100,6 +100,11 @@ func (c *chainView) Tips() blockSet {
|
|||||||
c.mtx.Lock()
|
c.mtx.Lock()
|
||||||
tip := c.tip()
|
tip := c.tip()
|
||||||
c.mtx.Unlock()
|
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.
|
return setFromSlice(tip) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
|
||||||
"github.com/daglabs/btcd/wire"
|
"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
|
// 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
|
// 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.
|
// 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)
|
nodes := make([]*blockNode, numNodes)
|
||||||
tip := parent
|
tips := parents
|
||||||
for i := 0; i < numNodes; i++ {
|
for i := 0; i < numNodes; i++ {
|
||||||
// This is invalid, but all that is needed is enough to get the
|
// This is invalid, but all that is needed is enough to get the
|
||||||
// synthetic tests to work.
|
// synthetic tests to work.
|
||||||
header := wire.BlockHeader{Nonce: testNoncePrng.Uint32()}
|
header := wire.BlockHeader{Nonce: testNoncePrng.Uint32()}
|
||||||
if tip != nil {
|
header.PrevBlocks = tips.hashes()
|
||||||
header.PrevBlocks = []daghash.Hash{tip.hash} // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
nodes[i] = newBlockNode(&header, tips)
|
||||||
}
|
tips = setFromSlice(nodes[i])
|
||||||
nodes[i] = newBlockNode(&header, setFromSlice(tip)) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
|
||||||
tip = nodes[i]
|
|
||||||
}
|
}
|
||||||
return nodes
|
return nodes
|
||||||
}
|
}
|
||||||
@ -79,8 +76,8 @@ func TestChainView(t *testing.T) {
|
|||||||
// \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a
|
// \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a
|
||||||
// \-> 3a'-> 4a' -> 5a'
|
// \-> 3a'-> 4a' -> 5a'
|
||||||
branch0Nodes := chainedNodes(nil, 5)
|
branch0Nodes := chainedNodes(nil, 5)
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[1], 25)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[1]), 25)
|
||||||
branch2Nodes := chainedNodes(branch1Nodes[0], 3)
|
branch2Nodes := chainedNodes(setFromSlice(branch1Nodes[0]), 3)
|
||||||
|
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -343,8 +340,8 @@ func TestChainViewSetTip(t *testing.T) {
|
|||||||
// structure.
|
// structure.
|
||||||
// 0 -> 1 -> 2 -> 3 -> 4
|
// 0 -> 1 -> 2 -> 3 -> 4
|
||||||
// \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a
|
// \-> 2a -> 3a -> 4a -> 5a -> 6a -> 7a -> ... -> 26a
|
||||||
branch0Nodes := chainedNodes(nil, 5)
|
branch0Nodes := chainedNodes(newSet(), 5)
|
||||||
branch1Nodes := chainedNodes(branch0Nodes[1], 25)
|
branch1Nodes := chainedNodes(setFromSlice(branch0Nodes[1]), 25)
|
||||||
|
|
||||||
tip := tstTip
|
tip := tstTip
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@ -429,9 +426,9 @@ func TestChainViewNil(t *testing.T) {
|
|||||||
genesis)
|
genesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the tip of an uninitialized view does not produce a node.
|
// Ensure the tips of an uninitialized view do not produce a node.
|
||||||
if tip := view.Tips(); tip != nil {
|
if tips := view.Tips(); len(tips) > 0 {
|
||||||
t.Fatalf("Tip: unexpected tip -- got %v, want nil", tip)
|
t.Fatalf("Tip: unexpected tips -- got %v, want nothing", tips)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the height of an uninitialized view is the expected value.
|
// Ensure the height of an uninitialized view is the expected value.
|
||||||
|
@ -343,14 +343,14 @@ func (b *BlockChain) TstSetCoinbaseMaturity(maturity uint16) {
|
|||||||
b.chainParams.CoinbaseMaturity = maturity
|
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
|
// 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
|
// it is not usable with all functions and the tests must take care when making
|
||||||
// use of it.
|
// 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
|
// Create a genesis block node and block index index populated with it
|
||||||
// for use when creating the fake chain below.
|
// 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 := newBlockIndex(nil, params)
|
||||||
index.AddNode(node)
|
index.AddNode(node)
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ func ExampleBlockChain_ProcessBlock() {
|
|||||||
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
fmt.Printf("Block accepted. Is it an orphan?: %v", isOrphan)
|
||||||
|
|
||||||
// Output:
|
// 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
|
// This example demonstrates how to convert the compact "bits" in a block header
|
||||||
|
@ -132,6 +132,10 @@ func chainSetup(dbName string, params *dagconfig.Params) (*blockdag.BlockChain,
|
|||||||
// TestFullBlocks ensures all tests generated by the fullblocktests package
|
// TestFullBlocks ensures all tests generated by the fullblocktests package
|
||||||
// have the expected result when processed via ProcessBlock.
|
// have the expected result when processed via ProcessBlock.
|
||||||
func TestFullBlocks(t *testing.T) {
|
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)
|
tests, err := fullblocktests.Generate(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to generate tests: %v", err)
|
t.Fatalf("failed to generate tests: %v", err)
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// TestNotifications ensures that notification callbacks are fired on events.
|
// TestNotifications ensures that notification callbacks are fired on events.
|
||||||
func TestNotifications(t *testing.T) {
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("Error loading file: %v\n", err)
|
t.Fatalf("Error loading file: %v\n", err)
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ func TestCheckBlockScripts(t *testing.T) {
|
|||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|
||||||
testBlockNum := 277647
|
testBlockNum := 277647
|
||||||
blockDataFile := fmt.Sprintf("%d.dat.bz2", testBlockNum)
|
blockDataFile := fmt.Sprintf("%d.dat", testBlockNum)
|
||||||
blocks, err := loadBlocks(blockDataFile)
|
blocks, err := loadBlocks(blockDataFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error loading file: %v\n", err)
|
t.Errorf("Error loading file: %v\n", err)
|
||||||
@ -33,7 +33,7 @@ func TestCheckBlockScripts(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
storeDataFile := fmt.Sprintf("%d.utxostore.bz2", testBlockNum)
|
storeDataFile := fmt.Sprintf("%d.utxostore", testBlockNum)
|
||||||
view, err := loadUtxoView(storeDataFile)
|
view, err := loadUtxoView(storeDataFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error loading txstore: %v\n", err)
|
t.Errorf("Error loading txstore: %v\n", err)
|
||||||
|
BIN
blockdag/testdata/277647.dat
vendored
Normal file
BIN
blockdag/testdata/277647.dat
vendored
Normal file
Binary file not shown.
BIN
blockdag/testdata/277647.dat.bz2
vendored
BIN
blockdag/testdata/277647.dat.bz2
vendored
Binary file not shown.
BIN
blockdag/testdata/277647.utxostore
vendored
Normal file
BIN
blockdag/testdata/277647.utxostore
vendored
Normal file
Binary file not shown.
BIN
blockdag/testdata/277647.utxostore.bz2
vendored
BIN
blockdag/testdata/277647.utxostore.bz2
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
Normal file
BIN
blockdag/testdata/blk_0_to_4.dat
vendored
Normal file
Binary file not shown.
BIN
blockdag/testdata/blk_0_to_4.dat.bz2
vendored
BIN
blockdag/testdata/blk_0_to_4.dat.bz2
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3A.dat
vendored
Normal file
BIN
blockdag/testdata/blk_3A.dat
vendored
Normal file
Binary file not shown.
BIN
blockdag/testdata/blk_3A.dat.bz2
vendored
BIN
blockdag/testdata/blk_3A.dat.bz2
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_3B.dat
vendored
Normal file
BIN
blockdag/testdata/blk_3B.dat
vendored
Normal file
Binary file not shown.
BIN
blockdag/testdata/blk_4A.dat.bz2
vendored
BIN
blockdag/testdata/blk_4A.dat.bz2
vendored
Binary file not shown.
BIN
blockdag/testdata/blk_5A.dat.bz2
vendored
BIN
blockdag/testdata/blk_5A.dat.bz2
vendored
Binary file not shown.
@ -316,140 +316,6 @@ func (view *UtxoViewpoint) fetchEntryByHash(db database.DB, hash *daghash.Hash)
|
|||||||
return entry, err
|
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
|
// 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
|
// the view. It will have no effect if the passed output does not exist in the
|
||||||
// view.
|
// view.
|
||||||
|
@ -1213,7 +1213,7 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error {
|
|||||||
tips := b.bestChain.Tips()
|
tips := b.bestChain.Tips()
|
||||||
header := block.MsgBlock().Header
|
header := block.MsgBlock().Header
|
||||||
prevHashes := header.PrevBlocks
|
prevHashes := header.PrevBlocks
|
||||||
if tips.hashesEqual(prevHashes) {
|
if !tips.hashesEqual(prevHashes) {
|
||||||
str := fmt.Sprintf("previous blocks must be the currents tips %v, "+
|
str := fmt.Sprintf("previous blocks must be the currents tips %v, "+
|
||||||
"instead got %v", tips, prevHashes)
|
"instead got %v", tips, prevHashes)
|
||||||
return ruleError(ErrPrevBlockNotBest, str)
|
return ruleError(ErrPrevBlockNotBest, str)
|
||||||
|
@ -83,8 +83,8 @@ func TestCheckConnectBlockTemplate(t *testing.T) {
|
|||||||
// (genesis block) -> 1 -> 2 -> 3 -> 4
|
// (genesis block) -> 1 -> 2 -> 3 -> 4
|
||||||
// \-> 3a
|
// \-> 3a
|
||||||
testFiles := []string{
|
testFiles := []string{
|
||||||
"blk_0_to_4.dat.bz2",
|
"blk_0_to_4.dat",
|
||||||
"blk_3A.dat.bz2",
|
"blk_3B.dat",
|
||||||
}
|
}
|
||||||
|
|
||||||
var blocks []*btcutil.Block
|
var blocks []*btcutil.Block
|
||||||
|
@ -24,8 +24,8 @@ var (
|
|||||||
bigOne = big.NewInt(1)
|
bigOne = big.NewInt(1)
|
||||||
|
|
||||||
// mainPowLimit is the highest proof of work value a Bitcoin block can
|
// mainPowLimit is the highest proof of work value a Bitcoin block can
|
||||||
// have for the main network. It is the value 2^224 - 1.
|
// have for the main network. It is the value 2^232 - 1.
|
||||||
mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne)
|
mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 232), bigOne)
|
||||||
|
|
||||||
// regressionPowLimit is the highest proof of work value a Bitcoin block
|
// 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.
|
// 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
|
// testNet3PowLimit is the highest proof of work value a Bitcoin block
|
||||||
// can have for the test network (version 3). It is the value
|
// can have for the test network (version 3). It is the value
|
||||||
// 2^224 - 1.
|
// 2^232 - 1.
|
||||||
testNet3PowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne)
|
testNet3PowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 232), bigOne)
|
||||||
|
|
||||||
// simNetPowLimit is the highest proof of work value a Bitcoin block
|
// 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.
|
// can have for the simulation test network. It is the value 2^255 - 1.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user