diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -95,10 +95,12 @@ CTxDestination &dest) = 0; //! Get public key. - virtual bool getPubKey(const CKeyID &address, CPubKey &pub_key) = 0; + virtual bool getPubKey(const CScript &script, const CKeyID &address, + CPubKey &pub_key) = 0; //! Get private key. - virtual bool getPrivKey(const CKeyID &address, CKey &key) = 0; + virtual bool getPrivKey(const CScript &script, const CKeyID &address, + CKey &key) = 0; //! Return whether wallet has private key. virtual bool isSpendable(const CTxDestination &dest) = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -128,12 +128,23 @@ const CChainParams &getChainParams() override { return m_wallet->chainParams; } - bool getPubKey(const CKeyID &address, CPubKey &pub_key) override { - return m_wallet->GetLegacyScriptPubKeyMan()->GetPubKey(address, - pub_key); + bool getPubKey(const CScript &script, const CKeyID &address, + CPubKey &pub_key) override { + const SigningProvider *provider = + m_wallet->GetSigningProvider(script); + if (provider) { + return provider->GetPubKey(address, pub_key); + } + return false; } - bool getPrivKey(const CKeyID &address, CKey &key) override { - return m_wallet->GetLegacyScriptPubKeyMan()->GetKey(address, key); + bool getPrivKey(const CScript &script, const CKeyID &address, + CKey &key) override { + const SigningProvider *provider = + m_wallet->GetSigningProvider(script); + if (provider) { + return provider->GetKey(address, key); + } + return false; } bool isSpendable(const CTxDestination &dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -507,7 +507,8 @@ if (ExtractDestination(out.txout.scriptPubKey, address)) { CPubKey pubkey; PKHash *pkhash = boost::get(&address); - if (pkhash && model->wallet().getPubKey(CKeyID(*pkhash), pubkey)) { + if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, + CKeyID(*pkhash), pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); } else { // in all error cases, simply assume 148 here diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -140,7 +140,8 @@ } CKey key; - if (!model->wallet().getPrivKey(CKeyID(*pkhash), key)) { + if (!model->wallet().getPrivKey(GetScriptForDestination(destination), + CKeyID(*pkhash), key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText( tr("Private key for the entered address is not available.")); diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp --- a/src/wallet/psbtwallet.cpp +++ b/src/wallet/psbtwallet.cpp @@ -43,18 +43,39 @@ return TransactionError::SIGHASH_MISMATCH; } + // Get the scriptPubKey to know which SigningProvider to use + CScript script; + if (!input.utxo.IsNull()) { + script = input.utxo.scriptPubKey; + } else { + // There's no UTXO so we can just skip this now + complete = false; + continue; + } + SignatureData sigdata; + input.FillSignatureData(sigdata); + const SigningProvider *provider = + pwallet->GetSigningProvider(script, sigdata); + if (!provider) { + complete = false; + continue; + } + complete &= - SignPSBTInput(HidingSigningProvider(pwallet->GetSigningProvider(), - !sign, !bip32derivs), + SignPSBTInput(HidingSigningProvider(provider, !sign, !bip32derivs), psbtx, i, sighash_type); } // Fill in the bip32 keypaths and redeemscripts for the outputs so that // hardware wallets can identify change for (size_t i = 0; i < psbtx.tx->vout.size(); ++i) { - UpdatePSBTOutput(HidingSigningProvider(pwallet->GetSigningProvider(), - true, !bip32derivs), - psbtx, i); + const CTxOut &out = psbtx.tx->vout.at(i); + const SigningProvider *provider = + pwallet->GetSigningProvider(out.scriptPubKey); + if (provider) { + UpdatePSBTOutput( + HidingSigningProvider(provider, true, !bip32derivs), psbtx, i); + } } return TransactionError::OK; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -622,7 +622,12 @@ throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } - const SigningProvider *provider = pwallet->GetSigningProvider(); + CScript script_pub_key = GetScriptForDestination(*pkhash); + const SigningProvider *provider = + pwallet->GetSigningProvider(script_pub_key); + if (!provider) { + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + } CKey key; CKeyID keyID(*pkhash); @@ -3519,14 +3524,18 @@ entry.pushKV("label", i->second.name); } - const SigningProvider *provider = pwallet->GetSigningProvider(); - if (scriptPubKey.IsPayToScriptHash()) { - const CScriptID &hash = - CScriptID(boost::get(address)); - CScript redeemScript; - if (provider->GetCScript(hash, redeemScript)) { - entry.pushKV("redeemScript", HexStr(redeemScript.begin(), - redeemScript.end())); + const SigningProvider *provider = + pwallet->GetSigningProvider(scriptPubKey); + if (provider) { + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID &hash = + CScriptID(boost::get(address)); + CScript redeemScript; + if (provider->GetCScript(hash, redeemScript)) { + entry.pushKV( + "redeemScript", + HexStr(redeemScript.begin(), redeemScript.end())); + } } } } @@ -3538,9 +3547,12 @@ entry.pushKV("spendable", out.fSpendable); entry.pushKV("solvable", out.fSolvable); if (out.fSolvable) { - auto descriptor = InferDescriptor( - scriptPubKey, *pwallet->GetLegacyScriptPubKeyMan()); - entry.pushKV("desc", descriptor->ToString()); + const SigningProvider *provider = + pwallet->GetSigningProvider(scriptPubKey); + if (provider) { + auto descriptor = InferDescriptor(scriptPubKey, *provider); + entry.pushKV("desc", descriptor->ToString()); + } } if (avoid_reuse) { entry.pushKV("reused", reused); @@ -3894,9 +3906,24 @@ // Parse the prevtxs array ParsePrevouts(request.params[1], nullptr, coins); + std::set providers; + for (const std::pair coin_pair : coins) { + const SigningProvider *provider = pwallet->GetSigningProvider( + coin_pair.second.GetTxOut().scriptPubKey); + if (provider) { + providers.insert(std::move(provider)); + } + } + if (providers.size() == 0) { + // When there are no available providers, use DUMMY_SIGNING_PROVIDER so + // we can check if the tx is complete + providers.insert(&DUMMY_SIGNING_PROVIDER); + } + UniValue result(UniValue::VOBJ); - SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, - request.params[2], result); + for (const SigningProvider *provider : providers) { + SignTransaction(mtx, provider, coins, request.params[2], result); + } return result; } @@ -4085,9 +4112,10 @@ const CTxDestination &dest) { UniValue ret(UniValue::VOBJ); UniValue detail = DescribeAddress(dest); + CScript script = GetScriptForDestination(dest); const SigningProvider *provider = nullptr; if (pwallet) { - provider = pwallet->GetSigningProvider(); + provider = pwallet->GetSigningProvider(script); } ret.pushKVs(detail); ret.pushKVs( @@ -4215,11 +4243,11 @@ CScript scriptPubKey = GetScriptForDestination(dest); ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); - const SigningProvider *provider = pwallet->GetSigningProvider(); + const SigningProvider *provider = pwallet->GetSigningProvider(scriptPubKey); isminetype mine = pwallet->IsMine(dest); ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); - bool solvable = IsSolvable(*provider, scriptPubKey); + bool solvable = provider && IsSolvable(*provider, scriptPubKey); ret.pushKV("solvable", solvable); if (solvable) { ret.pushKV("desc", @@ -4233,7 +4261,7 @@ } ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); - ScriptPubKeyMan *spk_man = pwallet->GetScriptPubKeyMan(); + ScriptPubKeyMan *spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey); if (spk_man) { if (const CKeyMetadata *meta = spk_man->GetMetadata(dest)) { ret.pushKV("timestamp", meta->nCreateTime); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1351,8 +1351,14 @@ parameters...); }; - ScriptPubKeyMan *GetScriptPubKeyMan() const; - const SigningProvider *GetSigningProvider() const; + //! Get the ScriptPubKeyMan for a script + ScriptPubKeyMan *GetScriptPubKeyMan(const CScript &script) const; + + //! Get the SigningProvider for a script + const SigningProvider *GetSigningProvider(const CScript &script) const; + const SigningProvider *GetSigningProvider(const CScript &script, + SignatureData &sigdata) const; + LegacyScriptPubKeyMan *GetLegacyScriptPubKeyMan() const; // Temporary LegacyScriptPubKeyMan accessors and aliases. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1464,7 +1464,11 @@ const CScript &scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - const SigningProvider *provider = GetSigningProvider(); + const SigningProvider *provider = GetSigningProvider(scriptPubKey); + if (!provider) { + // We don't know about this scriptpbuKey; + return false; + } if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR @@ -2343,7 +2347,8 @@ continue; } - const SigningProvider *provider = GetSigningProvider(); + const SigningProvider *provider = + GetSigningProvider(wtx.tx->vout[i].scriptPubKey); bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) @@ -2681,8 +2686,9 @@ SignatureData sigdata; SigHashType sigHashType = SigHashType().withForkId(); - const SigningProvider *provider = GetSigningProvider(); + const SigningProvider *provider = GetSigningProvider(scriptPubKey); if (!provider) { + // We don't know about this scriptpbuKey; return false; } @@ -3186,7 +3192,8 @@ const CScript &scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; - const SigningProvider *provider = GetSigningProvider(); + const SigningProvider *provider = + GetSigningProvider(scriptPubKey); if (!provider || !ProduceSignature( *provider, @@ -4505,11 +4512,18 @@ return true; } -ScriptPubKeyMan *CWallet::GetScriptPubKeyMan() const { +ScriptPubKeyMan *CWallet::GetScriptPubKeyMan(const CScript &script) const { + return m_spk_man.get(); +} + +const SigningProvider * +CWallet::GetSigningProvider(const CScript &script) const { return m_spk_man.get(); } -const SigningProvider *CWallet::GetSigningProvider() const { +const SigningProvider * +CWallet::GetSigningProvider(const CScript &script, + SignatureData &sigdata) const { return m_spk_man.get(); }