rpc: bumpfee: use correct maximum signed tx size for fee calculation
More accurate than simply adding one byte per input, and properly handles the case where the original transaction happened to have very small signatures
This commit is contained in:
parent
d625b907a1
commit
f62659448c
1 changed files with 35 additions and 4 deletions
|
@ -2664,6 +2664,33 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx)
|
||||||
|
{
|
||||||
|
CMutableTransaction txNew(tx);
|
||||||
|
std::vector<pair<CWalletTx *, unsigned int>> 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 = pwalletMain->mapWallet.find(input.prevout.hash);
|
||||||
|
assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
|
||||||
|
vCoins.emplace_back(make_pair(&(mi->second), input.prevout.n));
|
||||||
|
}
|
||||||
|
if (!pwalletMain->DummySignTx(txNew, vCoins)) {
|
||||||
|
// This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
|
||||||
|
// implies that we can sign for every input.
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed");
|
||||||
|
}
|
||||||
|
return GetVirtualTransactionSize(txNew);
|
||||||
|
}
|
||||||
|
|
||||||
UniValue bumpfee(const JSONRPCRequest& request)
|
UniValue bumpfee(const JSONRPCRequest& request)
|
||||||
{
|
{
|
||||||
if (!EnsureWalletIsAvailable(request.fHelp)) {
|
if (!EnsureWalletIsAvailable(request.fHelp)) {
|
||||||
|
@ -2769,9 +2796,9 @@ UniValue bumpfee(const JSONRPCRequest& request)
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output");
|
throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output");
|
||||||
}
|
}
|
||||||
|
|
||||||
// signature sizes can vary by a byte, so add 1 for each input when calculating the new fee
|
// Calculate the expected size of the new transaction.
|
||||||
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
|
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
|
||||||
const int64_t maxNewTxSize = txSize + wtx.tx->vin.size();
|
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx);
|
||||||
|
|
||||||
// optional parameters
|
// optional parameters
|
||||||
bool specifiedConfirmTarget = false;
|
bool specifiedConfirmTarget = false;
|
||||||
|
@ -2845,8 +2872,12 @@ UniValue bumpfee(const JSONRPCRequest& request)
|
||||||
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
|
nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
|
||||||
|
|
||||||
// New fee rate must be at least old rate + minimum incremental relay rate
|
// New fee rate must be at least old rate + minimum incremental relay rate
|
||||||
if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + walletIncrementalRelayFee.GetFeePerK()) {
|
// walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
|
||||||
nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + walletIncrementalRelayFee.GetFeePerK());
|
// in that unit (fee per kb).
|
||||||
|
// However, nOldFeeRate is a calculated value from the tx fee/size, so
|
||||||
|
// add 1 satoshi to the result, because it may have been rounded down.
|
||||||
|
if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
|
||||||
|
nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
|
||||||
nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
|
nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue