From 3ad6bbe6518e4bc545abee53439dee9752a2263b 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/bitcoin-tx.cpp | 2 +- src/coins.cpp | 20 +++++++++---- src/compressor.h | 3 +- src/core_write.cpp | 6 ++-- src/main.cpp | 25 ++++++++++------ src/main.h | 6 ++-- src/primitives/transaction.cpp | 45 ++++++++++++++++++++++++++-- src/primitives/transaction.h | 33 ++++++++++++++++++-- src/qt/coincontroldialog.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 | 6 ++-- 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/coins_tests.cpp | 4 +-- src/test/script_tests.cpp | 9 ++++-- src/test/test_bitcoin.cpp | 2 +- src/test/txvalidationcache_tests.cpp | 2 +- 27 files changed, 208 insertions(+), 85 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 2dcdcee950..b1e86e1207 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -275,10 +275,12 @@ libbitcoin_consensus_a_SOURCES = \ script/script.h \ script/script_error.cpp \ script/script_error.h \ + script/sign.cpp \ serialize.h \ tinyformat.h \ uint256.cpp \ uint256.h \ + utilmoneystr.cpp \ utilstrencodings.cpp \ utilstrencodings.h \ version.h diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index da6e4395ef..db8ebb6ad6 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -477,7 +477,7 @@ static void MutateTxSign(CMutableTransaction& tx, const 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 100c440ada..cdf477a00e 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -300,8 +300,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; } @@ -313,8 +316,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; @@ -346,8 +350,12 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, CAmount assert(coins); if (!coins->IsAvailable(txin.prevout.n)) continue; if (coins->nHeight <= nHeight) { - dResult += 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 fa702f0dfa..4052455321 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -114,7 +114,8 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { 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 8a548bd659..ca90f294ab 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -194,8 +194,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/main.cpp b/src/main.cpp index e11a9b41be..bd77e00d6e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1082,11 +1082,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state) CAmount nValueOut = 0; BOOST_FOREACH(const CTxOut& 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"); } @@ -2008,9 +2008,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()); @@ -2092,7 +2095,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/main.h b/src/main.h index 650c66dee5..c705144d34 100644 --- a/src/main.h +++ b/src/main.h @@ -415,8 +415,8 @@ class CScriptCheck { private: CScript scriptPubKey; - CAmount amount; - CAmount amountPreviousInput; + CTxOutValue amount; + CTxOutValue amountPreviousInput; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -426,7 +426,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/primitives/transaction.cpp b/src/primitives/transaction.cpp index e0865bde36..e70f8f99bb 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), nLockTime(0) {} diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 2056ccb0b2..75d9fa33cb 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -126,13 +126,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, int nType, int nVersion) { + 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() @@ -140,7 +165,7 @@ public: SetNull(); } - CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); + CTxOut(const CTxOutValue& nValueIn, CScript scriptPubKeyIn); ADD_SERIALIZE_METHODS; @@ -195,7 +220,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 837f8ba6c1..de83c4f03e 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -789,10 +789,10 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_CONFIRMATIONS, strPad(QString::number(out.nDepth), 8, " ")); // priority - double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 + double dPriority = ((double)out.tx->vout[out.i].nValue.GetAmount() / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority)); itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " ")); - dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); + dPrioritySum += (double)out.tx->vout[out.i].nValue.GetAmount() * (out.nDepth+1); nInputSum += nInputSize; // transaction hash diff --git a/src/rest.cpp b/src/rest.cpp index 2dff8d7dad..740f43e996 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -574,7 +574,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 3349733a6a..c8a3bb9e5f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -639,7 +639,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(); @@ -669,7 +670,6 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) " \"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", "") @@ -687,7 +687,6 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) 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"); } @@ -764,7 +763,8 @@ UniValue gettxout(const UniValue& params, bool fHelp) 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 60067c5e37..43c463c534 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -627,7 +627,7 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) 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 1ae5ec2ffc..9d42887bfd 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -102,7 +102,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); @@ -817,7 +818,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp) 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 f7a582abd6..2759a798b0 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -69,8 +69,8 @@ struct ECCryptoClosure ECCryptoClosure instance_of_eccryptoclosure; } -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) { @@ -86,7 +86,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP // Regardless of the verification result, the tx did not error. 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), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionNoWithdrawsSignatureChecker(&tx, nIn, amount, txdata), NULL); else return VerifyScript(tx.vin[nIn].scriptSig, CScript(scriptPubKey, scriptPubKey + scriptPubKeyLen), nIn < tx.wit.vtxinwit.size() ? &tx.wit.vtxinwit[nIn].scriptWitness : NULL, flags, TransactionSignatureChecker(&tx, nIn, amount, amountPreviousInput), NULL); @@ -95,14 +95,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, - uint64_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 + } } @@ -114,8 +125,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 0da3c0b52d..270da45fac 100644 --- a/src/script/bitcoinconsensus.h +++ b/src/script/bitcoinconsensus.h @@ -67,9 +67,9 @@ EXPORT_SYMBOL int bitcoinconsensus_verify_script(const unsigned char *scriptPubK const unsigned char *txTo , unsigned int txToLen, 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, uint64_t amount, - uint64_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 b5fbe1e1ac..67a4a67588 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 ebca53dae7..7d7bd16a8d 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 f29f5ad084..7c9083b9d0 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 2e10e32bac..7c4a208eb9 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 0b48ad9ace..f52fb7c8eb 100644 --- a/src/script/sigcache.h +++ b/src/script/sigcache.h @@ -22,7 +22,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 36738d2877..9d0caabfd8 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 { @@ -211,7 +211,7 @@ void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const Signatur } } -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 4094ac5290..803b50777a 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/coins_tests.cpp b/src/test/coins_tests.cpp index 243dae66ee..0710cc15e7 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) stack.push_back(new CCoinsViewCacheTest(&base)); // Start with one cache. // Track the txids we've used and whether they have been spent or not - std::map coinbaseids; + std::map coinbaseids; std::set alltxids; std::set duplicateids; @@ -244,7 +244,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) if (insecure_rand() % 10 == 0 || coinbaseids.size() < 10) { // 1/100 times create a duplicate coinbase if (insecure_rand() % 10 == 0 && coinbaseids.size()) { - std::map::iterator coinbaseIt = coinbaseids.lower_bound(GetRandHash()); + std::map::iterator coinbaseIt = coinbaseids.lower_bound(GetRandHash()); if (coinbaseIt == coinbaseids.end()) { coinbaseIt = coinbaseids.begin(); } diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index b0775e7264..86e1ad5eee 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -173,10 +173,15 @@ return; #if defined(HAVE_CONSENSUS_LIB) CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); stream << tx2; + CDataStream streamVal1(SER_NETWORK, PROTOCOL_VERSION); + stream << txCredit.vout[0].nValue; + CDataStream streamVal2(SER_NETWORK, PROTOCOL_VERSION); + stream << CTxOutValue(); + if (flags & bitcoinconsensus_SCRIPT_FLAGS_VERIFY_WITNESS) { - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), txCredit.vout[0].nValue, -1, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&streamVal1[0], streamVal1.size(), (const unsigned char*)&streamVal2[0], streamVal2.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); } else { - BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), 0, -1, (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); + BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&streamVal2[0], streamVal2.size(), (const unsigned char*)&streamVal2[0], streamVal2.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(begin_ptr(scriptPubKey), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, flags, NULL) == expect,message); } #endif diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 4acedc4b48..761b1c2a89 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -145,7 +145,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 ef1f12bac9..06fb892356 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -45,7 +45,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;