mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-05 13:46:42 +00:00
[DEV-338] Remove provisional nodes, and panic in case there's an error after verifying the block is valid (#162)
* [DEV-338] Remove provisional nodes, and panic in case there's an error after verifying the block is valid * [DEV-338] Improved deffered blockNode cleanup and cosmetic changes * [DEV-338] Fixed dag.index.SetStatusFlags for parents + cosmetic changes * [DEV-338] Fixed build * [DEV-338] Fixed comments
This commit is contained in:
parent
e3994cddac
commit
3a5abb6584
@ -61,6 +61,14 @@ func (dag *BlockDAG) maybeAcceptBlock(block *util.Block, flags BehaviorFlags) er
|
||||
blockHeader := &block.MsgBlock().Header
|
||||
newNode := newBlockNode(blockHeader, parents, dag.dagParams.K)
|
||||
newNode.status = statusDataStored
|
||||
// newBlockNode adds node into children maps of its parents. So it must be
|
||||
// removed in case of error.
|
||||
isOk := false
|
||||
defer func() {
|
||||
if !isOk {
|
||||
newNode.detachFromParents()
|
||||
}
|
||||
}()
|
||||
|
||||
dag.index.AddNode(newNode)
|
||||
err = dag.index.flushToDB()
|
||||
@ -82,6 +90,8 @@ func (dag *BlockDAG) maybeAcceptBlock(block *util.Block, flags BehaviorFlags) er
|
||||
dag.sendNotification(NTBlockAccepted, block)
|
||||
dag.dagLock.Lock()
|
||||
|
||||
isOk = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,11 @@ func initBlockNode(node *blockNode, blockHeader *wire.BlockHeader, parents block
|
||||
node.nonce = blockHeader.Nonce
|
||||
node.timestamp = blockHeader.Timestamp.Unix()
|
||||
node.merkleRoot = blockHeader.MerkleRoot
|
||||
|
||||
// update parents to point to new node
|
||||
for _, p := range parents {
|
||||
p.children[node.hash] = node
|
||||
}
|
||||
}
|
||||
|
||||
if len(parents) > 0 {
|
||||
@ -165,6 +170,15 @@ func newBlockNode(blockHeader *wire.BlockHeader, parents blockSet, phantomK uint
|
||||
return &node
|
||||
}
|
||||
|
||||
// newBlockNode adds node into children maps of its parents. So it must be
|
||||
// removed in case of error.
|
||||
func (node *blockNode) detachFromParents() {
|
||||
// remove node from parents
|
||||
for _, p := range node.parents {
|
||||
delete(p.children, node.hash)
|
||||
}
|
||||
}
|
||||
|
||||
// Header constructs a block header from the node and returns it.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
|
213
blockdag/dag.go
213
blockdag/dag.go
@ -631,17 +631,10 @@ func (dag *BlockDAG) checkFinalityRulesAndGetFinalityPointCandidate(node *blockN
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
func (dag *BlockDAG) applyUTXOChanges(node *blockNode, block *util.Block, fastAdd bool) (utxoDiff *UTXODiff, acceptedTxData []*TxWithBlockHash, err error) {
|
||||
// Prepare provisionalNodes for all the relevant nodes to avoid modifying the original nodes.
|
||||
// We avoid modifying the original nodes in this function because it could potentially
|
||||
// fail if the block is not valid, thus bringing all the affected nodes (and the virtual)
|
||||
// into an undefined state.
|
||||
provisionalSet := newProvisionalNodeSet()
|
||||
newNodeProvisional := provisionalSet.newProvisionalNode(node, true, true, block.Transactions())
|
||||
|
||||
// Clone the virtual block so that we don't modify the existing one.
|
||||
virtualClone := dag.virtual.clone()
|
||||
|
||||
newBlockUTXO, acceptedTxData, err := newNodeProvisional.verifyAndBuildUTXO(virtualClone, dag, fastAdd)
|
||||
newBlockUTXO, acceptedTxData, err := node.verifyAndBuildUTXO(virtualClone, dag, block.Transactions(), fastAdd)
|
||||
if err != nil {
|
||||
newErrString := fmt.Sprintf("error verifying UTXO for %v: %s", node, err)
|
||||
if err, ok := err.(RuleError); ok {
|
||||
@ -650,33 +643,29 @@ func (dag *BlockDAG) applyUTXOChanges(node *blockNode, block *util.Block, fastAd
|
||||
return nil, nil, errors.New(newErrString)
|
||||
}
|
||||
|
||||
err = newNodeProvisional.updateParents(virtualClone, newBlockUTXO)
|
||||
// since verifyAndBuildUTXO ran, we know for sure that block is valid -
|
||||
// therefore, if an error occured - this means there's some problem in the
|
||||
// internal structure of block nodes, and it's irrecoverable - therefore
|
||||
// panic
|
||||
|
||||
err = node.updateParents(virtualClone, newBlockUTXO)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed updating parents of %v: %s", node, err)
|
||||
panic(fmt.Errorf("failed updating parents of %v: %s", node, err))
|
||||
}
|
||||
|
||||
// Update the virtual block's children (the DAG tips) to include the new block.
|
||||
virtualClone.AddTip(node)
|
||||
|
||||
// Build a UTXO set for the new virtual block and update the DAG tips' diffs.
|
||||
virtualNodeProvisional := provisionalSet.newProvisionalNode(&virtualClone.blockNode, true, true, nil)
|
||||
newVirtualUTXO, _, err := virtualNodeProvisional.pastUTXO(virtualClone, dag.db)
|
||||
newVirtualUTXO, _, err := virtualClone.blockNode.pastUTXO(virtualClone, dag.db)
|
||||
if err != nil {
|
||||
newErrString := fmt.Sprintf("could not restore past UTXO for virtual %v: %s", virtualClone, err)
|
||||
if err, ok := err.(RuleError); ok {
|
||||
return nil, nil, ruleError(err.ErrorCode, newErrString)
|
||||
}
|
||||
return nil, nil, errors.New(newErrString)
|
||||
panic(fmt.Sprintf("could not restore past UTXO for virtual %v: %s", virtualClone, err))
|
||||
}
|
||||
|
||||
// Apply new utxoDiffs to all the tips
|
||||
err = updateTipsUTXO(virtualNodeProvisional.parents, virtualClone, newVirtualUTXO)
|
||||
err = updateTipsUTXO(virtualClone.parents, virtualClone, newVirtualUTXO)
|
||||
if err != nil {
|
||||
newErrString := fmt.Sprintf("failed updating the tips' UTXO: %s", err)
|
||||
if err, ok := err.(RuleError); ok {
|
||||
return nil, nil, ruleError(err.ErrorCode, newErrString)
|
||||
}
|
||||
return nil, nil, errors.New(newErrString)
|
||||
panic(fmt.Sprintf("failed updating the tips' UTXO: %s", err))
|
||||
}
|
||||
|
||||
// It is now safe to meld the UTXO set to base.
|
||||
@ -684,16 +673,12 @@ func (dag *BlockDAG) applyUTXOChanges(node *blockNode, block *util.Block, fastAd
|
||||
utxoDiff = diffSet.UTXODiff
|
||||
dag.updateVirtualUTXO(diffSet)
|
||||
|
||||
// It is now safe to commit all the provisionalNodes
|
||||
for _, provisional := range provisionalSet {
|
||||
provisional.commit()
|
||||
|
||||
// Set the status to valid for all index nodes to make sure the changes get
|
||||
// written to the database.
|
||||
if provisional != virtualNodeProvisional {
|
||||
dag.index.SetStatusFlags(provisional.original, statusValid)
|
||||
}
|
||||
// Set the status to valid for all index nodes to make sure the changes get
|
||||
// written to the database.
|
||||
for _, p := range node.parents {
|
||||
dag.index.SetStatusFlags(p, statusValid)
|
||||
}
|
||||
dag.index.SetStatusFlags(node, statusValid)
|
||||
|
||||
// It is now safe to apply the new virtual block
|
||||
dag.virtual = virtualClone
|
||||
@ -707,99 +692,29 @@ func (dag *BlockDAG) updateVirtualUTXO(newVirtualUTXODiffSet *DiffUTXOSet) {
|
||||
newVirtualUTXODiffSet.meldToBase()
|
||||
}
|
||||
|
||||
// provisionalNodeSet is a temporary collection of provisionalNodes. It is used exclusively
|
||||
// inside applyUTXOChanges. The purpose of this set is twofold:
|
||||
// 1. Provide easy access to all provisionalNodes created inside this particular call to applyUTXOChanges
|
||||
// 2. Avoid needless recreation of provisionalNodes
|
||||
type provisionalNodeSet map[daghash.Hash]*provisionalNode
|
||||
|
||||
// newProvisionalNodeSet creates an empty provisionalNodeSet
|
||||
func newProvisionalNodeSet() provisionalNodeSet {
|
||||
return provisionalNodeSet{}
|
||||
}
|
||||
|
||||
// provisionalNode is a temporary and partial copy of a blockNode. It is used exclusively
|
||||
// inside applyUTXOChanges. We use this struct instead of the original blockNode because
|
||||
// applyUTXOChanges has a few points of failure which, were we to modify it, would leave the
|
||||
// blockNode in an undefined state.
|
||||
//
|
||||
// Important: provisionalNode.original must be treated as immutable.
|
||||
type provisionalNode struct {
|
||||
original *blockNode
|
||||
selectedParent *provisionalNode
|
||||
parents []*provisionalNode
|
||||
children []*provisionalNode
|
||||
diff *UTXODiff
|
||||
diffChild *provisionalNode
|
||||
transactions []*util.Tx
|
||||
}
|
||||
|
||||
// newProvisionalNode takes a node and builds a provisionalNode from it.
|
||||
// To avoid building the entire DAG in provisionalNode format we pass withParents = true or withChildren = true,
|
||||
// only when the node's relatives (parents or children) are required.
|
||||
func (pns provisionalNodeSet) newProvisionalNode(node *blockNode, withParents bool, withChildren bool,
|
||||
transactions []*util.Tx) *provisionalNode {
|
||||
if existingProvisional, ok := pns[node.hash]; ok {
|
||||
return existingProvisional
|
||||
}
|
||||
|
||||
provisional := &provisionalNode{
|
||||
original: node,
|
||||
transactions: transactions,
|
||||
}
|
||||
if node.hash != zeroHash {
|
||||
pns[node.hash] = provisional
|
||||
}
|
||||
|
||||
if withParents {
|
||||
provisional.parents = []*provisionalNode{}
|
||||
for _, parent := range node.parents {
|
||||
provisional.parents = append(provisional.parents, pns.newProvisionalNode(parent, false, true, nil))
|
||||
}
|
||||
if node.selectedParent != nil {
|
||||
provisional.selectedParent = pns[node.selectedParent.hash]
|
||||
}
|
||||
}
|
||||
|
||||
if withChildren {
|
||||
provisional.children = []*provisionalNode{}
|
||||
for _, child := range node.children {
|
||||
provisional.children = append(provisional.children, pns.newProvisionalNode(child, false, false, nil))
|
||||
}
|
||||
if node.diffChild != nil {
|
||||
provisional.diffChild = pns[node.diffChild.hash]
|
||||
}
|
||||
}
|
||||
|
||||
if node.diff != nil {
|
||||
provisional.diff = node.diff.clone()
|
||||
}
|
||||
|
||||
return provisional
|
||||
}
|
||||
|
||||
// verifyAndBuildUTXO verifies all transactions in the given block (in provisionalNode format) and builds its UTXO
|
||||
func (p *provisionalNode) verifyAndBuildUTXO(virtual *virtualBlock, dag *BlockDAG, fastAdd bool) (utxoSet UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
||||
pastUTXO, pastUTXOaccpetedTxData, err := p.pastUTXO(virtual, dag.db)
|
||||
// verifyAndBuildUTXO verifies all transactions in the given block and builds its UTXO
|
||||
func (node *blockNode) verifyAndBuildUTXO(virtual *virtualBlock, dag *BlockDAG,
|
||||
transactions []*util.Tx, fastAdd bool) (utxoSet UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
||||
pastUTXO, pastUTXOaccpetedTxData, err := node.pastUTXO(virtual, dag.db)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !fastAdd {
|
||||
err = dag.checkConnectToPastUTXO(p, pastUTXO)
|
||||
err = dag.checkConnectToPastUTXO(node, pastUTXO, transactions)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
diff := NewUTXODiff()
|
||||
acceptedTxData = make([]*TxWithBlockHash, 0, len(pastUTXOaccpetedTxData)+len(p.transactions))
|
||||
acceptedTxData = make([]*TxWithBlockHash, 0, len(pastUTXOaccpetedTxData)+len(transactions))
|
||||
if len(pastUTXOaccpetedTxData) != 0 {
|
||||
acceptedTxData = append(acceptedTxData, pastUTXOaccpetedTxData...)
|
||||
}
|
||||
|
||||
for _, tx := range p.transactions {
|
||||
txDiff, err := pastUTXO.diffFromTx(tx.MsgTx(), p.original)
|
||||
for _, tx := range transactions {
|
||||
txDiff, err := pastUTXO.diffFromTx(tx.MsgTx(), node)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -809,7 +724,7 @@ func (p *provisionalNode) verifyAndBuildUTXO(virtual *virtualBlock, dag *BlockDA
|
||||
}
|
||||
acceptedTxData = append(acceptedTxData, &TxWithBlockHash{
|
||||
Tx: tx,
|
||||
InBlock: &p.original.hash,
|
||||
InBlock: &node.hash,
|
||||
})
|
||||
}
|
||||
|
||||
@ -826,9 +741,9 @@ type TxWithBlockHash struct {
|
||||
InBlock *daghash.Hash
|
||||
}
|
||||
|
||||
// pastUTXO returns the UTXO of a given block's (in provisionalNode format) past
|
||||
func (p *provisionalNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastUTXO UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
||||
pastUTXO, err = p.selectedParent.restoreUTXO(virtual)
|
||||
// pastUTXO returns the UTXO of a given block's past
|
||||
func (node *blockNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastUTXO UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
||||
pastUTXO, err = node.selectedParent.restoreUTXO(virtual)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -840,10 +755,10 @@ func (p *provisionalNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastU
|
||||
// Precalculate the amount of transactions in this block's blue set, besides the selected parent.
|
||||
// This is to avoid an attack in which an attacker fabricates a block that will deliberately cause
|
||||
// a lot of copying, causing a high cost to the whole network.
|
||||
blueBlocks := make([]*util.Block, 0, len(p.original.blues)-1)
|
||||
for i := len(p.original.blues) - 1; i >= 0; i-- {
|
||||
blueBlockNode := p.original.blues[i]
|
||||
if blueBlockNode == p.original.selectedParent {
|
||||
blueBlocks := make([]*util.Block, 0, len(node.blues)-1)
|
||||
for i := len(node.blues) - 1; i >= 0; i-- {
|
||||
blueBlockNode := node.blues[i]
|
||||
if blueBlockNode == node.selectedParent {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -874,7 +789,7 @@ func (p *provisionalNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastU
|
||||
// Add all transactions to the pastUTXO
|
||||
// Purposefully ignore failures - these are just unaccepted transactions
|
||||
for _, tx := range blueBlockTransactions {
|
||||
isAccepted := pastUTXO.AddTx(tx.Tx.MsgTx(), p.original.height)
|
||||
isAccepted := pastUTXO.AddTx(tx.Tx.MsgTx(), node.height)
|
||||
if isAccepted {
|
||||
acceptedTxData = append(acceptedTxData, tx)
|
||||
}
|
||||
@ -883,10 +798,10 @@ func (p *provisionalNode) pastUTXO(virtual *virtualBlock, db database.DB) (pastU
|
||||
return pastUTXO, acceptedTxData, nil
|
||||
}
|
||||
|
||||
// restoreUTXO restores the UTXO of a given block (in provisionalNode format) from its diff
|
||||
func (p *provisionalNode) restoreUTXO(virtual *virtualBlock) (UTXOSet, error) {
|
||||
stack := []*provisionalNode{p}
|
||||
current := p
|
||||
// restoreUTXO restores the UTXO of a given block from its diff
|
||||
func (node *blockNode) restoreUTXO(virtual *virtualBlock) (UTXOSet, error) {
|
||||
stack := []*blockNode{node}
|
||||
current := node
|
||||
|
||||
for current.diffChild != nil {
|
||||
current = current.diffChild
|
||||
@ -906,43 +821,41 @@ func (p *provisionalNode) restoreUTXO(virtual *virtualBlock) (UTXOSet, error) {
|
||||
return utxo, nil
|
||||
}
|
||||
|
||||
// updateParents adds this block (in provisionalNode format) to the children sets of its parents
|
||||
// updateParents adds this block to the children sets of its parents
|
||||
// and updates the diff of any parent whose DiffChild is this block
|
||||
func (p *provisionalNode) updateParents(virtual *virtualBlock, newBlockUTXO UTXOSet) error {
|
||||
func (node *blockNode) updateParents(virtual *virtualBlock, newBlockUTXO UTXOSet) error {
|
||||
virtualDiffFromNewBlock, err := virtual.utxoSet.diffFrom(newBlockUTXO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.diff = virtualDiffFromNewBlock
|
||||
node.diff = virtualDiffFromNewBlock
|
||||
|
||||
for _, parent := range p.parents {
|
||||
for _, parent := range node.parents {
|
||||
if parent.diffChild == nil {
|
||||
parentUTXO, err := parent.restoreUTXO(virtual)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parent.diffChild = p
|
||||
parent.diffChild = node
|
||||
parent.diff, err = newBlockUTXO.diffFrom(parentUTXO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
parent.children = append(parent.children, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateTipsUTXO builds and applies new diff UTXOs for all the DAG's tips (in provisionalNode format)
|
||||
func updateTipsUTXO(tipProvisionals []*provisionalNode, virtual *virtualBlock, virtualUTXO UTXOSet) error {
|
||||
for _, tipProvisional := range tipProvisionals {
|
||||
tipUTXO, err := tipProvisional.restoreUTXO(virtual)
|
||||
// updateTipsUTXO builds and applies new diff UTXOs for all the DAG's tips
|
||||
func updateTipsUTXO(tips blockSet, virtual *virtualBlock, virtualUTXO UTXOSet) error {
|
||||
for _, tip := range tips {
|
||||
tipUTXO, err := tip.restoreUTXO(virtual)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tipProvisional.diff, err = virtualUTXO.diffFrom(tipUTXO)
|
||||
tip.diff, err = virtualUTXO.diffFrom(tipUTXO)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -951,36 +864,6 @@ func updateTipsUTXO(tipProvisionals []*provisionalNode, virtual *virtualBlock, v
|
||||
return nil
|
||||
}
|
||||
|
||||
// commit updates the original blockNode this provisionalNode was created from with all the changes made to it
|
||||
func (p *provisionalNode) commit() {
|
||||
if p.selectedParent != nil {
|
||||
p.original.selectedParent = p.selectedParent.original
|
||||
}
|
||||
|
||||
if p.parents != nil {
|
||||
parents := newSet()
|
||||
for _, parent := range p.parents {
|
||||
parents.add(parent.original)
|
||||
}
|
||||
p.original.parents = parents
|
||||
}
|
||||
|
||||
if p.children != nil {
|
||||
children := newSet()
|
||||
for _, child := range p.children {
|
||||
children.add(child.original)
|
||||
}
|
||||
p.original.children = children
|
||||
}
|
||||
|
||||
if p.diff != nil {
|
||||
p.original.diff = p.diff
|
||||
}
|
||||
if p.diffChild != nil {
|
||||
p.original.diffChild = p.diffChild.original
|
||||
}
|
||||
}
|
||||
|
||||
// isCurrent returns whether or not the chain believes it is current. Several
|
||||
// factors are used to guess, but the key factors that allow the chain to
|
||||
// believe it is current are:
|
||||
|
@ -770,6 +770,11 @@ func TestIntervalBlockHashes(t *testing.T) {
|
||||
// The non-error-cases are tested in the more general tests.
|
||||
func TestPastUTXOErrors(t *testing.T) {
|
||||
targetErrorMessage := "dbFetchBlockByNode error"
|
||||
defer func() {
|
||||
if recover() == nil {
|
||||
t.Errorf("Got no panic on past UTXO error, while expected panic")
|
||||
}
|
||||
}()
|
||||
testErrorThroughPatching(
|
||||
t,
|
||||
targetErrorMessage,
|
||||
|
@ -821,6 +821,7 @@ func (dag *BlockDAG) createDAGState() error {
|
||||
// Store the genesis block into the database.
|
||||
return dbStoreBlock(dbTx, genesisBlock)
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -205,15 +205,15 @@ func ValidateTransactionScripts(tx *util.Tx, utxoSet UTXOSet, flags txscript.Scr
|
||||
|
||||
// checkBlockScripts executes and validates the scripts for all transactions in
|
||||
// the passed block using multiple goroutines.
|
||||
func checkBlockScripts(block *provisionalNode, utxoSet UTXOSet, scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache) error {
|
||||
func checkBlockScripts(block *blockNode, utxoSet UTXOSet, transactions []*util.Tx, scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache) error {
|
||||
// Collect all of the transaction inputs and required information for
|
||||
// validation for all transactions in the block into a single slice.
|
||||
numInputs := 0
|
||||
for _, tx := range block.transactions {
|
||||
for _, tx := range transactions {
|
||||
numInputs += len(tx.MsgTx().TxIn)
|
||||
}
|
||||
txValItems := make([]*txValidateItem, 0, numInputs)
|
||||
for _, tx := range block.transactions {
|
||||
for _, tx := range transactions {
|
||||
for txInIdx, txIn := range tx.MsgTx().TxIn {
|
||||
// Skip coinbases.
|
||||
if txIn.PreviousOutPoint.Index == math.MaxUint32 {
|
||||
@ -237,7 +237,7 @@ func checkBlockScripts(block *provisionalNode, utxoSet UTXOSet, scriptFlags txsc
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
|
||||
log.Tracef("block %v took %v to verify", block.original.hash, elapsed)
|
||||
log.Tracef("block %v took %v to verify", block.hash, elapsed)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -40,15 +40,12 @@ func TestCheckBlockScripts(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
node := &provisionalNode{
|
||||
original: &blockNode{
|
||||
hash: *blocks[0].Hash(),
|
||||
},
|
||||
transactions: blocks[0].Transactions(),
|
||||
node := &blockNode{
|
||||
hash: *blocks[0].Hash(),
|
||||
}
|
||||
|
||||
scriptFlags := txscript.ScriptNoFlags
|
||||
err = checkBlockScripts(node, utxoSet, scriptFlags, nil)
|
||||
err = checkBlockScripts(node, utxoSet, blocks[0].Transactions(), scriptFlags, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Transaction script validation failed: %v\n", err)
|
||||
return
|
||||
|
@ -67,7 +67,7 @@ func isNullOutpoint(outpoint *wire.OutPoint) bool {
|
||||
|
||||
// IsCoinBaseTx determines whether or not a transaction is a coinbase. A coinbase
|
||||
// is a special transaction created by miners that has no inputs. This is
|
||||
// represented in the block chain by a transaction with a single input that has
|
||||
// represented in the block dag by a transaction with a single input that has
|
||||
// a previous output transaction index set to the maximum value along with a
|
||||
// zero hash.
|
||||
//
|
||||
@ -91,7 +91,7 @@ func IsCoinBaseTx(msgTx *wire.MsgTx) bool {
|
||||
|
||||
// IsCoinBase determines whether or not a transaction is a coinbase. A coinbase
|
||||
// is a special transaction created by miners that has no inputs. This is
|
||||
// represented in the block chain by a transaction with a single input that has
|
||||
// represented in the block dag by a transaction with a single input that has
|
||||
// a previous output transaction index set to the maximum value along with a
|
||||
// zero hash.
|
||||
//
|
||||
@ -642,13 +642,13 @@ func checkSerializedHeight(coinbaseTx *util.Tx, wantHeight int32) error {
|
||||
}
|
||||
|
||||
// checkBlockHeaderContext performs several validation checks on the block header
|
||||
// which depend on its position within the block chain.
|
||||
// which depend on its position within the block dag.
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFFastAdd: All checks except those involving comparing the header against
|
||||
// the checkpoints are not performed.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
// This function MUST be called with the dag state lock held (for writes).
|
||||
func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestParent *blockNode, blockHeight int32, flags BehaviorFlags) error {
|
||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
||||
if !fastAdd {
|
||||
@ -677,7 +677,7 @@ func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestPar
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure chain matches up to predetermined checkpoints.
|
||||
// Ensure dag matches up to predetermined checkpoints.
|
||||
blockHash := header.BlockHash()
|
||||
if !dag.verifyCheckpoint(blockHeight, &blockHash) {
|
||||
str := fmt.Sprintf("block at height %d does not match "+
|
||||
@ -686,7 +686,7 @@ func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestPar
|
||||
}
|
||||
|
||||
// Find the previous checkpoint and prevent blocks which fork the main
|
||||
// chain before it. This prevents storage of new, otherwise valid,
|
||||
// dag before it. This prevents storage of new, otherwise valid,
|
||||
// blocks which build off of old blocks that are likely at a much easier
|
||||
// difficulty and therefore could be used to waste cache and disk space.
|
||||
checkpointNode, err := dag.findPreviousCheckpoint()
|
||||
@ -694,7 +694,7 @@ func (dag *BlockDAG) checkBlockHeaderContext(header *wire.BlockHeader, bluestPar
|
||||
return err
|
||||
}
|
||||
if checkpointNode != nil && blockHeight < checkpointNode.height {
|
||||
str := fmt.Sprintf("block at height %d forks the main chain "+
|
||||
str := fmt.Sprintf("block at height %d forks the main dag "+
|
||||
"before the previous checkpoint at height %d",
|
||||
blockHeight, checkpointNode.height)
|
||||
return ruleError(ErrForkTooOld, str)
|
||||
@ -740,7 +740,7 @@ func validateParents(blockHeader *wire.BlockHeader, parents blockSet) error {
|
||||
}
|
||||
|
||||
// checkBlockContext peforms several validation checks on the block which depend
|
||||
// on its position within the block chain.
|
||||
// on its position within the block dag.
|
||||
//
|
||||
// The flags modify the behavior of this function as follows:
|
||||
// - BFFastAdd: The transaction are not checked to see if they are finalized
|
||||
@ -749,7 +749,7 @@ func validateParents(blockHeader *wire.BlockHeader, parents blockSet) error {
|
||||
// The flags are also passed to checkBlockHeaderContext. See its documentation
|
||||
// for how the flags modify its behavior.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for writes).
|
||||
// This function MUST be called with the dag state lock held (for writes).
|
||||
func (dag *BlockDAG) checkBlockContext(block *util.Block, parents blockSet, bluestParent *blockNode, flags BehaviorFlags) error {
|
||||
err := validateParents(&block.MsgBlock().Header, parents)
|
||||
if err != nil {
|
||||
@ -801,12 +801,13 @@ func (dag *BlockDAG) checkBlockContext(block *util.Block, parents blockSet, blue
|
||||
// https://github.com/bitcoin/bips/blob/master/bip-0030.mediawiki and
|
||||
// http://r6.ca/blog/20120206T005236Z.html.
|
||||
//
|
||||
// This function MUST be called with the chain state lock held (for reads).
|
||||
func ensureNoDuplicateTx(block *provisionalNode, utxoSet UTXOSet) error {
|
||||
// This function MUST be called with the dag state lock held (for reads).
|
||||
func ensureNoDuplicateTx(block *blockNode, utxoSet UTXOSet,
|
||||
transactions []*util.Tx) error {
|
||||
// Fetch utxos for all of the transaction ouputs in this block.
|
||||
// Typically, there will not be any utxos for any of the outputs.
|
||||
fetchSet := make(map[wire.OutPoint]struct{})
|
||||
for _, tx := range block.transactions {
|
||||
for _, tx := range transactions {
|
||||
prevOut := wire.OutPoint{Hash: *tx.Hash()}
|
||||
for txOutIdx := range tx.MsgTx().TxOut {
|
||||
prevOut.Index = uint32(txOutIdx)
|
||||
@ -939,9 +940,10 @@ func CheckTransactionInputs(tx *util.Tx, txHeight int32, utxoSet UTXOSet, dagPar
|
||||
// block subsidy, or fail transaction script validation.
|
||||
//
|
||||
// This function MUST be called with the dag state lock held (for writes).
|
||||
func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTXOSet) error {
|
||||
func (dag *BlockDAG) checkConnectToPastUTXO(block *blockNode, pastUTXO UTXOSet,
|
||||
transactions []*util.Tx) error {
|
||||
|
||||
err := ensureNoDuplicateTx(block, pastUTXO)
|
||||
err := ensureNoDuplicateTx(block, pastUTXO, transactions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -953,7 +955,7 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTX
|
||||
// signature operations in each of the input transaction public key
|
||||
// scripts.
|
||||
totalSigOps := 0
|
||||
for i, tx := range block.transactions {
|
||||
for i, tx := range transactions {
|
||||
numsigOps := CountSigOps(tx)
|
||||
// Since the first (and only the first) transaction has
|
||||
// already been verified to be a coinbase transaction,
|
||||
@ -987,8 +989,8 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTX
|
||||
// against all the inputs when the signature operations are out of
|
||||
// bounds.
|
||||
var totalFees uint64
|
||||
for _, tx := range block.transactions {
|
||||
txFee, err := CheckTransactionInputs(tx, block.original.height, pastUTXO,
|
||||
for _, tx := range transactions {
|
||||
txFee, err := CheckTransactionInputs(tx, block.height, pastUTXO,
|
||||
dag.dagParams)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1010,10 +1012,10 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTX
|
||||
// errors here because those error conditions would have already been
|
||||
// caught by checkTransactionSanity.
|
||||
var totalSatoshiOut uint64
|
||||
for _, txOut := range block.transactions[0].MsgTx().TxOut {
|
||||
for _, txOut := range transactions[0].MsgTx().TxOut {
|
||||
totalSatoshiOut += txOut.Value
|
||||
}
|
||||
expectedSatoshiOut := CalcBlockSubsidy(block.original.height, dag.dagParams) +
|
||||
expectedSatoshiOut := CalcBlockSubsidy(block.height, dag.dagParams) +
|
||||
totalFees
|
||||
if totalSatoshiOut > expectedSatoshiOut {
|
||||
str := fmt.Sprintf("coinbase transaction for block pays %v "+
|
||||
@ -1030,7 +1032,7 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTX
|
||||
// portion of block handling.
|
||||
checkpoint := dag.LatestCheckpoint()
|
||||
runScripts := true
|
||||
if checkpoint != nil && block.original.height <= checkpoint.Height {
|
||||
if checkpoint != nil && block.height <= checkpoint.Height {
|
||||
runScripts = false
|
||||
}
|
||||
|
||||
@ -1038,20 +1040,20 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTX
|
||||
|
||||
// We obtain the MTP of the *previous* block in order to
|
||||
// determine if transactions in the current block are final.
|
||||
medianTime := block.original.selectedParent.CalcPastMedianTime()
|
||||
medianTime := block.selectedParent.CalcPastMedianTime()
|
||||
|
||||
// We also enforce the relative sequence number based
|
||||
// lock-times within the inputs of all transactions in this
|
||||
// candidate block.
|
||||
for _, tx := range block.transactions {
|
||||
for _, tx := range transactions {
|
||||
// A transaction can only be included within a block
|
||||
// once the sequence locks of *all* its inputs are
|
||||
// active.
|
||||
sequenceLock, err := dag.calcSequenceLock(block.original, pastUTXO, tx, false)
|
||||
sequenceLock, err := dag.calcSequenceLock(block, pastUTXO, tx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !SequenceLockActive(sequenceLock, block.original.height,
|
||||
if !SequenceLockActive(sequenceLock, block.height,
|
||||
medianTime) {
|
||||
str := fmt.Sprintf("block contains " +
|
||||
"transaction whose input sequence " +
|
||||
@ -1065,7 +1067,7 @@ func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTX
|
||||
// expensive ECDSA signature check scripts. Doing this last helps
|
||||
// prevent CPU exhaustion attacks.
|
||||
if runScripts {
|
||||
err := checkBlockScripts(block, pastUTXO, scriptFlags, dag.sigCache)
|
||||
err := checkBlockScripts(block, pastUTXO, transactions, scriptFlags, dag.sigCache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1085,8 +1087,8 @@ func countSpentOutputs(block *util.Block) int {
|
||||
}
|
||||
|
||||
// CheckConnectBlockTemplate fully validates that connecting the passed block to
|
||||
// the main chain does not violate any consensus rules, aside from the proof of
|
||||
// work requirement. The block must connect to the current tip of the main chain.
|
||||
// the main dag does not violate any consensus rules, aside from the proof of
|
||||
// work requirement. The block must connect to the current tip of the main dag.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) CheckConnectBlockTemplate(block *util.Block) error {
|
||||
@ -1097,7 +1099,7 @@ func (dag *BlockDAG) CheckConnectBlockTemplate(block *util.Block) error {
|
||||
flags := BFNoPoWCheck
|
||||
|
||||
// This only checks whether the block can be connected to the tip of the
|
||||
// current chain.
|
||||
// current dag.
|
||||
tips := dag.virtual.tips()
|
||||
header := block.MsgBlock().Header
|
||||
parentHashes := header.ParentHashes
|
||||
@ -1122,9 +1124,6 @@ func (dag *BlockDAG) CheckConnectBlockTemplate(block *util.Block) error {
|
||||
return err
|
||||
}
|
||||
|
||||
newProvisionalNode := &provisionalNode{
|
||||
original: newBlockNode(&header, dag.virtual.tips(), dag.dagParams.K),
|
||||
transactions: block.Transactions(),
|
||||
}
|
||||
return dag.checkConnectToPastUTXO(newProvisionalNode, dag.UTXOSet())
|
||||
return dag.checkConnectToPastUTXO(newBlockNode(&header, dag.virtual.tips(), dag.dagParams.K),
|
||||
dag.UTXOSet(), block.Transactions())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user