mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 21:56:50 +00:00
[DEV-43] Change UTXOViewpoint.BestHash() to Tips() and update all related logic (#15)
* [DEV-43] Changed BestHash to Tips, fixed logic inside utxoviewpoint.go. * [DEV-43] Fixed broken references. * [DEV-43] Replaced blockNode slices and hash slices with BlockSets. * [DEV-43] Did some renaming, unexported blockSet, and rewrote AppendTip as AddBlock. * [DEV-43] Removed explicit contains check from AddBlock.
This commit is contained in:
parent
4b5e99e486
commit
79a0c1f124
@ -24,18 +24,18 @@ import (
|
||||
func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||
// The height of this block is one more than the referenced previous
|
||||
// block.
|
||||
nodes, err := lookupPreviousNodes(block, b)
|
||||
parents, err := lookupPreviousNodes(block, b)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
firstNode := nodes[0] // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
blockHeight := firstNode.height + 1
|
||||
selectedParent := parents.first()
|
||||
blockHeight := selectedParent.height + 1
|
||||
block.SetHeight(blockHeight)
|
||||
|
||||
// The block must pass all of the validation rules which depend on the
|
||||
// position of the block within the block chain.
|
||||
err = b.checkBlockContext(block, firstNode, flags)
|
||||
err = b.checkBlockContext(block, selectedParent, flags)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -60,7 +60,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||
// if the block ultimately gets connected to the main chain, it starts out
|
||||
// on a side chain.
|
||||
blockHeader := &block.MsgBlock().Header
|
||||
newNode := newBlockNode(blockHeader, nodes)
|
||||
newNode := newBlockNode(blockHeader, parents)
|
||||
newNode.status = statusDataStored
|
||||
|
||||
b.index.AddNode(newNode)
|
||||
@ -72,7 +72,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||
// Connect the passed block to the chain while respecting proper chain
|
||||
// selection according to the chain with the most proof of work. This
|
||||
// also handles validation of the transaction scripts.
|
||||
isMainChain, err := b.connectBestChain(newNode, block, flags)
|
||||
isMainChain, err := b.connectBestChain(newNode, parents, block, flags)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -87,11 +87,11 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
||||
return isMainChain, nil
|
||||
}
|
||||
|
||||
func lookupPreviousNodes(block *btcutil.Block, blockChain *BlockChain) ([]*blockNode, error) {
|
||||
func lookupPreviousNodes(block *btcutil.Block, blockChain *BlockChain) (blockSet, error) {
|
||||
header := block.MsgBlock().Header
|
||||
prevHashes := header.PrevBlocks
|
||||
|
||||
nodes := make([]*blockNode, len(prevHashes))
|
||||
nodes := newSet()
|
||||
for _, prevHash := range prevHashes {
|
||||
node := blockChain.index.LookupNode(&prevHash)
|
||||
if node == nil {
|
||||
@ -102,7 +102,7 @@ func lookupPreviousNodes(block *btcutil.Block, blockChain *BlockChain) ([]*block
|
||||
return nil, ruleError(ErrInvalidAncestorBlock, str)
|
||||
}
|
||||
|
||||
nodes = append(nodes, node)
|
||||
nodes.add(node)
|
||||
}
|
||||
|
||||
return nodes, nil
|
||||
|
@ -72,7 +72,7 @@ type blockNode struct {
|
||||
// padding adds up.
|
||||
|
||||
// parents is the parent blocks for this node.
|
||||
parents []*blockNode
|
||||
parents blockSet
|
||||
|
||||
// selectedParent is the selected parent for this node.
|
||||
selectedParent *blockNode
|
||||
@ -80,11 +80,11 @@ type blockNode struct {
|
||||
// hash is the double sha 256 of the block.
|
||||
hash daghash.Hash
|
||||
|
||||
// workSum is the total amount of work in the chain up to and including
|
||||
// workSum is the total amount of work in the DAG up to and including
|
||||
// this node.
|
||||
workSum *big.Int
|
||||
|
||||
// height is the position in the block chain.
|
||||
// height is the position in the block DAG.
|
||||
height int32
|
||||
|
||||
// Some fields from block headers to aid in best chain selection and
|
||||
@ -108,7 +108,7 @@ type blockNode struct {
|
||||
// calculating the height and workSum from the respective fields on the first parent.
|
||||
// This function is NOT safe for concurrent access. It must only be called when
|
||||
// initially creating a node.
|
||||
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents []*blockNode) {
|
||||
func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents blockSet) {
|
||||
*node = blockNode{
|
||||
hash: blockHeader.BlockHash(),
|
||||
parents: parents,
|
||||
@ -120,7 +120,7 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents []*bl
|
||||
merkleRoot: blockHeader.MerkleRoot,
|
||||
}
|
||||
if len(parents) > 0 {
|
||||
node.selectedParent = parents[0]
|
||||
node.selectedParent = parents.first()
|
||||
node.height = node.selectedParent.height + 1
|
||||
node.workSum = node.workSum.Add(node.selectedParent.workSum, node.workSum)
|
||||
}
|
||||
@ -129,7 +129,7 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents []*bl
|
||||
// newBlockNode returns a new block node for the given block header and parent
|
||||
// nodes, calculating the height and workSum from the respective fields on the
|
||||
// parent. This function is NOT safe for concurrent access.
|
||||
func newBlockNode(blockHeader *wire.BlockHeader, parents []*blockNode) *blockNode {
|
||||
func newBlockNode(blockHeader *wire.BlockHeader, parents blockSet) *blockNode {
|
||||
var node blockNode
|
||||
initBlockNode(&node, blockHeader, parents)
|
||||
return &node
|
||||
|
130
blockdag/blockset.go
Normal file
130
blockdag/blockset.go
Normal file
@ -0,0 +1,130 @@
|
||||
package blockdag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||
)
|
||||
|
||||
// blockSet implements a basic unsorted set of blocks
|
||||
type blockSet map[daghash.Hash]*blockNode
|
||||
|
||||
// newSet creates a new, empty BlockSet
|
||||
func newSet() blockSet {
|
||||
return map[daghash.Hash]*blockNode{}
|
||||
}
|
||||
|
||||
// setFromSlice converts a slice of blocks into an unordered set represented as map
|
||||
func setFromSlice(blocks ...*blockNode) blockSet {
|
||||
set := newSet()
|
||||
for _, block := range blocks {
|
||||
set[block.hash] = block
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// toSlice converts a set of blocks into a slice
|
||||
func (bs blockSet) toSlice() []*blockNode {
|
||||
slice := []*blockNode{}
|
||||
|
||||
for _, block := range bs {
|
||||
slice = append(slice, block)
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
// add adds a block to this BlockSet
|
||||
func (bs blockSet) add(block *blockNode) {
|
||||
bs[block.hash] = block
|
||||
}
|
||||
|
||||
// remove removes a block from this BlockSet, if exists
|
||||
// Does nothing if this set does not contain the block
|
||||
func (bs blockSet) remove(block *blockNode) {
|
||||
delete(bs, block.hash)
|
||||
}
|
||||
|
||||
// clone clones thie block set
|
||||
func (bs blockSet) clone() blockSet {
|
||||
clone := newSet()
|
||||
for _, block := range bs {
|
||||
clone.add(block)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
// subtract returns the difference between the BlockSet and another BlockSet
|
||||
func (bs blockSet) subtract(other blockSet) blockSet {
|
||||
diff := newSet()
|
||||
for _, block := range bs {
|
||||
if !other.contains(block) {
|
||||
diff.add(block)
|
||||
}
|
||||
}
|
||||
return diff
|
||||
}
|
||||
|
||||
// addSet adds all blocks in other set to this set
|
||||
func (bs blockSet) addSet(other blockSet) {
|
||||
for _, block := range other {
|
||||
bs.add(block)
|
||||
}
|
||||
}
|
||||
|
||||
// addSlice adds provided slice to this set
|
||||
func (bs blockSet) addSlice(slice []*blockNode) {
|
||||
for _, block := range slice {
|
||||
bs.add(block)
|
||||
}
|
||||
}
|
||||
|
||||
// union returns a BlockSet that contains all blocks included in this set,
|
||||
// the other set, or both
|
||||
func (bs blockSet) union(other blockSet) blockSet {
|
||||
union := bs.clone()
|
||||
|
||||
union.addSet(other)
|
||||
|
||||
return union
|
||||
}
|
||||
|
||||
// contains returns true iff this set contains block
|
||||
func (bs blockSet) contains(block *blockNode) bool {
|
||||
_, ok := bs[block.hash]
|
||||
return ok
|
||||
}
|
||||
|
||||
// hashesEqual returns true if the given hashes are equal to the hashes
|
||||
// of the blocks in this set.
|
||||
// NOTE: The given hash slice must not contain duplicates.
|
||||
func (bs blockSet) hashesEqual(hashes []daghash.Hash) bool {
|
||||
if len(hashes) != len(bs) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, hash := range hashes {
|
||||
if _, wasFound := bs[hash]; !wasFound {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// first returns the first block in this set or nil if this set is empty.
|
||||
func (bs blockSet) first() *blockNode {
|
||||
for _, block := range bs {
|
||||
return block
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs blockSet) String() string {
|
||||
ids := []string{}
|
||||
for hash := range bs {
|
||||
ids = append(ids, hash.String())
|
||||
}
|
||||
return strings.Join(ids, ",")
|
||||
}
|
@ -819,13 +819,13 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// database and using that information to unspend all of the spent txos
|
||||
// and remove the utxos created by the blocks.
|
||||
view := NewUtxoViewpoint()
|
||||
view.SetBestHash(&b.bestChain.SelectedTip().hash)
|
||||
for e := detachNodes.Front(); e != nil; e = e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
view.SetTips(b.bestChain.Tips())
|
||||
for element := detachNodes.Front(); element != nil; element = element.Next() {
|
||||
node := element.Value.(*blockNode)
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
block, err = dbFetchBlockByNode(dbTx, n)
|
||||
block, err = dbFetchBlockByNode(dbTx, node)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
@ -854,7 +854,7 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
detachBlocks = append(detachBlocks, block)
|
||||
detachSpentTxOuts = append(detachSpentTxOuts, stxos)
|
||||
|
||||
err = view.disconnectTransactions(b.db, block, stxos)
|
||||
err = view.disconnectTransactions(b.db, node.parents, block, stxos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -873,20 +873,20 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// tweaking the chain and/or database. This approach catches these
|
||||
// issues before ever modifying the chain.
|
||||
var validationError error
|
||||
for e := attachNodes.Front(); e != nil; e = e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
for element := attachNodes.Front(); element != nil; element = element.Next() {
|
||||
node := element.Value.(*blockNode)
|
||||
|
||||
// If any previous nodes in attachNodes failed validation,
|
||||
// mark this one as having an invalid ancestor.
|
||||
if validationError != nil {
|
||||
b.index.SetStatusFlags(n, statusInvalidAncestor)
|
||||
b.index.SetStatusFlags(node, statusInvalidAncestor)
|
||||
continue
|
||||
}
|
||||
|
||||
var block *btcutil.Block
|
||||
err := b.db.View(func(dbTx database.Tx) error {
|
||||
var err error
|
||||
block, err = dbFetchBlockByNode(dbTx, n)
|
||||
block, err = dbFetchBlockByNode(dbTx, node)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
@ -899,12 +899,12 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// Skip checks if node has already been fully validated. Although
|
||||
// checkConnectBlock gets skipped, we still need to update the UTXO
|
||||
// view.
|
||||
if b.index.NodeStatus(n).KnownValid() {
|
||||
if b.index.NodeStatus(node).KnownValid() {
|
||||
err = view.fetchInputUtxos(b.db, block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = view.connectTransactions(block, nil)
|
||||
err = view.connectTransactions(node, block.Transactions(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -915,19 +915,19 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// thus will not be generated. This is done because the state
|
||||
// is not being immediately written to the database, so it is
|
||||
// not needed.
|
||||
err = b.checkConnectBlock(n, block, view, nil)
|
||||
err = b.checkConnectBlock(node, block, view, nil)
|
||||
if err != nil {
|
||||
// If the block failed validation mark it as invalid, then
|
||||
// continue to loop through remaining nodes, marking them as
|
||||
// having an invalid ancestor.
|
||||
if _, ok := err.(RuleError); ok {
|
||||
b.index.SetStatusFlags(n, statusValidateFailed)
|
||||
b.index.SetStatusFlags(node, statusValidateFailed)
|
||||
validationError = err
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
b.index.SetStatusFlags(n, statusValid)
|
||||
b.index.SetStatusFlags(node, statusValid)
|
||||
}
|
||||
|
||||
if validationError != nil {
|
||||
@ -940,11 +940,11 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// view to be valid from the viewpoint of each block being connected or
|
||||
// disconnected.
|
||||
view = NewUtxoViewpoint()
|
||||
view.SetBestHash(&b.bestChain.SelectedTip().hash)
|
||||
view.SetTips(b.bestChain.Tips())
|
||||
|
||||
// Disconnect blocks from the main chain.
|
||||
for i, e := 0, detachNodes.Front(); e != nil; i, e = i+1, e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
for i, element := 0, detachNodes.Front(); element != nil; i, element = i+1, element.Next() {
|
||||
node := element.Value.(*blockNode)
|
||||
block := detachBlocks[i]
|
||||
|
||||
// Load all of the utxos referenced by the block that aren't
|
||||
@ -956,22 +956,22 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
|
||||
// Update the view to unspend all of the spent txos and remove
|
||||
// the utxos created by the block.
|
||||
err = view.disconnectTransactions(b.db, block,
|
||||
err = view.disconnectTransactions(b.db, node.parents, block,
|
||||
detachSpentTxOuts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the database and chain state.
|
||||
err = b.disconnectBlock(n, block, view)
|
||||
err = b.disconnectBlock(node, block, view)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Connect the new best chain blocks.
|
||||
for i, e := 0, attachNodes.Front(); e != nil; i, e = i+1, e.Next() {
|
||||
n := e.Value.(*blockNode)
|
||||
for i, element := 0, attachNodes.Front(); element != nil; i, element = i+1, element.Next() {
|
||||
node := element.Value.(*blockNode)
|
||||
block := attachBlocks[i]
|
||||
|
||||
// Load all of the utxos referenced by the block that aren't
|
||||
@ -986,13 +986,13 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// to it. Also, provide an stxo slice so the spent txout
|
||||
// details are generated.
|
||||
stxos := make([]spentTxOut, 0, countSpentOutputs(block))
|
||||
err = view.connectTransactions(block, &stxos)
|
||||
err = view.connectTransactions(node, block.Transactions(), &stxos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update the database and chain state.
|
||||
err = b.connectBlock(n, block, view, stxos)
|
||||
err = b.connectBlock(node, block, view, stxos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1024,13 +1024,13 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
|
||||
// This is useful when using checkpoints.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||
func (b *BlockChain) connectBestChain(node *blockNode, parentNodes blockSet, block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||
|
||||
// We are extending the main (best) chain with a new block. This is the
|
||||
// most common case.
|
||||
parentHash := block.MsgBlock().Header.SelectedPrevBlock()
|
||||
if parentHash.IsEqual(&b.bestChain.SelectedTip().hash) {
|
||||
parentHashes := block.MsgBlock().Header.PrevBlocks
|
||||
if b.bestChain.Tips().hashesEqual(parentHashes) {
|
||||
// Skip checks if node has already been fully validated.
|
||||
fastAdd = fastAdd || b.index.NodeStatus(node).KnownValid()
|
||||
|
||||
@ -1038,7 +1038,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||
// to the main chain without violating any rules and without
|
||||
// actually connecting the block.
|
||||
view := NewUtxoViewpoint()
|
||||
view.SetBestHash(parentHash)
|
||||
view.SetTips(parentNodes)
|
||||
stxos := make([]spentTxOut, 0, countSpentOutputs(block))
|
||||
if !fastAdd {
|
||||
err := b.checkConnectBlock(node, block, view, &stxos)
|
||||
@ -1073,7 +1073,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = view.connectTransactions(block, &stxos)
|
||||
err = view.connectTransactions(node, block.Transactions(), &stxos)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -1097,7 +1097,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||
if node.workSum.Cmp(b.bestChain.SelectedTip().workSum) <= 0 {
|
||||
// Log information about how the block is forking the chain.
|
||||
fork := b.bestChain.FindFork(node)
|
||||
if fork.hash.IsEqual(parentHash) {
|
||||
if fork.hash.IsEqual(block.MsgBlock().Header.SelectedPrevBlock()) {
|
||||
log.Infof("FORK: Block %v forks the chain at height %d"+
|
||||
"/block %v, but does not cause a reorganize",
|
||||
node.hash, fork.height, fork.hash)
|
||||
|
@ -147,7 +147,7 @@ func TestCalcSequenceLock(t *testing.T) {
|
||||
})
|
||||
utxoView := NewUtxoViewpoint()
|
||||
utxoView.AddTxOuts(targetTx, int32(numBlocksToActivate)-4)
|
||||
utxoView.SetBestHash(&node.hash)
|
||||
utxoView.SetTips(setFromSlice(node))
|
||||
|
||||
// Create a utxo that spends the fake utxo created above for use in the
|
||||
// transactions created in the tests. It has an age of 4 blocks. Note
|
||||
|
@ -1162,7 +1162,7 @@ func (b *BlockChain) initChainState() error {
|
||||
// Initialize the block node for the block, connect it,
|
||||
// and add it to the block index.
|
||||
node := &blockNodes[i]
|
||||
initBlockNode(node, header, []*blockNode{parent}) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
initBlockNode(node, header, setFromSlice(parent)) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
node.status = status
|
||||
b.index.addNode(node)
|
||||
|
||||
|
@ -96,19 +96,19 @@ func (c *chainView) tip() *blockNode {
|
||||
// an empty slice if there is no tip.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (c *chainView) Tips() []*blockNode {
|
||||
func (c *chainView) Tips() blockSet {
|
||||
c.mtx.Lock()
|
||||
tip := c.tip()
|
||||
c.mtx.Unlock()
|
||||
return []*blockNode{tip} // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
return setFromSlice(tip) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
}
|
||||
|
||||
// SelecedTip returns the current selected tip block node for the chain view.
|
||||
// It will return nil if there is no tip.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (view *chainView) SelectedTip() *blockNode {
|
||||
return view.Tips()[0]
|
||||
func (c *chainView) SelectedTip() *blockNode {
|
||||
return c.Tips().first()
|
||||
}
|
||||
|
||||
// setTip sets the chain view to use the provided block node as the current tip
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/daglabs/btcd/wire"
|
||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
|
||||
// testNoncePrng provides a deterministic prng for the nonce in generated fake
|
||||
@ -31,7 +31,7 @@ func chainedNodes(parent *blockNode, numNodes int) []*blockNode {
|
||||
if tip != nil {
|
||||
header.PrevBlocks = []daghash.Hash{tip.hash} // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
}
|
||||
nodes[i] = newBlockNode(&header, []*blockNode{tip}) // 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]
|
||||
}
|
||||
return nodes
|
||||
|
@ -380,5 +380,5 @@ func newFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp t
|
||||
Bits: bits,
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
return newBlockNode(header, []*blockNode{parent}) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
return newBlockNode(header, setFromSlice(parent)) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
}
|
||||
|
@ -119,20 +119,32 @@ func (entry *UtxoEntry) Clone() *UtxoEntry {
|
||||
// The unspent outputs are needed by other transactions for things such as
|
||||
// script validation and double spend prevention.
|
||||
type UtxoViewpoint struct {
|
||||
entries map[wire.OutPoint]*UtxoEntry
|
||||
bestHash daghash.Hash
|
||||
entries map[wire.OutPoint]*UtxoEntry
|
||||
tips blockSet
|
||||
}
|
||||
|
||||
// BestHash returns the hash of the best block in the chain the view currently
|
||||
// respresents.
|
||||
func (view *UtxoViewpoint) BestHash() *daghash.Hash {
|
||||
return &view.bestHash
|
||||
// Tips returns the hashes of the tips in the DAG the view currently
|
||||
// represents.
|
||||
func (view *UtxoViewpoint) Tips() blockSet {
|
||||
return view.tips
|
||||
}
|
||||
|
||||
// SetBestHash sets the hash of the best block in the chain the view currently
|
||||
// respresents.
|
||||
func (view *UtxoViewpoint) SetBestHash(hash *daghash.Hash) {
|
||||
view.bestHash = *hash
|
||||
// SetTips sets the hashes of the tips in the DAG the view currently
|
||||
// represents.
|
||||
func (view *UtxoViewpoint) SetTips(tips blockSet) {
|
||||
view.tips = tips
|
||||
}
|
||||
|
||||
// AddBlock removes all the parents of block from the tips and adds
|
||||
// the given block to the tips.
|
||||
func (view *UtxoViewpoint) AddBlock(block *blockNode) {
|
||||
updatedTips := view.tips.clone()
|
||||
for _, parent := range block.parents {
|
||||
updatedTips.remove(parent)
|
||||
}
|
||||
|
||||
updatedTips.add(block)
|
||||
view.tips = updatedTips
|
||||
}
|
||||
|
||||
// LookupEntry returns information about a given transaction output according to
|
||||
@ -264,9 +276,9 @@ func (view *UtxoViewpoint) connectTransaction(tx *btcutil.Tx, blockHeight int32,
|
||||
// spend as spent, and setting the best hash for the view to the passed block.
|
||||
// In addition, when the 'stxos' argument is not nil, it will be updated to
|
||||
// append an entry for each spent txout.
|
||||
func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]spentTxOut) error {
|
||||
for _, tx := range block.Transactions() {
|
||||
err := view.connectTransaction(tx, block.Height(), stxos)
|
||||
func (view *UtxoViewpoint) connectTransactions(block *blockNode, transactions []*btcutil.Tx, stxos *[]spentTxOut) error {
|
||||
for _, tx := range transactions {
|
||||
err := view.connectTransaction(tx, block.height, stxos)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -274,7 +286,7 @@ func (view *UtxoViewpoint) connectTransactions(block *btcutil.Block, stxos *[]sp
|
||||
|
||||
// Update the best hash for view to include this block since all of its
|
||||
// transactions have been connected.
|
||||
view.SetBestHash(block.Hash())
|
||||
view.AddBlock(block)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -308,7 +320,7 @@ func (view *UtxoViewpoint) fetchEntryByHash(db database.DB, hash *daghash.Hash)
|
||||
// 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, block *btcutil.Block, stxos []spentTxOut) error {
|
||||
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 " +
|
||||
@ -432,9 +444,9 @@ func (view *UtxoViewpoint) disconnectTransactions(db database.DB, block *btcutil
|
||||
}
|
||||
}
|
||||
|
||||
// Update the best hash for view to the previous block since all of the
|
||||
// Update the tips for view to the block's parents since all of the
|
||||
// transactions for the current block have been disconnected.
|
||||
view.SetBestHash(block.MsgBlock().Header.SelectedPrevBlock())
|
||||
view.SetTips(parents)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -649,13 +649,13 @@ func checkSerializedHeight(coinbaseTx *btcutil.Tx, wantHeight int32) error {
|
||||
// the checkpoints are not performed.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error {
|
||||
func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, selectedParent *blockNode, flags BehaviorFlags) error {
|
||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||
if !fastAdd {
|
||||
// Ensure the difficulty specified in the block header matches
|
||||
// the calculated difficulty based on the previous block and
|
||||
// difficulty retarget rules.
|
||||
expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode,
|
||||
expectedDifficulty, err := b.calcNextRequiredDifficulty(selectedParent,
|
||||
header.Timestamp)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -669,7 +669,7 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
|
||||
// Ensure the timestamp for the block header is after the
|
||||
// median time of the last several blocks (medianTimeBlocks).
|
||||
medianTime := prevNode.CalcPastMedianTime()
|
||||
medianTime := selectedParent.CalcPastMedianTime()
|
||||
if !header.Timestamp.After(medianTime) {
|
||||
str := "block timestamp of %v is not after expected %v"
|
||||
str = fmt.Sprintf(str, header.Timestamp, medianTime)
|
||||
@ -679,7 +679,7 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
|
||||
// The height of this block is one more than the referenced previous
|
||||
// block.
|
||||
blockHeight := prevNode.height + 1
|
||||
blockHeight := selectedParent.height + 1
|
||||
|
||||
// Ensure chain matches up to predetermined checkpoints.
|
||||
blockHash := header.BlockHash()
|
||||
@ -731,10 +731,10 @@ func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode
|
||||
// for how the flags modify its behavior.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (b *BlockChain) checkBlockContext(block *btcutil.Block, prevNode *blockNode, flags BehaviorFlags) error {
|
||||
func (b *BlockChain) checkBlockContext(block *btcutil.Block, selectedParent *blockNode, flags BehaviorFlags) error {
|
||||
// Perform all block header related validation checks.
|
||||
header := &block.MsgBlock().Header
|
||||
err := b.checkBlockHeaderContext(header, prevNode, flags)
|
||||
err := b.checkBlockHeaderContext(header, selectedParent, flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -744,7 +744,7 @@ func (b *BlockChain) checkBlockContext(block *btcutil.Block, prevNode *blockNode
|
||||
// Obtain the latest state of the deployed CSV soft-fork in
|
||||
// order to properly guard the new validation behavior based on
|
||||
// the current BIP 9 version bits state.
|
||||
csvState, err := b.deploymentState(prevNode, dagconfig.DeploymentCSV)
|
||||
csvState, err := b.deploymentState(selectedParent, dagconfig.DeploymentCSV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -754,12 +754,12 @@ func (b *BlockChain) checkBlockContext(block *btcutil.Block, prevNode *blockNode
|
||||
// timestamps for all lock-time based checks.
|
||||
blockTime := header.Timestamp
|
||||
if csvState == ThresholdActive {
|
||||
blockTime = prevNode.CalcPastMedianTime()
|
||||
blockTime = selectedParent.CalcPastMedianTime()
|
||||
}
|
||||
|
||||
// The height of this block is one more than the referenced
|
||||
// previous block.
|
||||
blockHeight := prevNode.height + 1
|
||||
blockHeight := selectedParent.height + 1
|
||||
|
||||
// Ensure all transactions in the block are finalized.
|
||||
for _, tx := range block.Transactions() {
|
||||
@ -973,11 +973,11 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi
|
||||
}
|
||||
|
||||
// Ensure the view is for the node being checked.
|
||||
parentHash := block.MsgBlock().Header.SelectedPrevBlock()
|
||||
if !view.BestHash().IsEqual(parentHash) {
|
||||
parentHashes := block.MsgBlock().Header.PrevBlocks
|
||||
if !view.Tips().hashesEqual(parentHashes) {
|
||||
return AssertError(fmt.Sprintf("inconsistent view when "+
|
||||
"checking block connection: best hash is %v instead "+
|
||||
"of expected %v", view.BestHash(), parentHash))
|
||||
"checking block connection: tips are %v instead "+
|
||||
"of expected %v", view.Tips(), parentHashes))
|
||||
}
|
||||
|
||||
// BIP0030 added a rule to prevent blocks which contain duplicate
|
||||
@ -1189,9 +1189,9 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block *btcutil.Block, vi
|
||||
}
|
||||
}
|
||||
|
||||
// Update the best hash for view to include this block since all of its
|
||||
// Update the view tips to include this block since all of its
|
||||
// transactions have been connected.
|
||||
view.SetBestHash(&node.hash)
|
||||
view.AddBlock(node)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -1210,11 +1210,12 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error {
|
||||
|
||||
// This only checks whether the block can be connected to the tip of the
|
||||
// current chain.
|
||||
tip := b.bestChain.SelectedTip()
|
||||
tips := b.bestChain.Tips()
|
||||
header := block.MsgBlock().Header
|
||||
if tip.hash != *header.SelectedPrevBlock() {
|
||||
str := fmt.Sprintf("previous block must be the current chain tip %v, "+
|
||||
"instead got %v", tip.hash, header.SelectedPrevBlock())
|
||||
prevHashes := header.PrevBlocks
|
||||
if tips.hashesEqual(prevHashes) {
|
||||
str := fmt.Sprintf("previous blocks must be the currents tips %v, "+
|
||||
"instead got %v", tips, prevHashes)
|
||||
return ruleError(ErrPrevBlockNotBest, str)
|
||||
}
|
||||
|
||||
@ -1223,7 +1224,7 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error {
|
||||
return err
|
||||
}
|
||||
|
||||
err = b.checkBlockContext(block, tip, flags)
|
||||
err = b.checkBlockContext(block, b.bestChain.SelectedTip(), flags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1231,7 +1232,7 @@ func (b *BlockChain) CheckConnectBlockTemplate(block *btcutil.Block) error {
|
||||
// Leave the spent txouts entry nil in the state since the information
|
||||
// is not needed and thus extra work can be avoided.
|
||||
view := NewUtxoViewpoint()
|
||||
view.SetBestHash(&tip.hash)
|
||||
newNode := newBlockNode(&header, []*blockNode{tip}) // TODO: (Stas) This is wrong. Modified only to satisfy compilation.
|
||||
view.SetTips(tips)
|
||||
newNode := newBlockNode(&header, b.bestChain.Tips())
|
||||
return b.checkConnectBlock(newNode, block, view, nil)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user