Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 :
4 : #include <wallet/hdchain.h>
5 :
6 : #include <wallet/bip39.h>
7 : #include <chainparams.h>
8 : #include <tinyformat.h>
9 : #include <util/system.h>
10 :
11 : namespace wallet {
12 0 : bool CHDChain::SetNull()
13 : {
14 0 : LOCK(cs);
15 0 : nVersion = CURRENT_VERSION;
16 0 : id = uint256();
17 0 : fCrypted = false;
18 0 : vchSeed.clear();
19 0 : vchMnemonic.clear();
20 0 : vchMnemonicPassphrase.clear();
21 0 : mapAccounts.clear();
22 0 : return IsNull();
23 0 : }
24 :
25 22314 : bool CHDChain::IsNull() const
26 : {
27 22314 : LOCK(cs);
28 22314 : return vchSeed.empty() || id == uint256();
29 22314 : }
30 :
31 0 : void CHDChain::SetCrypted(bool fCryptedIn)
32 : {
33 0 : LOCK(cs);
34 0 : fCrypted = fCryptedIn;
35 0 : }
36 :
37 0 : bool CHDChain::IsCrypted() const
38 : {
39 0 : LOCK(cs);
40 0 : return fCrypted;
41 0 : }
42 :
43 0 : bool CHDChain::SetMnemonic(const SecureVector& vchMnemonic, const SecureVector& vchMnemonicPassphrase, bool fUpdateID)
44 : {
45 0 : return SetMnemonic(SecureString(vchMnemonic.begin(), vchMnemonic.end()), SecureString(vchMnemonicPassphrase.begin(), vchMnemonicPassphrase.end()), fUpdateID);
46 0 : }
47 :
48 0 : bool CHDChain::SetMnemonic(const SecureString& ssMnemonic, const SecureString& ssMnemonicPassphrase, bool fUpdateID)
49 : {
50 0 : LOCK(cs);
51 0 : SecureString ssMnemonicTmp = ssMnemonic;
52 :
53 0 : if (fUpdateID) {
54 : // can't (re)set mnemonic if seed was already set
55 0 : if (!IsNull())
56 0 : return false;
57 :
58 0 : if (ssMnemonicPassphrase.size() > 256) {
59 0 : throw std::runtime_error(std::string(__func__) + ": Mnemonic passphrase is too long, must be at most 256 characters");
60 : }
61 :
62 : // empty mnemonic i.e. "generate a new one"
63 0 : if (ssMnemonic.empty()) {
64 0 : ssMnemonicTmp = CMnemonic::Generate(gArgs.GetIntArg("-mnemonicbits", DEFAULT_MNEMONIC_BITS));
65 0 : }
66 : // NOTE: default mnemonic passphrase is an empty string
67 :
68 : // printf("mnemonic: %s\n", ssMnemonicTmp.c_str());
69 0 : if (!CMnemonic::Check(ssMnemonicTmp)) {
70 0 : throw std::runtime_error(std::string(__func__) + ": invalid mnemonic: `" + std::string(ssMnemonicTmp) + "`");
71 : }
72 :
73 0 : CMnemonic::ToSeed(ssMnemonicTmp, ssMnemonicPassphrase, vchSeed);
74 0 : id = GetSeedHash();
75 0 : }
76 :
77 0 : vchMnemonic = SecureVector(ssMnemonicTmp.begin(), ssMnemonicTmp.end());
78 0 : vchMnemonicPassphrase = SecureVector(ssMnemonicPassphrase.begin(), ssMnemonicPassphrase.end());
79 :
80 0 : return !IsNull();
81 0 : }
82 :
83 0 : bool CHDChain::GetMnemonic(SecureVector& vchMnemonicRet, SecureVector& vchMnemonicPassphraseRet) const
84 : {
85 0 : LOCK(cs);
86 : // mnemonic was not set, fail
87 0 : if (vchMnemonic.empty())
88 0 : return false;
89 :
90 0 : vchMnemonicRet = vchMnemonic;
91 0 : vchMnemonicPassphraseRet = vchMnemonicPassphrase;
92 0 : return true;
93 0 : }
94 :
95 0 : bool CHDChain::GetMnemonic(SecureString& ssMnemonicRet, SecureString& ssMnemonicPassphraseRet) const
96 : {
97 0 : LOCK(cs);
98 : // mnemonic was not set, fail
99 0 : if (vchMnemonic.empty())
100 0 : return false;
101 :
102 0 : ssMnemonicRet = SecureString(vchMnemonic.begin(), vchMnemonic.end());
103 0 : ssMnemonicPassphraseRet = SecureString(vchMnemonicPassphrase.begin(), vchMnemonicPassphrase.end());
104 :
105 0 : return true;
106 0 : }
107 :
108 0 : bool CHDChain::SetSeed(const SecureVector& vchSeedIn, bool fUpdateID)
109 : {
110 0 : LOCK(cs);
111 0 : vchSeed = vchSeedIn;
112 :
113 0 : if (fUpdateID) {
114 0 : id = GetSeedHash();
115 0 : }
116 :
117 0 : return !IsNull();
118 0 : }
119 :
120 0 : SecureVector CHDChain::GetSeed() const
121 : {
122 0 : LOCK(cs);
123 0 : return vchSeed;
124 0 : }
125 :
126 0 : uint256 CHDChain::GetSeedHash()
127 : {
128 0 : LOCK(cs);
129 0 : return Hash(vchSeed);
130 0 : }
131 :
132 : //! Try to derive an extended key, throw if it fails.
133 0 : static void DeriveExtKey(CExtKey& key_in, unsigned int index, CExtKey& key_out)
134 : {
135 0 : if (!key_in.Derive(key_out, index)) {
136 0 : throw std::runtime_error("Could not derive extended key");
137 : }
138 0 : }
139 :
140 0 : void CHDChain::DeriveChildExtKey(uint32_t nAccountIndex, bool fInternal, uint32_t nChildIndex, CExtKey& extKeyRet, KeyOriginInfo& key_origin)
141 : {
142 0 : LOCK(cs);
143 : // Use BIP44 keypath scheme i.e. m / purpose' / coin_type' / account' / change / address_index
144 0 : CExtKey masterKey; //hd master key
145 0 : CExtKey purposeKey; //key at m/purpose'
146 0 : CExtKey cointypeKey; //key at m/purpose'/coin_type'
147 0 : CExtKey accountKey; //key at m/purpose'/coin_type'/account'
148 0 : CExtKey changeKey; //key at m/purpose'/coin_type'/account'/change
149 0 : CExtKey childKey; //key at m/purpose'/coin_type'/account'/change/address_index
150 :
151 0 : masterKey.SetSeed(MakeByteSpan(vchSeed));
152 :
153 : // Use hardened derivation for purpose, coin_type and account
154 : // (keys >= 0x80000000 are hardened after bip32)
155 :
156 : // derive m/purpose'
157 0 : DeriveExtKey(masterKey, 44 | 0x80000000, purposeKey);
158 : // derive m/purpose'/coin_type'
159 0 : DeriveExtKey(purposeKey, Params().ExtCoinType() | 0x80000000, cointypeKey);
160 : // derive m/purpose'/coin_type'/account'
161 0 : DeriveExtKey(cointypeKey, nAccountIndex | 0x80000000, accountKey);
162 : // derive m/purpose'/coin_type'/account'/change
163 0 : DeriveExtKey(accountKey, fInternal ? 1 : 0, changeKey);
164 : // derive m/purpose'/coin_type'/account'/change/address_index
165 0 : DeriveExtKey(changeKey, nChildIndex, extKeyRet);
166 :
167 : #ifdef ENABLE_WALLET
168 : // We should never ever update an already existing key_origin here
169 0 : assert(key_origin.path.empty());
170 0 : key_origin.path.push_back(44 | 0x80000000);
171 0 : key_origin.path.push_back(Params().ExtCoinType() | 0x80000000);
172 0 : key_origin.path.push_back(nAccountIndex | 0x80000000);
173 0 : key_origin.path.push_back(fInternal ? 1 : 0);
174 0 : key_origin.path.push_back(nChildIndex);
175 :
176 0 : CKeyID master_id = masterKey.key.GetPubKey().GetID();
177 0 : std::copy(master_id.begin(), master_id.begin() + 4, key_origin.fingerprint);
178 : #endif
179 0 : }
180 :
181 0 : void CHDChain::AddAccount()
182 : {
183 0 : LOCK(cs);
184 0 : mapAccounts.insert(std::pair<uint32_t, CHDAccount>(mapAccounts.size(), CHDAccount()));
185 0 : }
186 :
187 0 : bool CHDChain::GetAccount(uint32_t nAccountIndex, CHDAccount& hdAccountRet)
188 : {
189 0 : LOCK(cs);
190 0 : if (nAccountIndex > mapAccounts.size() - 1)
191 0 : return false;
192 0 : hdAccountRet = mapAccounts[nAccountIndex];
193 0 : return true;
194 0 : }
195 :
196 0 : bool CHDChain::SetAccount(uint32_t nAccountIndex, const CHDAccount& hdAccount)
197 : {
198 0 : LOCK(cs);
199 : // can only replace existing accounts
200 0 : if (nAccountIndex > mapAccounts.size() - 1)
201 0 : return false;
202 0 : mapAccounts[nAccountIndex] = hdAccount;
203 0 : return true;
204 0 : }
205 :
206 0 : size_t CHDChain::CountAccounts()
207 : {
208 0 : LOCK(cs);
209 0 : return mapAccounts.size();
210 0 : }
211 :
212 0 : std::string CHDPubKey::GetKeyPath() const
213 : {
214 0 : return strprintf("m/%d'/%d'/%d'/%d/%d", BIP32_PURPOSE_STANDARD, Params().ExtCoinType(), nAccountIndex, nChangeIndex,
215 0 : extPubKey.nChild);
216 : }
217 : } // namespace wallet
|