// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "WalletSerialization.h" #include #include #include #include "Common/MemoryInputStream.h" #include "Common/StdInputStream.h" #include "Common/StdOutputStream.h" #include "CryptoNoteCore/CryptoNoteSerialization.h" #include "CryptoNoteCore/CryptoNoteTools.h" #include "Serialization/BinaryOutputStreamSerializer.h" #include "Serialization/BinaryInputStreamSerializer.h" #include "Serialization/SerializationOverloads.h" #include "Wallet/WalletErrors.h" #include "WalletLegacy/KeysStorage.h" #include "WalletLegacy/WalletLegacySerialization.h" using namespace Common; using namespace Crypto; namespace { //DO NOT CHANGE IT struct WalletRecordDto { PublicKey spendPublicKey; SecretKey spendSecretKey; uint64_t pendingBalance = 0; uint64_t actualBalance = 0; uint64_t creationTimestamp = 0; }; //DO NOT CHANGE IT struct ObsoleteSpentOutputDto { uint64_t amount; Hash transactionHash; uint32_t outputInTransaction; uint64_t walletIndex; Crypto::Hash spendingTransactionHash; }; //DO NOT CHANGE IT struct ObsoleteChangeDto { Hash txHash; uint64_t amount; }; //DO NOT CHANGE IT struct UnlockTransactionJobDto { uint32_t blockHeight; Hash transactionHash; uint64_t walletIndex; }; //DO NOT CHANGE IT struct WalletTransactionDto { WalletTransactionDto() {} WalletTransactionDto(const CryptoNote::WalletTransaction& wallet) { state = wallet.state; timestamp = wallet.timestamp; blockHeight = wallet.blockHeight; hash = wallet.hash; totalAmount = wallet.totalAmount; fee = wallet.fee; creationTime = wallet.creationTime; unlockTime = wallet.unlockTime; extra = wallet.extra; } CryptoNote::WalletTransactionState state; uint64_t timestamp; uint32_t blockHeight; Hash hash; int64_t totalAmount; uint64_t fee; uint64_t creationTime; uint64_t unlockTime; std::string extra; }; //DO NOT CHANGE IT struct WalletTransferDto { WalletTransferDto(uint32_t version) : version(version) {} WalletTransferDto(const CryptoNote::WalletTransfer& tr, uint32_t version) : WalletTransferDto(version) { address = tr.address; amount = tr.amount; type = static_cast(tr.type); } std::string address; uint64_t amount; uint8_t type; uint32_t version; }; void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) { serializer(value.spendPublicKey, "spend_public_key"); serializer(value.spendSecretKey, "spend_secret_key"); serializer(value.pendingBalance, "pending_balance"); serializer(value.actualBalance, "actual_balance"); serializer(value.creationTimestamp, "creation_timestamp"); } void serialize(ObsoleteSpentOutputDto& value, CryptoNote::ISerializer& serializer) { serializer(value.amount, "amount"); serializer(value.transactionHash, "transaction_hash"); serializer(value.outputInTransaction, "output_in_transaction"); serializer(value.walletIndex, "wallet_index"); serializer(value.spendingTransactionHash, "spending_transaction_hash"); } void serialize(ObsoleteChangeDto& value, CryptoNote::ISerializer& serializer) { serializer(value.txHash, "transaction_hash"); serializer(value.amount, "amount"); } void serialize(UnlockTransactionJobDto& value, CryptoNote::ISerializer& serializer) { serializer(value.blockHeight, "block_height"); serializer(value.transactionHash, "transaction_hash"); serializer(value.walletIndex, "wallet_index"); } void serialize(WalletTransactionDto& value, CryptoNote::ISerializer& serializer) { typedef std::underlying_type::type StateType; StateType state = static_cast(value.state); serializer(state, "state"); value.state = static_cast(state); serializer(value.timestamp, "timestamp"); CryptoNote::serializeBlockHeight(serializer, value.blockHeight, "block_height"); serializer(value.hash, "hash"); serializer(value.totalAmount, "total_amount"); serializer(value.fee, "fee"); serializer(value.creationTime, "creation_time"); serializer(value.unlockTime, "unlock_time"); serializer(value.extra, "extra"); } void serialize(WalletTransferDto& value, CryptoNote::ISerializer& serializer) { serializer(value.address, "address"); serializer(value.amount, "amount"); if (value.version > 2) { serializer(value.type, "type"); } } template std::string serialize(Object& obj, const std::string& name) { std::stringstream stream; StdOutputStream output(stream); CryptoNote::BinaryOutputStreamSerializer s(output); s(obj, Common::StringView(name)); stream.flush(); return stream.str(); } std::string encrypt(const std::string& plain, CryptoNote::CryptoContext& cryptoContext) { std::string cipher; cipher.resize(plain.size()); Crypto::chacha8(plain.data(), plain.size(), cryptoContext.key, cryptoContext.iv, &cipher[0]); return cipher; } void addToStream(const std::string& cipher, const std::string& name, Common::IOutputStream& destination) { CryptoNote::BinaryOutputStreamSerializer s(destination); s(const_cast(cipher), name); } template void serializeEncrypted(Object& obj, const std::string& name, CryptoNote::CryptoContext& cryptoContext, Common::IOutputStream& destination) { std::string plain = serialize(obj, name); std::string cipher = encrypt(plain, cryptoContext); addToStream(cipher, name, destination); } std::string readCipher(Common::IInputStream& source, const std::string& name) { std::string cipher; CryptoNote::BinaryInputStreamSerializer s(source); s(cipher, name); return cipher; } std::string decrypt(const std::string& cipher, CryptoNote::CryptoContext& cryptoContext) { std::string plain; plain.resize(cipher.size()); Crypto::chacha8(cipher.data(), cipher.size(), cryptoContext.key, cryptoContext.iv, &plain[0]); return plain; } template void deserialize(Object& obj, const std::string& name, const std::string& plain) { MemoryInputStream stream(plain.data(), plain.size()); CryptoNote::BinaryInputStreamSerializer s(stream); s(obj, Common::StringView(name)); } template void deserializeEncrypted(Object& obj, const std::string& name, CryptoNote::CryptoContext& cryptoContext, Common::IInputStream& source) { std::string cipher = readCipher(source, name); std::string plain = decrypt(cipher, cryptoContext); deserialize(obj, name, plain); } bool verifyKeys(const SecretKey& sec, const PublicKey& expected_pub) { PublicKey pub; bool r = Crypto::secret_key_to_public_key(sec, pub); return r && expected_pub == pub; } void throwIfKeysMissmatch(const SecretKey& sec, const PublicKey& expected_pub) { if (!verifyKeys(sec, expected_pub)) throw std::system_error(make_error_code(CryptoNote::error::WRONG_PASSWORD)); } CryptoNote::WalletTransaction convert(const CryptoNote::WalletLegacyTransaction& tx) { CryptoNote::WalletTransaction mtx; mtx.state = CryptoNote::WalletTransactionState::SUCCEEDED; mtx.timestamp = tx.timestamp; mtx.blockHeight = tx.blockHeight; mtx.hash = tx.hash; mtx.totalAmount = tx.totalAmount; mtx.fee = tx.fee; mtx.creationTime = tx.sentTime; mtx.unlockTime = tx.unlockTime; mtx.extra = tx.extra; mtx.isBase = tx.isCoinbase; return mtx; } CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) { CryptoNote::WalletTransfer mtr; mtr.address = tr.address; mtr.amount = tr.amount; return mtr; } } namespace CryptoNote { const uint32_t WalletSerializer::SERIALIZATION_VERSION = 5; void CryptoContext::incIv() { uint64_t * i = reinterpret_cast(&iv.data[0]); (*i)++; } WalletSerializer::WalletSerializer( ITransfersObserver& transfersObserver, PublicKey& viewPublicKey, SecretKey& viewSecretKey, uint64_t& actualBalance, uint64_t& pendingBalance, WalletsContainer& walletsContainer, TransfersSyncronizer& synchronizer, UnlockTransactionJobs& unlockTransactions, WalletTransactions& transactions, WalletTransfers& transfers, uint32_t transactionSoftLockTime, UncommitedTransactions& uncommitedTransactions ) : m_transfersObserver(transfersObserver), m_viewPublicKey(viewPublicKey), m_viewSecretKey(viewSecretKey), m_actualBalance(actualBalance), m_pendingBalance(pendingBalance), m_walletsContainer(walletsContainer), m_synchronizer(synchronizer), m_unlockTransactions(unlockTransactions), m_transactions(transactions), m_transfers(transfers), m_transactionSoftLockTime(transactionSoftLockTime), uncommitedTransactions(uncommitedTransactions) { } void WalletSerializer::save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache) { CryptoContext cryptoContext = generateCryptoContext(password); CryptoNote::BinaryOutputStreamSerializer s(destination); s.beginObject("wallet"); saveVersion(destination); saveIv(destination, cryptoContext.iv); saveKeys(destination, cryptoContext); saveWallets(destination, saveCache, cryptoContext); saveFlags(saveDetails, saveCache, destination, cryptoContext); if (saveDetails) { saveTransactions(destination, cryptoContext); saveTransfers(destination, cryptoContext); } if (saveCache) { saveBalances(destination, saveCache, cryptoContext); saveTransfersSynchronizer(destination, cryptoContext); saveUnlockTransactionsJobs(destination, cryptoContext); saveUncommitedTransactions(destination, cryptoContext); } s.endObject(); } CryptoContext WalletSerializer::generateCryptoContext(const std::string& password) { CryptoContext context; Crypto::cn_context c; Crypto::generate_chacha8_key(c, password, context.key); context.iv = Crypto::rand(); return context; } void WalletSerializer::saveVersion(Common::IOutputStream& destination) { uint32_t version = SERIALIZATION_VERSION; BinaryOutputStreamSerializer s(destination); s(version, "version"); } void WalletSerializer::saveIv(Common::IOutputStream& destination, Crypto::chacha8_iv& iv) { BinaryOutputStreamSerializer s(destination); s.binary(reinterpret_cast(&iv.data), sizeof(iv.data), "chacha_iv"); } void WalletSerializer::saveKeys(Common::IOutputStream& destination, CryptoContext& cryptoContext) { savePublicKey(destination, cryptoContext); saveSecretKey(destination, cryptoContext); } void WalletSerializer::savePublicKey(Common::IOutputStream& destination, CryptoContext& cryptoContext) { serializeEncrypted(m_viewPublicKey, "public_key", cryptoContext, destination); cryptoContext.incIv(); } void WalletSerializer::saveSecretKey(Common::IOutputStream& destination, CryptoContext& cryptoContext) { serializeEncrypted(m_viewSecretKey, "secret_key", cryptoContext, destination); cryptoContext.incIv(); } void WalletSerializer::saveFlags(bool saveDetails, bool saveCache, Common::IOutputStream& destination, CryptoContext& cryptoContext) { serializeEncrypted(saveDetails, "details", cryptoContext, destination); cryptoContext.incIv(); serializeEncrypted(saveCache, "cache", cryptoContext, destination); cryptoContext.incIv(); } void WalletSerializer::saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext) { auto& index = m_walletsContainer.get(); uint64_t count = index.size(); serializeEncrypted(count, "wallets_count", cryptoContext, destination); cryptoContext.incIv(); for (const auto& w: index) { WalletRecordDto dto; dto.spendPublicKey = w.spendPublicKey; dto.spendSecretKey = w.spendSecretKey; dto.pendingBalance = saveCache ? w.pendingBalance : 0; dto.actualBalance = saveCache ? w.actualBalance : 0; dto.creationTimestamp = static_cast(w.creationTimestamp); serializeEncrypted(dto, "", cryptoContext, destination); cryptoContext.incIv(); } } void WalletSerializer::saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext) { uint64_t actual = saveCache ? m_actualBalance : 0; uint64_t pending = saveCache ? m_pendingBalance : 0; serializeEncrypted(actual, "actual_balance", cryptoContext, destination); cryptoContext.incIv(); serializeEncrypted(pending, "pending_balance", cryptoContext, destination); cryptoContext.incIv(); } void WalletSerializer::saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext) { std::stringstream stream; m_synchronizer.save(stream); stream.flush(); std::string plain = stream.str(); serializeEncrypted(plain, "transfers_synchronizer", cryptoContext, destination); cryptoContext.incIv(); } void WalletSerializer::saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext) { auto& index = m_unlockTransactions.get(); auto& wallets = m_walletsContainer.get(); uint64_t jobsCount = index.size(); serializeEncrypted(jobsCount, "unlock_transactions_jobs_count", cryptoContext, destination); cryptoContext.incIv(); for (const auto& j: index) { auto containerIt = wallets.find(j.container); assert(containerIt != wallets.end()); auto rndIt = m_walletsContainer.project(containerIt); assert(rndIt != m_walletsContainer.get().end()); uint64_t walletIndex = std::distance(m_walletsContainer.get().begin(), rndIt); UnlockTransactionJobDto dto; dto.blockHeight = j.blockHeight; dto.transactionHash = j.transactionHash; dto.walletIndex = walletIndex; serializeEncrypted(dto, "", cryptoContext, destination); cryptoContext.incIv(); } } void WalletSerializer::saveUncommitedTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) { serializeEncrypted(uncommitedTransactions, "uncommited_transactions", cryptoContext, destination); } void WalletSerializer::saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) { uint64_t count = m_transactions.size(); serializeEncrypted(count, "transactions_count", cryptoContext, destination); cryptoContext.incIv(); for (const auto& tx: m_transactions) { WalletTransactionDto dto(tx); serializeEncrypted(dto, "", cryptoContext, destination); cryptoContext.incIv(); } } void WalletSerializer::saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext) { uint64_t count = m_transfers.size(); serializeEncrypted(count, "transfers_count", cryptoContext, destination); cryptoContext.incIv(); for (const auto& kv: m_transfers) { uint64_t txId = kv.first; WalletTransferDto tr(kv.second, SERIALIZATION_VERSION); serializeEncrypted(txId, "transaction_id", cryptoContext, destination); cryptoContext.incIv(); serializeEncrypted(tr, "transfer", cryptoContext, destination); cryptoContext.incIv(); } } void WalletSerializer::load(const std::string& password, Common::IInputStream& source) { CryptoNote::BinaryInputStreamSerializer s(source); s.beginObject("wallet"); uint32_t version = loadVersion(source); if (version > SERIALIZATION_VERSION) { throw std::system_error(make_error_code(error::WRONG_VERSION)); } else if (version != 1) { loadWallet(source, password, version); } else { loadWalletV1(source, password); } s.endObject(); } void WalletSerializer::loadWallet(Common::IInputStream& source, const std::string& password, uint32_t version) { CryptoNote::CryptoContext cryptoContext; bool details = false; bool cache = false; loadIv(source, cryptoContext.iv); generateKey(password, cryptoContext.key); loadKeys(source, cryptoContext); checkKeys(); loadWallets(source, cryptoContext); subscribeWallets(); loadFlags(details, cache, source, cryptoContext); if (details) { loadTransactions(source, cryptoContext); loadTransfers(source, cryptoContext, version); } if (version < 5) { updateTransfersSign(); cache = false; } if (cache) { loadBalances(source, cryptoContext); loadTransfersSynchronizer(source, cryptoContext); if (version < 5) { loadObsoleteSpentOutputs(source, cryptoContext); } loadUnlockTransactionsJobs(source, cryptoContext); if (version < 5) { loadObsoleteChange(source, cryptoContext); } if (version > 3) { loadUncommitedTransactions(source, cryptoContext); if (version >= 5) { initTransactionPool(); } } } else { resetCachedBalance(); } if (details && cache) { updateTransactionsBaseStatus(); } } void WalletSerializer::loadWalletV1(Common::IInputStream& source, const std::string& password) { CryptoNote::CryptoContext cryptoContext; CryptoNote::BinaryInputStreamSerializer encrypted(source); encrypted(cryptoContext.iv, "iv"); generateKey(password, cryptoContext.key); std::string cipher; encrypted(cipher, "data"); std::string plain = decrypt(cipher, cryptoContext); MemoryInputStream decryptedStream(plain.data(), plain.size()); CryptoNote::BinaryInputStreamSerializer serializer(decryptedStream); loadWalletV1Keys(serializer); checkKeys(); subscribeWallets(); bool detailsSaved; serializer(detailsSaved, "has_details"); if (detailsSaved) { loadWalletV1Details(serializer); } } void WalletSerializer::loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer) { CryptoNote::KeysStorage keys; keys.serialize(serializer, "keys"); m_viewPublicKey = keys.viewPublicKey; m_viewSecretKey = keys.viewSecretKey; WalletRecord wallet; wallet.spendPublicKey = keys.spendPublicKey; wallet.spendSecretKey = keys.spendSecretKey; wallet.actualBalance = 0; wallet.pendingBalance = 0; wallet.creationTimestamp = static_cast(keys.creationTimestamp); m_walletsContainer.get().push_back(wallet); } void WalletSerializer::loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer) { std::vector txs; std::vector trs; serializer(txs, "transactions"); serializer(trs, "transfers"); addWalletV1Details(txs, trs); } uint32_t WalletSerializer::loadVersion(Common::IInputStream& source) { CryptoNote::BinaryInputStreamSerializer s(source); uint32_t version = std::numeric_limits::max(); s(version, "version"); return version; } void WalletSerializer::loadIv(Common::IInputStream& source, Crypto::chacha8_iv& iv) { CryptoNote::BinaryInputStreamSerializer s(source); s.binary(static_cast(&iv.data), sizeof(iv.data), "chacha_iv"); } void WalletSerializer::generateKey(const std::string& password, Crypto::chacha8_key& key) { Crypto::cn_context context; Crypto::generate_chacha8_key(context, password, key); } void WalletSerializer::loadKeys(Common::IInputStream& source, CryptoContext& cryptoContext) { loadPublicKey(source, cryptoContext); loadSecretKey(source, cryptoContext); } void WalletSerializer::loadPublicKey(Common::IInputStream& source, CryptoContext& cryptoContext) { deserializeEncrypted(m_viewPublicKey, "public_key", cryptoContext, source); cryptoContext.incIv(); } void WalletSerializer::loadSecretKey(Common::IInputStream& source, CryptoContext& cryptoContext) { deserializeEncrypted(m_viewSecretKey, "secret_key", cryptoContext, source); cryptoContext.incIv(); } void WalletSerializer::checkKeys() { throwIfKeysMissmatch(m_viewSecretKey, m_viewPublicKey); } void WalletSerializer::loadFlags(bool& details, bool& cache, Common::IInputStream& source, CryptoContext& cryptoContext) { deserializeEncrypted(details, "details", cryptoContext, source); cryptoContext.incIv(); deserializeEncrypted(cache, "cache", cryptoContext, source); cryptoContext.incIv(); } void WalletSerializer::loadWallets(Common::IInputStream& source, CryptoContext& cryptoContext) { auto& index = m_walletsContainer.get(); uint64_t count = 0; deserializeEncrypted(count, "wallets_count", cryptoContext, source); cryptoContext.incIv(); bool isTrackingMode; for (uint64_t i = 0; i < count; ++i) { WalletRecordDto dto; deserializeEncrypted(dto, "", cryptoContext, source); cryptoContext.incIv(); if (i == 0) { isTrackingMode = dto.spendSecretKey == NULL_SECRET_KEY; } else if ((isTrackingMode && dto.spendSecretKey != NULL_SECRET_KEY) || (!isTrackingMode && dto.spendSecretKey == NULL_SECRET_KEY)) { throw std::system_error(make_error_code(error::BAD_ADDRESS), "All addresses must be whether tracking or not"); } if (dto.spendSecretKey != NULL_SECRET_KEY) { Crypto::PublicKey restoredPublicKey; bool r = Crypto::secret_key_to_public_key(dto.spendSecretKey, restoredPublicKey); if (!r || dto.spendPublicKey != restoredPublicKey) { throw std::system_error(make_error_code(error::WRONG_PASSWORD), "Restored spend public key doesn't correspond to secret key"); } } else { if (!Crypto::check_key(dto.spendPublicKey)) { throw std::system_error(make_error_code(error::WRONG_PASSWORD), "Public spend key is incorrect"); } } WalletRecord wallet; wallet.spendPublicKey = dto.spendPublicKey; wallet.spendSecretKey = dto.spendSecretKey; wallet.actualBalance = dto.actualBalance; wallet.pendingBalance = dto.pendingBalance; wallet.creationTimestamp = static_cast(dto.creationTimestamp); wallet.container = reinterpret_cast(i); //dirty hack. container field must be unique index.push_back(wallet); } } void WalletSerializer::subscribeWallets() { auto& index = m_walletsContainer.get(); for (auto it = index.begin(); it != index.end(); ++it) { const auto& wallet = *it; AccountSubscription sub; sub.keys.address.viewPublicKey = m_viewPublicKey; sub.keys.address.spendPublicKey = wallet.spendPublicKey; sub.keys.viewSecretKey = m_viewSecretKey; sub.keys.spendSecretKey = wallet.spendSecretKey; sub.transactionSpendableAge = m_transactionSoftLockTime; sub.syncStart.height = 0; sub.syncStart.timestamp = std::max(static_cast(wallet.creationTimestamp), ACCOUNT_CREATE_TIME_ACCURACY) - ACCOUNT_CREATE_TIME_ACCURACY; auto& subscription = m_synchronizer.addSubscription(sub); bool r = index.modify(it, [&subscription] (WalletRecord& rec) { rec.container = &subscription.getContainer(); }); assert(r); subscription.addObserver(&m_transfersObserver); } } void WalletSerializer::loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext) { deserializeEncrypted(m_actualBalance, "actual_balance", cryptoContext, source); cryptoContext.incIv(); deserializeEncrypted(m_pendingBalance, "pending_balance", cryptoContext, source); cryptoContext.incIv(); } void WalletSerializer::loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext) { std::string deciphered; deserializeEncrypted(deciphered, "transfers_synchronizer", cryptoContext, source); cryptoContext.incIv(); std::stringstream stream(deciphered); deciphered.clear(); m_synchronizer.load(stream); } void WalletSerializer::loadObsoleteSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) { uint64_t count = 0; deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source); cryptoContext.incIv(); for (uint64_t i = 0; i < count; ++i) { ObsoleteSpentOutputDto dto; deserializeEncrypted(dto, "", cryptoContext, source); cryptoContext.incIv(); } } void WalletSerializer::loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext) { auto& index = m_unlockTransactions.get(); auto& walletsIndex = m_walletsContainer.get(); const uint64_t walletsSize = walletsIndex.size(); uint64_t jobsCount = 0; deserializeEncrypted(jobsCount, "unlock_transactions_jobs_count", cryptoContext, source); cryptoContext.incIv(); for (uint64_t i = 0; i < jobsCount; ++i) { UnlockTransactionJobDto dto; deserializeEncrypted(dto, "", cryptoContext, source); cryptoContext.incIv(); assert(dto.walletIndex < walletsSize); UnlockTransactionJob job; job.blockHeight = dto.blockHeight; job.transactionHash = dto.transactionHash; job.container = walletsIndex[dto.walletIndex].container; index.insert(std::move(job)); } } void WalletSerializer::loadObsoleteChange(Common::IInputStream& source, CryptoContext& cryptoContext) { uint64_t count = 0; deserializeEncrypted(count, "changes_count", cryptoContext, source); cryptoContext.incIv(); for (uint64_t i = 0; i < count; i++) { ObsoleteChangeDto dto; deserializeEncrypted(dto, "", cryptoContext, source); cryptoContext.incIv(); } } void WalletSerializer::loadUncommitedTransactions(Common::IInputStream& source, CryptoContext& cryptoContext) { deserializeEncrypted(uncommitedTransactions, "uncommited_transactions", cryptoContext, source); } void WalletSerializer::initTransactionPool() { std::unordered_set uncommitedTransactionsSet; std::transform(uncommitedTransactions.begin(), uncommitedTransactions.end(), std::inserter(uncommitedTransactionsSet, uncommitedTransactionsSet.end()), [](const UncommitedTransactions::value_type& pair) { return getObjectHash(pair.second); }); m_synchronizer.initTransactionPool(uncommitedTransactionsSet); } void WalletSerializer::resetCachedBalance() { for (auto it = m_walletsContainer.begin(); it != m_walletsContainer.end(); ++it) { m_walletsContainer.modify(it, [](WalletRecord& wallet) { wallet.actualBalance = 0; wallet.pendingBalance = 0; }); } } // can't do it in loadTransactions, TransfersContainer is not yet loaded void WalletSerializer::updateTransactionsBaseStatus() { auto& transactions = m_transactions.get(); auto begin = std::begin(transactions); auto end = std::end(transactions); for (; begin != end; ++begin) { transactions.modify(begin, [this](WalletTransaction& tx) { auto& wallets = m_walletsContainer.get(); TransactionInformation txInfo; auto it = std::find_if(std::begin(wallets), std::end(wallets), [&](const WalletRecord& rec) { assert(rec.container != nullptr); return rec.container->getTransactionInformation(tx.hash, txInfo); }); tx.isBase = it != std::end(wallets) && txInfo.totalAmountIn == 0; }); } } void WalletSerializer::updateTransfersSign() { auto it = m_transfers.begin(); while (it != m_transfers.end()) { if (it->second.amount < 0) { it->second.amount = -it->second.amount; ++it; } else { it = m_transfers.erase(it); } } } void WalletSerializer::loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext) { uint64_t count = 0; deserializeEncrypted(count, "transactions_count", cryptoContext, source); cryptoContext.incIv(); m_transactions.get().reserve(count); for (uint64_t i = 0; i < count; ++i) { WalletTransactionDto dto; deserializeEncrypted(dto, "", cryptoContext, source); cryptoContext.incIv(); WalletTransaction tx; tx.state = dto.state; tx.timestamp = dto.timestamp; tx.blockHeight = dto.blockHeight; tx.hash = dto.hash; tx.totalAmount = dto.totalAmount; tx.fee = dto.fee; tx.creationTime = dto.creationTime; tx.unlockTime = dto.unlockTime; tx.extra = dto.extra; tx.isBase = false; m_transactions.get().push_back(std::move(tx)); } } void WalletSerializer::loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version) { uint64_t count = 0; deserializeEncrypted(count, "transfers_count", cryptoContext, source); cryptoContext.incIv(); m_transfers.reserve(count); for (uint64_t i = 0; i < count; ++i) { uint64_t txId = 0; deserializeEncrypted(txId, "transaction_id", cryptoContext, source); cryptoContext.incIv(); WalletTransferDto dto(version); deserializeEncrypted(dto, "transfer", cryptoContext, source); cryptoContext.incIv(); WalletTransfer tr; tr.address = dto.address; tr.amount = dto.amount; if (version > 2) { tr.type = static_cast(dto.type); } else { tr.type = WalletTransferType::USUAL; } m_transfers.push_back(std::make_pair(txId, tr)); } } void WalletSerializer::addWalletV1Details(const std::vector& txs, const std::vector& trs) { size_t txId = 0; m_transfers.reserve(trs.size()); for (const auto& tx: txs) { WalletTransaction mtx = convert(tx); m_transactions.get().push_back(std::move(mtx)); if (tx.firstTransferId != WALLET_LEGACY_INVALID_TRANSFER_ID && tx.transferCount != 0) { size_t firstTr = tx.firstTransferId; size_t lastTr = firstTr + tx.transferCount; if (lastTr > trs.size()) { throw std::system_error(make_error_code(error::INTERNAL_WALLET_ERROR)); } for (; firstTr < lastTr; firstTr++) { WalletTransfer tr = convert(trs[firstTr]); m_transfers.push_back(std::make_pair(txId, tr)); } } txId++; } } } //namespace CryptoNote