From b5f1b6dd38ff6bf49f8eac24095fe2bc128bbd19 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 15 Feb 2016 15:19:28 -0800 Subject: [PATCH] Refactors in anticipation of blinded values --- src/Makefile.am | 2 ++ src/bench/verify_script.cpp | 10 ++++-- src/bitcoin-tx.cpp | 2 +- src/coins.cpp | 20 ++++++++---- src/compressor.h | 3 +- src/core_write.cpp | 6 ++-- src/primitives/transaction.cpp | 45 +++++++++++++++++++++++++-- src/primitives/transaction.h | 33 ++++++++++++++++++-- src/qt/coincontroldialog.cpp | 10 +++--- src/qt/transactiondesc.cpp | 6 ++-- src/qt/transactionrecord.cpp | 4 +-- src/qt/walletmodel.cpp | 2 +- src/qt/walletmodeltransaction.cpp | 4 +-- src/rest.cpp | 3 +- src/rpc/blockchain.cpp | 8 ++--- src/rpc/mining.cpp | 2 +- src/rpc/rawtransaction.cpp | 5 +-- src/script/bitcoinconsensus.cpp | 31 +++++++++++++------ src/script/bitcoinconsensus.h | 5 +-- src/script/interpreter.cpp | 34 ++++++++++++++------ src/script/interpreter.h | 24 +++++++-------- src/script/script_error.cpp | 2 ++ src/script/script_error.h | 1 + src/script/sigcache.h | 2 +- src/script/sign.cpp | 4 +-- src/script/sign.h | 8 ++--- src/test/miner_tests.cpp | 12 ++++---- src/test/script_tests.cpp | 8 +++-- src/test/test_bitcoin.cpp | 2 +- src/test/txvalidationcache_tests.cpp | 2 +- src/validation.cpp | 25 +++++++++------ src/validation.h | 6 ++-- src/wallet/rpcwallet.cpp | 18 +++++------ src/wallet/wallet.cpp | 46 ++++++++++++++-------------- 34 files changed, 262 insertions(+), 133 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 29351ab958..0f9b9aaf30 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -292,10 +292,12 @@ libbitcoin_consensus_a_SOURCES = \ script/script_error.cpp \ script/script_error.h \ support/events.cpp \ + script/sign.cpp \ serialize.h \ tinyformat.h \ uint256.cpp \ uint256.h \ + utilmoneystr.cpp \ utilstrencodings.cpp \ utilstrencodings.h \ version.h diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index b178098a66..d2cd7ce3f3 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -89,11 +89,17 @@ static void VerifyScriptBench(benchmark::State& state) #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << txSpend; + CDataStream streamVal1(SER_NETWORK, PROTOCOL_VERSION); + streamVal1 << txCredit.vout[0].nValue; + CDataStream streamVal2(SER_NETWORK, PROTOCOL_VERSION); + streamVal2 << CTxOutValue(0); int csuccess = bitcoinconsensus_verify_script_with_amount( txCredit.vout[0].scriptPubKey.data(), txCredit.vout[0].scriptPubKey.size(), - txCredit.vout[0].nValue, - -1, + (const unsigned char*)&streamVal1[0], + streamVal1.size(), + (const unsigned char*)&streamVal2[0], + streamVal2.size(), (const unsigned char*)stream.data(), stream.size(), 0, flags, nullptr); assert(csuccess == 1); #endif diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 32d541c750..2910ff3a5d 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -617,7 +617,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; - const CAmount& amount = coins->vout[txin.prevout.n].nValue; + const CTxOutValue& amount = coins->vout[txin.prevout.n].nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: diff --git a/src/coins.cpp b/src/coins.cpp index fb87b6bffd..2ebca99668 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -332,8 +332,11 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const return 0; CAmount nResult = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - nResult += GetOutputFor(tx.vin[i]).nValue; + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const CTxOutValue& val = GetOutputFor(tx.vin[i]).nValue; + assert(val.IsAmount()); + nResult += val.GetAmount(); + } return nResult; } @@ -345,8 +348,9 @@ bool CCoinsViewCache::VerifyAmounts(const CTransaction& tx, const CAmount& exces CAmount nInAmount = GetValueIn(tx); for (std::vector::const_iterator it(tx.vout.begin()); it != tx.vout.end(); ++it) { - nInAmount -= it->nValue; - if (!MoneyRange(it->nValue) || (!MoneyRange(nInAmount) && !MoneyRange(-nInAmount))) + assert(it->nValue.IsAmount()); + nInAmount -= it->nValue.GetAmount();; + if (!MoneyRange(it->nValue.GetAmount()) || (!MoneyRange(nInAmount) && !MoneyRange(-nInAmount))) return false; } return excess == nInAmount; @@ -378,8 +382,12 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount assert(coins); if (!coins->IsAvailable(txin.prevout.n)) continue; if (coins->nHeight <= nHeight) { - dResult += (double)(coins->vout[txin.prevout.n].nValue) * (nHeight-coins->nHeight); - inChainInputValue += coins->vout[txin.prevout.n].nValue; + const CTxOutValue& val = coins->vout[txin.prevout.n].nValue; + CAmount nAmount = COIN; + if (val.IsAmount()) + nAmount = val.GetAmount(); + dResult += nAmount * (nHeight-coins->nHeight); + inChainInputValue += nAmount; } } return tx.ComputePriority(dResult); diff --git a/src/compressor.h b/src/compressor.h index 015911484a..2649dc2c8a 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -106,7 +106,8 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action) { if (!ser_action.ForRead()) { - uint64_t nVal = CompressAmount(txout.nValue); + assert(txout.nValue.IsAmount()); + uint64_t nVal = CompressAmount(txout.nValue.GetAmount()); READWRITE(VARINT(nVal)); } else { uint64_t nVal = 0; diff --git a/src/core_write.cpp b/src/core_write.cpp index e431b9f144..0d308bc38f 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -192,8 +192,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry) UniValue out(UniValue::VOBJ); - UniValue outValue(UniValue::VNUM, FormatMoney(txout.nValue)); - out.pushKV("value", outValue); + if (txout.nValue.IsAmount()) { + UniValue outValue(UniValue::VNUM, FormatMoney(txout.nValue.GetAmount())); + out.pushKV("value", outValue); + } out.pushKV("n", (int64_t)i); UniValue o(UniValue::VOBJ); diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index b788b92cdb..927217d0d3 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -43,7 +43,48 @@ std::string CTxIn::ToString() const return str; } -CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) + +CTxOutValue::CTxOutValue() +: nAmount(-1) +{ +} + +CTxOutValue::CTxOutValue(CAmount nAmountIn) +: nAmount(nAmountIn) +{ +} + +bool CTxOutValue::IsValid() const +{ + return MoneyRange(nAmount); +} + +bool CTxOutValue::IsNull() const +{ + return nAmount == -1; +} + +bool CTxOutValue::IsAmount() const +{ + return nAmount != -1; +} + +CAmount CTxOutValue::GetAmount() const +{ + assert(IsAmount()); + return nAmount; +} + +bool operator==(const CTxOutValue& a, const CTxOutValue& b) +{ + return a.nAmount == b.nAmount; +} + +bool operator!=(const CTxOutValue& a, const CTxOutValue& b) { + return !(a == b); +} + +CTxOut::CTxOut(const CTxOutValue& nValueIn, CScript scriptPubKeyIn) { nValue = nValueIn; scriptPubKey = scriptPubKeyIn; @@ -51,7 +92,7 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) std::string CTxOut::ToString() const { - return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); + return strprintf("CTxOut(nValue=%s, scriptPubKey=%s)", (nValue.IsAmount() ? strprintf("%d.%08d", nValue.GetAmount() / COIN, nValue.GetAmount() % COIN) : std::string("UNKNOWN")), HexStr(scriptPubKey).substr(0, 30)); } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nTxFee(0), vin(), vout(), nLockTime(0) {} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index bb81502568..2278741d5b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -127,13 +127,38 @@ public: std::string ToString() const; }; + +class CTxOutValue +{ + CAmount nAmount; +public: + CTxOutValue(); + CTxOutValue(CAmount); + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(nAmount); + } + + bool IsValid() const; + bool IsNull() const; + bool IsAmount() const; + + CAmount GetAmount() const; + + friend bool operator==(const CTxOutValue& a, const CTxOutValue& b); + friend bool operator!=(const CTxOutValue& a, const CTxOutValue& b); +}; + /** An output of a transaction. It contains the public key that the next input * must be able to sign with to claim it. */ class CTxOut { public: - CAmount nValue; + CTxOutValue nValue; CScript scriptPubKey; CTxOut() @@ -141,7 +166,7 @@ public: SetNull(); } - CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); + CTxOut(const CTxOutValue& nValueIn, CScript scriptPubKeyIn); ADD_SERIALIZE_METHODS; @@ -196,7 +221,9 @@ public: bool IsDust(const CFeeRate &minRelayTxFee) const { - return (nValue < GetDustThreshold(minRelayTxFee)); + if (!nValue.IsAmount()) + return false; // FIXME + return (nValue.GetAmount() < GetDustThreshold(minRelayTxFee)); } friend bool operator==(const CTxOut& a, const CTxOut& b) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index d4fd8bd372..52e7062833 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -471,10 +471,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nQuantity++; // Amount - nAmount += out.tx->tx->vout[out.i].nValue; + nAmount += out.tx->tx->vout[out.i].nValue.GetAmount(); // Priority - dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue * (out.nDepth+1); + dPriorityInputs += (double)out.tx->tx->vout[out.i].nValue.GetAmount() * (out.nDepth+1); // Bytes CTxDestination address; @@ -676,7 +676,7 @@ void CoinControlDialog::updateView() CAmount nSum = 0; int nChildren = 0; BOOST_FOREACH(const COutput& out, coins.second) { - nSum += out.tx->tx->vout[out.i].nValue; + nSum += out.tx->tx->vout[out.i].nValue.GetAmount(); nChildren++; CCoinControlWidgetItem *itemOutput; @@ -713,8 +713,8 @@ void CoinControlDialog::updateView() } // amount - itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue)); - itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue.GetAmount())); + itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue.GetAmount())); // padding so that sorting works correctly // date itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime())); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 3b29f66a72..6e823a7d52 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -198,9 +198,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco } } - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue) + "
"; + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -txout.nValue.GetAmount()) + "
"; if(toSelf) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, txout.nValue.GetAmount()) + "
"; } if (fAllToMe) @@ -307,7 +307,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } - strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); + strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue.GetAmount()); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + ""; strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + ""; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 489ae9ece7..df7ff7fac1 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -56,7 +56,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * TransactionRecord sub(hash, nTime); CTxDestination address; sub.idx = i; // vout index - sub.credit = txout.nValue; + sub.credit = txout.nValue.GetAmount(); sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) { @@ -143,7 +143,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * sub.address = mapValue["to"]; } - CAmount nValue = txout.nValue; + CAmount nValue = txout.nValue.GetAmount(); /* Add fee to first output */ if (nTxFee > 0) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 0a5a7c3e9f..bbeba25d82 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -67,7 +67,7 @@ CAmount WalletModel::getBalance(const CCoinControl *coinControl) const wallet->AvailableCoins(vCoins, true, coinControl); BOOST_FOREACH(const COutput& out, vCoins) if(out.fSpendable) - nBalance += out.tx->tx->vout[out.i].nValue; + nBalance = out.tx->tx->vout[out.i].nValue.GetAmount(); return nBalance; } diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index b4445c8166..461f39b56d 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -64,7 +64,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) if (out.amount() <= 0) continue; if (i == nChangePosRet) i++; - subtotal += walletTransaction->tx->vout[i].nValue; + subtotal += walletTransaction->tx->vout[i].nValue.GetAmount(); i++; } rcp.amount = subtotal; @@ -73,7 +73,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) { if (i == nChangePosRet) i++; - rcp.amount = walletTransaction->tx->vout[i].nValue; + rcp.amount = walletTransaction->tx->vout[i].nValue.GetAmount(); i++; } } diff --git a/src/rest.cpp b/src/rest.cpp index 54eefcafe3..162dd03d4b 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -575,7 +575,8 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) UniValue utxo(UniValue::VOBJ); utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer)); utxo.push_back(Pair("height", (int32_t)coin.nHeight)); - utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); + if (coin.out.nValue.IsAmount()) + utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue.GetAmount()))); // include the script in a json output UniValue o(UniValue::VOBJ); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 504fc4aceb..d646459d59 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -783,7 +783,8 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) stats.nTransactionOutputs++; ss << VARINT(i+1); ss << out; - nTotalAmount += out.nValue; + if (out.nValue.IsAmount()) + nTotalAmount += out.nValue.GetAmount(); } } stats.nSerializedSize += 32 + pcursor->GetValueSize(); @@ -862,7 +863,6 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request) " \"txouts\": n, (numeric) The number of output transactions\n" " \"bytes_serialized\": n, (numeric) The serialized size\n" " \"hash_serialized\": \"hash\", (string) The serialized hash\n" - " \"total_amount\": x.xxx (numeric) The total amount\n" "}\n" "\nExamples:\n" + HelpExampleCli("gettxoutsetinfo", "") @@ -880,7 +880,6 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request) ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs)); ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize)); ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex())); - ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount))); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); } @@ -957,7 +956,8 @@ UniValue gettxout(const JSONRPCRequest& request) ret.push_back(Pair("confirmations", 0)); else ret.push_back(Pair("confirmations", pindex->nHeight - coins.nHeight + 1)); - ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue))); + if (coins.vout[n].nValue.IsAmount()) + ret.push_back(Pair("value", ValueFromAmount(coins.vout[n].nValue.GetAmount()))); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true); ret.push_back(Pair("scriptPubKey", o)); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 55e7de57ff..695652ff79 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -632,7 +632,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("transactions", transactions)); result.push_back(Pair("coinbaseaux", aux)); - result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue)); + result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue.GetAmount())); result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); result.push_back(Pair("target", GetChallengeStrHex(*pblock))); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 8bd88921aa..a182322ffd 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -99,7 +99,8 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; UniValue out(UniValue::VOBJ); - out.push_back(Pair("value", ValueFromAmount(txout.nValue))); + if (txout.nValue.IsAmount()) + out.push_back(Pair("value", ValueFromAmount(txout.nValue.GetAmount()))); out.push_back(Pair("n", (int64_t)i)); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); @@ -839,7 +840,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) continue; } const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; - const CAmount& amount = coins->vout[txin.prevout.n].nValue; + const CTxOutValue& amount = coins->vout[txin.prevout.n].nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 8a82caf7b0..9e0b2896e7 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -76,8 +76,8 @@ static bool verify_flags(unsigned int flags) return (flags & ~(bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL)) == 0; } -static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CAmount amount, - CAmount amountPreviousInput, +static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, CTxOutValue amount, + CTxOutValue amountPreviousInput, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { @@ -96,7 +96,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP set_error(err, bitcoinconsensus_ERR_OK); PrecomputedTransactionData txdata(tx); - if (amountPreviousInput < -1 || (nIn != 0 && !MoneyRange(amountPreviousInput))) + if (amountPreviousInput.IsAmount() && (amountPreviousInput.GetAmount() < -1 || (nIn != 0 && !MoneyRange(amountPreviousInput.GetAmount())))) return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionNoWithdrawsSignatureChecker(&tx, nIn, amount, txdata), NULL); else return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), &tx.vin[nIn].scriptWitness, flags, TransactionSignatureChecker(&tx, nIn, amount, amountPreviousInput, txdata), NULL); @@ -105,14 +105,25 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP } } -int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, - int64_t amountPreviousInput, +int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, + const unsigned char* amount, unsigned int amountLen, + const unsigned char* amountPreviousInput, unsigned int amountPreviousInputLen, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err) { - CAmount am(amount); - CAmount prevInAm(amountPreviousInput); - return ::verify_script(scriptPubKey, scriptPubKeyLen, am, prevInAm, txTo, txToLen, nIn, flags, err); + try { + TxInputStream stream(SER_NETWORK, PROTOCOL_VERSION, amount, amountLen); + CTxOutValue am; + stream >> am; + + TxInputStream stream2(SER_NETWORK, PROTOCOL_VERSION, amountPreviousInput, amountPreviousInputLen); + CTxOutValue prevInAm; + stream >> prevInAm; + + return ::verify_script(scriptPubKey, scriptPubKeyLen, am, prevInAm, txTo, txToLen, nIn, flags, err); + } catch (const std::exception&) { + return set_error(err, bitcoinconsensus_ERR_TX_DESERIALIZE); // Error deserializing + } } @@ -124,8 +135,8 @@ int bitcoinconsensus_verify_script(const unsigned char *scriptPubKey, unsigned i return set_error(err, bitcoinconsensus_ERR_AMOUNT_REQUIRED); } - CAmount am(0); - CAmount prevInAm(-2); + CTxOutValue am(0); + CTxOutValue prevInAm(-2); return ::verify_script(scriptPubKey, scriptPubKeyLen, am, prevInAm, txTo, txToLen, nIn, flags, err); } diff --git a/src/script/bitcoinconsensus.h b/src/script/bitcoinconsensus.h index ea85996364..c9cf022435 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -72,8 +72,9 @@ EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubK unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); // Use -1 for amountPreviousInput if there is no previous input (ie nIn == 0) -EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, int64_t amount, - int64_t amountPreviousInput, +EXPORT_SYMBOL int bitcoinconsensus_verify_script_with_amount(const unsigned char *scriptPubKey, unsigned int scriptPubKeyLen, + const unsigned char* amount, unsigned int amountLen, + const unsigned char* amountPreviousInput, unsigned int amountPreviousInputLen, const unsigned char *txTo , unsigned int txToLen, unsigned int nIn, unsigned int flags, bitcoinconsensus_error* err); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index b42e58e860..78d6f0104c 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1097,13 +1097,19 @@ bool EvalScript(vector >& stack, const CScript& script, un CScript relockScript = CScript() << vgenesisHash << OP_WITHDRAWPROOFVERIFY; if (stack.size() == 1) { // increasing value of locked coins - CAmount minValue = checker.GetValueIn(); + if (!checker.GetValueIn().IsAmount()) + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); + CAmount minValue = checker.GetValueIn().GetAmount(); CTxOut newOutput = checker.GetOutputOffsetFromCurrent(0); if (newOutput.IsNull()) { newOutput = checker.GetOutputOffsetFromCurrent(-1); - minValue += checker.GetValueInPrevIn(); + if (!checker.GetValueInPrevIn().IsAmount()) + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); + minValue += checker.GetValueInPrevIn().GetAmount(); } - if (newOutput.scriptPubKey != relockScript || newOutput.nValue < minValue) + if (!newOutput.nValue.IsAmount()) + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); + if (newOutput.scriptPubKey != relockScript || newOutput.nValue.GetAmount() < minValue) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT); } else { // stack.size() >= 7...ie regular withdraw int stackReadPos = -2; @@ -1193,16 +1199,24 @@ bool EvalScript(vector >& stack, const CScript& script, un // We check values by doing the following: // * Tx must relock at least - // * Tx must send at least the withdraw value to its P2SH withdraw, but may send more - CAmount withdrawVal = locktx->vout[nlocktxOut].nValue; - CAmount lockValueRequired = checker.GetValueIn() - withdrawVal; + assert(locktx->vout[nlocktxOut].nValue.IsAmount()); // Its a SERIALIZE_BITCOIN_BLOCK_OR_TX + CAmount withdrawVal = locktx->vout[nlocktxOut].nValue.GetAmount(); + if (!checker.GetValueIn().IsAmount()) // Heh, you just destroyed coins + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); + + CAmount lockValueRequired = checker.GetValueIn().GetAmount() - withdrawVal; if (lockValueRequired > 0) { const CTxOut newLockOutput = checker.GetOutputOffsetFromCurrent(1); - if (newLockOutput.IsNull() || newLockOutput.scriptPubKey != relockScript || newLockOutput.nValue < lockValueRequired) + if (!newLockOutput.nValue.IsAmount()) + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); + if (newLockOutput.IsNull() || newLockOutput.scriptPubKey != relockScript || newLockOutput.nValue.GetAmount() < lockValueRequired) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT); } const CTxOut withdrawOutput = checker.GetOutputOffsetFromCurrent(0); - if (withdrawOutput.nValue < withdrawVal) + if (!withdrawOutput.nValue.IsAmount()) + return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS); + if (withdrawOutput.nValue.GetAmount() < withdrawVal) return set_error(serror, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT); CScript expectedWithdrawScriptPubKey; @@ -1379,7 +1393,7 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) hashOutputs = GetOutputsHash(txTo); } -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CTxOutValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; @@ -1580,12 +1594,12 @@ COutPoint TransactionSignatureChecker::GetPrevOut() const return txTo->vin[nIn].prevout; } -CAmount TransactionSignatureChecker::GetValueIn() const +CTxOutValue TransactionSignatureChecker::GetValueIn() const { return amount; } -CAmount TransactionSignatureChecker::GetValueInPrevIn() const +CTxOutValue TransactionSignatureChecker::GetValueInPrevIn() const { return amountPreviousInput; } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 4d121be393..f17001c257 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -132,7 +132,7 @@ enum SigVersion SIGVERSION_WITNESS_V0 = 1, }; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL); +uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CTxOutValue& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = NULL); class BaseSignatureChecker { @@ -150,7 +150,7 @@ public: virtual CTxOut GetOutputOffsetFromCurrent(const int offset) const; virtual COutPoint GetPrevOut() const; - virtual CAmount GetValueIn() const + virtual CTxOutValue GetValueIn() const { return -1; } @@ -160,7 +160,7 @@ public: return false; } - virtual CAmount GetValueInPrevIn() const + virtual CTxOutValue GetValueInPrevIn() const { return -1; } @@ -178,14 +178,14 @@ class TransactionNoWithdrawsSignatureChecker : public BaseSignatureChecker protected: const CTransaction* txTo; const unsigned int nIn; - const CAmount amount; + const CTxOutValue amount; const PrecomputedTransactionData* txdata; virtual bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionNoWithdrawsSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} - TransactionNoWithdrawsSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} + TransactionNoWithdrawsSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(NULL) {} + TransactionNoWithdrawsSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} bool CheckSig(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const; bool CheckLockTime(const CScriptNum& nLockTime) const; bool CheckSequence(const CScriptNum& nSequence) const; @@ -197,21 +197,21 @@ private: const CTransaction txTo; public: - MutableTransactionNoWithdrawsSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount) : TransactionNoWithdrawsSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} + MutableTransactionNoWithdrawsSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amount) : TransactionNoWithdrawsSignatureChecker(&txTo, nInIn, amount), txTo(*txToIn) {} }; class TransactionSignatureChecker : public TransactionNoWithdrawsSignatureChecker { private: - const CAmount amountPreviousInput; + const CTxOutValue amountPreviousInput; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CAmount& amountPreviousInputIn) : TransactionNoWithdrawsSignatureChecker(txToIn, nInIn, amountIn), amountPreviousInput(amountPreviousInputIn) {} - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const CAmount& amountPreviousInputIn, const PrecomputedTransactionData& txdataIn) : TransactionNoWithdrawsSignatureChecker(txToIn, nInIn, amountIn, txdataIn), amountPreviousInput(amountPreviousInputIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amountIn, const CTxOutValue& amountPreviousInputIn) : TransactionNoWithdrawsSignatureChecker(txToIn, nInIn, amountIn), amountPreviousInput(amountPreviousInputIn) {} + TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amountIn, const CTxOutValue& amountPreviousInputIn, const PrecomputedTransactionData& txdataIn) : TransactionNoWithdrawsSignatureChecker(txToIn, nInIn, amountIn, txdataIn), amountPreviousInput(amountPreviousInputIn) {} CTxOut GetOutputOffsetFromCurrent(const int offset) const; COutPoint GetPrevOut() const; - CAmount GetValueIn() const; - CAmount GetValueInPrevIn() const; + CTxOutValue GetValueIn() const; + CTxOutValue GetValueInPrevIn() const; bool IsConfirmedBitcoinBlock(const uint256& genesishash, const uint256& hash, bool fConservativeConfirmationRequirements) const; }; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index 1fa38c29ef..18f99535f7 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -97,6 +97,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Withdraw proof validation failed - output does not match expected"; case SCRIPT_ERR_WITHDRAW_VERIFY_BLOCKCONFIRMED: return "Withdraw proof validation failed - lock block was not sufficiently confirmed on sending chain"; + case SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS: + return "Withdraw proof validation failed - amounts in outputs were blinded"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index e5455c7b25..f10b1f2f74 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -70,6 +70,7 @@ typedef enum ScriptError_t SCRIPT_ERR_WITHDRAW_VERIFY_LOCKTX, SCRIPT_ERR_WITHDRAW_VERIFY_OUTPUT, SCRIPT_ERR_WITHDRAW_VERIFY_BLOCKCONFIRMED, + SCRIPT_ERR_WITHDRAW_VERIFY_BLINDED_AMOUNTS, SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sigcache.h b/src/script/sigcache.h index d9a869170f..b8cb69d37e 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -25,7 +25,7 @@ private: bool store; public: - CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amount, const CAmount& amountPreviousInput, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, amountPreviousInput), store(storeIn) {} + CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amount, const CTxOutValue& amountPreviousInput, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amount, amountPreviousInput), store(storeIn) {} bool VerifySignature(const std::vector& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; }; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 0a6317d867..e5124ffc6b 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -18,7 +18,7 @@ using namespace std; typedef std::vector valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} +TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { @@ -206,7 +206,7 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur tx.vin[nIn].scriptWitness = data.scriptWitness; } -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType) +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CTxOutValue& amount, int nHashType) { assert(nIn < txTo.vin.size()); diff --git a/src/script/sign.h b/src/script/sign.h index 05256f12c7..2eb08ec17b 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -35,11 +35,11 @@ class TransactionSignatureCreator : public BaseSignatureCreator { const CTransaction* txTo; unsigned int nIn; int nHashType; - CAmount amount; + CTxOutValue amount; const TransactionNoWithdrawsSignatureChecker checker; public: - TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); + TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const { return checker; } bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const; }; @@ -48,7 +48,7 @@ class MutableTransactionSignatureCreator : public TransactionSignatureCreator { CTransaction tx; public: - MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {} + MutableTransactionSignatureCreator(const CKeyStore* keystoreIn, const CMutableTransaction* txToIn, unsigned int nInIn, const CTxOutValue& amount, int nHashTypeIn) : TransactionSignatureCreator(keystoreIn, &tx, nInIn, amount, nHashTypeIn), tx(*txToIn) {} }; /** A signature creator that just produces 72-byte empty signatures. */ @@ -71,7 +71,7 @@ struct SignatureData { bool ProduceSignature(const BaseSignatureCreator& creator, const CScript& scriptPubKey, SignatureData& sigdata); /** Produce a script signature for a transaction. */ -bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CAmount& amount, int nHashType); +bool SignSignature(const CKeyStore &keystore, const CScript& fromPubKey, CMutableTransaction& txTo, unsigned int nIn, const CTxOutValue& amount, int nHashType); bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); /** Combine two script signatures using a generic signature checker, intelligently, possibly with OP_0 placeholders. */ diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 2293290d09..5dc0b454e7 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -142,7 +142,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, // of the transactions is below the min relay fee // Remove the low fee transaction and replace with a higher fee transaction mempool.removeRecursive(tx); - tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee + tx.vout[0].nValue = CTxOutValue(tx.vout[0].nValue.GetAmount()- 2); // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx)); pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); @@ -252,7 +252,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 1001; ++i) { - tx.vout[0].nValue -= LOWFEE; + tx.vout[0].nValue = CTxOutValue(tx.vout[0].nValue.GetAmount() - LOWFEE); hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails @@ -266,7 +266,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 1001; ++i) { - tx.vout[0].nValue -= LOWFEE; + tx.vout[0].nValue = CTxOutValue(tx.vout[0].nValue.GetAmount() - LOWFEE); hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes @@ -287,7 +287,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = BLOCKSUBSIDY; for (unsigned int i = 0; i < 128; ++i) { - tx.vout[0].nValue -= LOWFEE; + tx.vout[0].nValue = CTxOutValue(tx.vout[0].nValue.GetAmount() - LOWFEE); hash = tx.GetHash(); bool spendsCoinbase = (i == 0) ? true : false; // only first tx spends coinbase mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].scriptSig = CScript() << OP_1; tx.vin[1].prevout.hash = txFirst[0]->GetHash(); tx.vin[1].prevout.n = 0; - tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee + tx.vout[0].nValue = CTxOutValue(tx.vout[0].nValue.GetAmount()+BLOCKSUBSIDY-HIGHERFEE); //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); @@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector(script.begin(), script.end()); - tx.vout[0].nValue -= LOWFEE; + tx.vout[0].nValue = CTxOutValue(tx.vout[0].nValue.GetAmount() - LOWFEE); hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK_THROW(BlockAssembler(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index f0951255a7..8b68b90653 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -171,12 +171,16 @@ return; #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << tx2; + CDataStream streamVal1(SER_NETWORK, PROTOCOL_VERSION); + streamVal1 << txCredit.vout[0].nValue; + CDataStream streamVal2(SER_NETWORK, PROTOCOL_VERSION); + streamVal2 << CTxOutValue(); int libconsensus_flags = flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_ALL; if (libconsensus_flags == flags) { if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, -1, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&streamVal1[0], streamVal1.size(), (const unsigned char*)&streamVal2[0], streamVal2.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message); } else { - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, -1, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&streamVal2[0], streamVal2.size(), (const unsigned char*)&streamVal2[0], streamVal2.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect, message); BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL) == expect,message); } } diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 29f22c485f..f7e175c8eb 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -156,7 +156,7 @@ TestChain100Setup::~TestChain100Setup() CAmount TotalValueOut(const CMutableTransaction& tx) { CAmount nTotal = 0; BOOST_FOREACH(const CTxOut& txo, tx.vout) - nTotal += txo.nValue; + nTotal += txo.nValue.GetAmount(); return nTotal; } diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index cde5f86687..0b69334489 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -46,7 +46,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) spends[i].vout.resize(1); spends[i].vout[0].nValue = 11*CENT; spends[i].vout[0].scriptPubKey = scriptPubKey; - spends[i].nTxFee = coinbaseTxns[0].vout[0].nValue - 11*CENT; + spends[i].nTxFee = coinbaseTxns[0].vout[0].nValue.GetAmount() - 11*CENT; // Sign: std::vector vchSig; diff --git a/src/validation.cpp b/src/validation.cpp index 6dfac39d86..a0a91a2103 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -504,11 +504,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe CAmount nValueOut = 0; for (const auto& txout : tx.vout) { - if (txout.nValue < 0) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative"); - if (txout.nValue > MAX_MONEY) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge"); - nValueOut += txout.nValue; + if (!txout.nValue.IsValid()) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-amount-invalid"); + if (!txout.nValue.IsAmount()) + continue; + nValueOut += txout.nValue.GetAmount(); if (!MoneyRange(nValueOut)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } @@ -1432,9 +1432,12 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins } // Check for negative or overflow input values - nValueIn += coins->vout[prevout.n].nValue; - if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + const CTxOutValue& value = coins->vout[prevout.n].nValue; + if (value.IsAmount()) { + nValueIn += value.GetAmount(); + if (!MoneyRange(value.GetAmount()) || !MoneyRange(nValueIn)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + } if (coins->vout[prevout.n].scriptPubKey.IsWithdrawLock() && tx.vin[i].scriptSig.IsWithdrawProof()) { uint256 genesisHash(coins->vout[prevout.n].scriptPubKey.GetWithdrawLockGenesisHash()); @@ -1518,7 +1521,11 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // super-majority signaling has occurred. return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } - prevValueIn = coins->vout[tx.vin[i].prevout.n].nValue; + const CTxOutValue& value = coins->vout[tx.vin[i].prevout.n].nValue; + if (value.IsAmount()) + prevValueIn = value.GetAmount(); + else + prevValueIn = -1; } } } diff --git a/src/validation.h b/src/validation.h index 57f4c13558..3b2fe034f6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -449,8 +449,8 @@ class CScriptCheck { private: CScript scriptPubKey; - CAmount amount; - CAmount amountPreviousInput; + CTxOutValue amount; + CTxOutValue amountPreviousInput; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -460,7 +460,7 @@ private: public: CScriptCheck(): amount(0), amountPreviousInput(-1), ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} - CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, CAmount amountPreviousInputIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, const CTxOutValue& amountPreviousInputIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), amount(txFromIn.vout[txToIn.vin[nInIn].prevout.n].nValue), amountPreviousInput(amountPreviousInputIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index bd755c1978..d7a76e9ae1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -592,7 +592,7 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) BOOST_FOREACH(const CTxOut& txout, wtx.tx->vout) if (txout.scriptPubKey == scriptPubKey) if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; + nAmount += txout.nValue.GetAmount(); } return ValueFromAmount(nAmount); @@ -648,7 +648,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; + nAmount += txout.nValue.GetAmount(); } } @@ -1187,7 +1187,7 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) continue; tallyitem& item = mapTally[address]; - item.nAmount += txout.nValue; + item.nAmount += txout.nValue.GetAmount(); item.nConf = min(item.nConf, nDepth); item.txids.push_back(wtx.GetHash()); if (mine & ISMINE_WATCH_ONLY) @@ -2503,7 +2503,7 @@ UniValue listunspent(const JSONRPCRequest& request) } entry.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); - entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue))); + entry.push_back(Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue.GetAmount()))); entry.push_back(Pair("confirmations", out.nDepth)); entry.push_back(Pair("spendable", out.fSpendable)); entry.push_back(Pair("solvable", out.fSolvable)); @@ -2910,15 +2910,15 @@ UniValue bumpfee(const JSONRPCRequest& request) assert(nDelta > 0); CMutableTransaction tx(*(wtx.tx)); CTxOut* poutput = &(tx.vout[nOutput]); - if (poutput->nValue < nDelta) { + if (poutput->nValue.GetAmount() < nDelta) { throw JSONRPCError(RPC_MISC_ERROR, "Change output is too small to bump the fee"); } // If the output would become dust, discard it (converting the dust to fee) - poutput->nValue -= nDelta; - if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) { + poutput->nValue = CTxOutValue(poutput->nValue.GetAmount() - nDelta); + if (poutput->nValue.GetAmount() <= poutput->GetDustThreshold(::dustRelayFee)) { LogPrint("rpc", "Bumping fee and discarding dust output\n"); - nNewFee += poutput->nValue; + nNewFee += poutput->nValue.GetAmount(); tx.vout.erase(tx.vout.begin() + nOutput); } @@ -2936,7 +2936,7 @@ UniValue bumpfee(const JSONRPCRequest& request) std::map::const_iterator mi = pwalletMain->mapWallet.find(input.prevout.hash); assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size()); const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; - const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; + const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue.GetAmount(); SignatureData sigdata; if (!ProduceSignature(TransactionSignatureCreator(pwalletMain, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index dd4a4d1569..6151f164cd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -75,7 +75,7 @@ struct CompareValueOnly std::string COutput::ToString() const { - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); + return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue.GetAmount())); } const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const @@ -1226,7 +1226,7 @@ CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.tx->vout.size()) if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) - return prev.tx->vout[txin.prevout.n].nValue; + return prev.tx->vout[txin.prevout.n].nValue.GetAmount(); } } return 0; @@ -1239,9 +1239,9 @@ isminetype CWallet::IsMine(const CTxOut& txout) const CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const { - if (!MoneyRange(txout.nValue)) + if (!MoneyRange(txout.nValue.GetAmount())) throw std::runtime_error(std::string(__func__) + ": value out of range"); - return ((IsMine(txout) & filter) ? txout.nValue : 0); + return ((IsMine(txout) & filter) ? txout.nValue.GetAmount() : 0); } bool CWallet::IsChange(const CTxOut& txout) const @@ -1268,9 +1268,9 @@ bool CWallet::IsChange(const CTxOut& txout) const CAmount CWallet::GetChange(const CTxOut& txout) const { - if (!MoneyRange(txout.nValue)) + if (!MoneyRange(txout.nValue.GetAmount())) throw std::runtime_error(std::string(__func__) + ": value out of range"); - return (IsChange(txout) ? txout.nValue : 0); + return (IsChange(txout) ? txout.nValue.GetAmount() : 0); } bool CWallet::IsMine(const CTransaction& tx) const @@ -1492,7 +1492,7 @@ void CWalletTx::GetAmounts(list& listReceived, address = CNoDestination(); } - COutputEntry output = {address, txout.nValue, (int)i}; + COutputEntry output = {address, txout.nValue.GetAmount(), (int)i}; // If we are debited by the transaction, add the output as a "sent" entry if (nDebit > 0) @@ -2081,7 +2081,7 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { isminetype mine = IsMine(pcoin->tx->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && - !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) && + !IsLockedCoin((*it).first, i) && (pcoin->tx->vout[i].nValue.GetAmount() > 0 || fIncludeZeroValue) && (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) vCoins.push_back(COutput(pcoin, i, nDepth, ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || @@ -2167,7 +2167,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMin continue; int i = output.i; - CAmount n = pcoin->tx->vout[i].nValue; + CAmount n = pcoin->tx->vout[i].nValue.GetAmount(); pair > coin = make_pair(n,make_pair(pcoin, i)); @@ -2254,7 +2254,7 @@ bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& { if (!out.fSpendable) continue; - nValueRet += out.tx->tx->vout[out.i].nValue; + nValueRet += out.tx->tx->vout[out.i].nValue.GetAmount(); setCoinsRet.insert(make_pair(out.tx, out.i)); } return (nValueRet >= nTargetValue); @@ -2276,7 +2276,7 @@ bool CWallet::SelectCoins(const vector& vAvailableCoins, const CAmount& // Clearly invalid input, fail if (pcoin->tx->vout.size() <= outpoint.n) return false; - nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue; + nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue.GetAmount(); setPresetCoins.insert(make_pair(pcoin, outpoint.n)); } else return false; // TODO: Allow non-wallet inputs @@ -2320,7 +2320,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov for (size_t idx = 0; idx < tx.vout.size(); idx++) { const CTxOut& txOut = tx.vout[idx]; - CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1}; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue.GetAmount(), setSubtractFeeFromOutputs.count(idx) == 1}; vecSend.push_back(recipient); } @@ -2456,12 +2456,12 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt if (recipient.fSubtractFeeFromAmount) { - txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient + txout.nValue = CTxOutValue(txout.nValue.GetAmount() - (nFeeRet / nSubtractFeeFromAmount)); // Subtract fee equally from each selected recipient if (fFirst) // first receiver pays the remainder not divisible by output count { fFirst = false; - txout.nValue -= nFeeRet % nSubtractFeeFromAmount; + txout.nValue = CTxOutValue(txout.nValue.GetAmount() - (nFeeRet % nSubtractFeeFromAmount)); } } @@ -2469,7 +2469,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { - if (txout.nValue < 0) + if (txout.nValue.GetAmount() < 0) strFailReason = _("The transaction amount is too small to pay the fee"); else strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); @@ -2491,7 +2491,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt } for (const auto& pcoin : setCoins) { - CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue; + CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue.GetAmount(); //The coin age after the next block (depth+1) is used instead of the current, //reflecting an assumption the user would accept a bit more delay for //a chance at a free transaction. @@ -2545,13 +2545,13 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt // So instead we raise the change and deduct from the recipient. if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) { - CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; - newTxOut.nValue += nDust; // raise change until no more dust + CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue.GetAmount(); + newTxOut.nValue = CTxOutValue(newTxOut.nValue.GetAmount() + nDust); // raise change until no more dust for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient { if (vecSend[i].fSubtractFeeFromAmount) { - txNew.vout[i].nValue -= nDust; + txNew.vout[i].nValue = CTxOutValue(txNew.vout[i].nValue.GetAmount() - nDust); if (txNew.vout[i].IsDust(dustRelayFee)) { strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); @@ -2667,7 +2667,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) { CAmount extraFeePaid = nFeeRet - nFeeNeeded; vector::iterator change_position = txNew.vout.begin()+nChangePosInOut; - change_position->nValue += extraFeePaid; + change_position->nValue = CTxOutValue(change_position->nValue.GetAmount() + extraFeePaid); nFeeRet -= extraFeePaid; } break; // Done, enough fee included. @@ -2678,8 +2678,8 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet; vector::iterator change_position = txNew.vout.begin()+nChangePosInOut; // Only reduce change if remaining amount is still a large enough output. - if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) { - change_position->nValue -= additionalFeeNeeded; + if (change_position->nValue.GetAmount() >= MIN_FINAL_CHANGE + additionalFeeNeeded) { + change_position->nValue = CTxOutValue(change_position->nValue.GetAmount() - additionalFeeNeeded); nFeeRet += additionalFeeNeeded; break; // Done, able to increase fee from change } @@ -3146,7 +3146,7 @@ std::map CWallet::GetAddressBalances() if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) continue; - CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue; + CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue.GetAmount(); if (!balances.count(addr)) balances[addr] = 0;