Merge bitcoin/bitcoin#32618: wallet: Remove ISMINE_WATCHONLY and watchonly from RPCs
b1a8ac07e9doc: Release note for removed watchonly parameters and results (Ava Chow)15710869e1wallet: Remove ISMINE_WATCH_ONLY (Ava Chow)4439bf4b41wallet, spend: Remove fWatchOnly from CCoinControl (Ava Chow)1337c72198wallet, rpc: Remove watchonly from RPCs (Ava Chow)e81d95d435wallet: Remove watchonly balances (Ava Chow)d20dc9c6aawallet: Wallets without private keys cannot grind R (Ava Chow)9991f49c38test: Watchonly wallets should estimate larger size (Ava Chow) Pull request description: Descriptor wallets do not use the watchonly behavior as it is not possible to mix watchonly and non-watchonly in a descriptor wallet. With legacy wallets now removed, all of the watchonly handling and reporting code is no longer needed. This PR removes watchonly options and results from the RPCs and the handling of watchonly things from the wallet's internals. With all of the watchonly things removed, ISMINE_WATCH_ONLY is removed as well. Split from #32523 Depends on #32594 for tests that are easier to read ACKs for top commit: Eunovo: ACK https://github.com/bitcoin/bitcoin/pull/32618/commits/b1a8ac07e91dd1d305fcbc16ea931d60e46c0055 maflcko: re-ACKb1a8ac07e9🌈 rkrux: ACKb1a8ac07e9furszy: light code review ACKb1a8ac07e9Tree-SHA512: bc87f37a13294f7208991be8f93899b49e5bdf87c70e0f66d9c4cb09c03be6c202320406f27e9a35aa2f57319d19a3f0c07d5e5ddbc97c7edab165b1656d6612
This commit is contained in:
23 files changed
+174
-271
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
Wallet
|
||||||
|
------
|
||||||
|
|
||||||
|
* Since descriptor wallets do not allow mixing watchonly and non-watchonly descriptors,
|
||||||
|
the `include_watchonly` option (and its variants in naming) are removed from all RPCs
|
||||||
|
that had it.
|
||||||
|
* The `iswatchonly` field is removed from any RPCs that returned it.
|
||||||
@@ -89,8 +89,6 @@ public:
|
|||||||
//! If true, the selection process can add extra unselected inputs from the wallet
|
//! If true, the selection process can add extra unselected inputs from the wallet
|
||||||
//! while requires all selected inputs be used
|
//! while requires all selected inputs be used
|
||||||
bool m_allow_other_inputs = true;
|
bool m_allow_other_inputs = true;
|
||||||
//! Includes watch only addresses which are solvable
|
|
||||||
bool fAllowWatchOnly = false;
|
|
||||||
//! Override automatic min/max checks on fee, m_feerate must be set if true
|
//! Override automatic min/max checks on fee, m_feerate must be set if true
|
||||||
bool fOverrideFeeRate = false;
|
bool fOverrideFeeRate = false;
|
||||||
//! Override the wallet's m_pay_tx_fee if set
|
//! Override the wallet's m_pay_tx_fee if set
|
||||||
|
|||||||
@@ -263,13 +263,10 @@ Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
|
|||||||
const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
|
const int tx_depth{wallet.GetTxDepthInMainChain(wtx)};
|
||||||
|
|
||||||
if (!wallet.IsSpent(outpoint) && (allow_used_addresses || !wallet.IsSpentKey(txo.GetTxOut().scriptPubKey))) {
|
if (!wallet.IsSpent(outpoint) && (allow_used_addresses || !wallet.IsSpentKey(txo.GetTxOut().scriptPubKey))) {
|
||||||
// Get the amounts for mine and watchonly
|
// Get the amounts for mine
|
||||||
CAmount credit_mine = 0;
|
CAmount credit_mine = 0;
|
||||||
CAmount credit_watchonly = 0;
|
|
||||||
if (txo.GetIsMine() == ISMINE_SPENDABLE) {
|
if (txo.GetIsMine() == ISMINE_SPENDABLE) {
|
||||||
credit_mine = txo.GetTxOut().nValue;
|
credit_mine = txo.GetTxOut().nValue;
|
||||||
} else if (txo.GetIsMine() == ISMINE_WATCH_ONLY) {
|
|
||||||
credit_watchonly = txo.GetTxOut().nValue;
|
|
||||||
} else {
|
} else {
|
||||||
// We shouldn't see any other isminetypes
|
// We shouldn't see any other isminetypes
|
||||||
Assume(false);
|
Assume(false);
|
||||||
@@ -278,13 +275,10 @@ Balance GetBalance(const CWallet& wallet, const int min_depth, bool avoid_reuse)
|
|||||||
// Set the amounts in the return object
|
// Set the amounts in the return object
|
||||||
if (wallet.IsTxImmatureCoinBase(wtx) && wtx.isConfirmed()) {
|
if (wallet.IsTxImmatureCoinBase(wtx) && wtx.isConfirmed()) {
|
||||||
ret.m_mine_immature += credit_mine;
|
ret.m_mine_immature += credit_mine;
|
||||||
ret.m_watchonly_immature += credit_watchonly;
|
|
||||||
} else if (is_trusted && tx_depth >= min_depth) {
|
} else if (is_trusted && tx_depth >= min_depth) {
|
||||||
ret.m_mine_trusted += credit_mine;
|
ret.m_mine_trusted += credit_mine;
|
||||||
ret.m_watchonly_trusted += credit_watchonly;
|
|
||||||
} else if (!is_trusted && wtx.InMempool()) {
|
} else if (!is_trusted && wtx.InMempool()) {
|
||||||
ret.m_mine_untrusted_pending += credit_mine;
|
ret.m_mine_untrusted_pending += credit_mine;
|
||||||
ret.m_watchonly_untrusted_pending += credit_watchonly;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,9 +49,6 @@ struct Balance {
|
|||||||
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
|
CAmount m_mine_trusted{0}; //!< Trusted, at depth=GetBalance.min_depth or more
|
||||||
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
|
CAmount m_mine_untrusted_pending{0}; //!< Untrusted, but in mempool (pending)
|
||||||
CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
|
CAmount m_mine_immature{0}; //!< Immature coinbases in the main chain
|
||||||
CAmount m_watchonly_trusted{0};
|
|
||||||
CAmount m_watchonly_untrusted_pending{0};
|
|
||||||
CAmount m_watchonly_immature{0};
|
|
||||||
};
|
};
|
||||||
Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse = true);
|
Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse = true);
|
||||||
|
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ RPCHelpMan getaddressinfo()
|
|||||||
{RPCResult::Type::STR, "address", "The bitcoin address validated."},
|
{RPCResult::Type::STR, "address", "The bitcoin address validated."},
|
||||||
{RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
|
{RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded output script generated by the address."},
|
||||||
{RPCResult::Type::BOOL, "ismine", "If the address is yours."},
|
{RPCResult::Type::BOOL, "ismine", "If the address is yours."},
|
||||||
{RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
|
{RPCResult::Type::BOOL, "iswatchonly", "(DEPRECATED) Always false."},
|
||||||
{RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
|
{RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
|
||||||
{RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
|
{RPCResult::Type::STR, "desc", /*optional=*/true, "A descriptor for spending coins sent to this address (only when solvable)."},
|
||||||
{RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
|
{RPCResult::Type::STR, "parent_desc", /*optional=*/true, "The descriptor used to derive this address if this is a descriptor wallet"},
|
||||||
@@ -402,7 +402,7 @@ RPCHelpMan getaddressinfo()
|
|||||||
{RPCResult::Type::OBJ, "embedded", /*optional=*/true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
|
{RPCResult::Type::OBJ, "embedded", /*optional=*/true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
|
{RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
|
||||||
"and relation to the wallet (ismine, iswatchonly)."},
|
"and relation to the wallet (ismine)."},
|
||||||
}},
|
}},
|
||||||
{RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
|
{RPCResult::Type::BOOL, "iscompressed", /*optional=*/true, "If the pubkey is compressed."},
|
||||||
{RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
|
{RPCResult::Type::NUM_TIME, "timestamp", /*optional=*/true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
|
||||||
@@ -475,7 +475,7 @@ RPCHelpMan getaddressinfo()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
|
ret.pushKV("iswatchonly", false);
|
||||||
|
|
||||||
UniValue detail = DescribeWalletAddress(*pwallet, dest);
|
UniValue detail = DescribeWalletAddress(*pwallet, dest);
|
||||||
ret.pushKVs(std::move(detail));
|
ret.pushKVs(std::move(detail));
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ RPCHelpMan getbalance()
|
|||||||
{
|
{
|
||||||
{"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
|
{"dummy", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Remains for backward compatibility. Must be excluded or set to \"*\"."},
|
||||||
{"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
|
{"minconf", RPCArg::Type::NUM, RPCArg::Default{0}, "Only include transactions confirmed at least this many times."},
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also include balance in watch-only addresses (see 'importaddress')"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "No longer used"},
|
||||||
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
|
{"avoid_reuse", RPCArg::Type::BOOL, RPCArg::Default{true}, "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
@@ -203,13 +203,11 @@ RPCHelpMan getbalance()
|
|||||||
|
|
||||||
const auto min_depth{self.Arg<int>("minconf")};
|
const auto min_depth{self.Arg<int>("minconf")};
|
||||||
|
|
||||||
bool include_watchonly = ParseIncludeWatchonly(request.params[2], *pwallet);
|
|
||||||
|
|
||||||
bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
|
bool avoid_reuse = GetAvoidReuseFlag(*pwallet, request.params[3]);
|
||||||
|
|
||||||
const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
|
const auto bal = GetBalance(*pwallet, min_depth, avoid_reuse);
|
||||||
|
|
||||||
return ValueFromAmount(bal.m_mine_trusted + (include_watchonly ? bal.m_watchonly_trusted : 0));
|
return ValueFromAmount(bal.m_mine_trusted);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+103
-124
@@ -146,9 +146,6 @@ static void PreventOutdatedOptions(const UniValue& options)
|
|||||||
if (options.exists("changePosition")) {
|
if (options.exists("changePosition")) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position instead of changePosition");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use change_position instead of changePosition");
|
||||||
}
|
}
|
||||||
if (options.exists("includeWatching")) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use include_watching instead of includeWatching");
|
|
||||||
}
|
|
||||||
if (options.exists("lockUnspents")) {
|
if (options.exists("lockUnspents")) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents instead of lockUnspents");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Use lock_unspents instead of lockUnspents");
|
||||||
}
|
}
|
||||||
@@ -516,126 +513,118 @@ CreatedTransactionResult FundTransaction(CWallet& wallet, const CMutableTransact
|
|||||||
std::optional<unsigned int> change_position;
|
std::optional<unsigned int> change_position;
|
||||||
bool lockUnspents = false;
|
bool lockUnspents = false;
|
||||||
if (!options.isNull()) {
|
if (!options.isNull()) {
|
||||||
if (options.type() == UniValue::VBOOL) {
|
if (options.type() == UniValue::VBOOL) {
|
||||||
// backward compatibility bool only fallback
|
// backward compatibility bool only fallback, does nothing
|
||||||
coinControl.fAllowWatchOnly = options.get_bool();
|
} else {
|
||||||
}
|
RPCTypeCheckObj(options,
|
||||||
else {
|
{
|
||||||
RPCTypeCheckObj(options,
|
{"add_inputs", UniValueType(UniValue::VBOOL)},
|
||||||
{
|
{"include_unsafe", UniValueType(UniValue::VBOOL)},
|
||||||
{"add_inputs", UniValueType(UniValue::VBOOL)},
|
{"add_to_wallet", UniValueType(UniValue::VBOOL)},
|
||||||
{"include_unsafe", UniValueType(UniValue::VBOOL)},
|
{"changeAddress", UniValueType(UniValue::VSTR)},
|
||||||
{"add_to_wallet", UniValueType(UniValue::VBOOL)},
|
{"change_address", UniValueType(UniValue::VSTR)},
|
||||||
{"changeAddress", UniValueType(UniValue::VSTR)},
|
{"changePosition", UniValueType(UniValue::VNUM)},
|
||||||
{"change_address", UniValueType(UniValue::VSTR)},
|
{"change_position", UniValueType(UniValue::VNUM)},
|
||||||
{"changePosition", UniValueType(UniValue::VNUM)},
|
{"change_type", UniValueType(UniValue::VSTR)},
|
||||||
{"change_position", UniValueType(UniValue::VNUM)},
|
{"includeWatching", UniValueType(UniValue::VBOOL)},
|
||||||
{"change_type", UniValueType(UniValue::VSTR)},
|
{"include_watching", UniValueType(UniValue::VBOOL)},
|
||||||
{"includeWatching", UniValueType(UniValue::VBOOL)},
|
{"inputs", UniValueType(UniValue::VARR)},
|
||||||
{"include_watching", UniValueType(UniValue::VBOOL)},
|
{"lockUnspents", UniValueType(UniValue::VBOOL)},
|
||||||
{"inputs", UniValueType(UniValue::VARR)},
|
{"lock_unspents", UniValueType(UniValue::VBOOL)},
|
||||||
{"lockUnspents", UniValueType(UniValue::VBOOL)},
|
{"locktime", UniValueType(UniValue::VNUM)},
|
||||||
{"lock_unspents", UniValueType(UniValue::VBOOL)},
|
{"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
|
||||||
{"locktime", UniValueType(UniValue::VNUM)},
|
{"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
|
||||||
{"fee_rate", UniValueType()}, // will be checked by AmountFromValue() in SetFeeEstimateMode()
|
{"psbt", UniValueType(UniValue::VBOOL)},
|
||||||
{"feeRate", UniValueType()}, // will be checked by AmountFromValue() below
|
{"solving_data", UniValueType(UniValue::VOBJ)},
|
||||||
{"psbt", UniValueType(UniValue::VBOOL)},
|
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
|
||||||
{"solving_data", UniValueType(UniValue::VOBJ)},
|
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
|
||||||
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
|
{"replaceable", UniValueType(UniValue::VBOOL)},
|
||||||
{"subtract_fee_from_outputs", UniValueType(UniValue::VARR)},
|
{"conf_target", UniValueType(UniValue::VNUM)},
|
||||||
{"replaceable", UniValueType(UniValue::VBOOL)},
|
{"estimate_mode", UniValueType(UniValue::VSTR)},
|
||||||
{"conf_target", UniValueType(UniValue::VNUM)},
|
{"minconf", UniValueType(UniValue::VNUM)},
|
||||||
{"estimate_mode", UniValueType(UniValue::VSTR)},
|
{"maxconf", UniValueType(UniValue::VNUM)},
|
||||||
{"minconf", UniValueType(UniValue::VNUM)},
|
{"input_weights", UniValueType(UniValue::VARR)},
|
||||||
{"maxconf", UniValueType(UniValue::VNUM)},
|
{"max_tx_weight", UniValueType(UniValue::VNUM)},
|
||||||
{"input_weights", UniValueType(UniValue::VARR)},
|
},
|
||||||
{"max_tx_weight", UniValueType(UniValue::VNUM)},
|
true, true);
|
||||||
},
|
|
||||||
true, true);
|
|
||||||
|
|
||||||
if (options.exists("add_inputs")) {
|
if (options.exists("add_inputs")) {
|
||||||
coinControl.m_allow_other_inputs = options["add_inputs"].get_bool();
|
coinControl.m_allow_other_inputs = options["add_inputs"].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);
|
|
||||||
|
|
||||||
if (!IsValidDestination(dest)) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid bitcoin address");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
coinControl.destChange = dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.exists("changePosition") || options.exists("change_position")) {
|
|
||||||
int pos = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
|
|
||||||
if (pos < 0 || (unsigned int)pos > recipients.size()) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
|
|
||||||
}
|
|
||||||
change_position = (unsigned int)pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.exists("change_type")) {
|
|
||||||
if (options.exists("changeAddress") || options.exists("change_address")) {
|
if (options.exists("changeAddress") || options.exists("change_address")) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both change address and address type options");
|
const std::string change_address_str = (options.exists("change_address") ? options["change_address"] : options["changeAddress"]).get_str();
|
||||||
|
CTxDestination dest = DecodeDestination(change_address_str);
|
||||||
|
|
||||||
|
if (!IsValidDestination(dest)) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Change address must be a valid bitcoin address");
|
||||||
|
}
|
||||||
|
|
||||||
|
coinControl.destChange = dest;
|
||||||
}
|
}
|
||||||
if (std::optional<OutputType> parsed = ParseOutputType(options["change_type"].get_str())) {
|
|
||||||
coinControl.m_change_type.emplace(parsed.value());
|
if (options.exists("changePosition") || options.exists("change_position")) {
|
||||||
} else {
|
int pos = (options.exists("change_position") ? options["change_position"] : options["changePosition"]).getInt<int>();
|
||||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
|
if (pos < 0 || (unsigned int)pos > recipients.size()) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
|
||||||
|
}
|
||||||
|
change_position = (unsigned int)pos;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
|
if (options.exists("change_type")) {
|
||||||
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(include_watching_option, wallet);
|
if (options.exists("changeAddress") || options.exists("change_address")) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both change address and address type options");
|
||||||
if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
|
}
|
||||||
lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
|
if (std::optional<OutputType> parsed = ParseOutputType(options["change_type"].get_str())) {
|
||||||
}
|
coinControl.m_change_type.emplace(parsed.value());
|
||||||
|
} else {
|
||||||
if (options.exists("include_unsafe")) {
|
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
|
||||||
coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (options.exists("feeRate")) {
|
|
||||||
if (options.exists("fee_rate")) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/vB) and feeRate (" + CURRENCY_UNIT + "/kvB)");
|
|
||||||
}
|
}
|
||||||
if (options.exists("conf_target")) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
|
if (options.exists("lockUnspents") || options.exists("lock_unspents")) {
|
||||||
|
lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
|
||||||
}
|
}
|
||||||
if (options.exists("estimate_mode")) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
|
if (options.exists("include_unsafe")) {
|
||||||
|
coinControl.m_include_unsafe_inputs = options["include_unsafe"].get_bool();
|
||||||
}
|
}
|
||||||
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
|
|
||||||
coinControl.fOverrideFeeRate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.exists("replaceable")) {
|
if (options.exists("feeRate")) {
|
||||||
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
|
if (options.exists("fee_rate")) {
|
||||||
}
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/vB) and feeRate (" + CURRENCY_UNIT + "/kvB)");
|
||||||
|
}
|
||||||
if (options.exists("minconf")) {
|
if (options.exists("conf_target")) {
|
||||||
coinControl.m_min_depth = options["minconf"].getInt<int>();
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
|
||||||
|
}
|
||||||
if (coinControl.m_min_depth < 0) {
|
if (options.exists("estimate_mode")) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative minconf");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
|
||||||
|
}
|
||||||
|
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
|
||||||
|
coinControl.fOverrideFeeRate = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (options.exists("maxconf")) {
|
if (options.exists("replaceable")) {
|
||||||
coinControl.m_max_depth = options["maxconf"].getInt<int>();
|
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
|
||||||
|
|
||||||
if (coinControl.m_max_depth < coinControl.m_min_depth) {
|
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("maxconf can't be lower than minconf: %d < %d", coinControl.m_max_depth, coinControl.m_min_depth));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.exists("minconf")) {
|
||||||
|
coinControl.m_min_depth = options["minconf"].getInt<int>();
|
||||||
|
|
||||||
|
if (coinControl.m_min_depth < 0) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative minconf");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.exists("maxconf")) {
|
||||||
|
coinControl.m_max_depth = options["maxconf"].getInt<int>();
|
||||||
|
|
||||||
|
if (coinControl.m_max_depth < coinControl.m_min_depth) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("maxconf can't be lower than minconf: %d < %d", coinControl.m_max_depth, coinControl.m_min_depth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
|
||||||
}
|
}
|
||||||
SetFeeEstimateMode(wallet, coinControl, options["conf_target"], options["estimate_mode"], options["fee_rate"], override_min_fee);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// if options is null and not a bool
|
|
||||||
coinControl.fAllowWatchOnly = ParseIncludeWatchonly(NullUniValue, wallet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.exists("solving_data")) {
|
if (options.exists("solving_data")) {
|
||||||
@@ -757,13 +746,12 @@ RPCHelpMan fundrawtransaction()
|
|||||||
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
|
"Note that all inputs selected must be of standard form and P2SH scripts must be\n"
|
||||||
"in the wallet using importdescriptors (to calculate fees).\n"
|
"in the wallet using importdescriptors (to calculate fees).\n"
|
||||||
"You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
|
"You can see whether this is the case by checking the \"solvable\" field in the listunspent output.\n"
|
||||||
"Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only.\n"
|
|
||||||
"Note that if specifying an exact fee rate, the resulting transaction may have a higher fee rate\n"
|
"Note that if specifying an exact fee rate, the resulting transaction may have a higher fee rate\n"
|
||||||
"if the transaction has unconfirmed inputs. This is because the wallet will attempt to make the\n"
|
"if the transaction has unconfirmed inputs. This is because the wallet will attempt to make the\n"
|
||||||
"entire package have the given fee rate, not the resulting transaction.\n",
|
"entire package have the given fee rate, not the resulting transaction.\n",
|
||||||
{
|
{
|
||||||
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
|
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
|
||||||
{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "For backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
|
{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
|
||||||
Cat<std::vector<RPCArg>>(
|
Cat<std::vector<RPCArg>>(
|
||||||
{
|
{
|
||||||
{"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
|
{"add_inputs", RPCArg::Type::BOOL, RPCArg::Default{true}, "For a transaction with existing inputs, automatically include more if they are not enough."},
|
||||||
@@ -775,9 +763,7 @@ RPCHelpMan fundrawtransaction()
|
|||||||
{"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
|
{"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
|
||||||
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
||||||
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
||||||
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
|
{"includeWatching", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
|
||||||
"e.g. with 'importdescriptors'."},
|
|
||||||
{"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
{"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
||||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
||||||
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
||||||
@@ -1081,7 +1067,6 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
|
|||||||
Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
|
Txid hash{Txid::FromUint256(ParseHashV(request.params[0], "txid"))};
|
||||||
|
|
||||||
CCoinControl coin_control;
|
CCoinControl coin_control;
|
||||||
coin_control.fAllowWatchOnly = pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
|
||||||
// optional parameters
|
// optional parameters
|
||||||
coin_control.m_signal_bip125_rbf = true;
|
coin_control.m_signal_bip125_rbf = true;
|
||||||
std::vector<CTxOut> outputs;
|
std::vector<CTxOut> outputs;
|
||||||
@@ -1240,9 +1225,7 @@ RPCHelpMan send()
|
|||||||
{"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
{"change_position", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
||||||
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\" and \"bech32m\"."},
|
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if change_address is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\" and \"bech32m\"."},
|
||||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB.", RPCArgOptions{.also_positional = true}},
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB.", RPCArgOptions{.also_positional = true}},
|
||||||
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only.\n"
|
{"include_watching", RPCArg::Type::BOOL, RPCArg::Default{"false"}, "(DEPRECATED) No longer used"},
|
||||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
|
||||||
"e.g. with 'importdescriptors'."},
|
|
||||||
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically.",
|
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Specify inputs instead of adding them automatically.",
|
||||||
{
|
{
|
||||||
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", {
|
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "", {
|
||||||
@@ -1360,9 +1343,7 @@ RPCHelpMan sendall()
|
|||||||
{
|
{
|
||||||
{"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns the serialized transaction without broadcasting or adding it to the wallet"},
|
{"add_to_wallet", RPCArg::Type::BOOL, RPCArg::Default{true}, "When false, returns the serialized transaction without broadcasting or adding it to the wallet"},
|
||||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB.", RPCArgOptions{.also_positional = true}},
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB.", RPCArgOptions{.also_positional = true}},
|
||||||
{"include_watching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch-only.\n"
|
{"include_watching", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
"Only solvable inputs can be used. Watch-only destinations are solvable if the public key and/or output script was imported,\n"
|
|
||||||
"e.g. with 'importdescriptors'."},
|
|
||||||
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Use exactly the specified inputs to build the transaction. Specifying inputs is incompatible with the send_max, minconf, and maxconf options.",
|
{"inputs", RPCArg::Type::ARR, RPCArg::Default{UniValue::VARR}, "Use exactly the specified inputs to build the transaction. Specifying inputs is incompatible with the send_max, minconf, and maxconf options.",
|
||||||
{
|
{
|
||||||
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
|
||||||
@@ -1443,8 +1424,6 @@ RPCHelpMan sendall()
|
|||||||
|
|
||||||
SetFeeEstimateMode(*pwallet, coin_control, options["conf_target"], options["estimate_mode"], options["fee_rate"], /*override_min_fee=*/false);
|
SetFeeEstimateMode(*pwallet, coin_control, options["conf_target"], options["estimate_mode"], options["fee_rate"], /*override_min_fee=*/false);
|
||||||
|
|
||||||
coin_control.fAllowWatchOnly = ParseIncludeWatchonly(options["include_watching"], *pwallet);
|
|
||||||
|
|
||||||
if (options.exists("minconf")) {
|
if (options.exists("minconf")) {
|
||||||
if (options["minconf"].getInt<int>() < 0)
|
if (options["minconf"].getInt<int>() < 0)
|
||||||
{
|
{
|
||||||
@@ -1491,7 +1470,7 @@ RPCHelpMan sendall()
|
|||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not available. UTXO (%s:%d) was already spent.", input.prevout.hash.ToString(), input.prevout.n));
|
||||||
}
|
}
|
||||||
const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
|
const CWalletTx* tx{pwallet->GetWalletTx(input.prevout.hash)};
|
||||||
if (!tx || input.prevout.n >= tx->tx->vout.size() || !(pwallet->IsMine(tx->tx->vout[input.prevout.n]) & (coin_control.fAllowWatchOnly ? ISMINE_ALL : ISMINE_SPENDABLE))) {
|
if (!tx || input.prevout.n >= tx->tx->vout.size() || !(pwallet->IsMine(tx->tx->vout[input.prevout.n]) & ISMINE_SPENDABLE)) {
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not found. UTXO (%s:%d) is not part of wallet.", input.prevout.hash.ToString(), input.prevout.n));
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Input not found. UTXO (%s:%d) is not part of wallet.", input.prevout.hash.ToString(), input.prevout.n));
|
||||||
}
|
}
|
||||||
total_input_value += tx->tx->vout[input.prevout.n].nValue;
|
total_input_value += tx->tx->vout[input.prevout.n].nValue;
|
||||||
@@ -1720,7 +1699,7 @@ RPCHelpMan walletcreatefundedpsbt()
|
|||||||
{"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
|
{"changeAddress", RPCArg::Type::STR, RPCArg::DefaultHint{"automatic"}, "The bitcoin address to receive the change"},
|
||||||
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
{"changePosition", RPCArg::Type::NUM, RPCArg::DefaultHint{"random"}, "The index of the change output"},
|
||||||
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
{"change_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
|
||||||
{"includeWatching", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Also select inputs which are watch only"},
|
{"includeWatching", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
{"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
{"lockUnspents", RPCArg::Type::BOOL, RPCArg::Default{false}, "Lock selected unspent outputs"},
|
||||||
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
{"fee_rate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_ATOM + "/vB."},
|
||||||
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
{"feeRate", RPCArg::Type::AMOUNT, RPCArg::DefaultHint{"not set, fall back to wallet fee estimation"}, "Specify a fee rate in " + CURRENCY_UNIT + "/kvB."},
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ struct tallyitem
|
|||||||
CAmount nAmount{0};
|
CAmount nAmount{0};
|
||||||
int nConf{std::numeric_limits<int>::max()};
|
int nConf{std::numeric_limits<int>::max()};
|
||||||
std::vector<Txid> txids;
|
std::vector<Txid> txids;
|
||||||
bool fIsWatchonly{false};
|
|
||||||
tallyitem() = default;
|
tallyitem() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,10 +85,6 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
|
|||||||
|
|
||||||
isminefilter filter = ISMINE_SPENDABLE;
|
isminefilter filter = ISMINE_SPENDABLE;
|
||||||
|
|
||||||
if (ParseIncludeWatchonly(params[2], wallet)) {
|
|
||||||
filter |= ISMINE_WATCH_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<CTxDestination> filtered_address{std::nullopt};
|
std::optional<CTxDestination> filtered_address{std::nullopt};
|
||||||
if (!by_label && !params[3].isNull() && !params[3].get_str().empty()) {
|
if (!by_label && !params[3].isNull() && !params[3].get_str().empty()) {
|
||||||
if (!IsValidDestinationString(params[3].get_str())) {
|
if (!IsValidDestinationString(params[3].get_str())) {
|
||||||
@@ -129,8 +124,6 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
|
|||||||
item.nAmount += txout.nValue;
|
item.nAmount += txout.nValue;
|
||||||
item.nConf = std::min(item.nConf, nDepth);
|
item.nConf = std::min(item.nConf, nDepth);
|
||||||
item.txids.push_back(wtx.GetHash());
|
item.txids.push_back(wtx.GetHash());
|
||||||
if (mine & ISMINE_WATCH_ONLY)
|
|
||||||
item.fIsWatchonly = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,21 +140,17 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
|
|||||||
|
|
||||||
CAmount nAmount = 0;
|
CAmount nAmount = 0;
|
||||||
int nConf = std::numeric_limits<int>::max();
|
int nConf = std::numeric_limits<int>::max();
|
||||||
bool fIsWatchonly = false;
|
|
||||||
if (it != mapTally.end()) {
|
if (it != mapTally.end()) {
|
||||||
nAmount = (*it).second.nAmount;
|
nAmount = (*it).second.nAmount;
|
||||||
nConf = (*it).second.nConf;
|
nConf = (*it).second.nConf;
|
||||||
fIsWatchonly = (*it).second.fIsWatchonly;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (by_label) {
|
if (by_label) {
|
||||||
tallyitem& _item = label_tally[label];
|
tallyitem& _item = label_tally[label];
|
||||||
_item.nAmount += nAmount;
|
_item.nAmount += nAmount;
|
||||||
_item.nConf = std::min(_item.nConf, nConf);
|
_item.nConf = std::min(_item.nConf, nConf);
|
||||||
_item.fIsWatchonly = fIsWatchonly;
|
|
||||||
} else {
|
} else {
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
if (fIsWatchonly) obj.pushKV("involvesWatchonly", true);
|
|
||||||
obj.pushKV("address", EncodeDestination(address));
|
obj.pushKV("address", EncodeDestination(address));
|
||||||
obj.pushKV("amount", ValueFromAmount(nAmount));
|
obj.pushKV("amount", ValueFromAmount(nAmount));
|
||||||
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
||||||
@@ -190,8 +179,6 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons
|
|||||||
CAmount nAmount = entry.second.nAmount;
|
CAmount nAmount = entry.second.nAmount;
|
||||||
int nConf = entry.second.nConf;
|
int nConf = entry.second.nConf;
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
if (entry.second.fIsWatchonly)
|
|
||||||
obj.pushKV("involvesWatchonly", true);
|
|
||||||
obj.pushKV("amount", ValueFromAmount(nAmount));
|
obj.pushKV("amount", ValueFromAmount(nAmount));
|
||||||
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf));
|
||||||
obj.pushKV("label", entry.first);
|
obj.pushKV("label", entry.first);
|
||||||
@@ -210,7 +197,7 @@ RPCHelpMan listreceivedbyaddress()
|
|||||||
{
|
{
|
||||||
{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
|
{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
|
||||||
{"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include addresses that haven't received any payments."},
|
{"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include addresses that haven't received any payments."},
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see 'importaddress')"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If present and non-empty, only return information on this address."},
|
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If present and non-empty, only return information on this address."},
|
||||||
{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
|
{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
|
||||||
},
|
},
|
||||||
@@ -219,7 +206,6 @@ RPCHelpMan listreceivedbyaddress()
|
|||||||
{
|
{
|
||||||
{RPCResult::Type::OBJ, "", "",
|
{RPCResult::Type::OBJ, "", "",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::BOOL, "involvesWatchonly", /*optional=*/true, "Only returns true if imported addresses were involved in transaction"},
|
|
||||||
{RPCResult::Type::STR, "address", "The receiving address"},
|
{RPCResult::Type::STR, "address", "The receiving address"},
|
||||||
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
|
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
|
||||||
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
|
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
|
||||||
@@ -264,7 +250,7 @@ RPCHelpMan listreceivedbylabel()
|
|||||||
{
|
{
|
||||||
{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
|
{"minconf", RPCArg::Type::NUM, RPCArg::Default{1}, "The minimum number of confirmations before payments are included."},
|
||||||
{"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include labels that haven't received any payments."},
|
{"include_empty", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to include labels that haven't received any payments."},
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see 'importaddress')"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
|
{"include_immature_coinbase", RPCArg::Type::BOOL, RPCArg::Default{false}, "Include immature coinbase transactions."},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
@@ -272,7 +258,6 @@ RPCHelpMan listreceivedbylabel()
|
|||||||
{
|
{
|
||||||
{RPCResult::Type::OBJ, "", "",
|
{RPCResult::Type::OBJ, "", "",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::BOOL, "involvesWatchonly", /*optional=*/true, "Only returns true if imported addresses were involved in transaction"},
|
|
||||||
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
|
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
|
||||||
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
|
{RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
|
||||||
{RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
|
{RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
|
||||||
@@ -332,17 +317,12 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
|
|||||||
|
|
||||||
CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine, include_change);
|
CachedTxGetAmounts(wallet, wtx, listReceived, listSent, nFee, filter_ismine, include_change);
|
||||||
|
|
||||||
bool involvesWatchonly = CachedTxIsFromMe(wallet, wtx, ISMINE_WATCH_ONLY);
|
|
||||||
|
|
||||||
// Sent
|
// Sent
|
||||||
if (!filter_label.has_value())
|
if (!filter_label.has_value())
|
||||||
{
|
{
|
||||||
for (const COutputEntry& s : listSent)
|
for (const COutputEntry& s : listSent)
|
||||||
{
|
{
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
if (involvesWatchonly || (wallet.IsMine(s.destination) & ISMINE_WATCH_ONLY)) {
|
|
||||||
entry.pushKV("involvesWatchonly", true);
|
|
||||||
}
|
|
||||||
MaybePushAddress(entry, s.destination);
|
MaybePushAddress(entry, s.destination);
|
||||||
entry.pushKV("category", "send");
|
entry.pushKV("category", "send");
|
||||||
entry.pushKV("amount", ValueFromAmount(-s.amount));
|
entry.pushKV("amount", ValueFromAmount(-s.amount));
|
||||||
@@ -372,9 +352,6 @@ static void ListTransactions(const CWallet& wallet, const CWalletTx& wtx, int nM
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
if (involvesWatchonly || (wallet.IsMine(r.destination) & ISMINE_WATCH_ONLY)) {
|
|
||||||
entry.pushKV("involvesWatchonly", true);
|
|
||||||
}
|
|
||||||
MaybePushAddress(entry, r.destination);
|
MaybePushAddress(entry, r.destination);
|
||||||
PushParentDescriptors(wallet, wtx.tx->vout.at(r.vout).scriptPubKey, entry);
|
PushParentDescriptors(wallet, wtx.tx->vout.at(r.vout).scriptPubKey, entry);
|
||||||
if (wtx.IsCoinBase())
|
if (wtx.IsCoinBase())
|
||||||
@@ -450,14 +427,13 @@ RPCHelpMan listtransactions()
|
|||||||
"with the specified label, or \"*\" to disable filtering and return all transactions."},
|
"with the specified label, or \"*\" to disable filtering and return all transactions."},
|
||||||
{"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
|
{"count", RPCArg::Type::NUM, RPCArg::Default{10}, "The number of transactions to return"},
|
||||||
{"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
|
{"skip", RPCArg::Type::NUM, RPCArg::Default{0}, "The number of transactions to skip"},
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
},
|
},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
RPCResult::Type::ARR, "", "",
|
RPCResult::Type::ARR, "", "",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
|
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
|
||||||
{
|
{
|
||||||
{RPCResult::Type::BOOL, "involvesWatchonly", /*optional=*/true, "Only returns true if imported addresses were involved in transaction."},
|
|
||||||
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
|
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
|
||||||
{RPCResult::Type::STR, "category", "The transaction category.\n"
|
{RPCResult::Type::STR, "category", "The transaction category.\n"
|
||||||
"\"send\" Transactions sent.\n"
|
"\"send\" Transactions sent.\n"
|
||||||
@@ -510,10 +486,6 @@ RPCHelpMan listtransactions()
|
|||||||
nFrom = request.params[2].getInt<int>();
|
nFrom = request.params[2].getInt<int>();
|
||||||
isminefilter filter = ISMINE_SPENDABLE;
|
isminefilter filter = ISMINE_SPENDABLE;
|
||||||
|
|
||||||
if (ParseIncludeWatchonly(request.params[3], *pwallet)) {
|
|
||||||
filter |= ISMINE_WATCH_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nCount < 0)
|
if (nCount < 0)
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
|
||||||
if (nFrom < 0)
|
if (nFrom < 0)
|
||||||
@@ -559,7 +531,7 @@ RPCHelpMan listsinceblock()
|
|||||||
{
|
{
|
||||||
{"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If set, the block hash to list transactions since, otherwise list all transactions."},
|
{"blockhash", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "If set, the block hash to list transactions since, otherwise list all transactions."},
|
||||||
{"target_confirmations", RPCArg::Type::NUM, RPCArg::Default{1}, "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
|
{"target_confirmations", RPCArg::Type::NUM, RPCArg::Default{1}, "Return the nth block hash from the main chain. e.g. 1 would mean the best block hash. Note: this is not used as a filter, but only affects [lastblock] in the return value"},
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Include transactions to watch-only addresses (see 'importaddress')"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
{"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
|
{"include_removed", RPCArg::Type::BOOL, RPCArg::Default{true}, "Show transactions that were removed due to a reorg in the \"removed\" array\n"
|
||||||
"(not guaranteed to work on pruned nodes)"},
|
"(not guaranteed to work on pruned nodes)"},
|
||||||
{"include_change", RPCArg::Type::BOOL, RPCArg::Default{false}, "Also add entries for change outputs.\n"},
|
{"include_change", RPCArg::Type::BOOL, RPCArg::Default{false}, "Also add entries for change outputs.\n"},
|
||||||
@@ -572,7 +544,6 @@ RPCHelpMan listsinceblock()
|
|||||||
{
|
{
|
||||||
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
|
{RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
|
||||||
{
|
{
|
||||||
{RPCResult::Type::BOOL, "involvesWatchonly", /*optional=*/true, "Only returns true if imported addresses were involved in transaction."},
|
|
||||||
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
|
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address of the transaction (not returned if the output does not have an address, e.g. OP_RETURN null data)."},
|
||||||
{RPCResult::Type::STR, "category", "The transaction category.\n"
|
{RPCResult::Type::STR, "category", "The transaction category.\n"
|
||||||
"\"send\" Transactions sent.\n"
|
"\"send\" Transactions sent.\n"
|
||||||
@@ -638,10 +609,6 @@ RPCHelpMan listsinceblock()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ParseIncludeWatchonly(request.params[2], wallet)) {
|
|
||||||
filter |= ISMINE_WATCH_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
|
bool include_removed = (request.params[3].isNull() || request.params[3].get_bool());
|
||||||
bool include_change = (!request.params[4].isNull() && request.params[4].get_bool());
|
bool include_change = (!request.params[4].isNull() && request.params[4].get_bool());
|
||||||
|
|
||||||
@@ -701,8 +668,7 @@ RPCHelpMan gettransaction()
|
|||||||
"Get detailed information about in-wallet transaction <txid>\n",
|
"Get detailed information about in-wallet transaction <txid>\n",
|
||||||
{
|
{
|
||||||
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
|
{"txid", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction id"},
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
"Whether to include watch-only addresses in balance calculation and details[]"},
|
|
||||||
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
|
{"verbose", RPCArg::Type::BOOL, RPCArg::Default{false},
|
||||||
"Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
|
"Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
|
||||||
},
|
},
|
||||||
@@ -719,7 +685,6 @@ RPCHelpMan gettransaction()
|
|||||||
{
|
{
|
||||||
{RPCResult::Type::OBJ, "", "",
|
{RPCResult::Type::OBJ, "", "",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::BOOL, "involvesWatchonly", /*optional=*/true, "Only returns true if imported addresses were involved in transaction."},
|
|
||||||
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address involved in the transaction."},
|
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address involved in the transaction."},
|
||||||
{RPCResult::Type::STR, "category", "The transaction category.\n"
|
{RPCResult::Type::STR, "category", "The transaction category.\n"
|
||||||
"\"send\" Transactions sent.\n"
|
"\"send\" Transactions sent.\n"
|
||||||
@@ -767,10 +732,6 @@ RPCHelpMan gettransaction()
|
|||||||
|
|
||||||
isminefilter filter = ISMINE_SPENDABLE;
|
isminefilter filter = ISMINE_SPENDABLE;
|
||||||
|
|
||||||
if (ParseIncludeWatchonly(request.params[1], *pwallet)) {
|
|
||||||
filter |= ISMINE_WATCH_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
|
bool verbose = request.params[2].isNull() ? false : request.params[2].get_bool();
|
||||||
|
|
||||||
UniValue entry(UniValue::VOBJ);
|
UniValue entry(UniValue::VOBJ);
|
||||||
|
|||||||
@@ -29,21 +29,6 @@ bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param) {
|
|||||||
return avoid_reuse;
|
return avoid_reuse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used by RPC commands that have an include_watchonly parameter.
|
|
||||||
* We default to true for watchonly wallets if include_watchonly isn't
|
|
||||||
* explicitly set.
|
|
||||||
*/
|
|
||||||
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet)
|
|
||||||
{
|
|
||||||
if (include_watchonly.isNull()) {
|
|
||||||
// if include_watchonly isn't explicitly set, then check if we have a watchonly wallet
|
|
||||||
return wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise return whatever include_watchonly was set to
|
|
||||||
return include_watchonly.get_bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
|
bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest& request, std::string& wallet_name)
|
||||||
{
|
{
|
||||||
if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
|
if (request.URI.starts_with(WALLET_ENDPOINT_BASE)) {
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ void EnsureWalletIsUnlocked(const CWallet&);
|
|||||||
WalletContext& EnsureWalletContext(const std::any& context);
|
WalletContext& EnsureWalletContext(const std::any& context);
|
||||||
|
|
||||||
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param);
|
bool GetAvoidReuseFlag(const CWallet& wallet, const UniValue& param);
|
||||||
bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wallet);
|
|
||||||
std::string LabelFromValue(const UniValue& value);
|
std::string LabelFromValue(const UniValue& value);
|
||||||
//! Fetch parent descriptors of this scriptPubKey.
|
//! Fetch parent descriptors of this scriptPubKey.
|
||||||
void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry);
|
void PushParentDescriptors(const CWallet& wallet, const CScript& script_pubkey, UniValue& entry);
|
||||||
|
|||||||
@@ -573,7 +573,7 @@ RPCHelpMan simulaterawtransaction()
|
|||||||
},
|
},
|
||||||
{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
|
{"options", RPCArg::Type::OBJ_NAMED_PARAMS, RPCArg::Optional::OMITTED, "",
|
||||||
{
|
{
|
||||||
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::DefaultHint{"true for watch-only wallets, otherwise false"}, "Whether to include watch-only addresses (see RPC importaddress)"},
|
{"include_watchonly", RPCArg::Type::BOOL, RPCArg::Default{false}, "(DEPRECATED) No longer used"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -595,22 +595,7 @@ RPCHelpMan simulaterawtransaction()
|
|||||||
|
|
||||||
LOCK(wallet.cs_wallet);
|
LOCK(wallet.cs_wallet);
|
||||||
|
|
||||||
UniValue include_watchonly(UniValue::VNULL);
|
|
||||||
if (request.params[1].isObject()) {
|
|
||||||
UniValue options = request.params[1];
|
|
||||||
RPCTypeCheckObj(options,
|
|
||||||
{
|
|
||||||
{"include_watchonly", UniValueType(UniValue::VBOOL)},
|
|
||||||
},
|
|
||||||
true, true);
|
|
||||||
|
|
||||||
include_watchonly = options["include_watchonly"];
|
|
||||||
}
|
|
||||||
|
|
||||||
isminefilter filter = ISMINE_SPENDABLE;
|
isminefilter filter = ISMINE_SPENDABLE;
|
||||||
if (ParseIncludeWatchonly(include_watchonly, wallet)) {
|
|
||||||
filter |= ISMINE_WATCH_ONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& txs = request.params[0].get_array();
|
const auto& txs = request.params[0].get_array();
|
||||||
CAmount changes{0};
|
CAmount changes{0};
|
||||||
|
|||||||
@@ -204,7 +204,6 @@ isminetype LegacyDataSPKM::IsMine(const CScript& script) const
|
|||||||
case IsMineResult::NO:
|
case IsMineResult::NO:
|
||||||
return ISMINE_NO;
|
return ISMINE_NO;
|
||||||
case IsMineResult::WATCH_ONLY:
|
case IsMineResult::WATCH_ONLY:
|
||||||
return ISMINE_WATCH_ONLY;
|
|
||||||
case IsMineResult::SPENDABLE:
|
case IsMineResult::SPENDABLE:
|
||||||
return ISMINE_SPENDABLE;
|
return ISMINE_SPENDABLE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ static bool IsSegwit(const Descriptor& desc) {
|
|||||||
static bool UseMaxSig(const std::optional<CTxIn>& txin, const CCoinControl* coin_control) {
|
static bool UseMaxSig(const std::optional<CTxIn>& txin, const CCoinControl* coin_control) {
|
||||||
// Use max sig if watch only inputs were used or if this particular input is an external input
|
// Use max sig if watch only inputs were used or if this particular input is an external input
|
||||||
// to ensure a sufficient fee is attained for the requested feerate.
|
// to ensure a sufficient fee is attained for the requested feerate.
|
||||||
return coin_control && (coin_control->fAllowWatchOnly || (txin && coin_control->IsExternalSelected(txin->prevout)));
|
return coin_control && txin && coin_control->IsExternalSelected(txin->prevout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get the size of an input (in witness units) once it's signed.
|
/** Get the size of an input (in witness units) once it's signed.
|
||||||
@@ -429,7 +429,7 @@ CoinsResult AvailableCoins(const CWallet& wallet,
|
|||||||
// Because CalculateMaximumSignedInputSize infers a solvable descriptor to get the satisfaction size,
|
// 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.
|
// it is safe to assume that this input is solvable if input_bytes is greater than -1.
|
||||||
bool solvable = input_bytes > -1;
|
bool solvable = input_bytes > -1;
|
||||||
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
|
bool spendable = (mine & ISMINE_SPENDABLE) != ISMINE_NO;
|
||||||
|
|
||||||
// Filter by spendable outputs only
|
// Filter by spendable outputs only
|
||||||
if (!spendable && params.only_spendable) continue;
|
if (!spendable && params.only_spendable) continue;
|
||||||
|
|||||||
+1
-3
@@ -25,7 +25,6 @@ namespace wallet {
|
|||||||
*
|
*
|
||||||
* For LegacyScriptPubKeyMan,
|
* For LegacyScriptPubKeyMan,
|
||||||
* ISMINE_NO: the scriptPubKey is not in the wallet;
|
* ISMINE_NO: the scriptPubKey is not in the wallet;
|
||||||
* ISMINE_WATCH_ONLY: the scriptPubKey has been imported into the wallet;
|
|
||||||
* ISMINE_SPENDABLE: the scriptPubKey corresponds to an address owned by the wallet user (can spend with the private key);
|
* ISMINE_SPENDABLE: the scriptPubKey corresponds to an address owned by the wallet user (can spend with the private key);
|
||||||
* ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user;
|
* ISMINE_USED: the scriptPubKey corresponds to a used address owned by the wallet user;
|
||||||
* ISMINE_ALL: all ISMINE flags except for USED;
|
* ISMINE_ALL: all ISMINE flags except for USED;
|
||||||
@@ -40,10 +39,9 @@ namespace wallet {
|
|||||||
*/
|
*/
|
||||||
enum isminetype : unsigned int {
|
enum isminetype : unsigned int {
|
||||||
ISMINE_NO = 0,
|
ISMINE_NO = 0,
|
||||||
ISMINE_WATCH_ONLY = 1 << 0,
|
|
||||||
ISMINE_SPENDABLE = 1 << 1,
|
ISMINE_SPENDABLE = 1 << 1,
|
||||||
ISMINE_USED = 1 << 2,
|
ISMINE_USED = 1 << 2,
|
||||||
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE,
|
ISMINE_ALL = ISMINE_SPENDABLE,
|
||||||
ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED,
|
ISMINE_ALL_USED = ISMINE_ALL | ISMINE_USED,
|
||||||
ISMINE_ENUM_ELEMENTS,
|
ISMINE_ENUM_ELEMENTS,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4049,7 +4049,7 @@ util::Result<void> CWallet::ApplyMigrationData(WalletBatch& local_wallet_batch,
|
|||||||
|
|
||||||
bool CWallet::CanGrindR() const
|
bool CWallet::CanGrindR() const
|
||||||
{
|
{
|
||||||
return !IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER);
|
return !IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
|
bool DoMigration(CWallet& wallet, WalletContext& context, bilingual_str& error, MigrationResult& res) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
|
||||||
|
|||||||
@@ -827,7 +827,7 @@ class PSBTTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# Test that psbts with p2pkh outputs are created properly
|
# Test that psbts with p2pkh outputs are created properly
|
||||||
p2pkh = self.nodes[0].getnewaddress(address_type='legacy')
|
p2pkh = self.nodes[0].getnewaddress(address_type='legacy')
|
||||||
psbt = self.nodes[1].walletcreatefundedpsbt([], [{p2pkh : 1}], 0, {"includeWatching" : True}, True)
|
psbt = self.nodes[1].walletcreatefundedpsbt(inputs=[], outputs=[{p2pkh : 1}], bip32derivs=True)
|
||||||
self.nodes[0].decodepsbt(psbt['psbt'])
|
self.nodes[0].decodepsbt(psbt['psbt'])
|
||||||
|
|
||||||
# Test decoding error: invalid base64
|
# Test decoding error: invalid base64
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ class WalletTest(BitcoinTestFramework):
|
|||||||
assert_equal(self.nodes[0].getbalance("*"), 50)
|
assert_equal(self.nodes[0].getbalance("*"), 50)
|
||||||
assert_equal(self.nodes[0].getbalance("*", 1), 50)
|
assert_equal(self.nodes[0].getbalance("*", 1), 50)
|
||||||
assert_equal(self.nodes[0].getbalance(minconf=1), 50)
|
assert_equal(self.nodes[0].getbalance(minconf=1), 50)
|
||||||
assert_equal(self.nodes[0].getbalance(minconf=0, include_watchonly=True), 50)
|
assert_equal(self.nodes[0].getbalance(minconf=0), 50)
|
||||||
assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
|
assert_equal(self.nodes[0].getbalance("*", 1, True), 50)
|
||||||
assert_equal(self.nodes[1].getbalance(minconf=0, include_watchonly=True), 50)
|
assert_equal(self.nodes[1].getbalance(minconf=0), 50)
|
||||||
|
|
||||||
# Send 40 BTC from 0 to 1 and 60 BTC from 1 to 0.
|
# Send 40 BTC from 0 to 1 and 60 BTC from 1 to 0.
|
||||||
txs = create_transactions(self.nodes[0], self.nodes[1].getnewaddress(), 40, [Decimal('0.01')])
|
txs = create_transactions(self.nodes[0], self.nodes[1].getnewaddress(), 40, [Decimal('0.01')])
|
||||||
@@ -145,14 +145,10 @@ class WalletTest(BitcoinTestFramework):
|
|||||||
# getbalances
|
# getbalances
|
||||||
expected_balances_0 = {'mine': {'immature': Decimal('0E-8'),
|
expected_balances_0 = {'mine': {'immature': Decimal('0E-8'),
|
||||||
'trusted': Decimal('9.99'), # change from node 0's send
|
'trusted': Decimal('9.99'), # change from node 0's send
|
||||||
'untrusted_pending': Decimal('60.0')},
|
'untrusted_pending': Decimal('60.0')}}
|
||||||
'watchonly': {'immature': Decimal('5000'),
|
|
||||||
'trusted': Decimal('50.0'),
|
|
||||||
'untrusted_pending': Decimal('0E-8')}}
|
|
||||||
expected_balances_1 = {'mine': {'immature': Decimal('0E-8'),
|
expected_balances_1 = {'mine': {'immature': Decimal('0E-8'),
|
||||||
'trusted': Decimal('0E-8'), # node 1's send had an unsafe input
|
'trusted': Decimal('0E-8'), # node 1's send had an unsafe input
|
||||||
'untrusted_pending': Decimal('30.0') - fee_node_1}} # Doesn't include output of node 0's send since it was spent
|
'untrusted_pending': Decimal('30.0') - fee_node_1}} # Doesn't include output of node 0's send since it was spent
|
||||||
del expected_balances_0["watchonly"]
|
|
||||||
balances_0 = self.nodes[0].getbalances()
|
balances_0 = self.nodes[0].getbalances()
|
||||||
balances_1 = self.nodes[1].getbalances()
|
balances_1 = self.nodes[1].getbalances()
|
||||||
# remove lastprocessedblock keys (they will be tested later)
|
# remove lastprocessedblock keys (they will be tested later)
|
||||||
|
|||||||
@@ -532,7 +532,6 @@ class WalletTest(BitcoinTestFramework):
|
|||||||
assert_equal(address_info['address'], "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ")
|
assert_equal(address_info['address'], "mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ")
|
||||||
assert_equal(address_info["scriptPubKey"], "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac")
|
assert_equal(address_info["scriptPubKey"], "76a9144e3854046c7bd1594ac904e4793b6a45b36dea0988ac")
|
||||||
assert not address_info["ismine"]
|
assert not address_info["ismine"]
|
||||||
assert not address_info["iswatchonly"]
|
|
||||||
assert not address_info["isscript"]
|
assert not address_info["isscript"]
|
||||||
assert not address_info["ischange"]
|
assert not address_info["ischange"]
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
self.test_feerate_rounding()
|
self.test_feerate_rounding()
|
||||||
self.test_input_confs_control()
|
self.test_input_confs_control()
|
||||||
self.test_duplicate_outputs()
|
self.test_duplicate_outputs()
|
||||||
|
self.test_watchonly_cannot_grind_r()
|
||||||
|
|
||||||
def test_duplicate_outputs(self):
|
def test_duplicate_outputs(self):
|
||||||
self.log.info("Test deserializing and funding a transaction with duplicate outputs")
|
self.log.info("Test deserializing and funding a transaction with duplicate outputs")
|
||||||
@@ -781,7 +782,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
self.nodes[3].loadwallet('wwatch')
|
self.nodes[3].loadwallet('wwatch')
|
||||||
wwatch = self.nodes[3].get_wallet_rpc('wwatch')
|
wwatch = self.nodes[3].get_wallet_rpc('wwatch')
|
||||||
w3 = self.nodes[3].get_wallet_rpc(self.default_wallet_name)
|
w3 = self.nodes[3].get_wallet_rpc(self.default_wallet_name)
|
||||||
result = wwatch.fundrawtransaction(rawtx, includeWatching=True, changeAddress=w3.getrawchangeaddress(), subtractFeeFromOutputs=[0])
|
result = wwatch.fundrawtransaction(rawtx, changeAddress=w3.getrawchangeaddress(), subtractFeeFromOutputs=[0])
|
||||||
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
|
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
|
||||||
assert_equal(len(res_dec["vin"]), 1)
|
assert_equal(len(res_dec["vin"]), 1)
|
||||||
assert res_dec["vin"][0]["txid"] == self.watchonly_utxo['txid']
|
assert res_dec["vin"][0]["txid"] == self.watchonly_utxo['txid']
|
||||||
@@ -1517,5 +1518,28 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
wallet.unloadwallet()
|
wallet.unloadwallet()
|
||||||
|
|
||||||
|
def test_watchonly_cannot_grind_r(self):
|
||||||
|
self.log.info("Test that a watchonly wallet will estimate higher fees for a tx than the wallet with private keys")
|
||||||
|
self.nodes[0].createwallet("grind")
|
||||||
|
wallet = self.nodes[0].get_wallet_rpc("grind")
|
||||||
|
default_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
|
||||||
|
|
||||||
|
self.nodes[0].createwallet(wallet_name="grind_watchonly", disable_private_keys=True)
|
||||||
|
watchonly = self.nodes[0].get_wallet_rpc("grind_watchonly")
|
||||||
|
assert_equal(watchonly.importdescriptors(wallet.listdescriptors()["descriptors"])[0]["success"], True)
|
||||||
|
|
||||||
|
# Send to legacy address type so that we will have an ecdsa signature with a measurable effect on the feerate
|
||||||
|
default_wallet.sendtoaddress(wallet.getnewaddress(address_type="legacy"), 10)
|
||||||
|
self.generate(self.nodes[0], 1)
|
||||||
|
|
||||||
|
assert_equal(wallet.listunspent(), watchonly.listunspent())
|
||||||
|
|
||||||
|
ret_addr = default_wallet.getnewaddress()
|
||||||
|
tx = wallet.createrawtransaction([], [{ret_addr: 5}])
|
||||||
|
funded = wallet.fundrawtransaction(hexstring=tx, fee_rate=10)
|
||||||
|
|
||||||
|
watchonly_funded = watchonly.fundrawtransaction(hexstring=tx, fee_rate=10)
|
||||||
|
assert_greater_than(watchonly_funded["fee"], funded["fee"])
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
RawTransactionsTest(__file__).main()
|
RawTransactionsTest(__file__).main()
|
||||||
@@ -52,15 +52,12 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# Address Test - before import
|
# Address Test - before import
|
||||||
address_info = self.nodes[1].getaddressinfo(address1)
|
address_info = self.nodes[1].getaddressinfo(address1)
|
||||||
assert_equal(address_info['iswatchonly'], False)
|
|
||||||
assert_equal(address_info['ismine'], False)
|
assert_equal(address_info['ismine'], False)
|
||||||
|
|
||||||
address_info = self.nodes[1].getaddressinfo(address2)
|
address_info = self.nodes[1].getaddressinfo(address2)
|
||||||
assert_equal(address_info['iswatchonly'], False)
|
|
||||||
assert_equal(address_info['ismine'], False)
|
assert_equal(address_info['ismine'], False)
|
||||||
|
|
||||||
address_info = self.nodes[1].getaddressinfo(address3)
|
address_info = self.nodes[1].getaddressinfo(address3)
|
||||||
assert_equal(address_info['iswatchonly'], False)
|
|
||||||
assert_equal(address_info['ismine'], False)
|
assert_equal(address_info['ismine'], False)
|
||||||
|
|
||||||
# Send funds to self
|
# Send funds to self
|
||||||
@@ -92,7 +89,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
|
|||||||
wwatch = self.nodes[1].get_wallet_rpc('wwatch')
|
wwatch = self.nodes[1].get_wallet_rpc('wwatch')
|
||||||
wwatch.importdescriptors([{"desc": self.nodes[0].getaddressinfo(address2)["desc"], "timestamp": "now"}])
|
wwatch.importdescriptors([{"desc": self.nodes[0].getaddressinfo(address2)["desc"], "timestamp": "now"}])
|
||||||
wwatch.importprunedfunds(rawtransaction=rawtxn2, txoutproof=proof2)
|
wwatch.importprunedfunds(rawtransaction=rawtxn2, txoutproof=proof2)
|
||||||
assert [tx for tx in wwatch.listtransactions(include_watchonly=True) if tx['txid'] == txnid2]
|
assert [tx for tx in wwatch.listtransactions() if tx['txid'] == txnid2]
|
||||||
|
|
||||||
# Import with private key with no rescan
|
# Import with private key with no rescan
|
||||||
w1 = self.nodes[1].get_wallet_rpc(self.default_wallet_name)
|
w1 = self.nodes[1].get_wallet_rpc(self.default_wallet_name)
|
||||||
@@ -104,24 +101,21 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# Addresses Test - after import
|
# Addresses Test - after import
|
||||||
address_info = w1.getaddressinfo(address1)
|
address_info = w1.getaddressinfo(address1)
|
||||||
assert_equal(address_info['iswatchonly'], False)
|
|
||||||
assert_equal(address_info['ismine'], False)
|
assert_equal(address_info['ismine'], False)
|
||||||
address_info = wwatch.getaddressinfo(address2)
|
address_info = wwatch.getaddressinfo(address2)
|
||||||
assert_equal(address_info['iswatchonly'], False)
|
|
||||||
assert_equal(address_info['ismine'], True)
|
assert_equal(address_info['ismine'], True)
|
||||||
address_info = w1.getaddressinfo(address3)
|
address_info = w1.getaddressinfo(address3)
|
||||||
assert_equal(address_info['iswatchonly'], False)
|
|
||||||
assert_equal(address_info['ismine'], True)
|
assert_equal(address_info['ismine'], True)
|
||||||
|
|
||||||
# Remove transactions
|
# Remove transactions
|
||||||
assert_raises_rpc_error(-4, f'Transaction {txnid1} does not belong to this wallet', w1.removeprunedfunds, txnid1)
|
assert_raises_rpc_error(-4, f'Transaction {txnid1} does not belong to this wallet', w1.removeprunedfunds, txnid1)
|
||||||
assert not [tx for tx in w1.listtransactions(include_watchonly=True) if tx['txid'] == txnid1]
|
assert not [tx for tx in w1.listtransactions() if tx['txid'] == txnid1]
|
||||||
|
|
||||||
wwatch.removeprunedfunds(txnid2)
|
wwatch.removeprunedfunds(txnid2)
|
||||||
assert not [tx for tx in wwatch.listtransactions(include_watchonly=True) if tx['txid'] == txnid2]
|
assert not [tx for tx in wwatch.listtransactions() if tx['txid'] == txnid2]
|
||||||
|
|
||||||
w1.removeprunedfunds(txnid3)
|
w1.removeprunedfunds(txnid3)
|
||||||
assert not [tx for tx in w1.listtransactions(include_watchonly=True) if tx['txid'] == txnid3]
|
assert not [tx for tx in w1.listtransactions() if tx['txid'] == txnid3]
|
||||||
|
|
||||||
# Check various RPC parameter validation errors
|
# Check various RPC parameter validation errors
|
||||||
assert_raises_rpc_error(-22, "TX decode failed", w1.importprunedfunds, b'invalid tx'.hex(), proof1)
|
assert_raises_rpc_error(-22, "TX decode failed", w1.importprunedfunds, b'invalid tx'.hex(), proof1)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class ReceivedByTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
# save the number of coinbase reward addresses so far
|
# save the number of coinbase reward addresses so far
|
||||||
num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True))
|
num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True))
|
||||||
|
|
||||||
self.log.info("listreceivedbyaddress Test")
|
self.log.info("listreceivedbyaddress Test")
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ class ReceivedByTest(BitcoinTestFramework):
|
|||||||
# Test Address filtering
|
# Test Address filtering
|
||||||
# Only on addr
|
# Only on addr
|
||||||
expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]}
|
expected = {"address": addr, "label": "", "amount": Decimal("0.1"), "confirmations": 10, "txids": [txid, ]}
|
||||||
res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True, address_filter=addr)
|
res = self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, address_filter=addr)
|
||||||
assert_array_result(res, {"address": addr}, expected)
|
assert_array_result(res, {"address": addr}, expected)
|
||||||
assert_equal(len(res), 1)
|
assert_equal(len(res), 1)
|
||||||
# Test for regression on CLI calls with address string (#14173)
|
# Test for regression on CLI calls with address string (#14173)
|
||||||
@@ -75,7 +75,7 @@ class ReceivedByTest(BitcoinTestFramework):
|
|||||||
assert_array_result(cli_res, {"address": addr}, expected)
|
assert_array_result(cli_res, {"address": addr}, expected)
|
||||||
assert_equal(len(cli_res), 1)
|
assert_equal(len(cli_res), 1)
|
||||||
# Error on invalid address
|
# Error on invalid address
|
||||||
assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling")
|
assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, address_filter="bamboozling")
|
||||||
# Another address receive money
|
# Another address receive money
|
||||||
res = self.nodes[1].listreceivedbyaddress(0, True, True)
|
res = self.nodes[1].listreceivedbyaddress(0, True, True)
|
||||||
assert_equal(len(res), 2 + num_cb_reward_addresses) # Right now 2 entries
|
assert_equal(len(res), 2 + num_cb_reward_addresses) # Right now 2 entries
|
||||||
|
|||||||
@@ -282,7 +282,6 @@ class WalletMigrationTest(BitcoinTestFramework):
|
|||||||
# Transaction to multisig is in multisig1_watchonly and not multisig1
|
# Transaction to multisig is in multisig1_watchonly and not multisig1
|
||||||
_, multisig1 = self.migrate_and_get_rpc("multisig1")
|
_, multisig1 = self.migrate_and_get_rpc("multisig1")
|
||||||
assert_equal(multisig1.getaddressinfo(addr1)["ismine"], False)
|
assert_equal(multisig1.getaddressinfo(addr1)["ismine"], False)
|
||||||
assert_equal(multisig1.getaddressinfo(addr1)["iswatchonly"], False)
|
|
||||||
assert_equal(multisig1.getaddressinfo(addr1)["solvable"], False)
|
assert_equal(multisig1.getaddressinfo(addr1)["solvable"], False)
|
||||||
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", multisig1.gettransaction, txid)
|
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", multisig1.gettransaction, txid)
|
||||||
assert_equal(multisig1.getbalance(), 0)
|
assert_equal(multisig1.getbalance(), 0)
|
||||||
@@ -368,7 +367,7 @@ class WalletMigrationTest(BitcoinTestFramework):
|
|||||||
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_watchonly_txid)
|
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_watchonly_txid)
|
||||||
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_sent_watchonly_utxo['txid'])
|
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, received_sent_watchonly_utxo['txid'])
|
||||||
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, sent_watchonly_txid)
|
assert_raises_rpc_error(-5, "Invalid or non-wallet transaction id", imports0.gettransaction, sent_watchonly_txid)
|
||||||
assert_equal(len(imports0.listtransactions(include_watchonly=True)), 2)
|
assert_equal(len(imports0.listtransactions()), 2)
|
||||||
imports0.gettransaction(received_txid)
|
imports0.gettransaction(received_txid)
|
||||||
imports0.gettransaction(watchonly_spendable_txid)
|
imports0.gettransaction(watchonly_spendable_txid)
|
||||||
assert_equal(imports0.getbalance(), spendable_bal)
|
assert_equal(imports0.getbalance(), spendable_bal)
|
||||||
@@ -869,18 +868,14 @@ class WalletMigrationTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# Both addresses should only appear in the watchonly wallet
|
# Both addresses should only appear in the watchonly wallet
|
||||||
p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr)
|
p2pkh_addr_info = wallet.getaddressinfo(p2pkh_addr)
|
||||||
assert_equal(p2pkh_addr_info["iswatchonly"], False)
|
|
||||||
assert_equal(p2pkh_addr_info["ismine"], False)
|
assert_equal(p2pkh_addr_info["ismine"], False)
|
||||||
p2wpkh_addr_info = wallet.getaddressinfo(p2wpkh_addr)
|
p2wpkh_addr_info = wallet.getaddressinfo(p2wpkh_addr)
|
||||||
assert_equal(p2wpkh_addr_info["iswatchonly"], False)
|
|
||||||
assert_equal(p2wpkh_addr_info["ismine"], False)
|
assert_equal(p2wpkh_addr_info["ismine"], False)
|
||||||
|
|
||||||
watchonly_wallet = self.master_node.get_wallet_rpc(migrate_info["watchonly_name"])
|
watchonly_wallet = self.master_node.get_wallet_rpc(migrate_info["watchonly_name"])
|
||||||
watchonly_p2pkh_addr_info = watchonly_wallet.getaddressinfo(p2pkh_addr)
|
watchonly_p2pkh_addr_info = watchonly_wallet.getaddressinfo(p2pkh_addr)
|
||||||
assert_equal(watchonly_p2pkh_addr_info["iswatchonly"], False)
|
|
||||||
assert_equal(watchonly_p2pkh_addr_info["ismine"], True)
|
assert_equal(watchonly_p2pkh_addr_info["ismine"], True)
|
||||||
watchonly_p2wpkh_addr_info = watchonly_wallet.getaddressinfo(p2wpkh_addr)
|
watchonly_p2wpkh_addr_info = watchonly_wallet.getaddressinfo(p2wpkh_addr)
|
||||||
assert_equal(watchonly_p2wpkh_addr_info["iswatchonly"], False)
|
|
||||||
assert_equal(watchonly_p2wpkh_addr_info["ismine"], True)
|
assert_equal(watchonly_p2wpkh_addr_info["ismine"], True)
|
||||||
|
|
||||||
# There should only be raw or addr descriptors
|
# There should only be raw or addr descriptors
|
||||||
@@ -1113,7 +1108,6 @@ class WalletMigrationTest(BitcoinTestFramework):
|
|||||||
assert_equal(wallet.getbalances()['mine']['trusted'], 5)
|
assert_equal(wallet.getbalances()['mine']['trusted'], 5)
|
||||||
addr_info = wallet.getaddressinfo(wsh_pkh_addr)
|
addr_info = wallet.getaddressinfo(wsh_pkh_addr)
|
||||||
assert_equal(addr_info["ismine"], True)
|
assert_equal(addr_info["ismine"], True)
|
||||||
assert_equal(addr_info["iswatchonly"], False)
|
|
||||||
assert_equal(addr_info["solvable"], True)
|
assert_equal(addr_info["solvable"], True)
|
||||||
|
|
||||||
wallet.unloadwallet()
|
wallet.unloadwallet()
|
||||||
@@ -1225,7 +1219,6 @@ class WalletMigrationTest(BitcoinTestFramework):
|
|||||||
# information about the witnessScript
|
# information about the witnessScript
|
||||||
comp_wsh_pkh_addr_info = wallet.getaddressinfo(comp_wsh_pkh_addr)
|
comp_wsh_pkh_addr_info = wallet.getaddressinfo(comp_wsh_pkh_addr)
|
||||||
assert_equal(comp_wsh_pkh_addr_info["ismine"], True)
|
assert_equal(comp_wsh_pkh_addr_info["ismine"], True)
|
||||||
assert_equal(comp_wsh_pkh_addr_info["iswatchonly"], False)
|
|
||||||
assert "embedded" in comp_wsh_pkh_addr_info
|
assert "embedded" in comp_wsh_pkh_addr_info
|
||||||
|
|
||||||
# After migration, the invalid addresses should still not be detected as ismine and not watchonly.
|
# After migration, the invalid addresses should still not be detected as ismine and not watchonly.
|
||||||
@@ -1234,7 +1227,6 @@ class WalletMigrationTest(BitcoinTestFramework):
|
|||||||
for addr in invalid_addrs:
|
for addr in invalid_addrs:
|
||||||
addr_info = wallet.getaddressinfo(addr)
|
addr_info = wallet.getaddressinfo(addr)
|
||||||
assert_equal(addr_info["ismine"], False)
|
assert_equal(addr_info["ismine"], False)
|
||||||
assert_equal(addr_info["iswatchonly"], False)
|
|
||||||
assert "embedded" not in addr_info
|
assert "embedded" not in addr_info
|
||||||
|
|
||||||
wallet.unloadwallet()
|
wallet.unloadwallet()
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
arg_conf_target=None, arg_estimate_mode=None, arg_fee_rate=None,
|
arg_conf_target=None, arg_estimate_mode=None, arg_fee_rate=None,
|
||||||
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
|
conf_target=None, estimate_mode=None, fee_rate=None, add_to_wallet=None, psbt=None,
|
||||||
inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
|
inputs=None, add_inputs=None, include_unsafe=None, change_address=None, change_position=None, change_type=None,
|
||||||
include_watching=None, locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
|
locktime=None, lock_unspents=None, replaceable=None, subtract_fee_from_outputs=None,
|
||||||
expect_error=None, solving_data=None, minconf=None):
|
expect_error=None, solving_data=None, minconf=None):
|
||||||
assert_not_equal((amount is None), (data is None))
|
assert_not_equal((amount is None), (data is None))
|
||||||
|
|
||||||
@@ -91,8 +91,6 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
options["change_position"] = change_position
|
options["change_position"] = change_position
|
||||||
if change_type is not None:
|
if change_type is not None:
|
||||||
options["change_type"] = change_type
|
options["change_type"] = change_type
|
||||||
if include_watching is not None:
|
|
||||||
options["include_watching"] = include_watching
|
|
||||||
if locktime is not None:
|
if locktime is not None:
|
||||||
options["locktime"] = locktime
|
options["locktime"] = locktime
|
||||||
if lock_unspents is not None:
|
if lock_unspents is not None:
|
||||||
@@ -111,6 +109,11 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
if len(options.keys()) == 0:
|
if len(options.keys()) == 0:
|
||||||
options = None
|
options = None
|
||||||
|
|
||||||
|
expect_sign = from_wallet.getwalletinfo()["private_keys_enabled"]
|
||||||
|
expect_sign = expect_sign and solving_data is None
|
||||||
|
if inputs is not None:
|
||||||
|
expect_sign = expect_sign and all(["weight" not in i for i in inputs])
|
||||||
|
|
||||||
if expect_error is None:
|
if expect_error is None:
|
||||||
res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, fee_rate=arg_fee_rate, options=options)
|
res = from_wallet.send(outputs=outputs, conf_target=arg_conf_target, estimate_mode=arg_estimate_mode, fee_rate=arg_fee_rate, options=options)
|
||||||
else:
|
else:
|
||||||
@@ -143,7 +146,7 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
if locktime:
|
if locktime:
|
||||||
return res
|
return res
|
||||||
|
|
||||||
if from_wallet.getwalletinfo()["private_keys_enabled"] and not include_watching:
|
if expect_sign:
|
||||||
assert_equal(res["complete"], True)
|
assert_equal(res["complete"], True)
|
||||||
assert "txid" in res
|
assert "txid" in res
|
||||||
else:
|
else:
|
||||||
@@ -155,7 +158,7 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
if include_unsafe:
|
if include_unsafe:
|
||||||
from_balance += from_wallet.getbalances()["mine"]["untrusted_pending"]
|
from_balance += from_wallet.getbalances()["mine"]["untrusted_pending"]
|
||||||
|
|
||||||
if add_to_wallet and not include_watching:
|
if add_to_wallet:
|
||||||
# Ensure transaction exists in the wallet:
|
# Ensure transaction exists in the wallet:
|
||||||
tx = from_wallet.gettransaction(res["txid"])
|
tx = from_wallet.gettransaction(res["txid"])
|
||||||
assert tx
|
assert tx
|
||||||
@@ -216,17 +219,13 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
"desc": descsum_create("wpkh(" + xpub + "/0/0/*)"),
|
"desc": descsum_create("wpkh(" + xpub + "/0/0/*)"),
|
||||||
"timestamp": "now",
|
"timestamp": "now",
|
||||||
"range": [0, 100],
|
"range": [0, 100],
|
||||||
"keypool": True,
|
|
||||||
"active": True,
|
"active": True,
|
||||||
"watchonly": True
|
|
||||||
},{
|
},{
|
||||||
"desc": descsum_create("wpkh(" + xpub + "/0/1/*)"),
|
"desc": descsum_create("wpkh(" + xpub + "/0/1/*)"),
|
||||||
"timestamp": "now",
|
"timestamp": "now",
|
||||||
"range": [0, 100],
|
"range": [0, 100],
|
||||||
"keypool": True,
|
|
||||||
"active": True,
|
"active": True,
|
||||||
"internal": True,
|
"internal": True,
|
||||||
"watchonly": True
|
|
||||||
}])
|
}])
|
||||||
assert_equal(res, [{"success": True}, {"success": True}])
|
assert_equal(res, [{"success": True}, {"success": True}])
|
||||||
|
|
||||||
@@ -470,15 +469,15 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
ext_utxo = ext_fund.listunspent(addresses=[addr])[0]
|
ext_utxo = ext_fund.listunspent(addresses=[addr])[0]
|
||||||
|
|
||||||
# An external input without solving data should result in an error
|
# An external input without solving data should result in an error
|
||||||
self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, expect_error=(-4, "Not solvable pre-selected input COutPoint(%s, %s)" % (ext_utxo["txid"][0:10], ext_utxo["vout"])))
|
self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, expect_error=(-4, "Not solvable pre-selected input COutPoint(%s, %s)" % (ext_utxo["txid"][0:10], ext_utxo["vout"])))
|
||||||
|
|
||||||
# But funding should work when the solving data is provided
|
# But funding should work when the solving data is provided
|
||||||
res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"], addr_info["embedded"]["embedded"]["scriptPubKey"]]})
|
res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, solving_data={"pubkeys": [addr_info['pubkey']], "scripts": [addr_info["embedded"]["scriptPubKey"], addr_info["embedded"]["embedded"]["scriptPubKey"]]})
|
||||||
signed = ext_wallet.walletprocesspsbt(res["psbt"])
|
signed = ext_wallet.walletprocesspsbt(res["psbt"])
|
||||||
signed = ext_fund.walletprocesspsbt(res["psbt"])
|
signed = ext_fund.walletprocesspsbt(res["psbt"])
|
||||||
assert signed["complete"]
|
assert signed["complete"]
|
||||||
|
|
||||||
res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, include_watching=True, solving_data={"descriptors": [desc]})
|
res = self.test_send(from_wallet=ext_wallet, to_wallet=self.nodes[0], amount=15, inputs=[ext_utxo], add_inputs=True, psbt=True, solving_data={"descriptors": [desc]})
|
||||||
signed = ext_wallet.walletprocesspsbt(res["psbt"])
|
signed = ext_wallet.walletprocesspsbt(res["psbt"])
|
||||||
signed = ext_fund.walletprocesspsbt(res["psbt"])
|
signed = ext_fund.walletprocesspsbt(res["psbt"])
|
||||||
assert signed["complete"]
|
assert signed["complete"]
|
||||||
@@ -511,7 +510,6 @@ class WalletSendTest(BitcoinTestFramework):
|
|||||||
inputs=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": input_weight}],
|
inputs=[{"txid": ext_utxo["txid"], "vout": ext_utxo["vout"], "weight": input_weight}],
|
||||||
add_inputs=True,
|
add_inputs=True,
|
||||||
psbt=True,
|
psbt=True,
|
||||||
include_watching=True,
|
|
||||||
fee_rate=target_fee_rate_sat_vb
|
fee_rate=target_fee_rate_sat_vb
|
||||||
)
|
)
|
||||||
signed = ext_wallet.walletprocesspsbt(res["psbt"])
|
signed = ext_wallet.walletprocesspsbt(res["psbt"])
|
||||||
|
|||||||
Reference in new issue
Block a user