Calculate and store the number of bytes required to spend an input
This commit is contained in:
parent
a34ac6ae07
commit
12ec29d3bb
8 changed files with 127 additions and 68 deletions
|
@ -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);
|
||||
}
|
||||
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
|
||||
|
|
|
@ -258,3 +258,8 @@ int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t 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). */
|
||||
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
|
||||
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
|
||||
|
|
|
@ -194,11 +194,16 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
|
|||
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)
|
||||
{
|
||||
assert(tx.vin.size() > nIn);
|
||||
tx.vin[nIn].scriptSig = data.scriptSig;
|
||||
tx.vin[nIn].scriptWitness = data.scriptWitness;
|
||||
UpdateInput(tx.vin[nIn], data);
|
||||
}
|
||||
|
||||
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. */
|
||||
SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn);
|
||||
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
|
||||
* have all private keys. While this function does not need private keys, the passed
|
||||
|
|
|
@ -16,33 +16,6 @@
|
|||
#include <util.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
|
||||
//! 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)
|
||||
|
|
|
@ -1543,6 +1543,79 @@ int CWalletTx::GetRequestCount() const
|
|||
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,
|
||||
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);
|
||||
FeeCalculation feeCalc;
|
||||
CAmount nFeeNeeded;
|
||||
unsigned int nBytes;
|
||||
int nBytes;
|
||||
{
|
||||
std::set<CInputCoin> setCoins;
|
||||
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(),
|
||||
nSequence));
|
||||
|
||||
// Fill in dummy signatures for fee calculation.
|
||||
if (!DummySignTx(txNew, setCoins)) {
|
||||
nBytes = CalculateMaximumSignedTxSize(txNew, this);
|
||||
if (nBytes < 0) {
|
||||
strFailReason = _("Signing transaction failed");
|
||||
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);
|
||||
if (feeCalc.reason == FeeReason::FALLBACK && !g_wallet_allow_fallback_fee) {
|
||||
// eventually allow a fallback fee
|
||||
|
|
|
@ -269,6 +269,9 @@ public:
|
|||
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.
|
||||
* 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 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,
|
||||
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
|
||||
|
||||
|
@ -525,6 +534,9 @@ public:
|
|||
int i;
|
||||
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 */
|
||||
bool fSpendable;
|
||||
|
||||
|
@ -540,7 +552,12 @@ public:
|
|||
|
||||
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;
|
||||
|
@ -981,8 +998,14 @@ public:
|
|||
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
|
||||
bool AddAccountingEntry(const CAccountingEntry&);
|
||||
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
|
||||
template <typename ContainerType>
|
||||
bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const;
|
||||
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts) 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 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);
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue