Skip to content

Commit fa87eb8

Browse files
author
MacroFake
committed
rpc: Move output script RPCs to separate file
Can be reviewed with --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space
1 parent 2c56404 commit fa87eb8

File tree

4 files changed

+327
-288
lines changed

4 files changed

+327
-288
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ libbitcoin_node_a_SOURCES = \
384384
rpc/mining.cpp \
385385
rpc/misc.cpp \
386386
rpc/net.cpp \
387+
rpc/output_script.cpp \
387388
rpc/rawtransaction.cpp \
388389
rpc/server.cpp \
389390
rpc/server_util.cpp \

src/rpc/misc.cpp

Lines changed: 1 addition & 284 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Distributed under the MIT software license, see the accompanying
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

6+
#include <chainparams.h>
67
#include <httpserver.h>
78
#include <index/blockfilterindex.h>
89
#include <index/coinstatsindex.h>
@@ -11,303 +12,23 @@
1112
#include <interfaces/echo.h>
1213
#include <interfaces/init.h>
1314
#include <interfaces/ipc.h>
14-
#include <key_io.h>
1515
#include <node/context.h>
16-
#include <outputtype.h>
1716
#include <rpc/server.h>
1817
#include <rpc/server_util.h>
1918
#include <rpc/util.h>
2019
#include <scheduler.h>
21-
#include <script/descriptor.h>
2220
#include <univalue.h>
2321
#include <util/check.h>
24-
#include <util/strencodings.h>
2522
#include <util/syscall_sandbox.h>
2623
#include <util/system.h>
2724

28-
#include <optional>
2925
#include <stdint.h>
30-
#include <tuple>
3126
#ifdef HAVE_MALLOC_INFO
3227
#include <malloc.h>
3328
#endif
3429

3530
using node::NodeContext;
3631

