wallet: account for issuances during coin selection

Prior to coin selection we need to indicate that the issuances will take
extra space, otherwise we may fail to select enough coins to cover our
fees, triggering the new "fee needed exceeds fees available" assertion.
This commit is contained in:
Andrew Poelstra 2022-09-22 13:11:07 +00:00
parent 3c896b11e0
commit e5e3ec2700
No known key found for this signature in database
GPG key ID: C588D63CE41B97C1
2 changed files with 52 additions and 0 deletions

View file

@ -1080,6 +1080,28 @@ bool CWallet::CreateTransactionInternal(
// the blinding logic.
coin_selection_params.tx_noinputs_size += 70 + 66 +(MAX_RANGEPROOF_SIZE + DEFAULT_SURJECTIONPROOF_SIZE + WITNESS_SCALE_FACTOR - 1)/WITNESS_SCALE_FACTOR;
}
// If we are going to issue an asset, add the issuance data to the noinputs_size so that
// we allocate enough coins for them.
if (issuance_details) {
size_t issue_count = 0;
for (unsigned int i = 0; i < txNew.vout.size(); i++) {
if (txNew.vout[i].nAsset.IsExplicit() && txNew.vout[i].nAsset.GetAsset() == CAsset(uint256S("1"))) {
issue_count++;
} else if (txNew.vout[i].nAsset.IsExplicit() && txNew.vout[i].nAsset.GetAsset() == CAsset(uint256S("2"))) {
issue_count++;
}
}
if (issue_count > 0) {
// Allocate space for blinding nonce, entropy, and whichever of nAmount/nInflationKeys is null
coin_selection_params.tx_noinputs_size += 2 * 32 + 2 * (2 - issue_count);
}
// Allocate non-null nAmount/nInflationKeys and rangeproofs
if (issuance_details->blind_issuance) {
coin_selection_params.tx_noinputs_size += issue_count * (33 * WITNESS_SCALE_FACTOR + MAX_RANGEPROOF_SIZE + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
} else {
coin_selection_params.tx_noinputs_size += issue_count * 9;
}
}
// Include the fees for things that aren't inputs, excluding the change output
const CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);

View file

@ -91,6 +91,36 @@ class WalletCtTest(BitcoinTestFramework):
amt = satoshi_round(Decimal(amt - Decimal(0.0005)))
self.test_send(amt, 1, 2, True)
addresses = [ self.nodes[1].getnewaddress() for i in range(15) ] \
+ [ self.nodes[2].getnewaddress() for i in range(15) ]
txid = self.nodes[2].sendmany(amounts={address: satoshi_round(Decimal(0.00025)) for address in addresses})
self.log.info(f"Sent many small UTXOs to nodes 1 and 2 in {txid}")
self.nodes[2].generate(2)
self.sync_all()
self.log.info(f"Issuing some assets from node 1")
# Try issuing assets
amt = satoshi_round(Decimal(1))
res1 = self.nodes[1].issueasset(amt, amt, True);
res2 = self.nodes[1].issueasset(amt, amt, False);
assets = [ res1["asset"], res1["token"], res2["asset"], res2["token"] ]
addresses = [ self.nodes[2].getnewaddress() for i in range(len(assets)) ]
txid = self.nodes[1].sendmany(
amounts={address: amt for address in addresses},
output_assets={addresses[i]: assets[i] for i in range(len(assets))},
)
self.log.info(f"Sent them to node 2 in {txid}")
self.nodes[1].generate(2)
self.sync_all()
# Send them back
addresses = [ self.nodes[1].getnewaddress() for i in range(len(assets)) ]
txid = self.nodes[2].sendmany(
amounts={address: amt for address in addresses},
output_assets={addresses[i]: assets[i] for i in range(len(assets))},
)
self.log.info(f"Sent them back to node 1 in {txid}")
if __name__ == '__main__':
WalletCtTest().main()