2016-01-18 15:33:29 +00:00
|
|
|
// Copyright (c) 2011-2016 The Cryptonote developers
|
2015-09-18 11:55:31 +00:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
#include "WalletService.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <future>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <sstream>
|
|
|
|
#include <unordered_set>
|
|
|
|
|
2015-08-05 13:09:05 +00:00
|
|
|
#include <boost/filesystem/operations.hpp>
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
#include <System/Timer.h>
|
|
|
|
#include <System/InterruptedException.h>
|
|
|
|
#include "Common/Util.h"
|
|
|
|
|
|
|
|
#include "crypto/crypto.h"
|
2015-12-09 13:19:03 +00:00
|
|
|
#include "CryptoNote.h"
|
2015-07-30 15:22:07 +00:00
|
|
|
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
|
|
|
|
#include "CryptoNoteCore/CryptoNoteBasicImpl.h"
|
|
|
|
#include "CryptoNoteCore/TransactionExtra.h"
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
#include <System/EventLock.h>
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
#include "PaymentServiceJsonRpcMessages.h"
|
|
|
|
#include "WalletFactory.h"
|
|
|
|
#include "NodeFactory.h"
|
|
|
|
|
|
|
|
#include "Wallet/LegacyKeysImporter.h"
|
2015-12-09 13:19:03 +00:00
|
|
|
#include "Wallet/WalletErrors.h"
|
|
|
|
#include "Wallet/WalletUtils.h"
|
|
|
|
#include "WalletServiceErrorCategory.h"
|
2015-07-30 15:22:07 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
namespace PaymentService {
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
namespace {
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
bool checkPaymentId(const std::string& paymentId) {
|
|
|
|
if (paymentId.size() != 64) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::all_of(paymentId.begin(), paymentId.end(), [] (const char c) {
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c >= 'a' && c <= 'f') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c >= 'A' && c <= 'F') {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
Crypto::Hash parsePaymentId(const std::string& paymentIdStr) {
|
|
|
|
if (!checkPaymentId(paymentIdStr)) {
|
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_PAYMENT_ID_FORMAT));
|
|
|
|
}
|
|
|
|
|
|
|
|
Crypto::Hash paymentId;
|
|
|
|
bool r = Common::podFromHex(paymentIdStr, paymentId);
|
|
|
|
assert(r);
|
|
|
|
|
|
|
|
return paymentId;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool getPaymentIdFromExtra(const std::string& binaryString, Crypto::Hash& paymentId) {
|
|
|
|
return CryptoNote::getPaymentIdFromTxExtra(Common::asBinaryArray(binaryString), paymentId);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getPaymentIdStringFromExtra(const std::string& binaryString) {
|
|
|
|
Crypto::Hash paymentId;
|
|
|
|
|
|
|
|
if (!getPaymentIdFromExtra(binaryString, paymentId)) {
|
|
|
|
return std::string();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Common::podToHex(paymentId);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TransactionsInBlockInfoFilter {
|
|
|
|
TransactionsInBlockInfoFilter(const std::vector<std::string>& addressesVec, const std::string& paymentIdStr) {
|
|
|
|
addresses.insert(addressesVec.begin(), addressesVec.end());
|
|
|
|
|
|
|
|
if (!paymentIdStr.empty()) {
|
|
|
|
paymentId = parsePaymentId(paymentIdStr);
|
|
|
|
havePaymentId = true;
|
|
|
|
} else {
|
|
|
|
havePaymentId = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkTransaction(const CryptoNote::WalletTransactionWithTransfers& transaction) const {
|
|
|
|
if (havePaymentId) {
|
|
|
|
Crypto::Hash transactionPaymentId;
|
|
|
|
if (!getPaymentIdFromExtra(transaction.transaction.extra, transactionPaymentId)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (paymentId != transactionPaymentId) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addresses.empty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool haveAddress = false;
|
|
|
|
for (const CryptoNote::WalletTransfer& transfer: transaction.transfers) {
|
|
|
|
if (addresses.find(transfer.address) != addresses.end()) {
|
|
|
|
haveAddress = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return haveAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unordered_set<std::string> addresses;
|
|
|
|
bool havePaymentId = false;
|
|
|
|
Crypto::Hash paymentId;
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void addPaymentIdToExtra(const std::string& paymentId, std::string& extra) {
|
|
|
|
std::vector<uint8_t> extraVector;
|
|
|
|
if (!CryptoNote::createTxExtraWithPaymentId(paymentId, extraVector)) {
|
|
|
|
throw std::runtime_error("Couldn't add payment id to extra");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::copy(extraVector.begin(), extraVector.end(), std::back_inserter(extra));
|
|
|
|
}
|
|
|
|
|
|
|
|
void validatePaymentId(const std::string& paymentId, Logging::LoggerRef logger) {
|
|
|
|
if (!checkPaymentId(paymentId)) {
|
|
|
|
logger(Logging::WARNING) << "Can't validate payment id: " << paymentId;
|
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_PAYMENT_ID_FORMAT));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
bool createOutputBinaryFile(const std::string& filename, std::fstream& file) {
|
|
|
|
file.open(filename.c_str(), std::fstream::in | std::fstream::out | std::ofstream::binary);
|
|
|
|
if (file) {
|
|
|
|
file.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
file.open(filename.c_str(), std::fstream::out | std::fstream::binary);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string createTemporaryFile(const std::string& path, std::fstream& tempFile) {
|
|
|
|
bool created = false;
|
|
|
|
std::string temporaryName;
|
|
|
|
|
|
|
|
for (size_t i = 1; i < 100; i++) {
|
|
|
|
temporaryName = path + "." + std::to_string(i++);
|
|
|
|
|
|
|
|
if (createOutputBinaryFile(temporaryName, tempFile)) {
|
|
|
|
created = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!created) {
|
|
|
|
throw std::runtime_error("Couldn't create temporary file: " + temporaryName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return temporaryName;
|
|
|
|
}
|
|
|
|
|
|
|
|
//returns true on success
|
|
|
|
bool deleteFile(const std::string& filename) {
|
2015-08-05 13:09:05 +00:00
|
|
|
boost::system::error_code err;
|
|
|
|
return boost::filesystem::remove(filename, err) && !err;
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void replaceWalletFiles(const std::string &path, const std::string &tempFilePath) {
|
2015-07-30 15:22:07 +00:00
|
|
|
Tools::replace_file(tempFilePath, path);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
Crypto::Hash parseHash(const std::string& hashString, Logging::LoggerRef logger) {
|
|
|
|
Crypto::Hash hash;
|
|
|
|
|
|
|
|
if (!Common::podFromHex(hashString, hash)) {
|
|
|
|
logger(Logging::WARNING) << "Can't parse hash string " << hashString;
|
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_HASH_FORMAT));
|
|
|
|
}
|
|
|
|
|
|
|
|
return hash;
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> filterTransactions(
|
|
|
|
const std::vector<CryptoNote::TransactionsInBlockInfo>& blocks,
|
|
|
|
const TransactionsInBlockInfoFilter& filter) {
|
|
|
|
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> result;
|
|
|
|
|
|
|
|
for (const auto& block: blocks) {
|
|
|
|
CryptoNote::TransactionsInBlockInfo item;
|
|
|
|
item.blockHash = block.blockHash;
|
|
|
|
|
|
|
|
for (const auto& transaction: block.transactions) {
|
|
|
|
if (transaction.transaction.state != CryptoNote::WalletTransactionState::DELETED && filter.checkTransaction(transaction)) {
|
|
|
|
item.transactions.push_back(transaction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
result.push_back(std::move(item));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
PaymentService::TransactionRpcInfo convertTransactionWithTransfersToTransactionRpcInfo(
|
|
|
|
const CryptoNote::WalletTransactionWithTransfers& transactionWithTransfers) {
|
|
|
|
|
|
|
|
PaymentService::TransactionRpcInfo transactionInfo;
|
|
|
|
|
|
|
|
transactionInfo.state = static_cast<uint8_t>(transactionWithTransfers.transaction.state);
|
|
|
|
transactionInfo.transactionHash = Common::podToHex(transactionWithTransfers.transaction.hash);
|
|
|
|
transactionInfo.blockIndex = transactionWithTransfers.transaction.blockHeight;
|
|
|
|
transactionInfo.timestamp = transactionWithTransfers.transaction.timestamp;
|
|
|
|
transactionInfo.isBase = transactionWithTransfers.transaction.isBase;
|
|
|
|
transactionInfo.unlockTime = transactionWithTransfers.transaction.unlockTime;
|
|
|
|
transactionInfo.amount = transactionWithTransfers.transaction.totalAmount;
|
|
|
|
transactionInfo.fee = transactionWithTransfers.transaction.fee;
|
|
|
|
transactionInfo.extra = Common::toHex(transactionWithTransfers.transaction.extra.data(), transactionWithTransfers.transaction.extra.size());
|
|
|
|
transactionInfo.paymentId = getPaymentIdStringFromExtra(transactionWithTransfers.transaction.extra);
|
|
|
|
|
|
|
|
for (const CryptoNote::WalletTransfer& transfer: transactionWithTransfers.transfers) {
|
|
|
|
PaymentService::TransferRpcInfo rpcTransfer;
|
|
|
|
rpcTransfer.address = transfer.address;
|
|
|
|
rpcTransfer.amount = transfer.amount;
|
|
|
|
rpcTransfer.type = static_cast<uint8_t>(transfer.type);
|
|
|
|
|
|
|
|
transactionInfo.transfers.push_back(std::move(rpcTransfer));
|
|
|
|
}
|
|
|
|
|
|
|
|
return transactionInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<PaymentService::TransactionsInBlockRpcInfo> convertTransactionsInBlockInfoToTransactionsInBlockRpcInfo(
|
|
|
|
const std::vector<CryptoNote::TransactionsInBlockInfo>& blocks) {
|
|
|
|
|
|
|
|
std::vector<PaymentService::TransactionsInBlockRpcInfo> rpcBlocks;
|
|
|
|
rpcBlocks.reserve(blocks.size());
|
|
|
|
for (const auto& block: blocks) {
|
|
|
|
PaymentService::TransactionsInBlockRpcInfo rpcBlock;
|
|
|
|
rpcBlock.blockHash = Common::podToHex(block.blockHash);
|
|
|
|
|
|
|
|
for (const CryptoNote::WalletTransactionWithTransfers& transactionWithTransfers: block.transactions) {
|
|
|
|
PaymentService::TransactionRpcInfo transactionInfo = convertTransactionWithTransfersToTransactionRpcInfo(transactionWithTransfers);
|
|
|
|
rpcBlock.transactions.push_back(std::move(transactionInfo));
|
|
|
|
}
|
|
|
|
|
|
|
|
rpcBlocks.push_back(std::move(rpcBlock));
|
|
|
|
}
|
|
|
|
|
|
|
|
return rpcBlocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<PaymentService::TransactionHashesInBlockRpcInfo> convertTransactionsInBlockInfoToTransactionHashesInBlockRpcInfo(
|
|
|
|
const std::vector<CryptoNote::TransactionsInBlockInfo>& blocks) {
|
|
|
|
|
|
|
|
std::vector<PaymentService::TransactionHashesInBlockRpcInfo> transactionHashes;
|
|
|
|
transactionHashes.reserve(blocks.size());
|
|
|
|
for (const CryptoNote::TransactionsInBlockInfo& block: blocks) {
|
|
|
|
PaymentService::TransactionHashesInBlockRpcInfo item;
|
|
|
|
item.blockHash = Common::podToHex(block.blockHash);
|
|
|
|
|
|
|
|
for (const CryptoNote::WalletTransactionWithTransfers& transaction: block.transactions) {
|
|
|
|
item.transactionHashes.emplace_back(Common::podToHex(transaction.transaction.hash));
|
|
|
|
}
|
|
|
|
|
|
|
|
transactionHashes.push_back(std::move(item));
|
|
|
|
}
|
|
|
|
|
|
|
|
return transactionHashes;
|
|
|
|
}
|
|
|
|
|
|
|
|
void validateAddresses(const std::vector<std::string>& addresses, const CryptoNote::Currency& currency, Logging::LoggerRef logger) {
|
|
|
|
for (const auto& address: addresses) {
|
|
|
|
if (!CryptoNote::validateAddress(address, currency)) {
|
|
|
|
logger(Logging::WARNING) << "Can't validate address " << address;
|
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::BAD_ADDRESS));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> collectDestinationAddresses(const std::vector<PaymentService::WalletRpcOrder>& orders) {
|
|
|
|
std::vector<std::string> result;
|
|
|
|
|
|
|
|
result.reserve(orders.size());
|
|
|
|
for (const auto& order: orders) {
|
|
|
|
result.push_back(order.address);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CryptoNote::WalletOrder> convertWalletRpcOrdersToWalletOrders(const std::vector<PaymentService::WalletRpcOrder>& orders) {
|
|
|
|
std::vector<CryptoNote::WalletOrder> result;
|
|
|
|
result.reserve(orders.size());
|
|
|
|
|
|
|
|
for (const auto& order: orders) {
|
|
|
|
result.emplace_back(CryptoNote::WalletOrder {order.address, order.amount});
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
void createWalletFile(std::fstream& walletFile, const std::string& filename) {
|
2015-12-09 13:19:03 +00:00
|
|
|
boost::filesystem::path pathToWalletFile(filename);
|
|
|
|
boost::filesystem::path directory = pathToWalletFile.parent_path();
|
|
|
|
if (!directory.empty() && !Tools::directoryExists(directory.string())) {
|
|
|
|
throw std::runtime_error("Directory does not exist: " + directory.string());
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
walletFile.open(filename.c_str(), std::fstream::in | std::fstream::out | std::fstream::binary);
|
|
|
|
if (walletFile) {
|
|
|
|
walletFile.close();
|
|
|
|
throw std::runtime_error("Wallet file already exists");
|
|
|
|
}
|
|
|
|
|
|
|
|
walletFile.open(filename.c_str(), std::fstream::out);
|
|
|
|
walletFile.close();
|
|
|
|
|
|
|
|
walletFile.open(filename.c_str(), std::fstream::in | std::fstream::out | std::fstream::binary);
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
void saveWallet(CryptoNote::IWallet& wallet, std::fstream& walletFile, bool saveDetailed = true, bool saveCache = true) {
|
|
|
|
wallet.save(walletFile, saveDetailed, saveCache);
|
2015-05-27 12:08:46 +00:00
|
|
|
walletFile.flush();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
void secureSaveWallet(CryptoNote::IWallet& wallet, const std::string& path, bool saveDetailed = true, bool saveCache = true) {
|
2015-05-27 12:08:46 +00:00
|
|
|
std::fstream tempFile;
|
|
|
|
std::string tempFilePath = createTemporaryFile(path, tempFile);
|
|
|
|
|
|
|
|
try {
|
|
|
|
saveWallet(wallet, tempFile, saveDetailed, saveCache);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
deleteFile(tempFilePath);
|
|
|
|
tempFile.close();
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
tempFile.close();
|
|
|
|
|
|
|
|
replaceWalletFiles(path, tempFilePath);
|
|
|
|
}
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
void generateNewWallet(const CryptoNote::Currency ¤cy, const WalletConfiguration &conf, Logging::ILogger& logger, System::Dispatcher& dispatcher) {
|
2015-05-27 12:08:46 +00:00
|
|
|
Logging::LoggerRef log(logger, "generateNewWallet");
|
|
|
|
|
|
|
|
CryptoNote::INode* nodeStub = NodeFactory::createNodeStub();
|
|
|
|
std::unique_ptr<CryptoNote::INode> nodeGuard(nodeStub);
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
CryptoNote::IWallet* wallet = WalletFactory::createWallet(currency, *nodeStub, dispatcher);
|
2015-05-27 12:08:46 +00:00
|
|
|
std::unique_ptr<CryptoNote::IWallet> walletGuard(wallet);
|
|
|
|
|
|
|
|
log(Logging::INFO) << "Generating new wallet";
|
|
|
|
|
|
|
|
std::fstream walletFile;
|
|
|
|
createWalletFile(walletFile, conf.walletFile);
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
wallet->initialize(conf.walletPassword);
|
|
|
|
auto address = wallet->createAddress();
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
log(Logging::INFO) << "New wallet is generated. Address: " << address;
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
saveWallet(*wallet, walletFile, false, false);
|
2015-05-27 12:08:46 +00:00
|
|
|
log(Logging::INFO) << "Wallet is saved";
|
|
|
|
}
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
void importLegacyKeys(const std::string &legacyKeysFile, const WalletConfiguration &conf) {
|
2015-05-27 12:08:46 +00:00
|
|
|
std::stringstream archive;
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
CryptoNote::importLegacyKeys(legacyKeysFile, conf.walletPassword, archive);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
std::fstream walletFile;
|
|
|
|
createWalletFile(walletFile, conf.walletFile);
|
|
|
|
|
|
|
|
archive.flush();
|
|
|
|
walletFile << archive.rdbuf();
|
|
|
|
walletFile.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
WalletService::WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node,
|
2015-12-09 13:19:03 +00:00
|
|
|
CryptoNote::IWallet& wallet, const WalletConfiguration& conf, Logging::ILogger& logger) :
|
|
|
|
currency(currency),
|
|
|
|
wallet(wallet),
|
|
|
|
node(node),
|
2015-05-27 12:08:46 +00:00
|
|
|
config(conf),
|
|
|
|
inited(false),
|
2015-07-30 15:22:07 +00:00
|
|
|
logger(logger, "WalletService"),
|
|
|
|
dispatcher(sys),
|
2015-12-09 13:19:03 +00:00
|
|
|
readyEvent(dispatcher),
|
2015-07-30 15:22:07 +00:00
|
|
|
refreshContext(dispatcher)
|
2015-05-27 12:08:46 +00:00
|
|
|
{
|
2015-12-09 13:19:03 +00:00
|
|
|
readyEvent.set();
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WalletService::~WalletService() {
|
2015-12-09 13:19:03 +00:00
|
|
|
if (inited) {
|
|
|
|
wallet.stop();
|
|
|
|
refreshContext.wait();
|
|
|
|
wallet.shutdown();
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletService::init() {
|
|
|
|
loadWallet();
|
2015-12-09 13:19:03 +00:00
|
|
|
loadTransactionIdIndex();
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
refreshContext.spawn([this] { refresh(); });
|
2015-05-27 12:08:46 +00:00
|
|
|
|
|
|
|
inited = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletService::saveWallet() {
|
2015-12-09 13:19:03 +00:00
|
|
|
PaymentService::secureSaveWallet(wallet, config.walletFile, true, true);
|
2015-05-27 12:08:46 +00:00
|
|
|
logger(Logging::INFO) << "Wallet is saved";
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletService::loadWallet() {
|
|
|
|
std::ifstream inputWalletFile;
|
|
|
|
inputWalletFile.open(config.walletFile.c_str(), std::fstream::in | std::fstream::binary);
|
|
|
|
if (!inputWalletFile) {
|
|
|
|
throw std::runtime_error("Couldn't open wallet file");
|
|
|
|
}
|
|
|
|
|
|
|
|
logger(Logging::INFO) << "Loading wallet";
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
wallet.load(inputWalletFile, config.walletPassword);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
logger(Logging::INFO) << "Wallet loading is finished.";
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
void WalletService::loadTransactionIdIndex() {
|
|
|
|
transactionIdIndex.clear();
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
for (size_t i = 0; i < wallet.getTransactionCount(); ++i) {
|
|
|
|
transactionIdIndex.emplace(Common::podToHex(wallet.getTransaction(i).hash), i);
|
|
|
|
}
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::resetWallet() {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::INFO) << "Reseting wallet";
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
if (!inited) {
|
|
|
|
logger(Logging::WARNING) << "Reset impossible: Wallet Service is not initialized";
|
|
|
|
return make_error_code(CryptoNote::error::NOT_INITIALIZED);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
reset();
|
|
|
|
logger(Logging::INFO) << "Wallet has been reset";
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while reseting wallet: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while reseting wallet: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::replaceWithNewWallet(const std::string& viewSecretKeyText) {
|
2015-05-27 12:08:46 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
Crypto::SecretKey viewSecretKey;
|
|
|
|
if (!Common::podFromHex(viewSecretKeyText, viewSecretKey)) {
|
|
|
|
logger(Logging::WARNING) << "Cannot restore view secret key: " << viewSecretKeyText;
|
|
|
|
return make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_KEY_FORMAT);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
Crypto::PublicKey viewPublicKey;
|
|
|
|
if (!Crypto::secret_key_to_public_key(viewSecretKey, viewPublicKey)) {
|
|
|
|
logger(Logging::WARNING) << "Cannot derive view public key, wrong secret key: " << viewSecretKeyText;
|
|
|
|
return make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_KEY_FORMAT);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
replaceWithNewWallet(viewSecretKey);
|
|
|
|
logger(Logging::INFO) << "The container has been replaced";
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while replacing container: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
2015-12-09 13:19:03 +00:00
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while replacing container: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::createAddress(const std::string& spendSecretKeyText, std::string& address) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Creating address";
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
Crypto::SecretKey secretKey;
|
|
|
|
if (!Common::podFromHex(spendSecretKeyText, secretKey)) {
|
|
|
|
logger(Logging::WARNING) << "Wrong key format: " << spendSecretKeyText;
|
|
|
|
return make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_KEY_FORMAT);
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
address = wallet.createAddress(secretKey);
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while creating address: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Created address " << address;
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::createAddress(std::string& address) {
|
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Creating address";
|
|
|
|
|
|
|
|
address = wallet.createAddress();
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while creating address: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Created address " << address;
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::createTrackingAddress(const std::string& spendPublicKeyText, std::string& address) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Creating tracking address";
|
2015-07-30 15:22:07 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
Crypto::PublicKey publicKey;
|
|
|
|
if (!Common::podFromHex(spendPublicKeyText, publicKey)) {
|
|
|
|
logger(Logging::WARNING) << "Wrong key format: " << spendPublicKeyText;
|
|
|
|
return make_error_code(CryptoNote::error::WalletServiceErrorCode::WRONG_KEY_FORMAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
address = wallet.createAddress(publicKey);
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while creating tracking address: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Created address " << address;
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::deleteAddress(const std::string& address) {
|
2015-07-30 15:22:07 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Delete address request came";
|
|
|
|
wallet.deleteAddress(address);
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while deleting address: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Address " << address << " successfully deleted";
|
2015-07-30 15:22:07 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getSpendkeys(const std::string& address, std::string& publicSpendKeyText, std::string& secretSpendKeyText) {
|
2015-07-30 15:22:07 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
CryptoNote::KeyPair key = wallet.getAddressSpendKey(address);
|
|
|
|
|
|
|
|
publicSpendKeyText = Common::podToHex(key.publicKey);
|
|
|
|
secretSpendKeyText = Common::podToHex(key.secretKey);
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting spend key: " << x.what();
|
2015-07-30 15:22:07 +00:00
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getBalance(const std::string& address, uint64_t& availableBalance, uint64_t& lockedAmount) {
|
2015-07-30 15:22:07 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
logger(Logging::DEBUGGING) << "Getting balance for address " << address;
|
|
|
|
|
|
|
|
availableBalance = wallet.getActualBalance(address);
|
|
|
|
lockedAmount = wallet.getPendingBalance(address);
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting balance: " << x.what();
|
2015-07-30 15:22:07 +00:00
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << address << " actual balance: " << availableBalance << ", pending: " << lockedAmount;
|
2015-07-30 15:22:07 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getBalance(uint64_t& availableBalance, uint64_t& lockedAmount) {
|
2015-05-27 12:08:46 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
logger(Logging::DEBUGGING) << "Getting wallet balance";
|
|
|
|
|
|
|
|
availableBalance = wallet.getActualBalance();
|
|
|
|
lockedAmount = wallet.getPendingBalance();
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting balance: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Wallet actual balance: " << availableBalance << ", pending: " << lockedAmount;
|
2015-05-27 12:08:46 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getBlockHashes(uint32_t firstBlockIndex, uint32_t blockCount, std::vector<std::string>& blockHashes) {
|
2015-05-27 12:08:46 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
std::vector<Crypto::Hash> hashes = wallet.getBlockHashes(firstBlockIndex, blockCount);
|
|
|
|
|
|
|
|
blockHashes.reserve(hashes.size());
|
|
|
|
for (const auto& hash: hashes) {
|
|
|
|
blockHashes.push_back(Common::podToHex(hash));
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting block hashes: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getViewKey(std::string& viewSecretKey) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
CryptoNote::KeyPair viewKey = wallet.getViewKey();
|
|
|
|
viewSecretKey = Common::podToHex(viewKey.secretKey);
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting view key: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::getTransactionHashes(const std::vector<std::string>& addresses, const std::string& blockHashString,
|
|
|
|
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionHashesInBlockRpcInfo>& transactionHashes) {
|
2015-05-27 12:08:46 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
validateAddresses(addresses, currency, logger);
|
|
|
|
|
|
|
|
if (!paymentId.empty()) {
|
|
|
|
validatePaymentId(paymentId, logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
TransactionsInBlockInfoFilter transactionFilter(addresses, paymentId);
|
|
|
|
Crypto::Hash blockHash = parseHash(blockHashString, logger);
|
|
|
|
|
|
|
|
transactionHashes = getRpcTransactionHashes(blockHash, blockCount, transactionFilter);
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
2015-12-09 13:19:03 +00:00
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getTransactionHashes(const std::vector<std::string>& addresses, uint32_t firstBlockIndex,
|
|
|
|
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionHashesInBlockRpcInfo>& transactionHashes) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
validateAddresses(addresses, currency, logger);
|
|
|
|
|
|
|
|
if (!paymentId.empty()) {
|
|
|
|
validatePaymentId(paymentId, logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
TransactionsInBlockInfoFilter transactionFilter(addresses, paymentId);
|
|
|
|
transactionHashes = getRpcTransactionHashes(firstBlockIndex, blockCount, transactionFilter);
|
|
|
|
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
}
|
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getTransactions(const std::vector<std::string>& addresses, const std::string& blockHashString,
|
|
|
|
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionsInBlockRpcInfo>& transactions) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
validateAddresses(addresses, currency, logger);
|
|
|
|
|
|
|
|
if (!paymentId.empty()) {
|
|
|
|
validatePaymentId(paymentId, logger);
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
TransactionsInBlockInfoFilter transactionFilter(addresses, paymentId);
|
|
|
|
|
|
|
|
Crypto::Hash blockHash = parseHash(blockHashString, logger);
|
|
|
|
|
|
|
|
transactions = getRpcTransactions(blockHash, blockCount, transactionFilter);
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::getTransactions(const std::vector<std::string>& addresses, uint32_t firstBlockIndex,
|
|
|
|
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionsInBlockRpcInfo>& transactions) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
validateAddresses(addresses, currency, logger);
|
|
|
|
|
|
|
|
if (!paymentId.empty()) {
|
|
|
|
validatePaymentId(paymentId, logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
TransactionsInBlockInfoFilter transactionFilter(addresses, paymentId);
|
|
|
|
|
|
|
|
transactions = getRpcTransactions(firstBlockIndex, blockCount, transactionFilter);
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transactions: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
}
|
2015-07-30 15:22:07 +00:00
|
|
|
|
2015-05-27 12:08:46 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getTransaction(const std::string& transactionHash, TransactionRpcInfo& transaction) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
Crypto::Hash hash = parseHash(transactionHash, logger);
|
|
|
|
|
|
|
|
CryptoNote::WalletTransactionWithTransfers transactionWithTransfers = wallet.getTransaction(hash);
|
|
|
|
|
|
|
|
if (transactionWithTransfers.transaction.state == CryptoNote::WalletTransactionState::DELETED) {
|
|
|
|
logger(Logging::WARNING) << "Transaction " << transactionHash << " is deleted";
|
|
|
|
return make_error_code(CryptoNote::error::OBJECT_NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
transaction = convertTransactionWithTransfersToTransactionRpcInfo(transactionWithTransfers);
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transaction: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting transaction: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-07-30 15:22:07 +00:00
|
|
|
}
|
2015-12-09 13:19:03 +00:00
|
|
|
|
|
|
|
return std::error_code();
|
2015-07-30 15:22:07 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getAddresses(std::vector<std::string>& addresses) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
addresses.clear();
|
|
|
|
addresses.reserve(wallet.getAddressCount());
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
for (size_t i = 0; i < wallet.getAddressCount(); ++i) {
|
|
|
|
addresses.push_back(wallet.getAddress(i));
|
|
|
|
}
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
logger(Logging::WARNING) << "Can't get addresses: " << e.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
}
|
2015-08-11 14:33:19 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::sendTransaction(const SendTransaction::Request& request, std::string& transactionHash) {
|
2015-05-27 12:08:46 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
validateAddresses(request.sourceAddresses, currency, logger);
|
|
|
|
validateAddresses(collectDestinationAddresses(request.transfers), currency, logger);
|
|
|
|
if (!request.changeAddress.empty()) {
|
|
|
|
validateAddresses({ request.changeAddress }, currency, logger);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
CryptoNote::TransactionParameters sendParams;
|
|
|
|
if (!request.paymentId.empty()) {
|
|
|
|
addPaymentIdToExtra(request.paymentId, sendParams.extra);
|
|
|
|
} else {
|
|
|
|
sendParams.extra = Common::asString(Common::fromHex(request.extra));
|
|
|
|
}
|
2015-08-11 14:33:19 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
sendParams.sourceAddresses = request.sourceAddresses;
|
|
|
|
sendParams.destinations = convertWalletRpcOrdersToWalletOrders(request.transfers);
|
|
|
|
sendParams.fee = request.fee;
|
|
|
|
sendParams.mixIn = request.anonymity;
|
|
|
|
sendParams.unlockTimestamp = request.unlockTime;
|
|
|
|
sendParams.changeDestination = request.changeAddress;
|
2015-07-30 15:22:07 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
size_t transactionId = wallet.transfer(sendParams);
|
|
|
|
transactionHash = Common::podToHex(wallet.getTransaction(transactionId).hash);
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Transaction " << transactionHash << " has been sent";
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while sending transaction: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
2015-12-09 13:19:03 +00:00
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while sending transaction: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::createDelayedTransaction(const CreateDelayedTransaction::Request& request, std::string& transactionHash) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
2015-07-15 12:23:00 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
validateAddresses(request.addresses, currency, logger);
|
|
|
|
validateAddresses(collectDestinationAddresses(request.transfers), currency, logger);
|
|
|
|
if (!request.changeAddress.empty()) {
|
|
|
|
validateAddresses({ request.changeAddress }, currency, logger);
|
|
|
|
}
|
2015-07-15 12:23:00 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
CryptoNote::TransactionParameters sendParams;
|
|
|
|
if (!request.paymentId.empty()) {
|
|
|
|
addPaymentIdToExtra(request.paymentId, sendParams.extra);
|
2015-07-15 12:23:00 +00:00
|
|
|
} else {
|
2015-12-09 13:19:03 +00:00
|
|
|
sendParams.extra = Common::asString(Common::fromHex(request.extra));
|
2015-07-15 12:23:00 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
sendParams.sourceAddresses = request.addresses;
|
|
|
|
sendParams.destinations = convertWalletRpcOrdersToWalletOrders(request.transfers);
|
|
|
|
sendParams.fee = request.fee;
|
|
|
|
sendParams.mixIn = request.anonymity;
|
|
|
|
sendParams.unlockTimestamp = request.unlockTime;
|
|
|
|
sendParams.changeDestination = request.changeAddress;
|
2015-07-15 12:23:00 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
size_t transactionId = wallet.makeTransaction(sendParams);
|
|
|
|
transactionHash = Common::podToHex(wallet.getTransaction(transactionId).hash);
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Delayed transaction " << transactionHash << " has been created";
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while creating delayed transaction: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while creating delayed transaction: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::getDelayedTransactionHashes(std::vector<std::string>& transactionHashes) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
std::vector<size_t> transactionIds = wallet.getDelayedTransactionIds();
|
|
|
|
transactionHashes.reserve(transactionIds.size());
|
|
|
|
|
|
|
|
for (auto id: transactionIds) {
|
|
|
|
transactionHashes.emplace_back(Common::podToHex(wallet.getTransaction(id).hash));
|
2015-07-15 12:23:00 +00:00
|
|
|
}
|
2015-12-09 13:19:03 +00:00
|
|
|
|
2015-07-15 12:23:00 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting delayed transaction hashes: " << x.what();
|
2015-07-15 12:23:00 +00:00
|
|
|
return x.code();
|
2015-12-09 13:19:03 +00:00
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting delayed transaction hashes: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-07-15 12:23:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::deleteDelayedTransaction(const std::string& transactionHash) {
|
2015-05-27 12:08:46 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
parseHash(transactionHash, logger); //validate transactionHash parameter
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
auto idIt = transactionIdIndex.find(transactionHash);
|
|
|
|
if (idIt == transactionIdIndex.end()) {
|
|
|
|
return make_error_code(CryptoNote::error::WalletServiceErrorCode::OBJECT_NOT_FOUND);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
size_t transactionId = idIt->second;
|
|
|
|
wallet.rollbackUncommitedTransaction(transactionId);
|
2015-07-30 15:22:07 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Delayed transaction " << transactionHash << " has been canceled";
|
2015-05-27 12:08:46 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while deleting delayed transaction hashes: " << x.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
return x.code();
|
2015-12-09 13:19:03 +00:00
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while deleting delayed transaction hashes: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::sendDelayedTransaction(const std::string& transactionHash) {
|
2015-07-30 15:22:07 +00:00
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
System::EventLock lk(readyEvent);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
parseHash(transactionHash, logger); //validate transactionHash parameter
|
|
|
|
|
|
|
|
auto idIt = transactionIdIndex.find(transactionHash);
|
|
|
|
if (idIt == transactionIdIndex.end()) {
|
|
|
|
return make_error_code(CryptoNote::error::WalletServiceErrorCode::OBJECT_NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t transactionId = idIt->second;
|
|
|
|
wallet.commitTransaction(transactionId);
|
|
|
|
|
|
|
|
logger(Logging::DEBUGGING) << "Delayed transaction " << transactionHash << " has been sent";
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while sending delayed transaction hashes: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while sending delayed transaction hashes: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::error_code WalletService::getUnconfirmedTransactionHashes(const std::vector<std::string>& addresses, std::vector<std::string>& transactionHashes) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
validateAddresses(addresses, currency, logger);
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
std::vector<CryptoNote::WalletTransactionWithTransfers> transactions = wallet.getUnconfirmedTransactions();
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
TransactionsInBlockInfoFilter transactionFilter(addresses, "");
|
2015-05-27 12:08:46 +00:00
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
for (const auto& transaction: transactions) {
|
|
|
|
if (transactionFilter.checkTransaction(transaction)) {
|
|
|
|
transactionHashes.emplace_back(Common::podToHex(transaction.transaction.hash));
|
2015-07-30 15:22:07 +00:00
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::system_error& x) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::WARNING) << "Error while getting unconfirmed transaction hashes: " << x.what();
|
|
|
|
return x.code();
|
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting unconfirmed transaction hashes: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::error_code WalletService::getStatus(uint32_t& blockCount, uint32_t& knownBlockCount, std::string& lastBlockHash, uint32_t& peerCount) {
|
|
|
|
try {
|
|
|
|
System::EventLock lk(readyEvent);
|
|
|
|
|
|
|
|
knownBlockCount = node.getKnownBlockCount();
|
|
|
|
peerCount = node.getPeerCount();
|
|
|
|
blockCount = wallet.getBlockCount();
|
|
|
|
|
|
|
|
auto lastHashes = wallet.getBlockHashes(blockCount - 1, 1);
|
|
|
|
lastBlockHash = Common::podToHex(lastHashes.back());
|
|
|
|
} catch (std::system_error& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting status: " << x.what();
|
2015-07-30 15:22:07 +00:00
|
|
|
return x.code();
|
2015-12-09 13:19:03 +00:00
|
|
|
} catch (std::exception& x) {
|
|
|
|
logger(Logging::WARNING) << "Error while getting status: " << x.what();
|
|
|
|
return make_error_code(CryptoNote::error::INTERNAL_WALLET_ERROR);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::error_code();
|
|
|
|
}
|
|
|
|
|
2015-07-30 15:22:07 +00:00
|
|
|
void WalletService::refresh() {
|
|
|
|
try {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "Refresh is started";
|
2015-07-30 15:22:07 +00:00
|
|
|
for (;;) {
|
2015-12-09 13:19:03 +00:00
|
|
|
auto event = wallet.getEvent();
|
|
|
|
if (event.type == CryptoNote::TRANSACTION_CREATED) {
|
2015-10-01 15:27:18 +00:00
|
|
|
size_t transactionId = event.transactionCreated.transactionIndex;
|
2015-12-09 13:19:03 +00:00
|
|
|
transactionIdIndex.emplace(Common::podToHex(wallet.getTransaction(transactionId).hash), transactionId);
|
2015-07-30 15:22:07 +00:00
|
|
|
}
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::system_error& e) {
|
2015-12-09 13:19:03 +00:00
|
|
|
logger(Logging::DEBUGGING) << "refresh is stopped: " << e.what();
|
2015-07-30 15:22:07 +00:00
|
|
|
} catch (std::exception& e) {
|
|
|
|
logger(Logging::WARNING) << "exception thrown in refresh(): " << e.what();
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-09 13:19:03 +00:00
|
|
|
void WalletService::reset() {
|
|
|
|
PaymentService::secureSaveWallet(wallet, config.walletFile, false, false);
|
|
|
|
wallet.stop();
|
|
|
|
wallet.shutdown();
|
|
|
|
inited = false;
|
|
|
|
refreshContext.wait();
|
|
|
|
|
|
|
|
wallet.start();
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WalletService::replaceWithNewWallet(const Crypto::SecretKey& viewSecretKey) {
|
|
|
|
wallet.stop();
|
|
|
|
wallet.shutdown();
|
|
|
|
inited = false;
|
|
|
|
refreshContext.wait();
|
|
|
|
|
|
|
|
transactionIdIndex.clear();
|
|
|
|
|
|
|
|
wallet.start();
|
|
|
|
wallet.initializeWithViewKey(viewSecretKey, config.walletPassword);
|
|
|
|
inited = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> WalletService::getTransactions(const Crypto::Hash& blockHash, size_t blockCount) const {
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> result = wallet.getTransactions(blockHash, blockCount);
|
|
|
|
if (result.empty()) {
|
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::WalletServiceErrorCode::OBJECT_NOT_FOUND));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> WalletService::getTransactions(uint32_t firstBlockIndex, size_t blockCount) const {
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> result = wallet.getTransactions(firstBlockIndex, blockCount);
|
|
|
|
if (result.empty()) {
|
|
|
|
throw std::system_error(make_error_code(CryptoNote::error::WalletServiceErrorCode::OBJECT_NOT_FOUND));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TransactionHashesInBlockRpcInfo> WalletService::getRpcTransactionHashes(const Crypto::Hash& blockHash, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const {
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> allTransactions = getTransactions(blockHash, blockCount);
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> filteredTransactions = filterTransactions(allTransactions, filter);
|
|
|
|
return convertTransactionsInBlockInfoToTransactionHashesInBlockRpcInfo(filteredTransactions);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TransactionHashesInBlockRpcInfo> WalletService::getRpcTransactionHashes(uint32_t firstBlockIndex, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const {
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> allTransactions = getTransactions(firstBlockIndex, blockCount);
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> filteredTransactions = filterTransactions(allTransactions, filter);
|
|
|
|
return convertTransactionsInBlockInfoToTransactionHashesInBlockRpcInfo(filteredTransactions);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TransactionsInBlockRpcInfo> WalletService::getRpcTransactions(const Crypto::Hash& blockHash, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const {
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> allTransactions = getTransactions(blockHash, blockCount);
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> filteredTransactions = filterTransactions(allTransactions, filter);
|
|
|
|
return convertTransactionsInBlockInfoToTransactionsInBlockRpcInfo(filteredTransactions);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<TransactionsInBlockRpcInfo> WalletService::getRpcTransactions(uint32_t firstBlockIndex, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const {
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> allTransactions = getTransactions(firstBlockIndex, blockCount);
|
|
|
|
std::vector<CryptoNote::TransactionsInBlockInfo> filteredTransactions = filterTransactions(allTransactions, filter);
|
|
|
|
return convertTransactionsInBlockInfoToTransactionsInBlockRpcInfo(filteredTransactions);
|
2015-05-27 12:08:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} //namespace PaymentService
|