37-
static RPCHelpMan validateaddress()
38-
{
39-
return RPCHelpMan{
40-
"validateaddress",
41-
"\nReturn information about the given bitcoin address.\n",
42-
{
43-
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to validate"},
44-
},
45-
RPCResult{
46-
RPCResult::Type::OBJ, "", "",
47-
{
48-
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
49-
{RPCResult::Type::STR, "address", /*optional=*/true, "The bitcoin address validated"},
50-
{RPCResult::Type::STR_HEX, "scriptPubKey", /*optional=*/true, "The hex-encoded scriptPubKey generated by the address"},
51-
{RPCResult::Type::BOOL, "isscript", /*optional=*/true, "If the key is a script"},
52-
{RPCResult::Type::BOOL, "iswitness", /*optional=*/true, "If the address is a witness address"},
53-
{RPCResult::Type::NUM, "witness_version", /*optional=*/true, "The version number of the witness program"},
54-
{RPCResult::Type::STR_HEX, "witness_program", /*optional=*/true, "The hex value of the witness program"},
55-
{RPCResult::Type::STR, "error", /*optional=*/true, "Error message, if any"},
56-
{RPCResult::Type::ARR, "error_locations", /*optional=*/true, "Indices of likely error locations in address, if known (e.g. Bech32 errors)",
57-
{
58-
{RPCResult::Type::NUM, "index", "index of a potential error"},
59-
}},
60-
}
61-
},
62-
RPCExamples{
63-
HelpExampleCli("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"") +
64-
HelpExampleRpc("validateaddress", "\"" + EXAMPLE_ADDRESS[0] + "\"")
65-
},
66-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
67-
{
68-
std::string error_msg;
69-
std::vector<int> error_locations;
70-
CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg, &error_locations);
71-
const bool isValid = IsValidDestination(dest);
72-
CHECK_NONFATAL(isValid == error_msg.empty());
73-
74-
UniValue ret(UniValue::VOBJ);
75-
ret.pushKV("isvalid", isValid);
76-
if (isValid) {
77-
std::string currentAddress = EncodeDestination(dest);
78-
ret.pushKV("address", currentAddress);
79-
80-
CScript scriptPubKey = GetScriptForDestination(dest);
81-
ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
82-
83-
UniValue detail = DescribeAddress(dest);
84-
ret.pushKVs(detail);
85-
} else {
86-
UniValue error_indices(UniValue::VARR);
87-
for (int i : error_locations) error_indices.push_back(i);
88-
ret.pushKV("error_locations", error_indices);
89-
ret.pushKV("error", error_msg);
90-
}
91-
92-
return ret;
93-
},
94-
};
95-
}
96-
97-
static RPCHelpMan createmultisig()
98-
{
99-
return RPCHelpMan{"createmultisig",
100-
"\nCreates a multi-signature address with n signature of m keys required.\n"
101-
"It returns a json object with the address and redeemScript.\n",
102-
{
103-
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."},
104-
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The hex-encoded public keys.",
105-
{
106-
{"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "The hex-encoded public key"},
107-
}},
108-
{"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
109-
},
110-
RPCResult{
111-
RPCResult::Type::OBJ, "", "",
112-
{
113-
{RPCResult::Type::STR, "address", "The value of the new multisig address."},
114-
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
115-
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
116-
{RPCResult::Type::ARR, "warnings", /*optional=*/true, "Any warnings resulting from the creation of this multisig",
117-
{
118-
{RPCResult::Type::STR, "", ""},
119-
}},
120-
}
121-
},
122-
RPCExamples{
123-
"\nCreate a multisig address from 2 public keys\n"
124-
+ HelpExampleCli("createmultisig", "2 \"[\\\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\\\",\\\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\\\"]\"") +
125-
"\nAs a JSON-RPC call\n"
126-
+ HelpExampleRpc("createmultisig", "2, [\"03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd\",\"03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626\"]")
127-
},
128-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
129-
{
130-
int required = request.params[0].get_int();
131-
132-
// Get the public keys
133-
const UniValue& keys = request.params[1].get_array();
134-
std::vector<CPubKey> pubkeys;
135-
for (unsigned int i = 0; i < keys.size(); ++i) {
136-
if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) {
137-
pubkeys.push_back(HexToPubKey(keys[i].get_str()));
138-
} else {
139-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str()));
140-
}
141-
}
142-
143-
// Get the output type
144-
OutputType output_type = OutputType::LEGACY;
145-
if (!request.params[2].isNull()) {
146-
std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str());
147-
if (!parsed) {
148-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
149-
} else if (parsed.value() == OutputType::BECH32M) {
150-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses");
151-
}
152-
output_type = parsed.value();
153-
}
154-
155-
// Construct using pay-to-script-hash:
156-
FillableSigningProvider keystore;
157-
CScript inner;
158-
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);
159-
160-
// Make the descriptor
161-
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore);
162-
163-
UniValue result(UniValue::VOBJ);
164-
result.pushKV("address", EncodeDestination(dest));
165-
result.pushKV("redeemScript", HexStr(inner));
166-
result.pushKV("descriptor", descriptor->ToString());
167-
168-
UniValue warnings(UniValue::VARR);
169-
if (!request.params[2].isNull() && OutputTypeFromDestination(dest) != output_type) {
170-
// Only warns if the user has explicitly chosen an address type we cannot generate
171-
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
172-
}
173-
if (warnings.size()) result.pushKV("warnings", warnings);
174-
175-
return result;
176-
},
177-
};
178-
}
179-
180-
static RPCHelpMan getdescriptorinfo()
181-
{
182-
const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]0279be667ef9dcbbac55a06295Ce870b07029Bfcdb2dce28d959f2815b16f81798)";
183-
184-
return RPCHelpMan{"getdescriptorinfo",
185-
{"\nAnalyses a descriptor.\n"},
186-
{
187-
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
188-
},
189-
RPCResult{
190-
RPCResult::Type::OBJ, "", "",
191-
{
192-
{RPCResult::Type::STR, "descriptor", "The descriptor in canonical form, without private keys"},
193-
{RPCResult::Type::STR, "checksum", "The checksum for the input descriptor"},
194-
{RPCResult::Type::BOOL, "isrange", "Whether the descriptor is ranged"},
195-
{RPCResult::Type::BOOL, "issolvable", "Whether the descriptor is solvable"},
196-
{RPCResult::Type::BOOL, "hasprivatekeys", "Whether the input descriptor contained at least one private key"},
197-
}
198-
},
199-
RPCExamples{
200-
"Analyse a descriptor\n" +
201-
HelpExampleCli("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"") +
202-
HelpExampleRpc("getdescriptorinfo", "\"" + EXAMPLE_DESCRIPTOR + "\"")
203-
},
204-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
205-
{
206-
RPCTypeCheck(request.params, {UniValue::VSTR});
207-
208-
FlatSigningProvider provider;
209-
std::string error;
210-
auto desc = Parse(request.params[0].get_str(), provider, error);
211-
if (!desc) {
212-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
213-
}
214-
215-
UniValue result(UniValue::VOBJ);
216-
result.pushKV("descriptor", desc->ToString());
217-
result.pushKV("checksum", GetDescriptorChecksum(request.params[0].get_str()));
218-
result.pushKV("isrange", desc->IsRange());
219-
result.pushKV("issolvable", desc->IsSolvable());
220-
result.pushKV("hasprivatekeys", provider.keys.size() > 0);
221-
return result;
222-
},
223-
};
224-
}
225-
226-
static RPCHelpMan deriveaddresses()
227-
{
228-
const std::string EXAMPLE_DESCRIPTOR = "wpkh([d34db33f/84h/0h/0h]xpub6DJ2dNUysrn5Vt36jH2KLBT2i1auw1tTSSomg8PhqNiUtx8QX2SvC9nrHu81fT41fvDUnhMjEzQgXnQjKEu3oaqMSzhSrHMxyyoEAmUHQbY/0/*)#cjjspncu";
229-
230-
return RPCHelpMan{"deriveaddresses",
231-
{"\nDerives one or more addresses corresponding to an output descriptor.\n"
232-
"Examples of output descriptors are:\n"
233-
" pkh(<pubkey>) P2PKH outputs for the given pubkey\n"
234-
" wpkh(<pubkey>) Native segwit P2PKH outputs for the given pubkey\n"
235-
" sh(multi(<n>,<pubkey>,<pubkey>,...)) P2SH-multisig outputs for the given threshold and pubkeys\n"
236-
" raw(<hex script>) Outputs whose scriptPubKey equals the specified hex scripts\n"
237-
"\nIn the above, <pubkey> either refers to a fixed public key in hexadecimal notation, or to an xpub/xprv optionally followed by one\n"
238-
"or more path elements separated by \"/\", where \"h\" represents a hardened child key.\n"
239-
"For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n"},
240-
{
241-
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor."},
242-
{"range", RPCArg::Type::RANGE, RPCArg::Optional::OMITTED_NAMED_ARG, "If a ranged descriptor is used, this specifies the end or the range (in [begin,end] notation) to derive."},
243-
},
244-
RPCResult{
245-
RPCResult::Type::ARR, "", "",
246-
{
247-
{RPCResult::Type::STR, "address", "the derived addresses"},
248-
}
249-
},
250-
RPCExamples{
251-
"First three native segwit receive addresses\n" +
252-
HelpExampleCli("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\" \"[0,2]\"") +
253-
HelpExampleRpc("deriveaddresses", "\"" + EXAMPLE_DESCRIPTOR + "\", \"[0,2]\"")
254-
},
255-
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
256-
{
257-
RPCTypeCheck(request.params, {UniValue::VSTR, UniValueType()}); // Range argument is checked later
258-
const std::string desc_str = request.params[0].get_str();
259-
260-
int64_t range_begin = 0;
261-
int64_t range_end = 0;
262-
263-
if (request.params.size() >= 2 && !request.params[1].isNull()) {
264-
std::tie(range_begin, range_end) = ParseDescriptorRange(request.params[1]);
265-
}
266-
267-
FlatSigningProvider key_provider;
268-
std::string error;
269-
auto desc = Parse(desc_str, key_provider, error, /* require_checksum = */ true);
270-
if (!desc) {
271-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
272-
}
273-
274-
if (!desc->IsRange() && request.params.size() > 1) {
275-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range should not be specified for an un-ranged descriptor");
276-
}
277-
278-
if (desc->IsRange() && request.params.size() == 1) {
279-
throw JSONRPCError(RPC_INVALID_PARAMETER, "Range must be specified for a ranged descriptor");
280-
}
281-
282-
UniValue addresses(UniValue::VARR);
283-
284-
for (int i = range_begin; i <= range_end; ++i) {
285-
FlatSigningProvider provider;
286-
std::vector<CScript> scripts;
287-
if (!desc->Expand(i, key_provider, scripts, provider)) {
288-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot derive script without private keys");
289-
}
290-
291-
for (const CScript &script : scripts) {
292-
CTxDestination dest;
293-
if (!ExtractDestination(script, dest)) {
294-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Descriptor does not have a corresponding address");
295-
}
296-
297-
addresses.push_back(EncodeDestination(dest));
298-
}
299-
}
300-
301-
// This should not be possible, but an assert seems overkill:
302-
if (addresses.empty()) {
303-
throw JSONRPCError(RPC_MISC_ERROR, "Unexpected empty result");
304-
}
305-
306-
return addresses;
307-
},
308-
};
309-
}
310-
31132
static RPCHelpMan setmocktime()
31233
{
31334
return RPCHelpMan{"setmocktime",
@@ -703,10 +424,6 @@ void RegisterMiscRPCCommands(CRPCTable& t)
703424
static const CRPCCommand commands[]{
704425
{"control", &getmemoryinfo},
705426
{"control", &logging},
706-
{"util", &validateaddress},
707-
{"util", &createmultisig},
708-
{"util", &deriveaddresses},
709-
{"util", &getdescriptorinfo},
710427
{"util", &getindexinfo},
711428
{"hidden", &setmocktime},
712429
{"hidden", &mockscheduler},

0 commit comments

Comments
 (0)