9
9
#include < merkleblock.h>
10
10
#include < rpc/server.h>
11
11
#include < rpc/util.h>
12
+ #include < script/descriptor.h>
12
13
#include < script/script.h>
13
14
#include < script/standard.h>
14
15
#include < sync.h>
@@ -984,11 +985,14 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
984
985
const bool internal = data.exists (" internal" ) ? data[" internal" ].get_bool () : false ;
985
986
const bool watchOnly = data.exists (" watchonly" ) ? data[" watchonly" ].get_bool () : false ;
986
987
988
+ if (data.exists (" range" )) {
989
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Range should not be specified for a non-descriptor import" );
990
+ }
991
+
987
992
// Generate the script and destination for the scriptPubKey provided
988
993
CScript script;
989
- CTxDestination dest;
990
994
if (!isScript) {
991
- dest = DecodeDestination (output);
995
+ CTxDestination dest = DecodeDestination (output);
992
996
if (!IsValidDestination (dest)) {
993
997
throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid address \" " + output + " \" " );
994
998
}
@@ -999,6 +1003,7 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
999
1003
}
1000
1004
std::vector<unsigned char > vData (ParseHex (output));
1001
1005
script = CScript (vData.begin (), vData.end ());
1006
+ CTxDestination dest;
1002
1007
if (!ExtractDestination (script, dest) && !internal) {
1003
1008
throw JSONRPCError (RPC_INVALID_PARAMETER, " Internal must be set to true for nonstandard scriptPubKey imports." );
1004
1009
}
@@ -1103,6 +1108,91 @@ static UniValue ProcessImportLegacy(ImportData& import_data, std::map<CKeyID, CP
1103
1108
return warnings;
1104
1109
}
1105
1110
1111
+ static UniValue ProcessImportDescriptor (ImportData& import_data, std::map<CKeyID, CPubKey>& pubkey_map, std::map<CKeyID, CKey>& privkey_map, std::set<CScript>& script_pub_keys, bool & have_solving_data, const UniValue& data)
1112
+ {
1113
+ UniValue warnings (UniValue::VARR);
1114
+
1115
+ const std::string& descriptor = data[" desc" ].get_str ();
1116
+ FlatSigningProvider keys;
1117
+ auto parsed_desc = Parse (descriptor, keys);
1118
+ if (!parsed_desc) {
1119
+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Descriptor is invalid" );
1120
+ }
1121
+
1122
+ have_solving_data = parsed_desc->IsSolvable ();
1123
+ const bool watch_only = data.exists (" watchonly" ) ? data[" watchonly" ].get_bool () : false ;
1124
+
1125
+ int64_t range_start = 0 , range_end = 0 ;
1126
+ if (!parsed_desc->IsRange () && data.exists (" range" )) {
1127
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Range should not be specified for an un-ranged descriptor" );
1128
+ } else if (parsed_desc->IsRange ()) {
1129
+ if (!data.exists (" range" )) {
1130
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Descriptor is ranged, please specify the range" );
1131
+ }
1132
+ const UniValue& range = data[" range" ];
1133
+ range_start = range.exists (" start" ) ? range[" start" ].get_int64 () : 0 ;
1134
+ if (!range.exists (" end" )) {
1135
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " End of range for descriptor must be specified" );
1136
+ }
1137
+ range_end = range[" end" ].get_int64 ();
1138
+ if (range_end < range_start || range_start < 0 ) {
1139
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid descriptor range specified" );
1140
+ }
1141
+ }
1142
+
1143
+ const UniValue& priv_keys = data.exists (" keys" ) ? data[" keys" ].get_array () : UniValue ();
1144
+
1145
+ FlatSigningProvider out_keys;
1146
+
1147
+ // Expand all descriptors to get public keys and scripts.
1148
+ // TODO: get private keys from descriptors too
1149
+ for (int i = range_start; i <= range_end; ++i) {
1150
+ std::vector<CScript> scripts_temp;
1151
+ parsed_desc->Expand (i, keys, scripts_temp, out_keys);
1152
+ std::copy (scripts_temp.begin (), scripts_temp.end (), std::inserter (script_pub_keys, script_pub_keys.end ()));
1153
+ }
1154
+
1155
+ for (const auto & x : out_keys.scripts ) {
1156
+ import_data.import_scripts .emplace (x.second );
1157
+ }
1158
+
1159
+ std::copy (out_keys.pubkeys .begin (), out_keys.pubkeys .end (), std::inserter (pubkey_map, pubkey_map.end ()));
1160
+
1161
+ for (size_t i = 0 ; i < priv_keys.size (); ++i) {
1162
+ const auto & str = priv_keys[i].get_str ();
1163
+ CKey key = DecodeSecret (str);
1164
+ if (!key.IsValid ()) {
1165
+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid private key encoding" );
1166
+ }
1167
+ CPubKey pubkey = key.GetPubKey ();
1168
+ CKeyID id = pubkey.GetID ();
1169
+
1170
+ // Check if this private key corresponds to a public key from the descriptor
1171
+ if (!pubkey_map.count (id)) {
1172
+ warnings.push_back (" Ignoring irrelevant private key." );
1173
+ } else {
1174
+ privkey_map.emplace (id, key);
1175
+ }
1176
+ }
1177
+
1178
+ // Check if all the public keys have corresponding private keys in the import for spendability.
1179
+ // This does not take into account threshold multisigs which could be spendable without all keys.
1180
+ // Thus, threshold multisigs without all keys will be considered not spendable here, even if they are,
1181
+ // perhaps triggering a false warning message. This is consistent with the current wallet IsMine check.
1182
+ bool spendable = std::all_of (pubkey_map.begin (), pubkey_map.end (),
1183
+ [&](const std::pair<CKeyID, CPubKey>& used_key) {
1184
+ return privkey_map.count (used_key.first ) > 0 ;
1185
+ });
1186
+ if (!watch_only && !spendable) {
1187
+ warnings.push_back (" Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag." );
1188
+ }
1189
+ if (watch_only && spendable) {
1190
+ warnings.push_back (" All private keys are provided, outputs will be considered spendable. If this is intentional, do not specify the watchonly flag." );
1191
+ }
1192
+
1193
+ return warnings;
1194
+ }
1195
+
1106
1196
static UniValue ProcessImport (CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1107
1197
{
1108
1198
UniValue warnings (UniValue::VARR);
@@ -1122,7 +1212,15 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
1122
1212
std::set<CScript> script_pub_keys;
1123
1213
bool have_solving_data;
1124
1214
1125
- warnings = ProcessImportLegacy (import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data);
1215
+ if (data.exists (" scriptPubKey" ) && data.exists (" desc" )) {
1216
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Both a descriptor and a scriptPubKey should not be provided." );
1217
+ } else if (data.exists (" scriptPubKey" )) {
1218
+ warnings = ProcessImportLegacy (import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data);
1219
+ } else if (data.exists (" desc" )) {
1220
+ warnings = ProcessImportDescriptor (import_data, pubkey_map, privkey_map, script_pub_keys, have_solving_data, data);
1221
+ } else {
1222
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Either a descriptor or scriptPubKey must be provided." );
1223
+ }
1126
1224
1127
1225
// If private keys are disabled, abort if private keys are being imported
1128
1226
if (pwallet->IsWalletFlagSet (WALLET_FLAG_DISABLE_PRIVATE_KEYS) && !privkey_map.empty ()) {
@@ -1132,7 +1230,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
1132
1230
// Check whether we have any work to do
1133
1231
for (const CScript& script : script_pub_keys) {
1134
1232
if (::IsMine (*pwallet, script) & ISMINE_SPENDABLE) {
1135
- throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script" );
1233
+ throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script ( \" " + HexStr (script. begin (), script. end ()) + " \" ) " );
1136
1234
}
1137
1235
}
1138
1236
@@ -1172,8 +1270,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
1172
1270
}
1173
1271
CTxDestination dest;
1174
1272
ExtractDestination (script, dest);
1175
- if (!internal) {
1176
- assert (IsValidDestination (dest));
1273
+ if (!internal && IsValidDestination (dest)) {
1177
1274
pwallet->SetAddressBook (dest, label, " receive" );
1178
1275
}
1179
1276
}
@@ -1226,7 +1323,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
1226
1323
{
1227
1324
{" " , RPCArg::Type::OBJ, /* opt */ false , /* default_val */ " " , " " ,
1228
1325
{
1229
- {" scriptPubKey" , RPCArg::Type::STR, /* opt */ false , /* default_val */ " " , " Type of scriptPubKey (string for script, json for address)" ,
1326
+ {" desc" , RPCArg::Type::STR, /* opt */ true , /* default_val */ " " , " Descriptor to import. If using descriptor, do not also provide address/scriptPubKey, scripts, or pubkeys" },
1327
+ {" scriptPubKey" , RPCArg::Type::STR, /* opt */ false , /* default_val */ " " , " Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor" ,
1230
1328
/* oneline_description */ " " , {" \" <script>\" | { \" address\" :\" <address>\" }" , " string / json" }
1231
1329
},
1232
1330
{" timestamp" , RPCArg::Type::NUM, /* opt */ false , /* default_val */ " " , " Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n "
@@ -1249,6 +1347,12 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
1249
1347
{" key" , RPCArg::Type::STR, /* opt */ false , /* default_val */ " " , " " },
1250
1348
}
1251
1349
},
1350
+ {" range" , RPCArg::Type::OBJ, /* opt */ true , /* default_val */ " " , " If a ranged descriptor is used, this specifies the start and end of the range to import" ,
1351
+ {
1352
+ {" start" , RPCArg::Type::NUM, /* opt */ true , /* default_val */ " 0" , " Start of the range to import" },
1353
+ {" end" , RPCArg::Type::NUM, /* opt */ false , /* default_val */ " " , " End of the range to import (inclusive)" },
1354
+ }
1355
+ },
1252
1356
{" internal" , RPCArg::Type::BOOL, /* opt */ true , /* default_val */ " false" , " Stating whether matching outputs should be treated as not incoming payments (also known as change)" },
1253
1357
{" watchonly" , RPCArg::Type::BOOL, /* opt */ true , /* default_val */ " false" , " Stating whether matching outputs should be considered watchonly." },
1254
1358
{" label" , RPCArg::Type::STR, /* opt */ true , /* default_val */ " ''" , " Label to assign to the address, only allowed with internal=false" },
0 commit comments