[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:
Evgeny Khirin 2019-01-14 16:28:05 +02:00 committed by stasatdaglabs
parent e3994cddac
commit 3a5abb6584
8 changed files with 117 additions and 208 deletions

View File

@ -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
}

View File

@ -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.

View File

@ -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:

View File

@ -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,

View File

@ -821,6 +821,7 @@ func (dag *BlockDAG) createDAGState() error {
// Store the genesis block into the database.
return dbStoreBlock(dbTx, genesisBlock)
})
return err
}

View File

@ -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
}

View File

@ -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

View File

@ -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())
}