]>
Commit | Line | Data |
---|---|---|
b2120e22 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
5b40d886 | 3 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 4 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
e8ef3da7 | 5 | |
50c72f23 | 6 | #include "wallet/wallet.h" |
51ed9ec9 | 7 | |
81a45d69 | 8 | #include "asyncrpcqueue.h" |
3e1cf9b6 | 9 | #include "checkpoints.h" |
6a86c24d | 10 | #include "coincontrol.h" |
81a45d69 | 11 | #include "core_io.h" |
be126699 | 12 | #include "consensus/upgrades.h" |
da29ecbc | 13 | #include "consensus/validation.h" |
9bb37bf0 | 14 | #include "consensus/consensus.h" |
02e67455 | 15 | #include "init.h" |
3d31e09c | 16 | #include "key_io.h" |
8a893c94 | 17 | #include "main.h" |
b2a98c42 | 18 | #include "mmr.h" |
0689f46c | 19 | #include "net.h" |
fcab001b | 20 | #include "rpc/protocol.h" |
81a45d69 | 21 | #include "rpc/server.h" |
e088d65a | 22 | #include "script/script.h" |
23 | #include "script/sign.h" | |
14f888ca | 24 | #include "timedata.h" |
ad49c256 | 25 | #include "utilmoneystr.h" |
02e67455 | 26 | #include "zcash/Note.hpp" |
73699cea | 27 | #include "crypter.h" |
4a4e912b | 28 | #include "coins.h" |
81a45d69 | 29 | #include "wallet/asyncrpcoperation_saplingmigration.h" |
70b4ad2d | 30 | #include "zcash/zip32.h" |
ca4a5f26 | 31 | #include "cc/StakeGuard.h" |
b7c685b8 | 32 | #include "pbaas/identity.h" |
e7e14f44 | 33 | #include "pbaas/pbaas.h" |
1fae37f6 | 34 | |
d0c4197e PK |
35 | #include <assert.h> |
36 | ||
cae686d3 | 37 | #include <boost/algorithm/string/replace.hpp> |
2bb1c877 | 38 | #include <boost/filesystem.hpp> |
ad49c256 | 39 | #include <boost/thread.hpp> |
e8ef3da7 WL |
40 | |
41 | using namespace std; | |
c1c45943 | 42 | using namespace libzcash; |
e8ef3da7 | 43 | |
5b40d886 MF |
44 | /** |
45 | * Settings | |
46 | */ | |
c6cb21d1 | 47 | CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); |
aa279d61 | 48 | CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; |
77ed59df | 49 | unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; |
1bbca249 | 50 | bool bSpendZeroConfChange = true; |
c1c9d5b4 | 51 | bool fSendFreeTransactions = false; |
ed3e5e46 | 52 | bool fPayAtLeastCustomFee = true; |
bdc72415 | 53 | #include "komodo_defs.h" |
54 | ||
06f41160 | 55 | extern int32_t USE_EXTERNAL_PUBKEY; |
56 | extern std::string NOTARY_PUBKEY; | |
6ad13d7c | 57 | extern int32_t KOMODO_EXCHANGEWALLET; |
7c130297 | 58 | extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; |
b2a98c42 | 59 | extern uint160 ASSETCHAINS_CHAINID; |
1fae37f6 | 60 | extern int32_t VERUS_MIN_STAKEAGE; |
1f722359 | 61 | CBlockIndex *komodo_chainactive(int32_t height); |
a55973ae | 62 | extern std::string DONATION_PUBKEY; |
a20f2622 | 63 | extern BlockMap mapBlockIndex; |
e8ef3da7 | 64 | |
7e6d23b1 CD |
65 | /** |
66 | * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) | |
5b40d886 MF |
67 | * Override with -mintxfee |
68 | */ | |
9b1627d1 | 69 | CFeeRate CWallet::minTxFee = CFeeRate(1000); |
13fc83c7 | 70 | |
5b40d886 MF |
71 | /** @defgroup mapWallet |
72 | * | |
73 | * @{ | |
74 | */ | |
e8ef3da7 | 75 | |
d650f96d CM |
76 | struct CompareValueOnly |
77 | { | |
a372168e MF |
78 | bool operator()(const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t1, |
79 | const pair<CAmount, pair<const CWalletTx*, unsigned int> >& t2) const | |
d650f96d CM |
80 | { |
81 | return t1.first < t2.first; | |
82 | } | |
83 | }; | |
84 | ||
0ff96ff6 | 85 | class CompareValueMap |
86 | { | |
87 | public: | |
88 | CCurrencyValueMap totalTargetValues; | |
89 | CompareValueMap() {} | |
90 | CompareValueMap(const CCurrencyValueMap &targetValues) : totalTargetValues(targetValues) {} | |
91 | bool CompareMaps(const CCurrencyValueMap &m1, | |
92 | const CCurrencyValueMap &m2) const | |
93 | { | |
94 | // if we have a target to compare against, | |
95 | // check to see if one leaves less change after meeting the target | |
96 | if (totalTargetValues.valueMap.size()) | |
2ec1b02c | 97 | { |
0ff96ff6 | 98 | CCurrencyValueMap leftover1 = m1.SubtractToZero(totalTargetValues); |
99 | CCurrencyValueMap leftover2 = m2.SubtractToZero(totalTargetValues); | |
100 | ||
101 | if (leftover1 < leftover2 && leftover2 < leftover1) | |
2ec1b02c | 102 | { |
0ff96ff6 | 103 | if (leftover1.valueMap.size() < leftover2.valueMap.size()) |
104 | { | |
105 | return true; | |
106 | } | |
107 | else if (leftover2.valueMap.size() < leftover1.valueMap.size()) | |
108 | { | |
109 | return false; | |
110 | } | |
2ec1b02c | 111 | } |
0ff96ff6 | 112 | return leftover1 < leftover2; |
2ec1b02c | 113 | } |
0ff96ff6 | 114 | else if (m1 < m2 && m2 < m1) |
2ec1b02c | 115 | { |
116 | // this is used for sorting | |
117 | // what we care about most in this case is that we always give the same answer, | |
118 | // so, run a repeatable check, regardless of the order of operands. we'd also want | |
119 | // to be as close to right as possible. | |
0ff96ff6 | 120 | CCurrencyValueMap checkMap1 = m1.IntersectingValues(m2); |
2ec1b02c | 121 | CCurrencyValueMap checkMap2; |
122 | // where they intersect, they are empty, no way to know which is less for sorting | |
123 | if (!(checkMap2 < checkMap1)) | |
124 | { | |
125 | return false; | |
126 | } | |
0ff96ff6 | 127 | checkMap2 = checkMap1 - m2.IntersectingValues(m1); |
2ec1b02c | 128 | CAmount total = 0; |
129 | for (auto &oneCur : checkMap2.valueMap) | |
130 | { | |
131 | total += oneCur.second; | |
132 | } | |
133 | if (total < 0) | |
134 | { | |
135 | return true; | |
136 | } | |
137 | else | |
138 | { | |
139 | return false; | |
140 | } | |
141 | } | |
0ff96ff6 | 142 | return m1 < m2; |
56fe75cb | 143 | } |
144 | }; | |
145 | ||
02e67455 JG |
146 | std::string JSOutPoint::ToString() const |
147 | { | |
148 | return strprintf("JSOutPoint(%s, %d, %d)", hash.ToString().substr(0,10), js, n); | |
149 | } | |
150 | ||
ad49c256 WL |
151 | std::string COutput::ToString() const |
152 | { | |
805344dc | 153 | return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->vout[i].nValue)); |
ad49c256 WL |
154 | } |
155 | ||
93a18a36 GA |
156 | const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const |
157 | { | |
158 | LOCK(cs_wallet); | |
159 | std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); | |
160 | if (it == mapWallet.end()) | |
161 | return NULL; | |
162 | return &(it->second); | |
163 | } | |
164 | ||
c1c45943 | 165 | // Generate a new spending key and return its public payment address |
d6ad8cef | 166 | libzcash::SproutPaymentAddress CWallet::GenerateNewSproutZKey() |
c1c45943 | 167 | { |
d6b31d59 | 168 | AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata |
d6ad8cef | 169 | |
e5eab182 | 170 | auto k = SproutSpendingKey::random(); |
c1c45943 S |
171 | auto addr = k.address(); |
172 | ||
173 | // Check for collision, even though it is unlikely to ever occur | |
25d5e80c | 174 | if (CCryptoKeyStore::HaveSproutSpendingKey(addr)) |
92fc29a3 | 175 | throw std::runtime_error("CWallet::GenerateNewSproutZKey(): Collision detected"); |
c1c45943 S |
176 | |
177 | // Create new metadata | |
178 | int64_t nCreationTime = GetTime(); | |
d6b31d59 | 179 | mapSproutZKeyMetadata[addr] = CKeyMetadata(nCreationTime); |
c1c45943 | 180 | |
a0783bb9 | 181 | if (!AddSproutZKey(k)) |
92fc29a3 | 182 | throw std::runtime_error("CWallet::GenerateNewSproutZKey(): AddSproutZKey failed"); |
80ed13d5 | 183 | return addr; |
c1c45943 S |
184 | } |
185 | ||
efb7662d JG |
186 | // Generate a new Sapling spending key and return its public payment address |
187 | SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() | |
188 | { | |
8e91ebf7 | 189 | AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata |
70b4ad2d | 190 | |
f82a864d JG |
191 | // Create new metadata |
192 | int64_t nCreationTime = GetTime(); | |
70b4ad2d JG |
193 | CKeyMetadata metadata(nCreationTime); |
194 | ||
195 | // Try to get the seed | |
196 | HDSeed seed; | |
197 | if (!GetHDSeed(seed)) | |
198 | throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): HD seed not found"); | |
199 | ||
200 | auto m = libzcash::SaplingExtendedSpendingKey::Master(seed); | |
b6d1af89 | 201 | uint32_t bip44CoinType = Params().BIP44CoinType(); |
70b4ad2d JG |
202 | |
203 | // We use a fixed keypath scheme of m/32'/coin_type'/account' | |
204 | // Derive m/32' | |
205 | auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT); | |
206 | // Derive m/32'/coin_type' | |
b6d1af89 | 207 | auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT); |
70b4ad2d JG |
208 | |
209 | // Derive account key at next index, skip keys already known to the wallet | |
210 | libzcash::SaplingExtendedSpendingKey xsk; | |
211 | do | |
212 | { | |
213 | xsk = m_32h_cth.Derive(hdChain.saplingAccountCounter | ZIP32_HARDENED_KEY_LIMIT); | |
b6d1af89 JS |
214 | metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(hdChain.saplingAccountCounter) + "'"; |
215 | metadata.seedFp = hdChain.seedFp; | |
70b4ad2d JG |
216 | // Increment childkey index |
217 | hdChain.saplingAccountCounter++; | |
218 | } while (HaveSaplingSpendingKey(xsk.expsk.full_viewing_key())); | |
219 | ||
220 | // Update the chain model in the database | |
221 | if (fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(hdChain)) | |
222 | throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): Writing HD chain model failed"); | |
223 | ||
224 | auto ivk = xsk.expsk.full_viewing_key().in_viewing_key(); | |
225 | mapSaplingZKeyMetadata[ivk] = metadata; | |
a4ac4fc3 | 226 | |
70b4ad2d JG |
227 | auto addr = xsk.DefaultAddress(); |
228 | if (!AddSaplingZKey(xsk, addr)) { | |
f82a864d | 229 | throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): AddSaplingZKey failed"); |
efb7662d | 230 | } |
f82a864d JG |
231 | // return default sapling payment address. |
232 | return addr; | |
efb7662d JG |
233 | } |
234 | ||
235 | // Add spending key to keystore | |
5175a7f0 | 236 | bool CWallet::AddSaplingZKey( |
70b4ad2d | 237 | const libzcash::SaplingExtendedSpendingKey &sk, |
83c4e360 | 238 | const libzcash::SaplingPaymentAddress &defaultAddr) |
efb7662d JG |
239 | { |
240 | AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata | |
efb7662d | 241 | |
5175a7f0 | 242 | if (!CCryptoKeyStore::AddSaplingSpendingKey(sk, defaultAddr)) { |
efb7662d JG |
243 | return false; |
244 | } | |
f4207d0c JG |
245 | |
246 | if (!fFileBacked) { | |
247 | return true; | |
248 | } | |
efb7662d | 249 | |
c2dc091e | 250 | if (!IsCrypted()) { |
251 | auto ivk = sk.expsk.full_viewing_key().in_viewing_key(); | |
252 | return CWalletDB(strWalletFile).WriteSaplingZKey(ivk, sk, mapSaplingZKeyMetadata[ivk]); | |
253 | } | |
f82a864d | 254 | |
efb7662d JG |
255 | return true; |
256 | } | |
257 | ||
c2dc091e | 258 | // Add payment address -> incoming viewing key map entry |
259 | bool CWallet::AddSaplingIncomingViewingKey( | |
260 | const libzcash::SaplingIncomingViewingKey &ivk, | |
261 | const libzcash::SaplingPaymentAddress &addr) | |
262 | { | |
263 | AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata | |
264 | ||
265 | if (!CCryptoKeyStore::AddSaplingIncomingViewingKey(ivk, addr)) { | |
266 | return false; | |
267 | } | |
268 | ||
269 | if (!fFileBacked) { | |
270 | return true; | |
271 | } | |
272 | ||
273 | if (!IsCrypted()) { | |
274 | return CWalletDB(strWalletFile).WriteSaplingPaymentAddress(addr, ivk); | |
275 | } | |
276 | ||
277 | return true; | |
278 | } | |
279 | ||
efb7662d | 280 | |
c1c45943 | 281 | // Add spending key to keystore and persist to disk |
a0783bb9 | 282 | bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key) |
c1c45943 | 283 | { |
d6b31d59 | 284 | AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata |
c1c45943 S |
285 | auto addr = key.address(); |
286 | ||
25d5e80c | 287 | if (!CCryptoKeyStore::AddSproutSpendingKey(key)) |
c1c45943 S |
288 | return false; |
289 | ||
167cd333 | 290 | // check if we need to remove from viewing keys |
4c775177 JG |
291 | if (HaveSproutViewingKey(addr)) |
292 | RemoveSproutViewingKey(key.viewing_key()); | |
167cd333 | 293 | |
c1c45943 S |
294 | if (!fFileBacked) |
295 | return true; | |
296 | ||
297 | if (!IsCrypted()) { | |
298 | return CWalletDB(strWalletFile).WriteZKey(addr, | |
299 | key, | |
d6b31d59 | 300 | mapSproutZKeyMetadata[addr]); |
c1c45943 S |
301 | } |
302 | return true; | |
303 | } | |
304 | ||
fd61d6f5 | 305 | CPubKey CWallet::GenerateNewKey() |
9976cf07 | 306 | { |
95691680 | 307 | AssertLockHeld(cs_wallet); // mapKeyMetadata |
439e1497 | 308 | bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets |
38067c18 | 309 | |
dfa23b94 PW |
310 | CKey secret; |
311 | secret.MakeNewKey(fCompressed); | |
38067c18 PW |
312 | |
313 | // Compressed public keys were introduced in version 0.6.0 | |
314 | if (fCompressed) | |
439e1497 | 315 | SetMinVersion(FEATURE_COMPRPUBKEY); |
38067c18 | 316 | |
dfa23b94 | 317 | CPubKey pubkey = secret.GetPubKey(); |
d0c41a73 | 318 | assert(secret.VerifyPubKey(pubkey)); |
4addb2c0 PW |
319 | |
320 | // Create new metadata | |
51ed9ec9 | 321 | int64_t nCreationTime = GetTime(); |
4addb2c0 PW |
322 | mapKeyMetadata[pubkey.GetID()] = CKeyMetadata(nCreationTime); |
323 | if (!nTimeFirstKey || nCreationTime < nTimeFirstKey) | |
324 | nTimeFirstKey = nCreationTime; | |
325 | ||
dfa23b94 | 326 | if (!AddKeyPubKey(secret, pubkey)) |
5262fde0 | 327 | throw std::runtime_error("CWallet::GenerateNewKey(): AddKey failed"); |
dfa23b94 | 328 | return pubkey; |
9976cf07 | 329 | } |
e8ef3da7 | 330 | |
4addb2c0 | 331 | bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) |
e8ef3da7 | 332 | { |
95691680 | 333 | AssertLockHeld(cs_wallet); // mapKeyMetadata |
dfa23b94 | 334 | if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) |
acd65016 | 335 | return false; |
ccca27a7 CL |
336 | |
337 | // check if we need to remove from watch-only | |
338 | CScript script; | |
339 | script = GetScriptForDestination(pubkey.GetID()); | |
340 | if (HaveWatchOnly(script)) | |
341 | RemoveWatchOnly(script); | |
342 | ||
e8ef3da7 WL |
343 | if (!fFileBacked) |
344 | return true; | |
dfa23b94 | 345 | if (!IsCrypted()) { |
3869fb89 JG |
346 | return CWalletDB(strWalletFile).WriteKey(pubkey, |
347 | secret.GetPrivKey(), | |
4addb2c0 | 348 | mapKeyMetadata[pubkey.GetID()]); |
dfa23b94 | 349 | } |
84c3c2eb | 350 | return true; |
4e87d341 MC |
351 | } |
352 | ||
3869fb89 | 353 | bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, |
4addb2c0 | 354 | const vector<unsigned char> &vchCryptedSecret) |
4e87d341 | 355 | { |
efb7662d | 356 | |
4e87d341 MC |
357 | if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) |
358 | return false; | |
359 | if (!fFileBacked) | |
360 | return true; | |
96f34cd5 | 361 | { |
f8dcd5ca | 362 | LOCK(cs_wallet); |
96f34cd5 | 363 | if (pwalletdbEncryption) |
3869fb89 JG |
364 | return pwalletdbEncryption->WriteCryptedKey(vchPubKey, |
365 | vchCryptedSecret, | |
4addb2c0 | 366 | mapKeyMetadata[vchPubKey.GetID()]); |
96f34cd5 | 367 | else |
3869fb89 JG |
368 | return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, |
369 | vchCryptedSecret, | |
4addb2c0 | 370 | mapKeyMetadata[vchPubKey.GetID()]); |
96f34cd5 | 371 | } |
0767e691 | 372 | return false; |
4e87d341 MC |
373 | } |
374 | ||
73699cea | 375 | |
25d5e80c JG |
376 | bool CWallet::AddCryptedSproutSpendingKey( |
377 | const libzcash::SproutPaymentAddress &address, | |
378 | const libzcash::ReceivingKey &rk, | |
379 | const std::vector<unsigned char> &vchCryptedSecret) | |
73699cea | 380 | { |
25d5e80c | 381 | if (!CCryptoKeyStore::AddCryptedSproutSpendingKey(address, rk, vchCryptedSecret)) |
73699cea S |
382 | return false; |
383 | if (!fFileBacked) | |
384 | return true; | |
385 | { | |
386 | LOCK(cs_wallet); | |
82bd9ee8 | 387 | if (pwalletdbEncryption) { |
73699cea | 388 | return pwalletdbEncryption->WriteCryptedZKey(address, |
642a1caf | 389 | rk, |
73699cea | 390 | vchCryptedSecret, |
d6b31d59 | 391 | mapSproutZKeyMetadata[address]); |
82bd9ee8 | 392 | } else { |
73699cea | 393 | return CWalletDB(strWalletFile).WriteCryptedZKey(address, |
642a1caf | 394 | rk, |
73699cea | 395 | vchCryptedSecret, |
d6b31d59 | 396 | mapSproutZKeyMetadata[address]); |
82bd9ee8 | 397 | } |
73699cea S |
398 | } |
399 | return false; | |
400 | } | |
401 | ||
c2dc091e | 402 | bool CWallet::AddCryptedSaplingSpendingKey(const libzcash::SaplingExtendedFullViewingKey &extfvk, |
5175a7f0 | 403 | const std::vector<unsigned char> &vchCryptedSecret, |
83c4e360 | 404 | const libzcash::SaplingPaymentAddress &defaultAddr) |
bc6344b3 | 405 | { |
c2dc091e | 406 | if (!CCryptoKeyStore::AddCryptedSaplingSpendingKey(extfvk, vchCryptedSecret, defaultAddr)) |
bc6344b3 JG |
407 | return false; |
408 | if (!fFileBacked) | |
409 | return true; | |
410 | { | |
c2dc091e | 411 | LOCK(cs_wallet); |
412 | if (pwalletdbEncryption) { | |
413 | return pwalletdbEncryption->WriteCryptedSaplingZKey(extfvk, | |
414 | vchCryptedSecret, | |
415 | mapSaplingZKeyMetadata[extfvk.fvk.in_viewing_key()]); | |
416 | } else { | |
417 | return CWalletDB(strWalletFile).WriteCryptedSaplingZKey(extfvk, | |
418 | vchCryptedSecret, | |
419 | mapSaplingZKeyMetadata[extfvk.fvk.in_viewing_key()]); | |
420 | } | |
bc6344b3 JG |
421 | } |
422 | return false; | |
423 | } | |
424 | ||
4addb2c0 PW |
425 | bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta) |
426 | { | |
95691680 | 427 | AssertLockHeld(cs_wallet); // mapKeyMetadata |
4addb2c0 PW |
428 | if (meta.nCreateTime && (!nTimeFirstKey || meta.nCreateTime < nTimeFirstKey)) |
429 | nTimeFirstKey = meta.nCreateTime; | |
430 | ||
431 | mapKeyMetadata[pubkey.GetID()] = meta; | |
432 | return true; | |
433 | } | |
434 | ||
e5eab182 | 435 | bool CWallet::LoadZKeyMetadata(const SproutPaymentAddress &addr, const CKeyMetadata &meta) |
c1c45943 | 436 | { |
d6b31d59 EOW |
437 | AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata |
438 | mapSproutZKeyMetadata[addr] = meta; | |
c1c45943 S |
439 | return true; |
440 | } | |
441 | ||
2f15e86a GA |
442 | bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) |
443 | { | |
444 | return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); | |
445 | } | |
446 | ||
e5eab182 | 447 | bool CWallet::LoadCryptedZKey(const libzcash::SproutPaymentAddress &addr, const libzcash::ReceivingKey &rk, const std::vector<unsigned char> &vchCryptedSecret) |
73699cea | 448 | { |
25d5e80c | 449 | return CCryptoKeyStore::AddCryptedSproutSpendingKey(addr, rk, vchCryptedSecret); |
73699cea S |
450 | } |
451 | ||
c2dc091e | 452 | bool CWallet::LoadCryptedSaplingZKey( |
453 | const libzcash::SaplingExtendedFullViewingKey &extfvk, | |
454 | const std::vector<unsigned char> &vchCryptedSecret) | |
455 | { | |
456 | return CCryptoKeyStore::AddCryptedSaplingSpendingKey(extfvk, vchCryptedSecret, extfvk.DefaultAddress()); | |
457 | } | |
458 | ||
459 | bool CWallet::LoadSaplingZKeyMetadata(const libzcash::SaplingIncomingViewingKey &ivk, const CKeyMetadata &meta) | |
460 | { | |
461 | AssertLockHeld(cs_wallet); // mapSaplingZKeyMetadata | |
462 | mapSaplingZKeyMetadata[ivk] = meta; | |
463 | return true; | |
464 | } | |
465 | ||
466 | bool CWallet::LoadSaplingZKey(const libzcash::SaplingExtendedSpendingKey &key) | |
467 | { | |
468 | return CCryptoKeyStore::AddSaplingSpendingKey(key, key.DefaultAddress()); | |
469 | } | |
470 | ||
471 | bool CWallet::LoadSaplingPaymentAddress( | |
472 | const libzcash::SaplingPaymentAddress &addr, | |
473 | const libzcash::SaplingIncomingViewingKey &ivk) | |
474 | { | |
475 | return CCryptoKeyStore::AddSaplingIncomingViewingKey(ivk, addr); | |
476 | } | |
477 | ||
e5eab182 | 478 | bool CWallet::LoadZKey(const libzcash::SproutSpendingKey &key) |
c1c45943 | 479 | { |
25d5e80c | 480 | return CCryptoKeyStore::AddSproutSpendingKey(key); |
c1c45943 S |
481 | } |
482 | ||
4c775177 | 483 | bool CWallet::AddSproutViewingKey(const libzcash::SproutViewingKey &vk) |
167cd333 | 484 | { |
4c775177 | 485 | if (!CCryptoKeyStore::AddSproutViewingKey(vk)) { |
167cd333 | 486 | return false; |
bec22351 | 487 | } |
167cd333 | 488 | nTimeFirstKey = 1; // No birthday information for viewing keys. |
bec22351 | 489 | if (!fFileBacked) { |
167cd333 | 490 | return true; |
bec22351 | 491 | } |
4c775177 | 492 | return CWalletDB(strWalletFile).WriteSproutViewingKey(vk); |
167cd333 JG |
493 | } |
494 | ||
4c775177 | 495 | bool CWallet::RemoveSproutViewingKey(const libzcash::SproutViewingKey &vk) |
167cd333 JG |
496 | { |
497 | AssertLockHeld(cs_wallet); | |
4c775177 | 498 | if (!CCryptoKeyStore::RemoveSproutViewingKey(vk)) { |
167cd333 | 499 | return false; |
bec22351 JG |
500 | } |
501 | if (fFileBacked) { | |
4c775177 | 502 | if (!CWalletDB(strWalletFile).EraseSproutViewingKey(vk)) { |
167cd333 | 503 | return false; |
bec22351 JG |
504 | } |
505 | } | |
167cd333 JG |
506 | |
507 | return true; | |
508 | } | |
509 | ||
4c775177 | 510 | bool CWallet::LoadSproutViewingKey(const libzcash::SproutViewingKey &vk) |
167cd333 | 511 | { |
4c775177 | 512 | return CCryptoKeyStore::AddSproutViewingKey(vk); |
167cd333 JG |
513 | } |
514 | ||
922e8e29 | 515 | bool CWallet::AddCScript(const CScript& redeemScript) |
e679ec96 | 516 | { |
b7c685b8 | 517 | // if this is an identity, which we currently need to check, we |
518 | // store the ID as a script in the wallet script storage, but instead of using the | |
519 | // hash of the script, we store it under the name ID | |
922e8e29 | 520 | if (!CCryptoKeyStore::AddCScript(redeemScript)) |
e679ec96 GA |
521 | return false; |
522 | if (!fFileBacked) | |
523 | return true; | |
b7c685b8 | 524 | return CWalletDB(strWalletFile).WriteCScript(ScriptOrIdentityID(redeemScript), redeemScript); |
e679ec96 GA |
525 | } |
526 | ||
18116b06 WL |
527 | bool CWallet::LoadCScript(const CScript& redeemScript) |
528 | { | |
529 | /* A sanity check was added in pull #3843 to avoid adding redeemScripts | |
530 | * that never can be redeemed. However, old wallets may still contain | |
531 | * these. Do not add them to the wallet and warn. */ | |
a4f9bc97 | 532 | if (redeemScript.size() > CScript::MAX_SCRIPT_ELEMENT_SIZE) |
18116b06 | 533 | { |
b7c685b8 | 534 | std::string strAddr = EncodeDestination(ScriptOrIdentityID(redeemScript)); |
18116b06 | 535 | LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", |
a4f9bc97 | 536 | __func__, redeemScript.size(), CScript::MAX_SCRIPT_ELEMENT_SIZE, strAddr); |
18116b06 WL |
537 | return true; |
538 | } | |
539 | ||
540 | return CCryptoKeyStore::AddCScript(redeemScript); | |
541 | } | |
542 | ||
5bc89dab | 543 | bool CWallet::AddIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity) |
e14c324c | 544 | { |
545 | // if this is an identity, which we currently need to check, we | |
546 | // store the ID as a script in the wallet script storage, but instead of using the | |
547 | // hash of the script, we store it under the name ID | |
5bc89dab | 548 | if (!CCryptoKeyStore::AddIdentity(mapKey, identity)) |
e14c324c | 549 | return false; |
550 | if (!fFileBacked) | |
551 | return true; | |
5bc89dab | 552 | return CWalletDB(strWalletFile).WriteIdentity(mapKey, identity); |
e14c324c | 553 | } |
554 | ||
5bc89dab | 555 | bool CWallet::UpdateIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity) |
e14c324c | 556 | { |
5bc89dab | 557 | // if this is an identity, which we currently need to check, we |
558 | // store the ID as a script in the wallet script storage, but instead of using the | |
559 | // hash of the script, we store it under the name ID | |
560 | if (!CCryptoKeyStore::UpdateIdentity(mapKey, identity)) | |
e14c324c | 561 | return false; |
5bc89dab | 562 | if (!fFileBacked) |
563 | return true; | |
564 | return CWalletDB(strWalletFile).WriteIdentity(mapKey, identity); | |
e14c324c | 565 | } |
566 | ||
5bc89dab | 567 | bool CWallet::AddUpdateIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity) |
e14c324c | 568 | { |
569 | // if this is an identity, which we currently need to check, we | |
570 | // store the ID as a script in the wallet script storage, but instead of using the | |
571 | // hash of the script, we store it under the name ID | |
5bc89dab | 572 | if (!CCryptoKeyStore::AddUpdateIdentity(mapKey, identity)) |
e14c324c | 573 | return false; |
574 | if (!fFileBacked) | |
575 | return true; | |
5bc89dab | 576 | return CWalletDB(strWalletFile).WriteIdentity(mapKey, identity); |
577 | } | |
578 | ||
e5ace0f4 | 579 | void CWallet::ClearIdentities() |
580 | { | |
581 | if (fFileBacked) | |
582 | { | |
583 | for (auto idPair : mapIdentities) | |
584 | { | |
585 | CWalletDB(strWalletFile).EraseIdentity(idPair.first); | |
586 | } | |
587 | } | |
588 | ||
589 | CCryptoKeyStore::ClearIdentities(); | |
590 | } | |
591 | ||
5bc89dab | 592 | bool CWallet::RemoveIdentity(const CIdentityMapKey &mapKey, const uint256 &txid) |
593 | { | |
594 | CIdentityMapKey localKey = mapKey; | |
595 | std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> toErase; | |
596 | if (localKey.blockHeight == 0) | |
597 | { | |
e54e3c24 | 598 | localKey.blockHeight = INT_MAX; |
5bc89dab | 599 | } |
600 | if (!GetIdentity(mapKey, localKey, toErase)) | |
601 | { | |
602 | return false; | |
603 | } | |
604 | if (!txid.IsNull()) | |
605 | { | |
606 | std::pair<CIdentityMapKey, CIdentityMapValue> idEntry; | |
607 | for (auto id : toErase) | |
608 | { | |
609 | if (id.second.txid == txid) | |
610 | { | |
611 | idEntry = id; | |
612 | } | |
613 | } | |
614 | toErase.clear(); | |
615 | if (idEntry.first.IsValid() && idEntry.second.IsValid()) | |
616 | { | |
617 | toErase.push_back(idEntry); | |
618 | } | |
619 | } | |
620 | if (!CCryptoKeyStore::RemoveIdentity(mapKey, txid)) | |
621 | return false; | |
622 | if (!fFileBacked) | |
623 | return true; | |
624 | ||
625 | bool error = false; | |
626 | for (auto idPair : toErase) | |
627 | { | |
628 | error = CWalletDB(strWalletFile).EraseIdentity(idPair.first) ? error : true; | |
629 | } | |
630 | return error; | |
e14c324c | 631 | } |
632 | ||
5bc89dab | 633 | bool CWallet::LoadIdentity(const CIdentityMapKey &mapKey, const CIdentityMapValue &identity) |
e14c324c | 634 | { |
5bc89dab | 635 | return CCryptoKeyStore::AddUpdateIdentity(mapKey, identity); |
e14c324c | 636 | } |
637 | ||
248084b7 | 638 | // returns all key IDs that are destinations for UTXOs in the wallet |
639 | std::set<CKeyID> CWallet::GetTransactionDestinationIDs() | |
640 | { | |
641 | std::vector<COutput> vecOutputs; | |
642 | std::set<CKeyID> setKeyIDs; | |
643 | ||
20bcd9ba | 644 | AvailableCoins(vecOutputs, false, NULL, true, true, true, true); |
248084b7 | 645 | |
646 | for (int i = 0; i < vecOutputs.size(); i++) | |
647 | { | |
648 | auto &txout = vecOutputs[i]; | |
649 | txnouttype outType; | |
650 | std::vector<CTxDestination> dests; | |
651 | int nRequiredSigs; | |
652 | ||
653 | if (ExtractDestinations(txout.tx->vout[txout.i].scriptPubKey, outType, dests, nRequiredSigs)) | |
654 | { | |
29c07a8f | 655 | CScript scriptPubKey; |
656 | if (outType != TX_SCRIPTHASH || | |
657 | (dests.size() && | |
658 | GetCScript(GetDestinationID(dests[0]), scriptPubKey) && | |
659 | ExtractDestinations(scriptPubKey, outType, dests, nRequiredSigs))) | |
248084b7 | 660 | { |
29c07a8f | 661 | for (auto &dest : dests) |
248084b7 | 662 | { |
29c07a8f | 663 | if (dest.which() == COptCCParams::ADDRTYPE_PK || dest.which() == COptCCParams::ADDRTYPE_PKH) |
664 | { | |
665 | setKeyIDs.insert(GetDestinationID(dest)); | |
666 | } | |
248084b7 | 667 | } |
668 | } | |
669 | } | |
670 | } | |
671 | return setKeyIDs; | |
672 | } | |
673 | ||
d5087d1b | 674 | bool CWallet::AddWatchOnly(const CScript &dest) |
c8988460 PW |
675 | { |
676 | if (!CCryptoKeyStore::AddWatchOnly(dest)) | |
677 | return false; | |
678 | nTimeFirstKey = 1; // No birthday information for watch-only keys. | |
939ed973 | 679 | NotifyWatchonlyChanged(true); |
c8988460 PW |
680 | if (!fFileBacked) |
681 | return true; | |
682 | return CWalletDB(strWalletFile).WriteWatchOnly(dest); | |
683 | } | |
684 | ||
ccca27a7 CL |
685 | bool CWallet::RemoveWatchOnly(const CScript &dest) |
686 | { | |
687 | AssertLockHeld(cs_wallet); | |
688 | if (!CCryptoKeyStore::RemoveWatchOnly(dest)) | |
689 | return false; | |
690 | if (!HaveWatchOnly()) | |
691 | NotifyWatchonlyChanged(false); | |
692 | if (fFileBacked) | |
693 | if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) | |
694 | return false; | |
695 | ||
696 | return true; | |
697 | } | |
698 | ||
d5087d1b | 699 | bool CWallet::LoadWatchOnly(const CScript &dest) |
c8988460 | 700 | { |
c8988460 PW |
701 | return CCryptoKeyStore::AddWatchOnly(dest); |
702 | } | |
703 | ||
94f778bd | 704 | bool CWallet::Unlock(const SecureString& strWalletPassphrase) |
4e87d341 | 705 | { |
6cc4a62c GA |
706 | CCrypter crypter; |
707 | CKeyingMaterial vMasterKey; | |
4e87d341 | 708 | |
f8dcd5ca PW |
709 | { |
710 | LOCK(cs_wallet); | |
4e87d341 MC |
711 | BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) |
712 | { | |
713 | if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) | |
714 | return false; | |
715 | if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) | |
92f2c1fe | 716 | continue; // try another master key |
1f561f32 JG |
717 | if (CCryptoKeyStore::Unlock(vMasterKey)) { |
718 | // Now that the wallet is decrypted, ensure we have an HD seed. | |
719 | // https://github.com/zcash/zcash/issues/3607 | |
720 | if (!this->HaveHDSeed()) { | |
721 | this->GenerateNewSeed(); | |
722 | } | |
4e87d341 | 723 | return true; |
1f561f32 | 724 | } |
4e87d341 | 725 | } |
f8dcd5ca | 726 | } |
4e87d341 MC |
727 | return false; |
728 | } | |
729 | ||
94f778bd | 730 | bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) |
4e87d341 | 731 | { |
6cc4a62c | 732 | bool fWasLocked = IsLocked(); |
4e87d341 | 733 | |
6cc4a62c | 734 | { |
f8dcd5ca | 735 | LOCK(cs_wallet); |
4e87d341 MC |
736 | Lock(); |
737 | ||
738 | CCrypter crypter; | |
739 | CKeyingMaterial vMasterKey; | |
740 | BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) | |
741 | { | |
742 | if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) | |
743 | return false; | |
6cc4a62c | 744 | if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) |
4e87d341 MC |
745 | return false; |
746 | if (CCryptoKeyStore::Unlock(vMasterKey)) | |
747 | { | |
51ed9ec9 | 748 | int64_t nStartTime = GetTimeMillis(); |
ddebdd9a MC |
749 | crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); |
750 | pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); | |
751 | ||
752 | nStartTime = GetTimeMillis(); | |
753 | crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); | |
754 | pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; | |
755 | ||
756 | if (pMasterKey.second.nDeriveIterations < 25000) | |
757 | pMasterKey.second.nDeriveIterations = 25000; | |
758 | ||
881a85a2 | 759 | LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); |
ddebdd9a | 760 | |
4e87d341 MC |
761 | if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) |
762 | return false; | |
763 | if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) | |
764 | return false; | |
765 | CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); | |
766 | if (fWasLocked) | |
767 | Lock(); | |
768 | return true; | |
769 | } | |
770 | } | |
771 | } | |
6cc4a62c | 772 | |
4e87d341 MC |
773 | return false; |
774 | } | |
775 | ||
162bfc3a E |
776 | void CWallet::ChainTipAdded(const CBlockIndex *pindex, |
777 | const CBlock *pblock, | |
778 | SproutMerkleTree sproutTree, | |
779 | SaplingMerkleTree saplingTree) | |
780 | { | |
781 | IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree); | |
782 | UpdateSaplingNullifierNoteMapForBlock(pblock); | |
783 | } | |
784 | ||
f86ee1c2 EOW |
785 | void CWallet::ChainTip(const CBlockIndex *pindex, |
786 | const CBlock *pblock, | |
4fc309f0 EOW |
787 | SproutMerkleTree sproutTree, |
788 | SaplingMerkleTree saplingTree, | |
f86ee1c2 | 789 | bool added) |
769e031c JG |
790 | { |
791 | if (added) { | |
162bfc3a | 792 | ChainTipAdded(pindex, pblock, sproutTree, saplingTree); |
6921c81b S |
793 | // Prevent migration transactions from being created when node is syncing after launch, |
794 | // and also when node wakes up from suspension/hibernation and incoming blocks are old. | |
3ffc29b8 | 795 | if (!IsInitialBlockDownload(Params()) && |
6921c81b S |
796 | pblock->GetBlockTime() > GetAdjustedTime() - 3 * 60 * 60) |
797 | { | |
88d014d0 | 798 | RunSaplingMigration(pindex->GetHeight()); |
6921c81b | 799 | } |
769e031c | 800 | } else { |
40ef121e | 801 | DecrementNoteWitnesses(pindex); |
162bfc3a | 802 | UpdateSaplingNullifierNoteMapForBlock(pblock); |
769e031c JG |
803 | } |
804 | } | |
805 | ||
81a45d69 | 806 | void CWallet::RunSaplingMigration(int blockHeight) { |
2c6c5526 | 807 | if (!Params().GetConsensus().NetworkUpgradeActive(blockHeight, Consensus::UPGRADE_SAPLING)) { |
81a45d69 E |
808 | return; |
809 | } | |
810 | LOCK(cs_wallet); | |
162bfc3a E |
811 | if (!fSaplingMigrationEnabled) { |
812 | return; | |
813 | } | |
81a45d69 E |
814 | // The migration transactions to be sent in a particular batch can take |
815 | // significant time to generate, and this time depends on the speed of the user's | |
816 | // computer. If they were generated only after a block is seen at the target | |
817 | // height minus 1, then this could leak information. Therefore, for target | |
818 | // height N, implementations SHOULD start generating the transactions at around | |
819 | // height N-5 | |
820 | if (blockHeight % 500 == 495) { | |
d48c3efc | 821 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
7470ae88 | 822 | std::shared_ptr<AsyncRPCOperation> lastOperation = q->getOperationForId(saplingMigrationOperationId); |
d48c3efc E |
823 | if (lastOperation != nullptr) { |
824 | lastOperation->cancel(); | |
81a45d69 E |
825 | } |
826 | pendingSaplingMigrationTxs.clear(); | |
81a45d69 | 827 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_saplingmigration(blockHeight + 5)); |
d48c3efc | 828 | saplingMigrationOperationId = operation->getId(); |
81a45d69 E |
829 | q->addOperation(operation); |
830 | } else if (blockHeight % 500 == 499) { | |
d48c3efc | 831 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
7470ae88 | 832 | std::shared_ptr<AsyncRPCOperation> lastOperation = q->getOperationForId(saplingMigrationOperationId); |
d48c3efc E |
833 | if (lastOperation != nullptr) { |
834 | lastOperation->cancel(); | |
7c4ad6e2 | 835 | } |
81a45d69 | 836 | for (const CTransaction& transaction : pendingSaplingMigrationTxs) { |
81a45d69 | 837 | // Send the transaction |
3220d993 E |
838 | CWalletTx wtx(this, transaction); |
839 | CommitTransaction(wtx, boost::none); | |
81a45d69 E |
840 | } |
841 | pendingSaplingMigrationTxs.clear(); | |
842 | } | |
843 | } | |
844 | ||
845 | void CWallet::AddPendingSaplingMigrationTx(const CTransaction& tx) { | |
7c4ad6e2 | 846 | LOCK(cs_wallet); |
81a45d69 | 847 | pendingSaplingMigrationTxs.push_back(tx); |
769e031c JG |
848 | } |
849 | ||
ed6d0b5f PW |
850 | void CWallet::SetBestChain(const CBlockLocator& loc) |
851 | { | |
852 | CWalletDB walletdb(strWalletFile); | |
03f83b9b | 853 | SetBestChainINTERNAL(walletdb, loc); |
ed6d0b5f | 854 | } |
7414733b | 855 | |
e4f0d6a8 LR |
856 | std::set<std::pair<libzcash::PaymentAddress, uint256>> CWallet::GetNullifiersForAddresses( |
857 | const std::set<libzcash::PaymentAddress> & addresses) | |
0646f749 EOW |
858 | { |
859 | std::set<std::pair<libzcash::PaymentAddress, uint256>> nullifierSet; | |
e4f0d6a8 LR |
860 | // Sapling ivk -> list of addrs map |
861 | // (There may be more than one diversified address for a given ivk.) | |
862 | std::map<libzcash::SaplingIncomingViewingKey, std::vector<libzcash::SaplingPaymentAddress>> ivkMap; | |
863 | for (const auto & addr : addresses) { | |
864 | auto saplingAddr = boost::get<libzcash::SaplingPaymentAddress>(&addr); | |
865 | if (saplingAddr != nullptr) { | |
866 | libzcash::SaplingIncomingViewingKey ivk; | |
867 | this->GetSaplingIncomingViewingKey(*saplingAddr, ivk); | |
868 | ivkMap[ivk].push_back(*saplingAddr); | |
869 | } | |
870 | } | |
0646f749 | 871 | for (const auto & txPair : mapWallet) { |
e4f0d6a8 | 872 | // Sprout |
2f0b2a25 | 873 | for (const auto & noteDataPair : txPair.second.mapSproutNoteData) { |
e4f0d6a8 LR |
874 | auto & noteData = noteDataPair.second; |
875 | auto & nullifier = noteData.nullifier; | |
876 | auto & address = noteData.address; | |
877 | if (nullifier && addresses.count(address)) { | |
878 | nullifierSet.insert(std::make_pair(address, nullifier.get())); | |
879 | } | |
880 | } | |
881 | // Sapling | |
882 | for (const auto & noteDataPair : txPair.second.mapSaplingNoteData) { | |
883 | auto & noteData = noteDataPair.second; | |
884 | auto & nullifier = noteData.nullifier; | |
885 | auto & ivk = noteData.ivk; | |
886 | if (nullifier && ivkMap.count(ivk)) { | |
887 | for (const auto & addr : ivkMap[ivk]) { | |
888 | nullifierSet.insert(std::make_pair(addr, nullifier.get())); | |
889 | } | |
0646f749 EOW |
890 | } |
891 | } | |
892 | } | |
893 | return nullifierSet; | |
894 | } | |
895 | ||
e4f0d6a8 LR |
896 | bool CWallet::IsNoteSproutChange( |
897 | const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet, | |
898 | const PaymentAddress & address, | |
899 | const JSOutPoint & jsop) | |
0646f749 EOW |
900 | { |
901 | // A Note is marked as "change" if the address that received it | |
902 | // also spent Notes in the same transaction. This will catch, | |
903 | // for instance: | |
904 | // - Change created by spending fractions of Notes (because | |
905 | // z_sendmany sends change to the originating z-address). | |
906 | // - "Chaining Notes" used to connect JoinSplits together. | |
907 | // - Notes created by consolidation transactions (e.g. using | |
908 | // z_mergetoaddress). | |
909 | // - Notes sent from one address to itself. | |
f57f76d7 | 910 | for (const JSDescription & jsd : mapWallet[jsop.hash].vJoinSplit) { |
0646f749 EOW |
911 | for (const uint256 & nullifier : jsd.nullifiers) { |
912 | if (nullifierSet.count(std::make_pair(address, nullifier))) { | |
913 | return true; | |
914 | } | |
915 | } | |
916 | } | |
917 | return false; | |
918 | } | |
919 | ||
e4f0d6a8 LR |
920 | bool CWallet::IsNoteSaplingChange(const std::set<std::pair<libzcash::PaymentAddress, uint256>> & nullifierSet, |
921 | const libzcash::PaymentAddress & address, | |
922 | const SaplingOutPoint & op) | |
923 | { | |
924 | // A Note is marked as "change" if the address that received it | |
925 | // also spent Notes in the same transaction. This will catch, | |
926 | // for instance: | |
927 | // - Change created by spending fractions of Notes (because | |
928 | // z_sendmany sends change to the originating z-address). | |
929 | // - Notes created by consolidation transactions (e.g. using | |
930 | // z_mergetoaddress). | |
931 | // - Notes sent from one address to itself. | |
932 | for (const SpendDescription &spend : mapWallet[op.hash].vShieldedSpend) { | |
933 | if (nullifierSet.count(std::make_pair(address, spend.nullifier))) { | |
934 | return true; | |
935 | } | |
936 | } | |
937 | return false; | |
938 | } | |
939 | ||
439e1497 | 940 | bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) |
0b807a41 | 941 | { |
ca4cf5cf | 942 | LOCK(cs_wallet); // nWalletVersion |
0b807a41 PW |
943 | if (nWalletVersion >= nVersion) |
944 | return true; | |
945 | ||
439e1497 PW |
946 | // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way |
947 | if (fExplicit && nVersion > nWalletMaxVersion) | |
948 | nVersion = FEATURE_LATEST; | |
949 | ||
0b807a41 PW |
950 | nWalletVersion = nVersion; |
951 | ||
439e1497 PW |
952 | if (nVersion > nWalletMaxVersion) |
953 | nWalletMaxVersion = nVersion; | |
954 | ||
0b807a41 PW |
955 | if (fFileBacked) |
956 | { | |
957 | CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); | |
0b807a41 PW |
958 | if (nWalletVersion > 40000) |
959 | pwalletdb->WriteMinVersion(nWalletVersion); | |
960 | if (!pwalletdbIn) | |
961 | delete pwalletdb; | |
962 | } | |
963 | ||
964 | return true; | |
965 | } | |
966 | ||
439e1497 PW |
967 | bool CWallet::SetMaxVersion(int nVersion) |
968 | { | |
ca4cf5cf | 969 | LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion |
439e1497 PW |
970 | // cannot downgrade below current version |
971 | if (nWalletVersion > nVersion) | |
972 | return false; | |
973 | ||
974 | nWalletMaxVersion = nVersion; | |
975 | ||
976 | return true; | |
977 | } | |
978 | ||
3015e0bc | 979 | set<uint256> CWallet::GetConflicts(const uint256& txid) const |
731b89b8 GA |
980 | { |
981 | set<uint256> result; | |
982 | AssertLockHeld(cs_wallet); | |
983 | ||
984 | std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid); | |
985 | if (it == mapWallet.end()) | |
986 | return result; | |
987 | const CWalletTx& wtx = it->second; | |
988 | ||
93a18a36 | 989 | std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; |
731b89b8 GA |
990 | |
991 | BOOST_FOREACH(const CTxIn& txin, wtx.vin) | |
992 | { | |
93a18a36 GA |
993 | if (mapTxSpends.count(txin.prevout) <= 1) |
994 | continue; // No conflict if zero or one spends | |
995 | range = mapTxSpends.equal_range(txin.prevout); | |
996 | for (TxSpends::const_iterator it = range.first; it != range.second; ++it) | |
3015e0bc | 997 | result.insert(it->second); |
731b89b8 | 998 | } |
0f106047 JG |
999 | |
1000 | std::pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range_n; | |
1001 | ||
f57f76d7 | 1002 | for (const JSDescription& jsdesc : wtx.vJoinSplit) { |
0f106047 | 1003 | for (const uint256& nullifier : jsdesc.nullifiers) { |
3438e26c | 1004 | if (mapTxSproutNullifiers.count(nullifier) <= 1) { |
0f106047 JG |
1005 | continue; // No conflict if zero or one spends |
1006 | } | |
3438e26c | 1007 | range_n = mapTxSproutNullifiers.equal_range(nullifier); |
0f106047 JG |
1008 | for (TxNullifiers::const_iterator it = range_n.first; it != range_n.second; ++it) { |
1009 | result.insert(it->second); | |
1010 | } | |
1011 | } | |
1012 | } | |
3afc6ce2 S |
1013 | |
1014 | std::pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range_o; | |
1015 | ||
1016 | for (const SpendDescription &spend : wtx.vShieldedSpend) { | |
1017 | uint256 nullifier = spend.nullifier; | |
1018 | if (mapTxSaplingNullifiers.count(nullifier) <= 1) { | |
1019 | continue; // No conflict if zero or one spends | |
1020 | } | |
1021 | range_o = mapTxSaplingNullifiers.equal_range(nullifier); | |
1022 | for (TxNullifiers::const_iterator it = range_o.first; it != range_o.second; ++it) { | |
1023 | result.insert(it->second); | |
1024 | } | |
0f106047 | 1025 | } |
731b89b8 GA |
1026 | return result; |
1027 | } | |
1028 | ||
2bb1c877 JS |
1029 | void CWallet::Flush(bool shutdown) |
1030 | { | |
1031 | bitdb.Flush(shutdown); | |
1032 | } | |
1033 | ||
341e2385 | 1034 | bool CWallet::Verify(const string& walletFile, string& warningString, string& errorString) |
2bb1c877 JS |
1035 | { |
1036 | if (!bitdb.Open(GetDataDir())) | |
1037 | { | |
1038 | // try moving the database env out of the way | |
1039 | boost::filesystem::path pathDatabase = GetDataDir() / "database"; | |
1040 | boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); | |
1041 | try { | |
1042 | boost::filesystem::rename(pathDatabase, pathDatabaseBak); | |
1043 | LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); | |
1044 | } catch (const boost::filesystem::filesystem_error&) { | |
1045 | // failure is ok (well, not really, but it's not worse than what we started with) | |
1046 | } | |
efb7662d | 1047 | |
2bb1c877 JS |
1048 | // try again |
1049 | if (!bitdb.Open(GetDataDir())) { | |
1050 | // if it still fails, it probably means we can't even create the database env | |
1051 | string msg = strprintf(_("Error initializing wallet database environment %s!"), GetDataDir()); | |
1052 | errorString += msg; | |
1053 | return true; | |
1054 | } | |
1055 | } | |
efb7662d | 1056 | |
2bb1c877 JS |
1057 | if (GetBoolArg("-salvagewallet", false)) |
1058 | { | |
1059 | // Recover readable keypairs: | |
1060 | if (!CWalletDB::Recover(bitdb, walletFile, true)) | |
1061 | return false; | |
1062 | } | |
efb7662d | 1063 | |
2bb1c877 JS |
1064 | if (boost::filesystem::exists(GetDataDir() / walletFile)) |
1065 | { | |
1066 | CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); | |
1067 | if (r == CDBEnv::RECOVER_OK) | |
1068 | { | |
1069 | warningString += strprintf(_("Warning: wallet.dat corrupt, data salvaged!" | |
1070 | " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" | |
1071 | " your balance or transactions are incorrect you should" | |
1072 | " restore from a backup."), GetDataDir()); | |
1073 | } | |
1074 | if (r == CDBEnv::RECOVER_FAIL) | |
1075 | errorString += _("wallet.dat corrupt, salvage failed"); | |
1076 | } | |
efb7662d | 1077 | |
2bb1c877 JS |
1078 | return true; |
1079 | } | |
1080 | ||
0f106047 JG |
1081 | template <class T> |
1082 | void CWallet::SyncMetaData(pair<typename TxSpendMap<T>::iterator, typename TxSpendMap<T>::iterator> range) | |
731b89b8 GA |
1083 | { |
1084 | // We want all the wallet transactions in range to have the same metadata as | |
1085 | // the oldest (smallest nOrderPos). | |
1086 | // So: find smallest nOrderPos: | |
1087 | ||
1088 | int nMinOrderPos = std::numeric_limits<int>::max(); | |
1089 | const CWalletTx* copyFrom = NULL; | |
0f106047 | 1090 | for (typename TxSpendMap<T>::iterator it = range.first; it != range.second; ++it) |
731b89b8 GA |
1091 | { |
1092 | const uint256& hash = it->second; | |
1093 | int n = mapWallet[hash].nOrderPos; | |
1094 | if (n < nMinOrderPos) | |
1095 | { | |
1096 | nMinOrderPos = n; | |
1097 | copyFrom = &mapWallet[hash]; | |
1098 | } | |
1099 | } | |
1100 | // Now copy data from copyFrom to rest: | |
0f106047 | 1101 | for (typename TxSpendMap<T>::iterator it = range.first; it != range.second; ++it) |
731b89b8 GA |
1102 | { |
1103 | const uint256& hash = it->second; | |
1104 | CWalletTx* copyTo = &mapWallet[hash]; | |
1105 | if (copyFrom == copyTo) continue; | |
1106 | copyTo->mapValue = copyFrom->mapValue; | |
303f80fb | 1107 | // mapSproutNoteData and mapSaplingNoteData not copied on purpose |
c3a7307a | 1108 | // (it is always set correctly for each CWalletTx) |
731b89b8 GA |
1109 | copyTo->vOrderForm = copyFrom->vOrderForm; |
1110 | // fTimeReceivedIsTxTime not copied on purpose | |
1111 | // nTimeReceived not copied on purpose | |
1112 | copyTo->nTimeSmart = copyFrom->nTimeSmart; | |
1113 | copyTo->fFromMe = copyFrom->fFromMe; | |
1114 | copyTo->strFromAccount = copyFrom->strFromAccount; | |
731b89b8 GA |
1115 | // nOrderPos not copied on purpose |
1116 | // cached members not copied on purpose | |
1117 | } | |
1118 | } | |
1119 | ||
5b40d886 MF |
1120 | /** |
1121 | * Outpoint is spent if any non-conflicted transaction | |
1122 | * spends it: | |
1123 | */ | |
93a18a36 | 1124 | bool CWallet::IsSpent(const uint256& hash, unsigned int n) const |
731b89b8 | 1125 | { |
93a18a36 GA |
1126 | const COutPoint outpoint(hash, n); |
1127 | pair<TxSpends::const_iterator, TxSpends::const_iterator> range; | |
1128 | range = mapTxSpends.equal_range(outpoint); | |
731b89b8 | 1129 | |
93a18a36 | 1130 | for (TxSpends::const_iterator it = range.first; it != range.second; ++it) |
731b89b8 | 1131 | { |
93a18a36 GA |
1132 | const uint256& wtxid = it->second; |
1133 | std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); | |
1134 | if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) | |
1135 | return true; // Spent | |
731b89b8 | 1136 | } |
93a18a36 GA |
1137 | return false; |
1138 | } | |
1139 | ||
0f106047 JG |
1140 | /** |
1141 | * Note is spent if any non-conflicted transaction | |
1142 | * spends it: | |
1143 | */ | |
3b6dd486 | 1144 | bool CWallet::IsSproutSpent(const uint256& nullifier) const { |
0f106047 | 1145 | pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range; |
3438e26c | 1146 | range = mapTxSproutNullifiers.equal_range(nullifier); |
0f106047 JG |
1147 | |
1148 | for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) { | |
1149 | const uint256& wtxid = it->second; | |
1150 | std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); | |
1151 | if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) { | |
1152 | return true; // Spent | |
1153 | } | |
1154 | } | |
1155 | return false; | |
1156 | } | |
1157 | ||
3b6dd486 S |
1158 | bool CWallet::IsSaplingSpent(const uint256& nullifier) const { |
1159 | pair<TxNullifiers::const_iterator, TxNullifiers::const_iterator> range; | |
edfc6a78 S |
1160 | range = mapTxSaplingNullifiers.equal_range(nullifier); |
1161 | ||
1162 | for (TxNullifiers::const_iterator it = range.first; it != range.second; ++it) { | |
1163 | const uint256& wtxid = it->second; | |
0f106047 JG |
1164 | std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); |
1165 | if (mit != mapWallet.end() && mit->second.GetDepthInMainChain() >= 0) { | |
1166 | return true; // Spent | |
1167 | } | |
1168 | } | |
1169 | return false; | |
1170 | } | |
1171 | ||
d5e490d9 | 1172 | void CWallet::AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid) |
93a18a36 GA |
1173 | { |
1174 | mapTxSpends.insert(make_pair(outpoint, wtxid)); | |
1175 | ||
1176 | pair<TxSpends::iterator, TxSpends::iterator> range; | |
1177 | range = mapTxSpends.equal_range(outpoint); | |
0f106047 | 1178 | SyncMetaData<COutPoint>(range); |
93a18a36 GA |
1179 | } |
1180 | ||
d5e490d9 | 1181 | void CWallet::AddToSproutSpends(const uint256& nullifier, const uint256& wtxid) |
0f106047 | 1182 | { |
3438e26c | 1183 | mapTxSproutNullifiers.insert(make_pair(nullifier, wtxid)); |
0f106047 JG |
1184 | |
1185 | pair<TxNullifiers::iterator, TxNullifiers::iterator> range; | |
3438e26c | 1186 | range = mapTxSproutNullifiers.equal_range(nullifier); |
0f106047 JG |
1187 | SyncMetaData<uint256>(range); |
1188 | } | |
93a18a36 | 1189 | |
c343e2db S |
1190 | void CWallet::AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid) |
1191 | { | |
1192 | mapTxSaplingNullifiers.insert(make_pair(nullifier, wtxid)); | |
1193 | ||
1194 | pair<TxNullifiers::iterator, TxNullifiers::iterator> range; | |
1195 | range = mapTxSaplingNullifiers.equal_range(nullifier); | |
0f106047 JG |
1196 | SyncMetaData<uint256>(range); |
1197 | } | |
93a18a36 GA |
1198 | |
1199 | void CWallet::AddToSpends(const uint256& wtxid) | |
1200 | { | |
1201 | assert(mapWallet.count(wtxid)); | |
1202 | CWalletTx& thisTx = mapWallet[wtxid]; | |
1203 | if (thisTx.IsCoinBase()) // Coinbases don't spend anything! | |
1204 | return; | |
1205 | ||
0f106047 | 1206 | for (const CTxIn& txin : thisTx.vin) { |
d5e490d9 | 1207 | AddToTransparentSpends(txin.prevout, wtxid); |
0f106047 | 1208 | } |
f57f76d7 | 1209 | for (const JSDescription& jsdesc : thisTx.vJoinSplit) { |
0f106047 | 1210 | for (const uint256& nullifier : jsdesc.nullifiers) { |
d5e490d9 | 1211 | AddToSproutSpends(nullifier, wtxid); |
0f106047 JG |
1212 | } |
1213 | } | |
52332fb4 S |
1214 | for (const SpendDescription &spend : thisTx.vShieldedSpend) { |
1215 | AddToSaplingSpends(spend.nullifier, wtxid); | |
1216 | } | |
731b89b8 GA |
1217 | } |
1218 | ||
76b22658 JG |
1219 | void CWallet::ClearNoteWitnessCache() |
1220 | { | |
1221 | LOCK(cs_wallet); | |
1222 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
005f3ad1 | 1223 | for (mapSproutNoteData_t::value_type& item : wtxItem.second.mapSproutNoteData) { |
40600f50 | 1224 | item.second.witnesses.clear(); |
a4ef3aa9 | 1225 | item.second.witnessHeight = -1; |
76b22658 | 1226 | } |
403b9b4e | 1227 | for (mapSaplingNoteData_t::value_type& item : wtxItem.second.mapSaplingNoteData) { |
40600f50 | 1228 | item.second.witnesses.clear(); |
a4ef3aa9 | 1229 | item.second.witnessHeight = -1; |
76b22658 JG |
1230 | } |
1231 | } | |
a4ef3aa9 | 1232 | nWitnessCacheSize = 0; |
a7f86bc7 | 1233 | //fprintf(stderr,"Clear witness cache\n"); |
76b22658 JG |
1234 | } |
1235 | ||
b5380248 EOW |
1236 | template<typename NoteDataMap> |
1237 | void CopyPreviousWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize) | |
1238 | { | |
1239 | for (auto& item : noteDataMap) { | |
1240 | auto* nd = &(item.second); | |
1241 | // Only increment witnesses that are behind the current height | |
1242 | if (nd->witnessHeight < indexHeight) { | |
1243 | // Check the validity of the cache | |
1244 | // The only time a note witnessed above the current height | |
1245 | // would be invalid here is during a reindex when blocks | |
1246 | // have been decremented, and we are incrementing the blocks | |
1247 | // immediately after. | |
1248 | assert(nWitnessCacheSize >= nd->witnesses.size()); | |
1249 | // Witnesses being incremented should always be either -1 | |
1250 | // (never incremented or decremented) or one below indexHeight | |
1251 | assert((nd->witnessHeight == -1) || (nd->witnessHeight == indexHeight - 1)); | |
1252 | // Copy the witness for the previous block if we have one | |
1253 | if (nd->witnesses.size() > 0) { | |
1254 | nd->witnesses.push_front(nd->witnesses.front()); | |
1255 | } | |
1256 | if (nd->witnesses.size() > WITNESS_CACHE_SIZE) { | |
1257 | nd->witnesses.pop_back(); | |
be74c80d JG |
1258 | } |
1259 | } | |
b5380248 EOW |
1260 | } |
1261 | } | |
1262 | ||
f6d0d5ec EOW |
1263 | template<typename NoteDataMap> |
1264 | void AppendNoteCommitment(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize, const uint256& note_commitment) | |
1265 | { | |
1266 | for (auto& item : noteDataMap) { | |
1267 | auto* nd = &(item.second); | |
1268 | if (nd->witnessHeight < indexHeight && nd->witnesses.size() > 0) { | |
1269 | // Check the validity of the cache | |
1270 | // See comment in CopyPreviousWitnesses about validity. | |
1271 | assert(nWitnessCacheSize >= nd->witnesses.size()); | |
1272 | nd->witnesses.front().append(note_commitment); | |
8a7d37be | 1273 | } |
f6d0d5ec EOW |
1274 | } |
1275 | } | |
be74c80d | 1276 | |
f6d0d5ec EOW |
1277 | template<typename OutPoint, typename NoteData, typename Witness> |
1278 | void WitnessNoteIfMine(std::map<OutPoint, NoteData>& noteDataMap, int indexHeight, int64_t nWitnessCacheSize, const OutPoint& key, const Witness& witness) | |
1279 | { | |
1280 | if (noteDataMap.count(key) && noteDataMap[key].witnessHeight < indexHeight) { | |
1281 | auto* nd = &(noteDataMap[key]); | |
1282 | if (nd->witnesses.size() > 0) { | |
1283 | // We think this can happen because we write out the | |
1284 | // witness cache state after every block increment or | |
1285 | // decrement, but the block index itself is written in | |
1286 | // batches. So if the node crashes in between these two | |
1287 | // operations, it is possible for IncrementNoteWitnesses | |
1288 | // to be called again on previously-cached blocks. This | |
1289 | // doesn't affect existing cached notes because of the | |
1290 | // NoteData::witnessHeight checks. See #1378 for details. | |
1291 | LogPrintf("Inconsistent witness cache state found for %s\n- Cache size: %d\n- Top (height %d): %s\n- New (height %d): %s\n", | |
1292 | key.ToString(), nd->witnesses.size(), | |
1293 | nd->witnessHeight, | |
1294 | nd->witnesses.front().root().GetHex(), | |
1295 | indexHeight, | |
1296 | witness.root().GetHex()); | |
1297 | nd->witnesses.clear(); | |
be74c80d | 1298 | } |
f6d0d5ec EOW |
1299 | nd->witnesses.push_front(witness); |
1300 | // Set height to one less than pindex so it gets incremented | |
1301 | nd->witnessHeight = indexHeight - 1; | |
1302 | // Check the validity of the cache | |
1303 | assert(nWitnessCacheSize >= nd->witnesses.size()); | |
1304 | } | |
1305 | } | |
be74c80d | 1306 | |
be74c80d | 1307 | |
4a0bc604 EOW |
1308 | template<typename NoteDataMap> |
1309 | void UpdateWitnessHeights(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize) | |
1310 | { | |
1311 | for (auto& item : noteDataMap) { | |
1312 | auto* nd = &(item.second); | |
1313 | if (nd->witnessHeight < indexHeight) { | |
1314 | nd->witnessHeight = indexHeight; | |
1315 | // Check the validity of the cache | |
1316 | // See comment in CopyPreviousWitnesses about validity. | |
1317 | assert(nWitnessCacheSize >= nd->witnesses.size()); | |
1318 | } | |
1319 | } | |
1320 | } | |
be74c80d | 1321 | |
be74c80d JG |
1322 | void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, |
1323 | const CBlock* pblockIn, | |
4fc309f0 EOW |
1324 | SproutMerkleTree& sproutTree, |
1325 | SaplingMerkleTree& saplingTree) | |
be74c80d | 1326 | { |
49695a97 EOW |
1327 | LOCK(cs_wallet); |
1328 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
4b729ec5 | 1329 | ::CopyPreviousWitnesses(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize); |
1330 | ::CopyPreviousWitnesses(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize); | |
49695a97 | 1331 | } |
b5380248 | 1332 | |
49695a97 EOW |
1333 | if (nWitnessCacheSize < WITNESS_CACHE_SIZE) { |
1334 | nWitnessCacheSize += 1; | |
1335 | } | |
be74c80d | 1336 | |
49695a97 EOW |
1337 | const CBlock* pblock {pblockIn}; |
1338 | CBlock block; | |
1339 | if (!pblock) { | |
71cf6ba9 | 1340 | ReadBlockFromDisk(block, pindex, Params().GetConsensus()); |
49695a97 EOW |
1341 | pblock = █ |
1342 | } | |
be74c80d | 1343 | |
49695a97 EOW |
1344 | for (const CTransaction& tx : pblock->vtx) { |
1345 | auto hash = tx.GetHash(); | |
1346 | bool txIsOurs = mapWallet.count(hash); | |
45de2eda | 1347 | // Sprout |
f57f76d7 DA |
1348 | for (size_t i = 0; i < tx.vJoinSplit.size(); i++) { |
1349 | const JSDescription& jsdesc = tx.vJoinSplit[i]; | |
49695a97 EOW |
1350 | for (uint8_t j = 0; j < jsdesc.commitments.size(); j++) { |
1351 | const uint256& note_commitment = jsdesc.commitments[j]; | |
f86ee1c2 | 1352 | sproutTree.append(note_commitment); |
be74c80d | 1353 | |
49695a97 EOW |
1354 | // Increment existing witnesses |
1355 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
4b729ec5 | 1356 | ::AppendNoteCommitment(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize, note_commitment); |
be74c80d | 1357 | } |
b6961fc1 | 1358 | |
49695a97 EOW |
1359 | // If this is our note, witness it |
1360 | if (txIsOurs) { | |
1361 | JSOutPoint jsoutpt {hash, i, j}; | |
4b729ec5 | 1362 | ::WitnessNoteIfMine(mapWallet[hash].mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize, jsoutpt, sproutTree.witness()); |
be74c80d JG |
1363 | } |
1364 | } | |
1365 | } | |
45de2eda EOW |
1366 | // Sapling |
1367 | for (uint32_t i = 0; i < tx.vShieldedOutput.size(); i++) { | |
1368 | const uint256& note_commitment = tx.vShieldedOutput[i].cm; | |
1369 | saplingTree.append(note_commitment); | |
1370 | ||
1371 | // Increment existing witnesses | |
1372 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
4b729ec5 | 1373 | ::AppendNoteCommitment(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize, note_commitment); |
45de2eda | 1374 | } |
b6961fc1 | 1375 | |
45de2eda EOW |
1376 | // If this is our note, witness it |
1377 | if (txIsOurs) { | |
1378 | SaplingOutPoint outPoint {hash, i}; | |
4b729ec5 | 1379 | ::WitnessNoteIfMine(mapWallet[hash].mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize, outPoint, saplingTree.witness()); |
83d7b5b6 JG |
1380 | } |
1381 | } | |
49695a97 | 1382 | } |
b6961fc1 | 1383 | |
49695a97 EOW |
1384 | // Update witness heights |
1385 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
4b729ec5 | 1386 | ::UpdateWitnessHeights(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize); |
1387 | ::UpdateWitnessHeights(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize); | |
be74c80d | 1388 | } |
49695a97 EOW |
1389 | |
1390 | // For performance reasons, we write out the witness cache in | |
1391 | // CWallet::SetBestChain() (which also ensures that overall consistency | |
1392 | // of the wallet.dat is maintained). | |
be74c80d JG |
1393 | } |
1394 | ||
9d804cc6 | 1395 | template<typename NoteDataMap> |
47ab0926 | 1396 | bool DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t nWitnessCacheSize) |
be74c80d | 1397 | { |
8ba45413 | 1398 | extern int32_t KOMODO_REWIND; |
9feb4b9e | 1399 | |
9d804cc6 EOW |
1400 | for (auto& item : noteDataMap) { |
1401 | auto* nd = &(item.second); | |
1402 | // Only decrement witnesses that are not above the current height | |
1403 | if (nd->witnessHeight <= indexHeight) { | |
49695a97 | 1404 | // Check the validity of the cache |
9d804cc6 EOW |
1405 | // See comment below (this would be invalid if there were a |
1406 | // prior decrement). | |
1407 | assert(nWitnessCacheSize >= nd->witnesses.size()); | |
1408 | // Witnesses being decremented should always be either -1 | |
1409 | // (never incremented or decremented) or equal to the height | |
1410 | // of the block being removed (indexHeight) | |
47ab0926 | 1411 | if (!((nd->witnessHeight == -1) || (nd->witnessHeight == indexHeight))) |
1412 | { | |
1413 | printf("at height %d\n", indexHeight); | |
1414 | return false; | |
1415 | } | |
9d804cc6 EOW |
1416 | if (nd->witnesses.size() > 0) { |
1417 | nd->witnesses.pop_front(); | |
be74c80d | 1418 | } |
9d804cc6 EOW |
1419 | // indexHeight is the height of the block being removed, so |
1420 | // the new witness cache height is one below it. | |
1421 | nd->witnessHeight = indexHeight - 1; | |
be74c80d | 1422 | } |
9d804cc6 EOW |
1423 | // Check the validity of the cache |
1424 | // Technically if there are notes witnessed above the current | |
1425 | // height, their cache will now be invalid (relative to the new | |
1426 | // value of nWitnessCacheSize). However, this would only occur | |
1427 | // during a reindex, and by the time the reindex reaches the tip | |
1428 | // of the chain again, the existing witness caches will be valid | |
1429 | // again. | |
1430 | // We don't set nWitnessCacheSize to zero at the start of the | |
1431 | // reindex because the on-disk blocks had already resulted in a | |
1432 | // chain that didn't trigger the assertion below. | |
1433 | if (nd->witnessHeight < indexHeight) { | |
1434 | // Subtract 1 to compare to what nWitnessCacheSize will be after | |
1435 | // decrementing. | |
1436 | assert((nWitnessCacheSize - 1) >= nd->witnesses.size()); | |
4c73b58b | 1437 | } |
be74c80d | 1438 | } |
9feb4b9e | 1439 | assert(KOMODO_REWIND != 0 || nWitnessCacheSize > 0); |
47ab0926 | 1440 | return true; |
9d804cc6 EOW |
1441 | } |
1442 | ||
1443 | void CWallet::DecrementNoteWitnesses(const CBlockIndex* pindex) | |
1444 | { | |
1445 | LOCK(cs_wallet); | |
1446 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
47ab0926 | 1447 | if (!::DecrementNoteWitnesses(wtxItem.second.mapSproutNoteData, pindex->GetHeight(), nWitnessCacheSize)) |
1448 | needsRescan = true; | |
1449 | if (!::DecrementNoteWitnesses(wtxItem.second.mapSaplingNoteData, pindex->GetHeight(), nWitnessCacheSize)) | |
1450 | needsRescan = true; | |
9d804cc6 | 1451 | } |
6a23bf78 | 1452 | if (nWitnessCacheSize != 0) |
1453 | { | |
1454 | nWitnessCacheSize -= 1; | |
1455 | // TODO: If nWitnessCache is zero, we need to regenerate the caches (#1302) | |
1456 | if (nWitnessCacheSize == 0) | |
1457 | { | |
1458 | ClearNoteWitnessCache(); | |
1459 | } | |
1460 | //assert(nWitnessCacheSize > 0); | |
1461 | } | |
49695a97 EOW |
1462 | |
1463 | // For performance reasons, we write out the witness cache in | |
1464 | // CWallet::SetBestChain() (which also ensures that overall consistency | |
1465 | // of the wallet.dat is maintained). | |
be74c80d JG |
1466 | } |
1467 | ||
94f778bd | 1468 | bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) |
4e87d341 | 1469 | { |
6cc4a62c GA |
1470 | if (IsCrypted()) |
1471 | return false; | |
4e87d341 | 1472 | |
6cc4a62c | 1473 | CKeyingMaterial vMasterKey; |
4e87d341 | 1474 | |
6cc4a62c | 1475 | vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); |
65e3a1e7 | 1476 | GetRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); |
4e87d341 | 1477 | |
6cc4a62c | 1478 | CMasterKey kMasterKey; |
001a53d7 | 1479 | |
6cc4a62c | 1480 | kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); |
65e3a1e7 | 1481 | GetRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); |
4e87d341 | 1482 | |
6cc4a62c | 1483 | CCrypter crypter; |
51ed9ec9 | 1484 | int64_t nStartTime = GetTimeMillis(); |
6cc4a62c GA |
1485 | crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); |
1486 | kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); | |
ddebdd9a | 1487 | |
6cc4a62c GA |
1488 | nStartTime = GetTimeMillis(); |
1489 | crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); | |
1490 | kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; | |
ddebdd9a | 1491 | |
6cc4a62c GA |
1492 | if (kMasterKey.nDeriveIterations < 25000) |
1493 | kMasterKey.nDeriveIterations = 25000; | |
ddebdd9a | 1494 | |
881a85a2 | 1495 | LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); |
ddebdd9a | 1496 | |
6cc4a62c GA |
1497 | if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) |
1498 | return false; | |
1499 | if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) | |
1500 | return false; | |
4e87d341 | 1501 | |
6cc4a62c | 1502 | { |
f8dcd5ca | 1503 | LOCK(cs_wallet); |
4e87d341 MC |
1504 | mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; |
1505 | if (fFileBacked) | |
1506 | { | |
870da77d | 1507 | assert(!pwalletdbEncryption); |
96f34cd5 | 1508 | pwalletdbEncryption = new CWalletDB(strWalletFile); |
870da77d PK |
1509 | if (!pwalletdbEncryption->TxnBegin()) { |
1510 | delete pwalletdbEncryption; | |
1511 | pwalletdbEncryption = NULL; | |
0fb78eae | 1512 | return false; |
870da77d | 1513 | } |
96f34cd5 | 1514 | pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); |
4e87d341 MC |
1515 | } |
1516 | ||
1517 | if (!EncryptKeys(vMasterKey)) | |
96f34cd5 | 1518 | { |
870da77d | 1519 | if (fFileBacked) { |
96f34cd5 | 1520 | pwalletdbEncryption->TxnAbort(); |
870da77d PK |
1521 | delete pwalletdbEncryption; |
1522 | } | |
1523 | // We now probably have half of our keys encrypted in memory, and half not... | |
7e6d23b1 | 1524 | // die and let the user reload the unencrypted wallet. |
d0c4197e | 1525 | assert(false); |
96f34cd5 MC |
1526 | } |
1527 | ||
0b807a41 | 1528 | // Encryption was introduced in version 0.4.0 |
439e1497 | 1529 | SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); |
0b807a41 | 1530 | |
96f34cd5 MC |
1531 | if (fFileBacked) |
1532 | { | |
870da77d PK |
1533 | if (!pwalletdbEncryption->TxnCommit()) { |
1534 | delete pwalletdbEncryption; | |
5b40d886 | 1535 | // We now have keys encrypted in memory, but not on disk... |
7e6d23b1 | 1536 | // die to avoid confusion and let the user reload the unencrypted wallet. |
d0c4197e | 1537 | assert(false); |
870da77d | 1538 | } |
96f34cd5 | 1539 | |
fcfd7ff8 | 1540 | delete pwalletdbEncryption; |
96f34cd5 MC |
1541 | pwalletdbEncryption = NULL; |
1542 | } | |
4e87d341 | 1543 | |
37971fcc GA |
1544 | Lock(); |
1545 | Unlock(strWalletPassphrase); | |
1546 | NewKeyPool(); | |
4e87d341 | 1547 | Lock(); |
6cc4a62c | 1548 | |
d764d916 GA |
1549 | // Need to completely rewrite the wallet file; if we don't, bdb might keep |
1550 | // bits of the unencrypted private key in slack space in the database file. | |
b2d3b2d6 | 1551 | CDB::Rewrite(strWalletFile); |
fe4a6550 | 1552 | |
d764d916 | 1553 | } |
ab1b288f | 1554 | NotifyStatusChanged(this); |
9e9869d0 | 1555 | |
4e87d341 | 1556 | return true; |
e8ef3da7 WL |
1557 | } |
1558 | ||
51ed9ec9 | 1559 | int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) |
da7b8c12 | 1560 | { |
95691680 | 1561 | AssertLockHeld(cs_wallet); // nOrderPosNext |
51ed9ec9 | 1562 | int64_t nRet = nOrderPosNext++; |
4291e8fe PW |
1563 | if (pwalletdb) { |
1564 | pwalletdb->WriteOrderPosNext(nOrderPosNext); | |
1565 | } else { | |
1566 | CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); | |
1567 | } | |
da7b8c12 LD |
1568 | return nRet; |
1569 | } | |
1570 | ||
ddb709e9 | 1571 | CWallet::TxItems CWallet::OrderedTxItems(std::list<CAccountingEntry>& acentries, std::string strAccount) |
c3f95ef1 | 1572 | { |
95691680 | 1573 | AssertLockHeld(cs_wallet); // mapWallet |
c3f95ef1 LD |
1574 | CWalletDB walletdb(strWalletFile); |
1575 | ||
1576 | // First: get all CWalletTx and CAccountingEntry into a sorted-by-order multimap. | |
1577 | TxItems txOrdered; | |
1578 | ||
1579 | // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry | |
1580 | // would make this much faster for applications that do this a lot. | |
1581 | for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
1582 | { | |
1583 | CWalletTx* wtx = &((*it).second); | |
1584 | txOrdered.insert(make_pair(wtx->nOrderPos, TxPair(wtx, (CAccountingEntry*)0))); | |
1585 | } | |
ddb709e9 | 1586 | acentries.clear(); |
c3f95ef1 LD |
1587 | walletdb.ListAccountCreditDebit(strAccount, acentries); |
1588 | BOOST_FOREACH(CAccountingEntry& entry, acentries) | |
1589 | { | |
1590 | txOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry))); | |
1591 | } | |
1592 | ||
1593 | return txOrdered; | |
1594 | } | |
1595 | ||
1f722359 MT |
1596 | // looks through all wallet UTXOs and checks to see if any qualify to stake the block at the current height. it always returns the qualified |
1597 | // UTXO with the smallest coin age if there is more than one, as larger coin age will win more often and is worth saving | |
1598 | // each attempt consists of taking a VerusHash of the following values: | |
1599 | // ASSETCHAINS_MAGIC, nHeight, txid, voutNum | |
17d0160a | 1600 | bool CWallet::VerusSelectStakeOutput(CBlock *pBlock, arith_uint256 &hashResult, CTransaction &stakeSource, int32_t &voutNum, int32_t nHeight, uint32_t &bnTarget) const |
1f722359 | 1601 | { |
c5325a32 | 1602 | arith_uint256 target; |
1f722359 | 1603 | arith_uint256 curHash; |
1f722359 | 1604 | COutput *pwinner = NULL; |
0cc87e8f | 1605 | CWalletTx winnerWtx; |
1606 | ||
1fae37f6 MT |
1607 | txnouttype whichType; |
1608 | std:vector<std::vector<unsigned char>> vSolutions; | |
1f722359 | 1609 | |
12217420 | 1610 | pBlock->nNonce.SetPOSTarget(bnTarget, pBlock->nVersion); |
c5325a32 MT |
1611 | target.SetCompact(bnTarget); |
1612 | ||
b95cb937 MT |
1613 | auto consensusParams = Params().GetConsensus(); |
1614 | CValidationState state; | |
1615 | ||
672414d7 | 1616 | std::vector<COutput> vecOutputs; |
1617 | std::vector<CWalletTx> vwtx; | |
0cc87e8f | 1618 | CAmount totalStakingAmount = 0; |
1619 | ||
5dde9d7e | 1620 | uint32_t solutionVersion = CConstVerusSolutionVector::GetVersionByHeight(nHeight); |
1621 | bool extendedStake = solutionVersion >= CActivationHeight::ACTIVATE_EXTENDEDSTAKE; | |
1622 | ||
0cc87e8f | 1623 | { |
0cc87e8f | 1624 | LOCK2(cs_main, cs_wallet); |
fbafcefe | 1625 | pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true, false); |
0cc87e8f | 1626 | |
fbafcefe | 1627 | int newSize = 0; |
1628 | ||
1629 | for (int i = 0; i < vecOutputs.size(); i++) | |
0cc87e8f | 1630 | { |
fbafcefe | 1631 | auto &txout = vecOutputs[i]; |
5dde9d7e | 1632 | COptCCParams p; |
fbafcefe | 1633 | |
0cc87e8f | 1634 | if (txout.tx && |
3416e9be | 1635 | txout.i < txout.tx->vout.size() && |
0cc87e8f | 1636 | txout.tx->vout[txout.i].nValue > 0 && |
1637 | txout.fSpendable && | |
1638 | (txout.nDepth >= VERUS_MIN_STAKEAGE) && | |
6e7fb01d | 1639 | ((txout.tx->vout[txout.i].scriptPubKey.IsPayToCryptoCondition(p) && |
1640 | extendedStake && | |
50c7c41c | 1641 | p.IsValid() && |
1642 | txout.tx->vout[txout.i].scriptPubKey.IsSpendableOutputType(p)) || | |
1643 | (!p.IsValid() && | |
1644 | Solver(txout.tx->vout[txout.i].scriptPubKey, whichType, vSolutions) && | |
1645 | (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH)))) | |
0cc87e8f | 1646 | { |
1647 | totalStakingAmount += txout.tx->vout[txout.i].nValue; | |
fbafcefe | 1648 | // if all are valid, no change, else compress |
1649 | if (newSize != i) | |
1650 | { | |
1651 | vecOutputs[newSize] = txout; | |
1652 | } | |
1653 | newSize++; | |
1654 | } | |
1655 | } | |
1656 | ||
1657 | if (newSize) | |
1658 | { | |
1659 | // no reallocations to move objects. do all at once, so we can release the wallet lock | |
1660 | vecOutputs.resize(newSize); | |
1661 | vwtx.resize(newSize); | |
1662 | for (int i = 0; i < vecOutputs.size(); i++) | |
1663 | { | |
1664 | vwtx[i] = *vecOutputs[i].tx; | |
1665 | vecOutputs[i].tx = &vwtx[i]; | |
0cc87e8f | 1666 | } |
1667 | } | |
1668 | } | |
1669 | ||
1670 | if (totalStakingAmount) | |
1671 | { | |
6e7fb01d | 1672 | LogPrintf("Staking with %s VRSC\n", ValueFromAmount(totalStakingAmount).write().c_str()); |
0cc87e8f | 1673 | } |
1674 | else | |
1675 | { | |
6e7fb01d | 1676 | LogPrintf("No VRSC staking\n"); |
0cc87e8f | 1677 | return false; |
1678 | } | |
1f722359 | 1679 | |
af521e42 | 1680 | // we get these sources of entropy to prove all sources in the header |
1681 | int posHeight = -1, powHeight = -1, altHeight = -1; | |
1682 | uint256 pastHash; | |
1683 | { | |
1684 | LOCK(cs_main); | |
1685 | pastHash = chainActive.GetVerusEntropyHash(nHeight, &posHeight, &powHeight, &altHeight); | |
6e62f837 | 1686 | if (extendedStake && (altHeight == -1 && (powHeight == -1 || posHeight == -1))) |
5fd488ca | 1687 | { |
1688 | printf("Error retrieving entropy hash at height %d, posHeight: %d, powHeight: %d, altHeight: %d\n", nHeight, posHeight, powHeight, altHeight); | |
1689 | LogPrintf("Error retrieving entropy hash at height %d, posHeight: %d, powHeight: %d, altHeight: %d\n", nHeight, posHeight, powHeight, altHeight); | |
1690 | return false; | |
1691 | } | |
af521e42 | 1692 | } |
1693 | ||
6e62f837 | 1694 | // secondBlockHeight is either less than first or -1 if there isn't one |
1695 | int secondBlockHeight = altHeight != -1 ? | |
1696 | altHeight : | |
1697 | posHeight == -1 ? | |
1698 | posHeight : | |
1699 | powHeight == -1 ? | |
1700 | powHeight : | |
1701 | (posHeight > powHeight ? | |
1702 | powHeight : | |
1703 | posHeight); | |
1704 | ||
7f5a0ec7 | 1705 | int proveBlockHeight = posHeight > secondBlockHeight ? posHeight : ((powHeight == -1) ? posHeight : powHeight); |
af521e42 | 1706 | |
6e62f837 | 1707 | if (proveBlockHeight == -1) |
1708 | { | |
1709 | printf("No block suitable for proof for height %d, posHeight: %d, powHeight: %d, altHeight: %d\n", nHeight, posHeight, powHeight, altHeight); | |
1710 | LogPrintf("No block suitable for proof for height %d, posHeight: %d, powHeight: %d, altHeight: %d\n", nHeight, posHeight, powHeight, altHeight); | |
1711 | } | |
1712 | else | |
1f722359 | 1713 | { |
17d0160a | 1714 | CPOSNonce curNonce; |
b2a98c42 | 1715 | uint32_t srcIndex; |
1f722359 | 1716 | |
12d157e4 | 1717 | CCoinsViewCache view(pcoinsTip); |
1718 | CMutableTransaction checkStakeTx = CreateNewContextualCMutableTransaction(consensusParams, nHeight); | |
e54e3c24 | 1719 | std::vector<CTxDestination> addressRet; |
1720 | int nRequiredRet; | |
12d157e4 | 1721 | |
1f722359 MT |
1722 | BOOST_FOREACH(COutput &txout, vecOutputs) |
1723 | { | |
5dde9d7e | 1724 | COptCCParams p; |
1725 | std::vector<CTxDestination> destinations; | |
1726 | int nRequired = 0; | |
1727 | bool canSign = false, canSpend = false; | |
1728 | ||
0cc87e8f | 1729 | if (UintToArith256(txout.tx->GetVerusPOSHash(&(pBlock->nNonce), txout.i, nHeight, pastHash)) <= target && |
5dde9d7e | 1730 | ExtractDestinations(txout.tx->vout[txout.i].scriptPubKey, whichType, destinations, nRequired, this, &canSign, &canSpend) && |
6e7fb01d | 1731 | ((txout.tx->vout[txout.i].scriptPubKey.IsPayToCryptoCondition(p) && |
1732 | extendedStake && | |
5dde9d7e | 1733 | canSpend) || |
6e7fb01d | 1734 | (!p.IsValid() && (whichType == TX_PUBKEY || whichType == TX_PUBKEYHASH) && ::IsMine(*this, destinations[0])))) |
1f722359 | 1735 | { |
285c3564 | 1736 | uint256 txHash = txout.tx->GetHash(); |
a4777f81 | 1737 | checkStakeTx.vin.push_back(CTxIn(COutPoint(txHash, txout.i))); |
285c3564 | 1738 | |
56fe75cb | 1739 | LOCK2(cs_main, cs_wallet); |
0cc87e8f | 1740 | |
6e7fb01d | 1741 | if ((!pwinner || UintToArith256(curNonce) < UintToArith256(pBlock->nNonce)) && |
717f1db6 | 1742 | !cheatList.IsUTXOInList(COutPoint(txHash, txout.i), nHeight <= 100 ? 1 : nHeight-100)) |
c68ca1a2 | 1743 | { |
717f1db6 | 1744 | if (view.HaveCoins(txHash) && Consensus::CheckTxInputs(checkStakeTx, state, view, nHeight, consensusParams)) |
1745 | { | |
1746 | //printf("Found PoS block\nnNonce: %s\n", pBlock->nNonce.GetHex().c_str()); | |
1747 | pwinner = &txout; | |
1748 | curNonce = pBlock->nNonce; | |
af521e42 | 1749 | srcIndex = nHeight - txout.nDepth; |
717f1db6 | 1750 | } |
1751 | else | |
1752 | { | |
1753 | LogPrintf("Transaction %s failed to stake due to %s\n", txout.tx->GetHash().GetHex().c_str(), | |
1754 | view.HaveCoins(txHash) ? "bad inputs" : "unavailable coins"); | |
1755 | } | |
c68ca1a2 | 1756 | } |
12d157e4 | 1757 | |
1758 | checkStakeTx.vin.pop_back(); | |
1f722359 MT |
1759 | } |
1760 | } | |
1761 | if (pwinner) | |
1762 | { | |
0cc87e8f | 1763 | stakeSource = static_cast<CTransaction>(*pwinner->tx); |
31609f35 | 1764 | |
1765 | // arith_uint256 post; | |
1766 | // post.SetCompact(pBlock->GetVerusPOSTarget()); | |
1767 | // printf("Found stake transaction\n"); | |
1768 | // printf("POS hash: %s \ntarget: %s\n\n", | |
1769 | // stakeSource.GetVerusPOSHash(&(pBlock->nNonce), pwinner->i, nHeight, pastHash).GetHex().c_str(), | |
1770 | // ArithToUint256(post).GetHex().c_str()); | |
1771 | ||
1f722359 | 1772 | voutNum = pwinner->i; |
17d0160a | 1773 | pBlock->nNonce = curNonce; |
ec1c84a0 | 1774 | |
7695dab4 | 1775 | if (solutionVersion >= CActivationHeight::ACTIVATE_STAKEHEADER) |
ec1c84a0 | 1776 | { |
56fe75cb | 1777 | CDataStream headerStream = CDataStream(SER_NETWORK, PROTOCOL_VERSION); |
ec1c84a0 | 1778 | |
b2a98c42 MT |
1779 | // store: |
1780 | // 1. PBaaS header for this block | |
1781 | // 2. source transaction | |
1782 | // 3. block index of base MMR being used | |
1783 | // 4. source tx block index for proof | |
1784 | // 5. full merkle proof of source tx up to prior MMR root | |
1785 | // 6. block hash of block of entropyhash | |
1786 | // 7. proof of block hash (not full header) in the MMR for the block height of the entropy hash block | |
1787 | // all that data includes enough information to verify | |
1788 | // prior MMR, blockhash, transaction, entropy hash, and block indexes match | |
1789 | // also checks root match & block power | |
0cc87e8f | 1790 | auto mmrView = chainActive.GetMMV(); |
56fe75cb | 1791 | pBlock->SetPrevMMRRoot(mmrView.GetRoot()); |
1792 | pBlock->AddUpdatePBaaSHeader(); | |
ec1c84a0 | 1793 | |
56fe75cb | 1794 | // get map and MMR for stake source transaction |
1795 | CTransactionMap txMap(stakeSource); | |
1796 | TransactionMMView txView(txMap.transactionMMR); | |
af521e42 | 1797 | uint256 txRoot = txView.GetRoot(); |
ec1c84a0 | 1798 | |
f39725dc | 1799 | std::vector<CTransactionComponentProof> txProofVec; |
0ab273d2 | 1800 | txProofVec.emplace_back(txView, txMap, stakeSource, CTransactionHeader::TX_HEADER, 0); |
1801 | txProofVec.emplace_back(txView, txMap, stakeSource, CTransactionHeader::TX_OUTPUT, pwinner->i); | |
b2a98c42 | 1802 | |
56fe75cb | 1803 | // now, both the header and stake output are dependent on the transaction MMR root being provable up |
1804 | // through the block MMR, and since we don't cache the new MMR proof for transactions yet, we need the block to create the proof. | |
1805 | // when we switch to the new MMR in place of a merkle tree, we can keep that in the wallet as well | |
1806 | CBlock block; | |
1807 | if (!ReadBlockFromDisk(block, chainActive[srcIndex], Params().GetConsensus(), false)) | |
1808 | { | |
af521e42 | 1809 | LogPrintf("%s: ERROR: could not read block number %u from disk\n", __func__, srcIndex); |
56fe75cb | 1810 | return false; |
1811 | } | |
b2a98c42 | 1812 | |
2d88a342 | 1813 | BlockMMRange blockMMR(block.GetBlockMMRTree()); |
56fe75cb | 1814 | BlockMMView blockView(blockMMR); |
b2a98c42 | 1815 | |
af521e42 | 1816 | int txIndexPos; |
1817 | for (txIndexPos = 0; txIndexPos < blockMMR.size(); txIndexPos++) | |
1818 | { | |
1819 | uint256 txRootHashFromMMR = blockMMR[txIndexPos].hash; | |
1820 | if (txRootHashFromMMR == txRoot) | |
1821 | { | |
1822 | //printf("tx with root %s found in block\n", txRootHashFromMMR.GetHex().c_str()); | |
1823 | break; | |
1824 | } | |
1825 | } | |
f39725dc | 1826 | |
af521e42 | 1827 | if (txIndexPos == blockMMR.size()) |
56fe75cb | 1828 | { |
af521e42 | 1829 | LogPrintf("%s: ERROR: could not find source transaction root in block %u\n", __func__, srcIndex); |
56fe75cb | 1830 | return false; |
1831 | } | |
b2a98c42 | 1832 | |
af521e42 | 1833 | // prove the tx up to the MMR root, which also contains the block hash |
f39725dc | 1834 | CMMRProof txRootProof; |
af521e42 | 1835 | if (!blockView.GetProof(txRootProof, txIndexPos)) |
56fe75cb | 1836 | { |
af521e42 | 1837 | LogPrintf("%s: ERROR: could not create proof of source transaction in block %u\n", __func__, srcIndex); |
56fe75cb | 1838 | return false; |
1839 | } | |
1840 | ||
af521e42 | 1841 | mmrView.resize(proveBlockHeight + 1); |
1842 | chainActive.GetMerkleProof(mmrView, txRootProof, srcIndex); | |
1843 | ||
1844 | headerStream << CPartialTransactionProof(txRootProof, txProofVec); | |
56fe75cb | 1845 | |
af521e42 | 1846 | CMMRProof blockHeaderProof1; |
1847 | if (!chainActive.GetBlockProof(mmrView, blockHeaderProof1, proveBlockHeight)) | |
56fe75cb | 1848 | { |
af521e42 | 1849 | LogPrintf("%s: ERROR: could not create block proof for block %u\n", __func__, srcIndex); |
56fe75cb | 1850 | return false; |
1851 | } | |
af521e42 | 1852 | headerStream << CBlockHeaderProof(blockHeaderProof1, chainActive[proveBlockHeight]->GetBlockHeader()); |
b2a98c42 | 1853 | |
af521e42 | 1854 | CMMRProof blockHeaderProof2; |
1855 | if (!chainActive.GetBlockProof(mmrView, blockHeaderProof2, secondBlockHeight)) | |
1856 | { | |
1857 | LogPrintf("%s: ERROR: could not create block proof for second entropy source block %u\n", __func__, srcIndex); | |
5fd488ca | 1858 | chainActive.GetBlockProof(mmrView, blockHeaderProof2, secondBlockHeight); // repeat for debugging |
af521e42 | 1859 | return false; |
1860 | } | |
1861 | headerStream << CBlockHeaderProof(blockHeaderProof2, chainActive[secondBlockHeight]->GetBlockHeader()); | |
b2a98c42 | 1862 | |
56fe75cb | 1863 | std::vector<unsigned char> stx(headerStream.begin(), headerStream.end()); |
b2a98c42 | 1864 | |
af521e42 | 1865 | // printf("\nFound Stake transaction... all proof serialized size == %lu\n", stx.size()); |
b2a98c42 | 1866 | |
b2a98c42 MT |
1867 | CVerusSolutionVector(pBlock->nSolution).ResizeExtraData(stx.size()); |
1868 | ||
1869 | pBlock->SetExtraData(stx.data(), stx.size()); | |
f39725dc | 1870 | |
1871 | //CBlockHeader blkHeader(*pBlock); | |
1872 | //CDataStream s(SER_NETWORK, PROTOCOL_VERSION); | |
1873 | //printf("Staking block header size %ld\n", GetSerializeSize(s, blkHeader)); | |
ec1c84a0 | 1874 | } |
1f722359 MT |
1875 | return true; |
1876 | } | |
1877 | } | |
1878 | return false; | |
1879 | } | |
1880 | ||
855714b0 | 1881 | int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &bnTarget, arith_uint256 &hashResult, std::vector<unsigned char> &utxosig, CTxDestination &rewardDest) const |
1f722359 | 1882 | { |
1f722359 MT |
1883 | CTransaction stakeSource; |
1884 | int32_t voutNum, siglen = 0; | |
1885 | int64_t nValue; | |
1fae37f6 MT |
1886 | txnouttype whichType; |
1887 | std::vector<std::vector<unsigned char>> vSolutions; | |
1f722359 | 1888 | |
3bfa5e22 | 1889 | CBlockIndex *tipindex = chainActive.LastTip(); |
1890 | uint32_t stakeHeight = tipindex->GetHeight() + 1; | |
56fe75cb | 1891 | bool extendedStake = CConstVerusSolutionVector::GetVersionByHeight(stakeHeight) >= CActivationHeight::ACTIVATE_EXTENDEDSTAKE; |
8a727a26 | 1892 | |
1f722359 | 1893 | bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus()); |
1f722359 | 1894 | |
adb5cebb | 1895 | if (!VerusSelectStakeOutput(pBlock, hashResult, stakeSource, voutNum, stakeHeight, bnTarget)) |
1f722359 | 1896 | { |
0cc87e8f | 1897 | //LogPrintf("Searched for eligible staking transactions, no winners found\n"); |
1f722359 MT |
1898 | return 0; |
1899 | } | |
1900 | ||
1f722359 MT |
1901 | bool signSuccess; |
1902 | SignatureData sigdata; | |
86e31e3d | 1903 | uint64_t txfee; |
86e31e3d | 1904 | auto consensusBranchId = CurrentEpochBranchId(stakeHeight, Params().GetConsensus()); |
1f722359 MT |
1905 | |
1906 | const CKeyStore& keystore = *pwalletMain; | |
1907 | txNew.vin.resize(1); | |
1908 | txNew.vout.resize(1); | |
88d014d0 | 1909 | txfee = 0; |
1f722359 MT |
1910 | txNew.vin[0].prevout.hash = stakeSource.GetHash(); |
1911 | txNew.vin[0].prevout.n = voutNum; | |
1912 | ||
adb5cebb | 1913 | COptCCParams p; |
1914 | if (stakeSource.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid()) | |
1fae37f6 | 1915 | { |
855714b0 | 1916 | if (!p.vKeys.size()) |
1917 | { | |
1918 | LogPrintf("%s: Please report - no destination on stake source\n"); | |
1919 | return 0; | |
1920 | } | |
1921 | ||
adb5cebb | 1922 | // send output to same destination as source, convert stakeguard into normal output, since |
1923 | // that is a spendable output that only works in coinbases. preserve all recipients and | |
1924 | // min sigs | |
1925 | if (p.evalCode == EVAL_STAKEGUARD) | |
1926 | { | |
855714b0 | 1927 | // "CIdentity" is a dummy object type since an obj is not passed, and it does not make this an identity output |
1928 | txNew.vout[0].scriptPubKey = MakeMofNCCScript(CConditionObj<CIdentity>(0, p.vKeys, p.m)); | |
adb5cebb | 1929 | } |
1930 | else | |
8a727a26 | 1931 | { |
adb5cebb | 1932 | txNew.vout[0].scriptPubKey = stakeSource.vout[voutNum].scriptPubKey; |
8a727a26 | 1933 | } |
855714b0 | 1934 | rewardDest = p.vKeys[0]; |
1fae37f6 | 1935 | } |
adb5cebb | 1936 | else if (Solver(stakeSource.vout[voutNum].scriptPubKey, whichType, vSolutions)) |
e54e3c24 | 1937 | { |
adb5cebb | 1938 | if (whichType == TX_PUBKEY) |
1939 | { | |
1940 | txNew.vout[0].scriptPubKey << ToByteVector(vSolutions[0]) << OP_CHECKSIG; | |
855714b0 | 1941 | rewardDest = CPubKey(vSolutions[0]); |
adb5cebb | 1942 | } |
1943 | else if (whichType == TX_PUBKEYHASH) | |
1944 | { | |
1945 | txNew.vout[0].scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(vSolutions[0]) << OP_EQUALVERIFY << OP_CHECKSIG; | |
855714b0 | 1946 | rewardDest = CKeyID(uint160(vSolutions[0])); |
adb5cebb | 1947 | } |
1948 | else | |
1949 | { | |
1950 | LogPrintf("%s: Please report - found stake source that is not valid\n"); | |
1951 | return 0; | |
1952 | } | |
e54e3c24 | 1953 | } |
1fae37f6 | 1954 | else |
e54e3c24 | 1955 | { |
1fae37f6 | 1956 | return 0; |
e54e3c24 | 1957 | } |
1f722359 | 1958 | |
855714b0 | 1959 | // set expiry to time out after 100 blocks, so we can remove the transaction if it orphans |
1960 | txNew.nExpiryHeight = stakeHeight + 100; | |
7a993143 | 1961 | |
855714b0 | 1962 | uint256 srcBlock = uint256(); |
1963 | CBlockIndex *pSrcIndex; | |
8a727a26 | 1964 | |
855714b0 | 1965 | txNew.vout.push_back(CTxOut()); |
1966 | CTxOut &txOut1 = txNew.vout[1]; | |
1967 | txOut1.nValue = 0; | |
1968 | if (!GetTransaction(stakeSource.GetHash(), stakeSource, srcBlock)) | |
1969 | return 0; | |
1970 | ||
1971 | BlockMap::const_iterator it = mapBlockIndex.find(srcBlock); | |
1972 | if (it == mapBlockIndex.end() || (pSrcIndex = it->second) == 0) | |
1973 | return 0; | |
8a727a26 | 1974 | |
855714b0 | 1975 | // !! DISABLE THIS FOR RELEASE: THIS MAKES A CHEAT TRANSACTION FOR EVERY STAKE FOR TESTING |
1976 | //CMutableTransaction cheat; | |
1977 | //cheat = CMutableTransaction(txNew); | |
1978 | //printf("TESTING ONLY: THIS SHOULD NOT BE ENABLED FOR RELEASE - MAKING CHEAT TRANSACTION FOR TESTING\n"); | |
1979 | //cheat.vout[1].scriptPubKey << OP_RETURN | |
1980 | // << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, pSrcIndex->GetBlockHash(), pk).AsVector(); | |
1981 | // !! DOWN TO HERE | |
43260416 | 1982 | |
855714b0 | 1983 | if (USE_EXTERNAL_PUBKEY) |
1984 | { | |
1985 | rewardDest = CPubKey(ParseHex(NOTARY_PUBKEY)); | |
1986 | } | |
1987 | else if (!VERUS_DEFAULTID.IsNull()) | |
1988 | { | |
1989 | rewardDest = VERUS_DEFAULTID; | |
1990 | } | |
8a727a26 | 1991 | |
855714b0 | 1992 | if (rewardDest.which() == COptCCParams::ADDRTYPE_INVALID) |
1993 | { | |
1994 | printf("%s: Invalid reward destinaton for stake\n", __func__); | |
1995 | return 0; | |
41e9e058 | 1996 | } |
43260416 | 1997 | |
855714b0 | 1998 | txOut1.scriptPubKey << OP_RETURN |
1999 | << CStakeParams(pSrcIndex->GetHeight(), tipindex->GetHeight() + 1, tipindex->GetBlockHash(), rewardDest).AsVector(); | |
2000 | ||
2001 | // !! DISABLE THIS FOR RELEASE: REMOVE THIS TOO | |
2002 | //nValue = cheat.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee; | |
2003 | //cheat.nLockTime = 0; | |
2004 | //CTransaction cheatConst(cheat); | |
2005 | //SignatureData cheatSig; | |
2006 | //if (!ProduceSignature(TransactionSignatureCreator(&keystore, &cheatConst, 0, nValue, SIGHASH_ALL), stakeSource.vout[voutNum].scriptPubKey, cheatSig, consensusBranchId)) | |
2007 | // fprintf(stderr,"failed to create cheat test signature\n"); | |
2008 | //else | |
2009 | //{ | |
2010 | // uint8_t *ptr; | |
2011 | // UpdateTransaction(cheat,0,cheatSig); | |
2012 | // cheatList.Add(CTxHolder(CTransaction(cheat), tipindex->GetHeight() + 1)); | |
2013 | //} | |
2014 | // !! DOWN TO HERE | |
2015 | ||
31bbe234 | 2016 | nValue = txNew.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee; |
3bfa5e22 | 2017 | |
1f722359 MT |
2018 | txNew.nLockTime = 0; |
2019 | CTransaction txNewConst(txNew); | |
a4f9bc97 | 2020 | signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, nValue, stakeSource.vout[voutNum].scriptPubKey), stakeSource.vout[voutNum].scriptPubKey, sigdata, consensusBranchId); |
1f722359 | 2021 | if (!signSuccess) |
b2a59a12 | 2022 | { |
1f722359 | 2023 | fprintf(stderr,"failed to create signature\n"); |
b2a59a12 | 2024 | utxosig.clear(); |
2025 | } | |
1f722359 MT |
2026 | else |
2027 | { | |
2028 | uint8_t *ptr; | |
b2a59a12 | 2029 | UpdateTransaction(txNew, 0, sigdata); |
2030 | utxosig.resize(sigdata.scriptSig.size()); | |
2031 | memcpy(&(utxosig[0]), &(sigdata.scriptSig[0]), utxosig.size()); | |
1f722359 | 2032 | } |
b2a59a12 | 2033 | return(utxosig.size()); |
1f722359 MT |
2034 | } |
2035 | ||
95d888a6 PW |
2036 | void CWallet::MarkDirty() |
2037 | { | |
95d888a6 | 2038 | { |
f8dcd5ca | 2039 | LOCK(cs_wallet); |
95d888a6 PW |
2040 | BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) |
2041 | item.second.MarkDirty(); | |
2042 | } | |
2043 | } | |
2044 | ||
1a62587e | 2045 | /** |
9a2b8ae5 JG |
2046 | * Ensure that every note in the wallet (for which we possess a spending key) |
2047 | * has a cached nullifier. | |
1a62587e JG |
2048 | */ |
2049 | bool CWallet::UpdateNullifierNoteMap() | |
2050 | { | |
2051 | { | |
2052 | LOCK(cs_wallet); | |
2053 | ||
2054 | if (IsLocked()) | |
2055 | return false; | |
2056 | ||
2057 | ZCNoteDecryption dec; | |
2058 | for (std::pair<const uint256, CWalletTx>& wtxItem : mapWallet) { | |
005f3ad1 | 2059 | for (mapSproutNoteData_t::value_type& item : wtxItem.second.mapSproutNoteData) { |
1a62587e | 2060 | if (!item.second.nullifier) { |
9a2b8ae5 JG |
2061 | if (GetNoteDecryptor(item.second.address, dec)) { |
2062 | auto i = item.first.js; | |
f57f76d7 | 2063 | auto hSig = wtxItem.second.vJoinSplit[i].h_sig( |
9a2b8ae5 | 2064 | *pzcashParams, wtxItem.second.joinSplitPubKey); |
618206c7 | 2065 | item.second.nullifier = GetSproutNoteNullifier( |
f57f76d7 | 2066 | wtxItem.second.vJoinSplit[i], |
9a2b8ae5 JG |
2067 | item.second.address, |
2068 | dec, | |
2069 | hSig, | |
2070 | item.first.n); | |
2071 | } | |
1a62587e JG |
2072 | } |
2073 | } | |
ad1e90dd S |
2074 | |
2075 | // TODO: Sapling. This method is only called from RPC walletpassphrase, which is currently unsupported | |
2076 | // as RPC encryptwallet is hidden behind two flags: -developerencryptwallet -experimentalfeatures | |
2077 | ||
6e263a5f | 2078 | UpdateNullifierNoteMapWithTx(wtxItem.second); |
1a62587e JG |
2079 | } |
2080 | } | |
2081 | return true; | |
2082 | } | |
2083 | ||
6e263a5f | 2084 | /** |
ad1e90dd S |
2085 | * Update mapSproutNullifiersToNotes and mapSaplingNullifiersToNotes |
2086 | * with the cached nullifiers in this tx. | |
6e263a5f JG |
2087 | */ |
2088 | void CWallet::UpdateNullifierNoteMapWithTx(const CWalletTx& wtx) | |
8db7e25c JG |
2089 | { |
2090 | { | |
2091 | LOCK(cs_wallet); | |
005f3ad1 | 2092 | for (const mapSproutNoteData_t::value_type& item : wtx.mapSproutNoteData) { |
1a62587e | 2093 | if (item.second.nullifier) { |
f41bf503 | 2094 | mapSproutNullifiersToNotes[*item.second.nullifier] = item.first; |
1a62587e | 2095 | } |
8db7e25c | 2096 | } |
ad1e90dd S |
2097 | |
2098 | for (const mapSaplingNoteData_t::value_type& item : wtx.mapSaplingNoteData) { | |
1a62587e | 2099 | if (item.second.nullifier) { |
ad1e90dd | 2100 | mapSaplingNullifiersToNotes[*item.second.nullifier] = item.first; |
1a62587e | 2101 | } |
8db7e25c JG |
2102 | } |
2103 | } | |
2104 | } | |
2105 | ||
ad1e90dd S |
2106 | /** |
2107 | * Update mapSaplingNullifiersToNotes, computing the nullifier from a cached witness if necessary. | |
2108 | */ | |
2109 | void CWallet::UpdateSaplingNullifierNoteMapWithTx(CWalletTx& wtx) { | |
2110 | LOCK(cs_wallet); | |
2111 | ||
2112 | for (mapSaplingNoteData_t::value_type &item : wtx.mapSaplingNoteData) { | |
2113 | SaplingOutPoint op = item.first; | |
2114 | SaplingNoteData nd = item.second; | |
2115 | ||
c343e2db | 2116 | if (nd.witnesses.empty()) { |
ad1e90dd S |
2117 | // If there are no witnesses, erase the nullifier and associated mapping. |
2118 | if (item.second.nullifier) { | |
2119 | mapSaplingNullifiersToNotes.erase(item.second.nullifier.get()); | |
2120 | } | |
2121 | item.second.nullifier = boost::none; | |
2122 | } | |
c343e2db | 2123 | else { |
ad1e90dd S |
2124 | uint64_t position = nd.witnesses.front().position(); |
2125 | SaplingFullViewingKey fvk = mapSaplingFullViewingKeys.at(nd.ivk); | |
2126 | OutputDescription output = wtx.vShieldedOutput[op.n]; | |
2127 | auto optPlaintext = SaplingNotePlaintext::decrypt(output.encCiphertext, nd.ivk, output.ephemeralKey, output.cm); | |
2128 | if (!optPlaintext) { | |
2129 | // An item in mapSaplingNoteData must have already been successfully decrypted, | |
2130 | // otherwise the item would not exist in the first place. | |
2131 | assert(false); | |
2132 | } | |
2133 | auto optNote = optPlaintext.get().note(nd.ivk); | |
2134 | if (!optNote) { | |
2135 | assert(false); | |
2136 | } | |
2137 | auto optNullifier = optNote.get().nullifier(fvk, position); | |
2138 | if (!optNullifier) { | |
2139 | // This should not happen. If it does, maybe the position has been corrupted or miscalculated? | |
2140 | assert(false); | |
2141 | } | |
2142 | uint256 nullifier = optNullifier.get(); | |
2143 | mapSaplingNullifiersToNotes[nullifier] = op; | |
2144 | item.second.nullifier = nullifier; | |
2145 | } | |
2146 | } | |
2147 | } | |
2148 | ||
2149 | /** | |
2150 | * Iterate over transactions in a block and update the cached Sapling nullifiers | |
2151 | * for transactions which belong to the wallet. | |
2152 | */ | |
2153 | void CWallet::UpdateSaplingNullifierNoteMapForBlock(const CBlock *pblock) { | |
2154 | LOCK(cs_wallet); | |
2155 | ||
2156 | for (const CTransaction& tx : pblock->vtx) { | |
2157 | auto hash = tx.GetHash(); | |
2158 | bool txIsOurs = mapWallet.count(hash); | |
2159 | if (txIsOurs) { | |
2160 | UpdateSaplingNullifierNoteMapWithTx(mapWallet[hash]); | |
2161 | } | |
8db7e25c JG |
2162 | } |
2163 | } | |
2164 | ||
44bc988e | 2165 | bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb) |
e8ef3da7 | 2166 | { |
805344dc | 2167 | uint256 hash = wtxIn.GetHash(); |
731b89b8 GA |
2168 | |
2169 | if (fFromLoadWallet) | |
2170 | { | |
2171 | mapWallet[hash] = wtxIn; | |
09ec3af1 | 2172 | mapWallet[hash].BindWallet(this); |
6e263a5f | 2173 | UpdateNullifierNoteMapWithTx(mapWallet[hash]); |
93a18a36 | 2174 | AddToSpends(hash); |
731b89b8 GA |
2175 | } |
2176 | else | |
e8ef3da7 | 2177 | { |
f8dcd5ca | 2178 | LOCK(cs_wallet); |
e8ef3da7 WL |
2179 | // Inserts only if not already there, returns tx inserted or tx found |
2180 | pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn)); | |
2181 | CWalletTx& wtx = (*ret.first).second; | |
4c6e2295 | 2182 | wtx.BindWallet(this); |
6e263a5f | 2183 | UpdateNullifierNoteMapWithTx(wtx); |
e8ef3da7 WL |
2184 | bool fInsertedNew = ret.second; |
2185 | if (fInsertedNew) | |
9c7722b7 | 2186 | { |
e8ef3da7 | 2187 | wtx.nTimeReceived = GetAdjustedTime(); |
44bc988e | 2188 | wtx.nOrderPos = IncOrderPosNext(pwalletdb); |
c3f95ef1 LD |
2189 | |
2190 | wtx.nTimeSmart = wtx.nTimeReceived; | |
4f152496 | 2191 | if (!wtxIn.hashBlock.IsNull()) |
c3f95ef1 LD |
2192 | { |
2193 | if (mapBlockIndex.count(wtxIn.hashBlock)) | |
2194 | { | |
209377a7 | 2195 | int64_t latestNow = wtx.nTimeReceived; |
2196 | int64_t latestEntry = 0; | |
c3f95ef1 LD |
2197 | { |
2198 | // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future | |
51ed9ec9 | 2199 | int64_t latestTolerated = latestNow + 300; |
ddb709e9 LD |
2200 | std::list<CAccountingEntry> acentries; |
2201 | TxItems txOrdered = OrderedTxItems(acentries); | |
c3f95ef1 LD |
2202 | for (TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) |
2203 | { | |
2204 | CWalletTx *const pwtx = (*it).second.first; | |
2205 | if (pwtx == &wtx) | |
2206 | continue; | |
2207 | CAccountingEntry *const pacentry = (*it).second.second; | |
51ed9ec9 | 2208 | int64_t nSmartTime; |
c3f95ef1 LD |
2209 | if (pwtx) |
2210 | { | |
2211 | nSmartTime = pwtx->nTimeSmart; | |
2212 | if (!nSmartTime) | |
2213 | nSmartTime = pwtx->nTimeReceived; | |
2214 | } | |
2215 | else | |
2216 | nSmartTime = pacentry->nTime; | |
2217 | if (nSmartTime <= latestTolerated) | |
2218 | { | |
2219 | latestEntry = nSmartTime; | |
2220 | if (nSmartTime > latestNow) | |
2221 | latestNow = nSmartTime; | |
2222 | break; | |
2223 | } | |
2224 | } | |
2225 | } | |
2226 | ||
209377a7 | 2227 | int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); |
c3f95ef1 LD |
2228 | wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); |
2229 | } | |
2230 | else | |
5262fde0 | 2231 | LogPrintf("AddToWallet(): found %s in block %s not in index\n", |
805344dc | 2232 | wtxIn.GetHash().ToString(), |
7d9d134b | 2233 | wtxIn.hashBlock.ToString()); |
c3f95ef1 | 2234 | } |
93a18a36 | 2235 | AddToSpends(hash); |
9c7722b7 | 2236 | } |
e8ef3da7 WL |
2237 | |
2238 | bool fUpdated = false; | |
2239 | if (!fInsertedNew) | |
2240 | { | |
2241 | // Merge | |
4f152496 | 2242 | if (!wtxIn.hashBlock.IsNull() && wtxIn.hashBlock != wtx.hashBlock) |
e8ef3da7 WL |
2243 | { |
2244 | wtx.hashBlock = wtxIn.hashBlock; | |
2245 | fUpdated = true; | |
2246 | } | |
2247 | if (wtxIn.nIndex != -1 && (wtxIn.vMerkleBranch != wtx.vMerkleBranch || wtxIn.nIndex != wtx.nIndex)) | |
2248 | { | |
2249 | wtx.vMerkleBranch = wtxIn.vMerkleBranch; | |
2250 | wtx.nIndex = wtxIn.nIndex; | |
2251 | fUpdated = true; | |
2252 | } | |
ac1c9435 | 2253 | if (UpdatedNoteData(wtxIn, wtx)) { |
c3a7307a JG |
2254 | fUpdated = true; |
2255 | } | |
e8ef3da7 WL |
2256 | if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) |
2257 | { | |
2258 | wtx.fFromMe = wtxIn.fFromMe; | |
2259 | fUpdated = true; | |
2260 | } | |
e8ef3da7 WL |
2261 | } |
2262 | ||
0cc87e8f | 2263 | //// debug log out |
2264 | if (fDebug) | |
2265 | { | |
2266 | LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); | |
2267 | } | |
e8ef3da7 WL |
2268 | |
2269 | // Write to disk | |
2270 | if (fInsertedNew || fUpdated) | |
44bc988e | 2271 | if (!wtx.WriteToDisk(pwalletdb)) |
e8ef3da7 | 2272 | return false; |
ee4b170c | 2273 | |
93a18a36 GA |
2274 | // Break debit/credit balance caches: |
2275 | wtx.MarkDirty(); | |
e8ef3da7 | 2276 | |
fe4a6550 WL |
2277 | // Notify UI of new or updated transaction |
2278 | NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); | |
cae686d3 | 2279 | |
2280 | // notify an external script when a wallet transaction comes in or is updated | |
2281 | std::string strCmd = GetArg("-walletnotify", ""); | |
2282 | ||
2283 | if ( !strCmd.empty()) | |
2284 | { | |
805344dc | 2285 | boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); |
cae686d3 | 2286 | boost::thread t(runCommand, strCmd); // thread runs free |
2287 | } | |
2288 | ||
fe4a6550 | 2289 | } |
e8ef3da7 WL |
2290 | return true; |
2291 | } | |
2292 | ||
ac1c9435 JG |
2293 | bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) |
2294 | { | |
3a83e7c9 S |
2295 | bool unchangedSproutFlag = (wtxIn.mapSproutNoteData.empty() || wtxIn.mapSproutNoteData == wtx.mapSproutNoteData); |
2296 | if (!unchangedSproutFlag) { | |
2297 | auto tmp = wtxIn.mapSproutNoteData; | |
2298 | // Ensure we keep any cached witnesses we may already have | |
2299 | for (const std::pair <JSOutPoint, SproutNoteData> nd : wtx.mapSproutNoteData) { | |
2300 | if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) { | |
2301 | tmp.at(nd.first).witnesses.assign( | |
2302 | nd.second.witnesses.cbegin(), nd.second.witnesses.cend()); | |
2303 | } | |
2304 | tmp.at(nd.first).witnessHeight = nd.second.witnessHeight; | |
2305 | } | |
2306 | // Now copy over the updated note data | |
2307 | wtx.mapSproutNoteData = tmp; | |
ac1c9435 | 2308 | } |
3a83e7c9 S |
2309 | |
2310 | bool unchangedSaplingFlag = (wtxIn.mapSaplingNoteData.empty() || wtxIn.mapSaplingNoteData == wtx.mapSaplingNoteData); | |
2311 | if (!unchangedSaplingFlag) { | |
2312 | auto tmp = wtxIn.mapSaplingNoteData; | |
2313 | // Ensure we keep any cached witnesses we may already have | |
2314 | ||
2315 | for (const std::pair <SaplingOutPoint, SaplingNoteData> nd : wtx.mapSaplingNoteData) { | |
2316 | if (tmp.count(nd.first) && nd.second.witnesses.size() > 0) { | |
2317 | tmp.at(nd.first).witnesses.assign( | |
2318 | nd.second.witnesses.cbegin(), nd.second.witnesses.cend()); | |
2319 | } | |
2320 | tmp.at(nd.first).witnessHeight = nd.second.witnessHeight; | |
ac1c9435 | 2321 | } |
3a83e7c9 S |
2322 | |
2323 | // Now copy over the updated note data | |
2324 | wtx.mapSaplingNoteData = tmp; | |
ac1c9435 | 2325 | } |
3a83e7c9 S |
2326 | |
2327 | return !unchangedSproutFlag || !unchangedSaplingFlag; | |
ac1c9435 JG |
2328 | } |
2329 | ||
b27ae18a | 2330 | std::pair<bool, bool> CWallet::CheckAuthority(const CIdentity &identity) |
5bc89dab | 2331 | { |
14f010d8 | 2332 | std::pair<bool, bool> canSignCanSpend({false, false}); |
b27ae18a | 2333 | if (!identity.IsValidUnrevoked()) |
2334 | { | |
2335 | return canSignCanSpend; | |
2336 | } | |
5bc89dab | 2337 | std::set<CIdentityID> keySet; |
2338 | ||
2339 | // determine our status of cansign or canspend for this new ID | |
b27ae18a | 2340 | for (auto key : identity.primaryAddresses) |
5bc89dab | 2341 | { |
2342 | CKeyID keyID = CKeyID(GetDestinationID(key)); | |
2343 | if (HaveKey(keyID)) | |
2344 | { | |
2345 | keySet.insert(keyID); | |
2346 | } | |
2347 | } | |
2348 | ||
2349 | // if we have enough keys to fully authorize, it is ours | |
b27ae18a | 2350 | if (keySet.size() >= identity.minSigs) |
5bc89dab | 2351 | { |
2352 | canSignCanSpend.first = true; | |
2353 | canSignCanSpend.second = true; | |
2354 | } | |
2355 | else if (keySet.size()) | |
2356 | { | |
2357 | canSignCanSpend.first = true; | |
5bc89dab | 2358 | } |
2359 | return canSignCanSpend; | |
2360 | } | |
2361 | ||
2362 | bool CWallet::MarkIdentityDirty(const CIdentityID &idID) | |
2363 | { | |
2364 | bool found = false; | |
2365 | // if we already had signing authority, but not spending, enumerate wallet transactions sent to this ID and mark them dirty | |
2366 | // for proper balance calculation | |
2367 | for (auto &txidAndWtx : mapWallet) | |
2368 | { | |
2369 | bool dirty = false; | |
2370 | txnouttype txType; | |
2371 | std::vector<CTxDestination> addresses; | |
2372 | int minSigs; | |
2373 | for (auto txout : txidAndWtx.second.vout) | |
2374 | { | |
2375 | if (txout.scriptPubKey.IsPayToCryptoCondition() && ExtractDestinations(txout.scriptPubKey, txType, addresses, minSigs)) | |
2376 | { | |
2377 | for (auto dest : addresses) | |
2378 | { | |
2379 | if (GetDestinationID(dest) == idID) | |
2380 | { | |
2381 | dirty = true; | |
2382 | found = true; | |
2383 | break; | |
2384 | } | |
2385 | } | |
2386 | } | |
2387 | } | |
2388 | if (dirty) | |
2389 | { | |
2390 | txidAndWtx.second.MarkDirty(); | |
2391 | } | |
2392 | } | |
2393 | return found; | |
2394 | } | |
2395 | ||
5b40d886 MF |
2396 | /** |
2397 | * Add a transaction to the wallet, or update it. | |
2398 | * pblock is optional, but should be provided if the transaction is known to be in a block. | |
2399 | * If fUpdate is true, existing transactions will be updated. | |
3ff68c50 JG |
2400 | * |
2401 | * If pblock is null, this transaction has either recently entered the mempool from the | |
2402 | * network, is re-entering the mempool after a block was disconnected, or is exiting the | |
2403 | * mempool because it conflicts with another transaction. In all these cases, if there is | |
2404 | * an existing wallet transaction, the wallet transaction's Merkle branch data is _not_ | |
2405 | * updated; instead, the transaction being in the mempool or conflicted is determined on | |
2406 | * the fly in CMerkleTx::GetDepthInMainChain(). | |
5b40d886 | 2407 | */ |
d38da59b | 2408 | bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) |
e8ef3da7 | 2409 | { |
e8ef3da7 | 2410 | { |
53d56881 | 2411 | AssertLockHeld(cs_wallet); |
e2c86312 | 2412 | AssertLockHeld(cs_main); |
a20f2622 | 2413 | uint256 txHash = tx.GetHash(); |
44ac8ad4 | 2414 | |
d00f15b4 | 2415 | bool fExisted = mapWallet.count(txHash) != 0; |
6cc4a62c | 2416 | if (fExisted && !fUpdate) return false; |
78584ef7 | 2417 | auto sproutNoteData = FindMySproutNotes(tx); |
a4ecd0fa EOW |
2418 | auto saplingNoteDataAndAddressesToAdd = FindMySaplingNotes(tx); |
2419 | auto saplingNoteData = saplingNoteDataAndAddressesToAdd.first; | |
2420 | auto addressesToAdd = saplingNoteDataAndAddressesToAdd.second; | |
2421 | for (const auto &addressToAdd : addressesToAdd) { | |
2422 | if (!AddSaplingIncomingViewingKey(addressToAdd.second, addressToAdd.first)) { | |
2423 | return false; | |
2424 | } | |
2425 | } | |
b3ea772e | 2426 | |
efecad16 | 2427 | uint32_t nHeight = 0; |
2428 | if (pblock) | |
2429 | { | |
2430 | auto blkIndexIt = mapBlockIndex.find(pblock->GetHash()); | |
2431 | if (blkIndexIt != mapBlockIndex.end()) | |
2432 | { | |
2433 | nHeight = blkIndexIt->second->GetHeight(); | |
2434 | } | |
2435 | else | |
2436 | { | |
2437 | // this should never happen | |
2438 | assert(false); | |
2439 | } | |
2440 | } | |
2441 | ||
b3ea772e | 2442 | for (auto output : tx.vout) |
2443 | { | |
2444 | bool canSpend = false; | |
2445 | COptCCParams p; | |
2446 | ||
2f537fa1 | 2447 | if (output.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.version >= p.VERSION_V3) |
b3ea772e | 2448 | { |
5bc89dab | 2449 | CIdentityMapValue identity; |
e14c324c | 2450 | |
5bc89dab | 2451 | if (p.evalCode == EVAL_IDENTITY_PRIMARY && p.vData.size() && (*(CIdentity *)&identity = CIdentity(p.vData[0])).IsValid()) |
b3ea772e | 2452 | { |
e2c86312 | 2453 | identity.txid = txHash; |
22adf258 | 2454 | CIdentityMapKey idMapKey = CIdentityMapKey(identity.GetID(), |
5ae5090d | 2455 | nHeight, |
2456 | 1, | |
2457 | CIdentityMapKey::VALID); | |
5bc89dab | 2458 | |
b3ea772e | 2459 | std::set<CKeyID> keySet; |
22adf258 | 2460 | CIdentityID idID(identity.GetID()); |
5ae5090d | 2461 | int blockOrder = 1; |
5bc89dab | 2462 | bool doneWithID = false; |
2463 | ||
2464 | std::pair<CIdentityMapKey, CIdentityMapValue> idHistory; | |
2465 | ||
e2c86312 | 2466 | // if adding, current is what it will be, idHistory is what it was |
2467 | // if we are deleting, current identity is what it was, idHistory is what it will be | |
5bc89dab | 2468 | std::pair<bool, bool> wasCanSignCanSpend({false, false}); |
e54e3c24 | 2469 | std::pair<bool, bool> canSignCanSpend(CheckAuthority(identity)); |
b3ea772e | 2470 | |
e2c86312 | 2471 | // if the new identity is revoked, the recovery identity holds can sign/can spend authority |
d26748d5 | 2472 | if (identity.IsRevoked()) |
2473 | { | |
e2c86312 | 2474 | // if it's revoked, default is no authority for primary addresses, but we will have authority if |
2475 | // we have control over the recovery identity | |
d26748d5 | 2476 | std::pair<CIdentityMapKey, CIdentityMapValue> recoveryAuthority; |
2477 | if (GetIdentity(identity.recoveryAuthority, recoveryAuthority, nHeight ? nHeight : INT_MAX)) | |
2478 | { | |
2479 | canSignCanSpend = CheckAuthority(recoveryAuthority.second); | |
2480 | } | |
2481 | } | |
2482 | ||
e54e3c24 | 2483 | // does identity already exist in this wallet? |
5bc89dab | 2484 | if (GetIdentity(idID, idHistory, nHeight ? nHeight : INT_MAX)) |
b3ea772e | 2485 | { |
e54e3c24 | 2486 | wasCanSignCanSpend = CheckAuthority(idHistory.second); |
2487 | ||
e2c86312 | 2488 | if (idHistory.second.IsRevoked()) |
420019f6 | 2489 | { |
2490 | std::pair<CIdentityMapKey, CIdentityMapValue> oldRecoveryAuthority; | |
2491 | std::pair<bool, bool> auxCSCS; | |
2492 | // if we hold the recovery authority in our wallet, then set wasCanSignCanSpend pair to true | |
2493 | if (GetIdentity(idHistory.second.recoveryAuthority, | |
2494 | oldRecoveryAuthority, | |
2495 | nHeight ? nHeight : INT_MAX)) | |
2496 | { | |
2497 | auxCSCS = CheckAuthority(oldRecoveryAuthority.second); | |
2498 | wasCanSignCanSpend = std::pair<bool, bool>({wasCanSignCanSpend.first || auxCSCS.first, | |
2499 | wasCanSignCanSpend.second || auxCSCS.second}); | |
2500 | } | |
2501 | } | |
2502 | ||
e2c86312 | 2503 | // if this is an add of the initial registration, delete all other instances of the ID |
d00f15b4 | 2504 | if (CNameReservation(tx).IsValid()) |
2505 | { | |
2506 | while (GetIdentity(idID, idHistory)) | |
2507 | { | |
e2c86312 | 2508 | // any definition of this identity in this wallet must be |
2509 | // invalid now | |
d00f15b4 | 2510 | RemoveIdentity(idHistory.first, idHistory.second.txid); |
e2c86312 | 2511 | if (idHistory.second.txid != txHash) |
2512 | { | |
2513 | // any definition of this ID in this wallet that is not this definition | |
2514 | // must also be on an invalid transaction | |
2515 | EraseFromWallet(idHistory.second.txid); | |
2516 | } | |
e54e3c24 | 2517 | // set wasCanSignCanSpend to true, true to delete any dependent transactions |
2518 | wasCanSignCanSpend = {true, true}; | |
d00f15b4 | 2519 | } |
2520 | idHistory = std::pair<CIdentityMapKey, CIdentityMapValue>(); | |
e2c86312 | 2521 | wasCanSignCanSpend = std::pair<bool, bool>({false, false}); |
d00f15b4 | 2522 | } |
2523 | else if (nHeight && idHistory.first.blockHeight == nHeight && idHistory.second.txid != identity.txid) | |
e14c324c | 2524 | { |
e54e3c24 | 2525 | // this is one of more than one identity records in the same block |
22adf258 | 2526 | std::vector<std::pair<CIdentityMapKey, CIdentityMapValue>> thisHeightIdentities; |
2527 | CIdentityMapKey heightKey(idID, nHeight); | |
2528 | GetIdentity(heightKey, heightKey, thisHeightIdentities); | |
2529 | ||
93459a00 | 2530 | std::map<uint256, std::pair<CIdentityMapKey, CIdentityMapValue>> firstIDMap; |
22adf258 | 2531 | for (auto &foundID : thisHeightIdentities) |
2532 | { | |
2533 | firstIDMap[foundID.second.txid] = foundID; | |
2534 | } | |
2535 | ||
93459a00 | 2536 | if (firstIDMap.count(identity.txid)) |
2537 | { | |
e54e3c24 | 2538 | doneWithID = true; |
93459a00 | 2539 | } |
2540 | else | |
2541 | { | |
2542 | blockOrder = thisHeightIdentities.size() + 1; | |
2543 | firstIDMap.insert(make_pair(identity.txid, | |
2544 | make_pair(CIdentityMapKey(idID, | |
2545 | nHeight, | |
2546 | blockOrder, | |
d26748d5 | 2547 | (canSignCanSpend.first ? CIdentityMapKey::CAN_SIGN : 0) + canSignCanSpend.second ? CIdentityMapKey::CAN_SPEND : 0), |
93459a00 | 2548 | identity))); |
93459a00 | 2549 | |
e54e3c24 | 2550 | // now we have all the entries of the specified height, including those from before and the new one in the firstIDMap |
2551 | // the #1 in the block is one that has none of its input txes in the map. the last is not present in any input tx | |
2552 | // to sort, we make a new map, indexed by the one that it spends, then follow the chain | |
2553 | std::map<uint256, std::pair<CIdentityMapKey, CIdentityMapValue>> indexedByPrior; | |
2554 | std::pair<CIdentityMapKey, CIdentityMapValue> firstInBlock; | |
22adf258 | 2555 | |
e54e3c24 | 2556 | for (auto &idEntry : firstIDMap) |
22adf258 | 2557 | { |
e54e3c24 | 2558 | uint256 spendsTxId; |
2559 | CTransaction entryTx; | |
2560 | uint256 blkHash; | |
2561 | if (!myGetTransaction(idEntry.first, entryTx, blkHash)) | |
22adf258 | 2562 | { |
e54e3c24 | 2563 | LogPrint("%s - error: cannot retrieve transaction %s during sort of identity transactions in block, blockchain state may be corrupt and need resynchronization\n", __func__, idEntry.first.GetHex().c_str()); |
22adf258 | 2564 | } |
e54e3c24 | 2565 | else |
22adf258 | 2566 | { |
e54e3c24 | 2567 | bool isFirst = true; |
2568 | for (auto &input : entryTx.vin) | |
22adf258 | 2569 | { |
e54e3c24 | 2570 | auto idMapIt = firstIDMap.find(input.prevout.hash); |
2571 | if (idMapIt != firstIDMap.end()) | |
2572 | { | |
2573 | indexedByPrior[input.prevout.hash] = idEntry.second; | |
2574 | isFirst = false; | |
2575 | } | |
2576 | } | |
2577 | if (isFirst) | |
2578 | { | |
2579 | // this should first be added solo, so #1 should always be set | |
2580 | if (idEntry.second.first.blockOrder != 1) | |
2581 | { | |
2582 | LogPrint("%s - error: unexpected block order in %s\n", __func__, idEntry.first.GetHex().c_str()); | |
2583 | } | |
2584 | firstInBlock = idEntry.second; | |
22adf258 | 2585 | } |
22adf258 | 2586 | } |
2587 | } | |
22adf258 | 2588 | |
e54e3c24 | 2589 | if (!firstInBlock.first.IsValid()) |
22adf258 | 2590 | { |
e54e3c24 | 2591 | LogPrint("%s - error: missing first in block\n", __func__); |
2592 | } | |
2593 | else | |
2594 | { | |
2595 | // now validate that from 1st to last, we have order correct | |
2596 | std::pair<CIdentityMapKey, CIdentityMapValue> *pCurID; | |
2597 | int i = 1; | |
2598 | for (pCurID = &firstInBlock; pCurID; i++) | |
22adf258 | 2599 | { |
e54e3c24 | 2600 | if (pCurID->first.blockOrder != i) |
2601 | { | |
2602 | LogPrint("%s - error: incorrect block order in entry %s\n", __func__, pCurID->second.txid.GetHex().c_str()); | |
2603 | printf("%s - error: incorrect block order in entry %s\n", __func__, pCurID->second.txid.GetHex().c_str()); | |
2604 | } | |
22adf258 | 2605 | } |
2606 | } | |
2607 | } | |
e14c324c | 2608 | } |
d00f15b4 | 2609 | else if (nHeight && idHistory.first.blockHeight == nHeight) |
b3ea772e | 2610 | { |
e54e3c24 | 2611 | // nHeight means this is an add, it has the same txid as an ID already present in the wallet, so we can ignore |
2612 | doneWithID = true; | |
5bc89dab | 2613 | } |
e54e3c24 | 2614 | else |
5bc89dab | 2615 | { |
2616 | if (idHistory.first.flags & idHistory.first.CAN_SPEND) | |
2617 | { | |
2618 | wasCanSignCanSpend.first = true; | |
2619 | wasCanSignCanSpend.second = true; | |
2620 | } | |
2621 | else if ((idHistory.first.flags & idHistory.first.CAN_SIGN)) | |
2622 | { | |
2623 | wasCanSignCanSpend.first = true; | |
2624 | } | |
e54e3c24 | 2625 | // if we are supposed to remove the last entry, do so |
2626 | if (!pblock && txHash == idHistory.second.txid) | |
2627 | { | |
2628 | RemoveIdentity(idHistory.first, idHistory.second.txid); | |
2629 | } | |
5bc89dab | 2630 | } |
b3ea772e | 2631 | } |
5bc89dab | 2632 | else if (!pblock) |
b3ea772e | 2633 | { |
5bc89dab | 2634 | // not present, nothing to delete |
2635 | doneWithID = true; | |
b3ea772e | 2636 | } |
e14c324c | 2637 | |
5bc89dab | 2638 | if (!doneWithID) |
b3ea772e | 2639 | { |
5bc89dab | 2640 | if (pblock) |
e14c324c | 2641 | { |
956ab931 | 2642 | // if we used to be able to sign with this identity, can now, or we put it on a manual hold, and it's not invalid or blacklisted, store it |
2643 | if ((wasCanSignCanSpend.first || canSignCanSpend.first || (idHistory.first.flags & idHistory.first.MANUAL_HOLD)) && !(idHistory.first.flags & idHistory.first.BLACKLIST)) | |
e14c324c | 2644 | { |
956ab931 | 2645 | idMapKey = CIdentityMapKey(identity.GetID(), |
2646 | nHeight, | |
2647 | blockOrder, | |
2648 | idHistory.first.VALID | | |
2649 | ((idHistory.second.IsValid() ? idHistory.first.flags : 0) & idHistory.first.MANUAL_HOLD) | | |
2650 | (canSignCanSpend.first ? idHistory.first.CAN_SIGN : 0) | | |
2651 | (canSignCanSpend.second ? idHistory.first.CAN_SPEND : 0)); | |
c0724caf | 2652 | AddUpdateIdentity(idMapKey, identity); |
e14c324c | 2653 | } |
2654 | } | |
5bc89dab | 2655 | else |
e14c324c | 2656 | { |
e54e3c24 | 2657 | std::pair<bool, bool> swapBools = canSignCanSpend; |
2658 | canSignCanSpend = wasCanSignCanSpend; | |
2659 | wasCanSignCanSpend = swapBools; | |
e14c324c | 2660 | } |
5bc89dab | 2661 | |
996f6c33 | 2662 | // store transitions as needed in the wallet |
a20f2622 | 2663 | if (canSignCanSpend.first != wasCanSignCanSpend.first || canSignCanSpend.second != wasCanSignCanSpend.second) |
e14c324c | 2664 | { |
dcc9eba2 | 2665 | // mark all transactions dirty to recalculate numbers |
2666 | for (auto &txidAndWtx : mapWallet) | |
2667 | { | |
2668 | // mark the whole wallet dirty. if this is an issue, we can optimize. | |
2669 | txidAndWtx.second.MarkDirty(); | |
2670 | } | |
2671 | ||
5bc89dab | 2672 | if (canSignCanSpend.first != wasCanSignCanSpend.first) |
2673 | { | |
2f537fa1 | 2674 | if (canSignCanSpend.first) |
2675 | { | |
7428b90b | 2676 | // add all UTXOs sent to this ID to this wallet |
2677 | // and also check any other outputs that are sent to this ID to see if they have | |
2678 | // been spent, and if so, add them as spends as well | |
a20f2622 | 2679 | std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs; |
2a7946a4 | 2680 | std::set<uint256> unspentTxSet; |
2681 | GetAddressUnspent(idID, CScript::P2ID, unspentOutputs); | |
2682 | ||
2683 | // first, put all the txids of the UTXOs in a set to check intersection with wallet txes | |
2684 | // that may already include outputs to the newly controlled ID. we also need to check wallet | |
2685 | // txes that are not UTXOs to record spends, rather than considering them UTXOs | |
2686 | for (auto &newOut : unspentOutputs) | |
a20f2622 | 2687 | { |
2a7946a4 | 2688 | unspentTxSet.insert(newOut.first.txhash); |
2689 | } | |
e54e3c24 | 2690 | |
2a7946a4 | 2691 | // now, look through existing wallet txes for outputs to the ID, which we did not, but now will |
2692 | // consider as ours and add them to the outputs that need to be checked | |
2693 | for (auto &wtx : mapWallet) | |
2694 | { | |
2695 | // if it's not in the chain, or already in the unspent set to check, we don't need to add it to | |
2696 | // anything | |
2697 | if (!wtx.second.IsInMainChain() || | |
2698 | unspentTxSet.count(wtx.first)) | |
2699 | { | |
2700 | continue; | |
2701 | } | |
2702 | int unspentOutputsSize = unspentOutputs.size(); | |
2703 | for (int k = 0; k < wtx.second.vout.size(); k++) | |
2704 | { | |
2705 | const CTxOut &oneOut = wtx.second.vout[k]; | |
adbb6236 | 2706 | txnouttype newTypeRet; |
2707 | std::vector<CTxDestination> newAddressRet; | |
2708 | int newNRequired; | |
2709 | bool newCanSign, newCanSpend; | |
2a7946a4 | 2710 | // if we think this output isn't spent and we couldn't sign for it, but the ID enables us to, |
2711 | // then we need to check it further to see if we need to add other spending txes now | |
2712 | if (IsSpent(wtx.first, k) || | |
2713 | (ExtractDestinations(oneOut.scriptPubKey, newTypeRet, newAddressRet, newNRequired, this, &newCanSign, &newCanSpend, nHeight == 0 ? INT_MAX : nHeight) && newCanSign)) | |
2714 | { | |
2715 | continue; | |
2716 | } | |
2717 | for (auto &oneDest : newAddressRet) | |
a20f2622 | 2718 | { |
2a7946a4 | 2719 | if (oneDest.which() == COptCCParams::ADDRTYPE_ID && GetDestinationID(oneDest) == idID) |
a20f2622 | 2720 | { |
2a7946a4 | 2721 | CAddressUnspentKey unspentKey(CScript::P2ID, idID, wtx.first, k); |
2722 | CBlockIndex *pIndex = mapBlockIndex[wtx.second.hashBlock]; | |
2723 | unspentOutputs.push_back( | |
2724 | make_pair(unspentKey, CAddressUnspentValue(oneOut.nValue, oneOut.scriptPubKey, pIndex->GetHeight()))); | |
2725 | // if we add one on a tx, no need to check more here | |
2726 | break; | |
a20f2622 | 2727 | } |
2a7946a4 | 2728 | } |
2729 | // we only need to add one output to check all outputs below | |
2730 | if (unspentOutputsSize < unspentOutputs.size()) | |
2731 | { | |
2732 | break; | |
2733 | } | |
2734 | } | |
2735 | if (unspentOutputsSize < unspentOutputs.size()) | |
2736 | { | |
2737 | continue; | |
2738 | } | |
2739 | } | |
a20f2622 | 2740 | |
2a7946a4 | 2741 | auto consensus = Params().GetConsensus(); |
2742 | for (auto &newOut : unspentOutputs) | |
2743 | { | |
2744 | // Do not flush the wallet here for performance reasons | |
2745 | // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism | |
2746 | CWalletDB walletdb(strWalletFile, "r+", false); | |
2747 | ||
2748 | txnouttype newTypeRet; | |
2749 | std::vector<CTxDestination> newAddressRet; | |
2750 | int newNRequired; | |
2751 | bool newCanSign, newCanSpend; | |
2752 | const CWalletTx *pWtx = GetWalletTx(newOut.first.txhash); | |
2753 | ||
2754 | // check if already present and if its a CC output, so we know it can be sent to an identity | |
2755 | if (pWtx == nullptr && newOut.second.script.IsPayToCryptoCondition()) | |
2756 | { | |
2757 | CWalletTx wtx; | |
2758 | if (!(ExtractDestinations(newOut.second.script, newTypeRet, newAddressRet, newNRequired, this, &newCanSign, &newCanSpend, nHeight == 0 ? INT_MAX : nHeight + 1) && newCanSign)) | |
2759 | { | |
2760 | continue; | |
2761 | } | |
2762 | uint256 blkHash; | |
2763 | CTransaction newTx; | |
2764 | if (myGetTransaction(newOut.first.txhash, newTx, blkHash)) | |
2765 | { | |
2766 | wtx = CWalletTx(this, newTx); | |
2767 | ||
2768 | // Get merkle branch if transaction was found in a block | |
2769 | CBlock block; | |
2770 | auto blkIndexIt = mapBlockIndex.find(blkHash); | |
2771 | if (!blkHash.IsNull() && blkIndexIt != mapBlockIndex.end() && chainActive.Contains(blkIndexIt->second)) | |
2772 | { | |
2773 | // if it's supposed to be in a block, but can't be loaded, don't add without merkle | |
2774 | if (!ReadBlockFromDisk(block, blkIndexIt->second, consensus)) | |
a20f2622 | 2775 | { |
2a7946a4 | 2776 | continue; |
a20f2622 | 2777 | } |
2a7946a4 | 2778 | wtx.SetMerkleBranch(block); |
adbb6236 | 2779 | } |
2a7946a4 | 2780 | |
2781 | AddToWallet(wtx, false, &walletdb); | |
2782 | pWtx = GetWalletTx(newOut.first.txhash); | |
adbb6236 | 2783 | } |
2a7946a4 | 2784 | } |
2785 | else if (pWtx = GetWalletTx(newOut.first.txhash)) | |
2786 | { | |
2787 | if (!(ExtractDestinations(newOut.second.script, newTypeRet, newAddressRet, newNRequired, this, &newCanSign, &newCanSpend, nHeight == 0 ? INT_MAX : nHeight + 1) && newCanSign)) | |
adbb6236 | 2788 | { |
2a7946a4 | 2789 | continue; |
adbb6236 | 2790 | } |
2a7946a4 | 2791 | } |
7428b90b | 2792 | |
2a7946a4 | 2793 | // if we were or are now in the wallet, we need to see if we should record new spends |
2794 | if (pWtx != nullptr && newOut.second.script.IsPayToCryptoCondition()) | |
2795 | { | |
2796 | // while we know there is an unspent index to this ID on the new transaction output, we don't know | |
2797 | // if there are other outputs to this ID on the transaction, which are already spent. | |
2798 | // if so, we need to record the spends in the wallet as well, or it will add them but | |
2799 | // not consider them spent. | |
2800 | uint256 spendBlkHash; | |
2801 | CTransaction spendTx; | |
2802 | std::vector<CTxOut> checkIfSpent = pWtx->vout; | |
2803 | for (int i = 0; i < checkIfSpent.size(); i++) | |
adbb6236 | 2804 | { |
2a7946a4 | 2805 | // if it really came from the unspent index and is the same output, don't bother looking for a spend |
2806 | if (unspentTxSet.count(newOut.first.txhash) && newOut.first.index == i) | |
adbb6236 | 2807 | { |
2a7946a4 | 2808 | continue; |
2809 | } | |
7428b90b | 2810 | |
2a7946a4 | 2811 | // if we can't spend it, no need to check for spends |
2812 | if (!(ExtractDestinations(checkIfSpent[i].scriptPubKey, | |
2813 | newTypeRet, | |
2814 | newAddressRet, | |
2815 | newNRequired, | |
2816 | this, | |
2817 | &newCanSign, | |
2818 | &newCanSpend, | |
2819 | nHeight == 0 ? INT_MAX : nHeight + 1) && newCanSign)) | |
2820 | { | |
2821 | continue; | |
2822 | } | |
7428b90b | 2823 | |
2a7946a4 | 2824 | CSpentIndexValue spentInfo; |
2825 | CSpentIndexKey spentKey(newOut.first.txhash, i); | |
2826 | if (GetSpentIndex(spentKey, spentInfo)) | |
2827 | { | |
2828 | if (GetWalletTx(spentInfo.txid) == nullptr && | |
2829 | spentInfo.blockHeight <= nHeight && | |
2830 | myGetTransaction(spentInfo.txid, spendTx, spendBlkHash) && !spendBlkHash.IsNull()) | |
adbb6236 | 2831 | { |
2a7946a4 | 2832 | CWalletTx spendWtx(this, spendTx); |
2833 | ||
2834 | // Get merkle branch if transaction was found in a block | |
2835 | CBlock spendBlock; | |
2836 | auto spendBlkIndexIt = mapBlockIndex.find(spendBlkHash); | |
2837 | if (spendBlkIndexIt != mapBlockIndex.end() && | |
2838 | chainActive.Contains(spendBlkIndexIt->second) && | |
2839 | ReadBlockFromDisk(spendBlock, spendBlkIndexIt->second, consensus)) | |
7428b90b | 2840 | { |
2a7946a4 | 2841 | spendWtx.SetMerkleBranch(spendBlock); |
2842 | AddToWallet(spendWtx, false, &walletdb); | |
2843 | ||
2844 | // add these outputs to the outputs we need to check if spent | |
2845 | // as long as we are adding spending transactions that are earlier | |
2846 | // or up to this height, we follow the spends | |
2847 | checkIfSpent.insert(checkIfSpent.end(), spendTx.vout.begin(), spendTx.vout.end()); | |
7428b90b | 2848 | } |
2849 | } | |
a20f2622 | 2850 | } |
2851 | } | |
2852 | } | |
2853 | } | |
2f537fa1 | 2854 | } |
2855 | else | |
5bc89dab | 2856 | { |
2857 | // we have gone from canSign to no control over this ID, either by deletion of tx or removal from signers. this will take effect retroactively on deletion and next block on addition | |
2858 | // 1. remove all transactions that have UTXOs sent to this ID and are no longer can sign or can spend for us from the wallet | |
2859 | // 2. if deletion, remove all transactions since the last idHistory that are in the wallet due to this ID | |
2860 | // 3. remove all IDs from the wallet that are found in those removed transactions, are neither canSpend nor canSign, and are neither on manual hold nor present on any remaining transactions | |
7428b90b | 2861 | // 4. remove any transactions that are only in the wallet because they spend an output that was sent to this ID |
5bc89dab | 2862 | |
7428b90b | 2863 | std::set<CIdentityID> idsToCheck = std::set<CIdentityID>(); |
2864 | ||
2865 | if (!pblock) | |
2866 | { | |
2867 | idsToCheck.insert(idID); | |
2868 | } | |
5bc89dab | 2869 | |
2870 | // first and last blocks to consider when deleting spent transactions from the wallet | |
956ab931 | 2871 | uint32_t deleteSpentFrom; |
5bc89dab | 2872 | |
2873 | if (!pblock) | |
2874 | { | |
956ab931 | 2875 | deleteSpentFrom = idHistory.first.blockHeight + 1; |
5bc89dab | 2876 | } |
2877 | else | |
2878 | { | |
956ab931 | 2879 | deleteSpentFrom = idMapKey.blockHeight + 1; |
5bc89dab | 2880 | } |
2881 | ||
7428b90b | 2882 | std::map<const uint256 *, CWalletTx *> txesToErase; |
c6782713 | 2883 | |
5bc89dab | 2884 | for (auto &txidAndWtx : mapWallet) |
2885 | { | |
956ab931 | 2886 | const CBlockIndex *pIndex; |
996f6c33 | 2887 | if (txidAndWtx.second.GetDepthInMainChain(pIndex) > 0 && pIndex->GetHeight() <= deleteSpentFrom) |
5bc89dab | 2888 | { |
956ab931 | 2889 | continue; |
5bc89dab | 2890 | } |
2891 | ||
5bc89dab | 2892 | txnouttype txType; |
2893 | std::vector<CTxDestination> addresses; | |
2894 | int minSigs; | |
2895 | bool eraseTx = true; | |
5bc89dab | 2896 | int i = 0; |
2897 | uint256 hashTx = txidAndWtx.second.GetHash(); | |
2898 | ||
e2c86312 | 2899 | // if we still have z-address notes on the transaction, don't delete |
2900 | auto sprNoteData = FindMySproutNotes(txidAndWtx.second); | |
2901 | auto sapNoteDataAndAddressesToAdd = FindMySaplingNotes(txidAndWtx.second); | |
2902 | if (sprNoteData.size() || sapNoteDataAndAddressesToAdd.first.size()) | |
2903 | { | |
2904 | // don't erase the tx, but check to erase IDs | |
2905 | eraseTx = false; | |
2906 | } | |
2907 | ||
7428b90b | 2908 | // if the tx is spending from another in this wallet, we will not erase it |
2909 | // but check destinations before deciding not to erase IDs | |
adbb6236 | 2910 | if (IsFromMe(txidAndWtx.second, deleteSpentFrom - 1)) |
e2c86312 | 2911 | { |
2912 | eraseTx = false; | |
2913 | } | |
2914 | ||
7428b90b | 2915 | // look for a reason not to delete this tx or IDs it is sent to |
5bc89dab | 2916 | for (auto txout : txidAndWtx.second.vout) |
2917 | { | |
2918 | // we only want to remove UTXOs that are sent to this ID, used to be ours, and are no longer cansign | |
2919 | if (!txout.scriptPubKey.IsPayToCryptoCondition() || IsSpent(hashTx, i)) | |
2920 | { | |
7428b90b | 2921 | // if this is ours, we will not erase the tx |
2922 | // we already checked IsFromMe | |
5bc89dab | 2923 | if (IsMine(txout)) |
2924 | { | |
2925 | eraseTx = false; | |
7428b90b | 2926 | continue; |
5bc89dab | 2927 | } |
5bc89dab | 2928 | } |
2929 | bool canSignOut = false; | |
2930 | bool canSpendOut = false; | |
2931 | ||
adbb6236 | 2932 | if (ExtractDestinations(txout.scriptPubKey, txType, addresses, minSigs, this, &canSignOut, &canSpendOut, nHeight == 0 ? INT_MAX : nHeight + 1)) |
5bc89dab | 2933 | { |
2934 | if (canSignOut || canSpendOut) | |
2935 | { | |
2936 | // we should keep this transaction anyhow, check next | |
2937 | eraseTx = false; | |
e2c86312 | 2938 | continue; |
5bc89dab | 2939 | } |
7428b90b | 2940 | |
5bc89dab | 2941 | for (auto &dest : addresses) |
2942 | { | |
2943 | if (dest.which() == COptCCParams::ADDRTYPE_ID) | |
2944 | { | |
e2c86312 | 2945 | idsToCheck.insert(GetDestinationID(dest)); |
5bc89dab | 2946 | } |
2947 | } | |
2948 | } | |
2949 | ||
2950 | i++; | |
2951 | } | |
2952 | if (eraseTx) | |
2953 | { | |
7428b90b | 2954 | txesToErase.insert(make_pair(&txidAndWtx.first, &txidAndWtx.second)); |
5bc89dab | 2955 | } |
2956 | } | |
c6782713 | 2957 | |
7428b90b | 2958 | for (auto &oneTx : txesToErase) |
c6782713 | 2959 | { |
7428b90b | 2960 | EraseFromWallet(*oneTx.first); |
c6782713 | 2961 | } |
5bc89dab | 2962 | |
996f6c33 | 2963 | if (pblock && idsToCheck.count(idID)) |
2964 | { | |
2965 | // do not remove the current identity that was just added to take away our authority | |
2966 | // that is an important record to keep | |
2967 | idsToCheck.erase(idID); | |
2968 | } | |
2969 | ||
2970 | // now, we've deleted all transactions that were only in the wallet due to our ability to sign with the ID we just lost | |
5bc89dab | 2971 | // loop through all transactions and remove all IDs found in the remaining transactions from our idsToCheck set after we |
2972 | // have gone through all wallet transactions, we can delete all IDs remaining in the idsToCheck set | |
2973 | // that are not on manual hold | |
2974 | for (auto &txidAndWtx : mapWallet) | |
2975 | { | |
2976 | for (auto txout : txidAndWtx.second.vout) | |
2977 | { | |
2978 | if (!txout.scriptPubKey.IsPayToCryptoCondition()) | |
2979 | { | |
2980 | continue; | |
2981 | } | |
2982 | bool canSignOut = false; | |
2983 | bool canSpendOut = false; | |
2984 | txnouttype txType; | |
2985 | std::vector<CTxDestination> addresses; | |
2986 | int minSigs; | |
adbb6236 | 2987 | if (ExtractDestinations(txout.scriptPubKey, txType, addresses, minSigs, this, &canSignOut, &canSpendOut, nHeight == 0 ? INT_MAX : nHeight + 1)) |
5bc89dab | 2988 | { |
2989 | if (canSignOut || canSpendOut) | |
2990 | { | |
2991 | for (auto &dest : addresses) | |
2992 | { | |
2993 | if (dest.which() == COptCCParams::ADDRTYPE_ID) | |
2994 | { | |
2995 | idsToCheck.erase(GetDestinationID(dest)); | |
2996 | if (!idsToCheck.size()) | |
2997 | { | |
2998 | break; | |
2999 | } | |
3000 | } | |
3001 | } | |
e54e3c24 | 3002 | } |
5bc89dab | 3003 | } |
3004 | if (!idsToCheck.size()) | |
3005 | { | |
3006 | break; | |
3007 | } | |
3008 | } | |
3009 | if (!idsToCheck.size()) | |
3010 | { | |
3011 | break; | |
3012 | } | |
3013 | } | |
996f6c33 | 3014 | |
5bc89dab | 3015 | // delete all remaining IDs that are not held for manual hold |
3016 | for (auto &idToRemove : idsToCheck) | |
3017 | { | |
3018 | std::pair<CIdentityMapKey, CIdentityMapValue> identityToRemove; | |
3019 | ||
3020 | // if not cansign or canspend, no transactions we care about relating to it and no manual hold, delete the ID from the wallet | |
956ab931 | 3021 | // also keep the first transition after one we will keep |
5bc89dab | 3022 | if (GetIdentity(idToRemove, identityToRemove) && |
3023 | !((identityToRemove.first.flags & (identityToRemove.first.CAN_SIGN + identityToRemove.first.CAN_SPEND)) || identityToRemove.first.flags & identityToRemove.first.MANUAL_HOLD)) | |
3024 | { | |
d26748d5 | 3025 | std::pair<CIdentityMapKey, CIdentityMapValue> priorIdentity; |
3026 | ||
996f6c33 | 3027 | if (!GetPriorIdentity(identityToRemove.first, priorIdentity) || |
3028 | !((priorIdentity.first.flags & (priorIdentity.first.CAN_SIGN + priorIdentity.first.CAN_SPEND)) || identityToRemove.first.flags & identityToRemove.first.MANUAL_HOLD)) | |
956ab931 | 3029 | { |
d26748d5 | 3030 | // if we don't have recovery on a revoked ID in our wallet, then remove it |
3031 | std::pair<CIdentityMapKey, CIdentityMapValue> recoveryIdentity; | |
3032 | if (!identityToRemove.second.IsRevoked() || !GetIdentity(identityToRemove.second.recoveryAuthority, recoveryIdentity) || !(recoveryIdentity.first.flags & recoveryIdentity.first.CAN_SIGN)) | |
3033 | { | |
3034 | RemoveIdentity(CIdentityMapKey(idToRemove)); | |
3035 | } | |
956ab931 | 3036 | } |
5bc89dab | 3037 | } |
3038 | } | |
3039 | } | |
3040 | } | |
e14c324c | 3041 | } |
b3ea772e | 3042 | } |
3043 | } | |
b3ea772e | 3044 | } |
3045 | } | |
3046 | ||
efecad16 | 3047 | // for IsMine, the default height is max, not 0 |
3048 | nHeight = nHeight == 0 ? INT_MAX : nHeight; | |
3049 | if (fExisted || IsMine(tx, nHeight) || IsFromMe(tx, nHeight) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) | |
6cc4a62c | 3050 | { |
2f537fa1 | 3051 | CWalletTx wtx(this, tx); |
44bc988e | 3052 | |
78584ef7 S |
3053 | if (sproutNoteData.size() > 0) { |
3054 | wtx.SetSproutNoteData(sproutNoteData); | |
3055 | } | |
3056 | ||
3057 | if (saplingNoteData.size() > 0) { | |
3058 | wtx.SetSaplingNoteData(saplingNoteData); | |
be74c80d | 3059 | } |
c3a7307a | 3060 | |
6cc4a62c GA |
3061 | // Get merkle branch if transaction was found in a block |
3062 | if (pblock) | |
4b0deb3b | 3063 | wtx.SetMerkleBranch(*pblock); |
44bc988e CL |
3064 | |
3065 | // Do not flush the wallet here for performance reasons | |
3066 | // this is safe, as in case of a crash, we rescan the necessary blocks on startup through our SetBestChain-mechanism | |
3067 | CWalletDB walletdb(strWalletFile, "r+", false); | |
3068 | ||
3069 | return AddToWallet(wtx, false, &walletdb); | |
6cc4a62c | 3070 | } |
e8ef3da7 | 3071 | } |
e8ef3da7 WL |
3072 | return false; |
3073 | } | |
3074 | ||
d38da59b | 3075 | void CWallet::SyncTransaction(const CTransaction& tx, const CBlock* pblock) |
93a18a36 | 3076 | { |
7c8111f3 | 3077 | LOCK(cs_wallet); |
d38da59b | 3078 | if (!AddToWalletIfInvolvingMe(tx, pblock, true)) |
93a18a36 GA |
3079 | return; // Not one of ours |
3080 | ||
ac1c9435 JG |
3081 | MarkAffectedTransactionsDirty(tx); |
3082 | } | |
3083 | ||
3084 | void CWallet::MarkAffectedTransactionsDirty(const CTransaction& tx) | |
3085 | { | |
93a18a36 GA |
3086 | // If a transaction changes 'conflicted' state, that changes the balance |
3087 | // available of the outputs it spends. So force those to be | |
3088 | // recomputed, also: | |
3089 | BOOST_FOREACH(const CTxIn& txin, tx.vin) | |
3090 | { | |
3091 | if (mapWallet.count(txin.prevout.hash)) | |
3092 | mapWallet[txin.prevout.hash].MarkDirty(); | |
3093 | } | |
f57f76d7 | 3094 | for (const JSDescription& jsdesc : tx.vJoinSplit) { |
8db7e25c | 3095 | for (const uint256& nullifier : jsdesc.nullifiers) { |
f41bf503 | 3096 | if (mapSproutNullifiersToNotes.count(nullifier) && |
dae1c420 | 3097 | mapWallet.count(mapSproutNullifiersToNotes[nullifier].hash)) { |
f41bf503 | 3098 | mapWallet[mapSproutNullifiersToNotes[nullifier].hash].MarkDirty(); |
8db7e25c JG |
3099 | } |
3100 | } | |
3101 | } | |
dae1c420 S |
3102 | |
3103 | for (const SpendDescription &spend : tx.vShieldedSpend) { | |
3104 | uint256 nullifier = spend.nullifier; | |
3105 | if (mapSaplingNullifiersToNotes.count(nullifier) && | |
3106 | mapWallet.count(mapSaplingNullifiersToNotes[nullifier].hash)) { | |
3107 | mapWallet[mapSaplingNullifiersToNotes[nullifier].hash].MarkDirty(); | |
3108 | } | |
3109 | } | |
00588c3f PW |
3110 | } |
3111 | ||
3112 | void CWallet::EraseFromWallet(const uint256 &hash) | |
e8ef3da7 WL |
3113 | { |
3114 | if (!fFileBacked) | |
00588c3f | 3115 | return; |
e8ef3da7 | 3116 | { |
f8dcd5ca | 3117 | LOCK(cs_wallet); |
e8ef3da7 WL |
3118 | if (mapWallet.erase(hash)) |
3119 | CWalletDB(strWalletFile).EraseTx(hash); | |
3120 | } | |
00588c3f | 3121 | return; |
e8ef3da7 WL |
3122 | } |
3123 | ||
47ab0926 | 3124 | void CWallet::RescanWallet() |
3125 | { | |
3126 | if (needsRescan) | |
3127 | { | |
3128 | CBlockIndex *start = chainActive.Height() > 0 ? chainActive[1] : NULL; | |
3129 | if (start) | |
3130 | ScanForWalletTransactions(start, true); | |
3131 | needsRescan = false; | |
3132 | } | |
3133 | } | |
3134 | ||
e8ef3da7 | 3135 | |
1a62587e JG |
3136 | /** |
3137 | * Returns a nullifier if the SpendingKey is available | |
3138 | * Throws std::runtime_error if the decryptor doesn't match this note | |
3139 | */ | |
618206c7 S |
3140 | boost::optional<uint256> CWallet::GetSproutNoteNullifier(const JSDescription &jsdesc, |
3141 | const libzcash::SproutPaymentAddress &address, | |
3142 | const ZCNoteDecryption &dec, | |
3143 | const uint256 &hSig, | |
3144 | uint8_t n) const | |
1a62587e JG |
3145 | { |
3146 | boost::optional<uint256> ret; | |
5020a936 | 3147 | auto note_pt = libzcash::SproutNotePlaintext::decrypt( |
1a62587e JG |
3148 | dec, |
3149 | jsdesc.ciphertexts[n], | |
3150 | jsdesc.ephemeralKey, | |
3151 | hSig, | |
3152 | (unsigned char) n); | |
3153 | auto note = note_pt.note(address); | |
c1009374 SB |
3154 | |
3155 | // Check note plaintext against note commitment | |
3156 | if (note.cm() != jsdesc.commitments[n]) { | |
3157 | throw libzcash::note_decryption_failed(); | |
3158 | } | |
3159 | ||
9a2b8ae5 JG |
3160 | // SpendingKeys are only available if: |
3161 | // - We have them (this isn't a viewing key) | |
3162 | // - The wallet is unlocked | |
e5eab182 | 3163 | libzcash::SproutSpendingKey key; |
25d5e80c | 3164 | if (GetSproutSpendingKey(address, key)) { |
1a62587e JG |
3165 | ret = note.nullifier(key); |
3166 | } | |
3167 | return ret; | |
3168 | } | |
3169 | ||
e492d986 JG |
3170 | /** |
3171 | * Finds all output notes in the given transaction that have been sent to | |
3172 | * PaymentAddresses in this wallet. | |
3173 | * | |
3174 | * It should never be necessary to call this method with a CWalletTx, because | |
57faf44e | 3175 | * the result of FindMySproutNotes (for the addresses available at the time) will |
005f3ad1 | 3176 | * already have been cached in CWalletTx.mapSproutNoteData. |
e492d986 | 3177 | */ |
57faf44e | 3178 | mapSproutNoteData_t CWallet::FindMySproutNotes(const CTransaction &tx) const |
02e67455 | 3179 | { |
3fac1020 | 3180 | LOCK(cs_SpendingKeyStore); |
fa511e10 | 3181 | uint256 hash = tx.GetHash(); |
02e67455 | 3182 | |
005f3ad1 | 3183 | mapSproutNoteData_t noteData; |
f57f76d7 DA |
3184 | for (size_t i = 0; i < tx.vJoinSplit.size(); i++) { |
3185 | auto hSig = tx.vJoinSplit[i].h_sig(*pzcashParams, tx.joinSplitPubKey); | |
3186 | for (uint8_t j = 0; j < tx.vJoinSplit[i].ciphertexts.size(); j++) { | |
3fac1020 | 3187 | for (const NoteDecryptorMap::value_type& item : mapNoteDecryptors) { |
02e67455 | 3188 | try { |
02e67455 | 3189 | auto address = item.first; |
02e67455 | 3190 | JSOutPoint jsoutpt {hash, i, j}; |
618206c7 | 3191 | auto nullifier = GetSproutNoteNullifier( |
f57f76d7 | 3192 | tx.vJoinSplit[i], |
1a62587e JG |
3193 | address, |
3194 | item.second, | |
3195 | hSig, j); | |
3196 | if (nullifier) { | |
005f3ad1 | 3197 | SproutNoteData nd {address, *nullifier}; |
1a62587e JG |
3198 | noteData.insert(std::make_pair(jsoutpt, nd)); |
3199 | } else { | |
005f3ad1 | 3200 | SproutNoteData nd {address}; |
1a62587e JG |
3201 | noteData.insert(std::make_pair(jsoutpt, nd)); |
3202 | } | |
02e67455 | 3203 | break; |
51fde9ea | 3204 | } catch (const note_decryption_failed &err) { |
1a62587e | 3205 | // Couldn't decrypt with this decryptor |
32a103aa JG |
3206 | } catch (const std::exception &exc) { |
3207 | // Unexpected failure | |
57faf44e | 3208 | LogPrintf("FindMySproutNotes(): Unexpected error while testing decrypt:\n"); |
32a103aa | 3209 | LogPrintf("%s\n", exc.what()); |
02e67455 JG |
3210 | } |
3211 | } | |
3212 | } | |
3213 | } | |
3214 | return noteData; | |
3215 | } | |
3216 | ||
78584ef7 S |
3217 | |
3218 | /** | |
3219 | * Finds all output notes in the given transaction that have been sent to | |
3220 | * SaplingPaymentAddresses in this wallet. | |
3221 | * | |
3222 | * It should never be necessary to call this method with a CWalletTx, because | |
3223 | * the result of FindMySaplingNotes (for the addresses available at the time) will | |
3224 | * already have been cached in CWalletTx.mapSaplingNoteData. | |
3225 | */ | |
a4ecd0fa | 3226 | std::pair<mapSaplingNoteData_t, SaplingIncomingViewingKeyMap> CWallet::FindMySaplingNotes(const CTransaction &tx) const |
78584ef7 S |
3227 | { |
3228 | LOCK(cs_SpendingKeyStore); | |
3229 | uint256 hash = tx.GetHash(); | |
3230 | ||
3231 | mapSaplingNoteData_t noteData; | |
a4ecd0fa | 3232 | SaplingIncomingViewingKeyMap viewingKeysToAdd; |
78584ef7 S |
3233 | |
3234 | // Protocol Spec: 4.19 Block Chain Scanning (Sapling) | |
3235 | for (uint32_t i = 0; i < tx.vShieldedOutput.size(); ++i) { | |
3236 | const OutputDescription output = tx.vShieldedOutput[i]; | |
ec064abb S |
3237 | for (auto it = mapSaplingFullViewingKeys.begin(); it != mapSaplingFullViewingKeys.end(); ++it) { |
3238 | SaplingIncomingViewingKey ivk = it->first; | |
78584ef7 S |
3239 | auto result = SaplingNotePlaintext::decrypt(output.encCiphertext, ivk, output.ephemeralKey, output.cm); |
3240 | if (!result) { | |
3241 | continue; | |
3242 | } | |
a4ecd0fa EOW |
3243 | auto address = ivk.address(result.get().d); |
3244 | if (address && mapSaplingIncomingViewingKeys.count(address.get()) == 0) { | |
3245 | viewingKeysToAdd[address.get()] = ivk; | |
3246 | } | |
78584ef7 S |
3247 | // We don't cache the nullifier here as computing it requires knowledge of the note position |
3248 | // in the commitment tree, which can only be determined when the transaction has been mined. | |
3249 | SaplingOutPoint op {hash, i}; | |
3250 | SaplingNoteData nd; | |
3251 | nd.ivk = ivk; | |
3252 | noteData.insert(std::make_pair(op, nd)); | |
3253 | break; | |
3254 | } | |
3255 | } | |
3256 | ||
a4ecd0fa | 3257 | return std::make_pair(noteData, viewingKeysToAdd); |
78584ef7 S |
3258 | } |
3259 | ||
037cacf2 | 3260 | bool CWallet::IsSproutNullifierFromMe(const uint256& nullifier) const |
1551db87 JG |
3261 | { |
3262 | { | |
3263 | LOCK(cs_wallet); | |
f41bf503 S |
3264 | if (mapSproutNullifiersToNotes.count(nullifier) && |
3265 | mapWallet.count(mapSproutNullifiersToNotes.at(nullifier).hash)) { | |
1551db87 JG |
3266 | return true; |
3267 | } | |
3268 | } | |
3269 | return false; | |
3270 | } | |
3271 | ||
d7cf640b | 3272 | bool CWallet::IsSaplingNullifierFromMe(const uint256& nullifier) const |
be74c80d JG |
3273 | { |
3274 | { | |
3275 | LOCK(cs_wallet); | |
d7cf640b S |
3276 | if (mapSaplingNullifiersToNotes.count(nullifier) && |
3277 | mapWallet.count(mapSaplingNullifiersToNotes.at(nullifier).hash)) { | |
3278 | return true; | |
3279 | } | |
3280 | } | |
3281 | return false; | |
3282 | } | |
3283 | ||
8e8279e7 | 3284 | void CWallet::GetSproutNoteWitnesses(std::vector<JSOutPoint> notes, |
8ea8ef98 | 3285 | std::vector<boost::optional<SproutWitness>>& witnesses, |
8e8279e7 | 3286 | uint256 &final_anchor) |
be74c80d | 3287 | { |
29523dc7 EOW |
3288 | LOCK(cs_wallet); |
3289 | witnesses.resize(notes.size()); | |
3290 | boost::optional<uint256> rt; | |
3291 | int i = 0; | |
3292 | for (JSOutPoint note : notes) { | |
3293 | if (mapWallet.count(note.hash) && | |
3294 | mapWallet[note.hash].mapSproutNoteData.count(note) && | |
3295 | mapWallet[note.hash].mapSproutNoteData[note].witnesses.size() > 0) { | |
3296 | witnesses[i] = mapWallet[note.hash].mapSproutNoteData[note].witnesses.front(); | |
3297 | if (!rt) { | |
3298 | rt = witnesses[i]->root(); | |
3299 | } else { | |
3300 | assert(*rt == witnesses[i]->root()); | |
be74c80d | 3301 | } |
be74c80d | 3302 | } |
29523dc7 EOW |
3303 | i++; |
3304 | } | |
3305 | // All returned witnesses have the same anchor | |
3306 | if (rt) { | |
3307 | final_anchor = *rt; | |
be74c80d JG |
3308 | } |
3309 | } | |
3310 | ||
e6b0a8b9 | 3311 | void CWallet::GetSaplingNoteWitnesses(std::vector<SaplingOutPoint> notes, |
8ea8ef98 | 3312 | std::vector<boost::optional<SaplingWitness>>& witnesses, |
e6b0a8b9 EOW |
3313 | uint256 &final_anchor) |
3314 | { | |
3315 | LOCK(cs_wallet); | |
3316 | witnesses.resize(notes.size()); | |
3317 | boost::optional<uint256> rt; | |
3318 | int i = 0; | |
3319 | for (SaplingOutPoint note : notes) { | |
3320 | if (mapWallet.count(note.hash) && | |
3321 | mapWallet[note.hash].mapSaplingNoteData.count(note) && | |
3322 | mapWallet[note.hash].mapSaplingNoteData[note].witnesses.size() > 0) { | |
3323 | witnesses[i] = mapWallet[note.hash].mapSaplingNoteData[note].witnesses.front(); | |
3324 | if (!rt) { | |
3325 | rt = witnesses[i]->root(); | |
3326 | } else { | |
3327 | assert(*rt == witnesses[i]->root()); | |
3328 | } | |
be74c80d | 3329 | } |
e6b0a8b9 EOW |
3330 | i++; |
3331 | } | |
3332 | // All returned witnesses have the same anchor | |
3333 | if (rt) { | |
3334 | final_anchor = *rt; | |
be74c80d JG |
3335 | } |
3336 | } | |
3337 | ||
efecad16 | 3338 | isminetype CWallet::IsMine(const CTxIn &txin, uint32_t nHeight) const |
e8ef3da7 | 3339 | { |
e8ef3da7 | 3340 | { |
f8dcd5ca | 3341 | LOCK(cs_wallet); |
e8ef3da7 WL |
3342 | map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); |
3343 | if (mi != mapWallet.end()) | |
3344 | { | |
3345 | const CWalletTx& prev = (*mi).second; | |
3346 | if (txin.prevout.n < prev.vout.size()) | |
efecad16 | 3347 | return (::IsMine(*this, prev.vout[txin.prevout.n].scriptPubKey, nHeight)); |
e8ef3da7 WL |
3348 | } |
3349 | } | |
a3e192a3 | 3350 | return ISMINE_NO; |
e8ef3da7 WL |
3351 | } |
3352 | ||
efecad16 | 3353 | CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter, uint32_t nHeight) const |
e8ef3da7 | 3354 | { |
e8ef3da7 | 3355 | { |
f8dcd5ca | 3356 | LOCK(cs_wallet); |
e8ef3da7 WL |
3357 | map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); |
3358 | if (mi != mapWallet.end()) | |
3359 | { | |
3360 | const CWalletTx& prev = (*mi).second; | |
3361 | if (txin.prevout.n < prev.vout.size()) | |
efecad16 | 3362 | if (::IsMine(*this, prev.vout[txin.prevout.n].scriptPubKey, nHeight) & filter) |
47658758 | 3363 | return prev.vout[txin.prevout.n].nValue; // komodo_interest? |
e8ef3da7 WL |
3364 | } |
3365 | } | |
3366 | return 0; | |
3367 | } | |
3368 | ||
efecad16 | 3369 | CCurrencyValueMap CWallet::GetReserveDebit(const CTxIn &txin, const isminefilter& filter, uint32_t nHeight) const |
56fe75cb | 3370 | { |
3371 | { | |
3372 | LOCK(cs_wallet); | |
3373 | map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); | |
3374 | if (mi != mapWallet.end()) | |
3375 | { | |
3376 | const CWalletTx& prev = (*mi).second; | |
3377 | if (txin.prevout.n < prev.vout.size()) | |
efecad16 | 3378 | if (::IsMine(*this, prev.vout[txin.prevout.n].scriptPubKey, nHeight) & filter) |
56fe75cb | 3379 | return prev.vout[txin.prevout.n].ReserveOutValue(); |
3380 | } | |
3381 | } | |
3382 | return CCurrencyValueMap(); | |
3383 | } | |
3384 | ||
efecad16 | 3385 | isminetype CWallet::IsMine(const CTxOut& txout, uint32_t nHeight) const |
eca0b1ea | 3386 | { |
efecad16 | 3387 | return ::IsMine(*this, txout.scriptPubKey, nHeight); |
eca0b1ea JT |
3388 | } |
3389 | ||
efecad16 | 3390 | CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter, uint32_t nHeight) const |
eca0b1ea JT |
3391 | { |
3392 | if (!MoneyRange(txout.nValue)) | |
3393 | throw std::runtime_error("CWallet::GetCredit(): value out of range"); | |
efecad16 | 3394 | return ((IsMine(txout, nHeight) & filter) ? txout.nValue : 0); |
eca0b1ea JT |
3395 | } |
3396 | ||
e679ec96 GA |
3397 | bool CWallet::IsChange(const CTxOut& txout) const |
3398 | { | |
2a45a494 | 3399 | // TODO: fix handling of 'change' outputs. The assumption is that any |
d5087d1b | 3400 | // payment to a script that is ours, but is not in the address book |
2a45a494 GA |
3401 | // is change. That assumption is likely to break when we implement multisignature |
3402 | // wallets that return change back into a multi-signature-protected address; | |
3403 | // a better way of identifying which outputs are 'the send' and which are | |
3404 | // 'the change' will need to be implemented (maybe extend CWalletTx to remember | |
3405 | // which output, if any, was change). | |
d5087d1b | 3406 | if (::IsMine(*this, txout.scriptPubKey)) |
f8dcd5ca | 3407 | { |
d5087d1b PW |
3408 | CTxDestination address; |
3409 | if (!ExtractDestination(txout.scriptPubKey, address)) | |
3410 | return true; | |
3411 | ||
f8dcd5ca PW |
3412 | LOCK(cs_wallet); |
3413 | if (!mapAddressBook.count(address)) | |
3414 | return true; | |
3415 | } | |
e679ec96 GA |
3416 | return false; |
3417 | } | |
3418 | ||
eca0b1ea JT |
3419 | CAmount CWallet::GetChange(const CTxOut& txout) const |
3420 | { | |
3421 | if (!MoneyRange(txout.nValue)) | |
3422 | throw std::runtime_error("CWallet::GetChange(): value out of range"); | |
3423 | return (IsChange(txout) ? txout.nValue : 0); | |
3424 | } | |
3425 | ||
ea340a14 | 3426 | typedef vector<unsigned char> valtype; |
3427 | unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore); | |
3428 | ||
efecad16 | 3429 | bool CWallet::IsMine(const CTransaction& tx, uint32_t nHeight) |
eca0b1ea | 3430 | { |
90e75021 | 3431 | for (int i = 0; i < tx.vout.size(); i++) |
3432 | { | |
efecad16 | 3433 | isminetype mine; |
3434 | IsMine(tx, i, mine, nHeight); | |
3435 | if (mine) | |
eca0b1ea | 3436 | return true; |
90e75021 | 3437 | } |
3438 | return false; | |
3439 | } | |
3440 | ||
29bd53a1 MT |
3441 | // special case handling for non-standard/Verus OP_RETURN script outputs, which need the transaction |
3442 | // to determine ownership | |
efecad16 | 3443 | void CWallet::IsMine(const CTransaction& tx, uint32_t voutNum, isminetype &mine, uint32_t nHeight) |
29bd53a1 MT |
3444 | { |
3445 | vector<valtype> vSolutions; | |
3446 | txnouttype whichType; | |
0d7fed99 | 3447 | CScript scriptPubKey = tx.vout[voutNum].scriptPubKey; |
29bd53a1 | 3448 | |
0d7fed99 | 3449 | if (scriptPubKey.IsCheckLockTimeVerify()) |
b7c685b8 | 3450 | { |
0d7fed99 | 3451 | uint8_t pushOp = scriptPubKey[0]; |
3452 | uint32_t scriptStart = pushOp + 3; | |
b7c685b8 | 3453 | |
0d7fed99 | 3454 | // continue with post CLTV script |
3455 | scriptPubKey = CScript(scriptPubKey.size() > scriptStart ? scriptPubKey.begin() + scriptStart : scriptPubKey.end(), scriptPubKey.end()); | |
3456 | } | |
3457 | ||
3458 | COptCCParams p; | |
3459 | if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid()) | |
3460 | { | |
3461 | std::vector<CTxDestination> dests; | |
3462 | int minSigs; | |
3463 | bool canSign = false; | |
3464 | bool canSpend = false; | |
b7c685b8 | 3465 | |
efecad16 | 3466 | if (ExtractDestinations(scriptPubKey, whichType, dests, minSigs, this, &canSign, &canSpend, nHeight)) |
0d7fed99 | 3467 | { |
3468 | if (canSpend) | |
b7c685b8 | 3469 | { |
efecad16 | 3470 | mine = ISMINE_SPENDABLE; |
3471 | return; | |
b7c685b8 | 3472 | } |
0d7fed99 | 3473 | else if (canSign) |
b7c685b8 | 3474 | { |
efecad16 | 3475 | mine = ISMINE_WATCH_ONLY; |
3476 | return; | |
b7c685b8 | 3477 | } |
2f537fa1 | 3478 | else |
3479 | { | |
efecad16 | 3480 | mine = ISMINE_NO; |
3481 | return; | |
2f537fa1 | 3482 | } |
b7c685b8 | 3483 | } |
3484 | else | |
3485 | { | |
efecad16 | 3486 | mine = ISMINE_NO; |
3487 | return; | |
b7c685b8 | 3488 | } |
3489 | } | |
3490 | else if (!Solver(scriptPubKey, whichType, vSolutions)) | |
3491 | { | |
29bd53a1 | 3492 | if (this->HaveWatchOnly(scriptPubKey)) |
efecad16 | 3493 | { |
3494 | mine = ISMINE_WATCH_ONLY; | |
3495 | return; | |
3496 | } | |
3497 | mine = ISMINE_NO; | |
3498 | return; | |
29bd53a1 MT |
3499 | } |
3500 | ||
3501 | CKeyID keyID; | |
90e75021 | 3502 | CScriptID scriptID; |
29bd53a1 MT |
3503 | CScriptExt subscript; |
3504 | int voutNext = voutNum + 1; | |
3505 | ||
3506 | switch (whichType) | |
3507 | { | |
3508 | case TX_NONSTANDARD: | |
3509 | case TX_NULL_DATA: | |
3510 | break; | |
3511 | ||
68b9a352 | 3512 | case TX_CRYPTOCONDITION: |
ac53126c | 3513 | // for now, default is that the first value returned will be the target address, subsequent values will be |
3514 | // pubkeys. if we have the first in our wallet, we consider it spendable for now | |
3515 | if (vSolutions[0].size() == 33) | |
68b9a352 | 3516 | { |
ac53126c | 3517 | keyID = CPubKey(vSolutions[0]).GetID(); |
3518 | } | |
3519 | else if (vSolutions[0].size() == 20) | |
3520 | { | |
3521 | keyID = CKeyID(uint160(vSolutions[0])); | |
3522 | } | |
3523 | if (!keyID.IsNull() && HaveKey(keyID)) | |
3524 | { | |
efecad16 | 3525 | mine = ISMINE_SPENDABLE; |
3526 | return; | |
68b9a352 | 3527 | } |
3528 | break; | |
3529 | ||
29bd53a1 MT |
3530 | case TX_PUBKEY: |
3531 | keyID = CPubKey(vSolutions[0]).GetID(); | |
3532 | if (this->HaveKey(keyID)) | |
efecad16 | 3533 | { |
3534 | mine = ISMINE_SPENDABLE; | |
3535 | return; | |
3536 | } | |
29bd53a1 MT |
3537 | break; |
3538 | ||
3539 | case TX_PUBKEYHASH: | |
3540 | keyID = CKeyID(uint160(vSolutions[0])); | |
3541 | if (this->HaveKey(keyID)) | |
efecad16 | 3542 | { |
3543 | mine = ISMINE_SPENDABLE; | |
3544 | return; | |
3545 | } | |
29bd53a1 MT |
3546 | break; |
3547 | ||
3548 | case TX_SCRIPTHASH: | |
90e75021 | 3549 | scriptID = CScriptID(uint160(vSolutions[0])); |
29bd53a1 MT |
3550 | if (this->GetCScript(scriptID, subscript)) |
3551 | { | |
3552 | // if this is a CLTV, handle it differently | |
ef70c5b2 | 3553 | if (subscript.IsCheckLockTimeVerify()) |
29bd53a1 | 3554 | { |
efecad16 | 3555 | mine = (::IsMine(*this, subscript)); |
3556 | return; | |
29bd53a1 MT |
3557 | } |
3558 | else | |
3559 | { | |
3560 | isminetype ret = ::IsMine(*this, subscript); | |
3561 | if (ret == ISMINE_SPENDABLE) | |
efecad16 | 3562 | { |
3563 | mine = ret; | |
3564 | return; | |
3565 | } | |
29bd53a1 MT |
3566 | } |
3567 | } | |
ebee7b5b MT |
3568 | else if (tx.vout.size() > (voutNum + 1) && |
3569 | tx.vout.back().scriptPubKey.size() > 7 && | |
3570 | tx.vout.back().scriptPubKey[0] == OP_RETURN) | |
29bd53a1 MT |
3571 | { |
3572 | // get the opret script from next vout, verify that the front is CLTV and hash matches | |
3573 | // if so, remove it and use the solver | |
3574 | opcodetype op; | |
3575 | std::vector<uint8_t> opretData; | |
ebee7b5b MT |
3576 | CScript::const_iterator it = tx.vout.back().scriptPubKey.begin() + 1; |
3577 | if (tx.vout.back().scriptPubKey.GetOp2(it, op, &opretData)) | |
29bd53a1 | 3578 | { |
9feb4b9e | 3579 | if (opretData.size() > 0 && opretData[0] == OPRETTYPE_TIMELOCK) |
29bd53a1 | 3580 | { |
9feb4b9e | 3581 | CScript opretScript = CScript(opretData.begin() + 1, opretData.end()); |
29bd53a1 MT |
3582 | |
3583 | if (CScriptID(opretScript) == scriptID && | |
ef70c5b2 | 3584 | opretScript.IsCheckLockTimeVerify()) |
29bd53a1 | 3585 | { |
ea340a14 | 3586 | // if we find that this is ours, we need to add this script to the wallet, |
3587 | // and we can then recognize this transaction | |
3588 | isminetype t = ::IsMine(*this, opretScript); | |
3589 | if (t != ISMINE_NO) | |
3590 | { | |
3591 | this->AddCScript(opretScript); | |
3592 | } | |
efecad16 | 3593 | mine = t; |
3594 | return; | |
29bd53a1 MT |
3595 | } |
3596 | } | |
3597 | } | |
3598 | } | |
3599 | break; | |
3600 | ||
3601 | case TX_MULTISIG: | |
3602 | // Only consider transactions "mine" if we own ALL the | |
3603 | // keys involved. Multi-signature transactions that are | |
3604 | // partially owned (somebody else has a key that can spend | |
3605 | // them) enable spend-out-from-under-you attacks, especially | |
3606 | // in shared-wallet situations. | |
3607 | vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); | |
3608 | if (HaveKeys(keys, *this) == keys.size()) | |
efecad16 | 3609 | { |
3610 | mine = ISMINE_SPENDABLE; | |
3611 | return; | |
3612 | } | |
29bd53a1 MT |
3613 | break; |
3614 | } | |
3615 | ||
3616 | if (this->HaveWatchOnly(scriptPubKey)) | |
efecad16 | 3617 | { |
3618 | mine = ISMINE_WATCH_ONLY; | |
3619 | return; | |
3620 | } | |
29bd53a1 | 3621 | |
efecad16 | 3622 | mine = ISMINE_NO; |
29bd53a1 MT |
3623 | } |
3624 | ||
efecad16 | 3625 | bool CWallet::IsFromMe(const CTransaction& tx, uint32_t height) const |
eca0b1ea | 3626 | { |
4a5b9f74 | 3627 | { |
3628 | LOCK(cs_wallet); | |
3629 | for (auto &txin : tx.vin) | |
3630 | { | |
3631 | map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); | |
3632 | if (mi != mapWallet.end()) | |
3633 | { | |
3634 | const CWalletTx& prev = (*mi).second; | |
3635 | if (txin.prevout.n < prev.vout.size()) | |
adbb6236 | 3636 | { |
efecad16 | 3637 | if (::IsMine(*this, prev.vout[txin.prevout.n].scriptPubKey, height) & ISMINE_ALL) |
4a5b9f74 | 3638 | return true; |
adbb6236 | 3639 | } |
4a5b9f74 | 3640 | } |
3641 | } | |
1551db87 | 3642 | } |
f57f76d7 | 3643 | for (const JSDescription& jsdesc : tx.vJoinSplit) { |
1551db87 | 3644 | for (const uint256& nullifier : jsdesc.nullifiers) { |
037cacf2 | 3645 | if (IsSproutNullifierFromMe(nullifier)) { |
1551db87 JG |
3646 | return true; |
3647 | } | |
3648 | } | |
3649 | } | |
d7cf640b S |
3650 | for (const SpendDescription &spend : tx.vShieldedSpend) { |
3651 | if (IsSaplingNullifierFromMe(spend.nullifier)) { | |
3652 | return true; | |
3653 | } | |
3654 | } | |
1551db87 | 3655 | return false; |
eca0b1ea JT |
3656 | } |
3657 | ||
efecad16 | 3658 | CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter, uint32_t nHeight) const |
eca0b1ea JT |
3659 | { |
3660 | CAmount nDebit = 0; | |
3661 | BOOST_FOREACH(const CTxIn& txin, tx.vin) | |
3662 | { | |
efecad16 | 3663 | nDebit += GetDebit(txin, filter, nHeight); |
eca0b1ea JT |
3664 | if (!MoneyRange(nDebit)) |
3665 | throw std::runtime_error("CWallet::GetDebit(): value out of range"); | |
3666 | } | |
3667 | return nDebit; | |
3668 | } | |
3669 | ||
efecad16 | 3670 | CCurrencyValueMap CWallet::GetReserveDebit(const CTransaction& tx, const isminefilter& filter, uint32_t nHeight) const |
56fe75cb | 3671 | { |
3672 | CCurrencyValueMap retVal; | |
3673 | BOOST_FOREACH(const CTxIn& txin, tx.vin) | |
3674 | { | |
efecad16 | 3675 | retVal += GetReserveDebit(txin, filter, nHeight); |
56fe75cb | 3676 | } |
3677 | return retVal; | |
3678 | } | |
3679 | ||
efecad16 | 3680 | CAmount CWallet::GetCredit(const CTransaction& tx, const int32_t &voutNum, const isminefilter& filter, uint32_t nHeight) const |
fab1429d | 3681 | { |
3682 | if (voutNum >= tx.vout.size() || !MoneyRange(tx.vout[voutNum].nValue)) | |
3683 | throw std::runtime_error("CWallet::GetCredit(): value out of range"); | |
efecad16 | 3684 | return ((IsMine(tx.vout[voutNum], nHeight) & filter) ? tx.vout[voutNum].nValue : 0); |
fab1429d | 3685 | } |
3686 | ||
56fe75cb | 3687 | CCurrencyValueMap CWallet::GetReserveCredit(const CTransaction& tx, int32_t voutNum, const isminefilter& filter) const |
3a27113e | 3688 | { |
56fe75cb | 3689 | return ((IsMine(tx.vout[voutNum]) & filter) ? tx.vout[voutNum].ReserveOutValue() : CCurrencyValueMap()); |
3a27113e | 3690 | } |
3691 | ||
56fe75cb | 3692 | CCurrencyValueMap CWallet::GetReserveCredit(const CTransaction& tx, const isminefilter& filter) const |
3a27113e | 3693 | { |
56fe75cb | 3694 | CCurrencyValueMap nCredit; |
3a27113e | 3695 | for (int i = 0; i < tx.vout.size(); i++) |
3696 | { | |
3697 | nCredit += GetReserveCredit(tx, i, filter); | |
3698 | } | |
3699 | return nCredit; | |
3700 | } | |
3701 | ||
efecad16 | 3702 | CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter, uint32_t nHeight) const |
eca0b1ea JT |
3703 | { |
3704 | CAmount nCredit = 0; | |
fab1429d | 3705 | for (int i = 0; i < tx.vout.size(); i++) |
eca0b1ea | 3706 | { |
efecad16 | 3707 | nCredit += GetCredit(tx, i, filter, nHeight); |
eca0b1ea JT |
3708 | } |
3709 | return nCredit; | |
3710 | } | |
3711 | ||
3712 | CAmount CWallet::GetChange(const CTransaction& tx) const | |
3713 | { | |
3714 | CAmount nChange = 0; | |
3715 | BOOST_FOREACH(const CTxOut& txout, tx.vout) | |
3716 | { | |
3717 | nChange += GetChange(txout); | |
3718 | if (!MoneyRange(nChange)) | |
3719 | throw std::runtime_error("CWallet::GetChange(): value out of range"); | |
3720 | } | |
3721 | return nChange; | |
3722 | } | |
3723 | ||
e2416930 JG |
3724 | bool CWallet::IsHDFullyEnabled() const |
3725 | { | |
3726 | // Only Sapling addresses are HD for now | |
3727 | return false; | |
3728 | } | |
3729 | ||
3730 | void CWallet::GenerateNewSeed() | |
3731 | { | |
3732 | LOCK(cs_wallet); | |
3733 | ||
3734 | auto seed = HDSeed::Random(HD_WALLET_SEED_LENGTH); | |
3735 | ||
3736 | int64_t nCreationTime = GetTime(); | |
3737 | ||
3738 | // If the wallet is encrypted and locked, this will fail. | |
3739 | if (!SetHDSeed(seed)) | |
3740 | throw std::runtime_error(std::string(__func__) + ": SetHDSeed failed"); | |
3741 | ||
3742 | // store the key creation time together with | |
3743 | // the child index counter in the database | |
3744 | // as a hdchain object | |
3745 | CHDChain newHdChain; | |
3746 | newHdChain.nVersion = CHDChain::VERSION_HD_BASE; | |
3747 | newHdChain.seedFp = seed.Fingerprint(); | |
3748 | newHdChain.nCreateTime = nCreationTime; | |
3749 | SetHDChain(newHdChain, false); | |
3750 | } | |
3751 | ||
3752 | bool CWallet::SetHDSeed(const HDSeed& seed) | |
c3a7307a | 3753 | { |
e2416930 JG |
3754 | if (!CCryptoKeyStore::SetHDSeed(seed)) { |
3755 | return false; | |
3756 | } | |
3757 | ||
3758 | if (!fFileBacked) { | |
3759 | return true; | |
3760 | } | |
3761 | ||
3762 | { | |
3763 | LOCK(cs_wallet); | |
3764 | if (!IsCrypted()) { | |
3765 | return CWalletDB(strWalletFile).WriteHDSeed(seed); | |
3766 | } | |
3767 | } | |
3768 | return true; | |
3769 | } | |
3770 | ||
3771 | bool CWallet::SetCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char> &vchCryptedSecret) | |
3772 | { | |
3773 | if (!CCryptoKeyStore::SetCryptedHDSeed(seedFp, vchCryptedSecret)) { | |
3774 | return false; | |
3775 | } | |
3776 | ||
3777 | if (!fFileBacked) { | |
3778 | return true; | |
3779 | } | |
3780 | ||
3781 | { | |
3782 | LOCK(cs_wallet); | |
3783 | if (pwalletdbEncryption) | |
3784 | return pwalletdbEncryption->WriteCryptedHDSeed(seedFp, vchCryptedSecret); | |
3785 | else | |
3786 | return CWalletDB(strWalletFile).WriteCryptedHDSeed(seedFp, vchCryptedSecret); | |
3787 | } | |
3788 | return false; | |
3789 | } | |
3790 | ||
52cfa9c1 E |
3791 | HDSeed CWallet::GetHDSeedForRPC() const { |
3792 | HDSeed seed; | |
3793 | if (!pwalletMain->GetHDSeed(seed)) { | |
3794 | throw JSONRPCError(RPC_WALLET_ERROR, "HD seed not found"); | |
3795 | } | |
3796 | return seed; | |
3797 | } | |
3798 | ||
e2416930 JG |
3799 | void CWallet::SetHDChain(const CHDChain& chain, bool memonly) |
3800 | { | |
3801 | LOCK(cs_wallet); | |
3802 | if (!memonly && fFileBacked && !CWalletDB(strWalletFile).WriteHDChain(chain)) | |
3803 | throw std::runtime_error(std::string(__func__) + ": writing chain failed"); | |
3804 | ||
3805 | hdChain = chain; | |
3806 | } | |
3807 | ||
3808 | bool CWallet::LoadHDSeed(const HDSeed& seed) | |
3809 | { | |
3810 | return CBasicKeyStore::SetHDSeed(seed); | |
3811 | } | |
3812 | ||
3813 | bool CWallet::LoadCryptedHDSeed(const uint256& seedFp, const std::vector<unsigned char>& seed) | |
3814 | { | |
3815 | return CCryptoKeyStore::SetCryptedHDSeed(seedFp, seed); | |
3816 | } | |
3817 | ||
8e8279e7 | 3818 | void CWalletTx::SetSproutNoteData(mapSproutNoteData_t ¬eData) |
c3a7307a | 3819 | { |
005f3ad1 EOW |
3820 | mapSproutNoteData.clear(); |
3821 | for (const std::pair<JSOutPoint, SproutNoteData> nd : noteData) { | |
f57f76d7 DA |
3822 | if (nd.first.js < vJoinSplit.size() && |
3823 | nd.first.n < vJoinSplit[nd.first.js].ciphertexts.size()) { | |
c3a7307a | 3824 | // Store the address and nullifier for the Note |
005f3ad1 | 3825 | mapSproutNoteData[nd.first] = nd.second; |
c3a7307a | 3826 | } else { |
57faf44e | 3827 | // If FindMySproutNotes() was used to obtain noteData, |
c3a7307a | 3828 | // this should never happen |
8e8279e7 | 3829 | throw std::logic_error("CWalletTx::SetSproutNoteData(): Invalid note"); |
c3a7307a JG |
3830 | } |
3831 | } | |
3832 | } | |
3833 | ||
e6b0a8b9 EOW |
3834 | void CWalletTx::SetSaplingNoteData(mapSaplingNoteData_t ¬eData) |
3835 | { | |
3836 | mapSaplingNoteData.clear(); | |
3837 | for (const std::pair<SaplingOutPoint, SaplingNoteData> nd : noteData) { | |
3838 | if (nd.first.n < vShieldedOutput.size()) { | |
3839 | mapSaplingNoteData[nd.first] = nd.second; | |
3840 | } else { | |
3841 | throw std::logic_error("CWalletTx::SetSaplingNoteData(): Invalid note"); | |
c3a7307a JG |
3842 | } |
3843 | } | |
3844 | } | |
3845 | ||
79cd5c96 | 3846 | std::pair<SproutNotePlaintext, SproutPaymentAddress> CWalletTx::DecryptSproutNote( |
3847 | JSOutPoint jsop) const | |
3848 | { | |
3849 | LOCK(pwallet->cs_wallet); | |
3850 | ||
3851 | auto nd = this->mapSproutNoteData.at(jsop); | |
3852 | SproutPaymentAddress pa = nd.address; | |
3853 | ||
3854 | // Get cached decryptor | |
3855 | ZCNoteDecryption decryptor; | |
3856 | if (!pwallet->GetNoteDecryptor(pa, decryptor)) { | |
3857 | // Note decryptors are created when the wallet is loaded, so it should always exist | |
3858 | throw std::runtime_error(strprintf( | |
3859 | "Could not find note decryptor for payment address %s", | |
3860 | EncodePaymentAddress(pa))); | |
3861 | } | |
3862 | ||
3863 | auto hSig = this->vJoinSplit[jsop.js].h_sig(*pzcashParams, this->joinSplitPubKey); | |
3864 | try { | |
3865 | SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( | |
3866 | decryptor, | |
3867 | this->vJoinSplit[jsop.js].ciphertexts[jsop.n], | |
3868 | this->vJoinSplit[jsop.js].ephemeralKey, | |
3869 | hSig, | |
3870 | (unsigned char) jsop.n); | |
3871 | ||
3872 | return std::make_pair(plaintext, pa); | |
3873 | } catch (const note_decryption_failed &err) { | |
3874 | // Couldn't decrypt with this spending key | |
3875 | throw std::runtime_error(strprintf( | |
3876 | "Could not decrypt note for payment address %s", | |
3877 | EncodePaymentAddress(pa))); | |
3878 | } catch (const std::exception &exc) { | |
3879 | // Unexpected failure | |
3880 | throw std::runtime_error(strprintf( | |
3881 | "Error while decrypting note for payment address %s: %s", | |
3882 | EncodePaymentAddress(pa), exc.what())); | |
3883 | } | |
3884 | } | |
3885 | ||
3886 | boost::optional<std::pair< | |
3887 | SaplingNotePlaintext, | |
3888 | SaplingPaymentAddress>> CWalletTx::DecryptSaplingNote(SaplingOutPoint op) const | |
3889 | { | |
3890 | // Check whether we can decrypt this SaplingOutPoint | |
3891 | if (this->mapSaplingNoteData.count(op) == 0) { | |
3892 | return boost::none; | |
3893 | } | |
3894 | ||
3895 | auto output = this->vShieldedOutput[op.n]; | |
3896 | auto nd = this->mapSaplingNoteData.at(op); | |
3897 | ||
3898 | auto maybe_pt = SaplingNotePlaintext::decrypt( | |
3899 | output.encCiphertext, | |
3900 | nd.ivk, | |
3901 | output.ephemeralKey, | |
3902 | output.cm); | |
3903 | assert(static_cast<bool>(maybe_pt)); | |
3904 | auto notePt = maybe_pt.get(); | |
3905 | ||
3906 | auto maybe_pa = nd.ivk.address(notePt.d); | |
3907 | assert(static_cast<bool>(maybe_pa)); | |
3908 | auto pa = maybe_pa.get(); | |
3909 | ||
3910 | return std::make_pair(notePt, pa); | |
3911 | } | |
3912 | ||
3913 | boost::optional<std::pair< | |
3914 | SaplingNotePlaintext, | |
3915 | SaplingPaymentAddress>> CWalletTx::RecoverSaplingNote( | |
3916 | SaplingOutPoint op, std::set<uint256>& ovks) const | |
3917 | { | |
3918 | auto output = this->vShieldedOutput[op.n]; | |
3919 | ||
3920 | for (auto ovk : ovks) { | |
3921 | auto outPt = SaplingOutgoingPlaintext::decrypt( | |
3922 | output.outCiphertext, | |
3923 | ovk, | |
3924 | output.cv, | |
3925 | output.cm, | |
3926 | output.ephemeralKey); | |
3927 | if (!outPt) { | |
3928 | continue; | |
3929 | } | |
3930 | ||
3931 | auto maybe_pt = SaplingNotePlaintext::decrypt( | |
3932 | output.encCiphertext, | |
3933 | output.ephemeralKey, | |
3934 | outPt->esk, | |
3935 | outPt->pk_d, | |
3936 | output.cm); | |
3937 | assert(static_cast<bool>(maybe_pt)); | |
3938 | auto notePt = maybe_pt.get(); | |
3939 | ||
3940 | return std::make_pair(notePt, SaplingPaymentAddress(notePt.d, outPt->pk_d)); | |
3941 | } | |
3942 | ||
3943 | // Couldn't recover with any of the provided OutgoingViewingKeys | |
3944 | return boost::none; | |
3945 | } | |
3946 | ||
51ed9ec9 | 3947 | int64_t CWalletTx::GetTxTime() const |
e8ef3da7 | 3948 | { |
51ed9ec9 | 3949 | int64_t n = nTimeSmart; |
c3f95ef1 | 3950 | return n ? n : nTimeReceived; |
e8ef3da7 WL |
3951 | } |
3952 | ||
3953 | int CWalletTx::GetRequestCount() const | |
3954 | { | |
3955 | // Returns -1 if it wasn't being tracked | |
3956 | int nRequests = -1; | |
e8ef3da7 | 3957 | { |
f8dcd5ca | 3958 | LOCK(pwallet->cs_wallet); |
e8ef3da7 WL |
3959 | if (IsCoinBase()) |
3960 | { | |
3961 | // Generated block | |
4f152496 | 3962 | if (!hashBlock.IsNull()) |
e8ef3da7 WL |
3963 | { |
3964 | map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); | |
3965 | if (mi != pwallet->mapRequestCount.end()) | |
3966 | nRequests = (*mi).second; | |
3967 | } | |
3968 | } | |
3969 | else | |
3970 | { | |
3971 | // Did anyone request this transaction? | |
805344dc | 3972 | map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); |
e8ef3da7 WL |
3973 | if (mi != pwallet->mapRequestCount.end()) |
3974 | { | |
3975 | nRequests = (*mi).second; | |
3976 | ||
3977 | // How about the block it's in? | |
4f152496 | 3978 | if (nRequests == 0 && !hashBlock.IsNull()) |
e8ef3da7 WL |
3979 | { |
3980 | map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); | |
3981 | if (mi != pwallet->mapRequestCount.end()) | |
3982 | nRequests = (*mi).second; | |
3983 | else | |
3984 | nRequests = 1; // If it's in someone else's block it must have got out | |
3985 | } | |
3986 | } | |
3987 | } | |
3988 | } | |
3989 | return nRequests; | |
3990 | } | |
3991 | ||
86cf60b5 | 3992 | // GetAmounts will determine the transparent debits and credits for a given wallet tx. |
1b4568cb | 3993 | void CWalletTx::GetAmounts(list<COutputEntry>& listReceived, |
a372168e | 3994 | list<COutputEntry>& listSent, CAmount& nFee, string& strSentAccount, const isminefilter& filter) const |
e8ef3da7 | 3995 | { |
e07c8e91 | 3996 | nFee = 0; |
e8ef3da7 WL |
3997 | listReceived.clear(); |
3998 | listSent.clear(); | |
3999 | strSentAccount = strFromAccount; | |
4000 | ||
86cf60b5 | 4001 | // Is this tx sent/signed by me? |
a372168e | 4002 | CAmount nDebit = GetDebit(filter); |
44ac8ad4 | 4003 | |
4004 | bool isFromMyTaddr = false; | |
4005 | ||
4006 | for (auto &txin : vin) | |
4007 | { | |
4008 | map<uint256, CWalletTx>::const_iterator mi = pwallet->mapWallet.find(txin.prevout.hash); | |
4009 | if (mi != pwallet->mapWallet.end()) | |
4010 | { | |
4011 | const CWalletTx& prev = (*mi).second; | |
4012 | if (txin.prevout.n < prev.vout.size()) | |
4013 | { | |
4014 | if (::IsMine(*pwallet, prev.vout[txin.prevout.n].scriptPubKey) & filter) | |
4015 | { | |
4016 | isFromMyTaddr = true; | |
4017 | break; | |
4018 | } | |
4019 | } | |
4020 | } | |
4021 | } | |
4022 | ||
4023 | //bool isFromMyTaddr = pwallet->IsFromMe(*this); // IsFromMe(filter); // debit>0 means we signed/sent this transaction | |
86cf60b5 | 4024 | |
86cf60b5 S |
4025 | // Compute fee if we sent this transaction. |
4026 | if (isFromMyTaddr) { | |
f6863889 S |
4027 | CAmount nValueOut = GetValueOut(); // transparent outputs plus all Sprout vpub_old and negative Sapling valueBalance |
4028 | CAmount nValueIn = GetShieldedValueIn(); | |
86cf60b5 S |
4029 | nFee = nDebit - nValueOut + nValueIn; |
4030 | } | |
4031 | ||
4032 | // Create output entry for vpub_old/new, if we sent utxos from this transaction | |
4033 | if (isFromMyTaddr) { | |
4034 | CAmount myVpubOld = 0; | |
4035 | CAmount myVpubNew = 0; | |
f57f76d7 | 4036 | for (const JSDescription& js : vJoinSplit) { |
86cf60b5 S |
4037 | bool fMyJSDesc = false; |
4038 | ||
4039 | // Check input side | |
4040 | for (const uint256& nullifier : js.nullifiers) { | |
037cacf2 | 4041 | if (pwallet->IsSproutNullifierFromMe(nullifier)) { |
86cf60b5 S |
4042 | fMyJSDesc = true; |
4043 | break; | |
4044 | } | |
4045 | } | |
4046 | ||
4047 | // Check output side | |
4048 | if (!fMyJSDesc) { | |
005f3ad1 | 4049 | for (const std::pair<JSOutPoint, SproutNoteData> nd : this->mapSproutNoteData) { |
f57f76d7 | 4050 | if (nd.first.js < vJoinSplit.size() && nd.first.n < vJoinSplit[nd.first.js].ciphertexts.size()) { |
86cf60b5 S |
4051 | fMyJSDesc = true; |
4052 | break; | |
4053 | } | |
4054 | } | |
4055 | } | |
4056 | ||
4057 | if (fMyJSDesc) { | |
4058 | myVpubOld += js.vpub_old; | |
4059 | myVpubNew += js.vpub_new; | |
4060 | } | |
4061 | ||
4062 | if (!MoneyRange(js.vpub_old) || !MoneyRange(js.vpub_new) || !MoneyRange(myVpubOld) || !MoneyRange(myVpubNew)) { | |
4063 | throw std::runtime_error("CWalletTx::GetAmounts: value out of range"); | |
4064 | } | |
4065 | } | |
4066 | ||
4067 | // Create an output for the value taken from or added to the transparent value pool by JoinSplits | |
4068 | if (myVpubOld > myVpubNew) { | |
4069 | COutputEntry output = {CNoDestination(), myVpubOld - myVpubNew, (int)vout.size()}; | |
4070 | listSent.push_back(output); | |
4071 | } else if (myVpubNew > myVpubOld) { | |
4072 | COutputEntry output = {CNoDestination(), myVpubNew - myVpubOld, (int)vout.size()}; | |
4073 | listReceived.push_back(output); | |
4074 | } | |
e8ef3da7 WL |
4075 | } |
4076 | ||
f9816408 S |
4077 | // If we sent utxos from this transaction, create output for value taken from (negative valueBalance) |
4078 | // or added (positive valueBalance) to the transparent value pool by Sapling shielding and unshielding. | |
4079 | if (isFromMyTaddr) { | |
4080 | if (valueBalance < 0) { | |
4081 | COutputEntry output = {CNoDestination(), -valueBalance, (int) vout.size()}; | |
4082 | listSent.push_back(output); | |
4083 | } else if (valueBalance > 0) { | |
4084 | COutputEntry output = {CNoDestination(), valueBalance, (int) vout.size()}; | |
4085 | listReceived.push_back(output); | |
4086 | } | |
4087 | } | |
4088 | ||
e679ec96 | 4089 | // Sent/received. |
5bb76550 | 4090 | for (unsigned int i = 0; i < vout.size(); ++i) |
e8ef3da7 | 4091 | { |
1b4568cb | 4092 | const CTxOut& txout = vout[i]; |
a5c6c5d6 | 4093 | isminetype fIsMine = pwallet->IsMine(txout); |
96ed6821 LD |
4094 | // Only need to handle txouts if AT LEAST one of these is true: |
4095 | // 1) they debit from us (sent) | |
4096 | // 2) the output is to us (received) | |
4097 | if (nDebit > 0) | |
4098 | { | |
4099 | // Don't report 'change' txouts | |
73b4d696 | 4100 | if (!(filter & ISMINE_CHANGE) && pwallet->IsChange(txout)) |
96ed6821 | 4101 | continue; |
96ed6821 | 4102 | } |
a5c6c5d6 | 4103 | else if (!(fIsMine & filter)) |
96ed6821 LD |
4104 | continue; |
4105 | ||
4106 | // In either case, we need to get the destination address | |
10254401 | 4107 | CTxDestination address; |
10254401 | 4108 | if (!ExtractDestination(txout.scriptPubKey, address)) |
e8ef3da7 | 4109 | { |
7d306d64 | 4110 | //LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n",this->GetHash().ToString()); complains on the opreturns |
96ed6821 | 4111 | address = CNoDestination(); |
e8ef3da7 WL |
4112 | } |
4113 | ||
5bb76550 | 4114 | COutputEntry output = {address, txout.nValue, (int)i}; |
1b4568cb | 4115 | |
96ed6821 | 4116 | // If we are debited by the transaction, add the output as a "sent" entry |
e8ef3da7 | 4117 | if (nDebit > 0) |
1b4568cb | 4118 | listSent.push_back(output); |
e8ef3da7 | 4119 | |
96ed6821 | 4120 | // If we are receiving the output, add it as a "received" entry |
d512534c | 4121 | if (fIsMine & filter) |
1b4568cb | 4122 | listReceived.push_back(output); |
e8ef3da7 | 4123 | } |
e8ef3da7 WL |
4124 | } |
4125 | ||
a372168e MF |
4126 | void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived, |
4127 | CAmount& nSent, CAmount& nFee, const isminefilter& filter) const | |
e8ef3da7 | 4128 | { |
e07c8e91 | 4129 | nReceived = nSent = nFee = 0; |
e8ef3da7 | 4130 | |
a372168e | 4131 | CAmount allFee; |
e8ef3da7 | 4132 | string strSentAccount; |
1b4568cb CL |
4133 | list<COutputEntry> listReceived; |
4134 | list<COutputEntry> listSent; | |
d4640d7d | 4135 | GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); |
e8ef3da7 | 4136 | |
e8ef3da7 WL |
4137 | if (strAccount == strSentAccount) |
4138 | { | |
1b4568cb CL |
4139 | BOOST_FOREACH(const COutputEntry& s, listSent) |
4140 | nSent += s.amount; | |
e8ef3da7 WL |
4141 | nFee = allFee; |
4142 | } | |
e8ef3da7 | 4143 | { |
f8dcd5ca | 4144 | LOCK(pwallet->cs_wallet); |
1b4568cb | 4145 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
e8ef3da7 | 4146 | { |
1b4568cb | 4147 | if (pwallet->mapAddressBook.count(r.destination)) |
e8ef3da7 | 4148 | { |
1b4568cb | 4149 | map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(r.destination); |
61885513 | 4150 | if (mi != pwallet->mapAddressBook.end() && (*mi).second.name == strAccount) |
1b4568cb | 4151 | nReceived += r.amount; |
e8ef3da7 WL |
4152 | } |
4153 | else if (strAccount.empty()) | |
4154 | { | |
1b4568cb | 4155 | nReceived += r.amount; |
e8ef3da7 WL |
4156 | } |
4157 | } | |
4158 | } | |
4159 | } | |
4160 | ||
722fa283 | 4161 | |
44bc988e | 4162 | bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb) |
e8ef3da7 | 4163 | { |
805344dc | 4164 | return pwalletdb->WriteTx(GetHash(), *this); |
e8ef3da7 WL |
4165 | } |
4166 | ||
4bc00dc1 | 4167 | void CWallet::WitnessNoteCommitment(std::vector<uint256> commitments, |
8ea8ef98 | 4168 | std::vector<boost::optional<SproutWitness>>& witnesses, |
4bc00dc1 | 4169 | uint256 &final_anchor) |
a8ac403d | 4170 | { |
2dc35992 | 4171 | witnesses.resize(commitments.size()); |
a8ac403d | 4172 | CBlockIndex* pindex = chainActive.Genesis(); |
4fc309f0 | 4173 | SproutMerkleTree tree; |
a8ac403d SB |
4174 | |
4175 | while (pindex) { | |
4176 | CBlock block; | |
88d014d0 | 4177 | ReadBlockFromDisk(block, pindex, Params().GetConsensus(), 1); |
a8ac403d SB |
4178 | |
4179 | BOOST_FOREACH(const CTransaction& tx, block.vtx) | |
4180 | { | |
f57f76d7 | 4181 | BOOST_FOREACH(const JSDescription& jsdesc, tx.vJoinSplit) |
a8ac403d | 4182 | { |
22de1602 | 4183 | BOOST_FOREACH(const uint256 ¬e_commitment, jsdesc.commitments) |
a8ac403d | 4184 | { |
4bc00dc1 | 4185 | tree.append(note_commitment); |
1760b3cd | 4186 | |
8ea8ef98 | 4187 | BOOST_FOREACH(boost::optional<SproutWitness>& wit, witnesses) { |
2dc35992 | 4188 | if (wit) { |
4bc00dc1 | 4189 | wit->append(note_commitment); |
2dc35992 SB |
4190 | } |
4191 | } | |
4192 | ||
4193 | size_t i = 0; | |
4194 | BOOST_FOREACH(uint256& commitment, commitments) { | |
4bc00dc1 | 4195 | if (note_commitment == commitment) { |
2dc35992 | 4196 | witnesses.at(i) = tree.witness(); |
1760b3cd | 4197 | } |
2dc35992 | 4198 | i++; |
a8ac403d SB |
4199 | } |
4200 | } | |
4201 | } | |
4202 | } | |
4203 | ||
ccb439c5 | 4204 | uint256 current_anchor = tree.root(); |
a8ac403d SB |
4205 | |
4206 | // Consistency check: we should be able to find the current tree | |
4207 | // in our CCoins view. | |
4fc309f0 | 4208 | SproutMerkleTree dummy_tree; |
008f4ee8 | 4209 | assert(pcoinsTip->GetSproutAnchorAt(current_anchor, dummy_tree)); |
a8ac403d SB |
4210 | |
4211 | pindex = chainActive.Next(pindex); | |
4212 | } | |
4213 | ||
ccb439c5 SB |
4214 | // TODO: #93; Select a root via some heuristic. |
4215 | final_anchor = tree.root(); | |
a8ac403d | 4216 | |
8ea8ef98 | 4217 | BOOST_FOREACH(boost::optional<SproutWitness>& wit, witnesses) { |
2dc35992 SB |
4218 | if (wit) { |
4219 | assert(final_anchor == wit->root()); | |
4220 | } | |
1760b3cd | 4221 | } |
a8ac403d SB |
4222 | } |
4223 | ||
5b40d886 MF |
4224 | /** |
4225 | * Scan the block chain (starting in pindexStart) for transactions | |
4226 | * from or to us. If fUpdate is true, found transactions that already | |
4227 | * exist in the wallet will be updated. | |
4228 | */ | |
e8ef3da7 WL |
4229 | int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) |
4230 | { | |
4231 | int ret = 0; | |
75b8953a | 4232 | int64_t nNow = GetTime(); |
11982d36 | 4233 | const CChainParams& chainParams = Params(); |
e8ef3da7 WL |
4234 | |
4235 | CBlockIndex* pindex = pindexStart; | |
cd10562c S |
4236 | |
4237 | std::vector<uint256> myTxHashes; | |
4238 | ||
e8ef3da7 | 4239 | { |
55a1db4f | 4240 | LOCK2(cs_main, cs_wallet); |
39278369 CL |
4241 | |
4242 | // no need to read and scan block, if block was created before | |
4243 | // our wallet birthday (as adjusted for block time variability) | |
209377a7 | 4244 | while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) |
39278369 CL |
4245 | pindex = chainActive.Next(pindex); |
4246 | ||
4247 | ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup | |
11982d36 | 4248 | double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false); |
86131275 | 4249 | double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.LastTip(), false); |
e8ef3da7 WL |
4250 | while (pindex) |
4251 | { | |
4b729ec5 | 4252 | if (pindex->GetHeight() % 100 == 0 && dProgressTip - dProgressStart > 0.0) |
11982d36 | 4253 | ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); |
8da9dd07 | 4254 | |
e8ef3da7 | 4255 | CBlock block; |
71cf6ba9 | 4256 | ReadBlockFromDisk(block, pindex, Params().GetConsensus()); |
e8ef3da7 WL |
4257 | BOOST_FOREACH(CTransaction& tx, block.vtx) |
4258 | { | |
cd10562c S |
4259 | if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) { |
4260 | myTxHashes.push_back(tx.GetHash()); | |
e8ef3da7 | 4261 | ret++; |
cd10562c | 4262 | } |
e8ef3da7 | 4263 | } |
b6961fc1 | 4264 | |
4fc309f0 EOW |
4265 | SproutMerkleTree sproutTree; |
4266 | SaplingMerkleTree saplingTree; | |
b6961fc1 JG |
4267 | // This should never fail: we should always be able to get the tree |
4268 | // state on the path to the tip of our chain | |
f86ee1c2 EOW |
4269 | assert(pcoinsTip->GetSproutAnchorAt(pindex->hashSproutAnchor, sproutTree)); |
4270 | if (pindex->pprev) { | |
88d014d0 | 4271 | if (Params().GetConsensus().NetworkUpgradeActive(pindex->pprev->GetHeight(), Consensus::UPGRADE_SAPLING)) { |
d9fe33b8 S |
4272 | assert(pcoinsTip->GetSaplingAnchorAt(pindex->pprev->hashFinalSaplingRoot, saplingTree)); |
4273 | } | |
f86ee1c2 | 4274 | } |
b6961fc1 | 4275 | // Increment note witness caches |
162bfc3a | 4276 | ChainTipAdded(pindex, &block, sproutTree, saplingTree); |
b6961fc1 | 4277 | |
4c6d41b8 | 4278 | pindex = chainActive.Next(pindex); |
75b8953a B |
4279 | if (GetTime() >= nNow + 60) { |
4280 | nNow = GetTime(); | |
4b729ec5 | 4281 | LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->GetHeight(), Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex)); |
75b8953a | 4282 | } |
e8ef3da7 | 4283 | } |
cd10562c S |
4284 | |
4285 | // After rescanning, persist Sapling note data that might have changed, e.g. nullifiers. | |
4286 | // Do not flush the wallet here for performance reasons. | |
4287 | CWalletDB walletdb(strWalletFile, "r+", false); | |
4288 | for (auto hash : myTxHashes) { | |
4289 | CWalletTx wtx = mapWallet[hash]; | |
4290 | if (!wtx.mapSaplingNoteData.empty()) { | |
4291 | if (!wtx.WriteToDisk(&walletdb)) { | |
4292 | LogPrintf("Rescanning... WriteToDisk failed to update Sapling note data for: %s\n", hash.ToString()); | |
4293 | } | |
4294 | } | |
4295 | } | |
4296 | ||
39278369 | 4297 | ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI |
e8ef3da7 WL |
4298 | } |
4299 | return ret; | |
4300 | } | |
4301 | ||
4302 | void CWallet::ReacceptWalletTransactions() | |
4303 | { | |
7e6d23b1 | 4304 | // If transactions aren't being broadcasted, don't let them into local mempool either |
6f252627 WL |
4305 | if (!fBroadcastTransactions) |
4306 | return; | |
55a1db4f | 4307 | LOCK2(cs_main, cs_wallet); |
e9c3215b | 4308 | std::map<int64_t, CWalletTx*> mapSorted; |
4309 | ||
4310 | // Sort pending wallet transactions based on their initial wallet insertion order | |
93a18a36 | 4311 | BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) |
e8ef3da7 | 4312 | { |
93a18a36 GA |
4313 | const uint256& wtxid = item.first; |
4314 | CWalletTx& wtx = item.second; | |
805344dc | 4315 | assert(wtx.GetHash() == wtxid); |
e8ef3da7 | 4316 | |
93a18a36 GA |
4317 | int nDepth = wtx.GetDepthInMainChain(); |
4318 | ||
e9c3215b | 4319 | if (!wtx.IsCoinBase() && nDepth < 0) { |
4320 | mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); | |
e8ef3da7 WL |
4321 | } |
4322 | } | |
e9c3215b | 4323 | |
43260416 | 4324 | std::vector<uint256> vwtxh; |
4325 | ||
e9c3215b | 4326 | // Try to add wallet transactions to memory pool |
4327 | BOOST_FOREACH(PAIRTYPE(const int64_t, CWalletTx*)& item, mapSorted) | |
4328 | { | |
4329 | CWalletTx& wtx = *(item.second); | |
4330 | ||
4331 | LOCK(mempool.cs); | |
271326fa | 4332 | CValidationState state; |
4333 | // attempt to add them, but don't set any DOS level | |
085c3640 | 4334 | if (!::AcceptToMemoryPool(mempool, state, wtx, false, NULL, true, 0)) |
4335 | { | |
4336 | int nDoS; | |
4337 | bool invalid = state.IsInvalid(nDoS); | |
4338 | ||
4339 | // log rejection and deletion | |
4340 | // printf("ERROR reaccepting wallet transaction %s to mempool, reason: %s, DoS: %d\n", wtx.GetHash().ToString().c_str(), state.GetRejectReason().c_str(), nDoS); | |
4341 | ||
4342 | if (!wtx.IsCoinBase() && invalid && nDoS > 0) | |
4343 | { | |
d738023b | 4344 | LogPrintf("erasing transaction %s\n", wtx.GetHash().GetHex().c_str()); |
43260416 | 4345 | vwtxh.push_back(wtx.GetHash()); |
085c3640 | 4346 | } |
4347 | } | |
e9c3215b | 4348 | } |
43260416 | 4349 | for (auto hash : vwtxh) |
4350 | { | |
4351 | EraseFromWallet(hash); | |
4352 | } | |
e8ef3da7 WL |
4353 | } |
4354 | ||
0f5954c4 | 4355 | bool CWalletTx::RelayWalletTransaction() |
e8ef3da7 | 4356 | { |
7000ef1a | 4357 | if ( pwallet == 0 ) |
4358 | { | |
4359 | fprintf(stderr,"unexpected null pwallet in RelayWalletTransaction\n"); | |
4360 | return(false); | |
4361 | } | |
6f252627 | 4362 | assert(pwallet->GetBroadcastTransactions()); |
e8ef3da7 WL |
4363 | if (!IsCoinBase()) |
4364 | { | |
bd070d8b | 4365 | if (GetDepthInMainChain() == 0) |
4366 | { | |
4367 | // if tx is expired, dont relay | |
805344dc | 4368 | LogPrintf("Relaying wtx %s\n", GetHash().ToString()); |
d38da59b | 4369 | RelayTransaction((CTransaction)*this); |
0f5954c4 | 4370 | return true; |
e8ef3da7 WL |
4371 | } |
4372 | } | |
0f5954c4 | 4373 | return false; |
e8ef3da7 WL |
4374 | } |
4375 | ||
3015e0bc | 4376 | set<uint256> CWalletTx::GetConflicts() const |
731b89b8 GA |
4377 | { |
4378 | set<uint256> result; | |
4379 | if (pwallet != NULL) | |
4380 | { | |
805344dc | 4381 | uint256 myHash = GetHash(); |
3015e0bc | 4382 | result = pwallet->GetConflicts(myHash); |
731b89b8 GA |
4383 | result.erase(myHash); |
4384 | } | |
4385 | return result; | |
4386 | } | |
4387 | ||
bbacd882 CF |
4388 | CAmount CWalletTx::GetDebit(const isminefilter& filter) const |
4389 | { | |
4390 | if (vin.empty()) | |
4391 | return 0; | |
4392 | ||
4393 | CAmount debit = 0; | |
4394 | if(filter & ISMINE_SPENDABLE) | |
4395 | { | |
4396 | if (fDebitCached) | |
4397 | debit += nDebitCached; | |
4398 | else | |
4399 | { | |
efecad16 | 4400 | int depth = this->GetDepthInMainChain(); |
4401 | uint32_t height = chainActive.Height() - --depth; | |
4402 | nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE, height); | |
bbacd882 CF |
4403 | fDebitCached = true; |
4404 | debit += nDebitCached; | |
4405 | } | |
4406 | } | |
4407 | if(filter & ISMINE_WATCH_ONLY) | |
4408 | { | |
4409 | if(fWatchDebitCached) | |
4410 | debit += nWatchDebitCached; | |
4411 | else | |
4412 | { | |
efecad16 | 4413 | int depth = this->GetDepthInMainChain(); |
4414 | uint32_t height = chainActive.Height() - --depth; | |
4415 | nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY, height); | |
bbacd882 CF |
4416 | fWatchDebitCached = true; |
4417 | debit += nWatchDebitCached; | |
4418 | } | |
4419 | } | |
4420 | return debit; | |
4421 | } | |
4422 | ||
56fe75cb | 4423 | CCurrencyValueMap CWalletTx::GetReserveDebit(const isminefilter& filter) const |
3a27113e | 4424 | { |
4425 | if (vin.empty()) | |
56fe75cb | 4426 | return CCurrencyValueMap(); |
3a27113e | 4427 | |
efecad16 | 4428 | int depth = this->GetDepthInMainChain(); |
4429 | uint32_t height = chainActive.Height() - --depth; | |
4430 | ||
4431 | return pwallet->GetReserveDebit(*this, filter, height); | |
3a27113e | 4432 | } |
4433 | ||
bbacd882 CF |
4434 | CAmount CWalletTx::GetCredit(const isminefilter& filter) const |
4435 | { | |
4436 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4437 | if (IsCoinBase() && GetBlocksToMaturity() > 0) | |
4438 | return 0; | |
4439 | ||
4440 | int64_t credit = 0; | |
4441 | if (filter & ISMINE_SPENDABLE) | |
4442 | { | |
4443 | // GetBalance can assume transactions in mapWallet won't change | |
4444 | if (fCreditCached) | |
4445 | credit += nCreditCached; | |
4446 | else | |
4447 | { | |
efecad16 | 4448 | int depth = this->GetDepthInMainChain(); |
4449 | uint32_t height = chainActive.Height() - --depth; | |
4450 | ||
4451 | nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE, height); | |
bbacd882 CF |
4452 | fCreditCached = true; |
4453 | credit += nCreditCached; | |
4454 | } | |
4455 | } | |
4456 | if (filter & ISMINE_WATCH_ONLY) | |
4457 | { | |
4458 | if (fWatchCreditCached) | |
4459 | credit += nWatchCreditCached; | |
4460 | else | |
4461 | { | |
efecad16 | 4462 | int depth = this->GetDepthInMainChain(); |
4463 | uint32_t height = chainActive.Height() - --depth; | |
4464 | ||
4465 | nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY, height); | |
bbacd882 CF |
4466 | fWatchCreditCached = true; |
4467 | credit += nWatchCreditCached; | |
4468 | } | |
4469 | } | |
4470 | return credit; | |
4471 | } | |
4472 | ||
a78c55ad | 4473 | bool CWalletTx::HasMatureCoins() const |
4474 | { | |
4475 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4476 | if (!(IsCoinBase() && GetBlocksToMaturity() > 0)) | |
4477 | { | |
4478 | return true; | |
4479 | } | |
4480 | else | |
4481 | { | |
4482 | for (auto oneout : vout) | |
4483 | { | |
4484 | if (oneout.scriptPubKey.IsInstantSpend()) | |
4485 | { | |
4486 | return true; | |
4487 | } | |
4488 | } | |
4489 | return false; | |
4490 | } | |
4491 | } | |
4492 | ||
56fe75cb | 4493 | CCurrencyValueMap CWalletTx::GetReserveCredit(const isminefilter& filter) const |
3a27113e | 4494 | { |
4495 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4496 | if (IsCoinBase() && GetBlocksToMaturity() > 0) | |
56fe75cb | 4497 | return CCurrencyValueMap(); |
3a27113e | 4498 | |
56fe75cb | 4499 | return pwallet->GetReserveCredit(*this, filter); |
3a27113e | 4500 | } |
4501 | ||
bbacd882 CF |
4502 | CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const |
4503 | { | |
4504 | if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) | |
4505 | { | |
4506 | if (fUseCache && fImmatureCreditCached) | |
4507 | return nImmatureCreditCached; | |
4508 | nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); | |
4509 | fImmatureCreditCached = true; | |
4510 | return nImmatureCreditCached; | |
4511 | } | |
4512 | ||
4513 | return 0; | |
4514 | } | |
4515 | ||
56fe75cb | 4516 | CCurrencyValueMap CWalletTx::GetImmatureReserveCredit(bool fUseCache) const |
3a27113e | 4517 | { |
4518 | if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) | |
4519 | { | |
56fe75cb | 4520 | pwallet->GetReserveCredit(*this, ISMINE_SPENDABLE); |
3a27113e | 4521 | } |
4522 | ||
56fe75cb | 4523 | return CCurrencyValueMap(); |
3a27113e | 4524 | } |
4525 | ||
bbacd882 CF |
4526 | CAmount CWalletTx::GetAvailableCredit(bool fUseCache) const |
4527 | { | |
4528 | if (pwallet == 0) | |
4529 | return 0; | |
4530 | ||
4531 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4532 | if (IsCoinBase() && GetBlocksToMaturity() > 0) | |
4533 | return 0; | |
4534 | ||
4535 | if (fUseCache && fAvailableCreditCached) | |
4536 | return nAvailableCreditCached; | |
4537 | ||
4538 | CAmount nCredit = 0; | |
805344dc | 4539 | uint256 hashTx = GetHash(); |
bbacd882 CF |
4540 | for (unsigned int i = 0; i < vout.size(); i++) |
4541 | { | |
a88b514e | 4542 | if (!pwallet->IsSpent(hashTx, i) && vout[i].scriptPubKey.IsSpendableOutputType()) |
bbacd882 | 4543 | { |
fab1429d | 4544 | nCredit += pwallet->GetCredit(*this, i, ISMINE_SPENDABLE); |
bbacd882 CF |
4545 | } |
4546 | } | |
4547 | ||
4548 | nAvailableCreditCached = nCredit; | |
4549 | fAvailableCreditCached = true; | |
4550 | return nCredit; | |
4551 | } | |
4552 | ||
56fe75cb | 4553 | CCurrencyValueMap CWalletTx::GetAvailableReserveCredit(bool fUseCache) const |
3a27113e | 4554 | { |
56fe75cb | 4555 | CCurrencyValueMap retVal; |
3a27113e | 4556 | if (pwallet == 0) |
56fe75cb | 4557 | return retVal; |
3a27113e | 4558 | |
4559 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4560 | if (IsCoinBase() && GetBlocksToMaturity() > 0) | |
56fe75cb | 4561 | return retVal; |
3a27113e | 4562 | |
3a27113e | 4563 | uint256 hashTx = GetHash(); |
4564 | for (unsigned int i = 0; i < vout.size(); i++) | |
4565 | { | |
a88b514e | 4566 | if (!pwallet->IsSpent(hashTx, i) && vout[i].scriptPubKey.IsSpendableOutputType()) |
3a27113e | 4567 | { |
56fe75cb | 4568 | retVal += pwallet->GetReserveCredit(*this, i, ISMINE_SPENDABLE); |
3a27113e | 4569 | } |
4570 | } | |
56fe75cb | 4571 | return retVal; |
3a27113e | 4572 | } |
4573 | ||
bbacd882 CF |
4574 | CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const |
4575 | { | |
4576 | if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) | |
4577 | { | |
4578 | if (fUseCache && fImmatureWatchCreditCached) | |
4579 | return nImmatureWatchCreditCached; | |
4580 | nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); | |
4581 | fImmatureWatchCreditCached = true; | |
4582 | return nImmatureWatchCreditCached; | |
4583 | } | |
4584 | ||
4585 | return 0; | |
4586 | } | |
4587 | ||
56fe75cb | 4588 | CCurrencyValueMap CWalletTx::GetImmatureWatchOnlyReserveCredit(const bool& fUseCache) const |
3a27113e | 4589 | { |
4590 | if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) | |
4591 | { | |
56fe75cb | 4592 | return pwallet->GetReserveCredit(*this, ISMINE_WATCH_ONLY); |
3a27113e | 4593 | } |
4594 | ||
56fe75cb | 4595 | return CCurrencyValueMap(); |
3a27113e | 4596 | } |
4597 | ||
bbacd882 CF |
4598 | CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const |
4599 | { | |
4600 | if (pwallet == 0) | |
4601 | return 0; | |
4602 | ||
4603 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4604 | if (IsCoinBase() && GetBlocksToMaturity() > 0) | |
4605 | return 0; | |
4606 | ||
4607 | if (fUseCache && fAvailableWatchCreditCached) | |
4608 | return nAvailableWatchCreditCached; | |
4609 | ||
4610 | CAmount nCredit = 0; | |
4611 | for (unsigned int i = 0; i < vout.size(); i++) | |
4612 | { | |
805344dc | 4613 | if (!pwallet->IsSpent(GetHash(), i)) |
bbacd882 | 4614 | { |
fab1429d | 4615 | nCredit += pwallet->GetCredit(*this, i, ISMINE_WATCH_ONLY); |
bbacd882 CF |
4616 | if (!MoneyRange(nCredit)) |
4617 | throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); | |
4618 | } | |
4619 | } | |
4620 | ||
4621 | nAvailableWatchCreditCached = nCredit; | |
4622 | fAvailableWatchCreditCached = true; | |
4623 | return nCredit; | |
4624 | } | |
4625 | ||
56fe75cb | 4626 | CCurrencyValueMap CWalletTx::GetAvailableWatchOnlyReserveCredit(const bool& fUseCache) const |
3a27113e | 4627 | { |
56fe75cb | 4628 | CCurrencyValueMap retVal; |
3a27113e | 4629 | if (pwallet == 0) |
56fe75cb | 4630 | return retVal; |
3a27113e | 4631 | |
4632 | // Must wait until coinbase is safely deep enough in the chain before valuing it | |
4633 | if (IsCoinBase() && GetBlocksToMaturity() > 0) | |
56fe75cb | 4634 | return retVal; |
3a27113e | 4635 | |
3a27113e | 4636 | for (unsigned int i = 0; i < vout.size(); i++) |
4637 | { | |
4638 | if (!pwallet->IsSpent(GetHash(), i)) | |
4639 | { | |
56fe75cb | 4640 | retVal += pwallet->GetReserveCredit(*this, i, ISMINE_WATCH_ONLY); |
3a27113e | 4641 | } |
4642 | } | |
4643 | ||
56fe75cb | 4644 | return retVal; |
3a27113e | 4645 | } |
4646 | ||
bbacd882 CF |
4647 | CAmount CWalletTx::GetChange() const |
4648 | { | |
4649 | if (fChangeCached) | |
4650 | return nChangeCached; | |
4651 | nChangeCached = pwallet->GetChange(*this); | |
4652 | fChangeCached = true; | |
4653 | return nChangeCached; | |
4654 | } | |
4655 | ||
4656 | bool CWalletTx::IsTrusted() const | |
4657 | { | |
4658 | // Quick answer in most cases | |
75a4d512 | 4659 | if (!CheckFinalTx(*this)) |
bbacd882 CF |
4660 | return false; |
4661 | int nDepth = GetDepthInMainChain(); | |
4662 | if (nDepth >= 1) | |
4663 | return true; | |
4664 | if (nDepth < 0) | |
4665 | return false; | |
4666 | if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit | |
4667 | return false; | |
4668 | ||
4669 | // Trusted if all inputs are from us and are in the mempool: | |
4670 | BOOST_FOREACH(const CTxIn& txin, vin) | |
4671 | { | |
4672 | // Transactions not sent by us: not trusted | |
4673 | const CWalletTx* parent = pwallet->GetWalletTx(txin.prevout.hash); | |
4674 | if (parent == NULL) | |
4675 | return false; | |
bf412e62 | 4676 | if (!parent->vout.size()) |
4677 | { | |
420019f6 | 4678 | LogPrintf("%s: No spendable output in wallet for input to %s, num %d\n", __func__, txin.prevout.hash.GetHex().c_str(), txin.prevout.n); |
bf412e62 | 4679 | return false; |
4680 | } | |
bbacd882 CF |
4681 | const CTxOut& parentOut = parent->vout[txin.prevout.n]; |
4682 | if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) | |
4683 | return false; | |
4684 | } | |
4685 | return true; | |
4686 | } | |
4687 | ||
0f5954c4 GA |
4688 | std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime) |
4689 | { | |
4690 | std::vector<uint256> result; | |
4691 | ||
4692 | LOCK(cs_wallet); | |
4693 | // Sort them in chronological order | |
4694 | multimap<unsigned int, CWalletTx*> mapSorted; | |
bd070d8b | 4695 | uint32_t now = (uint32_t)time(NULL); |
47ab0926 | 4696 | std::vector<uint256> vwtxh; |
0f5954c4 GA |
4697 | BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) |
4698 | { | |
4699 | CWalletTx& wtx = item.second; | |
4700 | // Don't rebroadcast if newer than nTime: | |
4701 | if (wtx.nTimeReceived > nTime) | |
4702 | continue; | |
3abeed2c | 4703 | if ( (wtx.nLockTime >= LOCKTIME_THRESHOLD && wtx.nLockTime < now-KOMODO_MAXMEMPOOLTIME) || wtx.hashBlock.IsNull() ) |
bd070d8b | 4704 | { |
1aa576ad | 4705 | //LogPrintf("skip Relaying wtx %s nLockTime %u vs now.%u\n", wtx.GetHash().ToString(),(uint32_t)wtx.nLockTime,now); |
d738023b | 4706 | //vwtxh.push_back(wtx.GetHash()); |
3abeed2c | 4707 | continue; |
bd070d8b | 4708 | } |
0f5954c4 GA |
4709 | mapSorted.insert(make_pair(wtx.nTimeReceived, &wtx)); |
4710 | } | |
4711 | BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) | |
4712 | { | |
7000ef1a | 4713 | if ( item.second != 0 ) |
4714 | { | |
3872e9cb | 4715 | CWalletTx &wtx = *item.second; |
7000ef1a | 4716 | if (wtx.RelayWalletTransaction()) |
4717 | result.push_back(wtx.GetHash()); | |
4718 | } | |
0f5954c4 | 4719 | } |
47ab0926 | 4720 | for (auto hash : vwtxh) |
4721 | { | |
d738023b | 4722 | EraseFromWallets(hash); |
47ab0926 | 4723 | } |
0f5954c4 GA |
4724 | return result; |
4725 | } | |
4726 | ||
4727 | void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) | |
e8ef3da7 WL |
4728 | { |
4729 | // Do this infrequently and randomly to avoid giving away | |
4730 | // that these are our transactions. | |
6f252627 | 4731 | if (GetTime() < nNextResend || !fBroadcastTransactions) |
e8ef3da7 | 4732 | return; |
203d1ae6 LD |
4733 | bool fFirst = (nNextResend == 0); |
4734 | nNextResend = GetTime() + GetRand(30 * 60); | |
e8ef3da7 WL |
4735 | if (fFirst) |
4736 | return; | |
4737 | ||
4738 | // Only do it if there's been a new block since last time | |
0f5954c4 | 4739 | if (nBestBlockTime < nLastResend) |
e8ef3da7 | 4740 | return; |
203d1ae6 | 4741 | nLastResend = GetTime(); |
e8ef3da7 | 4742 | |
0f5954c4 GA |
4743 | // Rebroadcast unconfirmed txes older than 5 minutes before the last |
4744 | // block was found: | |
4745 | std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60); | |
4746 | if (!relayed.empty()) | |
4747 | LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); | |
e8ef3da7 WL |
4748 | } |
4749 | ||
5b40d886 | 4750 | /** @} */ // end of mapWallet |
e8ef3da7 WL |
4751 | |
4752 | ||
4753 | ||
4754 | ||
5b40d886 MF |
4755 | /** @defgroup Actions |
4756 | * | |
4757 | * @{ | |
4758 | */ | |
e8ef3da7 WL |
4759 | |
4760 | ||
a372168e | 4761 | CAmount CWallet::GetBalance() const |
e8ef3da7 | 4762 | { |
a372168e | 4763 | CAmount nTotal = 0; |
e8ef3da7 | 4764 | { |
55a1db4f | 4765 | LOCK2(cs_main, cs_wallet); |
e8ef3da7 WL |
4766 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4767 | { | |
4768 | const CWalletTx* pcoin = &(*it).second; | |
0542619d | 4769 | if (pcoin->IsTrusted()) |
8fdb7e10 | 4770 | nTotal += pcoin->GetAvailableCredit(); |
e8ef3da7 WL |
4771 | } |
4772 | } | |
4773 | ||
e8ef3da7 WL |
4774 | return nTotal; |
4775 | } | |
4776 | ||
56fe75cb | 4777 | CCurrencyValueMap CWallet::GetReserveBalance() const |
3a27113e | 4778 | { |
56fe75cb | 4779 | CCurrencyValueMap retVal; |
3a27113e | 4780 | { |
4781 | LOCK2(cs_main, cs_wallet); | |
4782 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
4783 | { | |
4784 | const CWalletTx* pcoin = &(*it).second; | |
4785 | if (pcoin->IsTrusted()) | |
56fe75cb | 4786 | retVal += pcoin->GetAvailableReserveCredit(); |
3a27113e | 4787 | } |
4788 | } | |
4789 | ||
56fe75cb | 4790 | return retVal; |
3a27113e | 4791 | } |
4792 | ||
a372168e | 4793 | CAmount CWallet::GetUnconfirmedBalance() const |
df5ccbd2 | 4794 | { |
a372168e | 4795 | CAmount nTotal = 0; |
df5ccbd2 | 4796 | { |
55a1db4f | 4797 | LOCK2(cs_main, cs_wallet); |
df5ccbd2 WL |
4798 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4799 | { | |
4800 | const CWalletTx* pcoin = &(*it).second; | |
75a4d512 | 4801 | if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) |
8fdb7e10 | 4802 | nTotal += pcoin->GetAvailableCredit(); |
4803 | } | |
4804 | } | |
4805 | return nTotal; | |
4806 | } | |
4807 | ||
56fe75cb | 4808 | CCurrencyValueMap CWallet::GetUnconfirmedReserveBalance() const |
3a27113e | 4809 | { |
56fe75cb | 4810 | CCurrencyValueMap retVal; |
3a27113e | 4811 | { |
4812 | LOCK2(cs_main, cs_wallet); | |
4813 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
4814 | { | |
4815 | const CWalletTx* pcoin = &(*it).second; | |
4816 | if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) | |
56fe75cb | 4817 | retVal += pcoin->GetAvailableReserveCredit(); |
3a27113e | 4818 | } |
4819 | } | |
56fe75cb | 4820 | return retVal; |
3a27113e | 4821 | } |
4822 | ||
a372168e | 4823 | CAmount CWallet::GetImmatureBalance() const |
8fdb7e10 | 4824 | { |
a372168e | 4825 | CAmount nTotal = 0; |
8fdb7e10 | 4826 | { |
55a1db4f | 4827 | LOCK2(cs_main, cs_wallet); |
8fdb7e10 | 4828 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4829 | { | |
966a0e8c PK |
4830 | const CWalletTx* pcoin = &(*it).second; |
4831 | nTotal += pcoin->GetImmatureCredit(); | |
df5ccbd2 WL |
4832 | } |
4833 | } | |
4834 | return nTotal; | |
4835 | } | |
e8ef3da7 | 4836 | |
56fe75cb | 4837 | CCurrencyValueMap CWallet::GetImmatureReserveBalance() const |
3a27113e | 4838 | { |
56fe75cb | 4839 | CCurrencyValueMap retVal; |
3a27113e | 4840 | { |
4841 | LOCK2(cs_main, cs_wallet); | |
4842 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
4843 | { | |
4844 | const CWalletTx* pcoin = &(*it).second; | |
56fe75cb | 4845 | retVal += pcoin->GetImmatureReserveCredit(); |
3a27113e | 4846 | } |
4847 | } | |
56fe75cb | 4848 | return retVal; |
3a27113e | 4849 | } |
4850 | ||
a372168e | 4851 | CAmount CWallet::GetWatchOnlyBalance() const |
ffd40da3 | 4852 | { |
a372168e | 4853 | CAmount nTotal = 0; |
ffd40da3 | 4854 | { |
39cc4922 | 4855 | LOCK2(cs_main, cs_wallet); |
ffd40da3 J |
4856 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4857 | { | |
4858 | const CWalletTx* pcoin = &(*it).second; | |
4859 | if (pcoin->IsTrusted()) | |
4860 | nTotal += pcoin->GetAvailableWatchOnlyCredit(); | |
4861 | } | |
4862 | } | |
870da77d | 4863 | |
ffd40da3 J |
4864 | return nTotal; |
4865 | } | |
4866 | ||
56fe75cb | 4867 | CCurrencyValueMap CWallet::GetWatchOnlyReserveBalance() const |
3a27113e | 4868 | { |
56fe75cb | 4869 | CCurrencyValueMap retVal; |
3a27113e | 4870 | { |
4871 | LOCK2(cs_main, cs_wallet); | |
4872 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
4873 | { | |
4874 | const CWalletTx* pcoin = &(*it).second; | |
4875 | if (pcoin->IsTrusted()) | |
56fe75cb | 4876 | retVal += pcoin->GetAvailableWatchOnlyReserveCredit(); |
3a27113e | 4877 | } |
4878 | } | |
4879 | ||
56fe75cb | 4880 | return retVal; |
3a27113e | 4881 | } |
4882 | ||
a372168e | 4883 | CAmount CWallet::GetUnconfirmedWatchOnlyBalance() const |
ffd40da3 | 4884 | { |
a372168e | 4885 | CAmount nTotal = 0; |
ffd40da3 | 4886 | { |
39cc4922 | 4887 | LOCK2(cs_main, cs_wallet); |
ffd40da3 J |
4888 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4889 | { | |
4890 | const CWalletTx* pcoin = &(*it).second; | |
75a4d512 | 4891 | if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) |
ffd40da3 J |
4892 | nTotal += pcoin->GetAvailableWatchOnlyCredit(); |
4893 | } | |
4894 | } | |
4895 | return nTotal; | |
4896 | } | |
4897 | ||
56fe75cb | 4898 | CCurrencyValueMap CWallet::GetUnconfirmedWatchOnlyReserveBalance() const |
3a27113e | 4899 | { |
56fe75cb | 4900 | CCurrencyValueMap retVal; |
3a27113e | 4901 | { |
4902 | LOCK2(cs_main, cs_wallet); | |
4903 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
4904 | { | |
4905 | const CWalletTx* pcoin = &(*it).second; | |
4906 | if (!CheckFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) | |
56fe75cb | 4907 | retVal += pcoin->GetAvailableWatchOnlyReserveCredit(); |
3a27113e | 4908 | } |
4909 | } | |
56fe75cb | 4910 | return retVal; |
3a27113e | 4911 | } |
4912 | ||
a372168e | 4913 | CAmount CWallet::GetImmatureWatchOnlyBalance() const |
ffd40da3 | 4914 | { |
a372168e | 4915 | CAmount nTotal = 0; |
ffd40da3 | 4916 | { |
39cc4922 | 4917 | LOCK2(cs_main, cs_wallet); |
ffd40da3 J |
4918 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4919 | { | |
4920 | const CWalletTx* pcoin = &(*it).second; | |
4921 | nTotal += pcoin->GetImmatureWatchOnlyCredit(); | |
4922 | } | |
4923 | } | |
4924 | return nTotal; | |
4925 | } | |
4926 | ||
56fe75cb | 4927 | CCurrencyValueMap CWallet::GetImmatureWatchOnlyReserveBalance() const |
3a27113e | 4928 | { |
56fe75cb | 4929 | CCurrencyValueMap retVal; |
3a27113e | 4930 | { |
4931 | LOCK2(cs_main, cs_wallet); | |
4932 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
4933 | { | |
4934 | const CWalletTx* pcoin = &(*it).second; | |
56fe75cb | 4935 | retVal += pcoin->GetImmatureWatchOnlyReserveCredit(); |
3a27113e | 4936 | } |
4937 | } | |
56fe75cb | 4938 | return retVal; |
3a27113e | 4939 | } |
4940 | ||
5b40d886 MF |
4941 | /** |
4942 | * populate vCoins with vector of available COutputs. | |
4943 | */ | |
79383e0a | 4944 | uint64_t komodo_interestnew(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); |
c60397dd | 4945 | uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); |
0ad6a463 | 4946 | |
20bcd9ba | 4947 | void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fIncludeCoinBase, bool fIncludeProtectedCoinbase, bool fIncludeImmatureCoins) const |
9b0369c7 | 4948 | { |
5c439232 | 4949 | uint64_t interest,*ptr; |
9b0369c7 CM |
4950 | vCoins.clear(); |
4951 | ||
4952 | { | |
ea3acaf3 | 4953 | LOCK2(cs_main, cs_wallet); |
1e435b54 | 4954 | uint32_t nHeight = chainActive.Height() + 1; |
9b0369c7 CM |
4955 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) |
4956 | { | |
93a18a36 | 4957 | const uint256& wtxid = it->first; |
9b0369c7 CM |
4958 | const CWalletTx* pcoin = &(*it).second; |
4959 | ||
75a4d512 | 4960 | if (!CheckFinalTx(*pcoin)) |
a2709fad GA |
4961 | continue; |
4962 | ||
0542619d | 4963 | if (fOnlyConfirmed && !pcoin->IsTrusted()) |
9b0369c7 CM |
4964 | continue; |
4965 | ||
1e435b54 | 4966 | bool isCoinbase = pcoin->IsCoinBase(); |
29c07a8f | 4967 | if (!fIncludeCoinBase && isCoinbase) |
2b1cda3b | 4968 | continue; |
1e435b54 | 4969 | |
29c07a8f | 4970 | if (!fIncludeImmatureCoins && isCoinbase && pcoin->GetBlocksToMaturity() > 0) |
9b0369c7 CM |
4971 | continue; |
4972 | ||
2b72d46f GA |
4973 | int nDepth = pcoin->GetDepthInMainChain(); |
4974 | if (nDepth < 0) | |
4975 | continue; | |
53dce971 | 4976 | |
1e435b54 | 4977 | uint32_t coinHeight = nHeight - nDepth; |
4978 | // even if we should include coinbases, we may opt to exclude protected coinbases, which must only be included when shielding | |
af521e42 | 4979 | if (isCoinbase && |
4980 | !fIncludeProtectedCoinbase && | |
4981 | Params().GetConsensus().fCoinbaseMustBeProtected && | |
4982 | CConstVerusSolutionVector::GetVersionByHeight(coinHeight) < CActivationHeight::SOLUTION_VERUSV4 && | |
4983 | CConstVerusSolutionVector::GetVersionByHeight(nHeight) < CActivationHeight::SOLUTION_VERUSV5) | |
1e435b54 | 4984 | continue; |
4985 | ||
fab1429d | 4986 | for (int i = 0; i < pcoin->vout.size(); i++) |
0ad6a463 | 4987 | { |
ea340a14 | 4988 | isminetype mine = IsMine(pcoin->vout[i]); |
a3e192a3 | 4989 | if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && |
219953ce | 4990 | !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0 || fIncludeZeroValue) && |
6a86c24d | 4991 | (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) |
0ad6a463 | 4992 | { |
6ad13d7c | 4993 | if ( KOMODO_EXCHANGEWALLET == 0 ) |
0ad6a463 | 4994 | { |
6ad13d7c | 4995 | uint32_t locktime; int32_t txheight; CBlockIndex *tipindex; |
4b729ec5 | 4996 | if ( ASSETCHAINS_SYMBOL[0] == 0 && chainActive.LastTip() != 0 && chainActive.LastTip()->GetHeight() >= 60000 ) |
0ad6a463 | 4997 | { |
6ad13d7c | 4998 | if ( pcoin->vout[i].nValue >= 10*COIN ) |
9d92c93d | 4999 | { |
86131275 | 5000 | if ( (tipindex= chainActive.LastTip()) != 0 ) |
6ad13d7c | 5001 | { |
4b729ec5 | 5002 | komodo_accrued_interest(&txheight,&locktime,wtxid,i,0,pcoin->vout[i].nValue,(int32_t)tipindex->GetHeight()); |
79383e0a | 5003 | interest = komodo_interestnew(txheight,pcoin->vout[i].nValue,locktime,tipindex->nTime); |
6ad13d7c | 5004 | } else interest = 0; |
4b729ec5 | 5005 | //interest = komodo_interestnew(chainActive.LastTip()->GetHeight()+1,pcoin->vout[i].nValue,pcoin->nLockTime,chainActive.LastTip()->nTime); |
6ad13d7c | 5006 | if ( interest != 0 ) |
5007 | { | |
5008 | //printf("wallet nValueRet %.8f += interest %.8f ht.%d lock.%u/%u tip.%u\n",(double)pcoin->vout[i].nValue/COIN,(double)interest/COIN,txheight,locktime,pcoin->nLockTime,tipindex->nTime); | |
4b729ec5 | 5009 | //fprintf(stderr,"wallet nValueRet %.8f += interest %.8f ht.%d lock.%u tip.%u\n",(double)pcoin->vout[i].nValue/COIN,(double)interest/COIN,chainActive.LastTip()->GetHeight()+1,pcoin->nLockTime,chainActive.LastTip()->nTime); |
6ad13d7c | 5010 | //ptr = (uint64_t *)&pcoin->vout[i].nValue; |
5011 | //(*ptr) += interest; | |
5012 | ptr = (uint64_t *)&pcoin->vout[i].interest; | |
5013 | (*ptr) = interest; | |
5014 | //pcoin->vout[i].nValue += interest; | |
5015 | } | |
5016 | else | |
5017 | { | |
5018 | ptr = (uint64_t *)&pcoin->vout[i].interest; | |
5019 | (*ptr) = 0; | |
5020 | } | |
e9e8044e | 5021 | } |
258748f5 | 5022 | else |
5023 | { | |
5024 | ptr = (uint64_t *)&pcoin->vout[i].interest; | |
5025 | (*ptr) = 0; | |
5026 | } | |
0ad6a463 | 5027 | } |
258748f5 | 5028 | else |
5029 | { | |
5030 | ptr = (uint64_t *)&pcoin->vout[i].interest; | |
5031 | (*ptr) = 0; | |
5032 | } | |
5033 | } | |
0ad6a463 | 5034 | vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); |
5035 | } | |
fdbb537d | 5036 | } |
9b0369c7 CM |
5037 | } |
5038 | } | |
5039 | } | |
5040 | ||
56fe75cb | 5041 | void CWallet::AvailableReserveCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeCoinBase, bool fIncludeNative, const CTxDestination *pOnlyFromDest, const CCurrencyValueMap *pOnlyTheseCurrencies) const |
989b1de1 MT |
5042 | { |
5043 | vCoins.clear(); | |
5044 | ||
5045 | { | |
5046 | LOCK2(cs_main, cs_wallet); | |
5047 | for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) | |
5048 | { | |
5049 | const uint256& wtxid = it->first; | |
5050 | const CWalletTx* pcoin = &(*it).second; | |
5051 | ||
5052 | if (!CheckFinalTx(*pcoin)) | |
5053 | continue; | |
5054 | ||
5055 | if (fOnlyConfirmed && !pcoin->IsTrusted()) | |
5056 | continue; | |
5057 | ||
5058 | if (pcoin->IsCoinBase() && !fIncludeCoinBase) | |
5059 | continue; | |
5060 | ||
5061 | if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) | |
5062 | continue; | |
5063 | ||
5064 | int nDepth = pcoin->GetDepthInMainChain(); | |
5065 | if (nDepth < 0) | |
5066 | continue; | |
5067 | ||
5068 | for (int i = 0; i < pcoin->vout.size(); i++) | |
5069 | { | |
989b1de1 | 5070 | isminetype mine = IsMine(pcoin->vout[i]); |
2f416b17 | 5071 | if (!(IsSpent(wtxid, i)) && |
5072 | mine != ISMINE_NO && | |
56fe75cb | 5073 | !IsLockedCoin((*it).first, i) && |
989b1de1 MT |
5074 | (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) |
5075 | { | |
5076 | COptCCParams p; | |
15197dca | 5077 | CCurrencyValueMap rOut = pcoin->vout[i].scriptPubKey.ReserveOutValue(p, true); |
a88b514e | 5078 | |
15197dca | 5079 | if (p.IsValid() && !pcoin->vout[i].scriptPubKey.IsSpendableOutputType(p)) |
5080 | { | |
5081 | continue; | |
5082 | } | |
56fe75cb | 5083 | if (pOnlyFromDest) |
989b1de1 | 5084 | { |
56fe75cb | 5085 | if (p.IsValid()) |
5086 | { | |
5087 | bool found = false; | |
5088 | for (auto &oneDest : p.vKeys) | |
5089 | { | |
5090 | if (GetDestinationID(oneDest) == GetDestinationID(*pOnlyFromDest)) | |
5091 | { | |
5092 | found = true; | |
5093 | break; | |
5094 | } | |
5095 | } | |
5096 | if (!found) | |
5097 | { | |
5098 | continue; | |
5099 | } | |
5100 | } | |
5101 | else | |
5102 | { | |
5103 | // support P2PK or P2PKH | |
5104 | CTxDestination dest; | |
5105 | if (!ExtractDestination(pcoin->vout[i].scriptPubKey, dest) || GetDestinationID(dest) != GetDestinationID(*pOnlyFromDest)) | |
5106 | { | |
5107 | continue; | |
5108 | } | |
5109 | } | |
5110 | } | |
5111 | // don't return zero valued outputs | |
15197dca | 5112 | if (rOut.CanonicalMap().valueMap.size() || pcoin->vout[i].nValue) |
56fe75cb | 5113 | { |
2f416b17 | 5114 | if ((rOut.valueMap.size() && (!pOnlyTheseCurrencies || (pOnlyTheseCurrencies && pOnlyTheseCurrencies->Intersects(rOut)))) || |
5115 | (fIncludeNative && pcoin->vout[i].nValue)) | |
56fe75cb | 5116 | { |
5117 | vCoins.push_back(COutput(pcoin, i, nDepth, (mine & ISMINE_SPENDABLE) != ISMINE_NO)); | |
5118 | } | |
989b1de1 MT |
5119 | } |
5120 | } | |
5121 | } | |
5122 | } | |
5123 | } | |
5124 | } | |
5125 | ||
eed98728 | 5126 | bool CWallet::GetAndValidateSaplingZAddress(const std::string &addressStr, libzcash::PaymentAddress &zaddress) |
5127 | { | |
5128 | std::string addrCopy = addressStr; | |
5129 | std::vector<std::string> addressParts; | |
5130 | boost::split(addressParts, addrCopy, boost::is_any_of(":")); | |
5131 | ||
5132 | if (addressParts.size() == 2 && addressParts[1] == "private") | |
5133 | { | |
5134 | // look up to see if this is the private address of an ID. if not, or if the ID does not have a valid, Sapling address, it is invalid | |
5135 | CTxDestination destination = DecodeDestination(addressParts[0]); | |
5136 | if (destination.which() == COptCCParams::ADDRTYPE_ID) | |
5137 | { | |
5138 | AssertLockHeld(cs_main); | |
5139 | CIdentity idSource = CIdentity::LookupIdentity(GetDestinationID(destination)); | |
5140 | if (idSource.IsValid() && idSource.privateAddresses.size() > 0) | |
5141 | { | |
5142 | zaddress = idSource.privateAddresses[0]; | |
1f5cde4e | 5143 | return true; |
eed98728 | 5144 | } |
eed98728 | 5145 | } |
1f5cde4e | 5146 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid ID or ID that does not have valid z-address specified"); |
eed98728 | 5147 | } |
5148 | ||
5149 | zaddress = DecodePaymentAddress(addrCopy); | |
5150 | bool hasZSource = boost::get<libzcash::SaplingPaymentAddress>(&zaddress) != nullptr; | |
5151 | if (!hasZSource && boost::get<libzcash::SproutPaymentAddress>(&zaddress) != nullptr) | |
5152 | { | |
1f5cde4e | 5153 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy Sprout address not supported. Use a transparent or Sapling compatible address"); |
eed98728 | 5154 | } |
5155 | return hasZSource; | |
5156 | } | |
5157 | ||
0ad6a463 | 5158 | static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,vector<char>& vfBest, CAmount& nBest, int iterations = 1000) |
831f59ce CM |
5159 | { |
5160 | vector<char> vfIncluded; | |
5161 | ||
5162 | vfBest.assign(vValue.size(), true); | |
5163 | nBest = nTotalLower; | |
5164 | ||
907a2aa4 GM |
5165 | seed_insecure_rand(); |
5166 | ||
831f59ce CM |
5167 | for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) |
5168 | { | |
5169 | vfIncluded.assign(vValue.size(), false); | |
a372168e | 5170 | CAmount nTotal = 0; |
831f59ce CM |
5171 | bool fReachedTarget = false; |
5172 | for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) | |
5173 | { | |
5174 | for (unsigned int i = 0; i < vValue.size(); i++) | |
5175 | { | |
907a2aa4 GM |
5176 | //The solver here uses a randomized algorithm, |
5177 | //the randomness serves no real security purpose but is just | |
5178 | //needed to prevent degenerate behavior and it is important | |
5b40d886 | 5179 | //that the rng is fast. We do not use a constant random sequence, |
907a2aa4 GM |
5180 | //because there may be some privacy improvement by making |
5181 | //the selection random. | |
5182 | if (nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) | |
831f59ce CM |
5183 | { |
5184 | nTotal += vValue[i].first; | |
5185 | vfIncluded[i] = true; | |
5186 | if (nTotal >= nTargetValue) | |
5187 | { | |
5188 | fReachedTarget = true; | |
5189 | if (nTotal < nBest) | |
5190 | { | |
5191 | nBest = nTotal; | |
5192 | vfBest = vfIncluded; | |
5193 | } | |
5194 | nTotal -= vValue[i].first; | |
5195 | vfIncluded[i] = false; | |
5196 | } | |
5197 | } | |
5198 | } | |
5199 | } | |
5200 | } | |
5201 | } | |
5202 | ||
2ec1b02c | 5203 | // returns true if the selection contributes to getting any closer to the target. for example, |
5204 | // if a candidate value map contains more of currencies already present and none of those that are needed | |
5205 | // but not present, it will return false. if it contains currencies that are needed, it will return | |
5206 | // true. | |
5207 | bool CloserToTarget(const CCurrencyValueMap &target, const CCurrencyValueMap ¤t, const CCurrencyValueMap &candidate) | |
5208 | { | |
5209 | CCurrencyValueMap workingTarget = target.SubtractToZero(current); // whatever is left is what we still need | |
2ec1b02c | 5210 | return workingTarget.SubtractToZero(candidate) < workingTarget; |
5211 | } | |
5212 | ||
56fe75cb | 5213 | static void ApproximateBestReserveSubset(vector<pair<CCurrencyValueMap, pair<const CWalletTx*,unsigned int> > >vValue, |
ee7656e8 | 5214 | const CCurrencyValueMap &totalToOptimize, |
2ec1b02c | 5215 | const CCurrencyValueMap &targetValues, |
56fe75cb | 5216 | vector<char>& vfBest, |
5217 | CCurrencyValueMap& bestTotals, | |
5218 | int iterations = 1000) | |
5219 | { | |
5220 | vector<char> vfIncluded; | |
5221 | ||
5222 | vfBest.assign(vValue.size(), true); | |
ee7656e8 | 5223 | bestTotals = totalToOptimize; |
56fe75cb | 5224 | |
5225 | seed_insecure_rand(); | |
5226 | ||
5227 | for (int nRep = 0; nRep < iterations && bestTotals != targetValues; nRep++) | |
5228 | { | |
5229 | vfIncluded.assign(vValue.size(), false); | |
5230 | CCurrencyValueMap totals; | |
0ff96ff6 | 5231 | std::set<uint160> satisfied; |
56fe75cb | 5232 | bool fReachedTarget = false; |
5233 | for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) | |
5234 | { | |
0ff96ff6 | 5235 | CCurrencyValueMap adjustedTarget(targetValues); |
5236 | CCurrencyValueMap presentValues; | |
56fe75cb | 5237 | for (unsigned int i = 0; i < vValue.size(); i++) |
5238 | { | |
5239 | //The solver here uses a randomized algorithm, | |
5240 | //the randomness serves no real security purpose but is just | |
5241 | //needed to prevent degenerate behavior and it is important | |
5242 | //that the rng is fast. We do not use a constant random sequence, | |
5243 | //because there may be some privacy improvement by making | |
5244 | //the selection random. | |
2ec1b02c | 5245 | /* |
5246 | printf("targetValues\n%s\ntotals\n%s\nvValue[i].first\n%s\n", targetValues.ToUniValue().write(1,2).c_str(), | |
5247 | totals.ToUniValue().write(1,2).c_str(), | |
5248 | vValue[i].first.ToUniValue().write(1,2).c_str()); | |
5249 | printf("iscloser: %d\n", CloserToTarget(targetValues, totals, vValue[i].first)); | |
5250 | */ | |
0ff96ff6 | 5251 | |
2ec1b02c | 5252 | if ((nPass == 0 ? insecure_rand()&1 : !vfIncluded[i]) && CloserToTarget(targetValues, totals, vValue[i].first)) |
56fe75cb | 5253 | { |
0ff96ff6 | 5254 | totals += vValue[i].first.IntersectingValues(targetValues); |
56fe75cb | 5255 | vfIncluded[i] = true; |
56a7b665 | 5256 | // we reached the target if we fulfill all currencies |
0ff96ff6 | 5257 | |
5258 | adjustedTarget = targetValues.SubtractToZero(totals); | |
5259 | ||
5260 | // loop through all those that have been zeroed in the adjusted target, and mark as satisfied | |
5261 | for (auto &oneCur : targetValues.NonIntersectingValues(adjustedTarget).valueMap) | |
5262 | { | |
5263 | satisfied.insert(oneCur.first); | |
5264 | } | |
5265 | ||
5266 | if (satisfied.size() == targetValues.valueMap.size()) | |
56fe75cb | 5267 | { |
5268 | fReachedTarget = true; | |
0ff96ff6 | 5269 | CompareValueMap comparator(targetValues); |
5270 | if (comparator.CompareMaps(totals, bestTotals)) | |
56fe75cb | 5271 | { |
5272 | bestTotals = totals; | |
5273 | vfBest = vfIncluded; | |
5274 | } | |
5275 | totals -= vValue[i].first; | |
5276 | vfIncluded[i] = false; | |
5277 | } | |
5278 | } | |
5279 | } | |
5280 | } | |
5281 | } | |
5282 | } | |
5283 | ||
79383e0a | 5284 | bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const |
e8ef3da7 | 5285 | { |
79383e0a | 5286 | int32_t count = 0; //uint64_t lowest_interest = 0; |
e8ef3da7 | 5287 | setCoinsRet.clear(); |
79383e0a | 5288 | //memset(interests,0,sizeof(interests)); |
e8ef3da7 | 5289 | nValueRet = 0; |
e8ef3da7 | 5290 | // List of values less than target |
a372168e MF |
5291 | pair<CAmount, pair<const CWalletTx*,unsigned int> > coinLowestLarger; |
5292 | coinLowestLarger.first = std::numeric_limits<CAmount>::max(); | |
e8ef3da7 | 5293 | coinLowestLarger.second.first = NULL; |
a372168e MF |
5294 | vector<pair<CAmount, pair<const CWalletTx*,unsigned int> > > vValue; |
5295 | CAmount nTotalLower = 0; | |
e8ef3da7 | 5296 | |
e333ab56 CM |
5297 | random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); |
5298 | ||
c8988460 | 5299 | BOOST_FOREACH(const COutput &output, vCoins) |
e8ef3da7 | 5300 | { |
c8988460 PW |
5301 | if (!output.fSpendable) |
5302 | continue; | |
5303 | ||
b80aa0c7 | 5304 | if (output.tx->vout[output.i].nValue == 0) |
5305 | { | |
5306 | continue; | |
5307 | } | |
5308 | ||
9b0369c7 | 5309 | const CWalletTx *pcoin = output.tx; |
e8ef3da7 | 5310 | |
a3e192a3 | 5311 | if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) |
9b0369c7 | 5312 | continue; |
e8ef3da7 | 5313 | |
9b0369c7 | 5314 | int i = output.i; |
a372168e | 5315 | CAmount n = pcoin->vout[i].nValue; |
e8ef3da7 | 5316 | |
a372168e | 5317 | pair<CAmount,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i)); |
e8ef3da7 | 5318 | |
9b0369c7 CM |
5319 | if (n == nTargetValue) |
5320 | { | |
5321 | setCoinsRet.insert(coin.second); | |
5322 | nValueRet += coin.first; | |
79383e0a | 5323 | //if ( KOMODO_EXCHANGEWALLET == 0 ) |
5324 | // *interestp += pcoin->vout[i].interest; | |
9b0369c7 CM |
5325 | return true; |
5326 | } | |
5327 | else if (n < nTargetValue + CENT) | |
5328 | { | |
5329 | vValue.push_back(coin); | |
5330 | nTotalLower += n; | |
79383e0a | 5331 | //if ( KOMODO_EXCHANGEWALLET == 0 && count < sizeof(interests)/sizeof(*interests) ) |
5332 | //{ | |
9067057a | 5333 | //fprintf(stderr,"count.%d %.8f\n",count,(double)pcoin->vout[i].interest/COIN); |
79383e0a | 5334 | //interests[count++] = pcoin->vout[i].interest; |
5335 | //} | |
5336 | if ( nTotalLower > 4*nTargetValue + CENT ) | |
92c2aa6b | 5337 | { |
eaba9a27 | 5338 | //fprintf(stderr,"why bother with all the utxo if we have double what is needed?\n"); |
92c2aa6b | 5339 | break; |
5340 | } | |
9b0369c7 CM |
5341 | } |
5342 | else if (n < coinLowestLarger.first) | |
5343 | { | |
5344 | coinLowestLarger = coin; | |
79383e0a | 5345 | //if ( KOMODO_EXCHANGEWALLET == 0 ) |
5346 | // lowest_interest = pcoin->vout[i].interest; | |
e8ef3da7 WL |
5347 | } |
5348 | } | |
5349 | ||
831f59ce | 5350 | if (nTotalLower == nTargetValue) |
e8ef3da7 | 5351 | { |
c376ac35 | 5352 | for (unsigned int i = 0; i < vValue.size(); ++i) |
e8ef3da7 WL |
5353 | { |
5354 | setCoinsRet.insert(vValue[i].second); | |
5355 | nValueRet += vValue[i].first; | |
79383e0a | 5356 | //if ( KOMODO_EXCHANGEWALLET == 0 && i < count ) |
5357 | // *interestp += interests[i]; | |
e8ef3da7 WL |
5358 | } |
5359 | return true; | |
5360 | } | |
5361 | ||
831f59ce | 5362 | if (nTotalLower < nTargetValue) |
e8ef3da7 WL |
5363 | { |
5364 | if (coinLowestLarger.second.first == NULL) | |
5365 | return false; | |
5366 | setCoinsRet.insert(coinLowestLarger.second); | |
5367 | nValueRet += coinLowestLarger.first; | |
79383e0a | 5368 | //if ( KOMODO_EXCHANGEWALLET == 0 ) |
5369 | // *interestp += lowest_interest; | |
e8ef3da7 WL |
5370 | return true; |
5371 | } | |
5372 | ||
e8ef3da7 | 5373 | // Solve subset sum by stochastic approximation |
d650f96d | 5374 | sort(vValue.rbegin(), vValue.rend(), CompareValueOnly()); |
831f59ce | 5375 | vector<char> vfBest; |
a372168e | 5376 | CAmount nBest; |
e8ef3da7 | 5377 | |
831f59ce CM |
5378 | ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); |
5379 | if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT) | |
5380 | ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000); | |
e8ef3da7 | 5381 | |
831f59ce CM |
5382 | // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, |
5383 | // or the next bigger coin is closer), return the bigger coin | |
5384 | if (coinLowestLarger.second.first && | |
5385 | ((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest)) | |
e8ef3da7 WL |
5386 | { |
5387 | setCoinsRet.insert(coinLowestLarger.second); | |
5388 | nValueRet += coinLowestLarger.first; | |
79383e0a | 5389 | //if ( KOMODO_EXCHANGEWALLET == 0 ) |
5390 | // *interestp += lowest_interest; | |
e8ef3da7 WL |
5391 | } |
5392 | else { | |
c376ac35 | 5393 | for (unsigned int i = 0; i < vValue.size(); i++) |
e8ef3da7 WL |
5394 | if (vfBest[i]) |
5395 | { | |
5396 | setCoinsRet.insert(vValue[i].second); | |
5397 | nValueRet += vValue[i].first; | |
79383e0a | 5398 | //if ( KOMODO_EXCHANGEWALLET == 0 && i < count ) |
5399 | // *interestp += interests[i]; | |
e8ef3da7 WL |
5400 | } |
5401 | ||
faaeae1e | 5402 | LogPrint("selectcoins", "SelectCoins() best subset: "); |
c376ac35 | 5403 | for (unsigned int i = 0; i < vValue.size(); i++) |
e8ef3da7 | 5404 | if (vfBest[i]) |
4f0f864d | 5405 | LogPrint("selectcoins", "%s", FormatMoney(vValue[i].first)); |
7d9d134b | 5406 | LogPrint("selectcoins", "total %s\n", FormatMoney(nBest)); |
e8ef3da7 WL |
5407 | } |
5408 | ||
5409 | return true; | |
5410 | } | |
5411 | ||
1e435b54 | 5412 | bool CWallet::SelectCoins(const CAmount& nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, bool& fOnlyProtectedCoinbaseCoinsRet, bool& fNeedProtectedCoinbaseCoinsRet, const CCoinControl* coinControl) const |
e8ef3da7 | 5413 | { |
1e435b54 | 5414 | // Output parameter fOnlyProtectedCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos. |
79383e0a | 5415 | uint64_t tmp; int32_t retval; |
5416 | //if ( interestp == 0 ) | |
5417 | //{ | |
5418 | // interestp = &tmp; | |
5419 | // *interestp = 0; | |
5420 | //} | |
2b1cda3b | 5421 | vector<COutput> vCoinsNoCoinbase, vCoinsWithCoinbase; |
1e435b54 | 5422 | AvailableCoins(vCoinsNoCoinbase, true, coinControl, false, true, false); |
5423 | AvailableCoins(vCoinsWithCoinbase, true, coinControl, false, true, true); | |
5424 | fOnlyProtectedCoinbaseCoinsRet = vCoinsNoCoinbase.size() == 0 && vCoinsWithCoinbase.size() > 0; | |
2b1cda3b S |
5425 | |
5426 | // If coinbase utxos can only be sent to zaddrs, exclude any coinbase utxos from coin selection. | |
5427 | bool fProtectCoinbase = Params().GetConsensus().fCoinbaseMustBeProtected; | |
5428 | vector<COutput> vCoins = (fProtectCoinbase) ? vCoinsNoCoinbase : vCoinsWithCoinbase; | |
5429 | ||
1e435b54 | 5430 | // Output parameter fNeedProtectedCoinbaseCoinsRet is set to true if coinbase utxos that must be shielded need to be spent to meet target amount |
2b1cda3b S |
5431 | if (fProtectCoinbase && vCoinsWithCoinbase.size() > vCoinsNoCoinbase.size()) { |
5432 | CAmount value = 0; | |
5433 | for (const COutput& out : vCoinsNoCoinbase) { | |
5434 | if (!out.fSpendable) { | |
5435 | continue; | |
5436 | } | |
5437 | value += out.tx->vout[out.i].nValue; | |
f87150f4 | 5438 | if ( KOMODO_EXCHANGEWALLET == 0 ) |
5439 | value += out.tx->vout[out.i].interest; | |
2b1cda3b S |
5440 | } |
5441 | if (value <= nTargetValue) { | |
5442 | CAmount valueWithCoinbase = 0; | |
5443 | for (const COutput& out : vCoinsWithCoinbase) { | |
5444 | if (!out.fSpendable) { | |
5445 | continue; | |
5446 | } | |
5447 | valueWithCoinbase += out.tx->vout[out.i].nValue; | |
f87150f4 | 5448 | if ( KOMODO_EXCHANGEWALLET == 0 ) |
5449 | valueWithCoinbase += out.tx->vout[out.i].interest; | |
2b1cda3b | 5450 | } |
1e435b54 | 5451 | fNeedProtectedCoinbaseCoinsRet = (valueWithCoinbase >= nTargetValue); |
2b1cda3b S |
5452 | } |
5453 | } | |
6a86c24d | 5454 | // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) |
aa30f655 | 5455 | if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) |
6a86c24d CL |
5456 | { |
5457 | BOOST_FOREACH(const COutput& out, vCoins) | |
5458 | { | |
aa30f655 MC |
5459 | if (!out.fSpendable) |
5460 | continue; | |
6a86c24d | 5461 | nValueRet += out.tx->vout[out.i].nValue; |
79383e0a | 5462 | //if ( KOMODO_EXCHANGEWALLET == 0 ) |
5463 | // *interestp += out.tx->vout[out.i].interest; | |
6a86c24d CL |
5464 | setCoinsRet.insert(make_pair(out.tx, out.i)); |
5465 | } | |
5466 | return (nValueRet >= nTargetValue); | |
5467 | } | |
aa30f655 MC |
5468 | // calculate value from preset inputs and store them |
5469 | set<pair<const CWalletTx*, uint32_t> > setPresetCoins; | |
5470 | CAmount nValueFromPresetInputs = 0; | |
5471 | ||
5472 | std::vector<COutPoint> vPresetInputs; | |
5473 | if (coinControl) | |
5474 | coinControl->ListSelected(vPresetInputs); | |
5475 | BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) | |
5476 | { | |
5477 | map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); | |
5478 | if (it != mapWallet.end()) | |
5479 | { | |
5480 | const CWalletTx* pcoin = &it->second; | |
5481 | // Clearly invalid input, fail | |
5482 | if (pcoin->vout.size() <= outpoint.n) | |
5483 | return false; | |
5484 | nValueFromPresetInputs += pcoin->vout[outpoint.n].nValue; | |
945f015d | 5485 | if ( KOMODO_EXCHANGEWALLET == 0 ) |
5486 | nValueFromPresetInputs += pcoin->vout[outpoint.n].interest; | |
aa30f655 MC |
5487 | setPresetCoins.insert(make_pair(pcoin, outpoint.n)); |
5488 | } else | |
5489 | return false; // TODO: Allow non-wallet inputs | |
5490 | } | |
5491 | ||
5492 | // remove preset inputs from vCoins | |
5493 | for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) | |
5494 | { | |
5495 | if (setPresetCoins.count(make_pair(it->tx, it->i))) | |
5496 | it = vCoins.erase(it); | |
5497 | else | |
5498 | ++it; | |
5499 | } | |
945f015d | 5500 | retval = false; |
5501 | if ( nTargetValue <= nValueFromPresetInputs ) | |
5502 | retval = true; | |
79383e0a | 5503 | else if ( SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) != 0 ) |
945f015d | 5504 | retval = true; |
79383e0a | 5505 | else if ( SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) != 0 ) |
945f015d | 5506 | retval = true; |
79383e0a | 5507 | else if ( bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet) != 0 ) |
945f015d | 5508 | retval = true; |
aa30f655 MC |
5509 | // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset |
5510 | setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); | |
aa30f655 MC |
5511 | // add preset inputs to the total value selected |
5512 | nValueRet += nValueFromPresetInputs; | |
945f015d | 5513 | return retval; |
e8ef3da7 WL |
5514 | } |
5515 | ||
56fe75cb | 5516 | bool CWallet::SelectReserveCoinsMinConf(const CCurrencyValueMap& targetValues, |
5517 | CAmount targetNativeValue, | |
5518 | int nConfMine, | |
5519 | int nConfTheirs, | |
5520 | std::vector<COutput> vCoins, | |
5521 | std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, | |
5522 | CCurrencyValueMap& valueRet, | |
5523 | CAmount &nativeValueRet) const | |
989b1de1 MT |
5524 | { |
5525 | int32_t count = 0; //uint64_t lowest_interest = 0; | |
5526 | setCoinsRet.clear(); | |
7beb1c0b | 5527 | valueRet.valueMap.clear(); |
5528 | nativeValueRet = 0; | |
989b1de1 | 5529 | //memset(interests,0,sizeof(interests)); |
56fe75cb | 5530 | |
ee7656e8 | 5531 | // for each currency type being looked for, store the lowest larger outputs found in order, up to a maximum of the number of |
5532 | // different currencies being looked for | |
5533 | std::map<uint160, std::multimap<CAmount, CReserveOutSelectionInfo>> coinsLowestLarger; | |
0ff96ff6 | 5534 | std::map<std::pair<const CWalletTx *, int>, CCurrencyValueMap> largerOuts; // all those that are >= than amount requested in at least one currency |
ee7656e8 | 5535 | std::multimap<int, std::pair<std::vector<uint160>, CReserveOutSelectionInfo>> multiSatisfy; // for outputs that satisfy >= one currency |
5536 | CCurrencyValueMap largerTotal; | |
5537 | std::map<uint160, std::multimap<CAmount, CReserveOutSelectionInfo>> coinsLargestLower; | |
0ff96ff6 | 5538 | std::map<std::pair<const CWalletTx *, int>, CCurrencyValueMap> lowerOuts; // all those that are lower or unneeded for larger and helpful |
ee7656e8 | 5539 | CCurrencyValueMap lowerTotal; |
5540 | ||
5541 | CCurrencyValueMap nativeCent(std::vector<uint160>({ASSETCHAINS_CHAINID}), std::vector<CAmount>({CENT})); | |
5542 | ||
5543 | CCurrencyValueMap totalToOptimize; | |
5544 | std::vector<std::pair<CCurrencyValueMap, std::pair<const CWalletTx*, unsigned int>>> vOutputsToOptimize; | |
989b1de1 MT |
5545 | |
5546 | random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); | |
5547 | ||
ee7656e8 | 5548 | CCurrencyValueMap nTotalTarget = (targetValues + CCurrencyValueMap(std::vector<uint160>({ASSETCHAINS_CHAINID}), std::vector<CAmount>({targetNativeValue}))).CanonicalMap(); |
a88b514e | 5549 | |
a1a85fd6 | 5550 | //printf("totaltarget: %s\n", nTotalTarget.ToUniValue().write().c_str()); |
56a7b665 | 5551 | |
0ff96ff6 | 5552 | // currencies in the target that are satisfied x4 in the lower list |
5553 | std::set<uint160> satisfied_x4; | |
5554 | CCurrencyValueMap targetx4(nTotalTarget * 4 + nativeCent); | |
5555 | ||
5556 | for (const COutput &output : vCoins) | |
989b1de1 MT |
5557 | { |
5558 | if (!output.fSpendable) | |
5559 | continue; | |
5560 | ||
5561 | const CWalletTx *pcoin = output.tx; | |
5562 | ||
5563 | if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) | |
5564 | continue; | |
5565 | ||
5566 | int i = output.i; | |
ee7656e8 | 5567 | CCurrencyValueMap nAll(pcoin->vout[i].scriptPubKey.ReserveOutValue()); // all currencies, whether in target or not |
0ff96ff6 | 5568 | CCurrencyValueMap nTotal = nAll.IntersectingValues(targetValues); // nTotal will be all currencies, including native, that are also in target |
56fe75cb | 5569 | CAmount nativeN = pcoin->vout[i].nValue; |
56a7b665 | 5570 | if (nativeN) |
5571 | { | |
5572 | nAll.valueMap[ASSETCHAINS_CHAINID] = nativeN; | |
5573 | if (targetNativeValue) | |
5574 | { | |
5575 | nTotal.valueMap[ASSETCHAINS_CHAINID] = nativeN; | |
5576 | } | |
5577 | } | |
989b1de1 | 5578 | |
56a7b665 | 5579 | // if it has no output types we care about, next |
ee7656e8 | 5580 | if (!nTotal.valueMap.size()) |
56fe75cb | 5581 | { |
5582 | continue; | |
5583 | } | |
989b1de1 | 5584 | |
a88b514e | 5585 | //printf("nTotal: %s\n", nTotal.ToUniValue().write().c_str()); |
5586 | ||
ee7656e8 | 5587 | CReserveOutSelectionInfo coin(pcoin, i, nAll); |
5588 | ||
5589 | // if all values are equivalent to targets, we've found the perfect output, no more searching needed | |
eed98728 | 5590 | // TODO: should we early out, even if we have extra currencies? If so, use nTotal to commpare |
0ff96ff6 | 5591 | if (nTotal == nTotalTarget) |
989b1de1 | 5592 | { |
ee7656e8 | 5593 | setCoinsRet.insert(std::make_pair(coin.pWtx, coin.n)); |
7beb1c0b | 5594 | valueRet = pcoin->vout[i].scriptPubKey.ReserveOutValue(); |
5595 | nativeValueRet = nativeN; | |
989b1de1 MT |
5596 | return true; |
5597 | } | |
56a7b665 | 5598 | |
0ff96ff6 | 5599 | CCurrencyValueMap subtractedFromTarget(nTotalTarget.SubtractToZero(nTotal)); |
5600 | ||
ee7656e8 | 5601 | // now, we need to loop through all targets to see if this satisfies any single currency requirement completely |
5602 | // if so, we will include it in the largest lower list for that currency | |
5603 | int numLarger = 0; | |
5604 | std::vector<uint160> multiCurrencies; | |
0ff96ff6 | 5605 | |
5606 | COutput sanitizedOutput(output.tx, output.i, 0, true); | |
5607 | ||
5608 | // if we have some entries larger than target | |
5609 | if (subtractedFromTarget.valueMap.size() < nTotalTarget.valueMap.size()) | |
989b1de1 | 5610 | { |
0ff96ff6 | 5611 | //printf("subtractedFromTarget:\n%s\nnTotalTarget:\n%s\nnTotal.NonIntersectingValues(subtractedFromTarget):\n%s\n", subtractedFromTarget.ToUniValue().write().c_str(), nTotal.ToUniValue().write().c_str(), nTotalTarget.NonIntersectingValues(subtractedFromTarget).ToUniValue().write().c_str()); |
5612 | for (auto oneCur : nTotal.NonIntersectingValues(subtractedFromTarget).valueMap) | |
989b1de1 | 5613 | { |
0ff96ff6 | 5614 | coinsLowestLarger[oneCur.first].insert(std::make_pair(oneCur.second, CReserveOutSelectionInfo(output.tx, output.i, nAll))); |
ee7656e8 | 5615 | multiCurrencies.push_back(oneCur.first); |
0ff96ff6 | 5616 | numLarger++; |
989b1de1 MT |
5617 | } |
5618 | } | |
ee7656e8 | 5619 | if (numLarger) |
989b1de1 | 5620 | { |
0ff96ff6 | 5621 | largerOuts.insert(std::make_pair(std::make_pair(output.tx, output.i), nAll)); |
ee7656e8 | 5622 | largerTotal += nTotal; |
5623 | multiSatisfy.insert(std::make_pair(numLarger, std::make_pair(multiCurrencies, coin))); | |
5624 | } | |
5625 | else | |
5626 | { | |
0ff96ff6 | 5627 | bool neededCurrency = false; |
ee7656e8 | 5628 | for (auto &oneCur : nTotal.valueMap) |
56a7b665 | 5629 | { |
0ff96ff6 | 5630 | if (satisfied_x4.count(oneCur.first)) |
ee7656e8 | 5631 | { |
0ff96ff6 | 5632 | continue; |
ee7656e8 | 5633 | } |
0ff96ff6 | 5634 | neededCurrency = true; |
5635 | coinsLargestLower[oneCur.first].insert(std::make_pair(oneCur.second, coin)); | |
56a7b665 | 5636 | } |
0ff96ff6 | 5637 | if (!neededCurrency) |
5638 | { | |
5639 | continue; | |
5640 | } | |
5641 | ||
5642 | lowerOuts.insert(std::make_pair(std::make_pair(output.tx, output.i), nAll)); | |
ee7656e8 | 5643 | lowerTotal += nTotal; |
0ff96ff6 | 5644 | |
5645 | CCurrencyValueMap adjTargetx4 = targetx4.SubtractToZero(lowerTotal); | |
5646 | //printf("targetx4:\n%s\nadjTargetx4:\n%s\n", targetx4.ToUniValue().write().c_str(), adjTargetx4.ToUniValue().write().c_str()); | |
5647 | ||
5648 | // loop through all those that have been zeroed in the adjusted target, and mark as satisfied | |
5649 | for (auto &oneCur : targetx4.NonIntersectingValues(adjTargetx4).valueMap) | |
5650 | { | |
5651 | //printf("satisfied x 4: %s\n", EncodeDestination(CIdentityID(oneCur.first)).c_str()); | |
5652 | satisfied_x4.insert(oneCur.first); | |
5653 | } | |
5654 | ||
5655 | if (satisfied_x4.size() == nTotalTarget.valueMap.size()) | |
56a7b665 | 5656 | { |
0ff96ff6 | 5657 | //printf("short circuit lower: lowerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str()); |
ee7656e8 | 5658 | break; |
56a7b665 | 5659 | } |
989b1de1 MT |
5660 | } |
5661 | } | |
5662 | ||
0ff96ff6 | 5663 | std::set<uint160> satisfied_larger; |
5664 | ||
5665 | CCurrencyValueMap newLargerTotal; | |
5666 | CCurrencyValueMap adjTotalTarget; | |
5667 | std::map<std::pair<const CWalletTx *, int>, CCurrencyValueMap> largerCoins; // int is the index into the vOutputsToOptimize to remove | |
5668 | ||
ee7656e8 | 5669 | // if our lower total + larger total are not enough, no way we have enough |
5670 | if ((lowerTotal + largerTotal) < nTotalTarget) | |
5671 | { | |
5672 | return false; | |
5673 | } | |
2ec1b02c | 5674 | |
0ff96ff6 | 5675 | //printf("\nlowerTotal:\n%s\nlargerTotal:\n%s\nnewLargerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), largerTotal.ToUniValue().write().c_str(), newLargerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str()); |
5676 | ||
5677 | for (auto &lowerOut : lowerOuts) | |
5678 | { | |
5679 | totalToOptimize += lowerOut.second; | |
5680 | vOutputsToOptimize.push_back(std::make_pair(lowerOut.second, std::make_pair(lowerOut.first.first, lowerOut.first.second))); | |
5681 | } | |
a88b514e | 5682 | |
ee7656e8 | 5683 | // if all the lower amounts are just what we need, and we don't add too many inputs in the process, use them all |
eed98728 | 5684 | size_t numInputsLimit = (size_t)GetArg("-mempooltxinputlimit", MAX_NUM_INPUTS_LIMIT); |
5685 | ||
5686 | if ((lowerTotal >= nTotalTarget && lowerTotal <= (nTotalTarget + nativeCent)) && lowerOuts.size() <= numInputsLimit) | |
989b1de1 | 5687 | { |
0ff96ff6 | 5688 | //printf("selecting all lowers\nlowerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str()); |
5689 | ||
ee7656e8 | 5690 | for (auto oneOut : lowerOuts) |
989b1de1 | 5691 | { |
0ff96ff6 | 5692 | setCoinsRet.insert(std::make_pair(oneOut.first.first, oneOut.first.second)); |
5693 | valueRet += oneOut.first.first->vout[oneOut.first.second].ReserveOutValue(); | |
5694 | nativeValueRet += oneOut.first.first->vout[oneOut.first.second].nValue; | |
989b1de1 MT |
5695 | } |
5696 | return true; | |
5697 | } | |
5698 | ||
0ff96ff6 | 5699 | //printf("\nlowerTotal:\n%s\nlargerTotal:\n%s\nTotalTarget:\n%s\n", lowerTotal.ToUniValue().write().c_str(), largerTotal.ToUniValue().write().c_str(), nTotalTarget.ToUniValue().write().c_str()); |
5700 | ||
ee7656e8 | 5701 | std::map<std::pair<const CWalletTx *, int>, CReserveOutSelectionInfo> added; |
0ff96ff6 | 5702 | largerTotal.valueMap.clear(); |
5703 | CCurrencyValueMap adjustedTarget; | |
ee7656e8 | 5704 | std::set<uint160> satisfied; |
5705 | ||
5706 | // short circuit best fit check with any exact amounts we may have | |
5707 | if (multiSatisfy.size()) | |
989b1de1 | 5708 | { |
ee7656e8 | 5709 | // each output for each currency will satisfy one or more currency requirements |
5710 | // first check those that satisfy more than one currency, then select those which are lowest value in currencies they satisfy | |
56a7b665 | 5711 | |
ee7656e8 | 5712 | // check in reverse to check those that satisfy most first |
5713 | for (auto multiIt = multiSatisfy.rbegin(); multiIt != multiSatisfy.rend(); multiIt++) | |
5714 | { | |
5715 | // if we have 0 left, we're done | |
0ff96ff6 | 5716 | if (nTotalTarget.valueMap.size() == satisfied.size()) |
ee7656e8 | 5717 | { |
0ff96ff6 | 5718 | //printf("satisfied all currencies. lowerTotal:\n%s\n", largerTotal.ToUniValue().write().c_str()); |
ee7656e8 | 5719 | break; |
5720 | } | |
5721 | ||
eed98728 | 5722 | // consider "satisfying" an exact match of any currency in the adjusted request, otherwise, we should fall through to the best fit solver |
ee7656e8 | 5723 | int newFound = 0; |
5724 | for (auto &oneCurID : multiIt->second.first) | |
5725 | { | |
5726 | if (!satisfied.count(oneCurID) && | |
5727 | multiIt->second.second.outVal.valueMap[oneCurID] == adjustedTarget.valueMap[oneCurID]) | |
5728 | { | |
ee7656e8 | 5729 | newFound++; |
5730 | } | |
5731 | } | |
5732 | ||
0ff96ff6 | 5733 | std::pair<const CWalletTx *, unsigned int> outPair({multiIt->second.second.pWtx, multiIt->second.second.n}); |
5734 | ||
ee7656e8 | 5735 | // if we don't satisfy any new currency with this output, don't add it as we care more if singles are lower as a priotity |
0ff96ff6 | 5736 | if (!newFound || added.count(outPair)) |
ee7656e8 | 5737 | { |
5738 | continue; | |
5739 | } | |
5740 | ||
5741 | // this satisfies at least 1 new currency, so use it and also reduce other currencies by all amounts that it includes | |
5742 | // don't check it again when looking later | |
0ff96ff6 | 5743 | added.insert(std::make_pair(outPair, multiIt->second.second)); |
ee7656e8 | 5744 | |
5745 | // add all currency values in the transaction, as some may partially satisfy, and we should early out when we have enough | |
0ff96ff6 | 5746 | // printf("multiIt->second.second.outVal:\n%s\n", multiIt->second.second.outVal.ToUniValue().write().c_str()); |
5747 | CCurrencyValueMap newAdded(multiIt->second.second.outVal.IntersectingValues(nTotalTarget)); | |
5748 | largerTotal += newAdded; | |
5749 | largerOuts.erase(outPair); | |
5750 | ||
5751 | // printf("adjustedTarget:\n%s\ntotalAdded:\n%s\n", adjustedTarget.ToUniValue().write().c_str(), totalAdded.ToUniValue().write().c_str()); | |
5752 | ||
5753 | // printf("adjustedTarget:\n%s\n", adjustedTarget.ToUniValue().write().c_str()); | |
5754 | // printf("nTotalTarget.NonIntersectingValues(adjustedTarget):\n%s\n", nTotalTarget.NonIntersectingValues(adjustedTarget).ToUniValue().write().c_str()); | |
5755 | ||
5756 | adjustedTarget = nTotalTarget.SubtractToZero(largerTotal); | |
5757 | ||
ee7656e8 | 5758 | // loop through all those that have been zeroed in the adjusted target, and mark as satisfied |
5759 | for (auto &oneCur : nTotalTarget.NonIntersectingValues(adjustedTarget).valueMap) | |
5760 | { | |
0ff96ff6 | 5761 | //printf("satisfied: %s\n", EncodeDestination(CIdentityID(oneCur.first)).c_str()); |
ee7656e8 | 5762 | satisfied.insert(oneCur.first); |
5763 | } | |
5764 | } | |
5765 | } | |
5766 | ||
5767 | // if we've satisfied all currency requirements with larger outputs that fit well, use what we have and be done | |
5768 | if (satisfied.size() == nTotalTarget.valueMap.size()) | |
5769 | { | |
5770 | for (auto &oneOut : added) | |
5771 | { | |
5772 | setCoinsRet.insert(std::make_pair(oneOut.second.pWtx, oneOut.second.n)); | |
5773 | valueRet += oneOut.second.outVal; | |
5774 | } | |
5775 | auto vRetIt = valueRet.valueMap.find(ASSETCHAINS_CHAINID); | |
5776 | if (vRetIt != valueRet.valueMap.end()) | |
5777 | { | |
5778 | nativeValueRet = vRetIt->second; | |
5779 | valueRet.valueMap.erase(vRetIt); | |
5780 | } | |
989b1de1 MT |
5781 | return true; |
5782 | } | |
5783 | ||
0ff96ff6 | 5784 | // fill up lower outputs with larger as well to ensure fill |
5785 | // those we add from multisatisfy check will be removed from optimized selection | |
5786 | for (auto &oneCurID : satisfied) | |
5787 | { | |
5788 | satisfied_x4.insert(oneCurID); | |
5789 | } | |
5790 | for (auto &largerOut : largerOuts) | |
5791 | { | |
5792 | COutput thisOutput(largerOut.first.first, largerOut.first.second, 0, true); | |
5793 | if (lowerOuts.count(std::make_pair(largerOut.first.first, largerOut.first.second))) | |
5794 | { | |
5795 | continue; | |
5796 | } | |
5797 | // if we have more, they only go into the lower, if they have | |
5798 | // coins in the currencies where we are not satisfied | |
5799 | ||
5800 | //printf("targetx4:\n%s\nlowerTotal:\n%s\nlargerOut.second:\n%s\n", targetx4.ToUniValue().write().c_str(), lowerTotal.ToUniValue().write().c_str(), largerOut.second.ToUniValue().write().c_str()); | |
5801 | ||
5802 | bool useThis = false; | |
5803 | for (auto &oneCur : largerOut.second.IntersectingValues(nTotalTarget).valueMap) | |
5804 | { | |
5805 | if (!satisfied.count(oneCur.first) && !satisfied_x4.count(oneCur.first)) | |
5806 | { | |
5807 | useThis = true; | |
5808 | } | |
5809 | } | |
5810 | ||
5811 | if (useThis) | |
5812 | { | |
5813 | CReserveOutSelectionInfo coin(largerOut.first.first, largerOut.first.second, largerOut.second); | |
5814 | ||
5815 | for (auto &oneCur : largerOut.second.valueMap) | |
5816 | { | |
5817 | coinsLargestLower[oneCur.first].insert(std::make_pair(oneCur.second, coin)); | |
5818 | } | |
5819 | ||
5820 | lowerOuts.insert(std::make_pair(std::make_pair(largerOut.first.first, largerOut.first.second), largerOut.second)); | |
5821 | ||
5822 | lowerTotal += largerOut.second; | |
5823 | ||
5824 | CCurrencyValueMap adjTargetx4 = targetx4.SubtractToZero(lowerTotal); | |
5825 | //printf("targetx4:\n%s\nadjTargetx4:\n%s\n", targetx4.ToUniValue().write().c_str(), adjTargetx4.ToUniValue().write().c_str()); | |
5826 | ||
5827 | // loop through all those that have been zeroed in the adjusted target, and mark as satisfied | |
5828 | for (auto &oneCur : targetx4.NonIntersectingValues(adjTargetx4).valueMap) | |
5829 | { | |
5830 | // don't consider it satisfied x4, unless we have at least 4 entries to choose from | |
5831 | if (coinsLargestLower.count(oneCur.first) && coinsLargestLower[oneCur.first].size() >= 4) | |
5832 | { | |
5833 | //printf("satisfied x 4: %s\n", EncodeDestination(CIdentityID(oneCur.first)).c_str()); | |
5834 | satisfied_x4.insert(oneCur.first); | |
5835 | } | |
5836 | } | |
5837 | totalToOptimize += largerOut.second; | |
5838 | vOutputsToOptimize.push_back(std::make_pair(largerOut.second, std::make_pair(largerOut.first.first, largerOut.first.second))); | |
5839 | } | |
5840 | } | |
5841 | ||
5842 | //printf("\nlargerTotal:\n%s\n", largerTotal.ToUniValue().write().c_str()); | |
5843 | // printf("adjustedTarget:\n%s\n", adjustedTarget.ToUniValue().write().c_str()); | |
5844 | ||
ee7656e8 | 5845 | // make new vector without those we have added due to exact fit, and use remaining and adjusted target to satisfy requests |
5846 | std::vector<int> vOutputsToRemove; | |
5847 | CCurrencyValueMap removedValue; | |
0ff96ff6 | 5848 | if (added.size()) |
ee7656e8 | 5849 | { |
0ff96ff6 | 5850 | for (int i = 0; i < vOutputsToOptimize.size(); i++) |
5851 | { | |
5852 | if (added.count(vOutputsToOptimize[i].second)) | |
5853 | { | |
5854 | vOutputsToRemove.push_back(i); | |
5855 | removedValue += vOutputsToOptimize[i].first; | |
5856 | } | |
5857 | } | |
5858 | ||
5859 | for (auto &oneOutput : added) | |
ee7656e8 | 5860 | { |
0ff96ff6 | 5861 | setCoinsRet.insert(std::make_pair(oneOutput.second.pWtx, oneOutput.second.n)); |
5862 | valueRet += oneOutput.second.outVal; | |
5863 | } | |
5864 | auto vRetIt = valueRet.valueMap.find(ASSETCHAINS_CHAINID); | |
5865 | if (vRetIt != valueRet.valueMap.end()) | |
5866 | { | |
5867 | nativeValueRet = vRetIt->second; | |
5868 | valueRet.valueMap.erase(vRetIt); | |
ee7656e8 | 5869 | } |
5870 | } | |
56fe75cb | 5871 | |
ee7656e8 | 5872 | // remove all that we've already added leaving a vector of those that we need to optimize |
5873 | for (int i = vOutputsToRemove.size() - 1; i >= 0; i--) | |
2ec1b02c | 5874 | { |
ee7656e8 | 5875 | vOutputsToOptimize.erase(vOutputsToOptimize.begin() + vOutputsToRemove[i]); |
2ec1b02c | 5876 | } |
2ec1b02c | 5877 | |
0ff96ff6 | 5878 | totalToOptimize = totalToOptimize.SubtractToZero(removedValue); |
5879 | CCurrencyValueMap newOptimizationTarget = nTotalTarget.SubtractToZero(largerTotal); | |
56a7b665 | 5880 | |
0ff96ff6 | 5881 | //printf("totalToOptimize:\n%s\nnewOptimizationTarget:\n%s\n", totalToOptimize.ToUniValue().write().c_str(), newOptimizationTarget.ToUniValue().write().c_str()); |
5882 | /* for (int i = 0; i < vOutputsToOptimize.size(); i++) | |
a88b514e | 5883 | { |
5884 | printf("output #%d:\nreserves:\n%s\nnative:\n%s\n", | |
5885 | i, | |
5886 | vOutputsToOptimize[i].first.ToUniValue().write().c_str(), | |
5887 | ValueFromAmount(vOutputsToOptimize[i].second.first->vout[vOutputsToOptimize[i].second.second].nValue).write().c_str()); | |
5888 | } */ | |
5889 | ||
989b1de1 | 5890 | vector<char> vfBest; |
56fe75cb | 5891 | CCurrencyValueMap bestTotals; |
989b1de1 | 5892 | |
ee7656e8 | 5893 | ApproximateBestReserveSubset(vOutputsToOptimize, totalToOptimize, newOptimizationTarget, vfBest, bestTotals, 1000); |
0ff96ff6 | 5894 | if (bestTotals != newOptimizationTarget && totalToOptimize >= newOptimizationTarget + nativeCent) |
a88b514e | 5895 | { |
0ff96ff6 | 5896 | //printf("bestTotals:\n%s\ntotalToOptimize:\n%s\nnewOptimizationTarget:\n%s\n", bestTotals.ToUniValue().write().c_str(), totalToOptimize.ToUniValue().write().c_str(), (newOptimizationTarget + nativeCent).ToUniValue().write().c_str()); |
ee7656e8 | 5897 | ApproximateBestReserveSubset(vOutputsToOptimize, totalToOptimize, newOptimizationTarget + nativeCent, vfBest, bestTotals, 1000); |
a88b514e | 5898 | } |
989b1de1 | 5899 | |
ee7656e8 | 5900 | for (unsigned int i = 0; i < vOutputsToOptimize.size(); i++) |
989b1de1 | 5901 | { |
56a7b665 | 5902 | if (vfBest[i]) |
5903 | { | |
ee7656e8 | 5904 | setCoinsRet.insert(vOutputsToOptimize[i].second); |
5905 | valueRet += vOutputsToOptimize[i].second.first->vout[vOutputsToOptimize[i].second.second].ReserveOutValue(); | |
5906 | nativeValueRet += vOutputsToOptimize[i].second.first->vout[vOutputsToOptimize[i].second.second].nValue; | |
0ff96ff6 | 5907 | /* printf("one selected\ntxid: %s, output: %d\nvalueOut: %s\n", |
5908 | vOutputsToOptimize[i].second.first->GetHash().GetHex().c_str(), | |
5909 | vOutputsToOptimize[i].second.second, | |
5910 | vOutputsToOptimize[i].first.ToUniValue().write(1,2).c_str()); */ | |
56a7b665 | 5911 | } |
989b1de1 | 5912 | } |
989b1de1 | 5913 | |
a88b514e | 5914 | CCurrencyValueMap checkReturn(valueRet); |
ec1b7e23 | 5915 | checkReturn.valueMap[ASSETCHAINS_CHAINID] = nativeValueRet; |
a88b514e | 5916 | |
ee7656e8 | 5917 | if (checkReturn.IntersectingValues(nTotalTarget) < nTotalTarget) |
5918 | { | |
5919 | return false; | |
5920 | } | |
5921 | ||
56a7b665 | 5922 | LogPrint("selectcoins", "SelectCoins() best subset: "); |
ee7656e8 | 5923 | for (unsigned int i = 0; i < vOutputsToOptimize.size(); i++) |
56a7b665 | 5924 | { |
5925 | if (vfBest[i]) | |
5926 | { | |
ee7656e8 | 5927 | LogPrint("selectcoins", "%s", FormatMoney(vOutputsToOptimize[i].first.valueMap[targetValues.valueMap.begin()->first])); |
56a7b665 | 5928 | } |
989b1de1 | 5929 | } |
56a7b665 | 5930 | LogPrint("selectcoins", "total %s\n", FormatMoney(bestTotals.valueMap[targetValues.valueMap.begin()->first])); |
989b1de1 MT |
5931 | |
5932 | return true; | |
5933 | } | |
5934 | ||
56fe75cb | 5935 | bool CWallet::SelectReserveCoins(const CCurrencyValueMap& targetReserveValues, |
5936 | CAmount targetNativeValue, | |
5937 | set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, | |
5938 | CCurrencyValueMap &valueRet, | |
5939 | CAmount &nativeRet, | |
5940 | bool& fOnlyCoinbaseCoinsRet, | |
5941 | bool& fNeedCoinbaseCoinsRet, | |
5942 | const CCoinControl* coinControl, | |
5943 | const CTxDestination *pOnlyFromDest) const | |
989b1de1 MT |
5944 | { |
5945 | // Output parameter fOnlyCoinbaseCoinsRet is set to true when the only available coins are coinbase utxos. | |
56a7b665 | 5946 | |
989b1de1 | 5947 | vector<COutput> vCoinsNoCoinbase, vCoinsWithCoinbase; |
56a7b665 | 5948 | AvailableReserveCoins(vCoinsNoCoinbase, true, coinControl, false, true, pOnlyFromDest, &targetReserveValues); |
5949 | AvailableReserveCoins(vCoinsWithCoinbase, true, coinControl, true, true, pOnlyFromDest, &targetReserveValues); | |
989b1de1 MT |
5950 | fOnlyCoinbaseCoinsRet = vCoinsNoCoinbase.size() == 0 && vCoinsWithCoinbase.size() > 0; |
5951 | ||
1188acf6 | 5952 | // coinbase protection forcing them to be spent only to z-addresses ended |
5953 | // when identities were released | |
5954 | bool fProtectCoinbase = false; | |
989b1de1 | 5955 | |
56a7b665 | 5956 | vector<COutput> vCoins = (fProtectCoinbase) ? vCoinsNoCoinbase : vCoinsWithCoinbase; |
c83e4936 | 5957 | |
989b1de1 MT |
5958 | // Output parameter fNeedCoinbaseCoinsRet is set to true if coinbase utxos need to be spent to meet target amount |
5959 | if (fProtectCoinbase && vCoinsWithCoinbase.size() > vCoinsNoCoinbase.size()) { | |
c83e4936 | 5960 | CCurrencyValueMap reserveValues; |
56fe75cb | 5961 | CAmount nativeValue = 0; |
989b1de1 MT |
5962 | for (const COutput& out : vCoinsNoCoinbase) { |
5963 | if (!out.fSpendable) { | |
5964 | continue; | |
5965 | } | |
56fe75cb | 5966 | nativeValue += out.tx->vout[out.i].nValue; |
c83e4936 | 5967 | reserveValues += out.tx->vout[out.i].ReserveOutValue(); |
989b1de1 | 5968 | } |
c83e4936 | 5969 | if (reserveValues < targetReserveValues || nativeValue < targetNativeValue) { |
5970 | CCurrencyValueMap reserveValuesWithCoinbase; | |
56fe75cb | 5971 | CAmount nativeValueWithCoinbase = 0; |
989b1de1 MT |
5972 | for (const COutput& out : vCoinsWithCoinbase) { |
5973 | if (!out.fSpendable) { | |
5974 | continue; | |
5975 | } | |
c83e4936 | 5976 | reserveValuesWithCoinbase += out.tx->vout[out.i].ReserveOutValue(); |
56fe75cb | 5977 | nativeValueWithCoinbase += out.tx->vout[out.i].nValue; |
989b1de1 | 5978 | } |
c83e4936 | 5979 | fNeedCoinbaseCoinsRet = (reserveValuesWithCoinbase >= targetReserveValues) && (nativeValueWithCoinbase >= targetNativeValue); |
989b1de1 MT |
5980 | } |
5981 | } | |
56fe75cb | 5982 | |
989b1de1 MT |
5983 | // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) |
5984 | if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs) | |
5985 | { | |
5986 | BOOST_FOREACH(const COutput& out, vCoins) | |
5987 | { | |
5988 | if (!out.fSpendable) | |
5989 | continue; | |
56fe75cb | 5990 | valueRet += out.tx->vout[out.i].ReserveOutValue(); |
5991 | nativeRet += out.tx->vout[out.i].nValue; | |
989b1de1 MT |
5992 | setCoinsRet.insert(make_pair(out.tx, out.i)); |
5993 | } | |
56fe75cb | 5994 | return (valueRet >= targetReserveValues) && (nativeRet >= targetNativeValue); |
989b1de1 | 5995 | } |
56fe75cb | 5996 | |
989b1de1 MT |
5997 | // calculate value from preset inputs and store them |
5998 | set<pair<const CWalletTx*, uint32_t> > setPresetCoins; | |
56fe75cb | 5999 | CCurrencyValueMap valueFromPresetInputs; |
6000 | CAmount nativeValueFromPresets = 0; | |
989b1de1 MT |
6001 | |
6002 | std::vector<COutPoint> vPresetInputs; | |
6003 | if (coinControl) | |
6004 | coinControl->ListSelected(vPresetInputs); | |
6005 | BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs) | |
6006 | { | |
6007 | map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); | |
6008 | if (it != mapWallet.end()) | |
6009 | { | |
6010 | const CWalletTx* pcoin = &it->second; | |
6011 | // Clearly invalid input, fail | |
6012 | if (pcoin->vout.size() <= outpoint.n) | |
6013 | return false; | |
56fe75cb | 6014 | valueFromPresetInputs += pcoin->vout[outpoint.n].ReserveOutValue(); |
6015 | nativeValueFromPresets += pcoin->vout[outpoint.n].nValue; | |
989b1de1 MT |
6016 | setPresetCoins.insert(make_pair(pcoin, outpoint.n)); |
6017 | } else | |
6018 | return false; // TODO: Allow non-wallet inputs | |
6019 | } | |
6020 | ||
6021 | // remove preset inputs from vCoins | |
6022 | for (vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();) | |
6023 | { | |
6024 | if (setPresetCoins.count(make_pair(it->tx, it->i))) | |
6025 | it = vCoins.erase(it); | |
6026 | else | |
6027 | ++it; | |
6028 | } | |
56a7b665 | 6029 | |
6030 | bool retval = false; | |
2f416b17 | 6031 | if ( targetNativeValue <= nativeRet && |
6032 | targetReserveValues <= targetReserveValues.IntersectingValues(valueFromPresetInputs) && targetNativeValue <= nativeValueFromPresets ) | |
989b1de1 | 6033 | retval = true; |
56fe75cb | 6034 | else if ( SelectReserveCoinsMinConf(targetReserveValues, targetNativeValue, 1, 6, vCoins, setCoinsRet, valueRet, nativeRet) != 0 ) |
989b1de1 | 6035 | retval = true; |
56fe75cb | 6036 | else if ( SelectReserveCoinsMinConf(targetReserveValues, targetNativeValue, 1, 1, vCoins, setCoinsRet, valueRet, nativeRet) != 0 ) |
989b1de1 | 6037 | retval = true; |
56fe75cb | 6038 | else if ( bSpendZeroConfChange && SelectReserveCoinsMinConf(targetReserveValues, targetNativeValue, 0, 1, vCoins, setCoinsRet, valueRet, nativeRet) != 0 ) |
989b1de1 MT |
6039 | retval = true; |
6040 | // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset | |
6041 | setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end()); | |
6042 | // add preset inputs to the total value selected | |
56fe75cb | 6043 | valueRet += valueFromPresetInputs; |
6044 | nativeRet += nativeValueFromPresets; | |
989b1de1 MT |
6045 | return retval; |
6046 | } | |
6047 | ||
aa30f655 MC |
6048 | bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason) |
6049 | { | |
6050 | vector<CRecipient> vecSend; | |
6051 | ||
6052 | // Turn the txout set into a CRecipient vector | |
6053 | BOOST_FOREACH(const CTxOut& txOut, tx.vout) | |
6054 | { | |
6055 | CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false}; | |
6056 | vecSend.push_back(recipient); | |
6057 | } | |
6058 | ||
6059 | CCoinControl coinControl; | |
6060 | coinControl.fAllowOtherInputs = true; | |
6061 | BOOST_FOREACH(const CTxIn& txin, tx.vin) | |
6062 | coinControl.Select(txin.prevout); | |
6063 | ||
6064 | CReserveKey reservekey(this); | |
6065 | CWalletTx wtx; | |
efb7662d | 6066 | |
aa30f655 MC |
6067 | if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false)) |
6068 | return false; | |
6069 | ||
6070 | if (nChangePosRet != -1) | |
6071 | tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]); | |
6072 | ||
6073 | // Add new txins (keeping original txin scriptSig/order) | |
6074 | BOOST_FOREACH(const CTxIn& txin, wtx.vin) | |
6075 | { | |
6076 | bool found = false; | |
6077 | BOOST_FOREACH(const CTxIn& origTxIn, tx.vin) | |
6078 | { | |
6079 | if (txin.prevout.hash == origTxIn.prevout.hash && txin.prevout.n == origTxIn.prevout.n) | |
6080 | { | |
6081 | found = true; | |
6082 | break; | |
6083 | } | |
6084 | } | |
6085 | if (!found) | |
6086 | tx.vin.push_back(txin); | |
6087 | } | |
6088 | ||
6089 | return true; | |
e8ef3da7 WL |
6090 | } |
6091 | ||
aa30f655 MC |
6092 | bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, |
6093 | int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign) | |
e8ef3da7 | 6094 | { |
79383e0a | 6095 | uint64_t interest2 = 0; CAmount nValue = 0; unsigned int nSubtractFeeFromAmount = 0; |
292623ad | 6096 | BOOST_FOREACH (const CRecipient& recipient, vecSend) |
e8ef3da7 | 6097 | { |
292623ad | 6098 | if (nValue < 0 || recipient.nAmount < 0) |
1f00f4e9 GA |
6099 | { |
6100 | strFailReason = _("Transaction amounts must be positive"); | |
e8ef3da7 | 6101 | return false; |
1f00f4e9 | 6102 | } |
292623ad CL |
6103 | nValue += recipient.nAmount; |
6104 | ||
6105 | if (recipient.fSubtractFeeFromAmount) | |
6106 | nSubtractFeeFromAmount++; | |
e8ef3da7 WL |
6107 | } |
6108 | if (vecSend.empty() || nValue < 0) | |
1f00f4e9 GA |
6109 | { |
6110 | strFailReason = _("Transaction amounts must be positive"); | |
e8ef3da7 | 6111 | return false; |
1f00f4e9 | 6112 | } |
e8ef3da7 | 6113 | |
b33d1f5e | 6114 | wtxNew.fTimeReceivedIsTxTime = true; |
4c6e2295 | 6115 | wtxNew.BindWallet(this); |
9bb37bf0 | 6116 | int nextBlockHeight = chainActive.Height() + 1; |
bb6c3482 | 6117 | |
6118 | CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); | |
6119 | txNew.nLockTime = (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now | |
e8ef3da7 | 6120 | |
9bb37bf0 | 6121 | // Activates after Overwinter network upgrade |
88d014d0 | 6122 | if (Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER)) { |
fa70084c | 6123 | if (txNew.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){ |
9bb37bf0 | 6124 | strFailReason = _("nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD."); |
7b92f27e | 6125 | return false; |
9bb37bf0 | 6126 | } |
e19d8b3d | 6127 | } |
15ec5525 | 6128 | |
25fee350 | 6129 | unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING; |
88d014d0 | 6130 | if (!Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING)) { |
15ec5525 JG |
6131 | max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING; |
6132 | } | |
efb7662d | 6133 | |
ba7fcc8d PT |
6134 | // Discourage fee sniping. |
6135 | // | |
6136 | // However because of a off-by-one-error in previous versions we need to | |
6137 | // neuter it by setting nLockTime to at least one less than nBestHeight. | |
6138 | // Secondly currently propagation of transactions created for block heights | |
6139 | // corresponding to blocks that were just mined may be iffy - transactions | |
6140 | // aren't re-accepted into the mempool - we additionally neuter the code by | |
6141 | // going ten blocks back. Doesn't yet do anything for sniping, but does act | |
6142 | // to shake out wallet bugs like not showing nLockTime'd transactions at | |
6143 | // all. | |
6144 | txNew.nLockTime = std::max(0, chainActive.Height() - 10); | |
6145 | ||
6146 | // Secondly occasionally randomly pick a nLockTime even further back, so | |
6147 | // that transactions that are delayed after signing for whatever reason, | |
6148 | // e.g. high-latency mix networks and some CoinJoin implementations, have | |
6149 | // better privacy. | |
6150 | if (GetRandInt(10) == 0) | |
6151 | txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | |
6152 | ||
6153 | assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | |
6154 | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | |
6155 | ||
e8ef3da7 | 6156 | { |
f8dcd5ca | 6157 | LOCK2(cs_main, cs_wallet); |
e8ef3da7 | 6158 | { |
c1c9d5b4 | 6159 | nFeeRet = 0; |
050d2e95 | 6160 | while (true) |
e8ef3da7 | 6161 | { |
f19ffc34 | 6162 | //interest = 0; |
4949004d PW |
6163 | txNew.vin.clear(); |
6164 | txNew.vout.clear(); | |
e8ef3da7 | 6165 | wtxNew.fFromMe = true; |
292623ad CL |
6166 | nChangePosRet = -1; |
6167 | bool fFirst = true; | |
e8ef3da7 | 6168 | |
292623ad CL |
6169 | CAmount nTotalValue = nValue; |
6170 | if (nSubtractFeeFromAmount == 0) | |
6171 | nTotalValue += nFeeRet; | |
e8ef3da7 WL |
6172 | double dPriority = 0; |
6173 | // vouts to the payees | |
292623ad | 6174 | BOOST_FOREACH (const CRecipient& recipient, vecSend) |
8de9bb53 | 6175 | { |
292623ad CL |
6176 | CTxOut txout(recipient.nAmount, recipient.scriptPubKey); |
6177 | ||
6178 | if (recipient.fSubtractFeeFromAmount) | |
6179 | { | |
6180 | txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient | |
6181 | ||
6182 | if (fFirst) // first receiver pays the remainder not divisible by output count | |
6183 | { | |
6184 | fFirst = false; | |
6185 | txout.nValue -= nFeeRet % nSubtractFeeFromAmount; | |
6186 | } | |
6187 | } | |
6188 | ||
2f537fa1 | 6189 | COptCCParams p; |
6190 | ||
6191 | if (txout.IsDust(::minRelayTxFee) && !(txout.nValue == 0 && txout.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode != EVAL_NONE)) | |
1f00f4e9 | 6192 | { |
292623ad CL |
6193 | if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) |
6194 | { | |
6195 | if (txout.nValue < 0) | |
6196 | strFailReason = _("The transaction amount is too small to pay the fee"); | |
6197 | else | |
6198 | strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); | |
6199 | } | |
6200 | else | |
6201 | strFailReason = _("Transaction amount too small"); | |
8de9bb53 | 6202 | return false; |
1f00f4e9 | 6203 | } |
4949004d | 6204 | txNew.vout.push_back(txout); |
8de9bb53 | 6205 | } |
e8ef3da7 WL |
6206 | |
6207 | // Choose coins to use | |
6208 | set<pair<const CWalletTx*,unsigned int> > setCoins; | |
a372168e | 6209 | CAmount nValueIn = 0; |
1e435b54 | 6210 | bool fOnlyProtectedCoinbaseCoins = false; |
6211 | bool fNeedProtectedCoinbaseCoins = false; | |
9152feb5 | 6212 | interest2 = 0; |
1e435b54 | 6213 | if (!SelectCoins(nTotalValue, setCoins, nValueIn, fOnlyProtectedCoinbaseCoins, fNeedProtectedCoinbaseCoins, coinControl)) |
1f00f4e9 | 6214 | { |
1e435b54 | 6215 | if (fOnlyProtectedCoinbaseCoins) { |
6216 | strFailReason = _("Coinbase funds earned while shielding protection is active can only be sent to a zaddr"); | |
6217 | } else if (fNeedProtectedCoinbaseCoins) { | |
6218 | strFailReason = _("Insufficient funds, protected coinbase funds can only be spent after they have been sent to a zaddr"); | |
2b1cda3b S |
6219 | } else { |
6220 | strFailReason = _("Insufficient funds"); | |
6221 | } | |
e8ef3da7 | 6222 | return false; |
1f00f4e9 | 6223 | } |
e8ef3da7 WL |
6224 | BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) |
6225 | { | |
a372168e | 6226 | CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; |
2d9b0b7f | 6227 | //The coin age after the next block (depth+1) is used instead of the current, |
d7836552 GM |
6228 | //reflecting an assumption the user would accept a bit more delay for |
6229 | //a chance at a free transaction. | |
2d9b0b7f | 6230 | //But mempool inputs might still be in the mempool, so their age stays 0 |
38dfbe15 | 6231 | //fprintf(stderr,"nCredit %.8f interest %.8f\n",(double)nCredit/COIN,(double)pcoin.first->vout[pcoin.second].interest/COIN); |
d1f29a9d | 6232 | if ( KOMODO_EXCHANGEWALLET == 0 && ASSETCHAINS_SYMBOL[0] == 0 ) |
608252ed | 6233 | { |
6ad13d7c | 6234 | interest2 += pcoin.first->vout[pcoin.second].interest; |
d1f29a9d | 6235 | //fprintf(stderr,"%.8f ",(double)pcoin.first->vout[pcoin.second].interest/COIN); |
85cb030e | 6236 | } |
2d9b0b7f AM |
6237 | int age = pcoin.first->GetDepthInMainChain(); |
6238 | if (age != 0) | |
6239 | age += 1; | |
6240 | dPriority += (double)nCredit * age; | |
e8ef3da7 | 6241 | } |
79383e0a | 6242 | //if ( KOMODO_EXCHANGEWALLET != 0 ) |
6243 | //{ | |
2626c7e3 | 6244 | //fprintf(stderr,"KOMODO_EXCHANGEWALLET disable interest sum %.8f, interest2 %.8f\n",(double)interest/COIN,(double)interest2/COIN); |
79383e0a | 6245 | //interest = 0; // interest2 also |
6246 | //} | |
1bc74c28 | 6247 | if ( ASSETCHAINS_SYMBOL[0] == 0 && DONATION_PUBKEY.size() == 66 && interest2 > 5000 ) |
6248 | { | |
817ad14d | 6249 | CScript scriptDonation = CScript() << ParseHex(DONATION_PUBKEY) << OP_CHECKSIG; |
6250 | CTxOut newTxOut(interest2,scriptDonation); | |
719aa8b1 | 6251 | int32_t nDonationPosRet = txNew.vout.size() - 1; // dont change first or last |
6252 | vector<CTxOut>::iterator position = txNew.vout.begin()+nDonationPosRet; | |
817ad14d | 6253 | txNew.vout.insert(position, newTxOut); |
1bc74c28 | 6254 | interest2 = 0; |
6255 | } | |
608252ed | 6256 | CAmount nChange = (nValueIn - nValue + interest2); |
b7c685b8 | 6257 | //fprintf(stderr,"wallet change %.8f (%.8f - %.8f) interest2 %.8f total %.8f\n",(double)nChange/COIN,(double)nValueIn/COIN,(double)nValue/COIN,(double)interest2/COIN,(double)nTotalValue/COIN); |
292623ad CL |
6258 | if (nSubtractFeeFromAmount == 0) |
6259 | nChange -= nFeeRet; | |
a7dd11c6 PW |
6260 | |
6261 | if (nChange > 0) | |
e8ef3da7 | 6262 | { |
bf798734 GA |
6263 | // Fill a vout to ourself |
6264 | // TODO: pass in scriptChange instead of reservekey so | |
6265 | // change transaction isn't always pay-to-bitcoin-address | |
e8ef3da7 | 6266 | CScript scriptChange; |
6a86c24d CL |
6267 | |
6268 | // coin control: send change to custom address | |
6269 | if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange)) | |
0be990ba | 6270 | scriptChange = GetScriptForDestination(coinControl->destChange); |
6a86c24d CL |
6271 | |
6272 | // no coin control: send change to newly generated address | |
6273 | else | |
6274 | { | |
6275 | // Note: We use a new key here to keep it from being obvious which side is the change. | |
6276 | // The drawback is that by not reusing a previous key, the change may be lost if a | |
6277 | // backup is restored, if the backup doesn't have the new private key for the change. | |
6278 | // If we reused the old key, it would be possible to add code to look for and | |
6279 | // rediscover unknown transactions that were written with keys of ours to recover | |
6280 | // post-backup change. | |
6281 | ||
6282 | // Reserve a new key pair from key pool | |
6283 | CPubKey vchPubKey; | |
3ec03ada | 6284 | extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY; |
6285 | if ( USE_EXTERNAL_PUBKEY == 0 ) | |
6286 | { | |
3ec03ada | 6287 | bool ret; |
6288 | ret = reservekey.GetReservedKey(vchPubKey); | |
6289 | assert(ret); // should never fail, as we just unlocked | |
6290 | scriptChange = GetScriptForDestination(vchPubKey.GetID()); | |
6291 | } | |
6292 | else | |
6293 | { | |
18c6cfce | 6294 | //fprintf(stderr,"use notary pubkey\n"); |
3ec03ada | 6295 | scriptChange = CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG; |
6296 | } | |
6a86c24d | 6297 | } |
e8ef3da7 | 6298 | |
8de9bb53 GA |
6299 | CTxOut newTxOut(nChange, scriptChange); |
6300 | ||
292623ad CL |
6301 | // We do not move dust-change to fees, because the sender would end up paying more than requested. |
6302 | // This would be against the purpose of the all-inclusive feature. | |
6303 | // So instead we raise the change and deduct from the recipient. | |
6304 | if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee)) | |
6305 | { | |
6306 | CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue; | |
6307 | newTxOut.nValue += nDust; // raise change until no more dust | |
6308 | for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient | |
6309 | { | |
6310 | if (vecSend[i].fSubtractFeeFromAmount) | |
6311 | { | |
6312 | txNew.vout[i].nValue -= nDust; | |
6313 | if (txNew.vout[i].IsDust(::minRelayTxFee)) | |
6314 | { | |
6315 | strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); | |
6316 | return false; | |
6317 | } | |
6318 | break; | |
6319 | } | |
6320 | } | |
6321 | } | |
6322 | ||
8de9bb53 | 6323 | // Never create dust outputs; if we would, just |
2f537fa1 | 6324 | // add the dust to the fee. Valid cryptoconditions with a valid eval function are allowed to create outputs of 0 |
13fc83c7 | 6325 | if (newTxOut.IsDust(::minRelayTxFee)) |
8de9bb53 GA |
6326 | { |
6327 | nFeeRet += nChange; | |
6328 | reservekey.ReturnKey(); | |
6329 | } | |
6330 | else | |
6331 | { | |
429dabb5 | 6332 | nChangePosRet = txNew.vout.size() - 1; // dont change first or last |
292623ad | 6333 | vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosRet; |
4949004d | 6334 | txNew.vout.insert(position, newTxOut); |
8de9bb53 | 6335 | } |
f38345e9 | 6336 | } else reservekey.ReturnKey(); |
e8ef3da7 WL |
6337 | |
6338 | // Fill vin | |
ba7fcc8d PT |
6339 | // |
6340 | // Note how the sequence number is set to max()-1 so that the | |
6341 | // nLockTime set above actually works. | |
e8ef3da7 | 6342 | BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) |
805344dc | 6343 | txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), |
ba7fcc8d | 6344 | std::numeric_limits<unsigned int>::max()-1)); |
989b1de1 MT |
6345 | |
6346 | // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects | |
6347 | size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); | |
6348 | { | |
6349 | LOCK(cs_main); | |
88d014d0 | 6350 | if (Params().GetConsensus().NetworkUpgradeActive(chainActive.Height() + 1, Consensus::UPGRADE_OVERWINTER)) { |
989b1de1 MT |
6351 | limit = 0; |
6352 | } | |
6353 | } | |
6354 | if (limit > 0) { | |
6355 | size_t n = txNew.vin.size(); | |
6356 | if (n > limit) { | |
6357 | strFailReason = _(strprintf("Too many transparent inputs %zu > limit %zu", n, limit).c_str()); | |
6358 | return false; | |
6359 | } | |
6360 | } | |
6361 | ||
6362 | // Grab the current consensus branch ID | |
6363 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); | |
6364 | ||
6365 | // Sign | |
6366 | int nIn = 0; | |
6367 | CTransaction txNewConst(txNew); | |
6368 | BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) | |
6369 | { | |
6370 | bool signSuccess; | |
6371 | const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; | |
6372 | SignatureData sigdata; | |
6373 | if (sign) | |
a4f9bc97 | 6374 | signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, scriptPubKey), scriptPubKey, sigdata, consensusBranchId); |
989b1de1 MT |
6375 | else |
6376 | signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata, consensusBranchId); | |
6377 | ||
6378 | if (!signSuccess) | |
6379 | { | |
6380 | strFailReason = _("Signing transaction failed"); | |
6381 | return false; | |
6382 | } else { | |
6383 | UpdateTransaction(txNew, nIn, sigdata); | |
6384 | } | |
6385 | ||
6386 | nIn++; | |
6387 | } | |
6388 | ||
6389 | unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); | |
6390 | ||
6391 | // Remove scriptSigs if we used dummy signatures for fee calculation | |
6392 | if (!sign) { | |
6393 | BOOST_FOREACH (CTxIn& vin, txNew.vin) | |
6394 | vin.scriptSig = CScript(); | |
6395 | } | |
6396 | ||
6397 | // Embed the constructed transaction data in wtxNew. | |
6398 | *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); | |
6399 | ||
6400 | // Limit size | |
6401 | if (nBytes >= max_tx_size) | |
6402 | { | |
6403 | strFailReason = _("Transaction too large"); | |
6404 | return false; | |
6405 | } | |
6406 | ||
6407 | dPriority = wtxNew.ComputePriority(dPriority, nBytes); | |
6408 | ||
6409 | // Can we complete this as a free transaction? | |
6410 | if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) | |
6411 | { | |
6412 | // Not enough fee: enough priority? | |
6413 | double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); | |
6414 | // Not enough mempool history to estimate: use hard-coded AllowFree. | |
6415 | if (dPriorityNeeded <= 0 && AllowFree(dPriority)) | |
6416 | break; | |
6417 | ||
6418 | // Small enough, and priority high enough, to send for free | |
6419 | if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) | |
6420 | break; | |
6421 | } | |
6422 | ||
6423 | CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); | |
6424 | if ( nFeeNeeded < 5000 ) | |
6425 | nFeeNeeded = 5000; | |
6426 | ||
6427 | // If we made it here and we aren't even able to meet the relay fee on the next pass, give up | |
6428 | // because we must be at the maximum allowed fee. | |
6429 | if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) | |
6430 | { | |
6431 | strFailReason = _("Transaction too large for fee policy"); | |
6432 | return false; | |
6433 | } | |
6434 | ||
6435 | if (nFeeRet >= nFeeNeeded) | |
6436 | break; // Done, enough fee included. | |
6437 | ||
6438 | // Include more fee and try again. | |
6439 | nFeeRet = nFeeNeeded; | |
6440 | continue; | |
6441 | } | |
6442 | } | |
6443 | } | |
6444 | ||
6445 | return true; | |
6446 | } | |
6447 | ||
56fe75cb | 6448 | // almost the same as CreateTransaction with the difference being that input and output are assumed to be |
6449 | // tokens/reserve currencies, not the native currency of this chain, represented as reserve outputs for both input and output. | |
6450 | // That means that all outputs must be reserve consuming outputs. Fee is added or converted from reserves if this is a | |
6451 | // fractional reserve chain. Fees are calculated based on the current reserve conversion price. | |
0f957664 | 6452 | int CWallet::CreateReserveTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, |
56fe75cb | 6453 | int& nChangePosRet, int &nChangeOutputs, std::string& strFailReason, const CCoinControl* coinControl, |
6454 | const CTxDestination *pOnlyFromDest, bool sign) | |
989b1de1 | 6455 | { |
56fe75cb | 6456 | CCurrencyValueMap totalReserveOutput; |
6457 | CAmount totalNativeOutput = 0; | |
6458 | ||
6459 | CCurrencyValueMap reserveCurrencies; | |
989b1de1 MT |
6460 | unsigned int nSubtractFeeFromAmount = 0; |
6461 | ||
56fe75cb | 6462 | // fees can only be deducted from fractional reserve outputs on fractional currency blockchains, otherwise, |
6463 | // Verus/Verustest must be used to cover fees. | |
6464 | bool isVerusActive = IsVerusActive(); | |
2f416b17 | 6465 | if (!isVerusActive) |
56fe75cb | 6466 | { |
6467 | if (ConnectedChains.ReserveCurrencies().size()) | |
6468 | { | |
6469 | for (auto &oneCur : ConnectedChains.ReserveCurrencies()) | |
6470 | { | |
6471 | reserveCurrencies.valueMap[oneCur.first] = 1; // we're only testing for intersection, not value | |
6472 | } | |
6473 | } | |
6474 | } | |
6475 | ||
6476 | // make sure we have some outputs | |
6477 | if (vecSend.empty()) | |
6478 | { | |
6479 | strFailReason = _("Transaction must have outputs"); | |
0f957664 | 6480 | return RPC_INVALID_PARAMETER; |
56fe75cb | 6481 | } |
989b1de1 | 6482 | |
2f416b17 | 6483 | CCurrencyDefinition newCurrency; |
6484 | ||
989b1de1 MT |
6485 | // make sure that there are recipients, all recipients expect reserve inputs, and amounts are all non-negative |
6486 | BOOST_FOREACH (const CRecipient& recipient, vecSend) | |
6487 | { | |
2f416b17 | 6488 | COptCCParams p; |
6489 | if (recipient.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CURRENCY_DEFINITION && p.vData.size() >= 1) | |
6490 | { | |
6491 | if (newCurrency.IsValid()) | |
6492 | { | |
6493 | strFailReason = _("A normal transaction cannot define define multiple currencies"); | |
0f957664 | 6494 | return RPC_INVALID_PARAMETER; |
2f416b17 | 6495 | } |
6496 | newCurrency = CCurrencyDefinition(p.vData[0]); | |
6497 | } | |
56fe75cb | 6498 | CCurrencyValueMap values = recipient.scriptPubKey.ReserveOutValue(); |
6499 | CCurrencyValueMap zeroes = values - values; // zero values of the same currencies | |
6500 | ||
6501 | if (!values.IsValid()) | |
989b1de1 | 6502 | { |
56fe75cb | 6503 | strFailReason = _("Output cannot have NULL currency type"); |
0f957664 | 6504 | return RPC_INVALID_PARAMETER; |
989b1de1 | 6505 | } |
56fe75cb | 6506 | if (values.HasNegative()) |
989b1de1 | 6507 | { |
56fe75cb | 6508 | strFailReason = _("Transaction output amounts must not be negative"); |
0f957664 | 6509 | return RPC_INVALID_PARAMETER; |
989b1de1 | 6510 | } |
2f416b17 | 6511 | |
56fe75cb | 6512 | totalNativeOutput += recipient.nAmount; |
f023c2d4 | 6513 | |
56fe75cb | 6514 | totalReserveOutput += values; |
f365e363 | 6515 | |
56fe75cb | 6516 | // if we should take from this output, it must be able to pay the fee. fail if it does not |
6517 | if (recipient.fSubtractFeeFromAmount && (recipient.nAmount > 0 || reserveCurrencies.Intersects(values))) | |
6518 | { | |
6519 | nSubtractFeeFromAmount++; | |
6520 | } | |
6521 | else if (recipient.fSubtractFeeFromAmount) | |
989b1de1 | 6522 | { |
56fe75cb | 6523 | strFailReason = _("Cannot specify to subtract fee from amount on non-native, non-reserve currency outputs"); |
0f957664 | 6524 | return RPC_INVALID_PARAMETER; |
989b1de1 | 6525 | } |
f365e363 | 6526 | |
56fe75cb | 6527 | // make sure we have no negative totals. we do not move this outside the loop, so we can check against overflow on every iteration |
6528 | if (totalReserveOutput.HasNegative()) | |
989b1de1 | 6529 | { |
f365e363 | 6530 | strFailReason = _("Transaction amounts must not be negative"); |
0f957664 | 6531 | return RPC_INVALID_PARAMETER; |
989b1de1 | 6532 | } |
989b1de1 MT |
6533 | } |
6534 | ||
2f416b17 | 6535 | if (newCurrency.IsValid()) |
6536 | { | |
6537 | totalReserveOutput.valueMap.erase(newCurrency.GetID()); | |
6538 | } | |
6539 | ||
f023c2d4 | 6540 | //printf("totalReserveOutput: %s\n", totalReserveOutput.ToUniValue().write(1,2).c_str()); |
6541 | ||
989b1de1 | 6542 | int nextBlockHeight = chainActive.Height() + 1; |
401e75b1 | 6543 | |
989b1de1 | 6544 | CMutableTransaction txNew = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); |
401e75b1 | 6545 | |
6546 | std::vector<CTxIn> extraInputs = wtxNew.vin; | |
6547 | ||
6548 | wtxNew.fTimeReceivedIsTxTime = true; | |
6549 | wtxNew.BindWallet(this); | |
989b1de1 MT |
6550 | txNew.nLockTime = (uint32_t)chainActive.LastTip()->nTime + 1; // set to a time close to now |
6551 | ||
9bb37bf0 | 6552 | // Activates after Overwinter network upgrade |
2c6c5526 | 6553 | if (Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_OVERWINTER)) { |
fa70084c | 6554 | if (txNew.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){ |
14556680 | 6555 | strFailReason = "nExpiryHeight must be less than" + std::to_string((uint32_t)TX_EXPIRY_HEIGHT_THRESHOLD); |
0f957664 | 6556 | return RPC_INVALID_PARAMETER; |
9bb37bf0 | 6557 | } |
989b1de1 MT |
6558 | } |
6559 | ||
6560 | unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING; | |
2c6c5526 | 6561 | if (!Params().GetConsensus().NetworkUpgradeActive(nextBlockHeight, Consensus::UPGRADE_SAPLING)) { |
15ec5525 JG |
6562 | max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING; |
6563 | } | |
989b1de1 MT |
6564 | |
6565 | // Discourage fee sniping. | |
6566 | // | |
6567 | // However because of a off-by-one-error in previous versions we need to | |
6568 | // neuter it by setting nLockTime to at least one less than nBestHeight. | |
6569 | // Secondly currently propagation of transactions created for block heights | |
6570 | // corresponding to blocks that were just mined may be iffy - transactions | |
6571 | // aren't re-accepted into the mempool - we additionally neuter the code by | |
6572 | // going ten blocks back. Doesn't yet do anything for sniping, but does act | |
6573 | // to shake out wallet bugs like not showing nLockTime'd transactions at | |
6574 | // all. | |
6575 | txNew.nLockTime = std::max(0, chainActive.Height() - 10); | |
6576 | ||
6577 | // Secondly occasionally randomly pick a nLockTime even further back, so | |
6578 | // that transactions that are delayed after signing for whatever reason, | |
6579 | // e.g. high-latency mix networks and some CoinJoin implementations, have | |
6580 | // better privacy. | |
6581 | if (GetRandInt(10) == 0) | |
6582 | txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100)); | |
6583 | ||
6584 | assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); | |
6585 | assert(txNew.nLockTime < LOCKTIME_THRESHOLD); | |
6586 | ||
56fe75cb | 6587 | CCurrencyValueMap exchangeRates; |
989b1de1 MT |
6588 | { |
6589 | LOCK2(cs_main, cs_wallet); | |
56fe75cb | 6590 | |
1fb6db72 | 6591 | auto currencyState = ConnectedChains.GetCurrencyState(nextBlockHeight - 1); |
6592 | for (int i = 0; i < currencyState.currencies.size(); i++) | |
6593 | { | |
6594 | exchangeRates.valueMap[currencyState.currencies[i]] = currencyState.PriceInReserve(i); | |
6595 | } | |
56fe75cb | 6596 | |
1fb6db72 | 6597 | nFeeRet = 5000; |
6598 | while (true) | |
6599 | { | |
6600 | //interest = 0; | |
6601 | txNew.vin.clear(); | |
6602 | txNew.vout.clear(); | |
6603 | wtxNew.fFromMe = true; | |
6604 | nChangePosRet = -1; | |
6605 | nChangeOutputs = 0; | |
6606 | bool fFirst = true; | |
6607 | ||
6608 | // dust threshold of reserve may be different than native coin, if so, convert | |
6609 | CAmount dustThreshold; | |
6610 | ||
6611 | CAmount nTotalNativeValue = totalNativeOutput; | |
6612 | CCurrencyValueMap totalReserveValue = totalReserveOutput; | |
6613 | ||
6614 | if (nSubtractFeeFromAmount == 0) | |
6615 | nTotalNativeValue += nFeeRet; | |
6616 | ||
6617 | double dPriority = 0; | |
6618 | // vouts to the payees | |
6619 | BOOST_FOREACH (const CRecipient& recipient, vecSend) | |
6620 | { | |
6621 | // native output value for a reserve output is generally 0. fees are paid by converting from | |
6622 | // reserve token and the difference between input and output in reserve is the fee | |
6623 | // the actual reserve token output value is in the scriptPubKey | |
6624 | CTxOut txout(recipient.nAmount, recipient.scriptPubKey); | |
6625 | CAmount nativeEquivalent = txout.nValue; | |
6626 | ||
6627 | // here, if we know that it isn't an opret, it will have an output that expects input | |
6628 | if (!recipient.scriptPubKey.IsOpReturn()) | |
989b1de1 | 6629 | { |
1fb6db72 | 6630 | COptCCParams p; |
6631 | CCurrencyValueMap reserveOutput = recipient.scriptPubKey.ReserveOutValue(p); | |
6632 | CCurrencyValueMap relevantReserves; | |
989b1de1 | 6633 | |
1fb6db72 | 6634 | if (recipient.fSubtractFeeFromAmount) |
989b1de1 | 6635 | { |
1fb6db72 | 6636 | CAmount subFee = nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient |
989b1de1 | 6637 | |
1fb6db72 | 6638 | if (fFirst) // first receiver pays the remainder not divisible by output count |
989b1de1 | 6639 | { |
1fb6db72 | 6640 | fFirst = false; |
6641 | subFee += nFeeRet % nSubtractFeeFromAmount; | |
6642 | } | |
989b1de1 | 6643 | |
1fb6db72 | 6644 | if (subFee <= txout.nValue) |
6645 | { | |
6646 | txout.nValue -= subFee; | |
6647 | } | |
6648 | else if ((relevantReserves = reserveOutput.IntersectingValues(reserveCurrencies)).valueMap.size()) | |
6649 | { | |
6650 | // if we will subtract from more than one currency, divide fee among them equally | |
6651 | CAmount subSubFee = subFee / relevantReserves.valueMap.size(); | |
6652 | CAmount extraFee = subFee % relevantReserves.valueMap.size(); | |
6653 | // TODO: this does not support multi-currency, since the outputs do not. it will need | |
6654 | // to be checked when we enable multi-currency outputs | |
6655 | for (auto &oneCur : relevantReserves.valueMap) | |
56fe75cb | 6656 | { |
1fb6db72 | 6657 | auto it = exchangeRates.valueMap.find(oneCur.first); |
6658 | assert(it != exchangeRates.valueMap.end()); | |
6659 | reserveOutput.valueMap[it->first] -= currencyState.NativeToReserveRaw(subFee + (extraFee ? 1 : 0), it->second); | |
6660 | if (extraFee) | |
989b1de1 | 6661 | { |
1fb6db72 | 6662 | extraFee--; |
989b1de1 | 6663 | } |
989b1de1 | 6664 | } |
1fb6db72 | 6665 | txout.scriptPubKey.SetReserveOutValue(reserveOutput); |
6666 | } | |
6667 | else | |
6668 | { | |
6669 | // asking to pay a fee on an output, but not being able to is not accepted, should | |
6670 | // never get here, as it should have been checked above | |
6671 | strFailReason = "Cannot subtract fee from amount on non-native, non-reserve currency outputs"; | |
6672 | return RPC_INVALID_PARAMETER; | |
989b1de1 | 6673 | } |
1fb6db72 | 6674 | } |
989b1de1 | 6675 | |
1fb6db72 | 6676 | dustThreshold = txout.GetDustThreshold(::minRelayTxFee); |
989b1de1 | 6677 | |
1fb6db72 | 6678 | // only non-crypto condition, and normal reserve outputs are subject to dust limitations |
6679 | if (!p.IsValid() || | |
6680 | p.evalCode == EVAL_RESERVE_OUTPUT || | |
6681 | p.evalCode == EVAL_RESERVE_DEPOSIT || | |
6682 | p.evalCode == EVAL_RESERVE_EXCHANGE || | |
6683 | p.evalCode == EVAL_NONE) | |
6684 | { | |
6685 | // add all values to a native equivalent | |
6686 | // reserve currencies have a native value as well | |
6687 | if (exchangeRates.IntersectingValues(reserveOutput).valueMap.size()) | |
989b1de1 | 6688 | { |
1fb6db72 | 6689 | nativeEquivalent += currencyState.ReserveToNativeRaw(relevantReserves, exchangeRates.AsCurrencyVector(currencyState.currencies)); |
6690 | } | |
6691 | else | |
6692 | { | |
6693 | nativeEquivalent += reserveOutput.valueMap.size() ? reserveOutput.valueMap.begin()->second : 0; | |
6694 | } | |
6695 | ||
6696 | if (nativeEquivalent < dustThreshold) | |
6697 | { | |
6698 | if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) | |
989b1de1 | 6699 | { |
1fb6db72 | 6700 | if (nativeEquivalent < 0) |
6701 | strFailReason = _("The transaction amount is too small to pay the fee"); | |
989b1de1 | 6702 | else |
1fb6db72 | 6703 | strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); |
989b1de1 | 6704 | } |
1fb6db72 | 6705 | else |
6706 | strFailReason = _("Transaction amount too small"); | |
6707 | return RPC_INVALID_PARAMETER; | |
989b1de1 MT |
6708 | } |
6709 | } | |
989b1de1 | 6710 | } |
1fb6db72 | 6711 | txNew.vout.push_back(txout); |
6712 | } | |
989b1de1 | 6713 | |
1fb6db72 | 6714 | // Choose coins to use |
6715 | set<pair<const CWalletTx*,unsigned int> > setCoins; | |
6716 | CCurrencyValueMap totalValueIn; | |
6717 | CAmount totalNativeValueIn = 0; | |
6718 | bool fOnlyCoinbaseCoins = false; | |
6719 | bool fNeedCoinbaseCoins = false; | |
6720 | ||
6721 | if (!SelectReserveCoins(totalReserveValue, | |
6722 | nTotalNativeValue, | |
6723 | setCoins, | |
6724 | totalValueIn, | |
6725 | totalNativeValueIn, | |
6726 | fOnlyCoinbaseCoins, | |
6727 | fNeedCoinbaseCoins, | |
6728 | coinControl, | |
6729 | pOnlyFromDest)) | |
6730 | { | |
6731 | strFailReason = _("Insufficient funds"); | |
6732 | return RPC_WALLET_INSUFFICIENT_FUNDS; | |
6733 | } | |
56fe75cb | 6734 | |
1fb6db72 | 6735 | /* |
6736 | if (totalValueIn.valueMap.count(ASSETCHAINS_CHAINID)) | |
6737 | { | |
6738 | for (auto oneOut : setCoins) | |
7beb1c0b | 6739 | { |
1fb6db72 | 6740 | UniValue oneTxObj(UniValue::VOBJ); |
6741 | TxToUniv(*oneOut.first, uint256(), oneTxObj); | |
6742 | printf("TRANSACTION\n%s\n", oneTxObj.write(1,2).c_str()); | |
7beb1c0b | 6743 | } |
1fb6db72 | 6744 | printf("totalValueIn: %s\ntotalReserveValue: %s\n", totalValueIn.ToUniValue().write().c_str(), totalReserveValue.ToUniValue().write().c_str()); |
6745 | } | |
6746 | */ | |
56a7b665 | 6747 | |
1fb6db72 | 6748 | std::vector<std::pair<std::pair<const CWalletTx*, unsigned int>, CAmount>> coinsWithEquivalentNative; |
6749 | if (reserveCurrencies.valueMap.size()) | |
6750 | { | |
6751 | BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) | |
989b1de1 | 6752 | { |
1fb6db72 | 6753 | CAmount nCredit = pcoin.first->vout[pcoin.second].nValue + |
6754 | currencyState.ReserveToNativeRaw(reserveCurrencies.IntersectingValues(pcoin.first->vout[pcoin.second].ReserveOutValue()), | |
6755 | exchangeRates.AsCurrencyVector(currencyState.currencies)); | |
6756 | coinsWithEquivalentNative.push_back(make_pair(pcoin, nCredit)); | |
989b1de1 | 6757 | } |
1fb6db72 | 6758 | } |
989b1de1 | 6759 | |
1fb6db72 | 6760 | CCurrencyValueMap reserveChange = totalValueIn - totalReserveValue; |
f023c2d4 | 6761 | |
1fb6db72 | 6762 | //printf("reservechange: %s\ntotalvaluein: %s\n", reserveChange.ToUniValue().write(1,2).c_str(), totalValueIn.ToUniValue().write(1,2).c_str()); |
f023c2d4 | 6763 | |
1fb6db72 | 6764 | CCurrencyValueMap convertibleChange = reserveCurrencies.IntersectingValues(reserveChange); |
6765 | CAmount nChange = totalNativeValueIn - nTotalNativeValue; | |
6766 | CAmount nConvertedReserveChange = 0; | |
989b1de1 | 6767 | |
1fb6db72 | 6768 | // /printf("tokenChange: %s\nnativeChange: %s\n", reserveChange.ToUniValue().write().c_str(), ValueFromAmount(nChange).write().c_str()); |
56a7b665 | 6769 | |
1fb6db72 | 6770 | // if we will try to take the fee from change |
6771 | if (nSubtractFeeFromAmount == 0) | |
6772 | { | |
6773 | if (nChange < nFeeRet && convertibleChange.valueMap.size()) | |
56fe75cb | 6774 | { |
1fb6db72 | 6775 | nConvertedReserveChange = currencyState.ReserveToNativeRaw(convertibleChange, |
6776 | exchangeRates.AsCurrencyVector(currencyState.currencies)); | |
56fe75cb | 6777 | } |
1fb6db72 | 6778 | nChange -= nFeeRet; |
6779 | } | |
989b1de1 | 6780 | |
1fb6db72 | 6781 | if ((nChange + nConvertedReserveChange > 0) || (reserveChange > CCurrencyValueMap())) |
6782 | { | |
6783 | // if we can and need to convert reserves, adjust reserve change output amounts as needed | |
6784 | if (nChange < 0 && convertibleChange.valueMap.size()) | |
989b1de1 | 6785 | { |
1fb6db72 | 6786 | // no native change output, so, convert what's needed and make reserve output(s) |
6787 | // attempt to convert equally across currencies, and if we fail, take the rest as | |
6788 | // first come, first served | |
6789 | CAmount nChangePerCur = -nChange / convertibleChange.valueMap.size(); | |
6790 | CAmount nChangeMod = -nChange % convertibleChange.valueMap.size(); | |
6791 | CAmount remainder = 0; | |
6792 | nChange = 0; | |
6793 | for (int i = 0; i < currencyState.currencies.size(); i++) | |
6794 | { | |
6795 | auto it = reserveChange.valueMap.find(currencyState.currencies[i]); | |
6796 | if (it != reserveChange.valueMap.end()) | |
6797 | { | |
6798 | CAmount feeAsReserve = currencyState.NativeToReserve(nChangePerCur + (nChangeMod ? 1 : 0), i); | |
6799 | if (it->second - feeAsReserve <= 0) | |
6800 | { | |
6801 | // if we don't have enough reserve in this currency for its part of the fee, we will | |
6802 | // take it all, and then take more from the other currencies as needed | |
6803 | remainder += currencyState.ReserveToNative(feeAsReserve - it->second, i); | |
6804 | reserveChange.valueMap.erase(it); | |
6805 | } | |
6806 | else | |
6807 | { | |
6808 | it->second -= feeAsReserve; | |
6809 | } | |
6810 | } | |
6811 | } | |
6812 | // if we have any fee left to convert because we were short on some currency, take it | |
6813 | // from the remaining reserves in change | |
6814 | if (remainder) | |
56fe75cb | 6815 | { |
56fe75cb | 6816 | for (int i = 0; i < currencyState.currencies.size(); i++) |
6817 | { | |
6818 | auto it = reserveChange.valueMap.find(currencyState.currencies[i]); | |
6819 | if (it != reserveChange.valueMap.end()) | |
6820 | { | |
1fb6db72 | 6821 | CAmount feeAsReserve = currencyState.NativeToReserve(remainder, i); |
56fe75cb | 6822 | if (it->second - feeAsReserve <= 0) |
6823 | { | |
6824 | // if we don't have enough reserve in this currency for its part of the fee, we will | |
6825 | // take it all, and then take more from the other currencies as needed | |
56fe75cb | 6826 | reserveChange.valueMap.erase(it); |
1fb6db72 | 6827 | remainder -= currencyState.ReserveToNative(it->second, i); |
56fe75cb | 6828 | } |
6829 | else | |
6830 | { | |
6831 | it->second -= feeAsReserve; | |
1fb6db72 | 6832 | break; |
56fe75cb | 6833 | } |
6834 | } | |
6835 | } | |
6836 | } | |
1fb6db72 | 6837 | } |
989b1de1 | 6838 | |
1fb6db72 | 6839 | // coin control: send change to custom address |
989b1de1 | 6840 | |
1fb6db72 | 6841 | // reserve tokens can currently only be sent to public keys or addresses that are in the current wallet |
6842 | // since reserve token outputs are CCs by definition | |
6843 | CTxDestination changeDest; | |
6844 | if (coinControl && coinControl->destChange.which() != COptCCParams::ADDRTYPE_INVALID) | |
6845 | { | |
6846 | changeDest = coinControl->destChange; | |
6847 | } | |
6848 | else | |
6849 | { | |
6850 | // no coin control: send change to newly generated address | |
6851 | ||
6852 | // Note: We use a new key here to keep it from being obvious which side is the change. | |
6853 | // The drawback is that by not reusing a previous key, the change may be lost if a | |
6854 | // backup is restored, if the backup doesn't have the new private key for the change. | |
6855 | // If we reused the old key, it would be possible to add code to look for and | |
6856 | // rediscover unknown transactions that were written with keys of ours to recover | |
6857 | // post-backup change. | |
6858 | ||
6859 | // Reserve a new key pair from key pool | |
6860 | extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY; | |
6861 | CPubKey pubKey; | |
44ac8ad4 | 6862 | if ( USE_EXTERNAL_PUBKEY != 0 ) |
989b1de1 | 6863 | { |
44ac8ad4 | 6864 | //fprintf(stderr,"use notary pubkey\n"); |
6865 | pubKey = CPubKey(ParseHex(NOTARY_PUBKEY)); | |
0fa1bca8 | 6866 | changeDest = CTxDestination(pubKey); |
6867 | } | |
6868 | else if (pOnlyFromDest && pOnlyFromDest->which() == COptCCParams::ADDRTYPE_ID) | |
6869 | { | |
6870 | changeDest = *pOnlyFromDest; | |
989b1de1 MT |
6871 | } |
6872 | else | |
6873 | { | |
44ac8ad4 | 6874 | bool ret; |
6875 | ret = reservekey.GetReservedKey(pubKey); | |
6876 | assert(ret); // should never fail, as we just unlocked | |
0fa1bca8 | 6877 | changeDest = CTxDestination(pubKey); |
989b1de1 | 6878 | } |
1fb6db72 | 6879 | } |
989b1de1 | 6880 | |
1fb6db72 | 6881 | // generate all necessary change outputs for all currencies |
6882 | // first determine if any outputs left are dust. if so, just add them to the fee | |
6883 | if (nChange < dustThreshold) | |
6884 | { | |
6885 | nFeeRet += nChange; | |
6886 | nChange = 0; | |
6887 | } | |
6888 | else | |
6889 | { | |
6890 | nChangePosRet = txNew.vout.size() - 1; // dont change first or last | |
6891 | nChangeOutputs++; | |
6892 | vector<CTxOut>::iterator position = txNew.vout.begin() + nChangePosRet; | |
6893 | txNew.vout.insert(position, CTxOut(nChange, GetScriptForDestination(changeDest))); | |
6894 | } | |
6895 | ||
6896 | // now, loop through the remaining reserve currencies and make a change output for each | |
6897 | // if dust, just remove | |
6898 | auto reserveIndexMap = currencyState.GetReserveMap(); | |
6899 | for (auto &curChangeOut : reserveChange.valueMap) | |
6900 | { | |
6901 | CAmount outVal; | |
6902 | assert(curChangeOut.first != ASSETCHAINS_CHAINID); | |
6903 | auto curIt = reserveIndexMap.find(curChangeOut.first); | |
6904 | if (curIt != reserveIndexMap.end()) | |
989b1de1 | 6905 | { |
1fb6db72 | 6906 | outVal = currencyState.ReserveToNative(curChangeOut.second, curIt->second); |
56fe75cb | 6907 | } |
6908 | else | |
6909 | { | |
1fb6db72 | 6910 | outVal = curChangeOut.second; |
56fe75cb | 6911 | } |
1fb6db72 | 6912 | |
6913 | if (outVal >= dustThreshold) | |
56fe75cb | 6914 | { |
1fb6db72 | 6915 | vector<CTxOut>::iterator position = txNew.vout.begin() + (nChangePosRet + nChangeOutputs++); |
6916 | CTokenOutput to = CTokenOutput(curChangeOut.first, curChangeOut.second); | |
6917 | txNew.vout.insert(position, CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, {changeDest}, 1, &to)))); | |
989b1de1 | 6918 | } |
1fb6db72 | 6919 | // if it is dust and we cannot convert to native, drop it. it may be taken by the miner |
6920 | else if (curIt != reserveIndexMap.end()) | |
989b1de1 | 6921 | { |
1fb6db72 | 6922 | nFeeRet += outVal; |
989b1de1 | 6923 | } |
1fb6db72 | 6924 | } |
989b1de1 | 6925 | |
1fb6db72 | 6926 | // if we made no change outputs, return the key |
6927 | if (!nChangeOutputs) | |
401e75b1 | 6928 | { |
1fb6db72 | 6929 | reservekey.ReturnKey(); |
401e75b1 | 6930 | } |
1fb6db72 | 6931 | } else reservekey.ReturnKey(); |
e8ef3da7 | 6932 | |
1fb6db72 | 6933 | // Fill vin |
6934 | // | |
6935 | // Note how the sequence number is set to max()-1 so that the | |
6936 | // nLockTime set above actually works. | |
6937 | for (auto &oneIn : extraInputs) | |
6938 | { | |
6939 | auto wit = mapWallet.find(oneIn.prevout.hash); | |
6940 | if (wit != mapWallet.end() && | |
6941 | wit->second.vout.size() > oneIn.prevout.n && | |
6942 | !wit->second.vout[oneIn.prevout.n].nValue && | |
6943 | wit->second.vout[oneIn.prevout.n].ReserveOutValue() == CCurrencyValueMap()) | |
31afbcc5 | 6944 | { |
1fb6db72 | 6945 | setCoins.insert(std::make_pair(&(wit->second), oneIn.prevout.n)); |
31afbcc5 | 6946 | } |
1fb6db72 | 6947 | else |
aa30f655 | 6948 | { |
1fb6db72 | 6949 | setCoins.clear(); |
6950 | break; | |
aa30f655 | 6951 | } |
1fb6db72 | 6952 | } |
6953 | BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) | |
6954 | txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(), | |
6955 | std::numeric_limits<unsigned int>::max()-1)); | |
aa30f655 | 6956 | |
1fb6db72 | 6957 | // Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects |
6958 | size_t limit = (size_t)GetArg("-mempooltxinputlimit", 0); | |
6959 | { | |
6960 | LOCK(cs_main); | |
6961 | if (Params().GetConsensus().NetworkUpgradeActive(chainActive.Height() + 1, Consensus::UPGRADE_OVERWINTER)) { | |
6962 | limit = 0; | |
6963 | } | |
6964 | } | |
6965 | if (limit > 0) { | |
6966 | size_t n = txNew.vin.size(); | |
6967 | if (n > limit) { | |
6968 | strFailReason = _(strprintf("Too many transparent inputs %zu > limit %zu", n, limit).c_str()); | |
6969 | return RPC_INVALID_PARAMETER; | |
aa30f655 | 6970 | } |
1fb6db72 | 6971 | } |
e8ef3da7 | 6972 | |
1fb6db72 | 6973 | // Grab the current consensus branch ID |
6974 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); | |
4949004d | 6975 | |
1fb6db72 | 6976 | // Sign |
6977 | int nIn = 0; | |
6978 | CTransaction txNewConst(txNew); | |
6979 | BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) | |
6980 | { | |
6981 | bool signSuccess; | |
6982 | const CScript& scriptPubKey = coin.first->vout[coin.second].scriptPubKey; | |
6983 | SignatureData sigdata; | |
6984 | if (sign) | |
6985 | signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->vout[coin.second].nValue, scriptPubKey), scriptPubKey, sigdata, consensusBranchId); | |
6986 | else | |
6987 | signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata, consensusBranchId); | |
6988 | ||
6989 | if (!signSuccess) | |
1f00f4e9 | 6990 | { |
1fb6db72 | 6991 | strFailReason = _("Signing transaction failed"); |
0f957664 | 6992 | return RPC_TRANSACTION_ERROR; |
1fb6db72 | 6993 | } else { |
6994 | UpdateTransaction(txNew, nIn, sigdata); | |
1f00f4e9 | 6995 | } |
aa30f655 | 6996 | |
1fb6db72 | 6997 | nIn++; |
6998 | } | |
e8ef3da7 | 6999 | |
1fb6db72 | 7000 | unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); |
aa279d61 | 7001 | |
1fb6db72 | 7002 | // Remove scriptSigs if we used dummy signatures for fee calculation |
7003 | if (!sign) { | |
7004 | BOOST_FOREACH (CTxIn& vin, txNew.vin) | |
7005 | vin.scriptSig = CScript(); | |
7006 | } | |
b33d1f5e | 7007 | |
1fb6db72 | 7008 | // Embed the constructed transaction data in wtxNew. |
7009 | *static_cast<CTransaction*>(&wtxNew) = CTransaction(txNew); | |
b33d1f5e | 7010 | |
1fb6db72 | 7011 | // Limit size |
7012 | if (nBytes >= max_tx_size) | |
7013 | { | |
7014 | strFailReason = _("Transaction too large"); | |
7015 | return RPC_TRANSACTION_ERROR; | |
7016 | } | |
e8ef3da7 | 7017 | |
1fb6db72 | 7018 | dPriority = wtxNew.ComputePriority(dPriority, nBytes); |
b33d1f5e | 7019 | |
1fb6db72 | 7020 | // Can we complete this as a free transaction? |
7021 | if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) | |
7022 | { | |
7023 | // Not enough fee: enough priority? | |
7024 | double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); | |
7025 | // Not enough mempool history to estimate: use hard-coded AllowFree. | |
7026 | if (dPriorityNeeded <= 0 && AllowFree(dPriority)) | |
7027 | break; | |
7028 | ||
7029 | // Small enough, and priority high enough, to send for free | |
7030 | if (dPriorityNeeded > 0 && dPriority >= dPriorityNeeded) | |
7031 | break; | |
7032 | } | |
7033 | ||
7034 | CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool); | |
7035 | if ( nFeeNeeded < 5000 ) | |
7036 | nFeeNeeded = 5000; | |
7037 | ||
7038 | // If we made it here and we aren't even able to meet the relay fee on the next pass, give up | |
7039 | // because we must be at the maximum allowed fee. | |
7040 | if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) | |
7041 | { | |
7042 | strFailReason = _("Transaction too large for fee policy"); | |
7043 | return RPC_TRANSACTION_ERROR; | |
e8ef3da7 | 7044 | } |
1fb6db72 | 7045 | |
7046 | if (nFeeRet >= nFeeNeeded) | |
7047 | break; // Done, enough fee included. | |
7048 | ||
7049 | // Include more fee and try again. | |
7050 | nFeeRet = nFeeNeeded; | |
7051 | continue; | |
e8ef3da7 WL |
7052 | } |
7053 | } | |
79e34907 | 7054 | return RPC_OK; |
e8ef3da7 WL |
7055 | } |
7056 | ||
5b40d886 MF |
7057 | /** |
7058 | * Call after CreateTransaction unless you want to abort | |
7059 | */ | |
3220d993 | 7060 | bool CWallet::CommitTransaction(CWalletTx& wtxNew, boost::optional<CReserveKey&> reservekey) |
e8ef3da7 | 7061 | { |
e8ef3da7 | 7062 | { |
f8dcd5ca | 7063 | LOCK2(cs_main, cs_wallet); |
7d9d134b | 7064 | LogPrintf("CommitTransaction:\n%s", wtxNew.ToString()); |
e8ef3da7 WL |
7065 | { |
7066 | // This is only to keep the database open to defeat the auto-flush for the | |
7067 | // duration of this scope. This is the only place where this optimization | |
7068 | // maybe makes sense; please don't do it anywhere else. | |
44bc988e | 7069 | CWalletDB* pwalletdb = fFileBacked ? new CWalletDB(strWalletFile,"r+") : NULL; |
e8ef3da7 | 7070 | |
3220d993 E |
7071 | if (reservekey) { |
7072 | // Take key pair from key pool so it won't be used again | |
7073 | reservekey.get().KeepKey(); | |
7074 | } | |
e8ef3da7 WL |
7075 | |
7076 | // Add tx to wallet, because if it has change it's also ours, | |
7077 | // otherwise just for transaction history. | |
44bc988e | 7078 | AddToWallet(wtxNew, false, pwalletdb); |
e8ef3da7 | 7079 | |
93a18a36 | 7080 | // Notify that old coins are spent |
e8ef3da7 WL |
7081 | set<CWalletTx*> setCoins; |
7082 | BOOST_FOREACH(const CTxIn& txin, wtxNew.vin) | |
7083 | { | |
7084 | CWalletTx &coin = mapWallet[txin.prevout.hash]; | |
4c6e2295 | 7085 | coin.BindWallet(this); |
805344dc | 7086 | NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); |
e8ef3da7 WL |
7087 | } |
7088 | ||
7089 | if (fFileBacked) | |
7090 | delete pwalletdb; | |
7091 | } | |
7092 | ||
7093 | // Track how many getdata requests our transaction gets | |
805344dc | 7094 | mapRequestCount[wtxNew.GetHash()] = 0; |
e8ef3da7 | 7095 | |
6f252627 | 7096 | if (fBroadcastTransactions) |
e8ef3da7 | 7097 | { |
6f252627 WL |
7098 | // Broadcast |
7099 | if (!wtxNew.AcceptToMemoryPool(false)) | |
7100 | { | |
68c266b2 | 7101 | fprintf(stderr,"commit failed\n"); |
83a426bc | 7102 | // This must not fail. The transaction has already been signed and recorded. |
7ff9d122 | 7103 | LogPrintf("CommitTransaction(): Error: Transaction not valid\n"); |
6f252627 WL |
7104 | return false; |
7105 | } | |
7106 | wtxNew.RelayWalletTransaction(); | |
e8ef3da7 | 7107 | } |
e8ef3da7 | 7108 | } |
e8ef3da7 WL |
7109 | return true; |
7110 | } | |
7111 | ||
a372168e | 7112 | CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool) |
b33d1f5e GA |
7113 | { |
7114 | // payTxFee is user-set "I want to pay this much" | |
a372168e | 7115 | CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); |
c1c9d5b4 CL |
7116 | // user selected total at least (default=true) |
7117 | if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK()) | |
7118 | nFeeNeeded = payTxFee.GetFeePerK(); | |
b33d1f5e GA |
7119 | // User didn't set: use -txconfirmtarget to estimate... |
7120 | if (nFeeNeeded == 0) | |
7121 | nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); | |
7122 | // ... unless we don't have enough mempool data, in which case fall | |
7123 | // back to a hard-coded fee | |
7124 | if (nFeeNeeded == 0) | |
13fc83c7 | 7125 | nFeeNeeded = minTxFee.GetFee(nTxBytes); |
aa279d61 GM |
7126 | // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee |
7127 | if (nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes)) | |
7128 | nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes); | |
7129 | // But always obey the maximum | |
7130 | if (nFeeNeeded > maxTxFee) | |
7131 | nFeeNeeded = maxTxFee; | |
b33d1f5e GA |
7132 | return nFeeNeeded; |
7133 | } | |
7134 | ||
e8ef3da7 | 7135 | |
cf53fd7c | 7136 | void komodo_prefetch(FILE *fp); |
e8ef3da7 | 7137 | |
eed1785f | 7138 | DBErrors CWallet::LoadWallet(bool& fFirstRunRet) |
e8ef3da7 WL |
7139 | { |
7140 | if (!fFileBacked) | |
4f76be1d | 7141 | return DB_LOAD_OK; |
e8ef3da7 | 7142 | fFirstRunRet = false; |
e86c03cf | 7143 | if ( 0 ) // doesnt help |
cf53fd7c | 7144 | { |
e86c03cf | 7145 | fprintf(stderr,"loading wallet %s %u\n",strWalletFile.c_str(),(uint32_t)time(NULL)); |
cf53fd7c | 7146 | FILE *fp; |
7147 | if ( (fp= fopen(strWalletFile.c_str(),"rb")) != 0 ) | |
7148 | { | |
7149 | komodo_prefetch(fp); | |
7150 | fclose(fp); | |
7151 | } | |
7152 | } | |
e86c03cf | 7153 | //fprintf(stderr,"prefetched wallet %s %u\n",strWalletFile.c_str(),(uint32_t)time(NULL)); |
eed1785f | 7154 | DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this); |
e86c03cf | 7155 | //fprintf(stderr,"loaded wallet %s %u\n",strWalletFile.c_str(),(uint32_t)time(NULL)); |
d764d916 | 7156 | if (nLoadWalletRet == DB_NEED_REWRITE) |
9e9869d0 | 7157 | { |
d764d916 GA |
7158 | if (CDB::Rewrite(strWalletFile, "\x04pool")) |
7159 | { | |
012ca1c9 | 7160 | LOCK(cs_wallet); |
d764d916 GA |
7161 | setKeyPool.clear(); |
7162 | // Note: can't top-up keypool here, because wallet is locked. | |
7163 | // User will be prompted to unlock wallet the next operation | |
c6de7c35 | 7164 | // that requires a new key. |
d764d916 | 7165 | } |
9e9869d0 PW |
7166 | } |
7167 | ||
7ec55267 MC |
7168 | if (nLoadWalletRet != DB_LOAD_OK) |
7169 | return nLoadWalletRet; | |
fd61d6f5 | 7170 | fFirstRunRet = !vchDefaultKey.IsValid(); |
e8ef3da7 | 7171 | |
39278369 CL |
7172 | uiInterface.LoadWallet(this); |
7173 | ||
116df55e | 7174 | return DB_LOAD_OK; |
e8ef3da7 WL |
7175 | } |
7176 | ||
ae3d0aba | 7177 | |
77cbd462 | 7178 | DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) |
518f3bda JG |
7179 | { |
7180 | if (!fFileBacked) | |
7181 | return DB_LOAD_OK; | |
77cbd462 | 7182 | DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx); |
518f3bda JG |
7183 | if (nZapWalletTxRet == DB_NEED_REWRITE) |
7184 | { | |
7185 | if (CDB::Rewrite(strWalletFile, "\x04pool")) | |
7186 | { | |
7187 | LOCK(cs_wallet); | |
7188 | setKeyPool.clear(); | |
7189 | // Note: can't top-up keypool here, because wallet is locked. | |
7190 | // User will be prompted to unlock wallet the next operation | |
5b40d886 | 7191 | // that requires a new key. |
518f3bda JG |
7192 | } |
7193 | } | |
7194 | ||
7195 | if (nZapWalletTxRet != DB_LOAD_OK) | |
7196 | return nZapWalletTxRet; | |
7197 | ||
7198 | return DB_LOAD_OK; | |
7199 | } | |
7200 | ||
7201 | ||
a41d5fe0 | 7202 | bool CWallet::SetAddressBook(const CTxDestination& address, const string& strName, const string& strPurpose) |
ae3d0aba | 7203 | { |
ca4cf5cf GA |
7204 | bool fUpdated = false; |
7205 | { | |
7206 | LOCK(cs_wallet); // mapAddressBook | |
7207 | std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address); | |
7208 | fUpdated = mi != mapAddressBook.end(); | |
7209 | mapAddressBook[address].name = strName; | |
7210 | if (!strPurpose.empty()) /* update purpose only if requested */ | |
7211 | mapAddressBook[address].purpose = strPurpose; | |
7212 | } | |
8d657a65 | 7213 | NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, |
ca4cf5cf | 7214 | strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); |
ae3d0aba WL |
7215 | if (!fFileBacked) |
7216 | return false; | |
07444da1 | 7217 | if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(EncodeDestination(address), strPurpose)) |
a41d5fe0 | 7218 | return false; |
07444da1 | 7219 | return CWalletDB(strWalletFile).WriteName(EncodeDestination(address), strName); |
ae3d0aba WL |
7220 | } |
7221 | ||
a41d5fe0 | 7222 | bool CWallet::DelAddressBook(const CTxDestination& address) |
ae3d0aba | 7223 | { |
b10e1470 | 7224 | { |
ca4cf5cf GA |
7225 | LOCK(cs_wallet); // mapAddressBook |
7226 | ||
7227 | if(fFileBacked) | |
b10e1470 | 7228 | { |
ca4cf5cf | 7229 | // Delete destdata tuples associated with address |
07444da1 | 7230 | std::string strAddress = EncodeDestination(address); |
ca4cf5cf GA |
7231 | BOOST_FOREACH(const PAIRTYPE(string, string) &item, mapAddressBook[address].destdata) |
7232 | { | |
7233 | CWalletDB(strWalletFile).EraseDestData(strAddress, item.first); | |
7234 | } | |
b10e1470 | 7235 | } |
ca4cf5cf | 7236 | mapAddressBook.erase(address); |
b10e1470 WL |
7237 | } |
7238 | ||
8d657a65 | 7239 | NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); |
ca4cf5cf | 7240 | |
ae3d0aba WL |
7241 | if (!fFileBacked) |
7242 | return false; | |
07444da1 PW |
7243 | CWalletDB(strWalletFile).ErasePurpose(EncodeDestination(address)); |
7244 | return CWalletDB(strWalletFile).EraseName(EncodeDestination(address)); | |
ae3d0aba WL |
7245 | } |
7246 | ||
fd61d6f5 | 7247 | bool CWallet::SetDefaultKey(const CPubKey &vchPubKey) |
ae3d0aba WL |
7248 | { |
7249 | if (fFileBacked) | |
7250 | { | |
7251 | if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey)) | |
7252 | return false; | |
7253 | } | |
7254 | vchDefaultKey = vchPubKey; | |
7255 | return true; | |
7256 | } | |
7257 | ||
5b40d886 MF |
7258 | /** |
7259 | * Mark old keypool keys as used, | |
efb7662d | 7260 | * and generate all new keys |
5b40d886 | 7261 | */ |
37971fcc GA |
7262 | bool CWallet::NewKeyPool() |
7263 | { | |
37971fcc | 7264 | { |
f8dcd5ca | 7265 | LOCK(cs_wallet); |
37971fcc | 7266 | CWalletDB walletdb(strWalletFile); |
51ed9ec9 | 7267 | BOOST_FOREACH(int64_t nIndex, setKeyPool) |
37971fcc GA |
7268 | walletdb.ErasePool(nIndex); |
7269 | setKeyPool.clear(); | |
7270 | ||
7271 | if (IsLocked()) | |
7272 | return false; | |
7273 | ||
51ed9ec9 | 7274 | int64_t nKeys = max(GetArg("-keypool", 100), (int64_t)0); |
37971fcc GA |
7275 | for (int i = 0; i < nKeys; i++) |
7276 | { | |
51ed9ec9 | 7277 | int64_t nIndex = i+1; |
37971fcc GA |
7278 | walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey())); |
7279 | setKeyPool.insert(nIndex); | |
7280 | } | |
f48742c2 | 7281 | LogPrintf("CWallet::NewKeyPool wrote %d new keys\n", nKeys); |
37971fcc GA |
7282 | } |
7283 | return true; | |
7284 | } | |
7285 | ||
13dd2d09 | 7286 | bool CWallet::TopUpKeyPool(unsigned int kpSize) |
e8ef3da7 | 7287 | { |
e8ef3da7 | 7288 | { |
f8dcd5ca PW |
7289 | LOCK(cs_wallet); |
7290 | ||
4e87d341 MC |
7291 | if (IsLocked()) |
7292 | return false; | |
7293 | ||
e8ef3da7 WL |
7294 | CWalletDB walletdb(strWalletFile); |
7295 | ||
7296 | // Top up key pool | |
13dd2d09 JG |
7297 | unsigned int nTargetSize; |
7298 | if (kpSize > 0) | |
7299 | nTargetSize = kpSize; | |
7300 | else | |
51ed9ec9 | 7301 | nTargetSize = max(GetArg("-keypool", 100), (int64_t) 0); |
13dd2d09 | 7302 | |
faf705a4 | 7303 | while (setKeyPool.size() < (nTargetSize + 1)) |
e8ef3da7 | 7304 | { |
51ed9ec9 | 7305 | int64_t nEnd = 1; |
e8ef3da7 WL |
7306 | if (!setKeyPool.empty()) |
7307 | nEnd = *(--setKeyPool.end()) + 1; | |
7308 | if (!walletdb.WritePool(nEnd, CKeyPool(GenerateNewKey()))) | |
5262fde0 | 7309 | throw runtime_error("TopUpKeyPool(): writing generated key failed"); |
e8ef3da7 | 7310 | setKeyPool.insert(nEnd); |
783b182c | 7311 | LogPrintf("keypool added key %d, size=%u\n", nEnd, setKeyPool.size()); |
e8ef3da7 | 7312 | } |
4e87d341 MC |
7313 | } |
7314 | return true; | |
7315 | } | |
7316 | ||
51ed9ec9 | 7317 | void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool) |
4e87d341 MC |
7318 | { |
7319 | nIndex = -1; | |
fd61d6f5 | 7320 | keypool.vchPubKey = CPubKey(); |
4e87d341 | 7321 | { |
f8dcd5ca PW |
7322 | LOCK(cs_wallet); |
7323 | ||
4e87d341 MC |
7324 | if (!IsLocked()) |
7325 | TopUpKeyPool(); | |
e8ef3da7 WL |
7326 | |
7327 | // Get the oldest key | |
4e87d341 MC |
7328 | if(setKeyPool.empty()) |
7329 | return; | |
7330 | ||
7331 | CWalletDB walletdb(strWalletFile); | |
7332 | ||
e8ef3da7 WL |
7333 | nIndex = *(setKeyPool.begin()); |
7334 | setKeyPool.erase(setKeyPool.begin()); | |
7335 | if (!walletdb.ReadPool(nIndex, keypool)) | |
5262fde0 | 7336 | throw runtime_error("ReserveKeyFromKeyPool(): read failed"); |
fd61d6f5 | 7337 | if (!HaveKey(keypool.vchPubKey.GetID())) |
5262fde0 | 7338 | throw runtime_error("ReserveKeyFromKeyPool(): unknown key in key pool"); |
fd61d6f5 | 7339 | assert(keypool.vchPubKey.IsValid()); |
36a65612 | 7340 | //LogPrintf("keypool reserve %d\n", nIndex); |
e8ef3da7 WL |
7341 | } |
7342 | } | |
7343 | ||
51ed9ec9 | 7344 | void CWallet::KeepKey(int64_t nIndex) |
e8ef3da7 WL |
7345 | { |
7346 | // Remove from key pool | |
7347 | if (fFileBacked) | |
7348 | { | |
7349 | CWalletDB walletdb(strWalletFile); | |
6cc4a62c | 7350 | walletdb.ErasePool(nIndex); |
e8ef3da7 | 7351 | } |
f48742c2 | 7352 | LogPrintf("keypool keep %d\n", nIndex); |
e8ef3da7 WL |
7353 | } |
7354 | ||
51ed9ec9 | 7355 | void CWallet::ReturnKey(int64_t nIndex) |
e8ef3da7 WL |
7356 | { |
7357 | // Return to key pool | |
f8dcd5ca PW |
7358 | { |
7359 | LOCK(cs_wallet); | |
e8ef3da7 | 7360 | setKeyPool.insert(nIndex); |
f8dcd5ca | 7361 | } |
36a65612 | 7362 | //LogPrintf("keypool return %d\n", nIndex); |
e8ef3da7 WL |
7363 | } |
7364 | ||
71ac5052 | 7365 | bool CWallet::GetKeyFromPool(CPubKey& result) |
e8ef3da7 | 7366 | { |
51ed9ec9 | 7367 | int64_t nIndex = 0; |
e8ef3da7 | 7368 | CKeyPool keypool; |
7db3b75b | 7369 | { |
f8dcd5ca | 7370 | LOCK(cs_wallet); |
ed02c95d GA |
7371 | ReserveKeyFromKeyPool(nIndex, keypool); |
7372 | if (nIndex == -1) | |
7db3b75b | 7373 | { |
ed02c95d GA |
7374 | if (IsLocked()) return false; |
7375 | result = GenerateNewKey(); | |
7db3b75b GA |
7376 | return true; |
7377 | } | |
ed02c95d GA |
7378 | KeepKey(nIndex); |
7379 | result = keypool.vchPubKey; | |
7db3b75b | 7380 | } |
7db3b75b | 7381 | return true; |
e8ef3da7 WL |
7382 | } |
7383 | ||
51ed9ec9 | 7384 | int64_t CWallet::GetOldestKeyPoolTime() |
e8ef3da7 | 7385 | { |
51ed9ec9 | 7386 | int64_t nIndex = 0; |
e8ef3da7 WL |
7387 | CKeyPool keypool; |
7388 | ReserveKeyFromKeyPool(nIndex, keypool); | |
4e87d341 MC |
7389 | if (nIndex == -1) |
7390 | return GetTime(); | |
e8ef3da7 WL |
7391 | ReturnKey(nIndex); |
7392 | return keypool.nTime; | |
7393 | } | |
7394 | ||
a372168e | 7395 | std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() |
22dfd735 | 7396 | { |
a372168e | 7397 | map<CTxDestination, CAmount> balances; |
22dfd735 | 7398 | |
7399 | { | |
7400 | LOCK(cs_wallet); | |
7401 | BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) | |
7402 | { | |
7403 | CWalletTx *pcoin = &walletEntry.second; | |
7404 | ||
75a4d512 | 7405 | if (!CheckFinalTx(*pcoin) || !pcoin->IsTrusted()) |
22dfd735 | 7406 | continue; |
7407 | ||
7408 | if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) | |
7409 | continue; | |
7410 | ||
7411 | int nDepth = pcoin->GetDepthInMainChain(); | |
a3e192a3 | 7412 | if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) |
22dfd735 | 7413 | continue; |
7414 | ||
b1093efa | 7415 | for (unsigned int i = 0; i < pcoin->vout.size(); i++) |
22dfd735 | 7416 | { |
b1093efa | 7417 | CTxDestination addr; |
22dfd735 | 7418 | if (!IsMine(pcoin->vout[i])) |
7419 | continue; | |
b1093efa GM |
7420 | if(!ExtractDestination(pcoin->vout[i].scriptPubKey, addr)) |
7421 | continue; | |
22dfd735 | 7422 | |
a372168e | 7423 | CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->vout[i].nValue; |
22dfd735 | 7424 | |
22dfd735 | 7425 | if (!balances.count(addr)) |
7426 | balances[addr] = 0; | |
7427 | balances[addr] += n; | |
7428 | } | |
7429 | } | |
7430 | } | |
7431 | ||
7432 | return balances; | |
7433 | } | |
7434 | ||
b1093efa | 7435 | set< set<CTxDestination> > CWallet::GetAddressGroupings() |
22dfd735 | 7436 | { |
95691680 | 7437 | AssertLockHeld(cs_wallet); // mapWallet |
b1093efa GM |
7438 | set< set<CTxDestination> > groupings; |
7439 | set<CTxDestination> grouping; | |
22dfd735 | 7440 | |
7441 | BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet) | |
7442 | { | |
7443 | CWalletTx *pcoin = &walletEntry.second; | |
7444 | ||
a3fad211 | 7445 | if (pcoin->vin.size() > 0) |
22dfd735 | 7446 | { |
a3fad211 | 7447 | bool any_mine = false; |
22dfd735 | 7448 | // group all input addresses with each other |
7449 | BOOST_FOREACH(CTxIn txin, pcoin->vin) | |
b1093efa GM |
7450 | { |
7451 | CTxDestination address; | |
a3fad211 GM |
7452 | if(!IsMine(txin)) /* If this input isn't mine, ignore it */ |
7453 | continue; | |
b1093efa GM |
7454 | if(!ExtractDestination(mapWallet[txin.prevout.hash].vout[txin.prevout.n].scriptPubKey, address)) |
7455 | continue; | |
7456 | grouping.insert(address); | |
a3fad211 | 7457 | any_mine = true; |
b1093efa | 7458 | } |
22dfd735 | 7459 | |
7460 | // group change with input addresses | |
a3fad211 GM |
7461 | if (any_mine) |
7462 | { | |
7463 | BOOST_FOREACH(CTxOut txout, pcoin->vout) | |
7464 | if (IsChange(txout)) | |
7465 | { | |
7466 | CTxDestination txoutAddr; | |
7467 | if(!ExtractDestination(txout.scriptPubKey, txoutAddr)) | |
7468 | continue; | |
7469 | grouping.insert(txoutAddr); | |
7470 | } | |
7471 | } | |
7472 | if (grouping.size() > 0) | |
7473 | { | |
7474 | groupings.insert(grouping); | |
7475 | grouping.clear(); | |
7476 | } | |
22dfd735 | 7477 | } |
7478 | ||
7479 | // group lone addrs by themselves | |
b1093efa | 7480 | for (unsigned int i = 0; i < pcoin->vout.size(); i++) |
22dfd735 | 7481 | if (IsMine(pcoin->vout[i])) |
7482 | { | |
b1093efa GM |
7483 | CTxDestination address; |
7484 | if(!ExtractDestination(pcoin->vout[i].scriptPubKey, address)) | |
7485 | continue; | |
7486 | grouping.insert(address); | |
22dfd735 | 7487 | groupings.insert(grouping); |
7488 | grouping.clear(); | |
7489 | } | |
7490 | } | |
7491 | ||
b1093efa GM |
7492 | set< set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses |
7493 | map< CTxDestination, set<CTxDestination>* > setmap; // map addresses to the unique group containing it | |
7494 | BOOST_FOREACH(set<CTxDestination> grouping, groupings) | |
22dfd735 | 7495 | { |
7496 | // make a set of all the groups hit by this new group | |
b1093efa GM |
7497 | set< set<CTxDestination>* > hits; |
7498 | map< CTxDestination, set<CTxDestination>* >::iterator it; | |
7499 | BOOST_FOREACH(CTxDestination address, grouping) | |
22dfd735 | 7500 | if ((it = setmap.find(address)) != setmap.end()) |
7501 | hits.insert((*it).second); | |
7502 | ||
7503 | // merge all hit groups into a new single group and delete old groups | |
b1093efa GM |
7504 | set<CTxDestination>* merged = new set<CTxDestination>(grouping); |
7505 | BOOST_FOREACH(set<CTxDestination>* hit, hits) | |
22dfd735 | 7506 | { |
7507 | merged->insert(hit->begin(), hit->end()); | |
7508 | uniqueGroupings.erase(hit); | |
7509 | delete hit; | |
7510 | } | |
7511 | uniqueGroupings.insert(merged); | |
7512 | ||
7513 | // update setmap | |
b1093efa | 7514 | BOOST_FOREACH(CTxDestination element, *merged) |
22dfd735 | 7515 | setmap[element] = merged; |
7516 | } | |
7517 | ||
b1093efa GM |
7518 | set< set<CTxDestination> > ret; |
7519 | BOOST_FOREACH(set<CTxDestination>* uniqueGrouping, uniqueGroupings) | |
22dfd735 | 7520 | { |
7521 | ret.insert(*uniqueGrouping); | |
7522 | delete uniqueGrouping; | |
7523 | } | |
7524 | ||
7525 | return ret; | |
7526 | } | |
7527 | ||
db954a65 | 7528 | std::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const |
3624356e | 7529 | { |
43422a01 | 7530 | LOCK(cs_wallet); |
3624356e GA |
7531 | set<CTxDestination> result; |
7532 | BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, mapAddressBook) | |
7533 | { | |
7534 | const CTxDestination& address = item.first; | |
7535 | const string& strName = item.second.name; | |
7536 | if (strName == strAccount) | |
7537 | result.insert(address); | |
7538 | } | |
7539 | return result; | |
7540 | } | |
7541 | ||
360cfe14 | 7542 | bool CReserveKey::GetReservedKey(CPubKey& pubkey) |
e8ef3da7 WL |
7543 | { |
7544 | if (nIndex == -1) | |
7545 | { | |
7546 | CKeyPool keypool; | |
7547 | pwallet->ReserveKeyFromKeyPool(nIndex, keypool); | |
0d7b28e5 MC |
7548 | if (nIndex != -1) |
7549 | vchPubKey = keypool.vchPubKey; | |
360cfe14 | 7550 | else { |
6c37f7fd | 7551 | return false; |
cee69980 | 7552 | } |
e8ef3da7 | 7553 | } |
fd61d6f5 | 7554 | assert(vchPubKey.IsValid()); |
360cfe14 PW |
7555 | pubkey = vchPubKey; |
7556 | return true; | |
e8ef3da7 WL |
7557 | } |
7558 | ||
7559 | void CReserveKey::KeepKey() | |
7560 | { | |
7561 | if (nIndex != -1) | |
7562 | pwallet->KeepKey(nIndex); | |
7563 | nIndex = -1; | |
fd61d6f5 | 7564 | vchPubKey = CPubKey(); |
e8ef3da7 WL |
7565 | } |
7566 | ||
7567 | void CReserveKey::ReturnKey() | |
7568 | { | |
7569 | if (nIndex != -1) | |
7570 | pwallet->ReturnKey(nIndex); | |
7571 | nIndex = -1; | |
fd61d6f5 | 7572 | vchPubKey = CPubKey(); |
e8ef3da7 | 7573 | } |
ae3d0aba | 7574 | |
434e4273 | 7575 | void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const |
30ab2c9c PW |
7576 | { |
7577 | setAddress.clear(); | |
7578 | ||
7579 | CWalletDB walletdb(strWalletFile); | |
7580 | ||
f8dcd5ca | 7581 | LOCK2(cs_main, cs_wallet); |
51ed9ec9 | 7582 | BOOST_FOREACH(const int64_t& id, setKeyPool) |
30ab2c9c PW |
7583 | { |
7584 | CKeyPool keypool; | |
7585 | if (!walletdb.ReadPool(id, keypool)) | |
5262fde0 | 7586 | throw runtime_error("GetAllReserveKeyHashes(): read failed"); |
fd61d6f5 | 7587 | assert(keypool.vchPubKey.IsValid()); |
10254401 PW |
7588 | CKeyID keyID = keypool.vchPubKey.GetID(); |
7589 | if (!HaveKey(keyID)) | |
5262fde0 | 7590 | throw runtime_error("GetAllReserveKeyHashes(): unknown key in key pool"); |
10254401 | 7591 | setAddress.insert(keyID); |
30ab2c9c PW |
7592 | } |
7593 | } | |
fe4a6550 WL |
7594 | |
7595 | void CWallet::UpdatedTransaction(const uint256 &hashTx) | |
7596 | { | |
7597 | { | |
7598 | LOCK(cs_wallet); | |
7599 | // Only notify UI if this transaction is in this wallet | |
7600 | map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx); | |
7601 | if (mi != mapWallet.end()) | |
7602 | NotifyTransactionChanged(this, hashTx, CT_UPDATED); | |
7603 | } | |
7604 | } | |
fdbb537d | 7605 | |
489fe227 | 7606 | class MiningAddressScript : public CReserveScript |
7607 | { | |
7608 | // CReserveScript requires implementing this function, so that if an | |
7609 | // internal (not-visible) wallet address is used, the wallet can mark it as | |
7610 | // important when a block is mined (so it then appears to the user). | |
7611 | // If -mineraddress is set, the user already knows about and is managing the | |
7612 | // address, so we don't need to do anything here. | |
7613 | void KeepScript() {} | |
7614 | }; | |
7615 | ||
7616 | void GetScriptForMiningAddress(boost::shared_ptr<CReserveScript> &script) | |
7617 | { | |
7618 | CTxDestination addr = DecodeDestination(GetArg("-mineraddress", "")); | |
7619 | if (!IsValidDestination(addr)) { | |
7620 | return; | |
7621 | } | |
7622 | ||
7623 | boost::shared_ptr<MiningAddressScript> mAddr(new MiningAddressScript()); | |
7624 | script = mAddr; | |
7625 | script->reserveScript = GetScriptForDestination(addr); | |
7626 | } | |
7627 | ||
f4055fe1 | 7628 | void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script) |
b2993bc5 | 7629 | { |
489fe227 | 7630 | if (!GetArg("-mineraddress", "").empty()) |
7631 | { | |
7632 | GetScriptForMiningAddress(script); | |
b2993bc5 JS |
7633 | return; |
7634 | } | |
7635 | ||
f4055fe1 | 7636 | boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this)); |
b2993bc5 | 7637 | CPubKey pubkey; |
f4055fe1 | 7638 | if (!rKey->GetReservedKey(pubkey)) |
b2993bc5 | 7639 | return; |
f4055fe1 JS |
7640 | |
7641 | script = rKey; | |
7642 | script->reserveScript = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; | |
b2993bc5 JS |
7643 | } |
7644 | ||
fdbb537d JG |
7645 | void CWallet::LockCoin(COutPoint& output) |
7646 | { | |
95691680 | 7647 | AssertLockHeld(cs_wallet); // setLockedCoins |
fdbb537d JG |
7648 | setLockedCoins.insert(output); |
7649 | } | |
7650 | ||
7651 | void CWallet::UnlockCoin(COutPoint& output) | |
7652 | { | |
95691680 | 7653 | AssertLockHeld(cs_wallet); // setLockedCoins |
fdbb537d JG |
7654 | setLockedCoins.erase(output); |
7655 | } | |
7656 | ||
7657 | void CWallet::UnlockAllCoins() | |
7658 | { | |
95691680 | 7659 | AssertLockHeld(cs_wallet); // setLockedCoins |
fdbb537d JG |
7660 | setLockedCoins.clear(); |
7661 | } | |
7662 | ||
7663 | bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const | |
7664 | { | |
95691680 | 7665 | AssertLockHeld(cs_wallet); // setLockedCoins |
fdbb537d JG |
7666 | COutPoint outpt(hash, n); |
7667 | ||
7668 | return (setLockedCoins.count(outpt) > 0); | |
7669 | } | |
7670 | ||
7671 | void CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts) | |
7672 | { | |
95691680 | 7673 | AssertLockHeld(cs_wallet); // setLockedCoins |
fdbb537d JG |
7674 | for (std::set<COutPoint>::iterator it = setLockedCoins.begin(); |
7675 | it != setLockedCoins.end(); it++) { | |
7676 | COutPoint outpt = (*it); | |
7677 | vOutpts.push_back(outpt); | |
7678 | } | |
7679 | } | |
7680 | ||
98a4f6a6 BM |
7681 | |
7682 | // Note Locking Operations | |
7683 | ||
e935beb8 | 7684 | void CWallet::LockNote(const JSOutPoint& output) |
98a4f6a6 | 7685 | { |
d358d145 S |
7686 | AssertLockHeld(cs_wallet); // setLockedSproutNotes |
7687 | setLockedSproutNotes.insert(output); | |
98a4f6a6 BM |
7688 | } |
7689 | ||
e935beb8 | 7690 | void CWallet::UnlockNote(const JSOutPoint& output) |
98a4f6a6 | 7691 | { |
d358d145 S |
7692 | AssertLockHeld(cs_wallet); // setLockedSproutNotes |
7693 | setLockedSproutNotes.erase(output); | |
98a4f6a6 BM |
7694 | } |
7695 | ||
9ae73247 | 7696 | void CWallet::UnlockAllSproutNotes() |
98a4f6a6 | 7697 | { |
d358d145 S |
7698 | AssertLockHeld(cs_wallet); // setLockedSproutNotes |
7699 | setLockedSproutNotes.clear(); | |
98a4f6a6 BM |
7700 | } |
7701 | ||
e935beb8 | 7702 | bool CWallet::IsLockedNote(const JSOutPoint& outpt) const |
98a4f6a6 | 7703 | { |
d358d145 | 7704 | AssertLockHeld(cs_wallet); // setLockedSproutNotes |
98a4f6a6 | 7705 | |
d358d145 | 7706 | return (setLockedSproutNotes.count(outpt) > 0); |
98a4f6a6 BM |
7707 | } |
7708 | ||
0f62cacf | 7709 | std::vector<JSOutPoint> CWallet::ListLockedSproutNotes() |
98a4f6a6 | 7710 | { |
d358d145 S |
7711 | AssertLockHeld(cs_wallet); // setLockedSproutNotes |
7712 | std::vector<JSOutPoint> vOutpts(setLockedSproutNotes.begin(), setLockedSproutNotes.end()); | |
98a4f6a6 BM |
7713 | return vOutpts; |
7714 | } | |
7715 | ||
b1c693e5 S |
7716 | void CWallet::LockNote(const SaplingOutPoint& output) |
7717 | { | |
7718 | AssertLockHeld(cs_wallet); | |
7719 | setLockedSaplingNotes.insert(output); | |
7720 | } | |
7721 | ||
7722 | void CWallet::UnlockNote(const SaplingOutPoint& output) | |
7723 | { | |
7724 | AssertLockHeld(cs_wallet); | |
7725 | setLockedSaplingNotes.erase(output); | |
7726 | } | |
7727 | ||
7728 | void CWallet::UnlockAllSaplingNotes() | |
7729 | { | |
7730 | AssertLockHeld(cs_wallet); | |
7731 | setLockedSaplingNotes.clear(); | |
7732 | } | |
7733 | ||
7734 | bool CWallet::IsLockedNote(const SaplingOutPoint& output) const | |
7735 | { | |
7736 | AssertLockHeld(cs_wallet); | |
7737 | return (setLockedSaplingNotes.count(output) > 0); | |
7738 | } | |
7739 | ||
7740 | std::vector<SaplingOutPoint> CWallet::ListLockedSaplingNotes() | |
7741 | { | |
7742 | AssertLockHeld(cs_wallet); | |
7743 | std::vector<SaplingOutPoint> vOutputs(setLockedSaplingNotes.begin(), setLockedSaplingNotes.end()); | |
7744 | return vOutputs; | |
7745 | } | |
7746 | ||
5b40d886 | 7747 | /** @} */ // end of Actions |
8b59a3d3 | 7748 | |
7749 | class CAffectedKeysVisitor : public boost::static_visitor<void> { | |
7750 | private: | |
7751 | const CKeyStore &keystore; | |
7752 | std::vector<CKeyID> &vKeys; | |
7753 | ||
7754 | public: | |
7755 | CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} | |
7756 | ||
7757 | void Process(const CScript &script) { | |
7758 | txnouttype type; | |
7759 | std::vector<CTxDestination> vDest; | |
7760 | int nRequired; | |
7761 | if (ExtractDestinations(script, type, vDest, nRequired)) { | |
7762 | BOOST_FOREACH(const CTxDestination &dest, vDest) | |
7763 | boost::apply_visitor(*this, dest); | |
7764 | } | |
7765 | } | |
7766 | ||
7767 | void operator()(const CKeyID &keyId) { | |
7768 | if (keystore.HaveKey(keyId)) | |
7769 | vKeys.push_back(keyId); | |
7770 | } | |
7771 | ||
b8deecdc | 7772 | void operator()(const CPubKey &key) { |
7773 | CKeyID keyId = key.GetID(); | |
7774 | if (keystore.HaveKey(keyId)) | |
7775 | vKeys.push_back(keyId); | |
7776 | } | |
7777 | ||
56fe75cb | 7778 | // TODO: need to finish storage of quantum public key in wallet |
7779 | void operator()(const CQuantumID &keyId) { | |
7780 | if (keystore.HaveKey(keyId)) | |
7781 | vKeys.push_back(keyId); | |
7782 | } | |
7783 | ||
2d8b9129 | 7784 | void operator()(const CIndexID &keyId) { |
7785 | } | |
7786 | ||
8b59a3d3 | 7787 | void operator()(const CScriptID &scriptId) { |
7788 | CScript script; | |
7789 | if (keystore.GetCScript(scriptId, script)) | |
7790 | Process(script); | |
7791 | } | |
7792 | ||
0d7fed99 | 7793 | void operator()(const CIdentityID &idId) { |
5bc89dab | 7794 | std::pair<CIdentityMapKey, CIdentityMapValue> identity; |
7795 | if (keystore.GetIdentity(idId, identity)) | |
0d7fed99 | 7796 | { |
5bc89dab | 7797 | for (auto dest : identity.second.primaryAddresses) |
0d7fed99 | 7798 | { |
7799 | boost::apply_visitor(*this, dest); | |
7800 | } | |
7801 | } | |
7802 | } | |
7803 | ||
8b59a3d3 | 7804 | void operator()(const CNoDestination &none) {} |
7805 | }; | |
7806 | ||
51ed9ec9 | 7807 | void CWallet::GetKeyBirthTimes(std::map<CKeyID, int64_t> &mapKeyBirth) const { |
95691680 | 7808 | AssertLockHeld(cs_wallet); // mapKeyMetadata |
434e4273 PW |
7809 | mapKeyBirth.clear(); |
7810 | ||
7811 | // get birth times for keys with metadata | |
7812 | for (std::map<CKeyID, CKeyMetadata>::const_iterator it = mapKeyMetadata.begin(); it != mapKeyMetadata.end(); it++) | |
7813 | if (it->second.nCreateTime) | |
7814 | mapKeyBirth[it->first] = it->second.nCreateTime; | |
7815 | ||
7816 | // map in which we'll infer heights of other keys | |
4c6d41b8 | 7817 | CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganised; use a 144-block safety margin |
434e4273 PW |
7818 | std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; |
7819 | std::set<CKeyID> setKeys; | |
7820 | GetKeys(setKeys); | |
7821 | BOOST_FOREACH(const CKeyID &keyid, setKeys) { | |
7822 | if (mapKeyBirth.count(keyid) == 0) | |
7823 | mapKeyFirstBlock[keyid] = pindexMax; | |
7824 | } | |
7825 | setKeys.clear(); | |
7826 | ||
7827 | // if there are no such keys, we're done | |
7828 | if (mapKeyFirstBlock.empty()) | |
7829 | return; | |
7830 | ||
7831 | // find first block that affects those keys, if there are any left | |
7832 | std::vector<CKeyID> vAffected; | |
7833 | for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) { | |
7834 | // iterate over all wallet transactions... | |
7835 | const CWalletTx &wtx = (*it).second; | |
145d5be8 | 7836 | BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock); |
4c6d41b8 | 7837 | if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) { |
434e4273 | 7838 | // ... which are already in a block |
4b729ec5 | 7839 | int nHeight = blit->second->GetHeight(); |
434e4273 PW |
7840 | BOOST_FOREACH(const CTxOut &txout, wtx.vout) { |
7841 | // iterate over all their outputs | |
8b59a3d3 | 7842 | CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); |
434e4273 PW |
7843 | BOOST_FOREACH(const CKeyID &keyid, vAffected) { |
7844 | // ... and all their affected keys | |
7845 | std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid); | |
4b729ec5 | 7846 | if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->GetHeight()) |
434e4273 PW |
7847 | rit->second = blit->second; |
7848 | } | |
7849 | vAffected.clear(); | |
7850 | } | |
7851 | } | |
7852 | } | |
7853 | ||
7854 | // Extract block timestamps for those keys | |
7855 | for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++) | |
209377a7 | 7856 | mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off |
434e4273 | 7857 | } |
b10e1470 WL |
7858 | |
7859 | bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value) | |
7860 | { | |
8476d5d4 CL |
7861 | if (boost::get<CNoDestination>(&dest)) |
7862 | return false; | |
7863 | ||
b10e1470 WL |
7864 | mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); |
7865 | if (!fFileBacked) | |
7866 | return true; | |
07444da1 | 7867 | return CWalletDB(strWalletFile).WriteDestData(EncodeDestination(dest), key, value); |
b10e1470 WL |
7868 | } |
7869 | ||
7870 | bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) | |
7871 | { | |
7872 | if (!mapAddressBook[dest].destdata.erase(key)) | |
7873 | return false; | |
7874 | if (!fFileBacked) | |
7875 | return true; | |
07444da1 | 7876 | return CWalletDB(strWalletFile).EraseDestData(EncodeDestination(dest), key); |
b10e1470 WL |
7877 | } |
7878 | ||
7879 | bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) | |
7880 | { | |
7881 | mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); | |
7882 | return true; | |
7883 | } | |
7884 | ||
7885 | bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const | |
7886 | { | |
7887 | std::map<CTxDestination, CAddressBookData>::const_iterator i = mapAddressBook.find(dest); | |
7888 | if(i != mapAddressBook.end()) | |
7889 | { | |
7890 | CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key); | |
7891 | if(j != i->second.destdata.end()) | |
7892 | { | |
7893 | if(value) | |
7894 | *value = j->second; | |
7895 | return true; | |
7896 | } | |
7897 | } | |
7898 | return false; | |
7899 | } | |
af8297c0 WL |
7900 | |
7901 | CKeyPool::CKeyPool() | |
7902 | { | |
7903 | nTime = GetTime(); | |
7904 | } | |
7905 | ||
7906 | CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn) | |
7907 | { | |
7908 | nTime = GetTime(); | |
7909 | vchPubKey = vchPubKeyIn; | |
7910 | } | |
7911 | ||
7912 | CWalletKey::CWalletKey(int64_t nExpires) | |
7913 | { | |
7914 | nTimeCreated = (nExpires ? GetTime() : 0); | |
7915 | nTimeExpires = nExpires; | |
7916 | } | |
0101483f | 7917 | |
d917c115 | 7918 | void CMerkleTx::SetMerkleBranch(const CBlock& block) |
0101483f | 7919 | { |
0101483f WL |
7920 | CBlock blockTmp; |
7921 | ||
4b0deb3b DK |
7922 | // Update the tx's hashBlock |
7923 | hashBlock = block.GetHash(); | |
0101483f | 7924 | |
4b0deb3b DK |
7925 | // Locate the transaction |
7926 | for (nIndex = 0; nIndex < (int)block.vtx.size(); nIndex++) | |
7927 | if (block.vtx[nIndex] == *(CTransaction*)this) | |
7928 | break; | |
7929 | if (nIndex == (int)block.vtx.size()) | |
7930 | { | |
7931 | vMerkleBranch.clear(); | |
7932 | nIndex = -1; | |
f9150807 | 7933 | LogPrintf("ERROR: %s: couldn't find tx (%s) in block (%s)\n", __func__, GetHash().GetHex().c_str(), hashBlock.GetHex().c_str()); |
0101483f WL |
7934 | } |
7935 | ||
4b0deb3b DK |
7936 | // Fill in merkle branch |
7937 | vMerkleBranch = block.GetMerkleBranch(nIndex); | |
0101483f WL |
7938 | } |
7939 | ||
a31e8bad | 7940 | int CMerkleTx::GetDepthInMainChainINTERNAL(const CBlockIndex* &pindexRet) const |
0101483f | 7941 | { |
4f152496 | 7942 | if (hashBlock.IsNull() || nIndex == -1) |
0101483f WL |
7943 | return 0; |
7944 | AssertLockHeld(cs_main); | |
7945 | ||
7946 | // Find the block it claims to be in | |
145d5be8 | 7947 | BlockMap::iterator mi = mapBlockIndex.find(hashBlock); |
0101483f WL |
7948 | if (mi == mapBlockIndex.end()) |
7949 | return 0; | |
7950 | CBlockIndex* pindex = (*mi).second; | |
7951 | if (!pindex || !chainActive.Contains(pindex)) | |
7952 | return 0; | |
7953 | ||
7954 | // Make sure the merkle branch connects to this block | |
7955 | if (!fMerkleVerified) | |
7956 | { | |
805344dc | 7957 | if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pindex->hashMerkleRoot) |
0101483f WL |
7958 | return 0; |
7959 | fMerkleVerified = true; | |
7960 | } | |
7961 | ||
7962 | pindexRet = pindex; | |
4b729ec5 | 7963 | return chainActive.Height() - pindex->GetHeight() + 1; |
0101483f WL |
7964 | } |
7965 | ||
a31e8bad | 7966 | int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const |
0101483f WL |
7967 | { |
7968 | AssertLockHeld(cs_main); | |
7969 | int nResult = GetDepthInMainChainINTERNAL(pindexRet); | |
805344dc | 7970 | if (nResult == 0 && !mempool.exists(GetHash())) |
0101483f WL |
7971 | return -1; // Not in chain, not in mempool |
7972 | ||
7973 | return nResult; | |
7974 | } | |
7975 | ||
7976 | int CMerkleTx::GetBlocksToMaturity() const | |
7977 | { | |
7a90b9dd | 7978 | if ( ASSETCHAINS_SYMBOL[0] == 0 ) |
7979 | COINBASE_MATURITY = _COINBASE_MATURITY; | |
0101483f WL |
7980 | if (!IsCoinBase()) |
7981 | return 0; | |
39267c35 | 7982 | int32_t depth = GetDepthInMainChain(); |
204cf3fc | 7983 | int32_t ut = UnlockTime(0); |
c2f6623f | 7984 | int32_t toMaturity = (ut - chainActive.Height()) < 0 ? 0 : ut - chainActive.Height(); |
7985 | //printf("depth.%i, unlockTime.%i, toMaturity.%i\n", depth, ut, toMaturity); | |
7986 | ut = (COINBASE_MATURITY - depth) < 0 ? 0 : COINBASE_MATURITY - depth; | |
204cf3fc | 7987 | return(ut < toMaturity ? toMaturity : ut); |
0101483f WL |
7988 | } |
7989 | ||
1371e6f5 | 7990 | bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree, bool fRejectAbsurdFee) |
0101483f WL |
7991 | { |
7992 | CValidationState state; | |
1371e6f5 | 7993 | return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL, fRejectAbsurdFee); |
0101483f WL |
7994 | } |
7995 | ||
cb0d208f S |
7996 | /** |
7997 | * Find notes in the wallet filtered by payment address, min depth and ability to spend. | |
7998 | * These notes are decrypted and added to the output parameter vector, outEntries. | |
7999 | */ | |
94e99acd | 8000 | void CWallet::GetFilteredNotes( |
a630f503 | 8001 | std::vector<SproutNoteEntry>& sproutEntries, |
94e99acd JG |
8002 | std::vector<SaplingNoteEntry>& saplingEntries, |
8003 | std::string address, | |
8004 | int minDepth, | |
8005 | bool ignoreSpent, | |
ef27d7e4 | 8006 | bool requireSpendingKey) |
a5ac2e25 | 8007 | { |
bdbe8e85 JG |
8008 | std::set<PaymentAddress> filterAddresses; |
8009 | ||
a5ac2e25 | 8010 | if (address.length() > 0) { |
e5eab182 | 8011 | filterAddresses.insert(DecodePaymentAddress(address)); |
a5ac2e25 S |
8012 | } |
8013 | ||
ef27d7e4 | 8014 | GetFilteredNotes(sproutEntries, saplingEntries, filterAddresses, minDepth, INT_MAX, ignoreSpent, requireSpendingKey); |
bdbe8e85 JG |
8015 | } |
8016 | ||
8017 | /** | |
095eee4f EOW |
8018 | * Find notes in the wallet filtered by payment addresses, min depth, max depth, |
8019 | * if the note is spent, if a spending key is required, and if the notes are locked. | |
bdbe8e85 JG |
8020 | * These notes are decrypted and added to the output parameter vector, outEntries. |
8021 | */ | |
8022 | void CWallet::GetFilteredNotes( | |
a630f503 | 8023 | std::vector<SproutNoteEntry>& sproutEntries, |
94e99acd | 8024 | std::vector<SaplingNoteEntry>& saplingEntries, |
bdbe8e85 JG |
8025 | std::set<PaymentAddress>& filterAddresses, |
8026 | int minDepth, | |
ef27d7e4 | 8027 | int maxDepth, |
bdbe8e85 | 8028 | bool ignoreSpent, |
ef27d7e4 EOW |
8029 | bool requireSpendingKey, |
8030 | bool ignoreLocked) | |
bdbe8e85 | 8031 | { |
a5ac2e25 S |
8032 | LOCK2(cs_main, cs_wallet); |
8033 | ||
8034 | for (auto & p : mapWallet) { | |
8035 | CWalletTx wtx = p.second; | |
8036 | ||
8037 | // Filter the transactions before checking for notes | |
095eee4f EOW |
8038 | if (!CheckFinalTx(wtx) || |
8039 | wtx.GetBlocksToMaturity() > 0 || | |
8040 | wtx.GetDepthInMainChain() < minDepth || | |
8041 | wtx.GetDepthInMainChain() > maxDepth) { | |
a5ac2e25 S |
8042 | continue; |
8043 | } | |
8044 | ||
005f3ad1 | 8045 | for (auto & pair : wtx.mapSproutNoteData) { |
a5ac2e25 | 8046 | JSOutPoint jsop = pair.first; |
005f3ad1 | 8047 | SproutNoteData nd = pair.second; |
e5eab182 | 8048 | SproutPaymentAddress pa = nd.address; |
a5ac2e25 S |
8049 | |
8050 | // skip notes which belong to a different payment address in the wallet | |
bdbe8e85 | 8051 | if (!(filterAddresses.empty() || filterAddresses.count(pa))) { |
a5ac2e25 S |
8052 | continue; |
8053 | } | |
8054 | ||
8055 | // skip note which has been spent | |
3b6dd486 | 8056 | if (ignoreSpent && nd.nullifier && IsSproutSpent(*nd.nullifier)) { |
a5ac2e25 S |
8057 | continue; |
8058 | } | |
8059 | ||
9a2b8ae5 | 8060 | // skip notes which cannot be spent |
ef27d7e4 | 8061 | if (requireSpendingKey && !HaveSproutSpendingKey(pa)) { |
9a2b8ae5 JG |
8062 | continue; |
8063 | } | |
efb7662d | 8064 | |
4e6400bc | 8065 | // skip locked notes |
ef27d7e4 | 8066 | if (ignoreLocked && IsLockedNote(jsop)) { |
4e6400bc BM |
8067 | continue; |
8068 | } | |
9a2b8ae5 | 8069 | |
f57f76d7 | 8070 | int i = jsop.js; // Index into CTransaction.vJoinSplit |
a5ac2e25 S |
8071 | int j = jsop.n; // Index into JSDescription.ciphertexts |
8072 | ||
8073 | // Get cached decryptor | |
8074 | ZCNoteDecryption decryptor; | |
8075 | if (!GetNoteDecryptor(pa, decryptor)) { | |
8076 | // Note decryptors are created when the wallet is loaded, so it should always exist | |
80ed13d5 | 8077 | throw std::runtime_error(strprintf("Could not find note decryptor for payment address %s", EncodePaymentAddress(pa))); |
a5ac2e25 S |
8078 | } |
8079 | ||
8080 | // determine amount of funds in the note | |
f57f76d7 | 8081 | auto hSig = wtx.vJoinSplit[i].h_sig(*pzcashParams, wtx.joinSplitPubKey); |
a5ac2e25 | 8082 | try { |
5020a936 | 8083 | SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt( |
a5ac2e25 | 8084 | decryptor, |
f57f76d7 DA |
8085 | wtx.vJoinSplit[i].ciphertexts[j], |
8086 | wtx.vJoinSplit[i].ephemeralKey, | |
a5ac2e25 S |
8087 | hSig, |
8088 | (unsigned char) j); | |
8089 | ||
a630f503 E |
8090 | sproutEntries.push_back(SproutNoteEntry { |
8091 | jsop, pa, plaintext.note(pa), plaintext.memo(), wtx.GetDepthInMainChain() }); | |
a5ac2e25 | 8092 | |
51fde9ea | 8093 | } catch (const note_decryption_failed &err) { |
a5ac2e25 | 8094 | // Couldn't decrypt with this spending key |
80ed13d5 | 8095 | throw std::runtime_error(strprintf("Could not decrypt note for payment address %s", EncodePaymentAddress(pa))); |
51fde9ea JG |
8096 | } catch (const std::exception &exc) { |
8097 | // Unexpected failure | |
80ed13d5 | 8098 | throw std::runtime_error(strprintf("Error while decrypting note for payment address %s: %s", EncodePaymentAddress(pa), exc.what())); |
a5ac2e25 S |
8099 | } |
8100 | } | |
94e99acd JG |
8101 | |
8102 | for (auto & pair : wtx.mapSaplingNoteData) { | |
8103 | SaplingOutPoint op = pair.first; | |
8104 | SaplingNoteData nd = pair.second; | |
8105 | ||
8106 | auto maybe_pt = SaplingNotePlaintext::decrypt( | |
8107 | wtx.vShieldedOutput[op.n].encCiphertext, | |
8108 | nd.ivk, | |
8109 | wtx.vShieldedOutput[op.n].ephemeralKey, | |
8110 | wtx.vShieldedOutput[op.n].cm); | |
8111 | assert(static_cast<bool>(maybe_pt)); | |
8112 | auto notePt = maybe_pt.get(); | |
8113 | ||
8114 | auto maybe_pa = nd.ivk.address(notePt.d); | |
8115 | assert(static_cast<bool>(maybe_pa)); | |
8116 | auto pa = maybe_pa.get(); | |
8117 | ||
8118 | // skip notes which belong to a different payment address in the wallet | |
8119 | if (!(filterAddresses.empty() || filterAddresses.count(pa))) { | |
8120 | continue; | |
8121 | } | |
8122 | ||
8123 | if (ignoreSpent && nd.nullifier && IsSaplingSpent(*nd.nullifier)) { | |
8124 | continue; | |
8125 | } | |
8126 | ||
8127 | // skip notes which cannot be spent | |
ef27d7e4 | 8128 | if (requireSpendingKey) { |
94e99acd JG |
8129 | libzcash::SaplingIncomingViewingKey ivk; |
8130 | libzcash::SaplingFullViewingKey fvk; | |
8131 | if (!(GetSaplingIncomingViewingKey(pa, ivk) && | |
8132 | GetSaplingFullViewingKey(ivk, fvk) && | |
8133 | HaveSaplingSpendingKey(fvk))) { | |
8134 | continue; | |
8135 | } | |
8136 | } | |
8137 | ||
8138 | // skip locked notes | |
0adb77d3 EOW |
8139 | if (ignoreLocked && IsLockedNote(op)) { |
8140 | continue; | |
8141 | } | |
94e99acd JG |
8142 | |
8143 | auto note = notePt.note(nd.ivk).get(); | |
8144 | saplingEntries.push_back(SaplingNoteEntry { | |
94e99acd JG |
8145 | op, pa, note, notePt.memo(), wtx.GetDepthInMainChain() }); |
8146 | } | |
d72c19a6 S |
8147 | } |
8148 | } | |
8149 | ||
d72c19a6 | 8150 | |
053cb349 JG |
8151 | // |
8152 | // Shielded key and address generalizations | |
8153 | // | |
8154 | ||
8155 | bool PaymentAddressBelongsToWallet::operator()(const libzcash::SproutPaymentAddress &zaddr) const | |
8156 | { | |
8157 | return m_wallet->HaveSproutSpendingKey(zaddr) || m_wallet->HaveSproutViewingKey(zaddr); | |
8158 | } | |
8159 | ||
8160 | bool PaymentAddressBelongsToWallet::operator()(const libzcash::SaplingPaymentAddress &zaddr) const | |
8161 | { | |
8162 | libzcash::SaplingIncomingViewingKey ivk; | |
8163 | ||
70b4ad2d JG |
8164 | // If we have a SaplingExtendedSpendingKey in the wallet, then we will |
8165 | // also have the corresponding SaplingFullViewingKey. | |
053cb349 JG |
8166 | return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) && |
8167 | m_wallet->HaveSaplingFullViewingKey(ivk); | |
8168 | } | |
8169 | ||
8170 | bool PaymentAddressBelongsToWallet::operator()(const libzcash::InvalidEncoding& no) const | |
8171 | { | |
8172 | return false; | |
8173 | } | |
e22c115e | 8174 | |
81e0fd2e JG |
8175 | bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SproutPaymentAddress &zaddr) const |
8176 | { | |
8177 | return m_wallet->HaveSproutSpendingKey(zaddr); | |
8178 | } | |
8179 | ||
8180 | bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::SaplingPaymentAddress &zaddr) const | |
8181 | { | |
8182 | libzcash::SaplingIncomingViewingKey ivk; | |
8183 | libzcash::SaplingFullViewingKey fvk; | |
8184 | ||
8185 | return m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) && | |
8186 | m_wallet->GetSaplingFullViewingKey(ivk, fvk) && | |
8187 | m_wallet->HaveSaplingSpendingKey(fvk); | |
8188 | } | |
8189 | ||
8190 | bool HaveSpendingKeyForPaymentAddress::operator()(const libzcash::InvalidEncoding& no) const | |
8191 | { | |
8192 | return false; | |
8193 | } | |
8194 | ||
e22c115e JG |
8195 | boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()( |
8196 | const libzcash::SproutPaymentAddress &zaddr) const | |
8197 | { | |
8198 | libzcash::SproutSpendingKey k; | |
8199 | if (m_wallet->GetSproutSpendingKey(zaddr, k)) { | |
8200 | return libzcash::SpendingKey(k); | |
8201 | } else { | |
8202 | return boost::none; | |
a5ac2e25 S |
8203 | } |
8204 | } | |
e22c115e JG |
8205 | |
8206 | boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()( | |
8207 | const libzcash::SaplingPaymentAddress &zaddr) const | |
8208 | { | |
5e360fb2 EOW |
8209 | libzcash::SaplingExtendedSpendingKey extsk; |
8210 | if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) { | |
8211 | return libzcash::SpendingKey(extsk); | |
e22c115e JG |
8212 | } else { |
8213 | return boost::none; | |
8214 | } | |
8215 | } | |
8216 | ||
8217 | boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()( | |
8218 | const libzcash::InvalidEncoding& no) const | |
8219 | { | |
8220 | // Defaults to InvalidEncoding | |
8221 | return libzcash::SpendingKey(); | |
8222 | } | |
fcab001b | 8223 | |
0f03de55 | 8224 | SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const { |
2fe39561 | 8225 | auto addr = sk.address(); |
9bcf90e2 EOW |
8226 | if (log){ |
8227 | LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr)); | |
8228 | } | |
fcab001b | 8229 | if (m_wallet->HaveSproutSpendingKey(addr)) { |
0f03de55 EOW |
8230 | return KeyAlreadyExists; |
8231 | } else if (m_wallet-> AddSproutZKey(sk)) { | |
9bcf90e2 | 8232 | m_wallet->mapSproutZKeyMetadata[addr].nCreateTime = nTime; |
0f03de55 EOW |
8233 | return KeyAdded; |
8234 | } else { | |
8235 | return KeyNotAdded; | |
fcab001b EOW |
8236 | } |
8237 | } | |
8238 | ||
0f03de55 | 8239 | SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const { |
fcab001b EOW |
8240 | auto fvk = sk.expsk.full_viewing_key(); |
8241 | auto ivk = fvk.in_viewing_key(); | |
8242 | auto addr = sk.DefaultAddress(); | |
8243 | { | |
9bcf90e2 EOW |
8244 | if (log){ |
8245 | LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr)); | |
8246 | } | |
fcab001b EOW |
8247 | // Don't throw error in case a key is already there |
8248 | if (m_wallet->HaveSaplingSpendingKey(fvk)) { | |
0f03de55 | 8249 | return KeyAlreadyExists; |
fcab001b | 8250 | } else { |
fcab001b | 8251 | if (!m_wallet-> AddSaplingZKey(sk, addr)) { |
0f03de55 | 8252 | return KeyNotAdded; |
fcab001b EOW |
8253 | } |
8254 | ||
8255 | // Sapling addresses can't have been used in transactions prior to activation. | |
8256 | if (params.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight == Consensus::NetworkUpgrade::ALWAYS_ACTIVE) { | |
9bcf90e2 | 8257 | m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = nTime; |
fcab001b | 8258 | } else { |
9bcf90e2 EOW |
8259 | // 154051200 seconds from epoch is Friday, 26 October 2018 00:00:00 GMT - definitely before Sapling activates |
8260 | m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = std::max((int64_t) 154051200, nTime); | |
fcab001b | 8261 | } |
2fe39561 EOW |
8262 | if (hdKeypath) { |
8263 | m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.get(); | |
8264 | } | |
8265 | if (seedFpStr) { | |
8266 | uint256 seedFp; | |
8267 | seedFp.SetHex(seedFpStr.get()); | |
8268 | m_wallet->mapSaplingZKeyMetadata[ivk].seedFp = seedFp; | |
8269 | } | |
0f03de55 | 8270 | return KeyAdded; |
fcab001b EOW |
8271 | } |
8272 | } | |
8273 | } | |
8274 | ||
0f03de55 | 8275 | SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { |
fcab001b EOW |
8276 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); |
8277 | } |