Skip to content

Commit

Permalink
wallet: account for issuances during coin selection
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
apoelstra committed Sep 22, 2022
1 parent 3c896b1 commit e5e3ec2
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/wallet/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
30 changes: 30 additions & 0 deletions test/functional/wallet_elements_regression_1172.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,36 @@ def run_test(self):
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()

0 comments on commit e5e3ec2

Please sign in to comment.