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