mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 23:09:44 -04:00
Allow createpsbt and walletcreatefundedpsbt to take psbt version
Use v2 for other RPCs. For some tests to work, PSBTv0 is set explicitly.
This commit is contained in:
parent
29c3fb76ef
commit
2bcb9d07b2
11 changed files with 108 additions and 87 deletions
|
@ -88,7 +88,7 @@ static constexpr uint8_t PSBT_SEPARATOR = 0x00;
|
||||||
const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MB
|
const std::streamsize MAX_FILE_SIZE_PSBT = 100000000; // 100 MB
|
||||||
|
|
||||||
// PSBT version number
|
// PSBT version number
|
||||||
static constexpr uint32_t PSBT_HIGHEST_VERSION = 0;
|
static constexpr uint32_t PSBT_HIGHEST_VERSION = 2;
|
||||||
|
|
||||||
/** A structure for PSBT proprietary types */
|
/** A structure for PSBT proprietary types */
|
||||||
struct PSBTProprietary
|
struct PSBTProprietary
|
||||||
|
|
|
@ -170,6 +170,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "walletcreatefundedpsbt", 3, "solving_data"},
|
{ "walletcreatefundedpsbt", 3, "solving_data"},
|
||||||
{ "walletcreatefundedpsbt", 3, "max_tx_weight"},
|
{ "walletcreatefundedpsbt", 3, "max_tx_weight"},
|
||||||
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
|
{ "walletcreatefundedpsbt", 4, "bip32derivs" },
|
||||||
|
{ "walletcreatefundedpsbt", 5, "psbt_version" },
|
||||||
{ "walletprocesspsbt", 1, "sign" },
|
{ "walletprocesspsbt", 1, "sign" },
|
||||||
{ "walletprocesspsbt", 3, "bip32derivs" },
|
{ "walletprocesspsbt", 3, "bip32derivs" },
|
||||||
{ "walletprocesspsbt", 4, "finalize" },
|
{ "walletprocesspsbt", 4, "finalize" },
|
||||||
|
@ -180,6 +181,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "createpsbt", 1, "outputs" },
|
{ "createpsbt", 1, "outputs" },
|
||||||
{ "createpsbt", 2, "locktime" },
|
{ "createpsbt", 2, "locktime" },
|
||||||
{ "createpsbt", 3, "replaceable" },
|
{ "createpsbt", 3, "replaceable" },
|
||||||
|
{ "createpsbt", 4, "psbt_version" },
|
||||||
{ "combinepsbt", 0, "txs"},
|
{ "combinepsbt", 0, "txs"},
|
||||||
{ "joinpsbts", 0, "txs"},
|
{ "joinpsbts", 0, "txs"},
|
||||||
{ "finalizepsbt", 1, "extract"},
|
{ "finalizepsbt", 1, "extract"},
|
||||||
|
|
|
@ -1717,7 +1717,12 @@ static RPCHelpMan createpsbt()
|
||||||
"Implements the Creator role.\n"
|
"Implements the Creator role.\n"
|
||||||
"Note that the transaction's inputs are not signed, and\n"
|
"Note that the transaction's inputs are not signed, and\n"
|
||||||
"it is not stored in the wallet or transmitted to the network.\n",
|
"it is not stored in the wallet or transmitted to the network.\n",
|
||||||
CreateTxDoc(),
|
Cat<std::vector<RPCArg>>(
|
||||||
|
CreateTxDoc(),
|
||||||
|
{
|
||||||
|
{"psbt_version", RPCArg::Type::NUM, RPCArg::Default{2}, "The PSBT version number to use."},
|
||||||
|
}
|
||||||
|
),
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)"
|
RPCResult::Type::STR, "", "The resulting raw transaction (base64-encoded string)"
|
||||||
},
|
},
|
||||||
|
@ -1726,7 +1731,6 @@ static RPCHelpMan createpsbt()
|
||||||
},
|
},
|
||||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||||
{
|
{
|
||||||
|
|
||||||
std::optional<bool> rbf;
|
std::optional<bool> rbf;
|
||||||
if (!request.params[3].isNull()) {
|
if (!request.params[3].isNull()) {
|
||||||
rbf = request.params[3].get_bool();
|
rbf = request.params[3].get_bool();
|
||||||
|
@ -1734,15 +1738,16 @@ static RPCHelpMan createpsbt()
|
||||||
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
|
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
|
||||||
|
|
||||||
// Make a blank psbt
|
// Make a blank psbt
|
||||||
PartiallySignedTransaction psbtx;
|
uint32_t psbt_version = 2;
|
||||||
psbtx.tx = rawTx;
|
if (!request.params[4].isNull()) {
|
||||||
for (unsigned int i = 0; i < rawTx.vin.size(); ++i) {
|
psbt_version = request.params[4].getInt<int>();
|
||||||
psbtx.inputs.emplace_back(0);
|
|
||||||
}
|
}
|
||||||
for (unsigned int i = 0; i < rawTx.vout.size(); ++i) {
|
if (psbt_version != 2 && psbt_version != 0) {
|
||||||
psbtx.outputs.emplace_back(0);
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "The PSBT version can only be 2 or 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PartiallySignedTransaction psbtx(rawTx, psbt_version);
|
||||||
|
|
||||||
// Serialize the PSBT
|
// Serialize the PSBT
|
||||||
DataStream ssTx{};
|
DataStream ssTx{};
|
||||||
ssTx << psbtx;
|
ssTx << psbtx;
|
||||||
|
@ -1801,14 +1806,7 @@ static RPCHelpMan converttopsbt()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a blank psbt
|
// Make a blank psbt
|
||||||
PartiallySignedTransaction psbtx;
|
PartiallySignedTransaction psbtx(tx, /*version=*/2);
|
||||||
psbtx.tx = tx;
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
|
||||||
psbtx.inputs.emplace_back(0);
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < tx.vout.size(); ++i) {
|
|
||||||
psbtx.outputs.emplace_back(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the PSBT
|
// Serialize the PSBT
|
||||||
DataStream ssTx{};
|
DataStream ssTx{};
|
||||||
|
|
|
@ -95,7 +95,7 @@ std::set<int> InterpretSubtractFeeFromOutputInstructions(const UniValue& sffo_in
|
||||||
static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const UniValue& options, const CMutableTransaction& rawTx)
|
static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const UniValue& options, const CMutableTransaction& rawTx)
|
||||||
{
|
{
|
||||||
// Make a blank psbt
|
// Make a blank psbt
|
||||||
PartiallySignedTransaction psbtx(rawTx);
|
PartiallySignedTransaction psbtx(rawTx, /*version=*/2);
|
||||||
|
|
||||||
// First fill transaction with our data without signing,
|
// First fill transaction with our data without signing,
|
||||||
// so external signers are not asked to sign more than once.
|
// so external signers are not asked to sign more than once.
|
||||||
|
@ -1173,7 +1173,7 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
|
||||||
|
|
||||||
result.pushKV("txid", txid.GetHex());
|
result.pushKV("txid", txid.GetHex());
|
||||||
} else {
|
} else {
|
||||||
PartiallySignedTransaction psbtx(mtx);
|
PartiallySignedTransaction psbtx(mtx, /*version=*/2);
|
||||||
bool complete = false;
|
bool complete = false;
|
||||||
const auto err{pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/false, /*bip32derivs=*/true)};
|
const auto err{pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/false, /*bip32derivs=*/true)};
|
||||||
CHECK_NONFATAL(!err);
|
CHECK_NONFATAL(!err);
|
||||||
|
@ -1725,6 +1725,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
||||||
FundTxDoc()),
|
FundTxDoc()),
|
||||||
RPCArgOptions{.oneline_description="options"}},
|
RPCArgOptions{.oneline_description="options"}},
|
||||||
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
|
{"bip32derivs", RPCArg::Type::BOOL, RPCArg::Default{true}, "Include BIP 32 derivation paths for public keys if we know them"},
|
||||||
|
{"psbt_version", RPCArg::Type::NUM, RPCArg::Default(2), "The PSBT version number to use."},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::OBJ, "", "",
|
RPCResult::Type::OBJ, "", "",
|
||||||
|
@ -1772,7 +1773,15 @@ RPCHelpMan walletcreatefundedpsbt()
|
||||||
auto txr = FundTransaction(wallet, rawTx, recipients, options, coin_control, /*override_min_fee=*/true);
|
auto txr = FundTransaction(wallet, rawTx, recipients, options, coin_control, /*override_min_fee=*/true);
|
||||||
|
|
||||||
// Make a blank psbt
|
// Make a blank psbt
|
||||||
PartiallySignedTransaction psbtx(CMutableTransaction(*txr.tx));
|
uint32_t psbt_version = 2;
|
||||||
|
if (!request.params[5].isNull()) {
|
||||||
|
psbt_version = request.params[5].getInt<int>();
|
||||||
|
}
|
||||||
|
if (psbt_version != 2 && psbt_version != 0) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "The PSBT version can only be 2 or 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
PartiallySignedTransaction psbtx(CMutableTransaction(*txr.tx), psbt_version);
|
||||||
|
|
||||||
// Fill transaction with out data but don't sign
|
// Fill transaction with out data but don't sign
|
||||||
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
|
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
|
||||||
|
|
|
@ -148,6 +148,7 @@
|
||||||
"bcrt1qqzh2ngh97ru8dfvgma25d6r595wcwqy0cee4cc": 1
|
"bcrt1qqzh2ngh97ru8dfvgma25d6r595wcwqy0cee4cc": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"version": 0,
|
||||||
"result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
|
"result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAAAAAA="
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -32,6 +32,7 @@ from test_framework.psbt import (
|
||||||
PSBT_IN_NON_WITNESS_UTXO,
|
PSBT_IN_NON_WITNESS_UTXO,
|
||||||
PSBT_IN_WITNESS_UTXO,
|
PSBT_IN_WITNESS_UTXO,
|
||||||
PSBT_OUT_TAP_TREE,
|
PSBT_OUT_TAP_TREE,
|
||||||
|
PSBT_OUT_SCRIPT,
|
||||||
)
|
)
|
||||||
from test_framework.script import CScript, OP_TRUE
|
from test_framework.script import CScript, OP_TRUE
|
||||||
from test_framework.script_util import MIN_STANDARD_TX_NONWITNESS_SIZE
|
from test_framework.script_util import MIN_STANDARD_TX_NONWITNESS_SIZE
|
||||||
|
@ -85,9 +86,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
|
|
||||||
# Modify the raw transaction by changing the output address, so the signature is no longer valid
|
# Modify the raw transaction by changing the output address, so the signature is no longer valid
|
||||||
signed_psbt_obj = PSBT.from_base64(signed_psbt)
|
signed_psbt_obj = PSBT.from_base64(signed_psbt)
|
||||||
substitute_addr = wallet.getnewaddress()
|
signed_psbt_obj.o[0].map[PSBT_OUT_SCRIPT] = CScript([OP_TRUE])
|
||||||
raw = wallet.createrawtransaction([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{substitute_addr: 0.9999}])
|
|
||||||
signed_psbt_obj.g.map[PSBT_GLOBAL_UNSIGNED_TX] = bytes.fromhex(raw)
|
|
||||||
|
|
||||||
# Check that the walletprocesspsbt call succeeds but also recognizes that the transaction is not complete
|
# Check that the walletprocesspsbt call succeeds but also recognizes that the transaction is not complete
|
||||||
signed_psbt_incomplete = wallet.walletprocesspsbt(signed_psbt_obj.to_base64(), finalize=False)
|
signed_psbt_incomplete = wallet.walletprocesspsbt(signed_psbt_obj.to_base64(), finalize=False)
|
||||||
|
@ -159,11 +158,11 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
psbtx1 = wallet.walletcreatefundedpsbt([], {target_address: 0.1}, 0, {'fee_rate': 1, 'maxconf': 0})['psbt']
|
psbtx1 = wallet.walletcreatefundedpsbt([], {target_address: 0.1}, 0, {'fee_rate': 1, 'maxconf': 0})['psbt']
|
||||||
|
|
||||||
# Make sure we only had the one input
|
# Make sure we only had the one input
|
||||||
tx1_inputs = self.nodes[0].decodepsbt(psbtx1)['tx']['vin']
|
tx1_inputs = self.nodes[0].decodepsbt(psbtx1)['inputs']
|
||||||
assert_equal(len(tx1_inputs), 1)
|
assert_equal(len(tx1_inputs), 1)
|
||||||
|
|
||||||
utxo1 = tx1_inputs[0]
|
utxo1 = tx1_inputs[0]
|
||||||
assert_equal(unconfirmed_txid, utxo1['txid'])
|
assert_equal(unconfirmed_txid, utxo1['previous_txid'])
|
||||||
|
|
||||||
signed_tx1 = wallet.walletprocesspsbt(psbtx1)
|
signed_tx1 = wallet.walletprocesspsbt(psbtx1)
|
||||||
txid1 = self.nodes[0].sendrawtransaction(signed_tx1['hex'])
|
txid1 = self.nodes[0].sendrawtransaction(signed_tx1['hex'])
|
||||||
|
@ -172,23 +171,23 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
assert txid1 in mempool
|
assert txid1 in mempool
|
||||||
|
|
||||||
self.log.info("Fail to craft a new PSBT that sends more funds with add_inputs = False")
|
self.log.info("Fail to craft a new PSBT that sends more funds with add_inputs = False")
|
||||||
assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually", wallet.walletcreatefundedpsbt, [{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': False})
|
assert_raises_rpc_error(-4, "The preselected coins total amount does not cover the transaction target. Please allow other inputs to be automatically selected or include more coins manually", wallet.walletcreatefundedpsbt, [{'txid': utxo1['previous_txid'], 'vout': utxo1['previous_vout']}], {target_address: 1}, 0, {'add_inputs': False})
|
||||||
|
|
||||||
self.log.info("Fail to craft a new PSBT with minconf above highest one")
|
self.log.info("Fail to craft a new PSBT with minconf above highest one")
|
||||||
assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, [{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10})
|
assert_raises_rpc_error(-4, "Insufficient funds", wallet.walletcreatefundedpsbt, [{'txid': utxo1['previous_txid'], 'vout': utxo1['previous_vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 3, 'fee_rate': 10})
|
||||||
|
|
||||||
self.log.info("Fail to broadcast a new PSBT with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs")
|
self.log.info("Fail to broadcast a new PSBT with maxconf 0 due to BIP125 rules to verify it actually chose unconfirmed outputs")
|
||||||
psbt_invalid = wallet.walletcreatefundedpsbt([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'maxconf': 0, 'fee_rate': 10})['psbt']
|
psbt_invalid = wallet.walletcreatefundedpsbt([{'txid': utxo1['previous_txid'], 'vout': utxo1['previous_vout']}], {target_address: 1}, 0, {'add_inputs': True, 'maxconf': 0, 'fee_rate': 10})['psbt']
|
||||||
signed_invalid = wallet.walletprocesspsbt(psbt_invalid)
|
signed_invalid = wallet.walletprocesspsbt(psbt_invalid)
|
||||||
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, signed_invalid['hex'])
|
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, signed_invalid['hex'])
|
||||||
|
|
||||||
self.log.info("Craft a replacement adding inputs with highest confs possible")
|
self.log.info("Craft a replacement adding inputs with highest confs possible")
|
||||||
psbtx2 = wallet.walletcreatefundedpsbt([{'txid': utxo1['txid'], 'vout': utxo1['vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 2, 'fee_rate': 10})['psbt']
|
psbtx2 = wallet.walletcreatefundedpsbt([{'txid': utxo1['previous_txid'], 'vout': utxo1['previous_vout']}], {target_address: 1}, 0, {'add_inputs': True, 'minconf': 2, 'fee_rate': 10})['psbt']
|
||||||
tx2_inputs = self.nodes[0].decodepsbt(psbtx2)['tx']['vin']
|
tx2_inputs = self.nodes[0].decodepsbt(psbtx2)['inputs']
|
||||||
assert_greater_than_or_equal(len(tx2_inputs), 2)
|
assert_greater_than_or_equal(len(tx2_inputs), 2)
|
||||||
for vin in tx2_inputs:
|
for vin in tx2_inputs:
|
||||||
if vin['txid'] != unconfirmed_txid:
|
if vin['previous_txid'] != unconfirmed_txid:
|
||||||
assert_greater_than_or_equal(self.nodes[0].gettxout(vin['txid'], vin['vout'])['confirmations'], 2)
|
assert_greater_than_or_equal(self.nodes[0].gettxout(vin['previous_txid'], vin['previous_vout'])['confirmations'], 2)
|
||||||
|
|
||||||
signed_tx2 = wallet.walletprocesspsbt(psbtx2)
|
signed_tx2 = wallet.walletprocesspsbt(psbtx2)
|
||||||
txid2 = self.nodes[0].sendrawtransaction(signed_tx2['hex'])
|
txid2 = self.nodes[0].sendrawtransaction(signed_tx2['hex'])
|
||||||
|
@ -205,7 +204,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
# The decodepsbt RPC is stateless and independent of any settings, we can always just call it on the first node
|
# The decodepsbt RPC is stateless and independent of any settings, we can always just call it on the first node
|
||||||
decoded_psbt = self.nodes[0].decodepsbt(psbtx["psbt"])
|
decoded_psbt = self.nodes[0].decodepsbt(psbtx["psbt"])
|
||||||
changepos = psbtx["changepos"]
|
changepos = psbtx["changepos"]
|
||||||
assert_equal(decoded_psbt["tx"]["vout"][changepos]["scriptPubKey"]["type"], expected_type)
|
assert_equal(decoded_psbt["outputs"][changepos]["script"]["type"], expected_type)
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
# Create and fund a raw tx for sending 10 BTC
|
# Create and fund a raw tx for sending 10 BTC
|
||||||
|
@ -247,7 +246,9 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
|
|
||||||
max_tx_weight_sufficient = 1000 # 1k vbytes is enough
|
max_tx_weight_sufficient = 1000 # 1k vbytes is enough
|
||||||
psbt = self.nodes[0].walletcreatefundedpsbt(outputs=dest_arg,locktime=0, options={"max_tx_weight": max_tx_weight_sufficient})["psbt"]
|
psbt = self.nodes[0].walletcreatefundedpsbt(outputs=dest_arg,locktime=0, options={"max_tx_weight": max_tx_weight_sufficient})["psbt"]
|
||||||
weight = self.nodes[0].decodepsbt(psbt)["tx"]["weight"]
|
psbt = self.nodes[0].walletprocesspsbt(psbt)["psbt"]
|
||||||
|
final_tx = self.nodes[0].finalizepsbt(psbt)["hex"]
|
||||||
|
weight = self.nodes[0].decoderawtransaction(final_tx)["weight"]
|
||||||
# ensure the transaction's weight is below the specified max_tx_weight.
|
# ensure the transaction's weight is below the specified max_tx_weight.
|
||||||
assert_greater_than_or_equal(max_tx_weight_sufficient, weight)
|
assert_greater_than_or_equal(max_tx_weight_sufficient, weight)
|
||||||
|
|
||||||
|
@ -258,7 +259,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
self.nodes[0].walletcreatefundedpsbt, [{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90})
|
self.nodes[0].walletcreatefundedpsbt, [{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90})
|
||||||
|
|
||||||
psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}, 0, {"add_inputs": True})['psbt']
|
psbtx1 = self.nodes[0].walletcreatefundedpsbt([{"txid": utxo1['txid'], "vout": utxo1['vout']}], {self.nodes[2].getnewaddress():90}, 0, {"add_inputs": True})['psbt']
|
||||||
assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['tx']['vin']), 2)
|
assert_equal(len(self.nodes[0].decodepsbt(psbtx1)['inputs']), 2)
|
||||||
|
|
||||||
# Inputs argument can be null
|
# Inputs argument can be null
|
||||||
self.nodes[0].walletcreatefundedpsbt(None, {self.nodes[2].getnewaddress():10})
|
self.nodes[0].walletcreatefundedpsbt(None, {self.nodes[2].getnewaddress():10})
|
||||||
|
@ -491,12 +492,14 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
# Update psbts, should only have data for one input and not the other
|
# Update psbts, should only have data for one input and not the other
|
||||||
psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
|
psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
|
||||||
psbt1_decoded = self.nodes[0].decodepsbt(psbt1)
|
psbt1_decoded = self.nodes[0].decodepsbt(psbt1)
|
||||||
assert psbt1_decoded['inputs'][0] and not psbt1_decoded['inputs'][1]
|
assert len(psbt1_decoded['inputs'][0].keys()) > 3
|
||||||
|
assert len(psbt1_decoded['inputs'][1].keys()) == 3
|
||||||
# Check that BIP32 path was added
|
# Check that BIP32 path was added
|
||||||
assert "bip32_derivs" in psbt1_decoded['inputs'][0]
|
assert "bip32_derivs" in psbt1_decoded['inputs'][0]
|
||||||
psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL", False)['psbt']
|
psbt2 = self.nodes[2].walletprocesspsbt(psbt_orig, False, "ALL", False)['psbt']
|
||||||
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
|
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
|
||||||
assert not psbt2_decoded['inputs'][0] and psbt2_decoded['inputs'][1]
|
assert len(psbt2_decoded['inputs'][0].keys()) == 3
|
||||||
|
assert len(psbt2_decoded['inputs'][1].keys()) > 3
|
||||||
# Check that BIP32 paths were not added
|
# Check that BIP32 paths were not added
|
||||||
assert "bip32_derivs" not in psbt2_decoded['inputs'][1]
|
assert "bip32_derivs" not in psbt2_decoded['inputs'][1]
|
||||||
|
|
||||||
|
@ -518,33 +521,33 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
unspent = self.nodes[0].listunspent()[0]
|
unspent = self.nodes[0].listunspent()[0]
|
||||||
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False, "add_inputs": True}, False)
|
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable": False, "add_inputs": True}, False)
|
||||||
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
|
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
|
||||||
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
|
for psbt_in in decoded_psbt["inputs"]:
|
||||||
assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
assert_greater_than(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
||||||
assert "bip32_derivs" not in psbt_in
|
assert "bip32_derivs" not in psbt_in
|
||||||
assert_equal(decoded_psbt["tx"]["locktime"], block_height+2)
|
assert_equal(decoded_psbt["fallback_locktime"], block_height+2)
|
||||||
|
|
||||||
# Same construction with only locktime set and RBF explicitly enabled
|
# Same construction with only locktime set and RBF explicitly enabled
|
||||||
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True, "add_inputs": True}, True)
|
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {"replaceable": True, "add_inputs": True}, True)
|
||||||
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
|
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
|
||||||
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
|
for psbt_in in decoded_psbt["inputs"]:
|
||||||
assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
assert_equal(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
||||||
assert "bip32_derivs" in psbt_in
|
assert "bip32_derivs" in psbt_in
|
||||||
assert_equal(decoded_psbt["tx"]["locktime"], block_height)
|
assert_equal(decoded_psbt["fallback_locktime"], block_height)
|
||||||
|
|
||||||
# Same construction without optional arguments
|
# Same construction without optional arguments
|
||||||
psbtx_info = self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
|
psbtx_info = self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
|
||||||
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
|
decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"])
|
||||||
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
|
for psbt_in in decoded_psbt["inputs"]:
|
||||||
assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
assert_equal(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
||||||
assert "bip32_derivs" in psbt_in
|
assert "bip32_derivs" in psbt_in
|
||||||
assert_equal(decoded_psbt["tx"]["locktime"], 0)
|
assert_equal(decoded_psbt["fallback_locktime"], 0)
|
||||||
|
|
||||||
# Same construction without optional arguments, for a node with -walletrbf=0
|
# Same construction without optional arguments, for a node with -walletrbf=0
|
||||||
unspent1 = self.nodes[1].listunspent()[0]
|
unspent1 = self.nodes[1].listunspent()[0]
|
||||||
psbtx_info = self.nodes[1].walletcreatefundedpsbt([{"txid":unspent1["txid"], "vout":unspent1["vout"]}], [{self.nodes[2].getnewaddress():unspent1["amount"]+1}], block_height, {"add_inputs": True})
|
psbtx_info = self.nodes[1].walletcreatefundedpsbt([{"txid":unspent1["txid"], "vout":unspent1["vout"]}], [{self.nodes[2].getnewaddress():unspent1["amount"]+1}], block_height, {"add_inputs": True})
|
||||||
decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"])
|
decoded_psbt = self.nodes[1].decodepsbt(psbtx_info["psbt"])
|
||||||
for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]):
|
for psbt_in in decoded_psbt["inputs"]:
|
||||||
assert_greater_than(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
assert_greater_than(psbt_in["sequence"], MAX_BIP125_RBF_SEQUENCE)
|
||||||
assert "bip32_derivs" in psbt_in
|
assert "bip32_derivs" in psbt_in
|
||||||
|
|
||||||
# Make sure change address wallet does not have P2SH innerscript access to results in success
|
# Make sure change address wallet does not have P2SH innerscript access to results in success
|
||||||
|
@ -614,7 +617,7 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
|
|
||||||
# Creator Tests
|
# Creator Tests
|
||||||
for creator in creators:
|
for creator in creators:
|
||||||
created_tx = self.nodes[0].createpsbt(inputs=creator['inputs'], outputs=creator['outputs'], replaceable=False)
|
created_tx = self.nodes[0].createpsbt(inputs=creator['inputs'], outputs=creator['outputs'], replaceable=False, psbt_version=creator['version'])
|
||||||
assert_equal(created_tx, creator['result'])
|
assert_equal(created_tx, creator['result'])
|
||||||
|
|
||||||
# Signer tests
|
# Signer tests
|
||||||
|
@ -668,54 +671,66 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
utxo1, utxo2, utxo3 = self.create_outpoints(self.nodes[1], outputs=[{addr1: 11}, {addr2: 11}, {addr3: 11}])
|
utxo1, utxo2, utxo3 = self.create_outpoints(self.nodes[1], outputs=[{addr1: 11}, {addr2: 11}, {addr3: 11}])
|
||||||
self.sync_all()
|
self.sync_all()
|
||||||
|
|
||||||
|
psbt_v2_required_keys = ["previous_vout", "sequence", "previous_txid"]
|
||||||
|
|
||||||
def test_psbt_input_keys(psbt_input, keys):
|
def test_psbt_input_keys(psbt_input, keys):
|
||||||
"""Check that the psbt input has only the expected keys."""
|
"""Check that the psbt input has only the expected keys."""
|
||||||
|
keys.extend(["previous_vout", "sequence", "previous_txid"])
|
||||||
assert_equal(set(keys), set(psbt_input.keys()))
|
assert_equal(set(keys), set(psbt_input.keys()))
|
||||||
|
|
||||||
# Create a PSBT. None of the inputs are filled initially
|
# Create a PSBT. None of the inputs are filled initially
|
||||||
psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():32.999})
|
psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():32.999})
|
||||||
decoded = self.nodes[1].decodepsbt(psbt)
|
decoded = self.nodes[1].decodepsbt(psbt)
|
||||||
test_psbt_input_keys(decoded['inputs'][0], [])
|
test_psbt_input_keys(decoded['inputs'][0], psbt_v2_required_keys)
|
||||||
test_psbt_input_keys(decoded['inputs'][1], [])
|
test_psbt_input_keys(decoded['inputs'][1], psbt_v2_required_keys)
|
||||||
test_psbt_input_keys(decoded['inputs'][2], [])
|
test_psbt_input_keys(decoded['inputs'][2], psbt_v2_required_keys)
|
||||||
|
|
||||||
# Update a PSBT with UTXOs from the node
|
# Update a PSBT with UTXOs from the node
|
||||||
# Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
|
# Bech32 inputs should be filled with witness UTXO. Other inputs should not be filled because they are non-witness
|
||||||
updated = self.nodes[1].utxoupdatepsbt(psbt)
|
updated = self.nodes[1].utxoupdatepsbt(psbt)
|
||||||
decoded = self.nodes[1].decodepsbt(updated)
|
decoded = self.nodes[1].decodepsbt(updated)
|
||||||
test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'non_witness_utxo'])
|
test_psbt_input_keys(decoded['inputs'][0], psbt_v2_required_keys + ['witness_utxo', 'non_witness_utxo'])
|
||||||
test_psbt_input_keys(decoded['inputs'][1], ['non_witness_utxo'])
|
test_psbt_input_keys(decoded['inputs'][1], psbt_v2_required_keys + ['non_witness_utxo'])
|
||||||
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo'])
|
test_psbt_input_keys(decoded['inputs'][2], psbt_v2_required_keys + ['non_witness_utxo'])
|
||||||
|
|
||||||
# Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in
|
# Try again, now while providing descriptors, making P2SH-segwit work, and causing bip32_derivs and redeem_script to be filled in
|
||||||
descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]
|
descs = [self.nodes[1].getaddressinfo(addr)['desc'] for addr in [addr1,addr2,addr3]]
|
||||||
updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)
|
updated = self.nodes[1].utxoupdatepsbt(psbt=psbt, descriptors=descs)
|
||||||
decoded = self.nodes[1].decodepsbt(updated)
|
decoded = self.nodes[1].decodepsbt(updated)
|
||||||
test_psbt_input_keys(decoded['inputs'][0], ['witness_utxo', 'non_witness_utxo', 'bip32_derivs'])
|
test_psbt_input_keys(decoded['inputs'][0], psbt_v2_required_keys + ['witness_utxo', 'non_witness_utxo', 'bip32_derivs'])
|
||||||
test_psbt_input_keys(decoded['inputs'][1], ['non_witness_utxo', 'bip32_derivs'])
|
test_psbt_input_keys(decoded['inputs'][1], psbt_v2_required_keys + ['non_witness_utxo', 'bip32_derivs'])
|
||||||
test_psbt_input_keys(decoded['inputs'][2], ['non_witness_utxo','witness_utxo', 'bip32_derivs', 'redeem_script'])
|
test_psbt_input_keys(decoded['inputs'][2], psbt_v2_required_keys + ['non_witness_utxo', 'witness_utxo', 'bip32_derivs', 'redeem_script'])
|
||||||
|
|
||||||
|
# Cannot join PSBTv2s
|
||||||
|
psbt1 = self.nodes[1].createpsbt(inputs=[utxo1], outputs={self.nodes[0].getnewaddress():Decimal('10.999')}, psbt_version=0)
|
||||||
|
psbt2 = self.nodes[1].createpsbt(inputs=[utxo1], outputs={self.nodes[0].getnewaddress():Decimal('10.999')}, psbt_version=2)
|
||||||
|
assert_raises_rpc_error(-8, "joinpsbts only operates on version 0 PSBTs", self.nodes[1].joinpsbts, [psbt1, psbt2])
|
||||||
|
|
||||||
# Two PSBTs with a common input should not be joinable
|
# Two PSBTs with a common input should not be joinable
|
||||||
psbt1 = self.nodes[1].createpsbt([utxo1], {self.nodes[0].getnewaddress():Decimal('10.999')})
|
psbt2 = self.nodes[1].createpsbt([utxo1], {self.nodes[0].getnewaddress():Decimal('10.999')}, psbt_version=0)
|
||||||
assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, updated])
|
assert_raises_rpc_error(-8, "exists in multiple PSBTs", self.nodes[1].joinpsbts, [psbt1, psbt2])
|
||||||
|
|
||||||
# Join two distinct PSBTs
|
# Join two distinct PSBTs
|
||||||
|
psbt1 = self.nodes[1].createpsbt(inputs=[utxo1, utxo2, utxo3], outputs={self.nodes[0].getnewaddress():32.999}, psbt_version=0)
|
||||||
addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit")
|
addr4 = self.nodes[1].getnewaddress("", "p2sh-segwit")
|
||||||
utxo4 = self.create_outpoints(self.nodes[0], outputs=[{addr4: 5}])[0]
|
utxo4 = self.create_outpoints(self.nodes[0], outputs=[{addr4: 5}])[0]
|
||||||
self.generate(self.nodes[0], 6)
|
self.generate(self.nodes[0], 6)
|
||||||
psbt2 = self.nodes[1].createpsbt([utxo4], {self.nodes[0].getnewaddress():Decimal('4.999')})
|
psbt2 = self.nodes[1].createpsbt([utxo4], {self.nodes[0].getnewaddress():Decimal('4.999')}, psbt_version=0)
|
||||||
psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt']
|
psbt2 = self.nodes[1].walletprocesspsbt(psbt2)['psbt']
|
||||||
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
|
psbt2_decoded = self.nodes[0].decodepsbt(psbt2)
|
||||||
assert "final_scriptwitness" in psbt2_decoded['inputs'][0] and "final_scriptSig" in psbt2_decoded['inputs'][0]
|
assert "final_scriptwitness" in psbt2_decoded['inputs'][0] and "final_scriptSig" in psbt2_decoded['inputs'][0]
|
||||||
joined = self.nodes[0].joinpsbts([psbt, psbt2])
|
joined = self.nodes[0].joinpsbts([psbt1, psbt2])
|
||||||
joined_decoded = self.nodes[0].decodepsbt(joined)
|
joined_decoded = self.nodes[0].decodepsbt(joined)
|
||||||
assert len(joined_decoded['inputs']) == 4 and len(joined_decoded['outputs']) == 2 and "final_scriptwitness" not in joined_decoded['inputs'][3] and "final_scriptSig" not in joined_decoded['inputs'][3]
|
assert_equal(len(joined_decoded['inputs']), 4)
|
||||||
|
assert_equal(len(joined_decoded['outputs']), 2)
|
||||||
|
assert "final_scriptwitness" not in joined_decoded['inputs'][3]
|
||||||
|
assert "final_scriptSig" not in joined_decoded['inputs'][3]
|
||||||
|
|
||||||
# Check that joining shuffles the inputs and outputs
|
# Check that joining shuffles the inputs and outputs
|
||||||
# 10 attempts should be enough to get a shuffled join
|
# 10 attempts should be enough to get a shuffled join
|
||||||
shuffled = False
|
shuffled = False
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
shuffled_joined = self.nodes[0].joinpsbts([psbt, psbt2])
|
shuffled_joined = self.nodes[0].joinpsbts([psbt1, psbt2])
|
||||||
shuffled |= joined != shuffled_joined
|
shuffled |= joined != shuffled_joined
|
||||||
if shuffled:
|
if shuffled:
|
||||||
break
|
break
|
||||||
|
@ -802,11 +817,9 @@ class PSBTTest(BitcoinTestFramework):
|
||||||
final = signed['hex']
|
final = signed['hex']
|
||||||
|
|
||||||
dec = self.nodes[0].decodepsbt(signed["psbt"])
|
dec = self.nodes[0].decodepsbt(signed["psbt"])
|
||||||
for i, txin in enumerate(dec["tx"]["vin"]):
|
for psbt_in in dec["inputs"]:
|
||||||
if txin["txid"] == ext_utxo["txid"] and txin["vout"] == ext_utxo["vout"]:
|
if psbt_in["previous_txid"] == ext_utxo["txid"] and psbt_in["previous_vout"] == ext_utxo["vout"]:
|
||||||
input_idx = i
|
|
||||||
break
|
break
|
||||||
psbt_in = dec["inputs"][input_idx]
|
|
||||||
scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
|
scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
|
||||||
witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
|
witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
|
||||||
input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
|
input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
|
||||||
|
|
|
@ -633,14 +633,14 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
|
||||||
{"fee_rate": 1, "add_inputs": False}, True)['psbt']
|
{"fee_rate": 1, "add_inputs": False}, True)['psbt']
|
||||||
psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True)
|
psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True)
|
||||||
original_txid = watcher.sendrawtransaction(psbt_signed["hex"])
|
original_txid = watcher.sendrawtransaction(psbt_signed["hex"])
|
||||||
assert_equal(len(watcher.decodepsbt(psbt)["tx"]["vin"]), 1)
|
assert_equal(len(watcher.decodepsbt(psbt)["inputs"]), 1)
|
||||||
|
|
||||||
# bumpfee can't be used on watchonly wallets
|
# bumpfee can't be used on watchonly wallets
|
||||||
assert_raises_rpc_error(-4, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.", watcher.bumpfee, original_txid)
|
assert_raises_rpc_error(-4, "bumpfee is not available with wallets that have private keys disabled. Use psbtbumpfee instead.", watcher.bumpfee, original_txid)
|
||||||
|
|
||||||
# Bump fee, obnoxiously high to add additional watchonly input
|
# Bump fee, obnoxiously high to add additional watchonly input
|
||||||
bumped_psbt = watcher.psbtbumpfee(original_txid, fee_rate=HIGH)
|
bumped_psbt = watcher.psbtbumpfee(original_txid, fee_rate=HIGH)
|
||||||
assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["tx"]["vin"]), 1)
|
assert_greater_than(len(watcher.decodepsbt(bumped_psbt['psbt'])["inputs"]), 1)
|
||||||
assert "txid" not in bumped_psbt
|
assert "txid" not in bumped_psbt
|
||||||
assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"])
|
assert_equal(bumped_psbt["origfee"], -watcher.gettransaction(original_txid)["fee"])
|
||||||
assert not watcher.finalizepsbt(bumped_psbt["psbt"])["complete"]
|
assert not watcher.finalizepsbt(bumped_psbt["psbt"])["complete"]
|
||||||
|
|
|
@ -1171,10 +1171,10 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||||
tx = wallet.send(outputs=[{addr1: 8}], **options)
|
tx = wallet.send(outputs=[{addr1: 8}], **options)
|
||||||
assert tx["complete"]
|
assert tx["complete"]
|
||||||
# Check that only the preset inputs were added to the tx
|
# Check that only the preset inputs were added to the tx
|
||||||
decoded_psbt_inputs = self.nodes[0].decodepsbt(tx["psbt"])['tx']['vin']
|
decoded_psbt_inputs = self.nodes[0].decodepsbt(tx["psbt"])["inputs"]
|
||||||
assert_equal(len(decoded_psbt_inputs), 2)
|
assert_equal(len(decoded_psbt_inputs), 2)
|
||||||
for input in decoded_psbt_inputs:
|
for input in decoded_psbt_inputs:
|
||||||
assert_equal(input["txid"], source_tx["txid"])
|
assert_equal(input["previous_txid"], source_tx["txid"])
|
||||||
|
|
||||||
# Case (5), assert that inputs are added to the tx by explicitly setting add_inputs=true
|
# Case (5), assert that inputs are added to the tx by explicitly setting add_inputs=true
|
||||||
options = {"add_inputs": True, "add_to_wallet": True}
|
options = {"add_inputs": True, "add_to_wallet": True}
|
||||||
|
@ -1213,10 +1213,10 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||||
})
|
})
|
||||||
psbt_tx = wallet.walletcreatefundedpsbt(outputs=[{addr1: 8}], inputs=inputs, **options)
|
psbt_tx = wallet.walletcreatefundedpsbt(outputs=[{addr1: 8}], inputs=inputs, **options)
|
||||||
# Check that only the preset inputs were added to the tx
|
# Check that only the preset inputs were added to the tx
|
||||||
decoded_psbt_inputs = self.nodes[0].decodepsbt(psbt_tx["psbt"])['tx']['vin']
|
decoded_psbt_inputs = self.nodes[0].decodepsbt(psbt_tx["psbt"])["inputs"]
|
||||||
assert_equal(len(decoded_psbt_inputs), 2)
|
assert_equal(len(decoded_psbt_inputs), 2)
|
||||||
for input in decoded_psbt_inputs:
|
for input in decoded_psbt_inputs:
|
||||||
assert_equal(input["txid"], source_tx["txid"])
|
assert_equal(input["previous_txid"], source_tx["txid"])
|
||||||
|
|
||||||
# Case (5), 'walletcreatefundedpsbt' command
|
# Case (5), 'walletcreatefundedpsbt' command
|
||||||
# Explicit add_inputs=true, no preset inputs
|
# Explicit add_inputs=true, no preset inputs
|
||||||
|
|
|
@ -35,13 +35,13 @@ class WalletMultisigDescriptorPSBTTest(BitcoinTestFramework):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_psbt(psbt, to, value, multisig):
|
def _check_psbt(psbt, to, value, multisig):
|
||||||
"""Helper function for any of the N participants to check the psbt with decodepsbt and verify it is OK before signing."""
|
"""Helper function for any of the N participants to check the psbt with decodepsbt and verify it is OK before signing."""
|
||||||
tx = multisig.decodepsbt(psbt)["tx"]
|
decoded = multisig.decodepsbt(psbt)
|
||||||
amount = 0
|
amount = 0
|
||||||
for vout in tx["vout"]:
|
for psbt_out in decoded["outputs"]:
|
||||||
address = vout["scriptPubKey"]["address"]
|
address = psbt_out["script"]["address"]
|
||||||
assert_equal(multisig.getaddressinfo(address)["ischange"], address != to)
|
assert_equal(multisig.getaddressinfo(address)["ischange"], address != to)
|
||||||
if address == to:
|
if address == to:
|
||||||
amount += vout["value"]
|
amount += psbt_out["amount"]
|
||||||
assert_approx(amount, float(value), vspan=0.001)
|
assert_approx(amount, float(value), vspan=0.001)
|
||||||
|
|
||||||
def participants_create_multisigs(self, external_xpubs, internal_xpubs):
|
def participants_create_multisigs(self, external_xpubs, internal_xpubs):
|
||||||
|
|
|
@ -380,10 +380,10 @@ class WalletSendTest(BitcoinTestFramework):
|
||||||
assert res["complete"]
|
assert res["complete"]
|
||||||
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address, change_position=0)
|
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_address=change_address, change_position=0)
|
||||||
assert res["complete"]
|
assert res["complete"]
|
||||||
assert_equal(self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["address"], change_address)
|
assert_equal(self.nodes[0].decodepsbt(res["psbt"])["outputs"][0]["script"]["address"], change_address)
|
||||||
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_type="legacy", change_position=0)
|
res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, add_to_wallet=False, change_type="legacy", change_position=0)
|
||||||
assert res["complete"]
|
assert res["complete"]
|
||||||
change_address = self.nodes[0].decodepsbt(res["psbt"])["tx"]["vout"][0]["scriptPubKey"]["address"]
|
change_address = self.nodes[0].decodepsbt(res["psbt"])["outputs"][0]["script"]["address"]
|
||||||
assert change_address[0] == "m" or change_address[0] == "n"
|
assert change_address[0] == "m" or change_address[0] == "n"
|
||||||
|
|
||||||
self.log.info("Set lock time...")
|
self.log.info("Set lock time...")
|
||||||
|
@ -483,11 +483,9 @@ class WalletSendTest(BitcoinTestFramework):
|
||||||
assert signed["complete"]
|
assert signed["complete"]
|
||||||
|
|
||||||
dec = self.nodes[0].decodepsbt(signed["psbt"])
|
dec = self.nodes[0].decodepsbt(signed["psbt"])
|
||||||
for i, txin in enumerate(dec["tx"]["vin"]):
|
for psbt_in in dec["inputs"]:
|
||||||
if txin["txid"] == ext_utxo["txid"] and txin["vout"] == ext_utxo["vout"]:
|
if psbt_in["previous_txid"] == ext_utxo["txid"] and psbt_in["previous_vout"] == ext_utxo["vout"]:
|
||||||
input_idx = i
|
|
||||||
break
|
break
|
||||||
psbt_in = dec["inputs"][input_idx]
|
|
||||||
scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
|
scriptsig_hex = psbt_in["final_scriptSig"]["hex"] if "final_scriptSig" in psbt_in else ""
|
||||||
witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
|
witness_stack_hex = psbt_in["final_scriptwitness"] if "final_scriptwitness" in psbt_in else None
|
||||||
input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
|
input_weight = calculate_input_weight(scriptsig_hex, witness_stack_hex)
|
||||||
|
|
|
@ -306,9 +306,9 @@ class SendallTest(BitcoinTestFramework):
|
||||||
decoded = self.nodes[0].decodepsbt(psbt)
|
decoded = self.nodes[0].decodepsbt(psbt)
|
||||||
assert_equal(len(decoded["inputs"]), 1)
|
assert_equal(len(decoded["inputs"]), 1)
|
||||||
assert_equal(len(decoded["outputs"]), 1)
|
assert_equal(len(decoded["outputs"]), 1)
|
||||||
assert_equal(decoded["tx"]["vin"][0]["txid"], utxo["txid"])
|
assert_equal(decoded["inputs"][0]["previous_txid"], utxo["txid"])
|
||||||
assert_equal(decoded["tx"]["vin"][0]["vout"], utxo["vout"])
|
assert_equal(decoded["inputs"][0]["previous_vout"], utxo["vout"])
|
||||||
assert_equal(decoded["tx"]["vout"][0]["scriptPubKey"]["address"], self.remainder_target)
|
assert_equal(decoded["outputs"][0]["script"]["address"], self.remainder_target)
|
||||||
|
|
||||||
@cleanup
|
@cleanup
|
||||||
def sendall_with_minconf(self):
|
def sendall_with_minconf(self):
|
||||||
|
|
Loading…
Add table
Reference in a new issue