// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see .
#include "WalletSerialization.h"
#include
#include
#include
#include "Common/MemoryInputStream.h"
#include "Common/StdInputStream.h"
#include "Common/StdOutputStream.h"
#include "CryptoNoteCore/CryptoNoteSerialization.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 SpentOutputDto {
uint64_t amount;
Hash transactionHash;
uint32_t outputInTransaction;
uint64_t walletIndex;
Crypto::Hash spendingTransactionHash;
};
//DO NOT CHANGE IT
struct ChangeDto {
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() {}
WalletTransferDto(const CryptoNote::WalletTransfer& tr) {
address = tr.address;
amount = tr.amount;
}
std::string address;
uint64_t amount;
};
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(SpentOutputDto& 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(ChangeDto& 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");
}
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 = 2;
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,
SpentOutputs& spentOutputs,
UnlockTransactionJobs& unlockTransactions,
TransactionChanges& change,
WalletTransactions& transactions,
WalletTransfers& transfers,
uint32_t transactionSoftLockTime
) :
m_transfersObserver(transfersObserver),
m_viewPublicKey(viewPublicKey),
m_viewSecretKey(viewSecretKey),
m_actualBalance(actualBalance),
m_pendingBalance(pendingBalance),
m_walletsContainer(walletsContainer),
m_synchronizer(synchronizer),
m_spentOutputs(spentOutputs),
m_unlockTransactions(unlockTransactions),
m_change(change),
m_transactions(transactions),
m_transfers(transfers),
m_transactionSoftLockTime(transactionSoftLockTime)
{ }
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);
saveSpentOutputs(destination, cryptoContext);
saveUnlockTransactionsJobs(destination, cryptoContext);
saveChange(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::saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
auto& index = m_spentOutputs.get();
uint64_t outsCount = index.size();
serializeEncrypted(outsCount, "spent_outputs_count", cryptoContext, destination);
cryptoContext.incIv();
for (const auto& o: index) {
auto it = m_walletsContainer.get().iterator_to(*o.wallet);
uint64_t walletIndex = std::distance(m_walletsContainer.get().begin(), it);
SpentOutputDto dto;
dto.amount = o.amount;
dto.transactionHash = o.transactionHash;
dto.outputInTransaction = o.outputInTransaction;
dto.walletIndex = walletIndex;
dto.spendingTransactionHash = o.spendingTransactionHash;
serializeEncrypted(dto, "", 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::saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
uint64_t count = m_change.size();
serializeEncrypted(count, "changes_count", cryptoContext, destination);
cryptoContext.incIv();
for (const auto& kv: m_change) {
ChangeDto dto;
dto.txHash = kv.first;
dto.amount = kv.second;
serializeEncrypted(dto, "", cryptoContext, destination);
cryptoContext.incIv();
}
}
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);
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) {
loadCurrentVersion(source, password);
} else if (version == 1) {
loadWalletV1(source, password);
} else {
throw std::system_error(make_error_code(error::WRONG_VERSION));
}
s.endObject();
}
void WalletSerializer::loadCurrentVersion(Common::IInputStream& source, const std::string& password) {
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);
}
if (cache) {
loadBalances(source, cryptoContext);
loadTransfersSynchronizer(source, cryptoContext);
loadSpentOutputs(source, cryptoContext);
loadUnlockTransactionsJobs(source, cryptoContext);
loadChange(source, cryptoContext);
}
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 = static_cast(wallet.creationTimestamp) - (60 * 60 * 24);
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::loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) {
auto& index = m_spentOutputs.get();
auto& walletsIndex = m_walletsContainer.get();
const uint64_t walletsSize = walletsIndex.size();
uint64_t count = 0;
deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source);
cryptoContext.incIv();
for (uint64_t i = 0; i < count; ++i) {
SpentOutputDto dto;
deserializeEncrypted(dto, "", cryptoContext, source);
cryptoContext.incIv();
assert(dto.walletIndex < walletsSize);
SpentOutput output;
output.amount = dto.amount;
output.transactionHash = dto.transactionHash;
output.outputInTransaction = dto.outputInTransaction;
output.spendingTransactionHash = dto.spendingTransactionHash;
output.wallet = &walletsIndex[dto.walletIndex];
index.insert(std::move(output));
}
}
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::loadChange(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++) {
ChangeDto dto;
deserializeEncrypted(dto, "", cryptoContext, source);
cryptoContext.incIv();
m_change[dto.txHash] = dto.amount;
}
}
// 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) {
int64_t id = 0;
assert(rec.container != nullptr);
return rec.container->getTransactionInformation(tx.hash, txInfo, id);
});
tx.isBase = it != std::end(wallets) && txInfo.totalAmountIn == 0;
});
}
}
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) {
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;
deserializeEncrypted(dto, "transfer", cryptoContext, source);
cryptoContext.incIv();
WalletTransfer tr;
tr.address = dto.address;
tr.amount = dto.amount;
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