diff --git a/blockchain/error.go b/blockchain/error.go index 2ddf0069..9ecbe07b 100644 --- a/blockchain/error.go +++ b/blockchain/error.go @@ -157,9 +157,9 @@ const ( // range or not referencing one at all. ErrBadTxInput - // ErrMissingTx indicates a transaction referenced by an input is - // missing. - ErrMissingTx + // ErrMissingTxOut indicates a transaction output referenced by an input + // either does not exist or has already been spent. + ErrMissingTxOut // ErrUnfinalizedTx indicates a transaction has not been finalized. // A valid block may only contain finalized transactions. @@ -179,10 +179,6 @@ const ( // coinbase that has not yet reached the required maturity. ErrImmatureSpend - // ErrDoubleSpend indicates a transaction is attempting to spend coins - // that have already been spent. - ErrDoubleSpend - // ErrSpendTooHigh indicates a transaction is attempting to spend more // value than the sum of all of its inputs. ErrSpendTooHigh @@ -480,12 +476,11 @@ var errorCodeStrings = map[ErrorCode]string{ ErrBadTxOutValue: "ErrBadTxOutValue", ErrDuplicateTxInputs: "ErrDuplicateTxInputs", ErrBadTxInput: "ErrBadTxInput", - ErrMissingTx: "ErrMissingTx", + ErrMissingTxOut: "ErrMissingTxOut", ErrUnfinalizedTx: "ErrUnfinalizedTx", ErrDuplicateTx: "ErrDuplicateTx", ErrOverwriteTx: "ErrOverwriteTx", ErrImmatureSpend: "ErrImmatureSpend", - ErrDoubleSpend: "ErrDoubleSpend", ErrSpendTooHigh: "ErrSpendTooHigh", ErrBadFees: "ErrBadFees", ErrTooManySigOps: "ErrTooManySigOps", diff --git a/blockchain/error_test.go b/blockchain/error_test.go index 5b1f3ecf..02842d22 100644 --- a/blockchain/error_test.go +++ b/blockchain/error_test.go @@ -39,12 +39,11 @@ func TestErrorCodeStringer(t *testing.T) { {blockchain.ErrDuplicateTxInputs, "ErrDuplicateTxInputs"}, {blockchain.ErrBadTxInput, "ErrBadTxInput"}, {blockchain.ErrBadCheckpoint, "ErrBadCheckpoint"}, - {blockchain.ErrMissingTx, "ErrMissingTx"}, + {blockchain.ErrMissingTxOut, "ErrMissingTxOut"}, {blockchain.ErrUnfinalizedTx, "ErrUnfinalizedTx"}, {blockchain.ErrDuplicateTx, "ErrDuplicateTx"}, {blockchain.ErrOverwriteTx, "ErrOverwriteTx"}, {blockchain.ErrImmatureSpend, "ErrImmatureSpend"}, - {blockchain.ErrDoubleSpend, "ErrDoubleSpend"}, {blockchain.ErrSpendTooHigh, "ErrSpendTooHigh"}, {blockchain.ErrBadFees, "ErrBadFees"}, {blockchain.ErrTooManySigOps, "ErrTooManySigOps"}, diff --git a/blockchain/fullblocktests/generate.go b/blockchain/fullblocktests/generate.go index 5842a45e..250e9d06 100644 --- a/blockchain/fullblocktests/generate.go +++ b/blockchain/fullblocktests/generate.go @@ -718,7 +718,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { acceptedToSideChainWithExpectedTip("b6") g.NextBlock("b8", outs[4], ticketOuts[4]) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // --------------------------------------------------------------------- // Too much proof-of-work coinbase tests. @@ -888,7 +888,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { // \-> b3(1) -> b4(2) g.SetTip("b21") g.NextBlock("b23", &b3Tx1Out, nil) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // Create block that forks and spends a tx created on a third fork. // @@ -900,7 +900,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { acceptedToSideChainWithExpectedTip("b21") g.NextBlock("b25", outs[6], ticketOuts[6]) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // --------------------------------------------------------------------- // Immature coinbase tests. @@ -1238,12 +1238,11 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { doubleSpendTx := g.CreateSpendTx(outs[12], lowFee) g.NextBlock("b42", outs[12], ticketOuts[12], additionalPoWTx(doubleSpendTx)) b42Tx1Out := chaingen.MakeSpendableOut(g.Tip(), 1, 0) - // TODO: This really shoud be ErrDoubleSpend - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) g.SetTip("b41") g.NextBlock("b43", &b42Tx1Out, ticketOuts[12]) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // --------------------------------------------------------------------- // Pay-to-script-hash signature operation count tests. @@ -1528,7 +1527,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { b.Transactions[1].TxIn[0].PreviousOutPoint.Hash = *hash b.Transactions[1].TxIn[0].PreviousOutPoint.Index = 0 }) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // Create block with stake tx in regular tx tree. // @@ -1598,7 +1597,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { g.NextBlock("b58", outs[16], ticketOuts[16], func(b *wire.MsgBlock) { b.Transactions[1].TxIn[0].PreviousOutPoint.Index = 42 }) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // Create block with transaction that pays more than its inputs. // @@ -1715,7 +1714,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { b.AddTransaction(tx3) b.AddTransaction(tx2) }) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // Create block that double spends a transaction created in the same // block. @@ -1730,8 +1729,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { b.AddTransaction(tx3) b.AddTransaction(tx4) }) - // TODO: This really shoud be ErrDoubleSpend - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // --------------------------------------------------------------------- // Extra subsidy tests. @@ -2086,7 +2084,7 @@ func Generate(includeLargeReorg bool) (tests [][]TestInstance, err error) { tx := g.CreateSpendTx(&b87OpReturnOut, zeroFee) b.AddTransaction(tx) }) - rejected(blockchain.ErrMissingTx) + rejected(blockchain.ErrMissingTxOut) // Create a block that has a transaction with multiple OP_RETURNs. Even // though a transaction with a large number of OP_RETURNS is not diff --git a/blockchain/scriptval.go b/blockchain/scriptval.go index 11075574..9c8b34c6 100644 --- a/blockchain/scriptval.go +++ b/blockchain/scriptval.go @@ -63,7 +63,7 @@ out: "transaction %v referenced from "+ "transaction %v", originTxHash, txVI.tx.Hash()) - err := ruleError(ErrMissingTx, str) + err := ruleError(ErrMissingTxOut, str) v.sendResult(err) break out } diff --git a/blockchain/sequencelock.go b/blockchain/sequencelock.go index cbfc8564..cb67a154 100644 --- a/blockchain/sequencelock.go +++ b/blockchain/sequencelock.go @@ -67,10 +67,11 @@ func (b *BlockChain) calcSequenceLock(node *blockNode, tx *dcrutil.Tx, view *Utx utxo := view.LookupEntry(&txIn.PreviousOutPoint.Hash) if utxo == nil { - str := fmt.Sprintf("unable to find unspent output "+ - "%v referenced from transaction %s:%d", - txIn.PreviousOutPoint, tx.Hash(), txInIndex) - return sequenceLock, ruleError(ErrMissingTx, str) + str := fmt.Sprintf("output %v referenced from "+ + "transaction %s:%d either does not exist or "+ + "has already been spent", txIn.PreviousOutPoint, + tx.Hash(), txInIndex) + return sequenceLock, ruleError(ErrMissingTxOut, str) } // Calculate the sequence locks from the point of view of the diff --git a/blockchain/validate.go b/blockchain/validate.go index efb6fdab..6e1eec27 100644 --- a/blockchain/validate.go +++ b/blockchain/validate.go @@ -1228,7 +1228,6 @@ func (b *BlockChain) checkDupTxs(txSet []*dcrutil.Tx, view *UtxoViewpoint) error // NOTE: The transaction MUST have already been sanity checked with the // CheckTransactionSanity function prior to calling this function. func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight int64, utxoView *UtxoViewpoint, checkFraudProof bool, chainParams *chaincfg.Params) (int64, error) { - msgTx := tx.MsgTx() // Expired transactions are not allowed. @@ -1267,22 +1266,15 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight for idx, txIn := range msgTx.TxIn { // Ensure the input is available. - txInHash := &txIn.PreviousOutPoint.Hash - utxoEntry, exists := utxoView.entries[*txInHash] - if !exists || utxoEntry == nil { - str := fmt.Sprintf("unable to find input "+ - "transaction %v for transaction %v", - txInHash, txHash) - return 0, ruleError(ErrMissingTx, str) - } - - // Ensure the transaction is not double spending coins. + originTxHash := &txIn.PreviousOutPoint.Hash originTxIndex := txIn.PreviousOutPoint.Index - if utxoEntry.IsOutputSpent(originTxIndex) { - str := fmt.Sprintf("transaction %s:%d tried "+ - "to double spend output %v", txHash, - idx, txIn.PreviousOutPoint) - return 0, ruleError(ErrDoubleSpend, str) + utxoEntry := utxoView.LookupEntry(originTxHash) + if utxoEntry == nil || utxoEntry.IsOutputSpent(originTxIndex) { + str := fmt.Sprintf("output %v referenced from "+ + "transaction %s:%d either does not exist or "+ + "has already been spent", txIn.PreviousOutPoint, + txHash, idx) + return 0, ruleError(ErrMissingTxOut, str) } // Check and make sure that the input is P2PKH or P2SH. @@ -1299,7 +1291,7 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight ", txout %v referenced a txout that "+ "was not a PubKeyHashTy or "+ "ScriptHashTy pkScrpt (class: %v, "+ - "version %v, script %x)", txInHash, + "version %v, script %x)", originTxHash, originTxIndex, class, pkVer, pkScrpt) return 0, ruleError(ErrSStxInScrType, errStr) } @@ -1375,12 +1367,13 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight // We also need to make sure that the SSGen outputs that are // P2PKH go to the addresses specified in the original SSTx. // Check that too. - utxoEntrySstx, exists := utxoView.entries[sstxHash] - if !exists || utxoEntrySstx == nil { - errStr := fmt.Sprintf("Unable to find input sstx "+ - "transaction %v for transaction %v", sstxHash, - txHash) - return 0, ruleError(ErrMissingTx, errStr) + utxoEntrySstx := utxoView.LookupEntry(&sstxHash) + if utxoEntrySstx == nil { + str := fmt.Sprintf("ticket output %v referenced from "+ + "transaction %s:%d either does not exist or "+ + "has already been spent", sstxIn.PreviousOutPoint, + txHash, 1) + return 0, ruleError(ErrMissingTxOut, str) } // While we're here, double check to make sure that the input @@ -1512,12 +1505,13 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight // We also need to make sure that the SSGen outputs that are // P2PKH go to the addresses specified in the original SSTx. // Check that too. - utxoEntrySstx, exists := utxoView.entries[sstxHash] - if !exists || utxoEntrySstx == nil { - errStr := fmt.Sprintf("Unable to find input sstx "+ - "transaction %v for transaction %v", sstxHash, - txHash) - return 0, ruleError(ErrMissingTx, errStr) + utxoEntrySstx := utxoView.LookupEntry(&sstxHash) + if utxoEntrySstx == nil { + str := fmt.Sprintf("ticket output %v referenced from "+ + "transaction %s:%d either does not exist or "+ + "has already been spent", sstxIn.PreviousOutPoint, + txHash, 0) + return 0, ruleError(ErrMissingTxOut, str) } // While we're here, double check to make sure that the input @@ -1620,15 +1614,17 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight } txInHash := &txIn.PreviousOutPoint.Hash - utxoEntry, exists := utxoView.entries[*txInHash] - if !exists || utxoEntry == nil { - str := fmt.Sprintf("unable to find input transaction "+ - "%v for transaction %v", txInHash, txHash) - return 0, ruleError(ErrMissingTx, str) + originTxIndex := txIn.PreviousOutPoint.Index + utxoEntry := utxoView.LookupEntry(txInHash) + if utxoEntry == nil || utxoEntry.IsOutputSpent(originTxIndex) { + str := fmt.Sprintf("output %v referenced from "+ + "transaction %s:%d either does not exist or "+ + "has already been spent", txIn.PreviousOutPoint, + txHash, idx) + return 0, ruleError(ErrMissingTxOut, str) } // Check fraud proof witness data. - originTxIndex := txIn.PreviousOutPoint.Index // Using zero value outputs as inputs is banned. if utxoEntry.AmountByIndex(originTxIndex) == 0 { @@ -1699,14 +1695,6 @@ func CheckTransactionInputs(subsidyCache *SubsidyCache, tx *dcrutil.Tx, txHeight } } - // Ensure the transaction is not double spending coins. - if utxoEntry.IsOutputSpent(originTxIndex) { - str := fmt.Sprintf("transaction %s:%d tried to double "+ - "spend output %v", txHash, originTxIndex, - txIn.PreviousOutPoint) - return 0, ruleError(ErrDoubleSpend, str) - } - // Ensure that the outpoint's tx tree makes sense. originTxOPTree := txIn.PreviousOutPoint.Tree originTxType := utxoEntry.TransactionType() @@ -1929,22 +1917,13 @@ func CountP2SHSigOps(tx *dcrutil.Tx, isCoinBaseTx bool, isStakeBaseTx bool, utxo // Ensure the referenced input transaction is available. originTxHash := &txIn.PreviousOutPoint.Hash originTxIndex := txIn.PreviousOutPoint.Index - utxoEntry, ok := utxoView.entries[*originTxHash] - if !ok || utxoEntry == nil { - str := fmt.Sprintf("unable to find unspent transaction"+ - " %v referenced from transaction %s:%d during "+ - "CountP2SHSigOps: output missing", - txIn.PreviousOutPoint.Hash, tx.Hash(), - txInIndex) - return 0, ruleError(ErrMissingTx, str) - } - - if utxoEntry.IsOutputSpent(originTxIndex) { - str := fmt.Sprintf("unable to find unspent output "+ - "%v referenced from transaction %s:%d during "+ - "CountP2SHSigOps: output spent", - txIn.PreviousOutPoint, tx.Hash(), txInIndex) - return 0, ruleError(ErrMissingTx, str) + utxoEntry := utxoView.LookupEntry(originTxHash) + if utxoEntry == nil || utxoEntry.IsOutputSpent(originTxIndex) { + str := fmt.Sprintf("output %v referenced from "+ + "transaction %s:%d either does not exist or "+ + "has already been spent", txIn.PreviousOutPoint, + tx.Hash(), txInIndex) + return 0, ruleError(ErrMissingTxOut, str) } // We're only interested in pay-to-script-hash types, so skip @@ -2342,7 +2321,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B // an error now. if node.hash.IsEqual(b.chainParams.GenesisHash) { str := "the coinbase for the genesis block is not spendable" - return ruleError(ErrMissingTx, str) + return ruleError(ErrMissingTxOut, str) } // Ensure the view is for the node being checked. diff --git a/blockchain/validate_test.go b/blockchain/validate_test.go index 54fe32c5..ecd43ae8 100644 --- a/blockchain/validate_test.go +++ b/blockchain/validate_test.go @@ -1651,10 +1651,10 @@ func TestBlockValidationRules(t *testing.T) { err) } - // Fails and hits ErrMissingTx. + // Fails and hits ErrMissingTxOut. err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != - blockchain.ErrMissingTx { + blockchain.ErrMissingTxOut { t.Errorf("Unexpected no or wrong error for invalMissingInsS154 test: %v", err) } @@ -1686,7 +1686,7 @@ func TestBlockValidationRules(t *testing.T) { } // ---------------------------------------------------------------------------- - // ErrMissingTx (formerly ErrZeroValueOutputSpend). In the latest version of + // ErrMissingTxOut (formerly ErrZeroValueOutputSpend). In the latest version of // the database, zero value outputs are automatically pruned, so the output // is simply missing. mtxFromB = new(wire.MsgTx) @@ -1720,9 +1720,9 @@ func TestBlockValidationRules(t *testing.T) { // Fails and hits ErrZeroValueOutputSpend. err = chain.CheckConnectBlock(b154test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != - blockchain.ErrMissingTx { + blockchain.ErrMissingTxOut { t.Errorf("Unexpected no or wrong error for "+ - "ErrMissingTx test: %v", err) + "ErrMissingTxOut test: %v", err) } // ---------------------------------------------------------------------------- @@ -1778,16 +1778,16 @@ func TestBlockValidationRules(t *testing.T) { err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params) if err != nil { - t.Errorf("got unexpected error for ErrMissingTx test 1 sanity "+ + t.Errorf("got unexpected error for ErrMissingTxOut test 1 sanity "+ "check: %v", err) } - // Fails and hits ErrMissingTx. + // Fails and hits ErrMissingTxOut. err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != - blockchain.ErrMissingTx { + blockchain.ErrMissingTxOut { t.Errorf("Unexpected no or wrong error for "+ - "ErrMissingTx test 1: %v", err) + "ErrMissingTxOut test 1: %v", err) } // ---------------------------------------------------------------------------- @@ -1824,16 +1824,16 @@ func TestBlockValidationRules(t *testing.T) { err = blockchain.CheckWorklessBlockSanity(b166test, timeSource, params) if err != nil { - t.Errorf("got unexpected error for ErrMissingTx test 2 sanity "+ + t.Errorf("got unexpected error for ErrMissingTxOut test 2 sanity "+ "check: %v", err) } - // Fails and hits ErrMissingTx. + // Fails and hits ErrMissingTxOut. err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != - blockchain.ErrMissingTx { + blockchain.ErrMissingTxOut { t.Errorf("Unexpected no or wrong error for "+ - "ErrMissingTx test 2: %v", err) + "ErrMissingTxOut test 2: %v", err) } // ---------------------------------------------------------------------------- @@ -1871,7 +1871,7 @@ func TestBlockValidationRules(t *testing.T) { "check: %v", err) } - // Fails and hits ErrMissingTx. It may not be immediately clear + // Fails and hits ErrMissingTxOut. It may not be immediately clear // why this happens, but in the case of the stake transaction // tree, because you can't spend in chains, the txlookup code // doesn't even bother to populate the spent list in the txlookup @@ -1901,10 +1901,10 @@ func TestBlockValidationRules(t *testing.T) { "check: %v", err) } - // Fails and hits ErrMissingTx. + // Fails and hits ErrMissingTxOut. err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != - blockchain.ErrMissingTx { + blockchain.ErrMissingTxOut { t.Errorf("Unexpected no or wrong error for "+ "double spend test 1: %v", err) } @@ -1926,10 +1926,10 @@ func TestBlockValidationRules(t *testing.T) { "check: %v", err) } - // Fails and hits ErrMissingTx. + // Fails and hits ErrMissingTxOut. err = chain.CheckConnectBlock(b166test, blockchain.BFNoPoWCheck) if err == nil || err.(blockchain.RuleError).ErrorCode != - blockchain.ErrMissingTx { + blockchain.ErrMissingTxOut { t.Errorf("Unexpected no or wrong error for "+ "double spend test 2: %v", err) } diff --git a/mempool/error.go b/mempool/error.go index ff9168d4..31d4ded2 100644 --- a/mempool/error.go +++ b/mempool/error.go @@ -75,8 +75,6 @@ func extractRejectCode(err error) (wire.RejectCode, bool) { switch err.ErrorCode { // Rejected due to duplicate. case blockchain.ErrDuplicateBlock: - fallthrough - case blockchain.ErrDoubleSpend: code = wire.RejectDuplicate // Rejected due to obsolete version. diff --git a/rpcserver.go b/rpcserver.go index caa30614..e22909f4 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -2900,7 +2900,7 @@ func chainErrToGBTErrString(err error) string { return "bad-txns-dupinputs" case blockchain.ErrBadTxInput: return "bad-txns-badinput" - case blockchain.ErrMissingTx: + case blockchain.ErrMissingTxOut: return "bad-txns-missinginput" case blockchain.ErrUnfinalizedTx: return "bad-txns-unfinalizedtx" @@ -2910,8 +2910,6 @@ func chainErrToGBTErrString(err error) string { return "bad-txns-overwrite" case blockchain.ErrImmatureSpend: return "bad-txns-maturity" - case blockchain.ErrDoubleSpend: - return "bad-txns-dblspend" case blockchain.ErrSpendTooHigh: return "bad-txns-highspend" case blockchain.ErrBadFees: