mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-10-14 00:59:33 +00:00
[DEV-255] create checkConnectToPastUTXO and move the required functionalities to it from checkConnectBlock1)
* [DEV-255] create checkConnectToPastUTXO and move the required functionalities to it from checkConnectBlock * [DEV-255] get rid of checkConnectBlock * [DEV-255] rename pNode -> node * [DEV-255] add comment to describe ErrWithDiff
This commit is contained in:
parent
3ff2ef19e4
commit
3ebded9ae7
@ -5,6 +5,7 @@
|
|||||||
package blockdag
|
package blockdag
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
@ -446,37 +447,21 @@ func (dag *BlockDAG) connectToDAG(node *blockNode, parentNodes blockSet, block *
|
|||||||
// Skip checks if node has already been fully validated.
|
// Skip checks if node has already been fully validated.
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd || dag.index.NodeStatus(node).KnownValid()
|
fastAdd := flags&BFFastAdd == BFFastAdd || dag.index.NodeStatus(node).KnownValid()
|
||||||
|
|
||||||
// Perform several checks to verify the block can be connected
|
// Connect the block to the DAG.
|
||||||
// to the DAG without violating any rules and without actually
|
err := dag.connectBlock(node, block, fastAdd)
|
||||||
// connecting the block.
|
if _, ok := err.(RuleError); ok {
|
||||||
if !fastAdd {
|
dag.index.SetStatusFlags(node, statusValidateFailed)
|
||||||
err := dag.checkConnectBlock(node, block)
|
} else {
|
||||||
if err == nil {
|
return err
|
||||||
dag.index.SetStatusFlags(node, statusValid)
|
|
||||||
} else if _, ok := err.(RuleError); ok {
|
|
||||||
dag.index.SetStatusFlags(node, statusValidateFailed)
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally ignore errors writing updated node status to DB. If
|
|
||||||
// it fails to write, it's not the end of the world. If the block is
|
|
||||||
// valid, we flush in connectBlock and if the block is invalid, the
|
|
||||||
// worst that can happen is we revalidate the block after a restart.
|
|
||||||
if writeErr := dag.index.flushToDB(); writeErr != nil {
|
|
||||||
log.Warnf("Error flushing block index changes to disk: %v",
|
|
||||||
writeErr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect the block to the DAG.
|
// Intentionally ignore errors writing updated node status to DB. If
|
||||||
err := dag.connectBlock(node, block)
|
// it fails to write, it's not the end of the world. If the block is
|
||||||
if err != nil {
|
// invalid, the worst that can happen is we revalidate the block
|
||||||
return err
|
// after a restart.
|
||||||
|
if writeErr := dag.index.flushToDB(); writeErr != nil {
|
||||||
|
log.Warnf("Error flushing block index changes to disk: %v",
|
||||||
|
writeErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -485,7 +470,14 @@ func (dag *BlockDAG) connectToDAG(node *blockNode, parentNodes blockSet, block *
|
|||||||
// connectBlock handles connecting the passed node/block to the DAG.
|
// connectBlock handles connecting the passed node/block to the DAG.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
// This function MUST be called with the chain state lock held (for writes).
|
||||||
func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block) error {
|
func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block, fastAdd bool) error {
|
||||||
|
// The coinbase for the Genesis block is not spendable, so just return
|
||||||
|
// an error now.
|
||||||
|
if node.hash.IsEqual(dag.dagParams.GenesisHash) {
|
||||||
|
str := "the coinbase for the genesis block is not spendable"
|
||||||
|
return ruleError(ErrMissingTxOut, str)
|
||||||
|
}
|
||||||
|
|
||||||
// No warnings about unknown rules or versions until the DAG is
|
// No warnings about unknown rules or versions until the DAG is
|
||||||
// current.
|
// current.
|
||||||
if dag.isCurrent() {
|
if dag.isCurrent() {
|
||||||
@ -503,7 +495,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the node to the virtual and update the UTXO set of the DAG.
|
// Add the node to the virtual and update the UTXO set of the DAG.
|
||||||
utxoDiff, acceptedTxsData, err := dag.applyUTXOChanges(node, block)
|
utxoDiff, acceptedTxsData, err := dag.applyUTXOChanges(node, block, fastAdd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -570,7 +562,7 @@ func (dag *BlockDAG) connectBlock(node *blockNode, block *util.Block) error {
|
|||||||
// 5. Updates each of the tips' utxoDiff.
|
// 5. Updates each of the tips' utxoDiff.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
// This function MUST be called with the chain state lock held (for writes).
|
||||||
func (dag *BlockDAG) applyUTXOChanges(node *blockNode, block *util.Block) (utxoDiff *UTXODiff, acceptedTxData []*TxWithBlockHash, err error) {
|
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.
|
// 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
|
// 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)
|
// fail if the block is not valid, thus bringing all the affected nodes (and the virtual)
|
||||||
@ -581,9 +573,13 @@ func (dag *BlockDAG) applyUTXOChanges(node *blockNode, block *util.Block) (utxoD
|
|||||||
// Clone the virtual block so that we don't modify the existing one.
|
// Clone the virtual block so that we don't modify the existing one.
|
||||||
virtualClone := dag.virtual.clone()
|
virtualClone := dag.virtual.clone()
|
||||||
|
|
||||||
newBlockUTXO, acceptedTxData, err := newNodeProvisional.verifyAndBuildUTXO(virtualClone, dag.db)
|
newBlockUTXO, acceptedTxData, err := newNodeProvisional.verifyAndBuildUTXO(virtualClone, dag, fastAdd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error verifying UTXO for %v: %s", node, err)
|
newErrString := fmt.Sprintf("error verifying UTXO for %v: %s", node, err)
|
||||||
|
if err, ok := err.(RuleError); ok {
|
||||||
|
return nil, nil, ruleError(err.ErrorCode, newErrString)
|
||||||
|
}
|
||||||
|
return nil, nil, errors.New(newErrString)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = newNodeProvisional.updateParents(virtualClone, newBlockUTXO)
|
err = newNodeProvisional.updateParents(virtualClone, newBlockUTXO)
|
||||||
@ -598,13 +594,21 @@ func (dag *BlockDAG) applyUTXOChanges(node *blockNode, block *util.Block) (utxoD
|
|||||||
virtualNodeProvisional := provisionalSet.newProvisionalNode(&virtualClone.blockNode, true, nil)
|
virtualNodeProvisional := provisionalSet.newProvisionalNode(&virtualClone.blockNode, true, nil)
|
||||||
newVirtualUTXO, _, err := virtualNodeProvisional.pastUTXO(virtualClone, dag.db)
|
newVirtualUTXO, _, err := virtualNodeProvisional.pastUTXO(virtualClone, dag.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("could not restore past UTXO for virtual %v: %s", virtualClone, err)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply new utxoDiffs to all the tips
|
// Apply new utxoDiffs to all the tips
|
||||||
err = updateTipsUTXO(virtualNodeProvisional.parents, virtualClone, newVirtualUTXO)
|
err = updateTipsUTXO(virtualNodeProvisional.parents, virtualClone, newVirtualUTXO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed updating the tips' UTXO: %s", err)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// It is now safe to meld the UTXO set to base.
|
// It is now safe to meld the UTXO set to base.
|
||||||
@ -704,12 +708,19 @@ func (pns provisionalNodeSet) newProvisionalNode(node *blockNode, withRelatives
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verifyAndBuildUTXO verifies all transactions in the given block (in provisionalNode format) and builds its UTXO
|
// verifyAndBuildUTXO verifies all transactions in the given block (in provisionalNode format) and builds its UTXO
|
||||||
func (p *provisionalNode) verifyAndBuildUTXO(virtual *virtualBlock, db database.DB) (utxoSet UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
func (p *provisionalNode) verifyAndBuildUTXO(virtual *virtualBlock, dag *BlockDAG, fastAdd bool) (utxoSet UTXOSet, acceptedTxData []*TxWithBlockHash, err error) {
|
||||||
pastUTXO, pastUTXOaccpetedTxData, err := p.pastUTXO(virtual, db)
|
pastUTXO, pastUTXOaccpetedTxData, err := p.pastUTXO(virtual, dag.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !fastAdd {
|
||||||
|
err = dag.checkConnectToPastUTXO(p, pastUTXO)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diff := NewUTXODiff()
|
diff := NewUTXODiff()
|
||||||
acceptedTxData = make([]*TxWithBlockHash, 0, len(pastUTXOaccpetedTxData)+len(p.transactions))
|
acceptedTxData = make([]*TxWithBlockHash, 0, len(pastUTXOaccpetedTxData)+len(p.transactions))
|
||||||
if len(pastUTXOaccpetedTxData) != 0 {
|
if len(pastUTXOaccpetedTxData) != 0 {
|
||||||
|
@ -205,6 +205,9 @@ const (
|
|||||||
// current chain tip. This is not a block validation rule, but is required
|
// current chain tip. This is not a block validation rule, but is required
|
||||||
// for block proposals submitted via getblocktemplate RPC.
|
// for block proposals submitted via getblocktemplate RPC.
|
||||||
ErrPrevBlockNotBest
|
ErrPrevBlockNotBest
|
||||||
|
|
||||||
|
// ErrWithDiff indicates that there was an error with UTXOSet.WithDiff
|
||||||
|
ErrWithDiff
|
||||||
)
|
)
|
||||||
|
|
||||||
// Map of ErrorCode values back to their constant names for pretty printing.
|
// Map of ErrorCode values back to their constant names for pretty printing.
|
||||||
@ -248,6 +251,7 @@ var errorCodeStrings = map[ErrorCode]string{
|
|||||||
ErrPreviousBlockUnknown: "ErrPreviousBlockUnknown",
|
ErrPreviousBlockUnknown: "ErrPreviousBlockUnknown",
|
||||||
ErrInvalidAncestorBlock: "ErrInvalidAncestorBlock",
|
ErrInvalidAncestorBlock: "ErrInvalidAncestorBlock",
|
||||||
ErrPrevBlockNotBest: "ErrPrevBlockNotBest",
|
ErrPrevBlockNotBest: "ErrPrevBlockNotBest",
|
||||||
|
ErrWithDiff: "ErrWithDiff",
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the ErrorCode as a human-readable name.
|
// String returns the ErrorCode as a human-readable name.
|
||||||
|
@ -54,6 +54,7 @@ func TestErrorCodeStringer(t *testing.T) {
|
|||||||
{ErrPreviousBlockUnknown, "ErrPreviousBlockUnknown"},
|
{ErrPreviousBlockUnknown, "ErrPreviousBlockUnknown"},
|
||||||
{ErrInvalidAncestorBlock, "ErrInvalidAncestorBlock"},
|
{ErrInvalidAncestorBlock, "ErrInvalidAncestorBlock"},
|
||||||
{ErrPrevBlockNotBest, "ErrPrevBlockNotBest"},
|
{ErrPrevBlockNotBest, "ErrPrevBlockNotBest"},
|
||||||
|
{ErrWithDiff, "ErrWithDiff"},
|
||||||
{0xffff, "Unknown ErrorCode (65535)"},
|
{0xffff, "Unknown ErrorCode (65535)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,15 +205,15 @@ func ValidateTransactionScripts(tx *util.Tx, utxoSet UTXOSet, flags txscript.Scr
|
|||||||
|
|
||||||
// checkBlockScripts executes and validates the scripts for all transactions in
|
// checkBlockScripts executes and validates the scripts for all transactions in
|
||||||
// the passed block using multiple goroutines.
|
// the passed block using multiple goroutines.
|
||||||
func checkBlockScripts(block *util.Block, utxoSet UTXOSet, scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache) error {
|
func checkBlockScripts(block *provisionalNode, utxoSet UTXOSet, scriptFlags txscript.ScriptFlags, sigCache *txscript.SigCache) error {
|
||||||
// Collect all of the transaction inputs and required information for
|
// Collect all of the transaction inputs and required information for
|
||||||
// validation for all transactions in the block into a single slice.
|
// validation for all transactions in the block into a single slice.
|
||||||
numInputs := 0
|
numInputs := 0
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.transactions {
|
||||||
numInputs += len(tx.MsgTx().TxIn)
|
numInputs += len(tx.MsgTx().TxIn)
|
||||||
}
|
}
|
||||||
txValItems := make([]*txValidateItem, 0, numInputs)
|
txValItems := make([]*txValidateItem, 0, numInputs)
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.transactions {
|
||||||
for txInIdx, txIn := range tx.MsgTx().TxIn {
|
for txInIdx, txIn := range tx.MsgTx().TxIn {
|
||||||
// Skip coinbases.
|
// Skip coinbases.
|
||||||
if txIn.PreviousOutPoint.Index == math.MaxUint32 {
|
if txIn.PreviousOutPoint.Index == math.MaxUint32 {
|
||||||
@ -237,7 +237,7 @@ func checkBlockScripts(block *util.Block, utxoSet UTXOSet, scriptFlags txscript.
|
|||||||
}
|
}
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
|
|
||||||
log.Tracef("block %v took %v to verify", block.Hash(), elapsed)
|
log.Tracef("block %v took %v to verify", block.original.hash, elapsed)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,15 @@ func TestCheckBlockScripts(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node := &provisionalNode{
|
||||||
|
original: &blockNode{
|
||||||
|
hash: *blocks[0].Hash(),
|
||||||
|
},
|
||||||
|
transactions: blocks[0].Transactions(),
|
||||||
|
}
|
||||||
|
|
||||||
scriptFlags := txscript.ScriptNoFlags
|
scriptFlags := txscript.ScriptNoFlags
|
||||||
err = checkBlockScripts(blocks[0], utxoSet, scriptFlags, nil)
|
err = checkBlockScripts(node, utxoSet, scriptFlags, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Transaction script validation failed: %v\n", err)
|
t.Errorf("Transaction script validation failed: %v\n", err)
|
||||||
return
|
return
|
||||||
|
@ -257,7 +257,7 @@ func (d *UTXODiff) WithDiff(diff *UTXODiff) (*UTXODiff, error) {
|
|||||||
result.toAdd.add(outPoint, utxoEntry)
|
result.toAdd.add(outPoint, utxoEntry)
|
||||||
}
|
}
|
||||||
if diff.toAdd.contains(outPoint) {
|
if diff.toAdd.contains(outPoint) {
|
||||||
return nil, errors.New("WithDiff: transaction both in d.toAdd and in other.toAdd")
|
return nil, ruleError(ErrWithDiff, "WithDiff: transaction both in d.toAdd and in other.toAdd")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ func (d *UTXODiff) WithDiff(diff *UTXODiff) (*UTXODiff, error) {
|
|||||||
result.toRemove.add(outPoint, utxoEntry)
|
result.toRemove.add(outPoint, utxoEntry)
|
||||||
}
|
}
|
||||||
if diff.toRemove.contains(outPoint) {
|
if diff.toRemove.contains(outPoint) {
|
||||||
return nil, errors.New("WithDiff: transaction both in d.toRemove and in other.toRemove")
|
return nil, ruleError(ErrWithDiff, "WithDiff: transaction both in d.toRemove and in other.toRemove")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,9 +367,9 @@ func diffFromTx(u UTXOSet, tx *wire.MsgTx, containingNode *blockNode) (*UTXODiff
|
|||||||
if entry, ok := u.Get(txIn.PreviousOutPoint); ok {
|
if entry, ok := u.Get(txIn.PreviousOutPoint); ok {
|
||||||
diff.toRemove.add(txIn.PreviousOutPoint, entry)
|
diff.toRemove.add(txIn.PreviousOutPoint, entry)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf(
|
return nil, ruleError(ErrMissingTxOut, fmt.Sprintf(
|
||||||
"Transaction %s is invalid because spends outpoint %s that is not in utxo set",
|
"Transaction %s is invalid because spends outpoint %s that is not in utxo set",
|
||||||
tx.TxHash(), txIn.PreviousOutPoint)
|
tx.TxHash(), txIn.PreviousOutPoint))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -848,6 +848,14 @@ func TestApplyUTXOChanges(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer teardownFunc()
|
defer teardownFunc()
|
||||||
|
|
||||||
|
dag.dagParams.CoinbaseMaturity = 1
|
||||||
|
// Create artificial checkpoint in order to prevent script validation for blocks that comes before it
|
||||||
|
dag.checkpoints = []dagconfig.Checkpoint{
|
||||||
|
{
|
||||||
|
Height: 1000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
cbTx, err := createCoinbaseTx(1, 1)
|
cbTx, err := createCoinbaseTx(1, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("createCoinbaseTx: %v", err)
|
t.Errorf("createCoinbaseTx: %v", err)
|
||||||
@ -878,7 +886,7 @@ func TestApplyUTXOChanges(t *testing.T) {
|
|||||||
initBlockNode(&node1, blockHeader, setFromSlice(dag.genesis), dagconfig.MainNetParams.K)
|
initBlockNode(&node1, blockHeader, setFromSlice(dag.genesis), dagconfig.MainNetParams.K)
|
||||||
|
|
||||||
//Checks that dag.applyUTXOChanges fails because we don't allow a transaction to spend another transaction from the same block
|
//Checks that dag.applyUTXOChanges fails because we don't allow a transaction to spend another transaction from the same block
|
||||||
_, _, err = dag.applyUTXOChanges(&node1, block1)
|
_, _, err = dag.applyUTXOChanges(&node1, block1, false)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("applyUTXOChanges expected an error\n")
|
t.Errorf("applyUTXOChanges expected an error\n")
|
||||||
}
|
}
|
||||||
@ -905,7 +913,7 @@ func TestApplyUTXOChanges(t *testing.T) {
|
|||||||
initBlockNode(&node2, blockHeader, setFromSlice(dag.genesis), dagconfig.MainNetParams.K)
|
initBlockNode(&node2, blockHeader, setFromSlice(dag.genesis), dagconfig.MainNetParams.K)
|
||||||
|
|
||||||
//Checks that dag.applyUTXOChanges doesn't fail because all of its transaction are dependant on transactions from previous blocks
|
//Checks that dag.applyUTXOChanges doesn't fail because all of its transaction are dependant on transactions from previous blocks
|
||||||
_, _, err = dag.applyUTXOChanges(&node2, block2)
|
_, _, err = dag.applyUTXOChanges(&node2, block2, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("applyUTXOChanges: %v", err)
|
t.Errorf("applyUTXOChanges: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -778,11 +778,11 @@ func (dag *BlockDAG) checkBlockContext(block *util.Block, parents blockSet, blue
|
|||||||
// http://r6.ca/blog/20120206T005236Z.html.
|
// http://r6.ca/blog/20120206T005236Z.html.
|
||||||
//
|
//
|
||||||
// This function MUST be called with the chain state lock held (for reads).
|
// This function MUST be called with the chain state lock held (for reads).
|
||||||
func (dag *BlockDAG) ensureNoDuplicateTx(node *blockNode, block *util.Block) error {
|
func ensureNoDuplicateTx(block *provisionalNode, utxoSet UTXOSet) error {
|
||||||
// Fetch utxos for all of the transaction ouputs in this block.
|
// Fetch utxos for all of the transaction ouputs in this block.
|
||||||
// Typically, there will not be any utxos for any of the outputs.
|
// Typically, there will not be any utxos for any of the outputs.
|
||||||
fetchSet := make(map[wire.OutPoint]struct{})
|
fetchSet := make(map[wire.OutPoint]struct{})
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.transactions {
|
||||||
prevOut := wire.OutPoint{Hash: *tx.Hash()}
|
prevOut := wire.OutPoint{Hash: *tx.Hash()}
|
||||||
for txOutIdx := range tx.MsgTx().TxOut {
|
for txOutIdx := range tx.MsgTx().TxOut {
|
||||||
prevOut.Index = uint32(txOutIdx)
|
prevOut.Index = uint32(txOutIdx)
|
||||||
@ -793,7 +793,7 @@ func (dag *BlockDAG) ensureNoDuplicateTx(node *blockNode, block *util.Block) err
|
|||||||
// Duplicate transactions are only allowed if the previous transaction
|
// Duplicate transactions are only allowed if the previous transaction
|
||||||
// is fully spent.
|
// is fully spent.
|
||||||
for outpoint := range fetchSet {
|
for outpoint := range fetchSet {
|
||||||
utxo, ok := dag.GetUTXOEntry(outpoint)
|
utxo, ok := utxoSet.Get(outpoint)
|
||||||
if ok {
|
if ok {
|
||||||
str := fmt.Sprintf("tried to overwrite transaction %v "+
|
str := fmt.Sprintf("tried to overwrite transaction %v "+
|
||||||
"at block height %d that is not fully spent",
|
"at block height %d that is not fully spent",
|
||||||
@ -905,12 +905,8 @@ func CheckTransactionInputs(tx *util.Tx, txHeight int32, utxoSet UTXOSet, dagPar
|
|||||||
return txFeeInSatoshi, nil
|
return txFeeInSatoshi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkConnectBlock performs several checks to confirm connecting the passed
|
// checkConnectToPastUTXO performs several checks to confirm connecting the passed
|
||||||
// block to the chain represented by the passed view does not violate any rules.
|
// block to the DAG represented by the passed view does not violate any rules.
|
||||||
// In addition, the passed view is updated to spend all of the referenced
|
|
||||||
// outputs and add all of the new utxos created by block. Thus, the view will
|
|
||||||
// represent the state of the chain as if the block were actually connected and
|
|
||||||
// consequently the best hash for the view is also updated to passed block.
|
|
||||||
//
|
//
|
||||||
// An example of some of the checks performed are ensuring connecting the block
|
// An example of some of the checks performed are ensuring connecting the block
|
||||||
// would not cause any duplicate transaction hashes for old transactions that
|
// would not cause any duplicate transaction hashes for old transactions that
|
||||||
@ -918,29 +914,10 @@ func CheckTransactionInputs(tx *util.Tx, txHeight int32, utxoSet UTXOSet, dagPar
|
|||||||
// signature operations per block, invalid values in relation to the expected
|
// signature operations per block, invalid values in relation to the expected
|
||||||
// block subsidy, or fail transaction script validation.
|
// block subsidy, or fail transaction script validation.
|
||||||
//
|
//
|
||||||
// The CheckConnectBlockTemplate function makes use of this function to perform
|
// This function MUST be called with the dag state lock held (for writes).
|
||||||
// the bulk of its work. The only difference is this function accepts a node
|
func (dag *BlockDAG) checkConnectToPastUTXO(block *provisionalNode, pastUTXO UTXOSet) error {
|
||||||
// which may or may not require reorganization to connect it to the main chain
|
|
||||||
// whereas CheckConnectBlockTemplate creates a new node which specifically
|
|
||||||
// connects to the end of the current main chain and then calls this function
|
|
||||||
// with that node.
|
|
||||||
//
|
|
||||||
// This function MUST be called with the chain state lock held (for writes).
|
|
||||||
func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error {
|
|
||||||
// If the side chain blocks end up in the database, a call to
|
|
||||||
// CheckBlockSanity should be done here in case a previous version
|
|
||||||
// allowed a block that is no longer valid. However, since the
|
|
||||||
// implementation only currently uses memory for the side chain blocks,
|
|
||||||
// it isn't currently necessary.
|
|
||||||
|
|
||||||
// The coinbase for the Genesis block is not spendable, so just return
|
err := ensureNoDuplicateTx(block, pastUTXO)
|
||||||
// an error now.
|
|
||||||
if node.hash.IsEqual(dag.dagParams.GenesisHash) {
|
|
||||||
str := "the coinbase for the genesis block is not spendable"
|
|
||||||
return ruleError(ErrMissingTxOut, str)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := dag.ensureNoDuplicateTx(node, block)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -951,9 +928,8 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
// expands the count to include a precise count of pay-to-script-hash
|
// expands the count to include a precise count of pay-to-script-hash
|
||||||
// signature operations in each of the input transaction public key
|
// signature operations in each of the input transaction public key
|
||||||
// scripts.
|
// scripts.
|
||||||
transactions := block.Transactions()
|
|
||||||
totalSigOps := 0
|
totalSigOps := 0
|
||||||
for i, tx := range transactions {
|
for i, tx := range block.transactions {
|
||||||
numsigOps := CountSigOps(tx)
|
numsigOps := CountSigOps(tx)
|
||||||
// Since the first (and only the first) transaction has
|
// Since the first (and only the first) transaction has
|
||||||
// already been verified to be a coinbase transaction,
|
// already been verified to be a coinbase transaction,
|
||||||
@ -961,7 +937,7 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
// countP2SHSigOps for whether or not the transaction is
|
// countP2SHSigOps for whether or not the transaction is
|
||||||
// a coinbase transaction rather than having to do a
|
// a coinbase transaction rather than having to do a
|
||||||
// full coinbase check again.
|
// full coinbase check again.
|
||||||
numP2SHSigOps, err := CountP2SHSigOps(tx, i == 0, dag.virtual.utxoSet)
|
numP2SHSigOps, err := CountP2SHSigOps(tx, i == 0, pastUTXO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -987,8 +963,8 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
// against all the inputs when the signature operations are out of
|
// against all the inputs when the signature operations are out of
|
||||||
// bounds.
|
// bounds.
|
||||||
var totalFees uint64
|
var totalFees uint64
|
||||||
for _, tx := range transactions {
|
for _, tx := range block.transactions {
|
||||||
txFee, err := CheckTransactionInputs(tx, node.height, dag.virtual.utxoSet,
|
txFee, err := CheckTransactionInputs(tx, block.original.height, pastUTXO,
|
||||||
dag.dagParams)
|
dag.dagParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1010,10 +986,10 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
// errors here because those error conditions would have already been
|
// errors here because those error conditions would have already been
|
||||||
// caught by checkTransactionSanity.
|
// caught by checkTransactionSanity.
|
||||||
var totalSatoshiOut uint64
|
var totalSatoshiOut uint64
|
||||||
for _, txOut := range transactions[0].MsgTx().TxOut {
|
for _, txOut := range block.transactions[0].MsgTx().TxOut {
|
||||||
totalSatoshiOut += txOut.Value
|
totalSatoshiOut += txOut.Value
|
||||||
}
|
}
|
||||||
expectedSatoshiOut := CalcBlockSubsidy(node.height, dag.dagParams) +
|
expectedSatoshiOut := CalcBlockSubsidy(block.original.height, dag.dagParams) +
|
||||||
totalFees
|
totalFees
|
||||||
if totalSatoshiOut > expectedSatoshiOut {
|
if totalSatoshiOut > expectedSatoshiOut {
|
||||||
str := fmt.Sprintf("coinbase transaction for block pays %v "+
|
str := fmt.Sprintf("coinbase transaction for block pays %v "+
|
||||||
@ -1030,7 +1006,7 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
// portion of block handling.
|
// portion of block handling.
|
||||||
checkpoint := dag.LatestCheckpoint()
|
checkpoint := dag.LatestCheckpoint()
|
||||||
runScripts := true
|
runScripts := true
|
||||||
if checkpoint != nil && node.height <= checkpoint.Height {
|
if checkpoint != nil && block.original.height <= checkpoint.Height {
|
||||||
runScripts = false
|
runScripts = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1038,20 +1014,20 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
|
|
||||||
// We obtain the MTP of the *previous* block in order to
|
// We obtain the MTP of the *previous* block in order to
|
||||||
// determine if transactions in the current block are final.
|
// determine if transactions in the current block are final.
|
||||||
medianTime := node.selectedParent.CalcPastMedianTime()
|
medianTime := block.original.selectedParent.CalcPastMedianTime()
|
||||||
|
|
||||||
// We also enforce the relative sequence number based
|
// We also enforce the relative sequence number based
|
||||||
// lock-times within the inputs of all transactions in this
|
// lock-times within the inputs of all transactions in this
|
||||||
// candidate block.
|
// candidate block.
|
||||||
for _, tx := range block.Transactions() {
|
for _, tx := range block.transactions {
|
||||||
// A transaction can only be included within a block
|
// A transaction can only be included within a block
|
||||||
// once the sequence locks of *all* its inputs are
|
// once the sequence locks of *all* its inputs are
|
||||||
// active.
|
// active.
|
||||||
sequenceLock, err := dag.calcSequenceLock(node, dag.virtual.utxoSet, tx, false)
|
sequenceLock, err := dag.calcSequenceLock(block.original, pastUTXO, tx, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !SequenceLockActive(sequenceLock, node.height,
|
if !SequenceLockActive(sequenceLock, block.original.height,
|
||||||
medianTime) {
|
medianTime) {
|
||||||
str := fmt.Sprintf("block contains " +
|
str := fmt.Sprintf("block contains " +
|
||||||
"transaction whose input sequence " +
|
"transaction whose input sequence " +
|
||||||
@ -1065,7 +1041,7 @@ func (dag *BlockDAG) checkConnectBlock(node *blockNode, block *util.Block) error
|
|||||||
// expensive ECDSA signature check scripts. Doing this last helps
|
// expensive ECDSA signature check scripts. Doing this last helps
|
||||||
// prevent CPU exhaustion attacks.
|
// prevent CPU exhaustion attacks.
|
||||||
if runScripts {
|
if runScripts {
|
||||||
err := checkBlockScripts(block, dag.virtual.utxoSet, scriptFlags, dag.sigCache)
|
err := checkBlockScripts(block, pastUTXO, scriptFlags, dag.sigCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1122,6 +1098,9 @@ func (dag *BlockDAG) CheckConnectBlockTemplate(block *util.Block) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
newNode := newBlockNode(&header, dag.virtual.tips(), dag.dagParams.K)
|
newProvisionalNode := &provisionalNode{
|
||||||
return dag.checkConnectBlock(newNode, block)
|
original: newBlockNode(&header, dag.virtual.tips(), dag.dagParams.K),
|
||||||
|
transactions: block.Transactions(),
|
||||||
|
}
|
||||||
|
return dag.checkConnectToPastUTXO(newProvisionalNode, dag.UTXOSet())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user