Merge ElementsProject/elements#1573: RPC documentation and option fixes
Some checks failed
CI / lint [focal] (push) Has been cancelled
CI / ARM [unit tests, no functional tests] [bullseye] (push) Has been cancelled
CI / Win64 [unit tests, no gui tests, no boost::process, no functional tests] [jammy] (push) Has been cancelled
CI / 32-bit + dash [gui] [Rocky 8] (push) Has been cancelled
CI / [TSan, depends, gui] [2404] (push) Has been cancelled
CI / [MSan, depends] [focal] (push) Has been cancelled
CI / [ASan + LSan + UBSan + integer, no depends] [jammy] (push) Has been cancelled
CI / [fuzzer,address,undefined,integer, no depends] [jammy] (push) Has been cancelled
CI / [no wallet] [bionic] (push) Has been cancelled
CI / macOS 10.15 [gui, no tests] [focal] (push) Has been cancelled
CI / macOS 13 native arm64 [gui, sqlite only] [no depends] (push) Has been cancelled
CI / Win64 native [msvc] (functional tests + unit tests) (push) Has been cancelled

94074cb1d3 fix RPC documentation inconsistencies (Tom Trevethan)
d59af1ef12 Merge ElementsProject/elements#1570: rpc: fix fields describtion in man (merge-script)
69c23bfe82 Merge ElementsProject/elements#1560: rpc: fix fields description in a man in 'decodepsbt' (merge-script)
cda99dbfea Merge ElementsProject/elements#1559: rpc: use null for optional parameters (merge-script)

Pull request description:

  Various fixes for RPC documentation accuracy.

  Fixes for the correct usage of `null` for optional RPC parameters.

ACKs for top commit:
  delta1:
    ACK 94074cb1d3; tested locally

Tree-SHA512: 5ef0c4f48ba2819f50c1d0fe1769509fbaac8ec2ac1e67d93f3e73557ae343cfe706534dc6f47ff249a84a7aa0e1c7f40515f0ae708157ea932087b04d2cc586
This commit is contained in:
merge-script 2026-07-21 16:30:22 +02:00
commit ce6a9d1f09
No known key found for this signature in database
GPG key ID: DE8F6EA20A661697
18 changed files with 135 additions and 56 deletions

View file

@ -1132,7 +1132,7 @@ static RPCHelpMan getblock()
{RPCResult::Type::ELISION, "", ""}
}},
}},
{RPCResult::Type::OBJ, "proposed", "Proposed parameters. Uninforced. Must be published in full",
{RPCResult::Type::OBJ, "proposed", "Proposed parameters. Unenforced. Must be published in full",
{
{RPCResult::Type::ELISION, "", "same entries as \"current\""}
}},
@ -2732,7 +2732,7 @@ static RPCHelpMan scantxoutset()
{RPCResult::Type::STR_HEX, "scriptPubKey", "The script key"},
{RPCResult::Type::STR, "desc", "A specialized descriptor for the matched scriptPubKey"},
{RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " of the unspent output"},
{RPCResult::Type::STR_HEX, "asset", "The asset ID"},
{RPCResult::Type::STR_HEX, "asset", /*optional=*/true, "The asset ID"},
{RPCResult::Type::NUM, "height", "Height of the unspent transaction output"},
}},
}},
@ -2776,7 +2776,7 @@ static RPCHelpMan scantxoutset()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
}
if (request.params.size() < 2) {
if (request.params[1].isNull()) {
throw JSONRPCError(RPC_MISC_ERROR, "scanobjects argument is required for the start action");
}
@ -3101,10 +3101,10 @@ static RPCHelpMan getsidechaininfo()
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR_HEX, "fedpegscript", "The fedpegscript from genesis block"},
{RPCResult::Type::ARR, "current_fedpegscripts", "The currently-enforced fedpegscripts in hex. Peg-ins for any entries on this list are honored by consensus and policy. Newest first. Two total entries are possible",
{{RPCResult::Type::STR_HEX, "", "active fedpegscript"}}},
{RPCResult::Type::ARR, "current_fedpeg_programs", "The currently-enforced fedpegscript scriptPubKeys in hex. Prior to a transition this may be P2SH scriptpubkey, otherwise it will be a native segwit script. Results are paired in-order with current_fedpegscripts",
{{RPCResult::Type::STR_HEX, "", "active fedpegscript scriptPubKeys"}}},
{RPCResult::Type::ARR, "current_fedpegscripts", "The currently-enforced fedpegscripts in hex. Peg-ins for any entries on this list are honored by consensus and policy. Newest first. Two total entries are possible",
{{RPCResult::Type::STR_HEX, "", "active fedpegscript"}}},
{RPCResult::Type::STR_HEX, "pegged_asset", "Pegged asset type"},
{RPCResult::Type::STR, "min_peg_diff", "The minimum difficulty parent chain header target. Peg-in headers that have less work will be rejected as an anti-Dos measure"},
{RPCResult::Type::STR_HEX, "parent_blockhash", "The parent genesis blockhash as source of pegged-in funds"},

