@@ -808,29 +808,24 @@ UniValue dumpwallet(const JSONRPCRequest& request)
808808static UniValue ProcessImport (CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
809809{
810810 try {
811- bool success = false ;
812-
813- // Required fields.
811+ // First ensure scriptPubKey has either a script or JSON with "address" string
814812 const UniValue& scriptPubKey = data[" scriptPubKey" ];
815-
816- // Should have script or JSON with "address".
817- if (!(scriptPubKey.getType () == UniValue::VOBJ && scriptPubKey.exists (" address" )) && !(scriptPubKey.getType () == UniValue::VSTR)) {
818- throw JSONRPCError (RPC_INVALID_PARAMETER, " Invalid scriptPubKey" );
813+ bool isScript = scriptPubKey.getType () == UniValue::VSTR;
814+ if (!isScript && !(scriptPubKey.getType () == UniValue::VOBJ && scriptPubKey.exists (" address" ))) {
815+ throw JSONRPCError (RPC_INVALID_PARAMETER, " scriptPubKey must be string with script or JSON with address string" );
819816 }
817+ const std::string& output = isScript ? scriptPubKey.get_str () : scriptPubKey[" address" ].get_str ();
820818
821819 // Optional fields.
822820 const std::string& strRedeemScript = data.exists (" redeemscript" ) ? data[" redeemscript" ].get_str () : " " ;
821+ const std::string& witness_script_hex = data.exists (" witnessscript" ) ? data[" witnessscript" ].get_str () : " " ;
823822 const UniValue& pubKeys = data.exists (" pubkeys" ) ? data[" pubkeys" ].get_array () : UniValue ();
824823 const UniValue& keys = data.exists (" keys" ) ? data[" keys" ].get_array () : UniValue ();
825824 const bool internal = data.exists (" internal" ) ? data[" internal" ].get_bool () : false ;
826825 const bool watchOnly = data.exists (" watchonly" ) ? data[" watchonly" ].get_bool () : false ;
827- const std::string& label = data.exists (" label" ) && !internal ? data[" label" ].get_str () : " " ;
828-
829- bool isScript = scriptPubKey.getType () == UniValue::VSTR;
830- bool isP2SH = strRedeemScript.length () > 0 ;
831- const std::string& output = isScript ? scriptPubKey.get_str () : scriptPubKey[" address" ].get_str ();
826+ const std::string& label = data.exists (" label" ) ? data[" label" ].get_str () : " " ;
832827
833- // Parse the output.
828+ // Generate the script and destination for the scriptPubKey provided
834829 CScript script;
835830 CTxDestination dest;
836831
@@ -854,35 +849,38 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
854849
855850 // Watchonly and private keys
856851 if (watchOnly && keys.size ()) {
857- throw JSONRPCError (RPC_INVALID_PARAMETER, " Incompatibility found between watchonly and keys" );
852+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Watch-only addresses should not include private keys" );
858853 }
859854
860- // Internal + Label
855+ // Internal addresses should not have a label
861856 if (internal && data.exists (" label" )) {
862- throw JSONRPCError (RPC_INVALID_PARAMETER, " Incompatibility found between internal and label" );
857+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Internal addresses should not have a label" );
863858 }
864859
865- // Keys / PubKeys size check.
866- if (!isP2SH && (keys. size () > 1 || pubKeys. size () > 1 )) { // Address / scriptPubKey
867- throw JSONRPCError (RPC_INVALID_PARAMETER, " More than private key given for one address " );
860+ // Force users to provide the witness script in its field rather than redeemscript
861+ if (!strRedeemScript. empty () && script. IsPayToWitnessScriptHash ()) {
862+ throw JSONRPCError (RPC_INVALID_PARAMETER, " P2WSH addresses have an empty redeemscript. Please provide the witnessscript instead. " );
868863 }
869864
870- // Invalid P2SH redeemScript
871- if (isP2SH && !IsHex (strRedeemScript)) {
872- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid redeem script" );
873- }
874-
875- // Process. //
865+ CScript scriptpubkey_script = script;
866+ CTxDestination scriptpubkey_dest = dest;
867+ bool allow_p2wpkh = true ;
876868
877869 // P2SH
878- if (isP2SH) {
870+ if (!strRedeemScript.empty () && script.IsPayToScriptHash ()) {
871+ // Check the redeemScript is valid
872+ if (!IsHex (strRedeemScript)) {
873+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid redeem script: must be hex string" );
874+ }
875+
879876 // Import redeem script.
880877 std::vector<unsigned char > vData (ParseHex (strRedeemScript));
881878 CScript redeemScript = CScript (vData.begin (), vData.end ());
879+ CScriptID redeem_id (redeemScript);
882880
883- // Invalid P2SH address
884- if (!script. IsPayToScriptHash () ) {
885- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid P2SH address / script " );
881+ // Check that the redeemScript and scriptPubKey match
882+ if (GetScriptForDestination (redeem_id) != script ) {
883+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " The redeemScript does not match the scriptPubKey " );
886884 }
887885
888886 pwallet->MarkDirty ();
@@ -891,103 +889,83 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
891889 throw JSONRPCError (RPC_WALLET_ERROR, " Error adding address to wallet" );
892890 }
893891
894- CScriptID redeem_id (redeemScript);
895892 if (!pwallet->HaveCScript (redeem_id) && !pwallet->AddCScript (redeemScript)) {
896893 throw JSONRPCError (RPC_WALLET_ERROR, " Error adding p2sh redeemScript to wallet" );
897894 }
898895
899- CScript redeemDestination = GetScriptForDestination (redeem_id);
896+ // Now set script to the redeemScript so we parse the inner script as P2WSH or P2WPKH below
897+ script = redeemScript;
898+ ExtractDestination (script, dest);
899+ }
900900
901- if (::IsMine (*pwallet, redeemDestination) == ISMINE_SPENDABLE) {
902- throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script" );
901+ // (P2SH-)P2WSH
902+ if (!witness_script_hex.empty () && script.IsPayToWitnessScriptHash ()) {
903+ if (!IsHex (witness_script_hex)) {
904+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid witness script: must be hex string" );
903905 }
904906
905- pwallet->MarkDirty ();
907+ // Generate the scripts
908+ std::vector<unsigned char > witness_script_parsed (ParseHex (witness_script_hex));
909+ CScript witness_script = CScript (witness_script_parsed.begin (), witness_script_parsed.end ());
910+ CScriptID witness_id (witness_script);
906911
907- if (!pwallet->AddWatchOnly (redeemDestination, timestamp)) {
908- throw JSONRPCError (RPC_WALLET_ERROR, " Error adding address to wallet" );
912+ // Check that the witnessScript and scriptPubKey match
913+ if (GetScriptForDestination (WitnessV0ScriptHash (witness_script)) != script) {
914+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " The witnessScript does not match the scriptPubKey or redeemScript" );
909915 }
910916
911- // add to address book or update label
912- if (IsValidDestination (dest )) {
913- pwallet-> SetAddressBook (dest, label, " receive " );
917+ // Add the witness script as watch only only if it is not for P2SH-P2WSH
918+ if (!scriptpubkey_script. IsPayToScriptHash () && !pwallet-> AddWatchOnly (witness_script, timestamp )) {
919+ throw JSONRPCError (RPC_WALLET_ERROR, " Error adding address to wallet " );
914920 }
915921
916- // Import private keys.
917- if (keys.size ()) {
918- for (size_t i = 0 ; i < keys.size (); i++) {
919- const std::string& privkey = keys[i].get_str ();
920-
921- CKey key = DecodeSecret (privkey);
922-
923- if (!key.IsValid ()) {
924- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid private key encoding" );
925- }
926-
927- CPubKey pubkey = key.GetPubKey ();
928- assert (key.VerifyPubKey (pubkey));
929-
930- CKeyID vchAddress = pubkey.GetID ();
931- pwallet->MarkDirty ();
932- pwallet->SetAddressBook (vchAddress, label, " receive" );
933-
934- if (pwallet->HaveKey (vchAddress)) {
935- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Already have this key" );
936- }
937-
938- pwallet->mapKeyMetadata [vchAddress].nCreateTime = timestamp;
922+ if (!pwallet->HaveCScript (witness_id) && !pwallet->AddCScript (witness_script)) {
923+ throw JSONRPCError (RPC_WALLET_ERROR, " Error adding p2wsh witnessScript to wallet" );
924+ }
939925
940- if (!pwallet->AddKeyPubKey (key, pubkey)) {
941- throw JSONRPCError (RPC_WALLET_ERROR, " Error adding key to wallet" );
942- }
926+ // Now set script to the witnessScript so we parse the inner script as P2PK or P2PKH below
927+ script = witness_script;
928+ ExtractDestination (script, dest);
929+ allow_p2wpkh = false ; // P2WPKH cannot be embedded in P2WSH
930+ }
943931
944- pwallet->UpdateTimeFirstKey (timestamp);
945- }
932+ // (P2SH-)P2PK/P2PKH/P2WPKH
933+ if (dest.type () == typeid (CKeyID) || dest.type () == typeid (WitnessV0KeyHash)) {
934+ if (!allow_p2wpkh && dest.type () == typeid (WitnessV0KeyHash)) {
935+ throw JSONRPCError (RPC_INVALID_PARAMETER, " P2WPKH cannot be embedded in P2WSH" );
946936 }
947-
948- success = true ;
949- } else {
950- // Import public keys.
951- if (pubKeys.size () && keys.size () == 0 ) {
937+ if (keys.size () > 1 || pubKeys.size () > 1 ) {
938+ throw JSONRPCError (RPC_INVALID_PARAMETER, " More than one key given for one single-key address" );
939+ }
940+ CPubKey pubkey;
941+ if (keys.size ()) {
942+ pubkey = DecodeSecret (keys[0 ].get_str ()).GetPubKey ();
943+ }
944+ if (pubKeys.size ()) {
952945 const std::string& strPubKey = pubKeys[0 ].get_str ();
953-
954946 if (!IsHex (strPubKey)) {
955947 throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Pubkey must be a hex string" );
956948 }
957-
958- std::vector<unsigned char > vData (ParseHex (strPubKey));
959- CPubKey pubKey (vData.begin (), vData.end ());
960-
961- if (!pubKey.IsFullyValid ()) {
962- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Pubkey is not a valid public key" );
963- }
964-
965- CTxDestination pubkey_dest = pubKey.GetID ();
966-
967- // Consistency check.
968- if (!(pubkey_dest == dest)) {
969- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Consistency check failed" );
970- }
971-
972- CScript pubKeyScript = GetScriptForDestination (pubkey_dest);
973-
974- if (::IsMine (*pwallet, pubKeyScript) == ISMINE_SPENDABLE) {
975- throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script" );
949+ std::vector<unsigned char > vData (ParseHex (pubKeys[0 ].get_str ()));
950+ CPubKey pubkey_temp (vData.begin (), vData.end ());
951+ if (pubkey.size () && pubkey_temp != pubkey) {
952+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Private key does not match public key for address" );
976953 }
977-
978- pwallet-> MarkDirty ();
979-
980- if (!pwallet-> AddWatchOnly (pubKeyScript, timestamp )) {
981- throw JSONRPCError (RPC_WALLET_ERROR , " Error adding address to wallet " );
954+ pubkey = pubkey_temp;
955+ }
956+ if (pubkey. size () > 0 ) {
957+ if (!pubkey. IsFullyValid ( )) {
958+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY , " Pubkey is not a valid public key " );
982959 }
983960
984- // add to address book or update label
985- if (IsValidDestination (pubkey_dest)) {
986- pwallet->SetAddressBook (pubkey_dest, label, " receive" );
961+ // Check the key corresponds to the destination given
962+ std::vector<CTxDestination> destinations = GetAllDestinationsForKey (pubkey);
963+ if (std::find (destinations.begin (), destinations.end (), dest) == destinations.end ()) {
964+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Key does not match address destination" );
987965 }
988966
989- // TODO Is this necessary?
990- CScript scriptRawPubKey = GetScriptForRawPubKey (pubKey );
967+ // This is necessary to force the wallet to import the pubKey
968+ CScript scriptRawPubKey = GetScriptForRawPubKey (pubkey );
991969
992970 if (::IsMine (*pwallet, scriptRawPubKey) == ISMINE_SPENDABLE) {
993971 throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script" );
@@ -998,73 +976,61 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
998976 if (!pwallet->AddWatchOnly (scriptRawPubKey, timestamp)) {
999977 throw JSONRPCError (RPC_WALLET_ERROR, " Error adding address to wallet" );
1000978 }
1001-
1002- success = true ;
1003979 }
980+ }
1004981
1005- // Import private keys.
1006- if (keys.size ()) {
1007- const std::string& strPrivkey = keys[0 ].get_str ();
1008-
1009- // Checks.
1010- CKey key = DecodeSecret (strPrivkey);
1011-
1012- if (!key.IsValid ()) {
1013- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid private key encoding" );
1014- }
1015-
1016- CPubKey pubKey = key.GetPubKey ();
1017- assert (key.VerifyPubKey (pubKey));
1018-
1019- CTxDestination pubkey_dest = pubKey.GetID ();
982+ // Import the address
983+ if (::IsMine (*pwallet, scriptpubkey_script) == ISMINE_SPENDABLE) {
984+ throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script" );
985+ }
1020986
1021- // Consistency check.
1022- if (!(pubkey_dest == dest)) {
1023- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Consistency check failed" );
1024- }
987+ pwallet->MarkDirty ();
1025988
1026- CKeyID vchAddress = pubKey. GetID ();
1027- pwallet-> MarkDirty ( );
1028- pwallet-> SetAddressBook (vchAddress, label, " receive " );
989+ if (!pwallet-> AddWatchOnly (scriptpubkey_script, timestamp)) {
990+ throw JSONRPCError (RPC_WALLET_ERROR, " Error adding address to wallet " );
991+ }
1029992
1030- if ( pwallet->HaveKey (vchAddress )) {
1031- throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script " );
1032- }
993+ if (!watchOnly && !pwallet-> HaveCScript ( CScriptID (scriptpubkey_script)) && ! pwallet->AddCScript (scriptpubkey_script )) {
994+ throw JSONRPCError (RPC_WALLET_ERROR, " Error adding scriptPubKey script to wallet " );
995+ }
1033996
1034- pwallet->mapKeyMetadata [vchAddress].nCreateTime = timestamp;
997+ // add to address book or update label
998+ if (IsValidDestination (scriptpubkey_dest)) {
999+ pwallet->SetAddressBook (scriptpubkey_dest, label, " receive" );
1000+ }
10351001
1036- if (!pwallet-> AddKeyPubKey (key, pubKey)) {
1037- throw JSONRPCError (RPC_WALLET_ERROR, " Error adding key to wallet " );
1038- }
1002+ // Import private keys.
1003+ for ( size_t i = 0 ; i < keys. size (); i++) {
1004+ const std::string& strPrivkey = keys[i]. get_str ();
10391005
1040- pwallet->UpdateTimeFirstKey (timestamp);
1006+ // Checks.
1007+ CKey key = DecodeSecret (strPrivkey);
10411008
1042- success = true ;
1009+ if (!key.IsValid ()) {
1010+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " Invalid private key encoding" );
10431011 }
10441012
1045- // Import scriptPubKey only.
1046- if (pubKeys.size () == 0 && keys.size () == 0 ) {
1047- if (::IsMine (*pwallet, script) == ISMINE_SPENDABLE) {
1048- throw JSONRPCError (RPC_WALLET_ERROR, " The wallet already contains the private key for this address or script" );
1049- }
1013+ CPubKey pubKey = key.GetPubKey ();
1014+ assert (key.VerifyPubKey (pubKey));
10501015
1051- pwallet->MarkDirty ();
1016+ CKeyID vchAddress = pubKey.GetID ();
1017+ pwallet->MarkDirty ();
10521018
1053- if (! pwallet->AddWatchOnly (script, timestamp )) {
1054- throw JSONRPCError (RPC_WALLET_ERROR , " Error adding address to wallet " );
1055- }
1019+ if (pwallet->HaveKey (vchAddress )) {
1020+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY , " Already have this key " );
1021+ }
10561022
1057- // add to address book or update label
1058- if (IsValidDestination (dest)) {
1059- pwallet->SetAddressBook (dest, label, " receive" );
1060- }
1023+ pwallet->mapKeyMetadata [vchAddress].nCreateTime = timestamp;
10611024
1062- success = true ;
1025+ if (!pwallet->AddKeyPubKey (key, pubKey)) {
1026+ throw JSONRPCError (RPC_WALLET_ERROR, " Error adding key to wallet" );
10631027 }
1028+
1029+ pwallet->UpdateTimeFirstKey (timestamp);
10641030 }
10651031
10661032 UniValue result = UniValue (UniValue::VOBJ);
1067- result.pushKV (" success" , UniValue (success ));
1033+ result.pushKV (" success" , UniValue (true ));
10681034 return result;
10691035 } catch (const UniValue& e) {
10701036 UniValue result = UniValue (UniValue::VOBJ);
@@ -1117,7 +1083,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
11171083 " \" now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n "
11181084 " 0 can be specified to scan the entire blockchain. Blocks up to 2 hours before the earliest key\n "
11191085 " creation time of all keys being imported by the importmulti call will be scanned.\n "
1120- " \" redeemscript\" : \" <script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH address or a P2SH scriptPubKey\n "
1086+ " \" redeemscript\" : \" <script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH or P2SH-P2WSH address/scriptPubKey\n "
1087+ " \" witnessscript\" : \" <script>\" , (string, optional) Allowed only if the scriptPubKey is a P2SH-P2WSH or P2WSH address/scriptPubKey\n "
11211088 " \" pubkeys\" : [\" <pubKey>\" , ... ] , (array, optional) Array of strings giving pubkeys that must occur in the output or redeemscript\n "
11221089 " \" keys\" : [\" <key>\" , ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n "
11231090 " \" internal\" : <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n "
0 commit comments