diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index dd09175c20..0ba7a537ff 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1188,7 +1188,7 @@ static RPCHelpMan decodepsbt() result.pushKV("fallback_locktime", static_cast(*psbtx.fallback_locktime)); } result.pushKV("input_count", static_cast(psbtx.inputs.size())); - result.pushKV("output_count", static_cast(psbtx.inputs.size())); + result.pushKV("output_count", static_cast(psbtx.outputs.size())); if (psbtx.m_tx_modifiable != std::nullopt) { result.pushKV("inputs_modifiable", psbtx.m_tx_modifiable->test(0)); result.pushKV("outputs_modifiable", psbtx.m_tx_modifiable->test(1)); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 32b52fded9..d17b7b8f8c 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -309,6 +309,9 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo } temp_mtx.vout = txouts; temp_mtx.witness.vtxoutwit.clear(); // ELEMENTS + for (auto& inwit : temp_mtx.witness.vtxinwit) { + inwit.scriptWitness.SetNull(); + } const int64_t maxTxSize{CalculateMaximumSignedTxSize(CTransaction(temp_mtx), &wallet, &new_coin_control).vsize}; Result res = CheckFeeRate(wallet, temp_mtx, *new_coin_control.m_feerate, maxTxSize, old_fee, errors); if (res != Result::OK) { diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index eff6f38cc6..113aececea 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -1869,6 +1869,7 @@ static util::Result CreateTransactionInternal( } // Error if this output is reduced to be below dust + txout.nValue = value; if (IsDust(txout, wallet.chain().relayDustFee())) { if (value < 0) { return util::Error{_("The transaction amount is too small to pay the fee")}; @@ -1876,8 +1877,6 @@ static util::Result CreateTransactionInternal( return util::Error{_("The transaction amount is too small to send after the fee has been deducted")}; } } - - txout.nValue = value; } ++i; } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b6d0fe3386..49f61cb2f8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1789,13 +1789,18 @@ bool FillInputToWeight(CMutableTransaction& mtx, size_t nIn, int64_t target_weig bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector &txouts, const CCoinControl* coin_control) const { // Fill in dummy signatures for fee calculation. - int nIn = 0; + size_t nIn = 0; const bool can_grind_r = CanGrindR(); for (const auto& txout : txouts) { CTxIn& txin = txNew.vin[nIn]; // If weight was provided, fill the input to that weight if (coin_control && coin_control->HasInputWeight(txin.prevout)) { + // ELEMENTS: ensure input witness is large enough for rare case in bumpfee + if (txNew.witness.vtxinwit.size() <= nIn) { + txNew.witness.vtxinwit.resize(nIn + 1); + } + assert(txNew.witness.vtxinwit.size() > nIn); if (!FillInputToWeight(txNew, nIn, coin_control->GetInputWeight(txin.prevout))) { return false; } diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 5ed5f3f653..e915a3d5a2 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -284,18 +284,10 @@ class BumpFeeTest(BitcoinTestFramework): self.generate(self.nodes[0], 1) utxos = wallet.listunspent() - # ELEMENTS FIXME: use createrawtransaction until sendall is fixed - # tx = wallet.sendall(recipients=[wallet.getnewaddress()], fee_rate=2, options={"inputs": [utxos[0]]}) - fee = Decimal("0.00001000") - tx = wallet.createrawtransaction([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{wallet.getnewaddress(): utxos[0]["amount"] - fee}, {"fee": fee}]) - signed = wallet.signrawtransactionwithwallet(tx) - assert signed["complete"] - txid = wallet.sendrawtransaction(signed["hex"]) - tx = {"txid": txid} + tx = wallet.sendall(recipients=[wallet.getnewaddress()], fee_rate=2, options={"inputs": [utxos[0]]}) # Set the only output with a crazy high feerate as change, should fail as the output would be dust - # ELEMENTS FIXME - # assert_raises_rpc_error(-4, "The transaction amount is too small to pay the fee", wallet.bumpfee, txid=tx["txid"], options={"fee_rate": 1100, "original_change_index": 0}) + assert_raises_rpc_error(-4, "The transaction amount is too small to pay the fee", wallet.bumpfee, txid=tx["txid"], options={"fee_rate": 1100, "original_change_index": 0}) # Specify single output as change successfully bumped = wallet.bumpfee(txid=tx["txid"], options={"fee_rate": 11, "original_change_index": 0}) # ELEMENTS @@ -353,7 +345,7 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address): # if this is a new_outputs test, check that outputs were indeed replaced if mode == "new_outputs": assert len(bumpedwtx["details"]) == 1 - # assert bumpedwtx["details"][0]["address"] == new_address # ELEMENTS FIXME: address differs + assert bumpedwtx["details"][0]["address"] == new_address self.clear_mempool() @@ -410,7 +402,6 @@ def test_notmine_bumpfee(self, rbf_node, peer_node, dest_address): entry = rbf_node.getmempoolentry(rbfid) old_fee = entry["fees"]["base"] old_feerate = int(old_fee / entry["vsize"] * Decimal(1e8)) - print(old_feerate) # ELEMENTS FIXME lint assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet", rbf_node.bumpfee, rbfid) @@ -421,13 +412,12 @@ def test_notmine_bumpfee(self, rbf_node, peer_node, dest_address): assert res[0]["allowed"] assert_greater_than(res[0]["fees"]["base"], old_fee) - # ELEMENTS FIXME: insufficient funds - # self.log.info("Test that psbtbumpfee works for non-owned inputs") - # psbt = rbf_node.psbtbumpfee(txid=rbfid) - # finish_psbtbumpfee(psbt["psbt"]) + self.log.info("Test that psbtbumpfee works for non-owned inputs") + psbt = rbf_node.psbtbumpfee(txid=rbfid) + finish_psbtbumpfee(psbt["psbt"]) - # psbt = rbf_node.psbtbumpfee(txid=rbfid, fee_rate=old_feerate + 10) - # finish_psbtbumpfee(psbt["psbt"]) + psbt = rbf_node.psbtbumpfee(txid=rbfid, fee_rate=old_feerate + 10) + finish_psbtbumpfee(psbt["psbt"]) self.clear_mempool() @@ -507,7 +497,7 @@ def test_small_output_with_feerate_succeeds(self, rbf_node, dest_address): tx_fee = rbfid_new_details["fee"] # Total value from input not going to destination - if tx_fee > Decimal('0.00040000'): # ELEMENTS FIXME: this was 0.00050000 in bitcoin (since spend_one_input leaves 0.0005 for the fee) + if tx_fee > Decimal('0.00050000'): break # input(s) have been added @@ -837,20 +827,19 @@ def test_feerate_checks_replaced_outputs(self, rbf_node, peer_node): # Calculate the minimum feerate required for the bump to work. # Since the bumped tx will replace all of the outputs with a single output, we can estimate that its size will 31 * (len(outputs) - 1) bytes smaller tx_size = tx_details["decoded"]["vsize"] - est_bumped_size = tx_size - (len(tx_details["decoded"]["vout"]) - 1) * 31 + est_bumped_size = tx_size - (len(tx_details["decoded"]["vout"]) - 1) * 66 # ELEMENTS: our outputs are larger inc_fee_rate = max(rbf_node.getmempoolinfo()["incrementalrelayfee"], Decimal(0.00005000)) # Wallet has a fixed incremental relay fee of 5 sat/vb # RPC gives us fee as negative min_fee = (-tx_details["fee"]["bitcoin"] + get_fee(est_bumped_size, inc_fee_rate)) * Decimal(1e8) min_fee_rate = (min_fee / est_bumped_size).quantize(Decimal("1.000")) - print(min_fee_rate) # ELEMENTS FIXME: lint # Attempt to bumpfee and replace all outputs with a single one using a feerate slightly less than the minimum new_outputs = [{rbf_node.getnewaddress(address_type="bech32"): 49}] - print(len(new_outputs)) # ELEMENTS FIXME: lint - # assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx_res["txid"], {"fee_rate": min_fee_rate - 1, "outputs": new_outputs}) # ELEMENTS FIXME + assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx_res["txid"], {"fee_rate": min_fee_rate - 1, "outputs": new_outputs}) # Bumpfee and replace all outputs with a single one using the minimum feerate - # rbf_node.bumpfee(tx_res["txid"], {"fee_rate": min_fee_rate, "outputs": new_outputs}) # ELEMENTS FIXME + min_fee_rate += 4 # ELEMENTS: additional offset + rbf_node.bumpfee(tx_res["txid"], {"fee_rate": min_fee_rate, "outputs": new_outputs}) self.clear_mempool()