From e5e3ec2700a1f72351e2d227da68a7cc07c7285a Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 22 Sep 2022 13:11:07 +0000 Subject: [PATCH] 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. --- src/wallet/spend.cpp | 22 ++++++++++++++ .../wallet_elements_regression_1172.py | 30 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 930e8ff204..3bd0ff9fc8 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -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); diff --git a/test/functional/wallet_elements_regression_1172.py b/test/functional/wallet_elements_regression_1172.py index e070a4b1e8..0c9afa6db9 100755 --- a/test/functional/wallet_elements_regression_1172.py +++ b/test/functional/wallet_elements_regression_1172.py @@ -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()