View file

@ -572,6 +572,10 @@ static RPCHelpMan getblocktemplate()
RPCResult{"If the proposal was not accepted with mode=='proposal'", RPCResult::Type::STR, "", "According to BIP22"},
RPCResult{"Otherwise", RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::ARR, "capabilities", "",
{
{RPCResult::Type::STR, "value", "A supported feature, for example 'proposal'"},
}},
{RPCResult::Type::NUM, "version", "The preferred block version"},
{RPCResult::Type::ARR, "rules", "specific block rules that are to be enforced",
{
@ -581,10 +585,6 @@ static RPCHelpMan getblocktemplate()
{
{RPCResult::Type::NUM, "rulename", "identifies the bit number as indicating acceptance and readiness for the named softfork rule"},
}},
{RPCResult::Type::ARR, "capabilities", "",
{
{RPCResult::Type::STR, "value", "A supported feature, for example 'proposal'"},
}},
{RPCResult::Type::NUM, "vbrequired", "bit mask of versionbits the server requires set in submissions"},
{RPCResult::Type::STR, "previousblockhash", "The hash of current highest block"},
{RPCResult::Type::ARR, "transactions", "contents of non-coinbase transactions that should be included in the next block",
@ -1393,7 +1393,7 @@ static RPCHelpMan getnewblockhex()
const NodeContext& node = EnsureAnyNodeContext(request.context);
std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(chainman.ActiveChainstate(), *node.mempool, Params()).CreateNewBlock(feeDestinationScript, std::chrono::seconds(required_wait), &proposed, data_commitments.empty() ? nullptr : &data_commitments));
if (!pblocktemplate.get()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Wallet keypool empty");
throw JSONRPCError(RPC_INTERNAL_ERROR, "Block template empty");
}
{
@ -1433,7 +1433,7 @@ static RPCHelpMan combineblocksigs()
},
},
},
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "The hex-encoded witnessScript for the signblockscript"},
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded witnessScript for the signblockscript. Required for dynafed blocks; omitted for non-dynafed blocks."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@ -1514,7 +1514,7 @@ static RPCHelpMan getcompactsketch()
{"block_hex", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "Hex serialized block proposal from `getnewblockhex`."},
},
RPCResult{
RPCResult::Type::STR, "sketch", "serialized block sketch",
RPCResult::Type::STR_HEX, "sketch", "serialized block sketch",
},
RPCExamples{
HelpExampleCli("getcompactsketch", ""),

View file

@ -310,7 +310,7 @@ static RPCHelpMan deriveaddresses()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
}
if (!desc->IsRange() && request.params.size() > 1) {
if (!desc->IsRange() && !request.params[1].isNull()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
}

View file

@ -171,7 +171,7 @@ static RPCHelpMan getrawtransaction()
},
{
RPCResult{"if verbose is not set or set to false",
RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'"
RPCResult::Type::STR_HEX, "data", "The serialized, hex-encoded data for 'txid'"
},
RPCResult{"if verbose is set to true",
RPCResult::Type::OBJ, "", "",
@ -812,7 +812,7 @@ static RPCHelpMan combinerawtransaction()
},
},
RPCResult{
RPCResult::Type::STR, "", "The hex-encoded raw transaction with signature(s)"
RPCResult::Type::STR_HEX, "", "The hex-encoded raw transaction with signature(s)"
},
RPCExamples{
HelpExampleCli("combinerawtransaction", R"('["myhex1", "myhex2", "myhex3"]')")
@ -893,7 +893,7 @@ static RPCHelpMan signrawtransactionwithkey()
{"hexstring", RPCArg::Type::STR, RPCArg::Optional::NO, "The transaction hex string"},
{"privkeys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The base58-encoded private keys for signing",
{
{"privatekey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "private key in base58-encoding"},
{"privatekey", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "private key in base58-encoding"},
},
},
{"prevtxs", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "The previous dependent transaction outputs",
@ -1237,11 +1237,9 @@ static RPCHelpMan decodepsbt()
{RPCResult::Type::NUM, "fallback_locktime", /*optional=*/true, "The locktime to fallback to if no inputs specify a required locktime."},
{RPCResult::Type::NUM, "input_count", /*optional=*/true, "The number of inputs in this psbt"},
{RPCResult::Type::NUM, "output_count", /*optional=*/true, "The number of outputs in this psbt."},
{RPCResult::Type::NUM, "inputs_modifiable", /*optional=*/true, "Whether inputs can be modified"},
{RPCResult::Type::NUM, "outputs_modifiable", /*optional=*/true, "Whether outputs can be modified"},
{RPCResult::Type::ARR, "sighash_single_indexes", /*optional=*/true, "The indexes which have SIGHASH_SINGLE signatures",
{{RPCResult::Type::NUM, "", "Index of an input with a SIGHASH_SINGLE signature"}},
},
{RPCResult::Type::BOOL, "inputs_modifiable", /*optional=*/true, "Whether inputs can be modified"},
{RPCResult::Type::BOOL, "outputs_modifiable", /*optional=*/true, "Whether outputs can be modified"},
{RPCResult::Type::BOOL, "has_sighash_single", /*optional=*/true, "Whether this PSBT has SIGHASH_SINGLE inputs"},
{RPCResult::Type::NUM, "psbt_version", "The PSBT version number. Not to be confused with the unsigned transaction version"},
{RPCResult::Type::OBJ_DYN, "scalar_offsets", /*optional=*/true, "The PSET scalar elements",
{
@ -2781,7 +2779,7 @@ static RPCHelpMan rawblindrawtransaction()
"Returns the hex-encoded raw transaction.\n"
"The input raw transaction cannot have already-blinded outputs.\n"
"The output keys used can be specified by using a confidential address in createrawtransaction.\n"
"If an additional blinded output is required to make a balanced blinding, a 0-value unspendable output will be added. Since there is no access to the wallet the blinding pubkey from the last output with blinding key will be repeated.\n"
"If blinded inputs exist but no output has a blinding pubkey, the caller must add another blindable output; this RPC cannot derive a wallet blinding key and will fail instead of adding a dummy output.\n"
"You can not blind issuances with this call.\n",
{
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "A hex-encoded raw transaction."},
@ -2810,7 +2808,7 @@ static RPCHelpMan rawblindrawtransaction()
{"ignoreblindfail", RPCArg::Type::BOOL, RPCArg::Default{true}, "Return a transaction even when a blinding attempt fails due to number of blinded inputs/outputs."},
},
RPCResult{
RPCResult::Type::STR, "transaction", "hex string of the transaction"
RPCResult::Type::STR_HEX, "transaction", "hex string of the transaction"
},
RPCExamples{""},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
@ -2844,7 +2842,7 @@ static RPCHelpMan rawblindrawtransaction()
}
if (inputAmounts.size() != tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter: one (potentially empty) input blind for each input must be provided");
"Invalid parameter: one (potentially empty) input amount for each input must be provided");
}
if (inputAssets.size() != tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
@ -2895,7 +2893,7 @@ static RPCHelpMan rawblindrawtransaction()
if (!IsHex(assetblind) || assetblind.length() != 32*2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of 32-byte hex-encoded strings");
if (!IsHex(asset) || asset.length() != 32*2)
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset blinds must be an array of 32-byte hex-encoded strings");
throw JSONRPCError(RPC_INVALID_PARAMETER, "input asset IDs must be an array of 32-byte hex-encoded strings");
input_blinds.push_back(uint256S(blind));
input_asset_blinds.push_back(uint256S(assetblind));

View file

@ -145,7 +145,7 @@ static RPCHelpMan help()
[&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
{
std::string strCommand;
if (jsonRequest.params.size() > 0) {
if (!jsonRequest.params[0].isNull()) {
strCommand = jsonRequest.params[0].get_str();
}
if (strCommand == "dump_all_command_conversions") {

View file

@ -1961,7 +1961,7 @@ RPCHelpMan restorewallet()
RPCHelpMan getwalletpakinfo()
{
return RPCHelpMan{"getwalletpakinfo",
"\nReturns relevant pegout authorization key (PAK) information about this wallet. Throws an error if initpegoutwallet` has not been invoked on this wallet.\n",
"\nReturns relevant pegout authorization key (PAK) information about this wallet. Throws an error if `initpegoutwallet` has not been invoked on this wallet.\n",
{},
RPCResult{
RPCResult::Type::OBJ, "", "",
@ -2153,7 +2153,7 @@ RPCHelpMan importissuanceblindingkey()
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
HelpExampleCli("importblindingkey", "\"my blinded CT address\" <blindinghex>")
HelpExampleCli("importissuanceblindingkey", "\"<txid>\" 0 \"<blindingkey>\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{

View file

@ -119,7 +119,7 @@ RPCHelpMan getreceivedbyaddress()
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6") +
"\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
+ HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 true") +
+ HelpExampleCli("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 6 \"\" true") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbyaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 6")
},
@ -177,9 +177,9 @@ RPCHelpMan getreceivedbylabel()
"\nThe amount with at least 6 confirmations\n"
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6") +
"\nThe amount with at least 6 confirmations including immature coinbase outputs\n"
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 true") +
+ HelpExampleCli("getreceivedbylabel", "\"tabby\" 6 \"\" true") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6, true")
+ HelpExampleRpc("getreceivedbylabel", "\"tabby\", 6, \"\", true")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@ -614,14 +614,14 @@ RPCHelpMan listunspent()
{RPCResult::Type::STR, "scriptPubKey", "the script key"},
{RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
{RPCResult::Type::STR_HEX, "amountcommitment", /*optional=*/true, "the transaction output commitment in hex"},
{RPCResult::Type::STR_HEX, "asset", "the transaction output asset in hex"},
{RPCResult::Type::STR_HEX, "asset", /*optional=*/true, "the transaction output asset in hex"},
{RPCResult::Type::STR_HEX, "assetcommitment", /*optional=*/true, "the transaction output asset commitment in hex"},
{RPCResult::Type::STR_HEX, "amountblinder", "the transaction output amount blinding factor in hex"},
{RPCResult::Type::STR_HEX, "assetblinder", "the transaction output asset blinding factor in hex"},
{RPCResult::Type::STR_HEX, "amountblinder", /*optional=*/true, "the transaction output amount blinding factor in hex"},
{RPCResult::Type::STR_HEX, "assetblinder", /*optional=*/true, "the transaction output asset blinding factor in hex"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
{RPCResult::Type::NUM, "ancestorcount", /*optional=*/true, "The number of in-mempool ancestor transactions, including this one (if transaction is in the mempool)"},
{RPCResult::Type::NUM, "ancestorsize", /*optional=*/true, "The virtual transaction size of in-mempool ancestors, including this one (if transaction is in the mempool)"},
{RPCResult::Type::STR_AMOUNT, "ancestorfees", /*optional=*/true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
{RPCResult::Type::NUM, "ancestorfees", /*optional=*/true, "The total fees of in-mempool ancestors (including this one) with fee deltas used for mining priority in " + CURRENCY_ATOM + " (if transaction is in the mempool)"},
{RPCResult::Type::STR_HEX, "redeemScript", /*optional=*/true, "The redeemScript if scriptPubKey is P2SH"},
{RPCResult::Type::STR, "witnessScript", /*optional=*/true, "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
{RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},

View file

@ -330,7 +330,7 @@ RPCHelpMan initpegoutwallet()
// Generate a new key that is added to wallet or set from argument
CPubKey online_pubkey;
if (request.params.size() < 3) {
if (request.params[2].isNull()) {
std::string error;
if (!pwallet->GetOnlinePakKey(online_pubkey, error)) {
throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
@ -347,7 +347,7 @@ RPCHelpMan initpegoutwallet()
// Parse offline counter
int counter = 0;
if (request.params.size() > 1) {
if (!request.params[1].isNull()) {
counter = request.params[1].get_int();
if (counter < 0 || counter > 1000000000) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "bip32_counter must be between 0 and 1,000,000,000, inclusive.");
@ -502,7 +502,7 @@ RPCHelpMan sendtomainchain_base()
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
bool subtract_fee = false;
if (request.params.size() > 2) {
if (!request.params[2].isNull()) {
subtract_fee = request.params[2].get_bool();
}
@ -523,7 +523,7 @@ RPCHelpMan sendtomainchain_base()
EnsureWalletIsUnlocked(*pwallet);
bool verbose = request.params[3].isNull() ? false: request.params[3].get_bool();
bool verbose = request.params[3].isNull() ? false : request.params[3].get_bool();
mapValue_t mapValue;
CCoinControl no_coin_control; // This is a deprecated API
return SendMoney(*pwallet, no_coin_control, recipients, std::move(mapValue), verbose, true /* ignore_blind_fail */);
@ -587,7 +587,7 @@ RPCHelpMan sendtomainchain_pak()
{
{RPCResult::Type::STR, "bitcoin_address", "destination address on Bitcoin mainchain"},
{RPCResult::Type::STR_HEX, "txid", "transaction ID of the resulting Liquid transaction"},
{RPCResult::Type::STR, "fee reason", "If verbose is set to true, the Liquid transaction fee reason"},
{RPCResult::Type::STR, "fee_reason", /*optional=*/true, "If verbose is set to true, the Liquid transaction fee reason"},
{RPCResult::Type::STR, "bitcoin_descriptor", "xpubkey of the child destination address"},
{RPCResult::Type::STR, "bip32_counter", "derivation counter for the `bitcoin_descriptor`"},
},
@ -615,7 +615,7 @@ RPCHelpMan sendtomainchain_pak()
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount for send, must send more than 0.00100000 BTC");
bool subtract_fee = false;
if (request.params.size() > 2) {
if (!request.params[2].isNull()) {
subtract_fee = request.params[2].get_bool();
}
@ -827,7 +827,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
std::vector<unsigned char> txOutProofData = ParseHex(request.params[1].get_str());
std::set<CScript> claim_scripts;
if (request.params.size() > 2) {
if (!request.params[2].isNull()) {
const std::string claim_script = request.params[2].get_str();
if (!IsHex(claim_script)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Given claim_script is not hex.");
@ -1003,7 +1003,7 @@ RPCHelpMan createrawpegin()
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "hex", "raw transaction data"},
{RPCResult::Type::STR_HEX, "hex", "raw transaction data"},
{RPCResult::Type::BOOL, "mature", "Whether the peg-in is mature (only included when validating peg-ins)"},
},
},
@ -1273,12 +1273,12 @@ RPCHelpMan blindrawtransaction()
}
bool ignore_blind_fail = true;
if (request.params.size() > 1) {
if (!request.params[1].isNull()) {
ignore_blind_fail = request.params[1].get_bool();
}
std::vector<std::vector<unsigned char> > auxiliary_generators;
if (request.params.size() > 2) {
if (!request.params[2].isNull()) {
UniValue assetCommitments = request.params[2].get_array();
if (assetCommitments.size() != 0 && assetCommitments.size() < tx.vin.size()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Asset commitment array must have at least as many entries as transaction inputs.");
@ -1542,11 +1542,11 @@ RPCHelpMan issueasset()
throw JSONRPCError(RPC_TYPE_ERROR, "Issuance must have one non-zero component");
}
bool blind_issuances = request.params.size() < 3 || request.params[2].get_bool();
bool blind_issuances = request.params[2].isNull() || request.params[2].get_bool();
// Check for optional contract to hash into definition
uint256 contract_hash;
if (request.params.size() >= 4) {
if (!request.params[3].isNull()) {
contract_hash = ParseHashV(request.params[3], "contract_hash");
}
@ -1733,7 +1733,7 @@ RPCHelpMan listissuances()
std::string assetstr;
CAsset asset_filter;
if (request.params.size() > 0) {
if (!request.params[0].isNull()) {
assetstr = request.params[0].get_str();
asset_filter = GetAssetFromString(assetstr);
}
@ -1956,7 +1956,7 @@ RPCHelpMan getpegoutkeys()
"\n(DEPRECATED) Please see `initpegoutwallet` and `sendtomainchain` for best-supported and easiest workflow. This call is for the Liquid network participants' `offline` wallet ONLY. Returns `sumkeys` corresponding to the sum of the Offline PAK and the imported Bitcoin key. The wallet must have the Offline private PAK to succeed. The output will be used in `generatepegoutproof` and `sendtomainchain`. Care is required to keep the bitcoin private key, as well as the `sumkey` safe, as a leak of both results in the leak of your `offlinekey`. Therefore it is recommended to create Bitcoin keys and do Bitcoin transaction signing directly on an offline wallet co-located with your offline Liquid wallet.\n",
{
{"btcprivkey", RPCArg::Type::STR, RPCArg::Optional::NO, "Base58 Bitcoin private key that will be combined with the offline privkey"},
{"offlinepubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "Hex pubkey of key to combine with btcprivkey. Primarily intended for integration testing."},
{"offlinepubkey", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "33-byte compressed public key encoded as 66 hex characters, to combine with btcprivkey. Primarily intended for integration testing."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
@ -1967,10 +1967,8 @@ RPCHelpMan getpegoutkeys()
},
},
RPCExamples{
HelpExampleCli("getpegoutkeys", "")
+ HelpExampleCli("getpegoutkeys", "\"5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF\" \"0389275d512326f7016e014d8625f709c01f23bd0dc16522bf9845a9ee1ef6cbf9\"")
+ HelpExampleRpc("getpegoutkeys", "")
+ HelpExampleRpc("getpegoutkeys", "\"5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF\", \"0389275d512326f7016e014d8625f709c01f23bd0dc16522bf9845a9ee1ef6cbf9\"")
HelpExampleCli("getpegoutkeys", "\"5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF\" \"0389275d512326f7016e014d8625f709c01f23bd0dc16522bf9845a9ee1ef6cbf9\"")
+ HelpExampleRpc("getpegoutkeys", "\"5Kb8kLf9zgWQnogidDA76MzPL6TsZZY36hWXMssSzNydYXYB9KF\", \"0389275d512326f7016e014d8625f709c01f23bd0dc16522bf9845a9ee1ef6cbf9\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{

View file

@ -862,7 +862,7 @@ RPCHelpMan signrawtransactionwithwallet()
{"redeemScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2SH) redeem script"},
{"witnessScript", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "(required for P2WSH or P2SH-P2WSH) witness script"},
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::OMITTED, "The amount spent (required if non-confidential segwit output)"},
{"amountcommitment", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The amount commitment spent (required if confidential segwit output)"},
{"amountcommitment", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The amount commitment spent (required if confidential segwit output)"},
},
},
},
@ -1554,7 +1554,7 @@ RPCHelpMan walletcreatefundedpsbt()
FundTxDoc()),
"options"},
{"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."},
{"psbt_version", RPCArg::Type::NUM, RPCArg::Default{2}, "The PSBT version number to use. Must be 2."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",

View file

@ -177,6 +177,39 @@ class BlockSignTest(BitcoinTestFramework):
for i in range(num_blocks):
self.mine_block(transactions)
def test_combineblocksigs_witnessscript_arg(self):
# Regression test for combineblocksigs witnessScript optionality.
node = self.nodes[0]
block = node.getnewblockhex()
is_dyna = node.getdeploymentinfo()['deployments']['dynafed']['bip9']['status'] == "active"
if is_dyna:
# no witnessScript must give RPC_INVALID_PARAMETER, not a help error from arg-count validation.
assert_raises_rpc_error(
-8,
"Signing dynamic blocks requires the witnessScript argument",
node.combineblocksigs,
block,
[],
)
# Supplying witnessScript must work normally.
result = node.combineblocksigs(block, [], self.witnessScript)
assert "hex" in result
assert "complete" in result
else:
# Non-dynafed: 2-argument call must not be rejected by arg-count validation.
try:
result = node.combineblocksigs(block, [])
assert "hex" in result
assert "complete" in result
except Exception as e:
# If exception it must not be the help string
assert "combineblocksigs" not in str(e), \
"Got help text — 2-arg call was rejected by arg-count validation"
raise
def run_test(self):
# Have every node except last import its block signing private key.
for i in range(self.num_keys):
@ -208,6 +241,10 @@ class BlockSignTest(BitcoinTestFramework):
assert_equal(info['deployments']['dynafed']['bip9']['status'], "defined")
# Test combineblocksigs witnessScript optionality pre-dynafed
self.log.info("Testing combineblocksigs witnessScript argument (pre-dynafed)")
self.test_combineblocksigs_witnessscript_arg()
# Next let's activate dynafed
blocks_til_dynafed = 431 - self.nodes[0].getblockcount()
self.log.info("Activating dynafed")
@ -218,6 +255,11 @@ class BlockSignTest(BitcoinTestFramework):
self.log.info("Mine some dynamic federation blocks without txns")
self.mine_blocks(10, False)
# Test combineblocksigs witnessScript optionality post-dynafed
self.log.info("Testing combineblocksigs witnessScript argument (post-dynafed)")
self.test_combineblocksigs_witnessscript_arg()
self.log.info("Mine some dynamic federation blocks with txns")
self.mine_blocks(10, True)

View file

@ -600,6 +600,12 @@ class FedPegTest(BitcoinTestFramework):
peg_out_txid = sidechain.sendtomainchain(some_btc_addr, 1)
self.log.info("sendtomainchain with null argument")
verbose_result = sidechain.sendtomainchain(some_btc_addr, 1, None, True)
assert isinstance(verbose_result, dict)
assert 'txid' in verbose_result
assert 'fee_reason' in verbose_result
peg_out_details = sidechain.decoderawtransaction(sidechain.getrawtransaction(peg_out_txid))
# peg-out, change, fee
assert len(peg_out_details["vout"]) == 3

View file

@ -112,12 +112,19 @@ class IssuanceTest(BitcoinTestFramework):
# Make sure test starts with no initial issuance.
assert_equal(len(self.nodes[0].listissuances()), 0)
self.log.info("listissuances with null argument")
assert_equal(self.nodes[0].listissuances(None), [])
# Unblinded issuance of asset
contract_hash = "deadbeef"*8
issued = self.nodes[0].issueasset(1, 1, False, contract_hash)
balance = self.nodes[0].getwalletinfo()["balance"]
assert_equal(balance[issued["asset"]], 1)
assert_equal(balance[issued["token"]], 1)
self.log.info("issueasset with null argument")
assert_equal(len(self.nodes[0].listissuances(None)), len(self.nodes[0].listissuances()))
# Quick unblinded reissuance check, making 2*COIN total
self.nodes[0].reissueasset(issued["asset"], 1)

View file

@ -71,6 +71,11 @@ class PAKTest (BitcoinTestFramework):
assert_equal(new_init["address_lookahead"][0], init_results[1]["address_lookahead"][2])
assert(new_init["liquid_pak"] != init_results[1]["liquid_pak"])
self.log.info("initpegoutwallet with null argument")
null_pak_init = self.nodes[2].initpegoutwallet(xpub, 5, None)
assert_equal(self.nodes[2].getwalletpakinfo()["bip32_counter"], "5")
assert null_pak_init["liquid_pak"]
# Restart and connect peers to check wallet persistence
self.stop_nodes()
self.start_nodes()
@ -187,6 +192,12 @@ class PAKTest (BitcoinTestFramework):
wpkh_stmc = self.nodes[1].sendtomainchain("", 1)
wpkh_txid = wpkh_stmc['txid']
self.log.info("sendtomainchain with null argument")
verbose_stmc = self.nodes[1].sendtomainchain("", 1, None, True)
assert isinstance(verbose_stmc, dict)
assert 'txid' in verbose_stmc
assert 'fee_reason' in verbose_stmc
# Also check some basic return fields of sendtomainchain with pak
assert_equal(wpkh_stmc["bitcoin_address"], wpkh_info["address_lookahead"][0])
validata = self.nodes[1].validateaddress(wpkh_stmc["bitcoin_address"])

View file

@ -339,6 +339,16 @@ class PeginSubsidyTest(BitcoinTestFramework):
assert_equal(len(pegin_tx["decoded"]["vout"]), 2)
self.generate(sidechain2, 1, sync_fun=sync_sidechain)
self.log.info("createrawpegin with null argument")
txid, vout, txoutproof, bitcoin_txhex, claim_script = parent_pegin(parent, sidechain2, amount=1.0, feerate=2.0)
pegintx = sidechain2.createrawpegin(bitcoin_txhex, txoutproof, None, 2.0)
signed = sidechain2.signrawtransactionwithwallet(pegintx["hex"])
assert_equal(signed["complete"], True)
pegin_txid = sidechain2.sendrawtransaction(signed["hex"])
pegin_tx = sidechain2.gettransaction(pegin_txid, True, True)
assert_equal(len(pegin_tx["decoded"]["vout"]), 2)
self.generate(sidechain2, 1, sync_fun=sync_sidechain)
self.log.info("claimpegin before enforcement, with validatepegin, below threshold")
txid, vout, txoutproof, bitcoin_txhex, claim_script = parent_pegin(parent, sidechain, amount=1.0, feerate=2.0)
pegin_txid = sidechain.claimpegin(bitcoin_txhex, txoutproof, claim_script)

View file

@ -17,6 +17,7 @@ class DeriveaddressesTest(BitcoinTestFramework):
descriptor = "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)#t6wfjs64"
address = "ert1qjqmxmkpmxt80xz4y3746zgt0q3u3ferrfpgxn5"
assert_equal(self.nodes[0].deriveaddresses(descriptor), [address])
assert_equal(self.nodes[0].deriveaddresses(descriptor, None), [address])
descriptor = descriptor[:-9]
assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, descriptor)

View file

@ -649,6 +649,10 @@ class RawTransactionsTest(BitcoinTestFramework):
rawtx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawtx)
blindedTx = self.nodes[1].blindrawtransaction(fundedTx['hex'])
assert fundedTx["changepos"] != -1
self.log.info("blindrawtransaction with null argument")
blindedTx_null_commitments = self.nodes[1].blindrawtransaction(fundedTx['hex'], None, None, False)
assert blindedTx_null_commitments
# Now we need to unlock.
self.nodes[1].walletpassphrase("test", 600)

View file

@ -94,6 +94,9 @@ class HelpRpcTest(BitcoinTestFramework):
# invalid argument
assert_raises_rpc_error(-1, 'JSON value is not a string as expected', node.help, 0)
# null argument
assert_equal(node.help(None), node.help())
# help of unknown command
assert_equal(node.help('foo'), 'help: unknown command: foo')

View file

@ -127,6 +127,5 @@ class ScantxoutsetTest(BitcoinTestFramework):
# Check that second arg is needed for start
assert_raises_rpc_error(-1, "scanobjects argument is required for the start action", self.nodes[0].scantxoutset, "start")
if __name__ == "__main__":
ScantxoutsetTest().main()