diff --git a/blockdag/accept.go b/blockdag/accept.go index d15e8ef1f..ec6251d9d 100644 --- a/blockdag/accept.go +++ b/blockdag/accept.go @@ -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 } diff --git a/blockdag/blocknode.go b/blockdag/blocknode.go index a4f5a4671..ce4232ac8 100644 --- a/blockdag/blocknode.go +++ b/blockdag/blocknode.go @@ -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. diff --git a/blockdag/dag.go b/blockdag/dag.go index 8e2793b94..e4d38790e 100644 --- a/blockdag/dag.go +++ b/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: diff --git a/blockdag/dag_test.go b/blockdag/dag_test.go index 08ff87d42..89d991e50 100644 --- a/blockdag/dag_test.go +++ b/blockdag/dag_test.go @@ -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, diff --git a/blockdag/dagio.go b/blockdag/dagio.go index 0c2a8a68a..abdac5ca0 100644 --- a/blockdag/dagio.go +++ b/blockdag/dagio.go @@ -821,6 +821,7 @@ func (dag *BlockDAG) createDAGState() error { // Store the genesis block into the database. return dbStoreBlock(dbTx, genesisBlock) }) + return err } diff --git a/blockdag/scriptval.go b/blockdag/scriptval.go index c20a3a871..168d391e2 100644 --- a/blockdag/scriptval.go +++ b/blockdag/scriptval.go @@ -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 } diff --git a/blockdag/scriptval_test.go b/blockdag/scriptval_test.go index 725244e8d..b179adc94 100644 --- a/blockdag/scriptval_test.go +++ b/blockdag/scriptval_test.go @@ -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 diff --git a/blockdag/validate.go b/blockdag/validate.go index 8b5078282..fbe987f23 100644 --- a/blockdag/validate.go +++ b/blockdag/validate.go @@ -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()) }