/root/bitcoin/src/external_signer.cpp
Line | Count | Source |
1 | | // Copyright (c) 2018-present The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <external_signer.h> |
6 | | |
7 | | #include <chainparams.h> |
8 | | #include <common/run_command.h> |
9 | | #include <core_io.h> |
10 | | #include <psbt.h> |
11 | | #include <util/strencodings.h> |
12 | | #include <util/subprocess.h> |
13 | | |
14 | | #include <algorithm> |
15 | | #include <stdexcept> |
16 | | #include <string> |
17 | | #include <vector> |
18 | | |
19 | | ExternalSigner::ExternalSigner(std::vector<std::string> command, std::string chain, std::string fingerprint, std::string name) |
20 | 0 | : m_command{std::move(command)}, m_chain{std::move(chain)}, m_fingerprint{std::move(fingerprint)}, m_name{std::move(name)} {} |
21 | | |
22 | | std::vector<std::string> ExternalSigner::NetworkArg() const |
23 | 0 | { |
24 | 0 | return {"--chain", m_chain}; |
25 | 0 | } |
26 | | |
27 | | bool ExternalSigner::Enumerate(const std::string& command, std::vector<ExternalSigner>& signers, const std::string& chain) |
28 | 0 | { |
29 | | // Call <command> enumerate |
30 | 0 | std::vector<std::string> cmd_args = Cat(subprocess::util::split(command), {"enumerate"}); |
31 | |
|
32 | 0 | const UniValue result = RunCommandParseJSON(cmd_args, ""); |
33 | 0 | if (!result.isArray()) { Branch (33:9): [True: 0, False: 0]
|
34 | 0 | throw std::runtime_error(strprintf("'%s' received invalid response, expected array of signers", command)); |
35 | 0 | } |
36 | 0 | for (const UniValue& signer : result.getValues()) { Branch (36:33): [True: 0, False: 0]
|
37 | | // Check for error |
38 | 0 | const UniValue& error = signer.find_value("error"); |
39 | 0 | if (!error.isNull()) { Branch (39:13): [True: 0, False: 0]
|
40 | 0 | if (!error.isStr()) { Branch (40:17): [True: 0, False: 0]
|
41 | 0 | throw std::runtime_error(strprintf("'%s' error", command)); |
42 | 0 | } |
43 | 0 | throw std::runtime_error(strprintf("'%s' error: %s", command, error.getValStr())); |
44 | 0 | } |
45 | | // Check if fingerprint is present |
46 | 0 | const UniValue& fingerprint = signer.find_value("fingerprint"); |
47 | 0 | if (fingerprint.isNull()) { Branch (47:13): [True: 0, False: 0]
|
48 | 0 | throw std::runtime_error(strprintf("'%s' received invalid response, missing signer fingerprint", command)); |
49 | 0 | } |
50 | 0 | const std::string& fingerprintStr{fingerprint.get_str()}; |
51 | 0 | if (fingerprintStr.size() != 8 || !IsHex(fingerprintStr)) { Branch (51:13): [True: 0, False: 0]
Branch (51:43): [True: 0, False: 0]
|
52 | 0 | throw std::runtime_error(strprintf("'%s' received invalid fingerprint, must be 8 hex characters", command)); |
53 | 0 | } |
54 | | // Skip duplicate signer |
55 | 0 | bool duplicate = false; |
56 | 0 | for (const ExternalSigner& signer : signers) { Branch (56:43): [True: 0, False: 0]
|
57 | 0 | if (signer.m_fingerprint.compare(fingerprintStr) == 0) duplicate = true; Branch (57:17): [True: 0, False: 0]
|
58 | 0 | } |
59 | 0 | if (duplicate) continue; Branch (59:13): [True: 0, False: 0]
|
60 | 0 | std::string name; |
61 | 0 | const UniValue& model_field = signer.find_value("model"); |
62 | 0 | if (model_field.isStr() && model_field.getValStr() != "") { Branch (62:13): [True: 0, False: 0]
Branch (62:36): [True: 0, False: 0]
|
63 | 0 | name += model_field.getValStr(); |
64 | 0 | } |
65 | 0 | signers.emplace_back(subprocess::util::split(command), chain, fingerprintStr, name); |
66 | 0 | } |
67 | 0 | return true; |
68 | 0 | } |
69 | | |
70 | | UniValue ExternalSigner::DisplayAddress(const std::string& descriptor) const |
71 | 0 | { |
72 | 0 | return RunCommandParseJSON(Cat(m_command, Cat(Cat({"--fingerprint", m_fingerprint}, NetworkArg()), {"displayaddress", "--desc", descriptor})), ""); |
73 | 0 | } |
74 | | |
75 | | UniValue ExternalSigner::GetDescriptors(const int account) |
76 | 0 | { |
77 | 0 | return RunCommandParseJSON(Cat(m_command, Cat(Cat({"--fingerprint", m_fingerprint}, NetworkArg()), {"getdescriptors", "--account", strprintf("%d", account)})), ""); |
78 | 0 | } |
79 | | |
80 | | bool ExternalSigner::SignTransaction(PartiallySignedTransaction& psbtx, std::string& error) |
81 | 0 | { |
82 | | // Serialize the PSBT |
83 | 0 | DataStream ssTx{}; |
84 | 0 | ssTx << psbtx; |
85 | | // parse ExternalSigner master fingerprint |
86 | 0 | std::vector<unsigned char> parsed_m_fingerprint = ParseHex(m_fingerprint); |
87 | | // Check if signer fingerprint matches any input master key fingerprint |
88 | 0 | auto matches_signer_fingerprint = [&](const PSBTInput& input) { |
89 | 0 | for (const auto& entry : input.hd_keypaths) { Branch (89:32): [True: 0, False: 0]
|
90 | 0 | if (std::ranges::equal(parsed_m_fingerprint, entry.second.fingerprint)) return true; Branch (90:17): [True: 0, False: 0]
|
91 | 0 | } |
92 | 0 | for (const auto& entry : input.m_tap_bip32_paths) { Branch (92:32): [True: 0, False: 0]
|
93 | 0 | if (std::ranges::equal(parsed_m_fingerprint, entry.second.second.fingerprint)) return true; Branch (93:17): [True: 0, False: 0]
|
94 | 0 | } |
95 | 0 | return false; |
96 | 0 | }; |
97 | |
|
98 | 0 | if (!std::any_of(psbtx.inputs.begin(), psbtx.inputs.end(), matches_signer_fingerprint)) { Branch (98:9): [True: 0, False: 0]
|
99 | 0 | error = "Signer fingerprint " + m_fingerprint + " does not match any of the inputs:\n" + EncodeBase64(ssTx.str()); |
100 | 0 | return false; |
101 | 0 | } |
102 | | |
103 | 0 | const std::vector<std::string> command = Cat(m_command, Cat({"--stdin", "--fingerprint", m_fingerprint}, NetworkArg())); |
104 | 0 | const std::string stdinStr = "signtx " + EncodeBase64(ssTx.str()); |
105 | |
|
106 | 0 | const UniValue signer_result = RunCommandParseJSON(command, stdinStr); |
107 | |
|
108 | 0 | if (signer_result.find_value("error").isStr()) { Branch (108:9): [True: 0, False: 0]
|
109 | 0 | error = signer_result.find_value("error").get_str(); |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | 0 | if (!signer_result.find_value("psbt").isStr()) { Branch (113:9): [True: 0, False: 0]
|
114 | 0 | error = "Unexpected result from signer"; |
115 | 0 | return false; |
116 | 0 | } |
117 | | |
118 | 0 | util::Result<PartiallySignedTransaction> signer_psbtx = DecodeBase64PSBT(signer_result.find_value("psbt").get_str()); |
119 | 0 | if (!signer_psbtx) { Branch (119:9): [True: 0, False: 0]
|
120 | 0 | error = strprintf("TX decode failed %s", util::ErrorString(signer_psbtx).original); |
121 | 0 | return false; |
122 | 0 | } |
123 | | |
124 | 0 | psbtx = *signer_psbtx; |
125 | |
|
126 | 0 | return true; |
127 | 0 | } |