1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2019 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <script/keyorigin.h>
7 #include <script/signingprovider.h>
8 #include <script/standard.h>
9 
10 #include <util/system.h>
11 
12 const SigningProvider& DUMMY_SIGNING_PROVIDER = SigningProvider();
13 
14 template<typename M, typename K, typename V>
LookupHelper(const M & map,const K & key,V & value)15 bool LookupHelper(const M& map, const K& key, V& value)
16 {
17     auto it = map.find(key);
18     if (it != map.end()) {
19         value = it->second;
20         return true;
21     }
22     return false;
23 }
24 
GetCScript(const CScriptID & scriptid,CScript & script) const25 bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const
26 {
27     return m_provider->GetCScript(scriptid, script);
28 }
29 
GetPubKey(const CKeyID & keyid,CPubKey & pubkey) const30 bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const
31 {
32     return m_provider->GetPubKey(keyid, pubkey);
33 }
34 
GetKey(const CKeyID & keyid,CKey & key) const35 bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const
36 {
37     if (m_hide_secret) return false;
38     return m_provider->GetKey(keyid, key);
39 }
40 
GetKeyOrigin(const CKeyID & keyid,KeyOriginInfo & info) const41 bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
42 {
43     if (m_hide_origin) return false;
44     return m_provider->GetKeyOrigin(keyid, info);
45 }
46 
GetCScript(const CScriptID & scriptid,CScript & script) const47 bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); }
GetPubKey(const CKeyID & keyid,CPubKey & pubkey) const48 bool FlatSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const { return LookupHelper(pubkeys, keyid, pubkey); }
GetKeyOrigin(const CKeyID & keyid,KeyOriginInfo & info) const49 bool FlatSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const
50 {
51     std::pair<CPubKey, KeyOriginInfo> out;
52     bool ret = LookupHelper(origins, keyid, out);
53     if (ret) info = std::move(out.second);
54     return ret;
55 }
GetKey(const CKeyID & keyid,CKey & key) const56 bool FlatSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const { return LookupHelper(keys, keyid, key); }
57 
Merge(const FlatSigningProvider & a,const FlatSigningProvider & b)58 FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvider& b)
59 {
60     FlatSigningProvider ret;
61     ret.scripts = a.scripts;
62     ret.scripts.insert(b.scripts.begin(), b.scripts.end());
63     ret.pubkeys = a.pubkeys;
64     ret.pubkeys.insert(b.pubkeys.begin(), b.pubkeys.end());
65     ret.keys = a.keys;
66     ret.keys.insert(b.keys.begin(), b.keys.end());
67     ret.origins = a.origins;
68     ret.origins.insert(b.origins.begin(), b.origins.end());
69     return ret;
70 }
71 
ImplicitlyLearnRelatedKeyScripts(const CPubKey & pubkey)72 void FillableSigningProvider::ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey)
73 {
74     AssertLockHeld(cs_KeyStore);
75     CKeyID key_id = pubkey.GetID();
76     // This adds the redeemscripts necessary to detect P2WPKH and P2SH-P2WPKH
77     // outputs. Technically P2WPKH outputs don't have a redeemscript to be
78     // spent. However, our current IsMine logic requires the corresponding
79     // P2SH-P2WPKH redeemscript to be present in the wallet in order to accept
80     // payment even to P2WPKH outputs.
81     // Also note that having superfluous scripts in the keystore never hurts.
82     // They're only used to guide recursion in signing and IsMine logic - if
83     // a script is present but we can't do anything with it, it has no effect.
84     // "Implicitly" refers to fact that scripts are derived automatically from
85     // existing keys, and are present in memory, even without being explicitly
86     // loaded (e.g. from a file).
87     if (pubkey.IsCompressed()) {
88         CScript script = GetScriptForDestination(WitnessV0KeyHash(key_id));
89         // This does not use AddCScript, as it may be overridden.
90         CScriptID id(script);
91         mapScripts[id] = std::move(script);
92     }
93 }
94 
GetPubKey(const CKeyID & address,CPubKey & vchPubKeyOut) const95 bool FillableSigningProvider::GetPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
96 {
97     CKey key;
98     if (!GetKey(address, key)) {
99         return false;
100     }
101     vchPubKeyOut = key.GetPubKey();
102     return true;
103 }
104 
AddKeyPubKey(const CKey & key,const CPubKey & pubkey)105 bool FillableSigningProvider::AddKeyPubKey(const CKey& key, const CPubKey &pubkey)
106 {
107     LOCK(cs_KeyStore);
108     mapKeys[pubkey.GetID()] = key;
109     ImplicitlyLearnRelatedKeyScripts(pubkey);
110     return true;
111 }
112 
HaveKey(const CKeyID & address) const113 bool FillableSigningProvider::HaveKey(const CKeyID &address) const
114 {
115     LOCK(cs_KeyStore);
116     return mapKeys.count(address) > 0;
117 }
118 
GetKeys() const119 std::set<CKeyID> FillableSigningProvider::GetKeys() const
120 {
121     LOCK(cs_KeyStore);
122     std::set<CKeyID> set_address;
123     for (const auto& mi : mapKeys) {
124         set_address.insert(mi.first);
125     }
126     return set_address;
127 }
128 
GetKey(const CKeyID & address,CKey & keyOut) const129 bool FillableSigningProvider::GetKey(const CKeyID &address, CKey &keyOut) const
130 {
131     LOCK(cs_KeyStore);
132     KeyMap::const_iterator mi = mapKeys.find(address);
133     if (mi != mapKeys.end()) {
134         keyOut = mi->second;
135         return true;
136     }
137     return false;
138 }
139 
AddCScript(const CScript & redeemScript)140 bool FillableSigningProvider::AddCScript(const CScript& redeemScript)
141 {
142     if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)
143         return error("FillableSigningProvider::AddCScript(): redeemScripts > %i bytes are invalid", MAX_SCRIPT_ELEMENT_SIZE);
144 
145     LOCK(cs_KeyStore);
146     mapScripts[CScriptID(redeemScript)] = redeemScript;
147     return true;
148 }
149 
HaveCScript(const CScriptID & hash) const150 bool FillableSigningProvider::HaveCScript(const CScriptID& hash) const
151 {
152     LOCK(cs_KeyStore);
153     return mapScripts.count(hash) > 0;
154 }
155 
GetCScripts() const156 std::set<CScriptID> FillableSigningProvider::GetCScripts() const
157 {
158     LOCK(cs_KeyStore);
159     std::set<CScriptID> set_script;
160     for (const auto& mi : mapScripts) {
161         set_script.insert(mi.first);
162     }
163     return set_script;
164 }
165 
GetCScript(const CScriptID & hash,CScript & redeemScriptOut) const166 bool FillableSigningProvider::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const
167 {
168     LOCK(cs_KeyStore);
169     ScriptMap::const_iterator mi = mapScripts.find(hash);
170     if (mi != mapScripts.end())
171     {
172         redeemScriptOut = (*mi).second;
173         return true;
174     }
175     return false;
176 }
177 
GetKeyForDestination(const SigningProvider & store,const CTxDestination & dest)178 CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination& dest)
179 {
180     // Only supports destinations which map to single public keys, i.e. P2PKH,
181     // P2WPKH, and P2SH-P2WPKH.
182     if (auto id = boost::get<PKHash>(&dest)) {
183         return ToKeyID(*id);
184     }
185     if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
186         return ToKeyID(*witness_id);
187     }
188     if (auto script_hash = boost::get<ScriptHash>(&dest)) {
189         CScript script;
190         CScriptID script_id(*script_hash);
191         CTxDestination inner_dest;
192         if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) {
193             if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
194                 return ToKeyID(*inner_witness_id);
195             }
196         }
197     }
198     return CKeyID();
199 }
200