// Copyright (c) 2020-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include using common::PSBTError; namespace wallet { std::unique_ptr ExternalSignerScriptPubKeyMan::LoadFromStorage(WalletStorage& storage, WalletDescriptor& descriptor, int64_t keypool_size, const KeyMap& keys, const CryptedKeyMap& ckeys) { return std::unique_ptr(new ExternalSignerScriptPubKeyMan(storage, descriptor, keypool_size, keys, ckeys)); } std::unique_ptr ExternalSignerScriptPubKeyMan::CreateNew(WalletStorage& storage, WalletBatch& batch, int64_t keypool_size, std::unique_ptr desc) { auto spkm = std::unique_ptr(new ExternalSignerScriptPubKeyMan(storage, keypool_size)); LOCK(spkm->cs_desc_man); assert(storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); assert(storage.IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)); int64_t creation_time = GetTime(); // Make the descriptor WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); spkm->m_wallet_descriptor = w_desc; // Store the descriptor if (!batch.WriteDescriptor(spkm->GetID(), spkm->m_wallet_descriptor)) { throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); } // TopUp spkm->TopUpWithDB(batch); storage.UnsetBlankWalletFlag(batch); return spkm; } util::Result ExternalSignerScriptPubKeyMan::GetExternalSigner() { const std::string command = gArgs.GetArg("-signer", ""); if (command == "") return util::Error{Untranslated("restart bitcoind with -signer=")}; std::vector signers; ExternalSigner::Enumerate(command, signers, Params().GetChainTypeString()); if (signers.empty()) return util::Error{Untranslated("No external signers found")}; // TODO: add fingerprint argument instead of failing in case of multiple signers. if (signers.size() > 1) return util::Error{Untranslated("More than one external signer found. Please connect only one at a time.")}; return signers[0]; } util::Result ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestination& dest, const ExternalSigner &signer) const { // TODO: avoid the need to infer a descriptor from inside a descriptor wallet const CScript& scriptPubKey = GetScriptForDestination(dest); auto provider = GetSolvingProvider(scriptPubKey); auto descriptor = InferDescriptor(scriptPubKey, *provider); const UniValue& result = signer.DisplayAddress(descriptor->ToString()); const UniValue& error = result.find_value("error"); if (error.isStr()) return util::Error{strprintf(_("Signer returned error: %s"), error.getValStr())}; const UniValue& ret_address = result.find_value("address"); if (!ret_address.isStr()) return util::Error{_("Signer did not echo address")}; if (ret_address.getValStr() != EncodeDestination(dest)) { return util::Error{strprintf(_("Signer echoed unexpected address %s"), ret_address.getValStr())}; } return util::Result(); } // If sign is true, transaction must previously have been filled std::optional ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, const common::PSBTFillOptions& options, int* n_signed) const { if (!options.sign) { return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, options, n_signed); } // Already complete if every input is now signed bool complete = true; for (const auto& input : psbt.inputs) { complete &= PSBTInputSigned(input); } if (complete) return {}; auto signer{GetExternalSigner()}; if (!signer) { LogWarning("%s", util::ErrorString(signer).original); return PSBTError::EXTERNAL_SIGNER_NOT_FOUND; } std::string failure_reason; if(!signer->SignTransaction(psbt, failure_reason)) { LogWarning("Failed to sign: %s\n", failure_reason); return PSBTError::EXTERNAL_SIGNER_FAILED; } if (options.finalize) FinalizePSBT(psbt); // This won't work in a multisig setup return {}; } } // namespace wallet