mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 04:52:59 +01:00
Calculate and store the number of bytes required to spend an input
This commit is contained in:
parent
a34ac6ae07
commit
12ec29d3bb
@ -101,5 +101,10 @@ static inline int64_t GetBlockWeight(const CBlock& block)
|
|||||||
{
|
{
|
||||||
return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
|
return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
}
|
}
|
||||||
|
static inline int64_t GetTransationInputWeight(const CTxIn& txin)
|
||||||
|
{
|
||||||
|
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
|
||||||
|
return ::GetSerializeSize(txin, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BITCOIN_CONSENSUS_VALIDATION_H
|
#endif // BITCOIN_CONSENSUS_VALIDATION_H
|
||||||
|
@ -258,3 +258,8 @@ int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost)
|
|||||||
{
|
{
|
||||||
return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
|
return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t GetVirtualTransactionInputSize(const CTxIn& txin, int64_t nSigOpCost)
|
||||||
|
{
|
||||||
|
return GetVirtualTransactionSize(GetTransationInputWeight(txin), nSigOpCost);
|
||||||
|
}
|
||||||
|
@ -102,5 +102,6 @@ extern unsigned int nBytesPerSigOp;
|
|||||||
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
|
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
|
||||||
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
|
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
|
||||||
int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
|
int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
|
||||||
|
int64_t GetVirtualTransactionInputSize(const CTxIn& tx, int64_t nSigOpCost = 0);
|
||||||
|
|
||||||
#endif // BITCOIN_POLICY_POLICY_H
|
#endif // BITCOIN_POLICY_POLICY_H
|
||||||
|
@ -194,11 +194,16 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UpdateInput(CTxIn& input, const SignatureData& data)
|
||||||
|
{
|
||||||
|
input.scriptSig = data.scriptSig;
|
||||||
|
input.scriptWitness = data.scriptWitness;
|
||||||
|
}
|
||||||
|
|
||||||
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data)
|
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data)
|
||||||
{
|
{
|
||||||
assert(tx.vin.size() > nIn);
|
assert(tx.vin.size() > nIn);
|
||||||
tx.vin[nIn].scriptSig = data.scriptSig;
|
UpdateInput(tx.vin[nIn], data);
|
||||||
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 CAmount& amount, int nHashType)
|
||||||
|
@ -80,6 +80,7 @@ SignatureData CombineSignatures(const CScript& scriptPubKey, const BaseSignature
|
|||||||
/** Extract signature data from a transaction, and insert it. */
|
/** Extract signature data from a transaction, and insert it. */
|
||||||
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
|
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
|
||||||
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data);
|
void UpdateTransaction(CMutableTransaction& tx, unsigned int nIn, const SignatureData& data);
|
||||||
|
void UpdateInput(CTxIn& input, const SignatureData& data);
|
||||||
|
|
||||||
/* Check whether we know how to sign for an output like this, assuming we
|
/* Check whether we know how to sign for an output like this, assuming we
|
||||||
* have all private keys. While this function does not need private keys, the passed
|
* have all private keys. While this function does not need private keys, the passed
|
||||||
|
@ -16,33 +16,6 @@
|
|||||||
#include <util.h>
|
#include <util.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
|
||||||
// Calculate the size of the transaction assuming all signatures are max size
|
|
||||||
// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
|
|
||||||
// TODO: re-use this in CWallet::CreateTransaction (right now
|
|
||||||
// CreateTransaction uses the constructed dummy-signed tx to do a priority
|
|
||||||
// calculation, but we should be able to refactor after priority is removed).
|
|
||||||
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
|
|
||||||
// be IsAllFromMe).
|
|
||||||
static int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet)
|
|
||||||
{
|
|
||||||
CMutableTransaction txNew(tx);
|
|
||||||
std::vector<CInputCoin> vCoins;
|
|
||||||
// Look up the inputs. We should have already checked that this transaction
|
|
||||||
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
|
|
||||||
// wallet, with a valid index into the vout array.
|
|
||||||
for (auto& input : tx.vin) {
|
|
||||||
const auto mi = wallet->mapWallet.find(input.prevout.hash);
|
|
||||||
assert(mi != wallet->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
|
|
||||||
vCoins.emplace_back(CInputCoin(&(mi->second), input.prevout.n));
|
|
||||||
}
|
|
||||||
if (!wallet->DummySignTx(txNew, vCoins)) {
|
|
||||||
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
|
||||||
// implies that we can sign for every input.
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return GetVirtualTransactionSize(txNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Check whether transaction has descendant in wallet or mempool, or has been
|
//! Check whether transaction has descendant in wallet or mempool, or has been
|
||||||
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
|
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
|
||||||
static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors)
|
static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors)
|
||||||
|
@ -1543,6 +1543,79 @@ int CWalletTx::GetRequestCount() const
|
|||||||
return nRequests;
|
return nRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper for producing a max-sized low-S signature (eg 72 bytes)
|
||||||
|
bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const
|
||||||
|
{
|
||||||
|
// Fill in dummy signatures for fee calculation.
|
||||||
|
const CScript& scriptPubKey = txout.scriptPubKey;
|
||||||
|
SignatureData sigdata;
|
||||||
|
|
||||||
|
if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
UpdateInput(tx_in, sigdata);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes)
|
||||||
|
bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const
|
||||||
|
{
|
||||||
|
// Fill in dummy signatures for fee calculation.
|
||||||
|
int nIn = 0;
|
||||||
|
for (const auto& txout : txouts)
|
||||||
|
{
|
||||||
|
if (!DummySignInput(txNew.vin[nIn], txout)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nIn++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet)
|
||||||
|
{
|
||||||
|
std::vector<CTxOut> txouts;
|
||||||
|
// Look up the inputs. We should have already checked that this transaction
|
||||||
|
// IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
|
||||||
|
// wallet, with a valid index into the vout array, and the ability to sign.
|
||||||
|
for (auto& input : tx.vin) {
|
||||||
|
const auto mi = wallet->mapWallet.find(input.prevout.hash);
|
||||||
|
if (mi == wallet->mapWallet.end()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(input.prevout.n < mi->second.tx->vout.size());
|
||||||
|
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
|
||||||
|
}
|
||||||
|
return CalculateMaximumSignedTxSize(tx, wallet, txouts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// txouts needs to be in the order of tx.vin
|
||||||
|
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts)
|
||||||
|
{
|
||||||
|
CMutableTransaction txNew(tx);
|
||||||
|
if (!wallet->DummySignTx(txNew, txouts)) {
|
||||||
|
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
||||||
|
// implies that we can sign for every input.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return GetVirtualTransactionSize(txNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet)
|
||||||
|
{
|
||||||
|
CMutableTransaction txn;
|
||||||
|
txn.vin.push_back(CTxIn(COutPoint()));
|
||||||
|
if (!wallet->DummySignInput(txn.vin[0], txout)) {
|
||||||
|
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
||||||
|
// implies that we can sign for every input.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return GetVirtualTransactionInputSize(txn.vin[0]);
|
||||||
|
}
|
||||||
|
|
||||||
void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
|
void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
|
||||||
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const
|
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const
|
||||||
{
|
{
|
||||||
@ -2752,7 +2825,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
|||||||
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
|
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
|
||||||
FeeCalculation feeCalc;
|
FeeCalculation feeCalc;
|
||||||
CAmount nFeeNeeded;
|
CAmount nFeeNeeded;
|
||||||
unsigned int nBytes;
|
int nBytes;
|
||||||
{
|
{
|
||||||
std::set<CInputCoin> setCoins;
|
std::set<CInputCoin> setCoins;
|
||||||
LOCK2(cs_main, cs_wallet);
|
LOCK2(cs_main, cs_wallet);
|
||||||
@ -2903,20 +2976,12 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
|||||||
txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
|
txNew.vin.push_back(CTxIn(coin.outpoint,CScript(),
|
||||||
nSequence));
|
nSequence));
|
||||||
|
|
||||||
// Fill in dummy signatures for fee calculation.
|
nBytes = CalculateMaximumSignedTxSize(txNew, this);
|
||||||
if (!DummySignTx(txNew, setCoins)) {
|
if (nBytes < 0) {
|
||||||
strFailReason = _("Signing transaction failed");
|
strFailReason = _("Signing transaction failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nBytes = GetVirtualTransactionSize(txNew);
|
|
||||||
|
|
||||||
// Remove scriptSigs to eliminate the fee calculation dummy signatures
|
|
||||||
for (auto& vin : txNew.vin) {
|
|
||||||
vin.scriptSig = CScript();
|
|
||||||
vin.scriptWitness.SetNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
|
nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
|
||||||
if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) {
|
if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) {
|
||||||
// eventually allow a fallback fee
|
// eventually allow a fallback fee
|
||||||
|
@ -269,6 +269,9 @@ public:
|
|||||||
bool IsCoinBase() const { return tx->IsCoinBase(); }
|
bool IsCoinBase() const { return tx->IsCoinBase(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//Get the marginal bytes of spending the specified output
|
||||||
|
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A transaction with a bunch of additional info that only the owner cares about.
|
* A transaction with a bunch of additional info that only the owner cares about.
|
||||||
* It includes any unrecorded transactions needed to link it back to the block chain.
|
* It includes any unrecorded transactions needed to link it back to the block chain.
|
||||||
@ -462,6 +465,12 @@ public:
|
|||||||
CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
|
CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
|
||||||
CAmount GetChange() const;
|
CAmount GetChange() const;
|
||||||
|
|
||||||
|
// Get the marginal bytes if spending the specified output from this transaction
|
||||||
|
int GetSpendSize(unsigned int out) const
|
||||||
|
{
|
||||||
|
return CalculateMaximumSignedInputSize(tx->vout[out], pwallet);
|
||||||
|
}
|
||||||
|
|
||||||
void GetAmounts(std::list<COutputEntry>& listReceived,
|
void GetAmounts(std::list<COutputEntry>& listReceived,
|
||||||
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
|
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
|
||||||
|
|
||||||
@ -525,6 +534,9 @@ public:
|
|||||||
int i;
|
int i;
|
||||||
int nDepth;
|
int nDepth;
|
||||||
|
|
||||||
|
/** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
|
||||||
|
int nInputBytes;
|
||||||
|
|
||||||
/** Whether we have the private keys to spend this output */
|
/** Whether we have the private keys to spend this output */
|
||||||
bool fSpendable;
|
bool fSpendable;
|
||||||
|
|
||||||
@ -540,7 +552,12 @@ public:
|
|||||||
|
|
||||||
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn)
|
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, bool fSolvableIn, bool fSafeIn)
|
||||||
{
|
{
|
||||||
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn;
|
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1;
|
||||||
|
// If known and signable by the given wallet, compute nInputBytes
|
||||||
|
// Failure will keep this value -1
|
||||||
|
if (fSpendable && tx) {
|
||||||
|
nInputBytes = tx->GetSpendSize(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ToString() const;
|
std::string ToString() const;
|
||||||
@ -981,8 +998,14 @@ public:
|
|||||||
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
|
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
|
||||||
bool AddAccountingEntry(const CAccountingEntry&);
|
bool AddAccountingEntry(const CAccountingEntry&);
|
||||||
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
|
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
|
||||||
template <typename ContainerType>
|
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts) const
|
||||||
bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const;
|
{
|
||||||
|
std::vector<CTxOut> v_txouts(txouts.size());
|
||||||
|
std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
|
||||||
|
return DummySignTx(txNew, v_txouts);
|
||||||
|
}
|
||||||
|
bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const;
|
||||||
|
bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const;
|
||||||
|
|
||||||
static CFeeRate minTxFee;
|
static CFeeRate minTxFee;
|
||||||
static CFeeRate fallbackFee;
|
static CFeeRate fallbackFee;
|
||||||
@ -1227,31 +1250,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes)
|
|
||||||
// ContainerType is meant to hold pair<CWalletTx *, int>, and be iterable
|
|
||||||
// so that each entry corresponds to each vIn, in order.
|
|
||||||
template <typename ContainerType>
|
|
||||||
bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const
|
|
||||||
{
|
|
||||||
// Fill in dummy signatures for fee calculation.
|
|
||||||
int nIn = 0;
|
|
||||||
for (const auto& coin : coins)
|
|
||||||
{
|
|
||||||
const CScript& scriptPubKey = coin.txout.scriptPubKey;
|
|
||||||
SignatureData sigdata;
|
|
||||||
|
|
||||||
if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
UpdateTransaction(txNew, nIn, sigdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
nIn++;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputType ParseOutputType(const std::string& str, OutputType default_type = OUTPUT_TYPE_DEFAULT);
|
OutputType ParseOutputType(const std::string& str, OutputType default_type = OUTPUT_TYPE_DEFAULT);
|
||||||
const std::string& FormatOutputType(OutputType type);
|
const std::string& FormatOutputType(OutputType type);
|
||||||
|
|
||||||
@ -1299,4 +1297,10 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Calculate the size of the transaction assuming all signatures are max size
|
||||||
|
// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
|
||||||
|
// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
|
||||||
|
// be IsAllFromMe).
|
||||||
|
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet);
|
||||||
|
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts);
|
||||||
#endif // BITCOIN_WALLET_WALLET_H
|
#endif // BITCOIN_WALLET_WALLET_H
|
||||||
|
Loading…
Reference in New Issue
Block a user