zcashd/src/primitives/transaction.cpp

441 lines
18 KiB
C++
Raw Permalink Normal View History

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2016-2023 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
2014-11-18 13:03:02 -08:00
#include "primitives/transaction.h"
Fix the dust threshold rate to three times 100 zats/1000 bytes. (We express it that way rather than 300 zats/1000 bytes, because the threshold is always rounded to an integer and then multiplied by 3.) Bitcoin Core added the concept of "dust" in bitcoin/bitcoin#2577. At that point the dust threshold was tied to three times the minRelayTxFee rate, with the motivation that if you'd pay more than a third of the minimum relay fee to spend something, it should be considered dust. This was implemented as a standard rule rejecting dust outputs. This motivation will not apply after ZIP 317 block construction is implemented: at that point the ZIP 317 marginal fee will be 5000 zats per logical action, but the dust threshold rate will still be three times 100 zats per 1000 bytes. Those costs would only coincide if the marginal size per logical action were 5000/300 * 1000 ~= 16667 bytes, and in practice the marginal size for any kind of input is much smaller than that. However, to avoid interoperability problems (older wallets creating transactions that newer nodes will reject because they view the outputs as dust), we will have to coordinate any increase in the dust threshold carefully. More history: in Zcash the minRelayTxFee rate was 5000 zats/1000 bytes at launch, changed to 1000 zats/1000 bytes in zcashd v1.0.3 and to 100 zats/1000 bytes in zcashd v1.0.7-1 (#2141). The relaying problem for shielded transactions (#1969) that prompted the latter change was fixed more thoroughly by the addition of `CFeeRate::GetFeeForRelay` in #4916, ensuring that a transaction paying `DEFAULT_FEE` can always be relayed. At the same time the default fee was set to 1000 zats, per ZIP 313. An earlier commit in this PR changed relaying policy to be more strict about enforcing minRelayTxFee. The commit just before this one also allowed `-minrelaytxfee=0`, which we are going to use to avoid some test breakage. But if the dust threshold rate were still set to three times the minRelayTxFee rate, then setting `-minrelaytxfee=0` would have the side effect of setting the dust threshold to zero, which is not intended. Bitcoin Core took a different approach to disentangling the dust threshold from the relay threshold, adding a `-dustrelayfee` option (bitcoin/bitcoin#9380). We don't want to do that because it is likely that we will change the dust policy again, and adding a user-visible config option might conflict with that. Also, it isn't a good idea for the dust threshold rate to be configurable per node; it's a standard rule parameter and should only be changed with network-wide coordination (if it is increased then wallets have to change before nodes, and vice versa if it is decreased). So for now we set it to a constant that matches the behaviour before this PR. Since we can no longer modify the dust threshold, we remove a check from transaction_tests.cpp that relied on doing so. This change also indirectly fixes a false-positive assertion error that would occur in `SpendableInputs::LimitToAmount` if we allowed the dust threshold to be zero. Signed-off-by: Daira Emma Hopwood <daira@jacaranda.org>
2023-04-12 08:58:24 -07:00
#include "policy/policy.h"
#include "hash.h"
#include "tinyformat.h"
scripted-diff: Move util files to separate directory. -BEGIN VERIFY SCRIPT- mkdir -p src/util git mv src/util.h src/util/system.h git mv src/util.cpp src/util/system.cpp git mv src/utilmoneystr.h src/util/moneystr.h git mv src/utilmoneystr.cpp src/util/moneystr.cpp git mv src/utilstrencodings.h src/util/strencodings.h git mv src/utilstrencodings.cpp src/util/strencodings.cpp git mv src/utiltime.h src/util/time.h git mv src/utiltime.cpp src/util/time.cpp sed -i -e 's/"util\.h"/"util\/system\.h"/g' $(git ls-files 'src/*.h' 'src/*.cpp') git checkout HEAD -- src/secp256k1 # exclude secp256k1, which has its own "util.h" sed -i -e 's/"utilmoneystr\.h"/"util\/moneystr\.h"/g' $(git ls-files 'src/*.h' 'src/*.cpp') sed -i -e 's/"utilstrencodings\.h"/"util\/strencodings\.h"/g' $(git ls-files 'src/*.h' 'src/*.cpp') sed -i -e 's/<utilstrencodings\.h>/<util\/strencodings\.h>/g' $(git ls-files 'src/*.h' 'src/*.cpp') sed -i -e 's/"utiltime\.h"/"util\/time\.h"/g' $(git ls-files 'src/*.h' 'src/*.cpp') sed -i -e 's/BITCOIN_UTIL_H/BITCOIN_UTIL_SYSTEM_H/g' src/util/system.h sed -i -e 's/BITCOIN_UTILMONEYSTR_H/BITCOIN_UTIL_MONEYSTR_H/g' src/util/moneystr.h sed -i -e 's/BITCOIN_UTILSTRENCODINGS_H/BITCOIN_UTIL_STRENCODINGS_H/g' src/util/strencodings.h sed -i -e 's/BITCOIN_UTILTIME_H/BITCOIN_UTIL_TIME_H/g' src/util/time.h sed -i -e 's/ util\.\(h\|cpp\)/ util\/system\.\1/g' src/Makefile.am sed -i -e 's/utilmoneystr\.\(h\|cpp\)/util\/moneystr\.\1/g' src/Makefile.am sed -i -e 's/utilstrencodings\.\(h\|cpp\)/util\/strencodings\.\1/g' src/Makefile.am sed -i -e 's/utiltime\.\(h\|cpp\)/util\/time\.\1/g' src/Makefile.am sed -i -e 's/src\/util\.cpp/src\/util\/system\.cpp/g' test/lint/lint-locale-dependence.sh sed -i -e 's/src\/utilmoneystr\.cpp/src\/util\/moneystr\.cpp/g' test/lint/lint-locale-dependence.sh sed -i -e 's/src\/utilstrencodings\.\(h\|cpp\)/src\/util\/strencodings\.\1/g' test/lint/lint-locale-dependence.sh -END VERIFY SCRIPT-
2018-10-22 15:51:11 -07:00
#include "util/strencodings.h"
#include "zip317.h"
2021-06-11 13:34:15 -07:00
#include <rust/transaction.h>
SaplingBundle::SaplingBundle(
const std::vector<SpendDescription>& vShieldedSpend,
const std::vector<OutputDescription>& vShieldedOutput,
const CAmount& valueBalanceSapling,
const binding_sig_t& bindingSig)
: valueBalanceSapling(valueBalanceSapling), bindingSigSapling(bindingSig)
{
for (auto &spend : vShieldedSpend) {
vSpendsSapling.emplace_back(spend.cv, spend.nullifier, spend.rk);
if (anchorSapling.IsNull()) {
anchorSapling = spend.anchor;
} else {
assert(anchorSapling == spend.anchor);
}
vSpendProofsSapling.push_back(spend.zkproof);
vSpendAuthSigSapling.push_back(spend.spendAuthSig);
}
for (auto &output : vShieldedOutput) {
vOutputsSapling.emplace_back(
output.cv,
output.cmu,
output.ephemeralKey,
output.encCiphertext,
output.outCiphertext);
vOutputProofsSapling.push_back(output.zkproof);
}
}
std::vector<SpendDescription> SaplingBundle::GetV4ShieldedSpend()
{
std::vector<SpendDescription> vShieldedSpend;
for (int i = 0; i < vSpendsSapling.size(); i++) {
auto spend = vSpendsSapling[i];
vShieldedSpend.emplace_back(
spend.cv,
anchorSapling,
spend.nullifier,
spend.rk,
vSpendProofsSapling[i],
vSpendAuthSigSapling[i]);
}
return vShieldedSpend;
}
std::vector<OutputDescription> SaplingBundle::GetV4ShieldedOutput()
{
std::vector<OutputDescription> vShieldedOutput;
for (int i = 0; i < vOutputsSapling.size(); i++) {
auto output = vOutputsSapling[i];
vShieldedOutput.emplace_back(
output.cv,
output.cmu,
output.ephemeralKey,
output.encCiphertext,
output.outCiphertext,
vOutputProofsSapling[i]);
}
return vShieldedOutput;
}
std::string COutPoint::ToString() const
{
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
}
2018-07-17 10:47:08 -07:00
std::string SaplingOutPoint::ToString() const
{
return strprintf("SaplingOutPoint(%s, %u)", hash.ToString().substr(0, 10), n);
}
std::string OrchardOutPoint::ToString() const
{
return strprintf("OrchardOutPoint(%s, %u)", hash.ToString().substr(0, 10), n);
}
CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = prevoutIn;
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
CTxIn::CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = COutPoint(hashPrevTx, nOut);
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
std::string CTxIn::ToString() const
{
std::string str;
str += "CTxIn(";
str += prevout.ToString();
if (prevout.IsNull())
str += strprintf(", coinbase %s", HexStr(scriptSig));
else
str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24));
if (nSequence != std::numeric_limits<unsigned int>::max())
str += strprintf(", nSequence=%u", nSequence);
str += ")";
return str;
}
CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
{
nValue = nValueIn;
scriptPubKey = scriptPubKeyIn;
}
Fix the dust threshold rate to three times 100 zats/1000 bytes. (We express it that way rather than 300 zats/1000 bytes, because the threshold is always rounded to an integer and then multiplied by 3.) Bitcoin Core added the concept of "dust" in bitcoin/bitcoin#2577. At that point the dust threshold was tied to three times the minRelayTxFee rate, with the motivation that if you'd pay more than a third of the minimum relay fee to spend something, it should be considered dust. This was implemented as a standard rule rejecting dust outputs. This motivation will not apply after ZIP 317 block construction is implemented: at that point the ZIP 317 marginal fee will be 5000 zats per logical action, but the dust threshold rate will still be three times 100 zats per 1000 bytes. Those costs would only coincide if the marginal size per logical action were 5000/300 * 1000 ~= 16667 bytes, and in practice the marginal size for any kind of input is much smaller than that. However, to avoid interoperability problems (older wallets creating transactions that newer nodes will reject because they view the outputs as dust), we will have to coordinate any increase in the dust threshold carefully. More history: in Zcash the minRelayTxFee rate was 5000 zats/1000 bytes at launch, changed to 1000 zats/1000 bytes in zcashd v1.0.3 and to 100 zats/1000 bytes in zcashd v1.0.7-1 (#2141). The relaying problem for shielded transactions (#1969) that prompted the latter change was fixed more thoroughly by the addition of `CFeeRate::GetFeeForRelay` in #4916, ensuring that a transaction paying `DEFAULT_FEE` can always be relayed. At the same time the default fee was set to 1000 zats, per ZIP 313. An earlier commit in this PR changed relaying policy to be more strict about enforcing minRelayTxFee. The commit just before this one also allowed `-minrelaytxfee=0`, which we are going to use to avoid some test breakage. But if the dust threshold rate were still set to three times the minRelayTxFee rate, then setting `-minrelaytxfee=0` would have the side effect of setting the dust threshold to zero, which is not intended. Bitcoin Core took a different approach to disentangling the dust threshold from the relay threshold, adding a `-dustrelayfee` option (bitcoin/bitcoin#9380). We don't want to do that because it is likely that we will change the dust policy again, and adding a user-visible config option might conflict with that. Also, it isn't a good idea for the dust threshold rate to be configurable per node; it's a standard rule parameter and should only be changed with network-wide coordination (if it is increased then wallets have to change before nodes, and vice versa if it is decreased). So for now we set it to a constant that matches the behaviour before this PR. Since we can no longer modify the dust threshold, we remove a check from transaction_tests.cpp that relied on doing so. This change also indirectly fixes a false-positive assertion error that would occur in `SpendableInputs::LimitToAmount` if we allowed the dust threshold to be zero. Signed-off-by: Daira Emma Hopwood <daira@jacaranda.org>
2023-04-12 08:58:24 -07:00
CAmount CTxOut::GetDustThreshold() const
{
// See the comment on ONE_THIRD_DUST_THRESHOLD_RATE in policy.h.
static const CFeeRate oneThirdDustThresholdRate {ONE_THIRD_DUST_THRESHOLD_RATE};
if (scriptPubKey.IsUnspendable())
return 0;
// A typical spendable txout is 34 bytes, and will need a txin of at
// least 148 bytes to spend. With ONE_THIRD_DUST_THRESHOLD_RATE == 100,
// the dust threshold for such a txout would be
// 3*floor(100*(34 + 148)/1000) zats = 54 zats.
size_t nSize = GetSerializeSize(*this, SER_DISK, 0) + 148u;
return 3*oneThirdDustThresholdRate.GetFee(nSize);
}
uint256 CTxOut::GetHash() const
{
return SerializeHash(*this);
}
std::string CTxOut::ToString() const
{
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
}
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0), valueBalanceSapling(0) {}
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
nConsensusBranchId(tx.GetConsensusBranchId()),
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
valueBalanceSapling(tx.GetValueBalanceSapling()), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
orchardBundle(tx.GetOrchardBundle()),
2019-06-16 04:39:05 -07:00
vJoinSplit(tx.vJoinSplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
bindingSig(tx.bindingSig)
{
}
uint256 CMutableTransaction::GetHash() const
{
2021-06-11 13:34:15 -07:00
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
uint256 hash;
if (!zcash_transaction_digests(
2021-06-11 13:34:15 -07:00
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size(),
hash.begin(),
nullptr))
{
throw std::ios_base::failure("CMutableTransaction::GetHash: Invalid transaction format");
}
2021-06-11 13:34:15 -07:00
return hash;
}
uint256 CMutableTransaction::GetAuthDigest() const
{
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
uint256 authDigest;
if (!zcash_transaction_digests(
2021-06-11 13:34:15 -07:00
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size(),
nullptr,
authDigest.begin()))
{
throw std::ios_base::failure("CMutableTransaction::GetAuthDigest: Invalid transaction format");
}
2021-06-11 13:34:15 -07:00
return authDigest;
}
void CTransaction::UpdateHash() const
{
2021-06-11 13:34:15 -07:00
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *this;
if (!zcash_transaction_digests(
2021-06-11 13:34:15 -07:00
reinterpret_cast<const unsigned char*>(ss.data()),
ss.size(),
2021-09-02 06:53:40 -07:00
const_cast<uint256*>(&wtxid.hash)->begin(),
const_cast<uint256*>(&wtxid.authDigest)->begin()))
{
throw std::ios_base::failure("CTransaction::UpdateHash: Invalid transaction format");
}
}
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION),
fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0),
nConsensusBranchId(std::nullopt),
vin(), vout(), nLockTime(0),
valueBalanceSapling(0), vShieldedSpend(), vShieldedOutput(),
orchardBundle(),
vJoinSplit(), joinSplitPubKey(), joinSplitSig(),
bindingSig() { }
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
nConsensusBranchId(tx.nConsensusBranchId),
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
valueBalanceSapling(tx.valueBalanceSapling), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
orchardBundle(tx.orchardBundle),
2019-06-16 04:39:05 -07:00
vJoinSplit(tx.vJoinSplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
bindingSig(tx.bindingSig)
{
UpdateHash();
}
// Protected constructor which only derived classes can call.
// For developer testing only.
CTransaction::CTransaction(
const CMutableTransaction &tx,
bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
nConsensusBranchId(tx.nConsensusBranchId),
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
valueBalanceSapling(tx.valueBalanceSapling), vShieldedSpend(tx.vShieldedSpend), vShieldedOutput(tx.vShieldedOutput),
orchardBundle(tx.orchardBundle),
2019-06-16 04:39:05 -07:00
vJoinSplit(tx.vJoinSplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig),
bindingSig(tx.bindingSig)
{
assert(evilDeveloperFlag);
}
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion),
fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId),
nConsensusBranchId(tx.nConsensusBranchId),
vin(std::move(tx.vin)), vout(std::move(tx.vout)),
nLockTime(tx.nLockTime), nExpiryHeight(tx.nExpiryHeight),
valueBalanceSapling(tx.valueBalanceSapling),
vShieldedSpend(std::move(tx.vShieldedSpend)), vShieldedOutput(std::move(tx.vShieldedOutput)),
orchardBundle(std::move(tx.orchardBundle)),
2019-06-16 04:39:05 -07:00
vJoinSplit(std::move(tx.vJoinSplit)),
joinSplitPubKey(std::move(tx.joinSplitPubKey)), joinSplitSig(std::move(tx.joinSplitSig)),
bindingSig(std::move(tx.bindingSig))
{
UpdateHash();
}
CTransaction& CTransaction::operator=(const CTransaction &tx) {
*const_cast<bool*>(&fOverwintered) = tx.fOverwintered;
*const_cast<int*>(&nVersion) = tx.nVersion;
*const_cast<uint32_t*>(&nVersionGroupId) = tx.nVersionGroupId;
nConsensusBranchId = tx.nConsensusBranchId;
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
*const_cast<uint32_t*>(&nExpiryHeight) = tx.nExpiryHeight;
valueBalanceSapling = tx.valueBalanceSapling;
*const_cast<std::vector<SpendDescription>*>(&vShieldedSpend) = tx.vShieldedSpend;
*const_cast<std::vector<OutputDescription>*>(&vShieldedOutput) = tx.vShieldedOutput;
orchardBundle = tx.orchardBundle;
2019-06-16 04:39:05 -07:00
*const_cast<std::vector<JSDescription>*>(&vJoinSplit) = tx.vJoinSplit;
2022-06-06 15:17:07 -07:00
*const_cast<ed25519::VerificationKey*>(&joinSplitPubKey) = tx.joinSplitPubKey;
*const_cast<ed25519::Signature*>(&joinSplitSig) = tx.joinSplitSig;
*const_cast<binding_sig_t*>(&bindingSig) = tx.bindingSig;
2021-09-02 06:53:40 -07:00
*const_cast<uint256*>(&wtxid.hash) = tx.wtxid.hash;
*const_cast<uint256*>(&wtxid.authDigest) = tx.wtxid.authDigest;
return *this;
}
CAmount CTransaction::GetValueOut() const
{
CAmount nValueOut = 0;
for (const auto& out : vout) {
if (!MoneyRange(out.nValue)) {
throw std::runtime_error("CTransaction::GetValueOut(): nValue out of range");
}
nValueOut += out.nValue;
if (!MoneyRange(nValueOut)) {
throw std::runtime_error("CTransaction::GetValueOut(): nValueOut out of range");
}
}
if (valueBalanceSapling <= 0) {
// NB: negative valueBalanceSapling "takes" money from the transparent value pool just as outputs do
if (valueBalanceSapling < -MAX_MONEY) {
throw std::runtime_error("CTransaction::GetValueOut(): valueBalanceSapling out of range");
}
nValueOut += -valueBalanceSapling;
if (!MoneyRange(nValueOut)) {
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
}
}
auto valueBalanceOrchard = orchardBundle.GetValueBalance();
if (valueBalanceOrchard <= 0) {
// NB: negative valueBalanceOrchard "takes" money from the transparent value pool just as outputs do
if (valueBalanceOrchard < -MAX_MONEY) {
throw std::runtime_error("CTransaction::GetValueOut(): valueBalanceOrchard out of range");
}
nValueOut += -valueBalanceOrchard;
if (!MoneyRange(nValueOut)) {
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
}
}
for (const auto& jsDescription : vJoinSplit) {
// NB: vpub_old "takes" money from the transparent value pool just as outputs do
if (!MoneyRange(jsDescription.vpub_old)) {
throw std::runtime_error("CTransaction::GetValueOut(): vpub_old out of range");
}
nValueOut += jsDescription.vpub_old;
if (!MoneyRange(nValueOut)) {
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
}
}
return nValueOut;
}
CAmount CTransaction::GetShieldedValueIn() const
{
CAmount nValue = 0;
if (valueBalanceSapling >= 0) {
// NB: positive valueBalanceSapling "gives" money to the transparent value pool just as inputs do
if (valueBalanceSapling > MAX_MONEY) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): valueBalanceSapling out of range");
}
nValue += valueBalanceSapling;
if (!MoneyRange(nValue)) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): value out of range");
}
}
auto valueBalanceOrchard = orchardBundle.GetValueBalance();
if (valueBalanceOrchard >= 0) {
// NB: positive valueBalanceOrchard "gives" money to the transparent value pool just as inputs do
if (valueBalanceOrchard > MAX_MONEY) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): valueBalanceOrchard out of range");
}
nValue += valueBalanceOrchard;
if (!MoneyRange(nValue)) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): nValue out of range");
}
}
for (const auto& jsDescription : vJoinSplit) {
// NB: vpub_new "gives" money to the transparent value pool just as inputs do
if (!MoneyRange(jsDescription.vpub_new)) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): vpub_new out of range");
}
nValue += jsDescription.vpub_new;
if (!MoneyRange(nValue)) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): value out of range");
}
}
if (IsCoinBase() && nValue != 0) {
throw std::runtime_error("CTransaction::GetShieldedValueIn(): shielded value of inputs must be zero in coinbase transactions.");
}
return nValue;
}
CAmount CTransaction::GetConventionalFee() const {
return CalculateConventionalFee(GetLogicalActionCount());
}
size_t CTransaction::GetLogicalActionCount() const {
return CalculateLogicalActionCount(
vin,
vout,
vJoinSplit.size(),
vShieldedSpend.size(),
vShieldedOutput.size(),
orchardBundle.GetNumActions());
}
std::string CTransaction::ToString() const
{
std::string str;
if (!fOverwintered) {
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
GetHash().ToString().substr(0,10),
nVersion,
vin.size(),
vout.size(),
nLockTime);
} else if (nVersion >= SAPLING_MIN_TX_VERSION) {
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u, valueBalanceSapling=%u, vSaplingSpend.size=%u, vSaplingOutput.size=%u",
GetHash().ToString().substr(0,10),
nVersion,
fOverwintered,
nVersionGroupId,
vin.size(),
vout.size(),
nLockTime,
nExpiryHeight,
valueBalanceSapling,
vShieldedSpend.size(),
vShieldedOutput.size());
if (nVersion >= ZIP225_MIN_TX_VERSION) {
str += strprintf(", nConsensusBranchId=%08x, valueBalanceOrchard=%u, vOrchardAction.size=%u",
nConsensusBranchId.value_or(0),
orchardBundle.GetValueBalance(),
orchardBundle.GetNumActions());
}
str += ")\n";
} else if (nVersion >= 3) {
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u)\n",
GetHash().ToString().substr(0,10),
nVersion,
fOverwintered,
nVersionGroupId,
vin.size(),
vout.size(),
nLockTime,
nExpiryHeight);
}
for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
for (unsigned int i = 0; i < vout.size(); i++)
str += " " + vout[i].ToString() + "\n";
return str;
}