From dcc378adae17d50da0f7876d731be629966c83e0 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 14 Dec 2016 13:13:29 -0500 Subject: [PATCH] Allow arbitrary assets for fees --- src/primitives/transaction.cpp | 23 ++++++++++++++--------- src/primitives/transaction.h | 4 ++-- src/qt/transactiondesc.cpp | 2 +- src/qt/transactionrecord.cpp | 2 +- src/rpc/rawtransaction.cpp | 8 +++++--- src/test/transaction_tests.cpp | 2 +- src/validation.cpp | 30 ++++++++++++++++-------------- src/validation.h | 2 +- src/wallet/rpcwallet.cpp | 4 ++-- src/wallet/wallet.cpp | 2 +- 10 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index d45e6de7da..1b97b9876d 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -158,25 +158,30 @@ uint256 CTransaction::GetWitnessHash() const bool CTransaction::HasValidFee() const { - CAmount totalFee = 0; + CAmountMap totalFee; for (unsigned int i = 0; i < vout.size(); i++) { CAmount fee = 0; - if (vout[i].IsFee()) + if (vout[i].IsFee()) { fee = vout[i].nValue.GetAmount(); - if (fee == 0 || !MoneyRange(fee)) { - return false; + if (fee == 0 || !MoneyRange(fee)) + return false; + uint256 assetid; + vout[i].nAsset.GetAssetID(assetid); + totalFee[assetid] += fee; } - totalFee += fee; } return MoneyRange(totalFee); } -CAmount CTransaction::GetFee() const +CAmountMap CTransaction::GetFee() const { - CAmount fee = 0; + CAmountMap fee; for (unsigned int i = 0; i < vout.size(); i++) - if (vout[i].IsFee()) - fee += vout[i].nValue.GetAmount(); + if (vout[i].IsFee()) { + uint256 assetid; + vout[i].nAsset.GetAssetID(assetid); + fee[assetid] += vout[i].nValue.GetAmount(); + } return fee; } diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 16deba00c1..056f24108b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -370,7 +370,7 @@ public: bool IsFee() const { uint256 assetid; - if (scriptPubKey == CScript() && nValue.IsAmount() && nAsset.GetAssetID(assetid) && assetid == BITCOINID) + if (scriptPubKey == CScript() && nValue.IsAmount() && nAsset.IsAssetID()) return true; return false; } @@ -664,7 +664,7 @@ public: bool HasValidFee() const; // Compute the fee from the explicit fee outputs. Must call HasValidFee first - CAmount GetFee() const; + CAmountMap GetFee() const; // Compute priority, given priority of inputs and (optionally) tx size double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 900ac39a66..59d1d17b6b 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -211,7 +211,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; } - CAmount nTxFee = wtx.tx->GetFee(); + CAmount nTxFee = wtx.tx->GetFee()[BITCOINID]; if (nTxFee > 0) strHTML += "" + tr("Transaction fee") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nTxFee) + "
"; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 010c84b890..06de13397b 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -113,7 +113,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // // Debit // - CAmount nTxFee = wtx.tx->GetFee(); + CAmount nTxFee = wtx.tx->GetFee()[BITCOINID]; for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++) { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 54b4c91e9f..fd12fef8d7 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -94,7 +94,6 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) entry.push_back(Pair("vsize", (int)::GetVirtualTransactionSize(tx))); entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); - entry.push_back(Pair("fee", ValueFromAmount(tx.GetFee()))); UniValue vin(UniValue::VARR); for (unsigned int i = 0; i < tx.vin.size(); i++) { @@ -127,7 +126,10 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) const CTxOut& txout = tx.vout[i]; UniValue out(UniValue::VOBJ); if (txout.nValue.IsAmount()) - out.push_back(Pair("value", ValueFromAmount(txout.nValue.GetAmount()))); + if (txout.IsFee()) + out.push_back(Pair("fee_value", ValueFromAmount(txout.nValue.GetAmount()))); + else + out.push_back(Pair("value", ValueFromAmount(txout.nValue.GetAmount()))); else { int exp; int mantissa; @@ -213,7 +215,6 @@ UniValue getrawtransaction(const JSONRPCRequest& request) " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" - " \"fee\" : x.xxx, (numeric) The transaction fee in " + CURRENCY_UNIT + "\n" " \"vin\" : [ (array of json objects)\n" " {\n" " \"txid\": \"id\", (string) The transaction id\n" @@ -230,6 +231,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request) " \"vout\" : [ (array of json objects)\n" " {\n" " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" + " \"fee_value\" : x.xxx, (numeric) The fee value in " + CURRENCY_UNIT + "\n" " \"n\" : n, (numeric) index\n" " \"assetid\" : \"hex\" (string) the asset id, if unblinded\n" " \"assettag\" : \"hex\" (string) the asset tag, if blinded\n" diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 827334e90c..873b2f4c7f 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -340,7 +340,7 @@ BOOST_AUTO_TEST_CASE(test_Get) t1.vout.resize(1); t1.vout[0].nValue = 90*CENT; t1.vout[0].scriptPubKey << OP_1; - BOOST_CHECK(CTransaction(t1).GetFee() == (50+21+22)*CENT - 90*CENT); + BOOST_CHECK(CTransaction(t1).GetFee()[BITCOINID] == (50+21+22)*CENT - 90*CENT); BOOST_CHECK(AreInputsStandard(t1, coins)); BOOST_CHECK(VerifyAmounts(coins, t1)); diff --git a/src/validation.cpp b/src/validation.cpp index 606b5b0105..a114ecded1 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -532,8 +532,10 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe // Coinbase transactions may not have eccessive scriptSigs or any fee outputs if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); - if (tx.HasValidFee() || tx.GetFee() != 0) - return state.DoS(100, false, REJECT_INVALID, "bad-cb-fee"); + + for (unsigned int i = 0; i < tx.vout.size(); i++) + if (tx.vout[i].IsFee()) + return state.DoS(100, false, REJECT_INVALID, "bad-cb-fee"); } else { @@ -824,16 +826,16 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, std::ve return true; } -bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmount& fees, const uint256& feeID) +bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmountMap& mapFees) { assert(tx.IsCoinBase()); - CAmount remaining = fees; + CAmountMap remaining = mapFees; for (unsigned int i = 0; i < tx.vout.size(); i++) { if (!tx.vout[i].nValue.IsAmount() || !tx.vout[i].nAsset.IsAssetID()) return false; - if (feeID != BITCOINID) - return false; - remaining -= tx.vout[i].nValue.GetAmount(); + uint256 assetid; + tx.vout[i].nAsset.GetAssetID(assetid); + remaining[assetid] -= tx.vout[i].nValue.GetAmount(); } return MoneyRange(remaining); } @@ -1032,7 +1034,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C if (!tx.HasValidFee()) return state.DoS(0, false, REJECT_INVALID, "bad-fees"); - CAmount nFees = tx.GetFee(); + CAmount nFees = tx.GetFee()[BITCOINID]; // nModifiedFees includes any fee deltas from PrioritiseTransaction CAmount nModifiedFees = nFees; @@ -2480,7 +2482,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CCheckQueueControl control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL); std::vector prevheights; - CAmount nFees = 0; + CAmountMap mapFees; int nInputs = 0; int64_t nSigOpsCost = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); @@ -2555,20 +2557,20 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } if (!tx.HasValidFee()) return state.DoS(100, error("ConnectBlock(): transaction fee overflowed"), REJECT_INVALID, "bad-fee-outofrange"); - nFees += tx.GetFee(); - if (!MoneyRange(nFees)) + mapFees += tx.GetFee(); + if (!MoneyRange(mapFees)) return state.DoS(100, error("ConnectBlock(): total block reward overflowed"), REJECT_INVALID, "bad-blockreward-outofrange"); } int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); - CAmount blockReward = nFees; + CAmountMap blockReward = mapFees; if (!MoneyRange(blockReward)) return state.DoS(100, error("ConnectBlock(): total block reward overflowed"), REJECT_INVALID, "bad-blockreward-outofrange"); - if (!VerifyCoinbaseAmount(*(block.vtx[0]), -blockReward, BITCOINID)) + if (!VerifyCoinbaseAmount(*(block.vtx[0]), blockReward)) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (limit=%d)", - blockReward), + blockReward[BITCOINID]), REJECT_INVALID, "bad-cb-amount"); //Don't DoS ban in case of RPC script check failure diff --git a/src/validation.h b/src/validation.h index b5a1e28942..64d61874a8 100644 --- a/src/validation.h +++ b/src/validation.h @@ -410,7 +410,7 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, std::ve * Verify the amounts of coinbase transactions. It will fail for any blinded amount or type. * Each output must be IsAmount && IsAssetID. */ -bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmount& fees, const uint256& feeID); +bool VerifyCoinbaseAmount(const CTransaction& tx, const CAmountMap& mapFees); /** * Check if transaction is final and can be included in a block with the diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c5bfdbbc63..a2c5464651 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1961,7 +1961,7 @@ UniValue gettransaction(const JSONRPCRequest& request) CAmountMap nCredit = wtx.GetCredit(filter); CAmountMap nDebit = wtx.GetDebit(filter); assert(wtx.tx->HasValidFee()); - CAmount nFee = (wtx.IsFromMe(filter) ? -wtx.tx->GetFee() : 0); + CAmount nFee = (wtx.IsFromMe(filter) ? -wtx.tx->GetFee()[BITCOINID] : 0); CAmountMap nNet = nCredit - nDebit; nNet[pwalletMain->GetAssetIDFromLabel("bitcoin")] -= nFee; @@ -3015,7 +3015,7 @@ UniValue bumpfee(const JSONRPCRequest& request) } // calculate the old fee and fee-rate - CAmount nOldFee = wtx.tx->GetFee(); + CAmount nOldFee = wtx.tx->GetFee()[BITCOINID]; CFeeRate nOldFeeRate(nOldFee, txSize); CAmount nNewFee; CFeeRate nNewFeeRate; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ff66176c1f..82db94fc01 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1466,7 +1466,7 @@ void CWalletTx::GetAmounts(list& listReceived, CAmountMap nDebit = GetDebit(filter); if (nDebit > CAmountMap()) // debit>0 means we signed/sent this transaction { - nFee = tx->GetFee(); + nFee = tx->GetFee()[BITCOINID]; } CTxDestination addressUnaccounted = CNoDestination();