Witness-only option for fundrawtransaction

Github-Pull: #25183
Rebased-From: 1c5cfd84b3dc14ab886acd47c7891c11eb2457ec
This commit is contained in:
Aurèle Oulès 2022-05-21 04:49:34 +02:00 committed by Luke Dashjr
parent 96ec3b67a7
commit 788a57d808
5 changed files with 46 additions and 0 deletions

View File

@ -138,6 +138,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "fundrawtransaction", 1, "lockUnspents"},
{ "fundrawtransaction", 1, "fee_rate"},
{ "fundrawtransaction", 1, "feeRate"},
{ "fundrawtransaction", 1, "segwit_inputs_only"},
{ "fundrawtransaction", 1, "subtractFeeFromOutputs"},
{ "fundrawtransaction", 1, "input_weights"},
{ "fundrawtransaction", 1, "conf_target"},

View File

@ -34,6 +34,8 @@ public:
std::optional<OutputType> m_change_type;
//! If false, only safe inputs will be used
bool m_include_unsafe_inputs = false;
//! If true, only segwit inputs are selected
bool m_segwit_inputs_only = false;
//! If true, the selection process can add extra unselected inputs from the wallet
//! while requires all selected inputs be used
bool m_allow_other_inputs = true;

View File

@ -534,6 +534,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
{"minconf", UniValueType(UniValue::VNUM)},
{"maxconf", UniValueType(UniValue::VNUM)},
{"input_weights", UniValueType(UniValue::VARR)},
{"segwit_inputs_only", UniValueType(UniValue::VBOOL)},
},
true, true);
@ -541,6 +542,10 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
coinControl.m_allow_other_inputs = options["add_inputs"].get_bool();
}
if (options.exists("segwit_inputs_only")) {
coinControl.m_segwit_inputs_only = options["segwit_inputs_only"].get_bool();
}
if (options.exists("changeAddress") || options.exists("change_address")) {
const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
CTxDestination dest = DecodeDestination(change_address_str);
@ -799,6 +804,7 @@ RPCHelpMan fundrawtransaction()
},
},
},
{"segwit_inputs_only", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to only use segwit inputs for transaction."},
},
FundTxDoc()),
RPCArgOptions{

View File

@ -408,6 +408,10 @@ CoinsResult AvailableCoins(const CWallet& wallet,
std::unique_ptr<SigningProvider> provider = wallet.GetSolvingProvider(output.scriptPubKey);
if (coinControl && coinControl->m_segwit_inputs_only && !IsSegWitOutput(*provider, wtx.tx->vout[i].scriptPubKey)) {
continue;
}
int input_bytes = CalculateMaximumSignedInputSize(output, COutPoint(), provider.get(), can_grind_r, coinControl);
// Because CalculateMaximumSignedInputSize infers a solvable descriptor to get the satisfaction size,
// it is safe to assume that this input is solvable if input_bytes is greater than -1.

View File

@ -136,6 +136,7 @@ class RawTransactionsTest(BitcoinTestFramework):
self.test_locked_wallet()
self.test_many_inputs_fee()
self.test_many_inputs_send()
self.test_witness_only()
self.test_op_return()
self.test_watchonly()
self.test_all_watched_funds()
@ -188,6 +189,38 @@ class RawTransactionsTest(BitcoinTestFramework):
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0 #test that we have enough inputs
def check_witness_inputs(self, vins):
for vin in vins:
# check vin is a segwit input
utxo = self.nodes[2].gettxout(vin['txid'], vin['vout'])
info = self.nodes[2].getaddressinfo(utxo['scriptPubKey']['address'])
if not info['iswitness']:
return False
return True
def test_witness_only(self):
self.log.info("Testing fundrawtxn with witness inputs only")
inputs = [ ]
# make sure legacy inputs are not accepted in witness only mode if no witness inputs are found
self.generatetoaddress(self.nodes[2], 2, self.nodes[2].getnewaddress(address_type='legacy'))
outputs = { self.nodes[2].getnewaddress() : 10.0 }
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx, {'segwit_inputs_only': True })
# make sure all inputs are of type witness
outputs = { self.nodes[2].getnewaddress() : 300.0 } # will generate many inputs
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
self.generatetoaddress(self.nodes[2], 150, self.nodes[2].getnewaddress(address_type='bech32'))
rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'segwit_inputs_only': True })
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
assert len(dec_tx['vin']) > 0
assert(self.check_witness_inputs(dec_tx['vin']))
def test_simple_two_coins(self):
self.log.info("Test fundrawtxn with 2 coins")
inputs = [ ]