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 210031 : bool CHDChain::IsNull() const
26 : {
27 210031 : LOCK(cs);
28 210031 : return vchSeed.empty() || id == uint256();
29 210031 : }
30 :
31 1450 : void CHDChain::SetCrypted(bool fCryptedIn)
32 : {
33 1450 : LOCK(cs);
34 1450 : fCrypted = fCryptedIn;
35 1450 : }
36 :
37 147700 : bool CHDChain::IsCrypted() const
38 : {
39 147700 : LOCK(cs);
40 147700 : return fCrypted;
41 147700 : }
42 :
43 1450 : bool CHDChain::SetMnemonic(const SecureVector& vchMnemonic, const SecureVector& vchMnemonicPassphrase, bool fUpdateID)
44 : {
45 1450 : return SetMnemonic(SecureString(vchMnemonic.begin(), vchMnemonic.end()), SecureString(vchMnemonicPassphrase.begin(), vchMnemonicPassphrase.end()), fUpdateID);
46 0 : }
47 :
48 2068 : bool CHDChain::SetMnemonic(const SecureString& ssMnemonic, const SecureString& ssMnemonicPassphrase, bool fUpdateID)
49 : {
50 2068 : LOCK(cs);
51 2068 : SecureString ssMnemonicTmp = ssMnemonic;
52 :
53 2068 : if (fUpdateID) {
54 : // can't (re)set mnemonic if seed was already set
55 618 : if (!IsNull())
56 0 : return false;
57 :
58 618 : 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 618 : if (ssMnemonic.empty()) {
64 598 : ssMnemonicTmp = CMnemonic::Generate(gArgs.GetIntArg("-mnemonicbits", DEFAULT_MNEMONIC_BITS));
65 598 : }
66 : // NOTE: default mnemonic passphrase is an empty string
67 :
68 : // printf("mnemonic: %s\n", ssMnemonicTmp.c_str());
69 618 : if (!CMnemonic::Check(ssMnemonicTmp)) {
70 0 : throw std::runtime_error(std::string(__func__) + ": invalid mnemonic: `" + std::string(ssMnemonicTmp) + "`");
71 : }
72 :
73 618 : CMnemonic::ToSeed(ssMnemonicTmp, ssMnemonicPassphrase, vchSeed);
74 618 : id = GetSeedHash();
75 618 : }
76 :
77 2068 : vchMnemonic = SecureVector(ssMnemonicTmp.begin(), ssMnemonicTmp.end());
78 2068 : vchMnemonicPassphrase = SecureVector(ssMnemonicPassphrase.begin(), ssMnemonicPassphrase.end());
79 :
80 2068 : return !IsNull();
81 2068 : }
82 :
83 1450 : bool CHDChain::GetMnemonic(SecureVector& vchMnemonicRet, SecureVector& vchMnemonicPassphraseRet) const
84 : {
85 1450 : LOCK(cs);
86 : // mnemonic was not set, fail
87 1450 : if (vchMnemonic.empty())
88 0 : return false;
89 :
90 1450 : vchMnemonicRet = vchMnemonic;
91 1450 : vchMnemonicPassphraseRet = vchMnemonicPassphrase;
92 1450 : return true;
93 1450 : }
94 :
95 88 : bool CHDChain::GetMnemonic(SecureString& ssMnemonicRet, SecureString& ssMnemonicPassphraseRet) const
96 : {
97 88 : LOCK(cs);
98 : // mnemonic was not set, fail
99 88 : if (vchMnemonic.empty())
100 2 : return false;
101 :
102 86 : ssMnemonicRet = SecureString(vchMnemonic.begin(), vchMnemonic.end());
103 86 : ssMnemonicPassphraseRet = SecureString(vchMnemonicPassphrase.begin(), vchMnemonicPassphrase.end());
104 :
105 86 : return true;
106 88 : }
107 :
108 1464 : bool CHDChain::SetSeed(const SecureVector& vchSeedIn, bool fUpdateID)
109 : {
110 1464 : LOCK(cs);
111 1464 : vchSeed = vchSeedIn;
112 :
113 1464 : if (fUpdateID) {
114 14 : id = GetSeedHash();
115 14 : }
116 :
117 1464 : return !IsNull();
118 1464 : }
119 :
120 1867 : SecureVector CHDChain::GetSeed() const
121 : {
122 1867 : LOCK(cs);
123 1867 : return vchSeed;
124 1867 : }
125 :
126 49213 : uint256 CHDChain::GetSeedHash()
127 : {
128 49213 : LOCK(cs);
129 49213 : return Hash(vchSeed);
130 49213 : }
131 :
132 : //! Try to derive an extended key, throw if it fails.
133 234460 : static void DeriveExtKey(CExtKey& key_in, unsigned int index, CExtKey& key_out)
134 : {
135 234460 : if (!key_in.Derive(key_out, index)) {
136 0 : throw std::runtime_error("Could not derive extended key");
137 : }
138 234460 : }
139 :
140 46892 : void CHDChain::DeriveChildExtKey(uint32_t nAccountIndex, bool fInternal, uint32_t nChildIndex, CExtKey& extKeyRet, KeyOriginInfo& key_origin)
141 : {
142 46892 : LOCK(cs);
143 : // Use BIP44 keypath scheme i.e. m / purpose' / coin_type' / account' / change / address_index
144 46892 : CExtKey masterKey; //hd master key
145 46892 : CExtKey purposeKey; //key at m/purpose'
146 46892 : CExtKey cointypeKey; //key at m/purpose'/coin_type'
147 46892 : CExtKey accountKey; //key at m/purpose'/coin_type'/account'
148 46892 : CExtKey changeKey; //key at m/purpose'/coin_type'/account'/change
149 46892 : CExtKey childKey; //key at m/purpose'/coin_type'/account'/change/address_index
150 :
151 46892 : 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 46892 : DeriveExtKey(masterKey, 44 | 0x80000000, purposeKey);
158 : // derive m/purpose'/coin_type'
159 46892 : DeriveExtKey(purposeKey, Params().ExtCoinType() | 0x80000000, cointypeKey);
160 : // derive m/purpose'/coin_type'/account'
161 46892 : DeriveExtKey(cointypeKey, nAccountIndex | 0x80000000, accountKey);
162 : // derive m/purpose'/coin_type'/account'/change
163 46892 : DeriveExtKey(accountKey, fInternal ? 1 : 0, changeKey);
164 : // derive m/purpose'/coin_type'/account'/change/address_index
165 46892 : DeriveExtKey(changeKey, nChildIndex, extKeyRet);
166 :
167 : #ifdef ENABLE_WALLET
168 : // We should never ever update an already existing key_origin here
169 46892 : assert(key_origin.path.empty());
170 46892 : key_origin.path.push_back(44 | 0x80000000);
171 46892 : key_origin.path.push_back(Params().ExtCoinType() | 0x80000000);
172 46892 : key_origin.path.push_back(nAccountIndex | 0x80000000);
173 46892 : key_origin.path.push_back(fInternal ? 1 : 0);
174 46892 : key_origin.path.push_back(nChildIndex);
175 :
176 46892 : CKeyID master_id = masterKey.key.GetPubKey().GetID();
177 46892 : std::copy(master_id.begin(), master_id.begin() + 4, key_origin.fingerprint);
178 : #endif
179 46892 : }
180 :
181 632 : void CHDChain::AddAccount()
182 : {
183 632 : LOCK(cs);
184 632 : mapAccounts.insert(std::pair<uint32_t, CHDAccount>(mapAccounts.size(), CHDAccount()));
185 632 : }
186 :
187 32945 : bool CHDChain::GetAccount(uint32_t nAccountIndex, CHDAccount& hdAccountRet)
188 : {
189 32945 : LOCK(cs);
190 32945 : if (nAccountIndex > mapAccounts.size() - 1)
191 0 : return false;
192 32945 : hdAccountRet = mapAccounts[nAccountIndex];
193 32945 : return true;
194 32945 : }
195 :
196 31845 : bool CHDChain::SetAccount(uint32_t nAccountIndex, const CHDAccount& hdAccount)
197 : {
198 31845 : LOCK(cs);
199 : // can only replace existing accounts
200 31845 : if (nAccountIndex > mapAccounts.size() - 1)
201 0 : return false;
202 31845 : mapAccounts[nAccountIndex] = hdAccount;
203 31845 : return true;
204 31845 : }
205 :
206 3262 : size_t CHDChain::CountAccounts()
207 : {
208 3262 : LOCK(cs);
209 3262 : return mapAccounts.size();
210 3262 : }
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
|