Bytecoin v.1.0.9 release

This commit is contained in:
Antonio Juarez 2015-12-09 14:19:03 +01:00
parent 00915e6d34
commit 6d4e1d1ea3
82 changed files with 7459 additions and 1943 deletions

View file

@ -1,3 +1,8 @@
Release notes 1.0.9
- New API for Bytecoin RPC Wallet
- Various improvements
Release notes 1.0.8 Release notes 1.0.8
- Fusion transactions for Bytecoin Wallet - Fusion transactions for Bytecoin Wallet

View file

@ -97,7 +97,8 @@ public:
virtual size_t transactionsCount() const = 0; virtual size_t transactionsCount() const = 0;
virtual uint64_t balance(uint32_t flags = IncludeDefault) const = 0; virtual uint64_t balance(uint32_t flags = IncludeDefault) const = 0;
virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags = IncludeDefault) const = 0; virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags = IncludeDefault) const = 0;
virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) const = 0; virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info,
uint64_t* amountIn = nullptr, uint64_t* amountOut = nullptr) const = 0;
virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags = IncludeDefault) const = 0; virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags = IncludeDefault) const = 0;
//only type flags are feasible for this function //only type flags are feasible for this function
virtual std::vector<TransactionOutputInformation> getTransactionInputs(const Crypto::Hash& transactionHash, uint32_t flags) const = 0; virtual std::vector<TransactionOutputInformation> getTransactionInputs(const Crypto::Hash& transactionHash, uint32_t flags) const = 0;

View file

@ -62,6 +62,16 @@ public:
virtual ITransfersContainer& getContainer() = 0; virtual ITransfersContainer& getContainer() = 0;
}; };
class ITransfersSynchronizerObserver {
public:
virtual void onBlocksAdded(const Crypto::PublicKey& viewPublicKey, const std::vector<Crypto::Hash>& blockHashes) {}
virtual void onBlockchainDetach(const Crypto::PublicKey& viewPublicKey, uint32_t blockIndex) {}
virtual void onTransactionDeleteBegin(const Crypto::PublicKey& viewPublicKey, Crypto::Hash transactionHash) {}
virtual void onTransactionDeleteEnd(const Crypto::PublicKey& viewPublicKey, Crypto::Hash transactionHash) {}
virtual void onTransactionUpdated(const Crypto::PublicKey& viewPublicKey, const Crypto::Hash& transactionHash,
const std::vector<ITransfersContainer*>& containers) {}
};
class ITransfersSynchronizer : public IStreamSerializable { class ITransfersSynchronizer : public IStreamSerializable {
public: public:
virtual ~ITransfersSynchronizer() {} virtual ~ITransfersSynchronizer() {}
@ -71,6 +81,7 @@ public:
virtual void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions) = 0; virtual void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions) = 0;
// returns nullptr if address is not found // returns nullptr if address is not found
virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) = 0; virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) = 0;
virtual std::vector<Crypto::Hash> getViewKeyKnownBlocks(const Crypto::PublicKey& publicViewKey) = 0;
}; };
} }

View file

@ -32,7 +32,8 @@ enum class WalletTransactionState : uint8_t {
SUCCEEDED = 0, SUCCEEDED = 0,
FAILED, FAILED,
CANCELLED, CANCELLED,
CREATED CREATED,
DELETED
}; };
enum WalletEventType { enum WalletEventType {
@ -40,7 +41,7 @@ enum WalletEventType {
TRANSACTION_UPDATED, TRANSACTION_UPDATED,
BALANCE_UNLOCKED, BALANCE_UNLOCKED,
SYNC_PROGRESS_UPDATED, SYNC_PROGRESS_UPDATED,
SYNC_COMPLETED SYNC_COMPLETED,
}; };
struct WalletTransactionCreatedData { struct WalletTransactionCreatedData {
@ -80,7 +81,8 @@ struct WalletTransaction {
enum class WalletTransferType : uint8_t { enum class WalletTransferType : uint8_t {
USUAL = 0, USUAL = 0,
DONATION DONATION,
CHANGE
}; };
struct WalletOrder { struct WalletOrder {
@ -100,13 +102,24 @@ struct DonationSettings {
}; };
struct TransactionParameters { struct TransactionParameters {
std::string sourceAddress; std::vector<std::string> sourceAddresses;
std::vector<WalletOrder> destinations; std::vector<WalletOrder> destinations;
uint64_t fee = 0; uint64_t fee = 0;
uint64_t mixIn = 0; uint64_t mixIn = 0;
std::string extra; std::string extra;
uint64_t unlockTimestamp = 0; uint64_t unlockTimestamp = 0;
DonationSettings donation; DonationSettings donation;
std::string changeDestination;
};
struct WalletTransactionWithTransfers {
WalletTransaction transaction;
std::vector<WalletTransfer> transfers;
};
struct TransactionsInBlockInfo {
Crypto::Hash blockHash;
std::vector<WalletTransactionWithTransfers> transactions;
}; };
class IWallet { class IWallet {
@ -124,6 +137,7 @@ public:
virtual size_t getAddressCount() const = 0; virtual size_t getAddressCount() const = 0;
virtual std::string getAddress(size_t index) const = 0; virtual std::string getAddress(size_t index) const = 0;
virtual KeyPair getAddressSpendKey(size_t index) const = 0; virtual KeyPair getAddressSpendKey(size_t index) const = 0;
virtual KeyPair getAddressSpendKey(const std::string& address) const = 0;
virtual KeyPair getViewKey() const = 0; virtual KeyPair getViewKey() const = 0;
virtual std::string createAddress() = 0; virtual std::string createAddress() = 0;
virtual std::string createAddress(const Crypto::SecretKey& spendSecretKey) = 0; virtual std::string createAddress(const Crypto::SecretKey& spendSecretKey) = 0;
@ -140,12 +154,20 @@ public:
virtual size_t getTransactionTransferCount(size_t transactionIndex) const = 0; virtual size_t getTransactionTransferCount(size_t transactionIndex) const = 0;
virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const = 0; virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const = 0;
virtual size_t transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; virtual WalletTransactionWithTransfers getTransaction(const Crypto::Hash& transactionHash) const = 0;
virtual size_t transfer(const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; virtual std::vector<TransactionsInBlockInfo> getTransactions(const Crypto::Hash& blockHash, size_t count) const = 0;
virtual size_t transfer(const std::string& sourceAddress, const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; virtual std::vector<TransactionsInBlockInfo> getTransactions(uint32_t blockIndex, size_t count) const = 0;
virtual size_t transfer(const std::string& sourceAddress, const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) = 0; virtual std::vector<Crypto::Hash> getBlockHashes(uint32_t blockIndex, size_t count) const = 0;
virtual uint32_t getBlockCount() const = 0;
virtual std::vector<WalletTransactionWithTransfers> getUnconfirmedTransactions() const = 0;
virtual std::vector<size_t> getDelayedTransactionIds() const = 0;
virtual size_t transfer(const TransactionParameters& sendingTransaction) = 0; virtual size_t transfer(const TransactionParameters& sendingTransaction) = 0;
virtual size_t makeTransaction(const TransactionParameters& sendingTransaction) = 0;
virtual void commitTransaction(size_t transactionId) = 0;
virtual void rollbackUncommitedTransaction(size_t transactionId) = 0;
virtual void start() = 0; virtual void start() = 0;
virtual void stop() = 0; virtual void stop() = 0;

View file

@ -87,12 +87,16 @@ bool BlockchainExplorer::PoolUpdateGuard::beginUpdate() {
for (;;) { for (;;) {
switch (state) { switch (state) {
case State::NONE: case State::NONE:
return true; if (m_state.compare_exchange_weak(state, State::UPDATING)) {
return true;
}
break;
case State::UPDATING: case State::UPDATING:
if (m_state.compare_exchange_weak(state, State::UPDATE_REQUIRED)) { if (m_state.compare_exchange_weak(state, State::UPDATE_REQUIRED)) {
return false; return false;
} }
break;
case State::UPDATE_REQUIRED: case State::UPDATE_REQUIRED:
return false; return false;

View file

@ -30,6 +30,7 @@ file(GLOB_RECURSE JsonRpcServer JsonRpcServer/*)
file(GLOB_RECURSE PaymentGate PaymentGate/*) file(GLOB_RECURSE PaymentGate PaymentGate/*)
file(GLOB_RECURSE PaymentGateService PaymentGateService/*) file(GLOB_RECURSE PaymentGateService PaymentGateService/*)
file(GLOB_RECURSE Miner Miner/*)
source_group("" FILES $${Common} ${ConnectivityTool} ${Crypto} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${JsonRpcServer} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet} ${WalletLegacy}) source_group("" FILES $${Common} ${ConnectivityTool} ${Crypto} ${CryptoNoteCore} ${CryptoNoteProtocol} ${Daemon} ${JsonRpcServer} ${Http} ${Logging} ${NodeRpcProxy} ${P2p} ${Rpc} ${Serialization} ${SimpleWallet} ${System} ${Transfers} ${Wallet} ${WalletLegacy})
@ -54,17 +55,17 @@ add_executable(ConnectivityTool ${ConnectivityTool})
add_executable(Daemon ${Daemon}) add_executable(Daemon ${Daemon})
add_executable(SimpleWallet ${SimpleWallet}) add_executable(SimpleWallet ${SimpleWallet})
add_executable(PaymentGateService ${PaymentGateService}) add_executable(PaymentGateService ${PaymentGateService})
add_executable(Miner ${Miner})
if (MSVC)
target_link_libraries(System ws2_32)
endif ()
target_link_libraries(ConnectivityTool CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES}) target_link_libraries(ConnectivityTool CryptoNoteCore Common Logging Crypto P2P Rpc Http Serialization System ${Boost_LIBRARIES})
target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) target_link_libraries(Daemon CryptoNoteCore P2P Rpc Serialization System Http Logging Common Crypto upnpc-static BlockchainExplorer ${Boost_LIBRARIES})
target_link_libraries(SimpleWallet Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES}) target_link_libraries(SimpleWallet Wallet NodeRpcProxy Transfers Rpc Http Serialization CryptoNoteCore System Logging Common Crypto ${Boost_LIBRARIES})
target_link_libraries(PaymentGateService PaymentGate JsonRpcServer Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static BlockchainExplorer ${Boost_LIBRARIES}) target_link_libraries(PaymentGateService PaymentGate JsonRpcServer Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static BlockchainExplorer ${Boost_LIBRARIES})
target_link_libraries(Miner CryptoNoteCore Rpc Serialization System Http Logging Common Crypto ${Boost_LIBRARIES})
if (MSVC)
target_link_libraries(ConnectivityTool ws2_32)
target_link_libraries(SimpleWallet ws2_32)
endif ()
add_dependencies(Rpc version) add_dependencies(Rpc version)
@ -78,3 +79,4 @@ set_property(TARGET ConnectivityTool PROPERTY OUTPUT_NAME "connectivity_tool")
set_property(TARGET Daemon PROPERTY OUTPUT_NAME "bytecoind") set_property(TARGET Daemon PROPERTY OUTPUT_NAME "bytecoind")
set_property(TARGET SimpleWallet PROPERTY OUTPUT_NAME "simplewallet") set_property(TARGET SimpleWallet PROPERTY OUTPUT_NAME "simplewallet")
set_property(TARGET PaymentGateService PROPERTY OUTPUT_NAME "walletd") set_property(TARGET PaymentGateService PROPERTY OUTPUT_NAME "walletd")
set_property(TARGET Miner PROPERTY OUTPUT_NAME "miner")

View file

@ -120,6 +120,32 @@ public:
} }
} }
template<typename F, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
void notify(F notification, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) {
std::vector<T*> observersCopy;
{
std::unique_lock<std::mutex> lock(m_observersMutex);
observersCopy = m_observers;
}
for (T* observer : observersCopy) {
(observer->*notification)(arg0, arg1, arg2, arg3, arg4);
}
}
template<typename F, typename Arg0, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
void notify(F notification, const Arg0& arg0, const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4, const Arg5& arg5) {
std::vector<T*> observersCopy;
{
std::unique_lock<std::mutex> lock(m_observersMutex);
observersCopy = m_observers;
}
for (T* observer : observersCopy) {
(observer->*notification)(arg0, arg1, arg2, arg3, arg4, arg5);
}
}
#else #else
template<typename F, typename... Args> template<typename F, typename... Args>

37
src/Common/ScopeExit.cpp Normal file
View file

@ -0,0 +1,37 @@
// 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 <http://www.gnu.org/licenses/>.
#include "ScopeExit.h"
namespace Tools {
ScopeExit::ScopeExit(std::function<void()>&& handler) :
m_handler(std::move(handler)),
m_cancelled(false) {
}
ScopeExit::~ScopeExit() {
if (!m_cancelled) {
m_handler();
}
}
void ScopeExit::cancel() {
m_cancelled = true;
}
}

41
src/Common/ScopeExit.h Normal file
View file

@ -0,0 +1,41 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <functional>
namespace Tools {
class ScopeExit {
public:
ScopeExit(std::function<void()>&& handler);
~ScopeExit();
ScopeExit(const ScopeExit&) = delete;
ScopeExit(ScopeExit&&) = delete;
ScopeExit& operator=(const ScopeExit&) = delete;
ScopeExit& operator=(ScopeExit&&) = delete;
void cancel();
private:
std::function<void()> m_handler;
bool m_cancelled;
};
}

View file

@ -359,4 +359,9 @@ std::string get_nix_version_display_string()
return std::error_code(code, std::system_category()); return std::error_code(code, std::system_category());
} }
bool directoryExists(const std::string& path) {
boost::system::error_code ec;
return boost::filesystem::is_directory(path, ec);
}
} }

View file

@ -26,4 +26,5 @@ namespace Tools
std::string get_os_version_string(); std::string get_os_version_string();
bool create_directories_if_necessary(const std::string& path); bool create_directories_if_necessary(const std::string& path);
std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name);
bool directoryExists(const std::string& path);
} }

View file

@ -165,7 +165,8 @@ const CheckpointData CHECKPOINTS[] = {
{810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"}, {810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"},
{816000, "32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745"}, {816000, "32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745"},
{822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"}, {822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"},
{841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"} {841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"},
{890000, "a7132932ea31236ce6b8775cd1380edf90b5e536ee4202c77b69a3d62445fcd2"}
}; };
} // CryptoNote } // CryptoNote

View file

@ -319,7 +319,6 @@ m_currency(currency),
m_tx_pool(tx_pool), m_tx_pool(tx_pool),
m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_limit(0),
m_is_in_checkpoint_zone(false), m_is_in_checkpoint_zone(false),
m_is_blockchain_storing(false),
m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger), m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger),
m_checkpoints(logger) { m_checkpoints(logger) {

View file

@ -107,7 +107,6 @@ namespace CryptoNote {
bool get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out); bool get_out_by_msig_gindex(uint64_t amount, uint64_t gindex, MultisignatureOutput& out);
bool checkTransactionInputs(const Transaction& tx, uint32_t& pmax_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail = 0); bool checkTransactionInputs(const Transaction& tx, uint32_t& pmax_used_block_height, Crypto::Hash& max_used_block_id, BlockInfo* tail = 0);
uint64_t getCurrentCumulativeBlocksizeLimit(); uint64_t getCurrentCumulativeBlocksizeLimit();
bool isStoringBlockchain(){return m_is_blockchain_storing;}
uint64_t blockDifficulty(size_t i); uint64_t blockDifficulty(size_t i);
bool getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight); bool getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight);
bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins); bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins);
@ -248,7 +247,6 @@ namespace CryptoNote {
std::string m_config_folder; std::string m_config_folder;
Checkpoints m_checkpoints; Checkpoints m_checkpoints;
std::atomic<bool> m_is_in_checkpoint_zone; std::atomic<bool> m_is_in_checkpoint_zone;
std::atomic<bool> m_is_blockchain_storing;
typedef SwappedVector<BlockEntry> Blocks; typedef SwappedVector<BlockEntry> Blocks;
typedef std::unordered_map<Crypto::Hash, uint32_t> BlockMap; typedef std::unordered_map<Crypto::Hash, uint32_t> BlockMap;

View file

@ -99,11 +99,6 @@ bool core::handle_command_line(const boost::program_options::variables_map& vm)
return true; return true;
} }
bool core::is_ready() {
return !m_blockchain.isStoringBlockchain();
}
uint32_t core::get_current_blockchain_height() { uint32_t core::get_current_blockchain_height() {
return m_blockchain.getCurrentBlockchainHeight(); return m_blockchain.getCurrentBlockchainHeight();
} }

View file

@ -97,7 +97,6 @@ namespace CryptoNote {
std::vector<Crypto::Hash> buildSparseChain() override; std::vector<Crypto::Hash> buildSparseChain() override;
std::vector<Crypto::Hash> buildSparseChain(const Crypto::Hash& startBlockId) override; std::vector<Crypto::Hash> buildSparseChain(const Crypto::Hash& startBlockId) override;
void on_synchronized() override; void on_synchronized() override;
bool is_ready() override;
virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) override; virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) override;
bool get_blocks(uint32_t start_offset, uint32_t count, std::list<Block>& blocks, std::list<Transaction>& txs); bool get_blocks(uint32_t start_offset, uint32_t count, std::list<Block>& blocks, std::list<Transaction>& txs);

View file

@ -29,6 +29,7 @@ CoreConfig::CoreConfig() {
void CoreConfig::init(const boost::program_options::variables_map& options) { void CoreConfig::init(const boost::program_options::variables_map& options) {
if (options.count(command_line::arg_data_dir.name) != 0 && (!options[command_line::arg_data_dir.name].defaulted() || configFolder == Tools::getDefaultDataDirectory())) { if (options.count(command_line::arg_data_dir.name) != 0 && (!options[command_line::arg_data_dir.name].defaulted() || configFolder == Tools::getDefaultDataDirectory())) {
configFolder = command_line::get_arg(options, command_line::arg_data_dir); configFolder = command_line::get_arg(options, command_line::arg_data_dir);
configFolderDefaulted = options[command_line::arg_data_dir.name].defaulted();
} }
} }

View file

@ -31,6 +31,7 @@ public:
void init(const boost::program_options::variables_map& options); void init(const boost::program_options::variables_map& options);
std::string configFolder; std::string configFolder;
bool configFolderDefaulted = true;
}; };
} //namespace CryptoNote } //namespace CryptoNote

View file

@ -68,7 +68,6 @@ public:
virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0; virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0;
virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; //Deprecated. Should be removed with CryptoNoteProtocolHandler. virtual bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS_request& arg, NOTIFY_RESPONSE_GET_OBJECTS_request& rsp) = 0; //Deprecated. Should be removed with CryptoNoteProtocolHandler.
virtual void on_synchronized() = 0; virtual void on_synchronized() = 0;
virtual bool is_ready() = 0;
virtual size_t addChain(const std::vector<const IBlock*>& chain) = 0; virtual size_t addChain(const std::vector<const IBlock*>& chain) = 0;
virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) = 0; virtual void get_blockchain_top(uint32_t& height, Crypto::Hash& top_id) = 0;

View file

@ -108,6 +108,9 @@ bool CryptoNoteProtocolHandler::start_sync(CryptoNoteConnectionContext& context)
logger(Logging::TRACE) << context << "Starting synchronization"; logger(Logging::TRACE) << context << "Starting synchronization";
if (context.m_state == CryptoNoteConnectionContext::state_synchronizing) { if (context.m_state == CryptoNoteConnectionContext::state_synchronizing) {
assert(context.m_needed_objects.empty());
assert(context.m_requested_objects.empty());
NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>(); NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
r.block_ids = m_core.buildSparseChain(); r.block_ids = m_core.buildSparseChain();
logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size(); logger(Logging::TRACE) << context << "-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size();
@ -355,15 +358,16 @@ int CryptoNoteProtocolHandler::handle_response_get_objects(int command, NOTIFY_R
} }
} }
auto req_it = context.m_requested_objects.find(get_block_hash(b)); auto blockHash = get_block_hash(b);
auto req_it = context.m_requested_objects.find(blockHash);
if (req_it == context.m_requested_objects.end()) { if (req_it == context.m_requested_objects.end()) {
logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(getBinaryArrayHash(asBinaryArray(block_entry.block))) logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(blockHash)
<< " wasn't requested, dropping connection"; << " wasn't requested, dropping connection";
context.m_state = CryptoNoteConnectionContext::state_shutdown; context.m_state = CryptoNoteConnectionContext::state_shutdown;
return 1; return 1;
} }
if (b.transactionHashes.size() != block_entry.txs.size()) { if (b.transactionHashes.size() != block_entry.txs.size()) {
logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(getBinaryArrayHash(asBinaryArray(block_entry.block))) logger(Logging::ERROR) << context << "sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << Common::podToHex(blockHash)
<< ", transactionHashes.size()=" << b.transactionHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"; << ", transactionHashes.size()=" << b.transactionHashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection";
context.m_state = CryptoNoteConnectionContext::state_shutdown; context.m_state = CryptoNoteConnectionContext::state_shutdown;
return 1; return 1;
@ -437,6 +441,8 @@ int CryptoNoteProtocolHandler::processObjects(CryptoNoteConnectionContext& conte
} else if (bvc.m_already_exists) { } else if (bvc.m_already_exists) {
logger(Logging::DEBUGGING) << context << "Block already exists, switching to idle state"; logger(Logging::DEBUGGING) << context << "Block already exists, switching to idle state";
context.m_state = CryptoNoteConnectionContext::state_idle; context.m_state = CryptoNoteConnectionContext::state_idle;
context.m_needed_objects.clear();
context.m_requested_objects.clear();
return 1; return 1;
} }

View file

@ -202,11 +202,21 @@ int main(int argc, char* argv[])
RpcServerConfig rpcConfig; RpcServerConfig rpcConfig;
rpcConfig.init(vm); rpcConfig.init(vm);
if (!coreConfig.configFolderDefaulted) {
if (!Tools::directoryExists(coreConfig.configFolder)) {
throw std::runtime_error("Directory does not exist: " + coreConfig.configFolder);
}
} else {
if (!Tools::create_directories_if_necessary(coreConfig.configFolder)) {
throw std::runtime_error("Can't create directory: " + coreConfig.configFolder);
}
}
System::Dispatcher dispatcher; System::Dispatcher dispatcher;
CryptoNote::CryptoNoteProtocolHandler cprotocol(currency, dispatcher, ccore, nullptr, logManager); CryptoNote::CryptoNoteProtocolHandler cprotocol(currency, dispatcher, ccore, nullptr, logManager);
CryptoNote::NodeServer p2psrv(dispatcher, cprotocol, logManager); CryptoNote::NodeServer p2psrv(dispatcher, cprotocol, logManager);
CryptoNote::RpcServer rpcServer(dispatcher, logManager, ccore, p2psrv); CryptoNote::RpcServer rpcServer(dispatcher, logManager, ccore, p2psrv, cprotocol);
cprotocol.set_p2p_endpoint(&p2psrv); cprotocol.set_p2p_endpoint(&p2psrv);
ccore.set_cryptonote_protocol(&cprotocol); ccore.set_cryptonote_protocol(&cprotocol);

View file

@ -0,0 +1,105 @@
// 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 <http://www.gnu.org/licenses/>.
#include "BlockchainMonitor.h"
#include "Common/StringTools.h"
#include <System/EventLock.h>
#include <System/Timer.h>
#include <System/InterruptedException.h>
#include "Rpc/CoreRpcServerCommandsDefinitions.h"
#include "Rpc/JsonRpc.h"
#include "Rpc/HttpClient.h"
BlockchainMonitor::BlockchainMonitor(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, size_t pollingInterval, Logging::ILogger& logger):
m_dispatcher(dispatcher),
m_daemonHost(daemonHost),
m_daemonPort(daemonPort),
m_pollingInterval(pollingInterval),
m_stopped(false),
m_httpEvent(dispatcher),
m_sleepingContext(dispatcher),
m_logger(logger, "BlockchainMonitor") {
m_httpEvent.set();
}
void BlockchainMonitor::waitBlockchainUpdate() {
m_logger(Logging::DEBUGGING) << "Waiting for blockchain updates";
m_stopped = false;
Crypto::Hash lastBlockHash = requestLastBlockHash();
while(!m_stopped) {
m_sleepingContext.spawn([this] () {
System::Timer timer(m_dispatcher);
timer.sleep(std::chrono::seconds(m_pollingInterval));
});
m_sleepingContext.wait();
if (lastBlockHash != requestLastBlockHash()) {
m_logger(Logging::DEBUGGING) << "Blockchain has been updated";
break;
}
}
if (m_stopped) {
m_logger(Logging::DEBUGGING) << "Blockchain monitor has been stopped";
throw System::InterruptedException();
}
}
void BlockchainMonitor::stop() {
m_logger(Logging::DEBUGGING) << "Sending stop signal to blockchain monitor";
m_stopped = true;
m_sleepingContext.interrupt();
m_sleepingContext.wait();
}
Crypto::Hash BlockchainMonitor::requestLastBlockHash() {
m_logger(Logging::DEBUGGING) << "Requesting last block hash";
try {
CryptoNote::HttpClient client(m_dispatcher, m_daemonHost, m_daemonPort);
CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request request;
CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response response;
System::EventLock lk(m_httpEvent);
CryptoNote::JsonRpc::invokeJsonRpcCommand(client, "getlastblockheader", request, response);
if (response.status != CORE_RPC_STATUS_OK) {
throw std::runtime_error("Core responded with wrong status: " + response.status);
}
Crypto::Hash blockHash;
if (!Common::podFromHex(response.block_header.hash, blockHash)) {
throw std::runtime_error("Couldn't parse block hash: " + response.block_header.hash);
}
m_logger(Logging::DEBUGGING) << "Last block hash: " << Common::podToHex(blockHash);
return blockHash;
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Failed to request last block hash: " << e.what();
throw;
}
}

View file

@ -0,0 +1,46 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "CryptoTypes.h"
#include <System/ContextGroup.h>
#include <System/Dispatcher.h>
#include <System/Event.h>
#include "Logging/LoggerRef.h"
class BlockchainMonitor {
public:
BlockchainMonitor(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, size_t pollingInterval, Logging::ILogger& logger);
void waitBlockchainUpdate();
void stop();
private:
System::Dispatcher& m_dispatcher;
std::string m_daemonHost;
uint16_t m_daemonPort;
size_t m_pollingInterval;
bool m_stopped;
System::Event m_httpEvent;
System::ContextGroup m_sleepingContext;
Logging::LoggerRef m_logger;
Crypto::Hash requestLastBlockHash();
};

157
src/Miner/Miner.cpp Normal file
View file

@ -0,0 +1,157 @@
// 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 <http://www.gnu.org/licenses/>.
#include "Miner.h"
#include <functional>
#include "crypto/crypto.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include <System/InterruptedException.h>
namespace CryptoNote {
Miner::Miner(System::Dispatcher& dispatcher, Logging::ILogger& logger) :
m_dispatcher(dispatcher),
m_miningStopped(dispatcher),
m_state(MiningState::MINING_STOPPED),
m_logger(logger, "Miner") {
}
Miner::~Miner() {
assert(m_state != MiningState::MINING_IN_PROGRESS);
}
Block Miner::mine(const BlockMiningParameters& blockMiningParameters, size_t threadCount) {
if (threadCount == 0) {
throw std::runtime_error("Miner requires at least one thread");
}
if (m_state == MiningState::MINING_IN_PROGRESS) {
throw std::runtime_error("Mining is already in progress");
}
m_state = MiningState::MINING_IN_PROGRESS;
m_miningStopped.clear();
runWorkers(blockMiningParameters, threadCount);
assert(m_state != MiningState::MINING_IN_PROGRESS);
if (m_state == MiningState::MINING_STOPPED) {
m_logger(Logging::DEBUGGING) << "Mining has been stopped";
throw System::InterruptedException();
}
assert(m_state == MiningState::BLOCK_FOUND);
return m_block;
}
void Miner::stop() {
MiningState state = MiningState::MINING_IN_PROGRESS;
if (m_state.compare_exchange_weak(state, MiningState::MINING_STOPPED)) {
m_miningStopped.wait();
m_miningStopped.clear();
}
}
void Miner::runWorkers(BlockMiningParameters blockMiningParameters, size_t threadCount) {
assert(threadCount > 0);
m_logger(Logging::INFO) << "Starting mining for difficulty " << blockMiningParameters.difficulty;
try {
blockMiningParameters.blockTemplate.nonce = Crypto::rand<uint32_t>();
for (size_t i = 0; i < threadCount; ++i) {
m_workers.emplace_back(std::unique_ptr<System::RemoteContext<void>> (
new System::RemoteContext<void>(m_dispatcher, std::bind(&Miner::workerFunc, this, blockMiningParameters.blockTemplate, blockMiningParameters.difficulty, threadCount)))
);
blockMiningParameters.blockTemplate.nonce++;
}
m_workers.clear();
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Error occured during mining: " << e.what();
m_state = MiningState::MINING_STOPPED;
}
m_miningStopped.set();
}
void Miner::workerFunc(const Block& blockTemplate, difficulty_type difficulty, uint32_t nonceStep) {
try {
Block block = blockTemplate;
Crypto::cn_context cryptoContext;
while (m_state == MiningState::MINING_IN_PROGRESS) {
Crypto::Hash hash;
if (!get_block_longhash(cryptoContext, block, hash)) {
//error occured
m_logger(Logging::DEBUGGING) << "calculating long hash error occured";
m_state = MiningState::MINING_STOPPED;
return;
}
if (check_hash(hash, difficulty)) {
m_logger(Logging::INFO) << "Found block for difficulty " << difficulty;
if (!setStateBlockFound()) {
m_logger(Logging::DEBUGGING) << "block is already found or mining stopped";
return;
}
m_block = block;
return;
}
block.nonce += nonceStep;
}
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Miner got error: " << e.what();
m_state = MiningState::MINING_STOPPED;
}
}
bool Miner::setStateBlockFound() {
auto state = m_state.load();
for (;;) {
switch (state) {
case MiningState::BLOCK_FOUND:
return false;
case MiningState::MINING_IN_PROGRESS:
if (m_state.compare_exchange_weak(state, MiningState::BLOCK_FOUND)) {
return true;
}
break;
case MiningState::MINING_STOPPED:
return false;
default:
assert(false);
return false;
}
}
}
} //namespace CryptoNote

67
src/Miner/Miner.h Normal file
View file

@ -0,0 +1,67 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <atomic>
#include <thread>
#include <System/Dispatcher.h>
#include <System/Event.h>
#include <System/RemoteContext.h>
#include "CryptoNote.h"
#include "CryptoNoteCore/Difficulty.h"
#include "Logging/LoggerRef.h"
namespace CryptoNote {
struct BlockMiningParameters {
Block blockTemplate;
difficulty_type difficulty;
};
class Miner {
public:
Miner(System::Dispatcher& dispatcher, Logging::ILogger& logger);
~Miner();
Block mine(const BlockMiningParameters& blockMiningParameters, size_t threadCount);
//NOTE! this is blocking method
void stop();
private:
System::Dispatcher& m_dispatcher;
System::Event m_miningStopped;
enum class MiningState : uint8_t { MINING_STOPPED, BLOCK_FOUND, MINING_IN_PROGRESS};
std::atomic<MiningState> m_state;
std::vector<std::unique_ptr<System::RemoteContext<void>>> m_workers;
Block m_block;
Logging::LoggerRef m_logger;
void runWorkers(BlockMiningParameters blockMiningParameters, size_t threadCount);
void workerFunc(const Block& blockTemplate, difficulty_type difficulty, uint32_t nonceStep);
bool setStateBlockFound();
};
} //namespace CryptoNote

31
src/Miner/MinerEvent.h Normal file
View file

@ -0,0 +1,31 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
namespace Miner {
enum class MinerEventType: uint8_t {
BLOCK_MINED,
BLOCKCHAIN_UPDATED,
};
struct MinerEvent {
MinerEventType type;
};
} //namespace Miner

276
src/Miner/MinerManager.cpp Normal file
View file

@ -0,0 +1,276 @@
// 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 <http://www.gnu.org/licenses/>.
#include "MinerManager.h"
#include <System/EventLock.h>
#include <System/InterruptedException.h>
#include <System/Timer.h>
#include "Common/StringTools.h"
#include "CryptoNoteConfig.h"
#include "CryptoNoteCore/CryptoNoteTools.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "CryptoNoteCore/TransactionExtra.h"
#include "Rpc/HttpClient.h"
#include "Rpc/CoreRpcServerCommandsDefinitions.h"
#include "Rpc/JsonRpc.h"
using namespace CryptoNote;
namespace Miner {
namespace {
MinerEvent BlockMinedEvent() {
MinerEvent event;
event.type = MinerEventType::BLOCK_MINED;
return event;
}
MinerEvent BlockchainUpdatedEvent() {
MinerEvent event;
event.type = MinerEventType::BLOCKCHAIN_UPDATED;
return event;
}
void adjustMergeMiningTag(Block& blockTemplate) {
if (blockTemplate.majorVersion == BLOCK_MAJOR_VERSION_2) {
CryptoNote::TransactionExtraMergeMiningTag mmTag;
mmTag.depth = 0;
if (!CryptoNote::get_aux_block_header_hash(blockTemplate, mmTag.merkleRoot)) {
throw std::runtime_error("Couldn't get block header hash");
}
blockTemplate.parentBlock.baseTransaction.extra.clear();
if (!CryptoNote::appendMergeMiningTagToExtra(blockTemplate.parentBlock.baseTransaction.extra, mmTag)) {
throw std::runtime_error("Couldn't append merge mining tag");
}
}
}
}
MinerManager::MinerManager(System::Dispatcher& dispatcher, const CryptoNote::MiningConfig& config, Logging::ILogger& logger) :
m_dispatcher(dispatcher),
m_logger(logger, "MinerManager"),
m_contextGroup(dispatcher),
m_config(config),
m_miner(dispatcher, logger),
m_blockchainMonitor(dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.scanPeriod, logger),
m_eventOccurred(dispatcher),
m_httpEvent(dispatcher),
m_lastBlockTimestamp(0) {
m_httpEvent.set();
}
MinerManager::~MinerManager() {
}
void MinerManager::start() {
m_logger(Logging::DEBUGGING) << "starting";
BlockMiningParameters params;
for (;;) {
m_logger(Logging::INFO) << "requesting mining parameters";
try {
params = requestMiningParameters(m_dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.miningAddress);
} catch (ConnectException& e) {
m_logger(Logging::WARNING) << "Couldn't connect to daemon: " << e.what();
System::Timer timer(m_dispatcher);
timer.sleep(std::chrono::seconds(m_config.scanPeriod));
continue;
}
adjustBlockTemplate(params.blockTemplate);
break;
}
startBlockchainMonitoring();
startMining(params);
eventLoop();
}
void MinerManager::eventLoop() {
size_t blocksMined = 0;
for (;;) {
m_logger(Logging::DEBUGGING) << "waiting for event";
MinerEvent event = waitEvent();
switch (event.type) {
case MinerEventType::BLOCK_MINED: {
m_logger(Logging::DEBUGGING) << "got BLOCK_MINED event";
stopBlockchainMonitoring();
if (submitBlock(m_minedBlock, m_config.daemonHost, m_config.daemonPort)) {
m_lastBlockTimestamp = m_minedBlock.timestamp;
if (m_config.blocksLimit != 0 && ++blocksMined == m_config.blocksLimit) {
m_logger(Logging::INFO) << "Miner mined requested " << m_config.blocksLimit << " blocks. Quitting";
return;
}
}
BlockMiningParameters params = requestMiningParameters(m_dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.miningAddress);
adjustBlockTemplate(params.blockTemplate);
startBlockchainMonitoring();
startMining(params);
break;
}
case MinerEventType::BLOCKCHAIN_UPDATED: {
m_logger(Logging::DEBUGGING) << "got BLOCKCHAIN_UPDATED event";
stopMining();
stopBlockchainMonitoring();
BlockMiningParameters params = requestMiningParameters(m_dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.miningAddress);
adjustBlockTemplate(params.blockTemplate);
startBlockchainMonitoring();
startMining(params);
break;
}
default:
assert(false);
return;
}
}
}
MinerEvent MinerManager::waitEvent() {
while(m_events.empty()) {
m_eventOccurred.wait();
m_eventOccurred.clear();
}
MinerEvent event = std::move(m_events.front());
m_events.pop();
return event;
}
void MinerManager::pushEvent(MinerEvent&& event) {
m_events.push(std::move(event));
m_eventOccurred.set();
}
void MinerManager::startMining(const CryptoNote::BlockMiningParameters& params) {
m_contextGroup.spawn([this, params] () {
try {
m_minedBlock = m_miner.mine(params, m_config.threadCount);
pushEvent(BlockMinedEvent());
} catch (System::InterruptedException&) {
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Miner context unexpectedly finished: " << e.what();
}
});
}
void MinerManager::stopMining() {
m_miner.stop();
}
void MinerManager::startBlockchainMonitoring() {
m_contextGroup.spawn([this] () {
try {
m_blockchainMonitor.waitBlockchainUpdate();
pushEvent(BlockchainUpdatedEvent());
} catch (System::InterruptedException&) {
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "BlockchainMonitor context unexpectedly finished: " << e.what();
}
});
}
void MinerManager::stopBlockchainMonitoring() {
m_blockchainMonitor.stop();
}
bool MinerManager::submitBlock(const Block& minedBlock, const std::string& daemonHost, uint16_t daemonPort) {
try {
HttpClient client(m_dispatcher, daemonHost, daemonPort);
COMMAND_RPC_SUBMITBLOCK::request request;
request.emplace_back(Common::toHex(toBinaryArray(minedBlock)));
COMMAND_RPC_SUBMITBLOCK::response response;
System::EventLock lk(m_httpEvent);
JsonRpc::invokeJsonRpcCommand(client, "submitblock", request, response);
m_logger(Logging::INFO) << "Block has been successfully submitted. Block hash: " << Common::podToHex(get_block_hash(minedBlock));
return true;
} catch (std::exception& e) {
m_logger(Logging::WARNING) << "Couldn't submit block: " << Common::podToHex(get_block_hash(minedBlock)) << ", reason: " << e.what();
return false;
}
}
BlockMiningParameters MinerManager::requestMiningParameters(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, const std::string& miningAddress) {
try {
HttpClient client(dispatcher, daemonHost, daemonPort);
COMMAND_RPC_GETBLOCKTEMPLATE::request request;
request.wallet_address = miningAddress;
request.reserve_size = 0;
COMMAND_RPC_GETBLOCKTEMPLATE::response response;
System::EventLock lk(m_httpEvent);
JsonRpc::invokeJsonRpcCommand(client, "getblocktemplate", request, response);
if (response.status != CORE_RPC_STATUS_OK) {
throw std::runtime_error("Core responded with wrong status: " + response.status);
}
BlockMiningParameters params;
params.difficulty = response.difficulty;
if(!fromBinaryArray(params.blockTemplate, Common::fromHex(response.blocktemplate_blob))) {
throw std::runtime_error("Couldn't deserialize block template");
}
m_logger(Logging::DEBUGGING) << "Requested block template with previous block hash: " << Common::podToHex(params.blockTemplate.previousBlockHash);
return params;
} catch (std::exception& e) {
m_logger(Logging::WARNING) << "Couldn't get block template: " << e.what();
throw;
}
}
void MinerManager::adjustBlockTemplate(CryptoNote::Block& blockTemplate) const {
adjustMergeMiningTag(blockTemplate);
if (m_config.firstBlockTimestamp == 0) {
//no need to fix timestamp
return;
}
if (m_lastBlockTimestamp == 0) {
blockTemplate.timestamp = m_config.firstBlockTimestamp;
} else if (m_lastBlockTimestamp != 0 && m_config.blockTimestampInterval != 0) {
blockTemplate.timestamp = m_lastBlockTimestamp + m_config.blockTimestampInterval;
}
}
} //namespace Miner

76
src/Miner/MinerManager.h Normal file
View file

@ -0,0 +1,76 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <queue>
#include <System/ContextGroup.h>
#include <System/Event.h>
#include "BlockchainMonitor.h"
#include "Logging/LoggerRef.h"
#include "Miner.h"
#include "MinerEvent.h"
#include "MiningConfig.h"
namespace System {
class Dispatcher;
}
namespace Miner {
class MinerManager {
public:
MinerManager(System::Dispatcher& dispatcher, const CryptoNote::MiningConfig& config, Logging::ILogger& logger);
~MinerManager();
void start();
private:
System::Dispatcher& m_dispatcher;
Logging::LoggerRef m_logger;
System::ContextGroup m_contextGroup;
CryptoNote::MiningConfig m_config;
CryptoNote::Miner m_miner;
BlockchainMonitor m_blockchainMonitor;
System::Event m_eventOccurred;
System::Event m_httpEvent;
std::queue<MinerEvent> m_events;
CryptoNote::Block m_minedBlock;
uint64_t m_lastBlockTimestamp;
void eventLoop();
MinerEvent waitEvent();
void pushEvent(MinerEvent&& event);
void startMining(const CryptoNote::BlockMiningParameters& params);
void stopMining();
void startBlockchainMonitoring();
void stopBlockchainMonitoring();
bool submitBlock(const CryptoNote::Block& minedBlock, const std::string& daemonHost, uint16_t daemonPort);
CryptoNote::BlockMiningParameters requestMiningParameters(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, const std::string& miningAddress);
void adjustBlockTemplate(CryptoNote::Block& blockTemplate) const;
};
} //namespace Miner

138
src/Miner/MiningConfig.cpp Normal file
View file

@ -0,0 +1,138 @@
// 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 <http://www.gnu.org/licenses/>.
#include "MiningConfig.h"
#include <iostream>
#include <thread>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include "CryptoNoteConfig.h"
#include "Logging/ILogger.h"
namespace po = boost::program_options;
namespace CryptoNote {
namespace {
const size_t DEFAULT_SCANT_PERIOD = 30;
const char* DEFAULT_DAEMON_HOST = "127.0.0.1";
const size_t CONCURRENCY_LEVEL = std::thread::hardware_concurrency();
po::options_description cmdOptions;
void parseDaemonAddress(const std::string& daemonAddress, std::string& daemonHost, uint16_t& daemonPort) {
std::vector<std::string> splittedAddress;
boost::algorithm::split(splittedAddress, daemonAddress, boost::algorithm::is_any_of(":"));
if (splittedAddress.size() != 2) {
throw std::runtime_error("Wrong daemon address format");
}
if (splittedAddress[0].empty() || splittedAddress[1].empty()) {
throw std::runtime_error("Wrong daemon address format");
}
daemonHost = splittedAddress[0];
try {
daemonPort = boost::lexical_cast<uint16_t>(splittedAddress[1]);
} catch (std::exception&) {
throw std::runtime_error("Wrong daemon address format");
}
}
}
MiningConfig::MiningConfig(): help(false) {
cmdOptions.add_options()
("help,h", "produce this help message and exit")
("address", po::value<std::string>(), "Valid cryptonote miner's address")
("daemon-host", po::value<std::string>()->default_value(DEFAULT_DAEMON_HOST), "Daemon host")
("daemon-rpc-port", po::value<uint16_t>()->default_value(static_cast<uint16_t>(RPC_DEFAULT_PORT)), "Daemon's RPC port")
("daemon-address", po::value<std::string>(), "Daemon host:port. If you use this option you must not use --daemon-host and --daemon-port options")
("threads", po::value<size_t>()->default_value(CONCURRENCY_LEVEL), "Mining threads count. Must not be greater than you concurrency level. Default value is your hardware concurrency level")
("scan-time", po::value<size_t>()->default_value(DEFAULT_SCANT_PERIOD), "Blockchain polling interval (seconds). How often miner will check blockchain for updates")
("log-level", po::value<int>()->default_value(1), "Log level. Must be 0..5")
("limit", po::value<size_t>()->default_value(0), "Mine exact quantity of blocks. 0 means no limit")
("first-block-timestamp", po::value<uint64_t>()->default_value(0), "Set timestamp to the first mined block. 0 means leave timestamp unchanged")
("block-timestamp-interval", po::value<int64_t>()->default_value(0), "Timestamp step for each subsequent block. May be set only if --first-block-timestamp has been set."
" If not set blocks' timestamps remain unchanged");
}
void MiningConfig::parse(int argc, char** argv) {
po::variables_map options;
po::store(po::parse_command_line(argc, argv, cmdOptions), options);
po::notify(options);
if (options.count("help") != 0) {
help = true;
return;
}
if (options.count("address") == 0) {
throw std::runtime_error("Specify --address option");
}
miningAddress = options["address"].as<std::string>();
if (!options["daemon-address"].empty()) {
if (!options["daemon-host"].defaulted() || !options["daemon-rpc-port"].defaulted()) {
throw std::runtime_error("Either --daemon-host or --daemon-rpc-port is already specified. You must not specify --daemon-address");
}
parseDaemonAddress(options["daemon-address"].as<std::string>(), daemonHost, daemonPort);
} else {
daemonHost = options["daemon-host"].as<std::string>();
daemonPort = options["daemon-rpc-port"].as<uint16_t>();
}
threadCount = options["threads"].as<size_t>();
if (threadCount == 0 || threadCount > CONCURRENCY_LEVEL) {
throw std::runtime_error("--threads option must be 1.." + std::to_string(CONCURRENCY_LEVEL));
}
scanPeriod = options["scan-time"].as<size_t>();
if (scanPeriod == 0) {
throw std::runtime_error("--scan-time must not be zero");
}
logLevel = static_cast<uint8_t>(options["log-level"].as<int>());
if (logLevel > static_cast<uint8_t>(Logging::TRACE)) {
throw std::runtime_error("--log-level value is too big");
}
blocksLimit = options["limit"].as<size_t>();
if (!options["block-timestamp-interval"].defaulted() && options["first-block-timestamp"].defaulted()) {
throw std::runtime_error("If you specify --block-timestamp-interval you must specify --first-block-timestamp either");
}
firstBlockTimestamp = options["first-block-timestamp"].as<uint64_t>();
blockTimestampInterval = options["block-timestamp-interval"].as<int64_t>();
}
void MiningConfig::printHelp() {
std::cout << cmdOptions << std::endl;
}
}

43
src/Miner/MiningConfig.h Normal file
View file

@ -0,0 +1,43 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
namespace CryptoNote {
struct MiningConfig {
MiningConfig();
void parse(int argc, char** argv);
void printHelp();
std::string miningAddress;
std::string daemonHost;
uint16_t daemonPort;
size_t threadCount;
size_t scanPeriod;
uint8_t logLevel;
size_t blocksLimit;
uint64_t firstBlockTimestamp;
int64_t blockTimestampInterval;
bool help;
};
} //namespace CryptoNote

52
src/Miner/main.cpp Normal file
View file

@ -0,0 +1,52 @@
// 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 <http://www.gnu.org/licenses/>.
#include "Common/SignalHandler.h"
#include "Logging/LoggerGroup.h"
#include "Logging/ConsoleLogger.h"
#include "Logging/LoggerRef.h"
#include "MinerManager.h"
#include <System/Dispatcher.h>
int main(int argc, char** argv) {
try {
CryptoNote::MiningConfig config;
config.parse(argc, argv);
if (config.help) {
config.printHelp();
return 0;
}
Logging::LoggerGroup loggerGroup;
Logging::ConsoleLogger consoleLogger(static_cast<Logging::Level>(config.logLevel));
loggerGroup.addLogger(consoleLogger);
System::Dispatcher dispatcher;
Miner::MinerManager app(dispatcher, config, loggerGroup);
app.start();
} catch (std::exception& e) {
std::cerr << "Fatal: " << e.what() << std::endl;
return 1;
}
return 0;
}

View file

@ -225,36 +225,36 @@ void NodeRpcProxy::updateBlockchainStatus() {
m_lastKnowHash = blockHash; m_lastKnowHash = blockHash;
m_nodeHeight.store(static_cast<uint32_t>(rsp.block_header.height), std::memory_order_relaxed); m_nodeHeight.store(static_cast<uint32_t>(rsp.block_header.height), std::memory_order_relaxed);
m_lastLocalBlockTimestamp.store(rsp.block_header.timestamp, std::memory_order_relaxed); m_lastLocalBlockTimestamp.store(rsp.block_header.timestamp, std::memory_order_relaxed);
// TODO request and update network height
m_networkHeight.store(static_cast<uint32_t>(rsp.block_header.height), std::memory_order_relaxed);
m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight.load(std::memory_order_relaxed));
//if (m_networkHeight.load(std::memory_order_relaxed) != rsp.block_header.network_height) {
// m_networkHeight.store(rsp.block_header.height, std::memory_order_relaxed);
// m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight);
//}
m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight.load(std::memory_order_relaxed)); m_observerManager.notify(&INodeObserver::localBlockchainUpdated, m_nodeHeight.load(std::memory_order_relaxed));
} }
} }
updatePeerCount(); CryptoNote::COMMAND_RPC_GET_INFO::request getInfoReq = AUTO_VAL_INIT(getInfoReq);
CryptoNote::COMMAND_RPC_GET_INFO::response getInfoResp = AUTO_VAL_INIT(getInfoResp);
ec = jsonCommand("/getinfo", getInfoReq, getInfoResp);
if (!ec) {
//a quirk to let wallets work with previous versions daemons.
//Previous daemons didn't have the 'last_known_block_index' parameter in RPC so it may have zero value.
auto lastKnownBlockIndex = std::max(getInfoResp.last_known_block_index, m_nodeHeight.load(std::memory_order_relaxed));
if (m_networkHeight.load(std::memory_order_relaxed) != lastKnownBlockIndex) {
m_networkHeight.store(lastKnownBlockIndex, std::memory_order_relaxed);
m_observerManager.notify(&INodeObserver::lastKnownBlockHeightUpdated, m_networkHeight.load(std::memory_order_relaxed));
}
updatePeerCount(getInfoResp.incoming_connections_count + getInfoResp.outgoing_connections_count);
}
if (m_connected != m_httpClient->isConnected()) { if (m_connected != m_httpClient->isConnected()) {
m_connected = m_httpClient->isConnected(); m_connected = m_httpClient->isConnected();
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected); m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
} }
} }
void NodeRpcProxy::updatePeerCount() { void NodeRpcProxy::updatePeerCount(size_t peerCount) {
CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); if (peerCount != m_peerCount) {
CryptoNote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp); m_peerCount = peerCount;
m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount.load(std::memory_order_relaxed));
std::error_code ec = jsonCommand("/getinfo", req, rsp);
if (!ec) {
size_t peerCount = rsp.incoming_connections_count + rsp.outgoing_connections_count;
if (peerCount != m_peerCount) {
m_peerCount = peerCount;
m_observerManager.notify(&INodeObserver::peerCountUpdated, m_peerCount.load(std::memory_order_relaxed));
}
} }
} }
@ -306,7 +306,7 @@ uint32_t NodeRpcProxy::getLocalBlockCount() const {
} }
uint32_t NodeRpcProxy::getKnownBlockCount() const { uint32_t NodeRpcProxy::getKnownBlockCount() const {
return m_networkHeight.load(std::memory_order_relaxed); return m_networkHeight.load(std::memory_order_relaxed) + 1;
} }
uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const { uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const {

View file

@ -92,7 +92,7 @@ private:
void updateNodeStatus(); void updateNodeStatus();
void updateBlockchainStatus(); void updateBlockchainStatus();
bool updatePoolStatus(); bool updatePoolStatus();
void updatePeerCount(); void updatePeerCount(size_t peerCount);
void updatePoolState(const std::vector<std::unique_ptr<ITransactionReader>>& addedTxs, const std::vector<Crypto::Hash>& deletedTxsIds); void updatePoolState(const std::vector<std::unique_ptr<ITransactionReader>>& addedTxs, const std::vector<Crypto::Hash>& deletedTxsIds);
std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction); std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction);

View file

@ -20,138 +20,114 @@
namespace PaymentService { namespace PaymentService {
void TransferDestination::serialize(CryptoNote::ISerializer& serializer) { void Reset::Request::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(amount, "amount"); serializer(viewSecretKey, "viewSecretKey");
r &= serializer(address, "address"); }
if (!r) { void Reset::Response::serialize(CryptoNote::ISerializer& serializer) {
}
void GetViewKey::Request::serialize(CryptoNote::ISerializer& serializer) {
}
void GetViewKey::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(viewSecretKey, "viewSecretKey");
}
void GetStatus::Request::serialize(CryptoNote::ISerializer& serializer) {
}
void GetStatus::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(blockCount, "blockCount");
serializer(knownBlockCount, "knownBlockCount");
serializer(lastBlockHash, "lastBlockHash");
serializer(peerCount, "peerCount");
}
void GetAddresses::Request::serialize(CryptoNote::ISerializer& serializer) {
}
void GetAddresses::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(addresses, "addresses");
}
void CreateAddress::Request::serialize(CryptoNote::ISerializer& serializer) {
bool hasSecretKey = serializer(spendSecretKey, "spendSecretKey");
bool hasPublicKey = serializer(spendPublicKey, "spendPublicKey");
if (hasSecretKey && hasPublicKey) {
//TODO: replace it with error codes
throw RequestSerializationError(); throw RequestSerializationError();
} }
} }
void SendTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { void CreateAddress::Response::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(destinations, "destinations");
r &= serializer(fee, "fee");
r &= serializer(mixin, "mixin");
serializer(unlockTime, "unlock_time");
serializer(paymentId, "payment_id");
if (!r) {
throw RequestSerializationError();
}
}
void SendTransactionResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionId, "transaction_id");
}
void GetAddressRequest::serialize(CryptoNote::ISerializer& serializer) {
serializer(index, "index");
}
void DeleteAddressRequest::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(address, "address");
if (!r) {
throw RequestSerializationError();
}
}
void DeleteAddressResponse::serialize(CryptoNote::ISerializer& serializer) {
}
void CreateAddressResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(address, "address"); serializer(address, "address");
} }
void GetAddressCountResponse::serialize(CryptoNote::ISerializer& serializer) { void DeleteAddress::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(count, "count"); if (!serializer(address, "address")) {
} throw RequestSerializationError();
void GetAddressResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(address, "address");
}
void GetActualBalanceRequest::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(address, "address");
if (!r) {
throw std::runtime_error("Required parameter is missing");
} }
} }
void GetPendingBalanceRequest::serialize(CryptoNote::ISerializer& serializer) { void DeleteAddress::Response::serialize(CryptoNote::ISerializer& serializer) {
}
void GetSpendKeys::Request::serialize(CryptoNote::ISerializer& serializer) {
if (!serializer(address, "address")) {
throw RequestSerializationError();
}
}
void GetSpendKeys::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(spendSecretKey, "spendSecretKey");
serializer(spendPublicKey, "spendPublicKey");
}
void GetBalance::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(address, "address"); serializer(address, "address");
} }
void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer) { void GetBalance::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(actualBalance, "actual_balance"); serializer(availableBalance, "availableBalance");
serializer(lockedAmount, "lockedAmount");
} }
void GetPendingBalanceResponse::serialize(CryptoNote::ISerializer& serializer) { void GetBlockHashes::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(pendingBalance, "pending_balance"); bool r = serializer(firstBlockIndex, "firstBlockIndex");
} r &= serializer(blockCount, "blockCount");
void GetTransactionsCountResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionsCount, "transactions_count");
}
void GetTransfersCountResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(transfersCount, "transfers_count");
}
void GetTransactionIdByTransferIdRequest::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(transferId, "transfer_id");
if (!r) { if (!r) {
throw RequestSerializationError(); throw RequestSerializationError();
} }
} }
void GetTransactionIdByTransferIdResponse::serialize(CryptoNote::ISerializer& serializer) { void GetBlockHashes::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionid, "transaction_id"); serializer(blockHashes, "blockHashes");
} }
void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer) { void TransactionHashesInBlockRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(transactionId, "transaction_id"); serializer(blockHash, "blockHash");
serializer(transactionHashes, "transactionHashes");
}
if (!r) { void GetTransactionHashes::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(addresses, "addresses");
if (serializer(blockHash, "blockHash") == serializer(firstBlockIndex, "firstBlockIndex")) {
throw RequestSerializationError(); throw RequestSerializationError();
} }
}
void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) { if (!serializer(blockCount, "blockCount")) {
serializer(firstTransferId, "first_transfer_id"); throw RequestSerializationError();
serializer(transferCount, "transfer_count");
serializer(totalAmount, "total_amount");
serializer(fee, "fee");
serializer(hash, "hash");
serializer(blockHeight, "block_height");
serializer(state, "state");
serializer(timestamp, "timestamp");
serializer(extra, "extra");
serializer(transfers, "transfers");
}
void GetTransactionResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(found, "found");
if (found) {
serializer(transactionInfo, "transaction_info");
} }
serializer(paymentId, "paymentId");
} }
void ListTransactionsRequest::serialize(CryptoNote::ISerializer& serializer) { void GetTransactionHashes::Response::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(startingTransactionId, "starting_transaction_id"); serializer(items, "items");
r &= serializer(maxTransactionCount, "max_transaction_count");
if (!r) {
throw std::runtime_error("Required parameter is missing");
}
}
void ListTransactionsResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactions, "transactions");
} }
void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) { void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
@ -160,43 +136,155 @@ void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
serializer(amount, "amount"); serializer(amount, "amount");
} }
void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer) { void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(transferId, "transfer_id"); serializer(state, "state");
serializer(transactionHash, "transactionHash");
if (!r) { serializer(blockIndex, "blockIndex");
throw RequestSerializationError(); serializer(timestamp, "timestamp");
} serializer(isBase, "isBase");
} serializer(unlockTime, "unlockTime");
void GetTransferResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(found, "found");
if (found) {
serializer(transferInfo, "transfer_info");
}
}
void GetIncomingPaymentsRequest::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(payments, "payments");
if (!r) {
throw RequestSerializationError();
}
}
void PaymentsById::serialize(CryptoNote::ISerializer& serializer) {
serializer(id, "id");
serializer(payments, "payments");
}
void GetIncomingPaymentsResponse::serialize(CryptoNote::ISerializer& serializer) {
serializer(payments, "payments");
}
void PaymentDetails::serialize(CryptoNote::ISerializer& serializer) {
serializer(txHash, "tx_hash");
serializer(amount, "amount"); serializer(amount, "amount");
serializer(blockHeight, "block_height"); serializer(fee, "fee");
serializer(unlockTime, "unlock_time"); serializer(transfers, "transfers");
serializer(extra, "extra");
serializer(paymentId, "paymentId");
}
void GetTransaction::Request::serialize(CryptoNote::ISerializer& serializer) {
if (!serializer(transactionHash, "transactionHash")) {
throw RequestSerializationError();
}
}
void GetTransaction::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(transaction, "transaction");
}
void TransactionsInBlockRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
serializer(blockHash, "blockHash");
serializer(transactions, "transactions");
}
void GetTransactions::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(addresses, "addresses");
if (serializer(blockHash, "blockHash") == serializer(firstBlockIndex, "firstBlockIndex")) {
throw RequestSerializationError();
}
if (!serializer(blockCount, "blockCount")) {
throw RequestSerializationError();
}
serializer(paymentId, "paymentId");
}
void GetTransactions::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(items, "items");
}
void GetUnconfirmedTransactionHashes::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(addresses, "addresses");
}
void GetUnconfirmedTransactionHashes::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionHashes, "transactionHashes");
}
void WalletRpcOrder::serialize(CryptoNote::ISerializer& serializer) {
bool r = serializer(address, "address");
r &= serializer(amount, "amount");
if (!r) {
throw RequestSerializationError();
}
}
void SendTransaction::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(sourceAddresses, "addresses");
if (!serializer(transfers, "transfers")) {
throw RequestSerializationError();
}
serializer(changeAddress, "changeAddress");
if (!serializer(fee, "fee")) {
throw RequestSerializationError();
}
if (!serializer(anonymity, "anonymity")) {
throw RequestSerializationError();
}
bool hasExtra = serializer(extra, "extra");
bool hasPaymentId = serializer(paymentId, "paymentId");
if (hasExtra && hasPaymentId) {
throw RequestSerializationError();
}
serializer(unlockTime, "unlockTime");
}
void SendTransaction::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionHash, "transactionHash");
}
void CreateDelayedTransaction::Request::serialize(CryptoNote::ISerializer& serializer) {
serializer(addresses, "addresses");
if (!serializer(transfers, "transfers")) {
throw RequestSerializationError();
}
serializer(changeAddress, "changeAddress");
if (!serializer(fee, "fee")) {
throw RequestSerializationError();
}
if (!serializer(anonymity, "anonymity")) {
throw RequestSerializationError();
}
bool hasExtra = serializer(extra, "extra");
bool hasPaymentId = serializer(paymentId, "paymentId");
if (hasExtra && hasPaymentId) {
throw RequestSerializationError();
}
serializer(unlockTime, "unlockTime");
}
void CreateDelayedTransaction::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionHash, "transactionHash");
}
void GetDelayedTransactionHashes::Request::serialize(CryptoNote::ISerializer& serializer) {
}
void GetDelayedTransactionHashes::Response::serialize(CryptoNote::ISerializer& serializer) {
serializer(transactionHashes, "transactionHashes");
}
void DeleteDelayedTransaction::Request::serialize(CryptoNote::ISerializer& serializer) {
if (!serializer(transactionHash, "transactionHash")) {
throw RequestSerializationError();
}
}
void DeleteDelayedTransaction::Response::serialize(CryptoNote::ISerializer& serializer) {
}
void SendDelayedTransaction::Request::serialize(CryptoNote::ISerializer& serializer) {
if (!serializer(transactionHash, "transactionHash")) {
throw RequestSerializationError();
}
}
void SendDelayedTransaction::Response::serialize(CryptoNote::ISerializer& serializer) {
} }
} }

View file

@ -18,122 +18,166 @@
#pragma once #pragma once
#include <exception> #include <exception>
#include <limits>
#include <vector> #include <vector>
#include "Serialization/ISerializer.h" #include "Serialization/ISerializer.h"
namespace PaymentService { namespace PaymentService {
const uint32_t DEFAULT_ANONYMITY_LEVEL = 6;
class RequestSerializationError: public std::exception { class RequestSerializationError: public std::exception {
public: public:
virtual const char* what() const throw() override { return "Request error"; } virtual const char* what() const throw() override { return "Request error"; }
}; };
struct TransferDestination { struct Reset {
uint64_t amount; struct Request {
std::string address; std::string viewSecretKey;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetViewKey {
struct Request {
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::string viewSecretKey;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetStatus {
struct Request {
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
uint32_t blockCount;
uint32_t knownBlockCount;
std::string lastBlockHash;
uint32_t peerCount;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetAddresses {
struct Request {
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::vector<std::string> addresses;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct CreateAddress {
struct Request {
std::string spendSecretKey;
std::string spendPublicKey;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct DeleteAddress {
struct Request {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetSpendKeys {
struct Request {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::string spendSecretKey;
std::string spendPublicKey;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetBalance {
struct Request {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
uint64_t availableBalance;
uint64_t lockedAmount;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetBlockHashes {
struct Request {
uint32_t firstBlockIndex;
uint32_t blockCount;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::vector<std::string> blockHashes;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct TransactionHashesInBlockRpcInfo {
std::string blockHash;
std::vector<std::string> transactionHashes;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
}; };
struct SendTransactionRequest { struct GetTransactionHashes {
SendTransactionRequest() : unlockTime(0) {} struct Request {
std::vector<std::string> addresses;
std::string blockHash;
uint32_t firstBlockIndex = std::numeric_limits<uint32_t>::max();
uint32_t blockCount;
std::string paymentId;
std::vector<TransferDestination> destinations; void serialize(CryptoNote::ISerializer& serializer);
uint64_t fee; };
uint64_t mixin;
uint64_t unlockTime;
std::string paymentId;
void serialize(CryptoNote::ISerializer& serializer); struct Response {
}; std::vector<TransactionHashesInBlockRpcInfo> items;
struct SendTransactionResponse { void serialize(CryptoNote::ISerializer& serializer);
uint64_t transactionId; };
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetAddressRequest {
GetAddressRequest() : index(0) {}
size_t index;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetAddressCountResponse {
std::size_t count;
void serialize(CryptoNote::ISerializer& serializer);
};
struct DeleteAddressRequest {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct DeleteAddressResponse {
void serialize(CryptoNote::ISerializer& serializer);
};
struct CreateAddressResponse {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetAddressResponse {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetActualBalanceRequest {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetActualBalanceResponse {
uint64_t actualBalance;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetPendingBalanceRequest {
std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetPendingBalanceResponse {
uint64_t pendingBalance;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetTransactionsCountResponse {
uint64_t transactionsCount;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetTransfersCountResponse {
uint64_t transfersCount;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetTransactionIdByTransferIdRequest {
uint64_t transferId;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetTransactionIdByTransferIdResponse {
uint64_t transactionid;
void serialize(CryptoNote::ISerializer& serializer);
};
struct GetTransactionRequest {
uint64_t transactionId;
void serialize(CryptoNote::ISerializer& serializer);
}; };
struct TransferRpcInfo { struct TransferRpcInfo {
@ -145,80 +189,157 @@ struct TransferRpcInfo {
}; };
struct TransactionRpcInfo { struct TransactionRpcInfo {
uint64_t firstTransferId;
uint64_t transferCount;
int64_t totalAmount;
uint64_t fee;
std::string hash;
uint64_t blockHeight;
uint64_t timestamp;
std::string extra;
uint8_t state; uint8_t state;
std::string transactionHash;
uint32_t blockIndex;
uint64_t timestamp;
bool isBase;
uint64_t unlockTime;
int64_t amount;
uint64_t fee;
std::vector<TransferRpcInfo> transfers; std::vector<TransferRpcInfo> transfers;
std::string extra;
std::string paymentId;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
}; };
struct GetTransactionResponse { struct GetTransaction {
bool found; struct Request {
TransactionRpcInfo transactionInfo; std::string transactionHash;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
TransactionRpcInfo transaction;
void serialize(CryptoNote::ISerializer& serializer);
};
}; };
struct ListTransactionsRequest { struct TransactionsInBlockRpcInfo {
uint32_t startingTransactionId; std::string blockHash;
uint32_t maxTransactionCount;
void serialize(CryptoNote::ISerializer& serializer);
};
struct ListTransactionsResponse {
std::vector<TransactionRpcInfo> transactions; std::vector<TransactionRpcInfo> transactions;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
}; };
struct GetTransferRequest { struct GetTransactions {
uint64_t transferId; struct Request {
std::vector<std::string> addresses;
std::string blockHash;
uint32_t firstBlockIndex = std::numeric_limits<uint32_t>::max();
uint32_t blockCount;
std::string paymentId;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::vector<TransactionsInBlockRpcInfo> items;
void serialize(CryptoNote::ISerializer& serializer);
};
}; };
struct GetTransferResponse { struct GetUnconfirmedTransactionHashes {
bool found; struct Request {
TransferRpcInfo transferInfo; std::vector<std::string> addresses;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::vector<std::string> transactionHashes;
void serialize(CryptoNote::ISerializer& serializer);
};
}; };
struct GetIncomingPaymentsRequest { struct WalletRpcOrder {
std::vector<std::string> payments; std::string address;
void serialize(CryptoNote::ISerializer& serializer);
};
struct PaymentDetails
{
std::string txHash;
uint64_t amount; uint64_t amount;
uint64_t blockHeight;
uint64_t unlockTime;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
}; };
struct PaymentsById { struct SendTransaction {
std::string id; struct Request {
std::vector<PaymentDetails> payments; std::vector<std::string> sourceAddresses;
std::vector<WalletRpcOrder> transfers;
std::string changeAddress;
uint64_t fee = 0;
uint32_t anonymity = DEFAULT_ANONYMITY_LEVEL;
std::string extra;
std::string paymentId;
uint64_t unlockTime = 0;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::string transactionHash;
void serialize(CryptoNote::ISerializer& serializer);
};
}; };
struct GetIncomingPaymentsResponse { struct CreateDelayedTransaction {
std::vector<PaymentsById> payments; struct Request {
std::vector<std::string> addresses;
std::vector<WalletRpcOrder> transfers;
std::string changeAddress;
uint64_t fee = 0;
uint32_t anonymity = DEFAULT_ANONYMITY_LEVEL;
std::string extra;
std::string paymentId;
uint64_t unlockTime = 0;
void serialize(CryptoNote::ISerializer& serializer); void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::string transactionHash;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct GetDelayedTransactionHashes {
struct Request {
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
std::vector<std::string> transactionHashes;
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct DeleteDelayedTransaction {
struct Request {
std::string transactionHash;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
void serialize(CryptoNote::ISerializer& serializer);
};
};
struct SendDelayedTransaction {
struct Request {
std::string transactionHash;
void serialize(CryptoNote::ISerializer& serializer);
};
struct Response {
void serialize(CryptoNote::ISerializer& serializer);
};
}; };
} //namespace PaymentService } //namespace PaymentService

View file

@ -17,10 +17,11 @@
#include "PaymentServiceJsonRpcServer.h" #include "PaymentServiceJsonRpcServer.h"
#include <functional>
#include "PaymentServiceJsonRpcMessages.h" #include "PaymentServiceJsonRpcMessages.h"
#include "WalletService.h" #include "WalletService.h"
#include "Common/JsonValue.h"
#include "Serialization/JsonInputValueSerializer.h" #include "Serialization/JsonInputValueSerializer.h"
#include "Serialization/JsonOutputStreamSerializer.h" #include "Serialization/JsonOutputStreamSerializer.h"
@ -31,299 +32,157 @@ PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys
, service(service) , service(service)
, logger(loggerGroup, "PaymentServiceJsonRpcServer") , logger(loggerGroup, "PaymentServiceJsonRpcServer")
{ {
handlers.emplace("reset", jsonHandler<Reset::Request, Reset::Response>(std::bind(&PaymentServiceJsonRpcServer::handleReset, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("createAddress", jsonHandler<CreateAddress::Request, CreateAddress::Response>(std::bind(&PaymentServiceJsonRpcServer::handleCreateAddress, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("deleteAddress", jsonHandler<DeleteAddress::Request, DeleteAddress::Response>(std::bind(&PaymentServiceJsonRpcServer::handleDeleteAddress, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getSpendKeys", jsonHandler<GetSpendKeys::Request, GetSpendKeys::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetSpendKeys, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getBalance", jsonHandler<GetBalance::Request, GetBalance::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetBalance, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getBlockHashes", jsonHandler<GetBlockHashes::Request, GetBlockHashes::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetBlockHashes, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getTransactionHashes", jsonHandler<GetTransactionHashes::Request, GetTransactionHashes::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetTransactionHashes, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getTransactions", jsonHandler<GetTransactions::Request, GetTransactions::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetTransactions, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getUnconfirmedTransactionHashes", jsonHandler<GetUnconfirmedTransactionHashes::Request, GetUnconfirmedTransactionHashes::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetUnconfirmedTransactionHashes, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getTransaction", jsonHandler<GetTransaction::Request, GetTransaction::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetTransaction, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("sendTransaction", jsonHandler<SendTransaction::Request, SendTransaction::Response>(std::bind(&PaymentServiceJsonRpcServer::handleSendTransaction, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("createDelayedTransaction", jsonHandler<CreateDelayedTransaction::Request, CreateDelayedTransaction::Response>(std::bind(&PaymentServiceJsonRpcServer::handleCreateDelayedTransaction, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getDelayedTransactionHashes", jsonHandler<GetDelayedTransactionHashes::Request, GetDelayedTransactionHashes::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetDelayedTransactionHashes, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("deleteDelayedTransaction", jsonHandler<DeleteDelayedTransaction::Request, DeleteDelayedTransaction::Response>(std::bind(&PaymentServiceJsonRpcServer::handleDeleteDelayedTransaction, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("sendDelayedTransaction", jsonHandler<SendDelayedTransaction::Request, SendDelayedTransaction::Response>(std::bind(&PaymentServiceJsonRpcServer::handleSendDelayedTransaction, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getViewKey", jsonHandler<GetViewKey::Request, GetViewKey::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetViewKey, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getStatus", jsonHandler<GetStatus::Request, GetStatus::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetStatus, this, std::placeholders::_1, std::placeholders::_2)));
handlers.emplace("getAddresses", jsonHandler<GetAddresses::Request, GetAddresses::Response>(std::bind(&PaymentServiceJsonRpcServer::handleGetAddresses, this, std::placeholders::_1, std::placeholders::_2)));
} }
void PaymentServiceJsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { void PaymentServiceJsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) {
try { try {
prepareJsonResponse(req, resp); prepareJsonResponse(req, resp);
if (!req.contains("method")) {
logger(Logging::WARNING) << "Field \"method\" is not found in json request: " << req;
makeGenericErrorReponse(resp, "Invalid Request", -3600);
return;
}
if (!req("method").isString()) {
logger(Logging::WARNING) << "Field \"method\" is not a string type: " << req;
makeGenericErrorReponse(resp, "Invalid Request", -3600);
return;
}
std::string method = req("method").getString(); std::string method = req("method").getString();
CryptoNote::JsonOutputStreamSerializer outputSerializer; auto it = handlers.find(method);
if (it == handlers.end()) {
if (method == "send_transaction") { logger(Logging::WARNING) << "Requested method not found: " << method;
SendTransactionRequest sendReq;
SendTransactionResponse sendResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(sendReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec = service.sendTransaction(sendReq, sendResp);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(sendResp, outputSerializer);
} else if (method == "get_address") {
GetAddressRequest getAddrReq;
GetAddressResponse getAddrResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(getAddrReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec = service.getAddress(getAddrReq.index, getAddrResp.address);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(getAddrResp, outputSerializer);
} else if (method == "create_address") {
CreateAddressResponse createAddrResp;
std::error_code ec = service.createAddress(createAddrResp.address);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(createAddrResp, outputSerializer);
} else if (method == "get_address_count") {
GetAddressCountResponse addressCountResp;
std::error_code ec = service.getAddressCount(addressCountResp.count);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(addressCountResp, outputSerializer);
} else if (method == "delete_address") {
DeleteAddressRequest delAddrReq;
DeleteAddressResponse delAddrResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(delAddrReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec = service.deleteAddress(delAddrReq.address);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(delAddrResp, outputSerializer);
} else if (method == "get_actual_balance") {
GetActualBalanceRequest actualReq;
GetActualBalanceResponse actualResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(actualReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec;
if (actualReq.address == "") {
ec = service.getActualBalance(actualResp.actualBalance);
} else {
ec = service.getActualBalance(actualReq.address, actualResp.actualBalance);
}
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(actualResp, outputSerializer);
} else if (method == "get_pending_balance") {
GetPendingBalanceRequest pendingReq;
GetPendingBalanceResponse pendingResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(pendingReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec;
if (pendingReq.address == "") {
ec = service.getPendingBalance(pendingResp.pendingBalance);
} else {
ec = service.getPendingBalance(pendingReq.address, pendingResp.pendingBalance);
}
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(pendingResp, outputSerializer);
} else if (method == "get_transactions_count") {
GetTransactionsCountResponse txResp;
std::error_code ec = service.getTransactionsCount(txResp.transactionsCount);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(txResp, outputSerializer);
} else if (method == "get_transfers_count") {
GetTransfersCountResponse trResp;
std::error_code ec = service.getTransfersCount(trResp.transfersCount);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(trResp, outputSerializer);
} else if (method == "get_transaction_id_by_transfer_id") {
GetTransactionIdByTransferIdRequest getReq;
GetTransactionIdByTransferIdResponse getResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(getReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
size_t txId;
std::error_code ec = service.getTransactionByTransferId(getReq.transferId, txId);
getResp.transactionid = txId;
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(getResp, outputSerializer);
} else if (method == "get_transaction") {
GetTransactionRequest getReq;
GetTransactionResponse getResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(getReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec = service.getTransaction(getReq.transactionId, getResp.found, getResp.transactionInfo);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(getResp, outputSerializer);
} else if (method == "list_transactions") {
ListTransactionsRequest listReq;
ListTransactionsResponse listResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(listReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec = service.listTransactions(static_cast<size_t>(listReq.startingTransactionId), listReq.maxTransactionCount, listResp.transactions);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(listResp, outputSerializer);
} else if (method == "get_transfer") {
GetTransferRequest getReq;
GetTransferResponse getResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(getReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
std::error_code ec = service.getTransfer(getReq.transferId, getResp.found, getResp.transferInfo);
if (ec) {
makeErrorResponse(ec, resp);
return;
}
serialize(getResp, outputSerializer);
} else if (method == "get_incoming_payments") {
GetIncomingPaymentsRequest getReq;
GetIncomingPaymentsResponse getResp;
//XXX: refactor it when migrate to different exception types in different subsystems!
try {
CryptoNote::JsonInputValueSerializer inputSerializer(req("params"));
serialize(getReq, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
return;
}
WalletService::IncomingPayments payments;
std::error_code ec = service.getIncomingPayments(getReq.payments, payments);
if (ec) {
if (ec == make_error_code(std::errc::argument_out_of_domain)) {
makeGenericErrorReponse(resp, "Invalid Request", -32600);
} else {
makeErrorResponse(ec, resp);
}
return;
}
for (auto p: payments) {
PaymentsById pbid;
pbid.id = std::move(p.first);
pbid.payments = std::move(p.second);
getResp.payments.push_back(std::move(pbid));
}
serialize(getResp, outputSerializer);
} else {
logger(Logging::DEBUGGING) << "Requested method not found: " << method;
makeMethodNotFoundResponse(resp); makeMethodNotFoundResponse(resp);
return; return;
} }
fillJsonResponse(outputSerializer.getValue(), resp); logger(Logging::DEBUGGING) << method << " request came";
} catch (RequestSerializationError&) { Common::JsonValue params(Common::JsonValue::OBJECT);
logger(Logging::WARNING) << "Wrong request came"; if (req.contains("params")) {
makeGenericErrorReponse(resp, "Invalid Request", -32600); params = req("params");
}
it->second(params, resp);
} catch (std::exception& e) { } catch (std::exception& e) {
logger(Logging::WARNING) << "Error occurred while processing JsonRpc request: " << e.what(); logger(Logging::WARNING) << "Error occurred while processing JsonRpc request: " << e.what();
makeGenericErrorReponse(resp, e.what()); makeGenericErrorReponse(resp, e.what());
} }
} }
std::error_code PaymentServiceJsonRpcServer::handleReset(const Reset::Request& request, Reset::Response& response) {
if (request.viewSecretKey.empty()) {
return service.resetWallet();
} else {
return service.replaceWithNewWallet(request.viewSecretKey);
}
}
std::error_code PaymentServiceJsonRpcServer::handleCreateAddress(const CreateAddress::Request& request, CreateAddress::Response& response) {
if (request.spendSecretKey.empty() && request.spendPublicKey.empty()) {
return service.createAddress(response.address);
} else if (!request.spendSecretKey.empty()) {
return service.createAddress(request.spendSecretKey, response.address);
} else {
return service.createTrackingAddress(request.spendPublicKey, response.address);
}
}
std::error_code PaymentServiceJsonRpcServer::handleDeleteAddress(const DeleteAddress::Request& request, DeleteAddress::Response& response) {
return service.deleteAddress(request.address);
}
std::error_code PaymentServiceJsonRpcServer::handleGetSpendKeys(const GetSpendKeys::Request& request, GetSpendKeys::Response& response) {
return service.getSpendkeys(request.address, response.spendPublicKey, response.spendSecretKey);
}
std::error_code PaymentServiceJsonRpcServer::handleGetBalance(const GetBalance::Request& request, GetBalance::Response& response) {
if (!request.address.empty()) {
return service.getBalance(request.address, response.availableBalance, response.lockedAmount);
} else {
return service.getBalance(response.availableBalance, response.lockedAmount);
}
}
std::error_code PaymentServiceJsonRpcServer::handleGetBlockHashes(const GetBlockHashes::Request& request, GetBlockHashes::Response& response) {
return service.getBlockHashes(request.firstBlockIndex, request.blockCount, response.blockHashes);
}
std::error_code PaymentServiceJsonRpcServer::handleGetTransactionHashes(const GetTransactionHashes::Request& request, GetTransactionHashes::Response& response) {
if (!request.blockHash.empty()) {
return service.getTransactionHashes(request.addresses, request.blockHash, request.blockCount, request.paymentId, response.items);
} else {
return service.getTransactionHashes(request.addresses, request.firstBlockIndex, request.blockCount, request.paymentId, response.items);
}
}
std::error_code PaymentServiceJsonRpcServer::handleGetTransactions(const GetTransactions::Request& request, GetTransactions::Response& response) {
if (!request.blockHash.empty()) {
return service.getTransactions(request.addresses, request.blockHash, request.blockCount, request.paymentId, response.items);
} else {
return service.getTransactions(request.addresses, request.firstBlockIndex, request.blockCount, request.paymentId, response.items);
}
}
std::error_code PaymentServiceJsonRpcServer::handleGetUnconfirmedTransactionHashes(const GetUnconfirmedTransactionHashes::Request& request, GetUnconfirmedTransactionHashes::Response& response) {
return service.getUnconfirmedTransactionHashes(request.addresses, response.transactionHashes);
}
std::error_code PaymentServiceJsonRpcServer::handleGetTransaction(const GetTransaction::Request& request, GetTransaction::Response& response) {
return service.getTransaction(request.transactionHash, response.transaction);
}
std::error_code PaymentServiceJsonRpcServer::handleSendTransaction(const SendTransaction::Request& request, SendTransaction::Response& response) {
return service.sendTransaction(request, response.transactionHash);
}
std::error_code PaymentServiceJsonRpcServer::handleCreateDelayedTransaction(const CreateDelayedTransaction::Request& request, CreateDelayedTransaction::Response& response) {
return service.createDelayedTransaction(request, response.transactionHash);
}
std::error_code PaymentServiceJsonRpcServer::handleGetDelayedTransactionHashes(const GetDelayedTransactionHashes::Request& request, GetDelayedTransactionHashes::Response& response) {
return service.getDelayedTransactionHashes(response.transactionHashes);
}
std::error_code PaymentServiceJsonRpcServer::handleDeleteDelayedTransaction(const DeleteDelayedTransaction::Request& request, DeleteDelayedTransaction::Response& response) {
return service.deleteDelayedTransaction(request.transactionHash);
}
std::error_code PaymentServiceJsonRpcServer::handleSendDelayedTransaction(const SendDelayedTransaction::Request& request, SendDelayedTransaction::Response& response) {
return service.sendDelayedTransaction(request.transactionHash);
}
std::error_code PaymentServiceJsonRpcServer::handleGetViewKey(const GetViewKey::Request& request, GetViewKey::Response& response) {
return service.getViewKey(response.viewSecretKey);
}
std::error_code PaymentServiceJsonRpcServer::handleGetStatus(const GetStatus::Request& request, GetStatus::Response& response) {
return service.getStatus(response.blockCount, response.knownBlockCount, response.lastBlockHash, response.peerCount);
}
std::error_code PaymentServiceJsonRpcServer::handleGetAddresses(const GetAddresses::Request& request, GetAddresses::Response& response) {
return service.getAddresses(response.addresses);
}
} }

View file

@ -17,7 +17,13 @@
#pragma once #pragma once
#include <unordered_map>
#include "Common/JsonValue.h"
#include "JsonRpcServer/JsonRpcServer.h" #include "JsonRpcServer/JsonRpcServer.h"
#include "PaymentServiceJsonRpcMessages.h"
#include "Serialization/JsonInputValueSerializer.h"
#include "Serialization/JsonOutputStreamSerializer.h"
namespace PaymentService { namespace PaymentService {
@ -34,6 +40,56 @@ protected:
private: private:
WalletService& service; WalletService& service;
Logging::LoggerRef logger; Logging::LoggerRef logger;
typedef std::function<void (const Common::JsonValue& jsonRpcParams, Common::JsonValue& jsonResponse)> HandlerFunction;
template <typename RequestType, typename ResponseType, typename RequestHandler>
HandlerFunction jsonHandler(RequestHandler handler) {
return [handler] (const Common::JsonValue& jsonRpcParams, Common::JsonValue& jsonResponse) mutable {
RequestType request;
ResponseType response;
try {
CryptoNote::JsonInputValueSerializer inputSerializer(const_cast<Common::JsonValue&>(jsonRpcParams));
serialize(request, inputSerializer);
} catch (std::exception&) {
makeGenericErrorReponse(jsonResponse, "Invalid Request", -32600);
return;
}
std::error_code ec = handler(request, response);
if (ec) {
makeErrorResponse(ec, jsonResponse);
return;
}
CryptoNote::JsonOutputStreamSerializer outputSerializer;
serialize(response, outputSerializer);
fillJsonResponse(outputSerializer.getValue(), jsonResponse);
};
}
std::unordered_map<std::string, HandlerFunction> handlers;
std::error_code handleReset(const Reset::Request& request, Reset::Response& response);
std::error_code handleCreateAddress(const CreateAddress::Request& request, CreateAddress::Response& response);
std::error_code handleDeleteAddress(const DeleteAddress::Request& request, DeleteAddress::Response& response);
std::error_code handleGetSpendKeys(const GetSpendKeys::Request& request, GetSpendKeys::Response& response);
std::error_code handleGetBalance(const GetBalance::Request& request, GetBalance::Response& response);
std::error_code handleGetBlockHashes(const GetBlockHashes::Request& request, GetBlockHashes::Response& response);
std::error_code handleGetTransactionHashes(const GetTransactionHashes::Request& request, GetTransactionHashes::Response& response);
std::error_code handleGetTransactions(const GetTransactions::Request& request, GetTransactions::Response& response);
std::error_code handleGetUnconfirmedTransactionHashes(const GetUnconfirmedTransactionHashes::Request& request, GetUnconfirmedTransactionHashes::Response& response);
std::error_code handleGetTransaction(const GetTransaction::Request& request, GetTransaction::Response& response);
std::error_code handleSendTransaction(const SendTransaction::Request& request, SendTransaction::Response& response);
std::error_code handleCreateDelayedTransaction(const CreateDelayedTransaction::Request& request, CreateDelayedTransaction::Response& response);
std::error_code handleGetDelayedTransactionHashes(const GetDelayedTransactionHashes::Request& request, GetDelayedTransactionHashes::Response& response);
std::error_code handleDeleteDelayedTransaction(const DeleteDelayedTransaction::Request& request, DeleteDelayedTransaction::Response& response);
std::error_code handleSendDelayedTransaction(const SendDelayedTransaction::Request& request, SendDelayedTransaction::Response& response);
std::error_code handleGetViewKey(const GetViewKey::Request& request, GetViewKey::Response& response);
std::error_code handleGetStatus(const GetStatus::Request& request, GetStatus::Response& response);
std::error_code handleGetAddresses(const GetAddresses::Request& request, GetAddresses::Response& response);
}; };
} //namespace PaymentService }//namespace PaymentService

File diff suppressed because it is too large Load diff

View file

@ -35,12 +35,6 @@
namespace PaymentService { namespace PaymentService {
struct SendTransactionRequest;
struct SendTransactionResponse;
struct TransferDestination;
struct TransactionRpcInfo;
struct TransferRpcInfo;
struct WalletConfiguration { struct WalletConfiguration {
std::string walletFile; std::string walletFile;
std::string walletPassword; std::string walletPassword;
@ -48,71 +42,74 @@ struct WalletConfiguration {
void generateNewWallet(const CryptoNote::Currency &currency, const WalletConfiguration &conf, Logging::ILogger &logger, System::Dispatcher& dispatcher); void generateNewWallet(const CryptoNote::Currency &currency, const WalletConfiguration &conf, Logging::ILogger &logger, System::Dispatcher& dispatcher);
struct TransactionsInBlockInfoFilter;
class WalletService { class WalletService {
public: public:
typedef std::map<std::string, std::vector<PaymentDetails> > IncomingPayments; WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, CryptoNote::IWallet& wallet, const WalletConfiguration& conf, Logging::ILogger& logger);
explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const WalletConfiguration& conf, Logging::ILogger& logger);
virtual ~WalletService(); virtual ~WalletService();
void init(); void init();
void saveWallet(); void saveWallet();
std::error_code sendTransaction(const SendTransactionRequest& req, SendTransactionResponse& resp); std::error_code resetWallet();
std::error_code getIncomingPayments(const std::vector<std::string>& payments, IncomingPayments& result); std::error_code replaceWithNewWallet(const std::string& viewSecretKey);
std::error_code getAddress(size_t index, std::string& address); std::error_code createAddress(const std::string& spendSecretKeyText, std::string& address);
std::error_code getAddressCount(size_t& count);
std::error_code createAddress(std::string& address); std::error_code createAddress(std::string& address);
std::error_code createTrackingAddress(const std::string& spendPublicKeyText, std::string& address);
std::error_code deleteAddress(const std::string& address); std::error_code deleteAddress(const std::string& address);
std::error_code getActualBalance(const std::string& address, uint64_t& actualBalance); std::error_code getSpendkeys(const std::string& address, std::string& publicSpendKeyText, std::string& secretSpendKeyText);
std::error_code getPendingBalance(const std::string& address, uint64_t& pendingBalance); std::error_code getBalance(const std::string& address, uint64_t& availableBalance, uint64_t& lockedAmount);
std::error_code getActualBalance(uint64_t& actualBalance); std::error_code getBalance(uint64_t& availableBalance, uint64_t& lockedAmount);
std::error_code getPendingBalance(uint64_t& pendingBalance); std::error_code getBlockHashes(uint32_t firstBlockIndex, uint32_t blockCount, std::vector<std::string>& blockHashes);
std::error_code getTransactionsCount(uint64_t& txCount); std::error_code getViewKey(std::string& viewSecretKey);
std::error_code getTransfersCount(uint64_t& trCount); std::error_code getTransactionHashes(const std::vector<std::string>& addresses, const std::string& blockHash,
std::error_code getTransactionByTransferId(size_t transfer, size_t& transaction); uint32_t blockCount, const std::string& paymentId, std::vector<TransactionHashesInBlockRpcInfo>& transactionHashes);
std::error_code getTransaction(size_t txId, bool& found, TransactionRpcInfo& rpcInfo); std::error_code getTransactionHashes(const std::vector<std::string>& addresses, uint32_t firstBlockIndex,
std::error_code listTransactions(size_t startingTxId, uint32_t maxTxCount, std::vector<TransactionRpcInfo>& txsRpcInfo); uint32_t blockCount, const std::string& paymentId, std::vector<TransactionHashesInBlockRpcInfo>& transactionHashes);
std::error_code getTransfer(size_t txId, bool& found, TransferRpcInfo& rpcInfo); std::error_code getTransactions(const std::vector<std::string>& addresses, const std::string& blockHash,
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionsInBlockRpcInfo>& transactionHashes);
std::error_code getTransactions(const std::vector<std::string>& addresses, uint32_t firstBlockIndex,
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionsInBlockRpcInfo>& transactionHashes);
std::error_code getTransaction(const std::string& transactionHash, TransactionRpcInfo& transaction);
std::error_code getAddresses(std::vector<std::string>& addresses);
std::error_code sendTransaction(const SendTransaction::Request& request, std::string& transactionHash);
std::error_code createDelayedTransaction(const CreateDelayedTransaction::Request& request, std::string& transactionHash);
std::error_code getDelayedTransactionHashes(std::vector<std::string>& transactionHashes);
std::error_code deleteDelayedTransaction(const std::string& transactionHash);
std::error_code sendDelayedTransaction(const std::string& transactionHash);
std::error_code getUnconfirmedTransactionHashes(const std::vector<std::string>& addresses, std::vector<std::string>& transactionHashes);
std::error_code getStatus(uint32_t& blockCount, uint32_t& knownBlockCount, std::string& lastBlockHash, uint32_t& peerCount);
private: private:
void refresh(); void refresh();
void reset();
void loadWallet(); void loadWallet();
void loadPaymentsCacheAndTransferIndices(); void loadTransactionIdIndex();
void insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed);
void fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo); void replaceWithNewWallet(const Crypto::SecretKey& viewSecretKey);
void makeOrders(const std::vector<TransferDestination>& destinations, std::vector<CryptoNote::WalletOrder>& transfers);
struct PaymentItem { std::vector<CryptoNote::TransactionsInBlockInfo> getTransactions(const Crypto::Hash& blockHash, size_t blockCount) const;
std::string paymentId; std::vector<CryptoNote::TransactionsInBlockInfo> getTransactions(uint32_t firstBlockIndex, size_t blockCount) const;
size_t transactionId;
bool confirmed;
};
typedef boost::multi_index::hashed_unique<BOOST_MULTI_INDEX_MEMBER(PaymentItem, size_t, transactionId)> TxIdIndex; std::vector<TransactionHashesInBlockRpcInfo> getRpcTransactionHashes(const Crypto::Hash& blockHash, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
typedef boost::multi_index::hashed_non_unique<BOOST_MULTI_INDEX_MEMBER(PaymentItem, std::string, paymentId)> PaymentIndex; std::vector<TransactionHashesInBlockRpcInfo> getRpcTransactionHashes(uint32_t firstBlockIndex, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
typedef boost::multi_index::multi_index_container<
PaymentItem,
boost::multi_index::indexed_by<
TxIdIndex,
PaymentIndex
>
> PaymentsContainer;
std::unique_ptr<CryptoNote::IWallet > wallet; std::vector<TransactionsInBlockRpcInfo> getRpcTransactions(const Crypto::Hash& blockHash, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
CryptoNote::INode* node; std::vector<TransactionsInBlockRpcInfo> getRpcTransactions(uint32_t firstBlockIndex, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
const CryptoNote::Currency& currency;
CryptoNote::IWallet& wallet;
CryptoNote::INode& node;
const WalletConfiguration& config; const WalletConfiguration& config;
bool inited; bool inited;
Logging::LoggerRef logger; Logging::LoggerRef logger;
std::vector<size_t> transfersIndices;
System::Dispatcher& dispatcher; System::Dispatcher& dispatcher;
System::Event readyEvent;
System::ContextGroup refreshContext; System::ContextGroup refreshContext;
PaymentsContainer paymentsCache; std::map<std::string, size_t> transactionIdIndex;
PaymentsContainer::nth_index<0>::type& txIdIndex;
PaymentsContainer::nth_index<1>::type& paymentIdIndex;
}; };
} //namespace PaymentService } //namespace PaymentService

View file

@ -0,0 +1,26 @@
// 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 <http://www.gnu.org/licenses/>.
#include "WalletServiceErrorCategory.h"
namespace CryptoNote {
namespace error {
WalletServiceErrorCategory WalletServiceErrorCategory::INSTANCE;
}
}

View file

@ -0,0 +1,75 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <system_error>
namespace CryptoNote {
namespace error {
enum class WalletServiceErrorCode {
WRONG_KEY_FORMAT = 1,
WRONG_PAYMENT_ID_FORMAT,
WRONG_HASH_FORMAT,
OBJECT_NOT_FOUND
};
// custom category:
class WalletServiceErrorCategory : public std::error_category {
public:
static WalletServiceErrorCategory INSTANCE;
virtual const char* name() const throw() {
return "WalletServiceErrorCategory";
}
virtual std::error_condition default_error_condition(int ev) const throw() {
return std::error_condition(ev, *this);
}
virtual std::string message(int ev) const {
WalletServiceErrorCode code = static_cast<WalletServiceErrorCode>(ev);
switch (code) {
case WalletServiceErrorCode::WRONG_KEY_FORMAT: return "Wrong key format";
case WalletServiceErrorCode::WRONG_PAYMENT_ID_FORMAT: return "Wrong payment id format";
case WalletServiceErrorCode::WRONG_HASH_FORMAT: return "Wrong block id format";
case WalletServiceErrorCode::OBJECT_NOT_FOUND: return "Requested object not found";
default: return "Unknown error";
}
}
private:
WalletServiceErrorCategory() {
}
};
} //namespace error
} //namespace CryptoNote
inline std::error_code make_error_code(CryptoNote::error::WalletServiceErrorCode e) {
return std::error_code(static_cast<int>(e), CryptoNote::error::WalletServiceErrorCategory::INSTANCE);
}
namespace std {
template <>
struct is_error_code_enum<CryptoNote::error::WalletServiceErrorCode>: public true_type {};
}

View file

@ -28,6 +28,7 @@
#include "CryptoNoteCore/Core.h" #include "CryptoNoteCore/Core.h"
#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h" #include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h"
#include "P2p/NetNode.h" #include "P2p/NetNode.h"
#include "PaymentGate/WalletFactory.h"
#include <System/Context.h> #include <System/Context.h>
#ifdef ERROR #ifdef ERROR
@ -86,8 +87,8 @@ bool PaymentGateService::init(int argc, char** argv) {
WalletConfiguration PaymentGateService::getWalletConfig() const { WalletConfiguration PaymentGateService::getWalletConfig() const {
return WalletConfiguration{ return WalletConfiguration{
config.gateConfiguration.walletFile, config.gateConfiguration.containerFile,
config.gateConfiguration.walletPassword config.gateConfiguration.containerPassword
}; };
} }
@ -132,6 +133,16 @@ void PaymentGateService::stop() {
} }
void PaymentGateService::runInProcess(Logging::LoggerRef& log) { void PaymentGateService::runInProcess(Logging::LoggerRef& log) {
if (!config.coreConfig.configFolderDefaulted) {
if (!Tools::directoryExists(config.coreConfig.configFolder)) {
throw std::runtime_error("Directory does not exist: " + config.coreConfig.configFolder);
}
} else {
if (!Tools::create_directories_if_necessary(config.coreConfig.configFolder)) {
throw std::runtime_error("Can't create directory: " + config.coreConfig.configFolder);
}
}
log(Logging::INFO) << "Starting Payment Gate with local node"; log(Logging::INFO) << "Starting Payment Gate with local node";
CryptoNote::Currency currency = currencyBuilder.currency(); CryptoNote::Currency currency = currencyBuilder.currency();
@ -206,11 +217,13 @@ void PaymentGateService::runRpcProxy(Logging::LoggerRef& log) {
void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node) { void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, CryptoNote::INode& node) {
PaymentService::WalletConfiguration walletConfiguration{ PaymentService::WalletConfiguration walletConfiguration{
config.gateConfiguration.walletFile, config.gateConfiguration.containerFile,
config.gateConfiguration.walletPassword config.gateConfiguration.containerPassword
}; };
service = new PaymentService::WalletService(currency, *dispatcher, node, walletConfiguration, logger); std::unique_ptr<CryptoNote::IWallet> wallet (WalletFactory::createWallet(currency, node, *dispatcher));
service = new PaymentService::WalletService(currency, *dispatcher, node, *wallet, walletConfiguration, logger);
std::unique_ptr<PaymentService::WalletService> serviceGuard(service); std::unique_ptr<PaymentService::WalletService> serviceGuard(service);
try { try {
service->init(); service->init();
@ -221,13 +234,10 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency,
if (config.gateConfiguration.printAddresses) { if (config.gateConfiguration.printAddresses) {
// print addresses and exit // print addresses and exit
size_t addressCount = 0; std::vector<std::string> addresses;
service->getAddressCount(addressCount); service->getAddresses(addresses);
for (size_t i = 0; i < addressCount; ++i) { for (const auto& address: addresses) {
std::string address; std::cout << "Address: " << address << std::endl;
if (service->getAddress(i, address) == std::error_code()) {
std::cout << "Address: " << address << std::endl;
}
} }
} else { } else {
PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger); PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger);
@ -236,7 +246,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency,
try { try {
service->saveWallet(); service->saveWallet();
} catch (std::exception& ex) { } catch (std::exception& ex) {
Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING, Logging::YELLOW) << "Couldn't save wallet: " << ex.what(); Logging::LoggerRef(logger, "saveWallet")(Logging::WARNING, Logging::YELLOW) << "Couldn't save container: " << ex.what();
} }
} }
} }

View file

@ -28,7 +28,7 @@ namespace po = boost::program_options;
namespace PaymentService { namespace PaymentService {
Configuration::Configuration() { Configuration::Configuration() {
generateNewWallet = false; generateNewContainer = false;
daemonize = false; daemonize = false;
registerService = false; registerService = false;
unregisterService = false; unregisterService = false;
@ -44,9 +44,9 @@ void Configuration::initOptions(boost::program_options::options_description& des
desc.add_options() desc.add_options()
("bind-address", po::value<std::string>()->default_value("0.0.0.0"), "payment service bind address") ("bind-address", po::value<std::string>()->default_value("0.0.0.0"), "payment service bind address")
("bind-port", po::value<uint16_t>()->default_value(8070), "payment service bind port") ("bind-port", po::value<uint16_t>()->default_value(8070), "payment service bind port")
("wallet-file,w", po::value<std::string>(), "wallet file") ("container-file,w", po::value<std::string>(), "container file")
("wallet-password,p", po::value<std::string>(), "wallet password") ("container-password,p", po::value<std::string>(), "container password")
("generate-wallet,g", "generate new wallet file and exit") ("generate-container,g", "generate new container file with one wallet and exit")
("daemon,d", "run as daemon in Unix or as service in Windows") ("daemon,d", "run as daemon in Unix or as service in Windows")
#ifdef _WIN32 #ifdef _WIN32
("register-service", "register service and exit (Windows only)") ("register-service", "register service and exit (Windows only)")
@ -103,16 +103,16 @@ void Configuration::init(const boost::program_options::variables_map& options) {
bindPort = options["bind-port"].as<uint16_t>(); bindPort = options["bind-port"].as<uint16_t>();
} }
if (options.count("wallet-file") != 0) { if (options.count("container-file") != 0) {
walletFile = options["wallet-file"].as<std::string>(); containerFile = options["container-file"].as<std::string>();
} }
if (options.count("wallet-password") != 0) { if (options.count("container-password") != 0) {
walletPassword = options["wallet-password"].as<std::string>(); containerPassword = options["container-password"].as<std::string>();
} }
if (options.count("generate-wallet") != 0) { if (options.count("generate-container") != 0) {
generateNewWallet = true; generateNewContainer = true;
} }
if (options.count("address") != 0) { if (options.count("address") != 0) {
@ -120,8 +120,8 @@ void Configuration::init(const boost::program_options::variables_map& options) {
} }
if (!registerService && !unregisterService) { if (!registerService && !unregisterService) {
if (walletFile.empty() || walletPassword.empty()) { if (containerFile.empty() || containerPassword.empty()) {
throw ConfigurationError("Both wallet-file and wallet-password parameters are required"); throw ConfigurationError("Both container-file and container-password parameters are required");
} }
} }
} }

View file

@ -39,12 +39,12 @@ struct Configuration {
std::string bindAddress; std::string bindAddress;
uint16_t bindPort; uint16_t bindPort;
std::string walletFile; std::string containerFile;
std::string walletPassword; std::string containerPassword;
std::string logFile; std::string logFile;
std::string serverRoot; std::string serverRoot;
bool generateNewWallet; bool generateNewContainer;
bool daemonize; bool daemonize;
bool registerService; bool registerService;
bool unregisterService; bool unregisterService;

View file

@ -305,7 +305,7 @@ int main(int argc, char** argv) {
const auto& config = pg.getConfig(); const auto& config = pg.getConfig();
if (config.gateConfiguration.generateNewWallet) { if (config.gateConfiguration.generateNewContainer) {
System::Dispatcher d; System::Dispatcher d;
generateNewWallet(pg.getCurrency(), pg.getWalletConfig(), pg.getLogger(), d); generateNewWallet(pg.getCurrency(), pg.getWalletConfig(), pg.getLogger(), d);
return 0; return 0;

View file

@ -277,6 +277,7 @@ struct COMMAND_RPC_GET_INFO {
uint64_t incoming_connections_count; uint64_t incoming_connections_count;
uint64_t white_peerlist_size; uint64_t white_peerlist_size;
uint64_t grey_peerlist_size; uint64_t grey_peerlist_size;
uint32_t last_known_block_index;
void serialize(ISerializer &s) { void serialize(ISerializer &s) {
KV_MEMBER(status) KV_MEMBER(status)
@ -289,6 +290,7 @@ struct COMMAND_RPC_GET_INFO {
KV_MEMBER(incoming_connections_count) KV_MEMBER(incoming_connections_count)
KV_MEMBER(white_peerlist_size) KV_MEMBER(white_peerlist_size)
KV_MEMBER(grey_peerlist_size) KV_MEMBER(grey_peerlist_size)
KV_MEMBER(last_known_block_index)
} }
}; };
}; };

View file

@ -27,6 +27,9 @@
#include "CryptoNoteCore/IBlock.h" #include "CryptoNoteCore/IBlock.h"
#include "CryptoNoteCore/Miner.h" #include "CryptoNoteCore/Miner.h"
#include "CryptoNoteCore/TransactionExtra.h" #include "CryptoNoteCore/TransactionExtra.h"
#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h"
#include "P2p/NetNode.h" #include "P2p/NetNode.h"
#include "CoreRpcServerErrorCodes.h" #include "CoreRpcServerErrorCodes.h"
@ -78,50 +81,50 @@ RpcServer::HandlerFunction jsonMethod(bool (RpcServer::*handler)(typename Comman
} }
std::unordered_map<std::string, RpcServer::HandlerFunction> RpcServer::s_handlers = { std::unordered_map<std::string, RpcServer::RpcHandler<RpcServer::HandlerFunction>> RpcServer::s_handlers = {
// binary handlers // binary handlers
{ "/getblocks.bin", binMethod<COMMAND_RPC_GET_BLOCKS_FAST>(&RpcServer::on_get_blocks) }, { "/getblocks.bin", { binMethod<COMMAND_RPC_GET_BLOCKS_FAST>(&RpcServer::on_get_blocks), false } },
{ "/queryblocks.bin", binMethod<COMMAND_RPC_QUERY_BLOCKS>(&RpcServer::on_query_blocks) }, { "/queryblocks.bin", { binMethod<COMMAND_RPC_QUERY_BLOCKS>(&RpcServer::on_query_blocks), false } },
{ "/queryblockslite.bin", binMethod<COMMAND_RPC_QUERY_BLOCKS_LITE>(&RpcServer::on_query_blocks_lite) }, { "/queryblockslite.bin", { binMethod<COMMAND_RPC_QUERY_BLOCKS_LITE>(&RpcServer::on_query_blocks_lite), false } },
{ "/get_o_indexes.bin", binMethod<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(&RpcServer::on_get_indexes) }, { "/get_o_indexes.bin", { binMethod<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(&RpcServer::on_get_indexes), false } },
{ "/getrandom_outs.bin", binMethod<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(&RpcServer::on_get_random_outs) }, { "/getrandom_outs.bin", { binMethod<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(&RpcServer::on_get_random_outs), false } },
{ "/get_pool_changes.bin", binMethod<COMMAND_RPC_GET_POOL_CHANGES>(&RpcServer::onGetPoolChanges) }, { "/get_pool_changes.bin", { binMethod<COMMAND_RPC_GET_POOL_CHANGES>(&RpcServer::onGetPoolChanges), false } },
{ "/get_pool_changes_lite.bin", binMethod<COMMAND_RPC_GET_POOL_CHANGES_LITE>(&RpcServer::onGetPoolChangesLite) }, { "/get_pool_changes_lite.bin", { binMethod<COMMAND_RPC_GET_POOL_CHANGES_LITE>(&RpcServer::onGetPoolChangesLite), false } },
// json handlers // json handlers
{ "/getinfo", jsonMethod<COMMAND_RPC_GET_INFO>(&RpcServer::on_get_info) }, { "/getinfo", { jsonMethod<COMMAND_RPC_GET_INFO>(&RpcServer::on_get_info), true } },
{ "/getheight", jsonMethod<COMMAND_RPC_GET_HEIGHT>(&RpcServer::on_get_height) }, { "/getheight", { jsonMethod<COMMAND_RPC_GET_HEIGHT>(&RpcServer::on_get_height), true } },
{ "/gettransactions", jsonMethod<COMMAND_RPC_GET_TRANSACTIONS>(&RpcServer::on_get_transactions)}, { "/gettransactions", { jsonMethod<COMMAND_RPC_GET_TRANSACTIONS>(&RpcServer::on_get_transactions), false } },
{ "/sendrawtransaction", jsonMethod<COMMAND_RPC_SEND_RAW_TX>(&RpcServer::on_send_raw_tx) }, { "/sendrawtransaction", { jsonMethod<COMMAND_RPC_SEND_RAW_TX>(&RpcServer::on_send_raw_tx), false } },
{ "/start_mining", jsonMethod<COMMAND_RPC_START_MINING>(&RpcServer::on_start_mining) }, { "/start_mining", { jsonMethod<COMMAND_RPC_START_MINING>(&RpcServer::on_start_mining), false } },
{ "/stop_mining", jsonMethod<COMMAND_RPC_STOP_MINING>(&RpcServer::on_stop_mining) }, { "/stop_mining", { jsonMethod<COMMAND_RPC_STOP_MINING>(&RpcServer::on_stop_mining), false } },
{ "/stop_daemon", jsonMethod<COMMAND_RPC_STOP_DAEMON>(&RpcServer::on_stop_daemon) }, { "/stop_daemon", { jsonMethod<COMMAND_RPC_STOP_DAEMON>(&RpcServer::on_stop_daemon), true } },
// json rpc // json rpc
{ "/json_rpc", std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) } { "/json_rpc", { std::bind(&RpcServer::processJsonRpcRequest, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), true } }
}; };
RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p) : RpcServer::RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p, const ICryptoNoteProtocolQuery& protocolQuery) :
HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(p2p) { HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(p2p), m_protocolQuery(protocolQuery) {
} }
void RpcServer::processRequest(const HttpRequest& request, HttpResponse& response) { void RpcServer::processRequest(const HttpRequest& request, HttpResponse& response) {
auto url = request.getUrl(); auto url = request.getUrl();
auto it = s_handlers.find(url); auto it = s_handlers.find(url);
if (it == s_handlers.end()) { if (it == s_handlers.end()) {
response.setStatus(HttpResponse::STATUS_404); response.setStatus(HttpResponse::STATUS_404);
return; return;
} }
if (url != "/json_rpc" && !checkCoreReady()) { if (!it->second.allowBusyCore && !isCoreReady()) {
response.setStatus(HttpResponse::STATUS_500); response.setStatus(HttpResponse::STATUS_500);
response.setBody("Core is busy"); response.setBody("Core is busy");
return; return;
} }
it->second(this, request, response); it->second.handler(this, request, response);
} }
bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& response) { bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& response) {
@ -138,15 +141,15 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
jsonRequest.parseRequest(request.getBody()); jsonRequest.parseRequest(request.getBody());
jsonResponse.setId(jsonRequest.getId()); // copy id jsonResponse.setId(jsonRequest.getId()); // copy id
static std::unordered_map<std::string, JsonMemberMethod> jsonRpcHandlers = { static std::unordered_map<std::string, RpcServer::RpcHandler<JsonMemberMethod>> jsonRpcHandlers = {
{ "getblockcount", makeMemberMethod(&RpcServer::on_getblockcount) }, { "getblockcount", { makeMemberMethod(&RpcServer::on_getblockcount), true } },
{ "on_getblockhash", makeMemberMethod(&RpcServer::on_getblockhash) }, { "on_getblockhash", { makeMemberMethod(&RpcServer::on_getblockhash), false } },
{ "getblocktemplate", makeMemberMethod(&RpcServer::on_getblocktemplate) }, { "getblocktemplate", { makeMemberMethod(&RpcServer::on_getblocktemplate), false } },
{ "getcurrencyid", makeMemberMethod(&RpcServer::on_get_currency_id) }, { "getcurrencyid", { makeMemberMethod(&RpcServer::on_get_currency_id), true } },
{ "submitblock", makeMemberMethod(&RpcServer::on_submitblock) }, { "submitblock", { makeMemberMethod(&RpcServer::on_submitblock), false } },
{ "getlastblockheader", makeMemberMethod(&RpcServer::on_get_last_block_header) }, { "getlastblockheader", { makeMemberMethod(&RpcServer::on_get_last_block_header), false } },
{ "getblockheaderbyhash", makeMemberMethod(&RpcServer::on_get_block_header_by_hash) }, { "getblockheaderbyhash", { makeMemberMethod(&RpcServer::on_get_block_header_by_hash), false } },
{ "getblockheaderbyheight", makeMemberMethod(&RpcServer::on_get_block_header_by_height) } { "getblockheaderbyheight", { makeMemberMethod(&RpcServer::on_get_block_header_by_height), false } }
}; };
auto it = jsonRpcHandlers.find(jsonRequest.getMethod()); auto it = jsonRpcHandlers.find(jsonRequest.getMethod());
@ -154,11 +157,11 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
throw JsonRpcError(JsonRpc::errMethodNotFound); throw JsonRpcError(JsonRpc::errMethodNotFound);
} }
if (jsonRequest.getMethod() != "getcurrencyid" && !checkCoreReady()) { if (!it->second.allowBusyCore && !isCoreReady()) {
throw JsonRpcError(CORE_RPC_ERROR_CODE_CORE_BUSY, "Core is busy"); throw JsonRpcError(CORE_RPC_ERROR_CODE_CORE_BUSY, "Core is busy");
} }
it->second(this, jsonRequest, jsonResponse); it->second.handler(this, jsonRequest, jsonResponse);
} catch (const JsonRpcError& err) { } catch (const JsonRpcError& err) {
jsonResponse.setError(err); jsonResponse.setError(err);
@ -171,10 +174,8 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
return true; return true;
} }
#define CHECK_CORE_READY() bool RpcServer::isCoreReady() {
return m_core.currency().isTestnet() || m_p2p.get_payload_object().isSynchronized();
bool RpcServer::checkCoreReady() {
return m_core.is_ready() && m_p2p.get_payload_object().isSynchronized();
} }
// //
@ -219,8 +220,6 @@ bool RpcServer::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, C
} }
bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res) { bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, COMMAND_RPC_QUERY_BLOCKS::response& res) {
CHECK_CORE_READY();
uint32_t startHeight; uint32_t startHeight;
uint32_t currentHeight; uint32_t currentHeight;
uint32_t fullOffset; uint32_t fullOffset;
@ -238,8 +237,6 @@ bool RpcServer::on_query_blocks(const COMMAND_RPC_QUERY_BLOCKS::request& req, CO
} }
bool RpcServer::on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res) { bool RpcServer::on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::request& req, COMMAND_RPC_QUERY_BLOCKS_LITE::response& res) {
CHECK_CORE_READY();
uint32_t startHeight; uint32_t startHeight;
uint32_t currentHeight; uint32_t currentHeight;
uint32_t fullOffset; uint32_t fullOffset;
@ -256,8 +253,6 @@ bool RpcServer::on_query_blocks_lite(const COMMAND_RPC_QUERY_BLOCKS_LITE::reques
} }
bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) {
CHECK_CORE_READY();
std::vector<uint32_t> outputIndexes; std::vector<uint32_t> outputIndexes;
if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) { if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) {
res.status = "Failed"; res.status = "Failed";
@ -271,7 +266,6 @@ bool RpcServer::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::
} }
bool RpcServer::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { bool RpcServer::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) {
CHECK_CORE_READY();
res.status = "Failed"; res.status = "Failed";
if (!m_core.get_random_outs_for_amounts(req, res)) { if (!m_core.get_random_outs_for_amounts(req, res)) {
return true; return true;
@ -301,8 +295,6 @@ bool RpcServer::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOU
} }
bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp) { bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& req, COMMAND_RPC_GET_POOL_CHANGES::response& rsp) {
CHECK_CORE_READY();
rsp.status = CORE_RPC_STATUS_OK; rsp.status = CORE_RPC_STATUS_OK;
std::vector<CryptoNote::Transaction> addedTransactions; std::vector<CryptoNote::Transaction> addedTransactions;
rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds); rsp.isTailBlockActual = m_core.getPoolChanges(req.tailBlockId, req.knownTxsIds, addedTransactions, rsp.deletedTxsIds);
@ -320,8 +312,6 @@ bool RpcServer::onGetPoolChanges(const COMMAND_RPC_GET_POOL_CHANGES::request& re
bool RpcServer::onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp) { bool RpcServer::onGetPoolChangesLite(const COMMAND_RPC_GET_POOL_CHANGES_LITE::request& req, COMMAND_RPC_GET_POOL_CHANGES_LITE::response& rsp) {
CHECK_CORE_READY();
rsp.status = CORE_RPC_STATUS_OK; rsp.status = CORE_RPC_STATUS_OK;
rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, req.knownTxsIds, rsp.addedTxs, rsp.deletedTxsIds); rsp.isTailBlockActual = m_core.getPoolChangesLite(req.tailBlockId, req.knownTxsIds, rsp.addedTxs, rsp.deletedTxsIds);
@ -343,19 +333,18 @@ bool RpcServer::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RP
res.incoming_connections_count = total_conn - res.outgoing_connections_count; res.incoming_connections_count = total_conn - res.outgoing_connections_count;
res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count(); res.white_peerlist_size = m_p2p.getPeerlistManager().get_white_peers_count();
res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count(); res.grey_peerlist_size = m_p2p.getPeerlistManager().get_gray_peers_count();
res.last_known_block_index = std::max(static_cast<uint32_t>(1), m_protocolQuery.getObservedHeight()) - 1;
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
return true; return true;
} }
bool RpcServer::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { bool RpcServer::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) {
CHECK_CORE_READY();
res.height = m_core.get_current_blockchain_height(); res.height = m_core.get_current_blockchain_height();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
return true; return true;
} }
bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) {
CHECK_CORE_READY();
std::vector<Hash> vh; std::vector<Hash> vh;
for (const auto& tx_hex_str : req.txs_hashes) { for (const auto& tx_hex_str : req.txs_hashes) {
BinaryArray b; BinaryArray b;
@ -387,8 +376,6 @@ bool RpcServer::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request&
} }
bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) {
CHECK_CORE_READY();
BinaryArray tx_blob; BinaryArray tx_blob;
if (!fromHex(req.tx_as_hex, tx_blob)) if (!fromHex(req.tx_as_hex, tx_blob))
{ {
@ -429,7 +416,6 @@ bool RpcServer::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMM
} }
bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) { bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) {
CHECK_CORE_READY();
AccountPublicAddress adr; AccountPublicAddress adr;
if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) { if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) {
res.status = "Failed, wrong address"; res.status = "Failed, wrong address";
@ -446,7 +432,6 @@ bool RpcServer::on_start_mining(const COMMAND_RPC_START_MINING::request& req, CO
} }
bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) { bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) {
CHECK_CORE_READY();
if (!m_core.get_miner().stop()) { if (!m_core.get_miner().stop()) {
res.status = "Failed, mining not stopped"; res.status = "Failed, mining not stopped";
return true; return true;
@ -456,7 +441,6 @@ bool RpcServer::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMM
} }
bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) { bool RpcServer::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res) {
CHECK_CORE_READY();
if (m_core.currency().isTestnet()) { if (m_core.currency().isTestnet()) {
m_p2p.sendStopSignal(); m_p2p.sendStopSignal();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;

View file

@ -27,21 +27,28 @@ namespace CryptoNote {
class core; class core;
class NodeServer; class NodeServer;
class ICryptoNoteProtocolQuery;
class RpcServer : public HttpServer { class RpcServer : public HttpServer {
public: public:
RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p); RpcServer(System::Dispatcher& dispatcher, Logging::ILogger& log, core& c, NodeServer& p2p, const ICryptoNoteProtocolQuery& protocolQuery);
typedef std::function<bool(RpcServer*, const HttpRequest& request, HttpResponse& response)> HandlerFunction; typedef std::function<bool(RpcServer*, const HttpRequest& request, HttpResponse& response)> HandlerFunction;
private: private:
template <class Handler>
struct RpcHandler {
const Handler handler;
const bool allowBusyCore;
};
typedef void (RpcServer::*HandlerPtr)(const HttpRequest& request, HttpResponse& response); typedef void (RpcServer::*HandlerPtr)(const HttpRequest& request, HttpResponse& response);
static std::unordered_map<std::string, HandlerFunction> s_handlers; static std::unordered_map<std::string, RpcHandler<HandlerFunction>> s_handlers;
virtual void processRequest(const HttpRequest& request, HttpResponse& response) override; virtual void processRequest(const HttpRequest& request, HttpResponse& response) override;
bool processJsonRpcRequest(const HttpRequest& request, HttpResponse& response); bool processJsonRpcRequest(const HttpRequest& request, HttpResponse& response);
bool checkCoreReady(); bool isCoreReady();
// binary handlers // binary handlers
bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res);
@ -76,6 +83,7 @@ private:
Logging::LoggerRef logger; Logging::LoggerRef logger;
core& m_core; core& m_core;
NodeServer& m_p2p; NodeServer& m_p2p;
const ICryptoNoteProtocolQuery& m_protocolQuery;
}; };
} }

View file

@ -41,7 +41,10 @@ inline std::vector<uint8_t> stringToVector(const std::string& s) {
namespace CryptoNote { namespace CryptoNote {
BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const Hash& genesisBlockHash) : BlockchainSynchronizer::BlockchainSynchronizer(INode& node, const Hash& genesisBlockHash) :
m_node(node), m_genesisBlockHash(genesisBlockHash), m_currentState(State::stopped), m_futureState(State::stopped), shouldSyncConsumersPool(true) { m_node(node),
m_genesisBlockHash(genesisBlockHash),
m_currentState(State::stopped),
m_futureState(State::stopped) {
} }
BlockchainSynchronizer::~BlockchainSynchronizer() { BlockchainSynchronizer::~BlockchainSynchronizer() {
@ -57,7 +60,6 @@ void BlockchainSynchronizer::addConsumer(IBlockchainConsumer* consumer) {
} }
m_consumers.insert(std::make_pair(consumer, std::make_shared<SynchronizationState>(m_genesisBlockHash))); m_consumers.insert(std::make_pair(consumer, std::make_shared<SynchronizationState>(m_genesisBlockHash)));
shouldSyncConsumersPool = true;
} }
bool BlockchainSynchronizer::removeConsumer(IBlockchainConsumer* consumer) { bool BlockchainSynchronizer::removeConsumer(IBlockchainConsumer* consumer) {
@ -70,23 +72,81 @@ bool BlockchainSynchronizer::removeConsumer(IBlockchainConsumer* consumer) {
return m_consumers.erase(consumer) > 0; return m_consumers.erase(consumer) > 0;
} }
IStreamSerializable* BlockchainSynchronizer::getConsumerState(IBlockchainConsumer* consumer) { IStreamSerializable* BlockchainSynchronizer::getConsumerState(IBlockchainConsumer* consumer) const {
assert(consumer != nullptr);
if (!(checkIfStopped() && checkIfShouldStop())) {
throw std::runtime_error("Can't get consumer state, because BlockchainSynchronizer isn't stopped");
}
std::unique_lock<std::mutex> lk(m_consumersMutex); std::unique_lock<std::mutex> lk(m_consumersMutex);
return getConsumerSynchronizationState(consumer);
auto it = m_consumers.find(consumer);
if (it == m_consumers.end()) {
return nullptr;
}
return it->second.get();
} }
std::vector<Crypto::Hash> BlockchainSynchronizer::getConsumerKnownBlocks(IBlockchainConsumer& consumer) const {
std::unique_lock<std::mutex> lk(m_consumersMutex);
auto state = getConsumerSynchronizationState(&consumer);
if (state == nullptr) {
throw std::invalid_argument("Consumer not found");
}
return state->getKnownBlockHashes();
}
std::future<std::error_code> BlockchainSynchronizer::addUnconfirmedTransaction(const ITransactionReader& transaction) {
std::unique_lock<std::mutex> lock(m_stateMutex);
if (m_currentState == State::stopped || m_futureState == State::stopped) {
throw std::runtime_error("Can't add transaction, because BlockchainSynchronizer is stopped");
}
std::promise<std::error_code> promise;
auto future = promise.get_future();
m_addTransactionTasks.emplace_back(&transaction, std::move(promise));
m_hasWork.notify_one();
return future;
}
std::future<void> BlockchainSynchronizer::removeUnconfirmedTransaction(const Crypto::Hash& transactionHash) {
std::unique_lock<std::mutex> lock(m_stateMutex);
if (m_currentState == State::stopped || m_futureState == State::stopped) {
throw std::runtime_error("Can't remove transaction, because BlockchainSynchronizer is stopped");
}
std::promise<void> promise;
auto future = promise.get_future();
m_removeTransactionTasks.emplace_back(&transactionHash, std::move(promise));
m_hasWork.notify_one();
return future;
}
std::error_code BlockchainSynchronizer::doAddUnconfirmedTransaction(const ITransactionReader& transaction) {
std::unique_lock<std::mutex> lk(m_consumersMutex);
std::error_code ec;
auto addIt = m_consumers.begin();
for (; addIt != m_consumers.end(); ++addIt) {
ec = addIt->first->addUnconfirmedTransaction(transaction);
if (ec) {
break;
}
}
if (ec) {
auto transactionHash = transaction.getTransactionHash();
for (auto rollbackIt = m_consumers.begin(); rollbackIt != addIt; ++rollbackIt) {
rollbackIt->first->removeUnconfirmedTransaction(transactionHash);
}
}
return ec;
}
void BlockchainSynchronizer::doRemoveUnconfirmedTransaction(const Crypto::Hash& transactionHash) {
std::unique_lock<std::mutex> lk(m_consumersMutex);
for (auto& consumer : m_consumers) {
consumer.first->removeUnconfirmedTransaction(transactionHash);
}
}
void BlockchainSynchronizer::save(std::ostream& os) { void BlockchainSynchronizer::save(std::ostream& os) {
os.write(reinterpret_cast<const char*>(&m_genesisBlockHash), sizeof(m_genesisBlockHash)); os.write(reinterpret_cast<const char*>(&m_genesisBlockHash), sizeof(m_genesisBlockHash));
@ -103,16 +163,14 @@ void BlockchainSynchronizer::load(std::istream& in) {
//--------------------------- FSM ------------------------------------ //--------------------------- FSM ------------------------------------
bool BlockchainSynchronizer::setFutureState(State s) { bool BlockchainSynchronizer::setFutureState(State s) {
return setFutureStateIf(s, std::bind( return setFutureStateIf(s, [this, s] { return s > m_futureState; });
[](State futureState, State s) -> bool {
return s > futureState;
}, std::ref(m_futureState), s));
} }
bool BlockchainSynchronizer::setFutureStateIf(State s, std::function<bool(void)>&& pred) { bool BlockchainSynchronizer::setFutureStateIf(State s, std::function<bool(void)>&& pred) {
std::unique_lock<std::mutex> lk(m_stateMutex); std::unique_lock<std::mutex> lk(m_stateMutex);
if (pred()) { if (pred()) {
m_futureState = s; m_futureState = s;
m_hasWork.notify_one();
return true; return true;
} }
@ -129,10 +187,37 @@ void BlockchainSynchronizer::actualizeFutureState() {
m_node.removeObserver(this); m_node.removeObserver(this);
} }
while (!m_removeTransactionTasks.empty()) {
auto& task = m_removeTransactionTasks.front();
const Crypto::Hash& transactionHash = *task.first;
auto detachedPromise = std::move(task.second);
m_removeTransactionTasks.pop_front();
try {
doRemoveUnconfirmedTransaction(transactionHash);
detachedPromise.set_value();
} catch (...) {
detachedPromise.set_exception(std::current_exception());
}
}
while (!m_addTransactionTasks.empty()) {
auto& task = m_addTransactionTasks.front();
const ITransactionReader& transaction = *task.first;
auto detachedPromise = std::move(task.second);
m_addTransactionTasks.pop_front();
try {
auto ec = doAddUnconfirmedTransaction(transaction);
detachedPromise.set_value(ec);
} catch (...) {
detachedPromise.set_exception(std::current_exception());
}
}
m_currentState = m_futureState; m_currentState = m_futureState;
switch (m_futureState) { switch (m_futureState) {
case State::stopped: case State::stopped:
m_futureState = State::stopped;
break; break;
case State::blockchainSync: case State::blockchainSync:
m_futureState = State::poolSync; m_futureState = State::poolSync;
@ -145,21 +230,22 @@ void BlockchainSynchronizer::actualizeFutureState() {
startPoolSync(); startPoolSync();
break; break;
case State::idle: case State::idle:
m_futureState = State::idle; m_hasWork.wait(lk, [this] {
return m_futureState != State::idle || !m_removeTransactionTasks.empty() || !m_addTransactionTasks.empty();
});
lk.unlock(); lk.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
break; break;
default: default:
break; break;
} }
} }
bool BlockchainSynchronizer::checkIfShouldStop() { bool BlockchainSynchronizer::checkIfShouldStop() const {
std::unique_lock<std::mutex> lk(m_stateMutex); std::unique_lock<std::mutex> lk(m_stateMutex);
return m_futureState == State::stopped; return m_futureState == State::stopped;
} }
bool BlockchainSynchronizer::checkIfStopped() { bool BlockchainSynchronizer::checkIfStopped() const {
std::unique_lock<std::mutex> lk(m_stateMutex); std::unique_lock<std::mutex> lk(m_stateMutex);
return m_currentState == State::stopped; return m_currentState == State::stopped;
} }
@ -178,16 +264,11 @@ void BlockchainSynchronizer::start() {
throw std::runtime_error("Can't start, because BlockchainSynchronizer has no consumers"); throw std::runtime_error("Can't start, because BlockchainSynchronizer has no consumers");
} }
if (!setFutureStateIf(State::blockchainSync, std::bind( if (!setFutureStateIf(State::blockchainSync, [this] { return m_currentState == State::stopped && m_futureState == State::stopped; })) {
[](State currentState, State futureState) -> bool {
return currentState == State::stopped && futureState == State::stopped;
}, std::ref(m_currentState), std::ref(m_futureState)))) {
throw std::runtime_error("BlockchainSynchronizer already started"); throw std::runtime_error("BlockchainSynchronizer already started");
} }
shouldSyncConsumersPool = true; workingThread.reset(new std::thread([this] { workingProcedure(); }));
workingThread.reset(new std::thread([this] {this->workingProcedure(); }));
} }
void BlockchainSynchronizer::stop() { void BlockchainSynchronizer::stop() {
@ -201,7 +282,11 @@ void BlockchainSynchronizer::stop() {
workingThread.reset(); workingThread.reset();
} }
void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint32_t height) { void BlockchainSynchronizer::localBlockchainUpdated(uint32_t /*height*/) {
setFutureState(State::blockchainSync);
}
void BlockchainSynchronizer::lastKnownBlockHeightUpdated(uint32_t /*height*/) {
setFutureState(State::blockchainSync); setFutureState(State::blockchainSync);
} }
@ -210,52 +295,27 @@ void BlockchainSynchronizer::poolChanged() {
} }
//--------------------------- FSM END ------------------------------------ //--------------------------- FSM END ------------------------------------
BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getUnionPoolHistory() { void BlockchainSynchronizer::getPoolUnionAndIntersection(std::unordered_set<Crypto::Hash>& poolUnion, std::unordered_set<Crypto::Hash>& poolIntersection) const {
GetPoolRequest request; std::unique_lock<std::mutex> lk(m_consumersMutex);
std::unordered_set<Hash> unionHistory;
{ auto itConsumers = m_consumers.begin();
std::unique_lock<std::mutex> lk(m_consumersMutex); poolUnion = itConsumers->first->getKnownPoolTxIds();
for (auto& consumer : m_consumers) { poolIntersection = itConsumers->first->getKnownPoolTxIds();
std::vector<Hash> consumerKnownIds; ++itConsumers;
consumer.first->getKnownPoolTxIds(consumerKnownIds);
for (auto& txId : consumerKnownIds) { for (; itConsumers != m_consumers.end(); ++itConsumers) {
unionHistory.insert(txId); const std::unordered_set<Crypto::Hash>& consumerKnownIds = itConsumers->first->getKnownPoolTxIds();
poolUnion.insert(consumerKnownIds.begin(), consumerKnownIds.end());
for (auto itIntersection = poolIntersection.begin(); itIntersection != poolIntersection.end();) {
if (consumerKnownIds.count(*itIntersection) == 0) {
itIntersection = poolIntersection.erase(itIntersection);
} else {
++itIntersection;
} }
} }
} }
for (auto& id : unionHistory) {
request.knownTxIds.push_back(id);
}
request.lastKnownBlock = lastBlockId;
return request;
}
BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getIntersectedPoolHistory() {
GetPoolRequest request;
{
std::unique_lock<std::mutex> lk(m_consumersMutex);
auto it = m_consumers.begin();
it->first->getKnownPoolTxIds(request.knownTxIds);
++it;
for (; it != m_consumers.end(); ++it) { //iterate over consumers
std::vector<Hash> consumerKnownIds;
it->first->getKnownPoolTxIds(consumerKnownIds);
for (auto itReq = request.knownTxIds.begin(); itReq != request.knownTxIds.end();) { //iterate over intersection
if (std::count(consumerKnownIds.begin(), consumerKnownIds.end(), *itReq) == 0) { //consumer doesn't contain id from intersection, so delete this id from intersection
itReq = request.knownTxIds.erase(itReq);
} else {
++itReq;
}
}
}
}
request.lastKnownBlock = lastBlockId;
return request;
} }
BlockchainSynchronizer::GetBlocksRequest BlockchainSynchronizer::getCommonHistory() { BlockchainSynchronizer::GetBlocksRequest BlockchainSynchronizer::getCommonHistory() {
@ -290,42 +350,34 @@ void BlockchainSynchronizer::startBlockchainSync() {
try { try {
if (!req.knownBlocks.empty()) { if (!req.knownBlocks.empty()) {
asyncOperationCompleted = std::promise<std::error_code>(); auto queryBlocksCompleted = std::promise<std::error_code>();
asyncOperationWaitFuture = asyncOperationCompleted.get_future(); auto queryBlocksWaitFuture = queryBlocksCompleted.get_future();
m_node.queryBlocks m_node.queryBlocks(
(std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight, std::move(req.knownBlocks),
std::bind(&BlockchainSynchronizer::onGetBlocksCompleted, this, std::placeholders::_1)); req.syncStart.timestamp,
response.newBlocks,
response.startHeight,
[&queryBlocksCompleted](std::error_code ec) {
auto detachedPromise = std::move(queryBlocksCompleted);
detachedPromise.set_value(ec);
});
std::error_code ec = asyncOperationWaitFuture.get(); std::error_code ec = queryBlocksWaitFuture.get();
if (ec) { if (ec) {
setFutureStateIf(State::idle, std::bind( setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
[](State futureState) -> bool {
return futureState != State::stopped;
}, std::ref(m_futureState)));
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec); m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec);
} else { } else {
processBlocks(response); processBlocks(response);
} }
} }
} catch (std::exception& e) { } catch (std::exception&) {
std::cout << e.what() << std::endl; setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
setFutureStateIf(State::idle, std::bind( m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument));
[](State futureState) -> bool {
return futureState != State::stopped;
}, std::ref(m_futureState)));
m_observerManager.notify(
&IBlockchainSynchronizerObserver::synchronizationCompleted,
std::make_error_code(std::errc::invalid_argument));
} }
} }
void BlockchainSynchronizer::onGetBlocksCompleted(std::error_code ec) {
decltype(asyncOperationCompleted) detachedPromise = std::move(asyncOperationCompleted);
detachedPromise.set_value(ec);
}
void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) { void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
BlockchainInterval interval; BlockchainInterval interval;
interval.startHeight = response.startHeight; interval.startHeight = response.startHeight;
@ -335,6 +387,7 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
if (checkIfShouldStop()) { if (checkIfShouldStop()) {
break; break;
} }
CompleteBlock completeBlock; CompleteBlock completeBlock;
completeBlock.blockHash = block.blockHash; completeBlock.blockHash = block.blockHash;
interval.blocks.push_back(completeBlock.blockHash); interval.blocks.push_back(completeBlock.blockHash);
@ -344,19 +397,11 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
try { try {
for (const auto& txShortInfo : block.txsShortInfo) { for (const auto& txShortInfo : block.txsShortInfo) {
completeBlock.transactions.push_back(createTransactionPrefix(txShortInfo.txPrefix, completeBlock.transactions.push_back(createTransactionPrefix(txShortInfo.txPrefix, reinterpret_cast<const Hash&>(txShortInfo.txId)));
reinterpret_cast<const Hash&>(txShortInfo.txId)));
} }
} catch (std::exception&) { } catch (std::exception&) {
setFutureStateIf(State::idle, std::bind( setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
[](State futureState) -> bool { m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument));
return futureState != State::stopped;
}, std::ref(m_futureState)));
m_observerManager.notify(
&IBlockchainSynchronizerObserver::synchronizationCompleted,
std::make_error_code(std::errc::invalid_argument));
return; return;
} }
} }
@ -373,22 +418,18 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
switch (result) { switch (result) {
case UpdateConsumersResult::errorOccurred: case UpdateConsumersResult::errorOccurred:
if (setFutureStateIf(State::idle, std::bind( if (setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; })) {
[](State futureState) -> bool { m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument));
return futureState != State::stopped;
}, std::ref(m_futureState)))) {
m_observerManager.notify(
&IBlockchainSynchronizerObserver::synchronizationCompleted,
std::make_error_code(std::errc::invalid_argument));
} }
break; break;
case UpdateConsumersResult::nothingChanged: case UpdateConsumersResult::nothingChanged:
if (m_node.getLastKnownBlockHeight() != m_node.getLastLocalBlockHeight()) { if (m_node.getLastKnownBlockHeight() != m_node.getLastLocalBlockHeight()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} else { } else {
break; break;
} }
case UpdateConsumersResult::addedNewBlocks: case UpdateConsumersResult::addedNewBlocks:
setFutureState(State::blockchainSync); setFutureState(State::blockchainSync);
m_observerManager.notify( m_observerManager.notify(
@ -404,12 +445,11 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
} }
if (checkIfShouldStop()) { //Sic! if (checkIfShouldStop()) { //Sic!
m_observerManager.notify( m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::interrupted));
&IBlockchainSynchronizerObserver::synchronizationCompleted,
std::make_error_code(std::errc::interrupted));
} }
} }
/// \pre m_consumersMutex is locked
BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateConsumers(const BlockchainInterval& interval, const std::vector<CompleteBlock>& blocks) { BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateConsumers(const BlockchainInterval& interval, const std::vector<CompleteBlock>& blocks) {
bool smthChanged = false; bool smthChanged = false;
@ -424,15 +464,9 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons
if (result.hasNewBlocks) { if (result.hasNewBlocks) {
uint32_t startOffset = result.newBlockHeight - interval.startHeight; uint32_t startOffset = result.newBlockHeight - interval.startHeight;
// update consumer // update consumer
if (kv.first->onNewBlocks( if (kv.first->onNewBlocks(blocks.data() + startOffset, result.newBlockHeight, static_cast<uint32_t>(blocks.size()) - startOffset)) {
blocks.data() + startOffset,
result.newBlockHeight,
static_cast<uint32_t>(blocks.size()) - startOffset)) {
// update state if consumer succeeded // update state if consumer succeeded
kv.second->addBlocks( kv.second->addBlocks(interval.blocks.data() + startOffset, result.newBlockHeight, static_cast<uint32_t>(interval.blocks.size()) - startOffset);
interval.blocks.data() + startOffset,
result.newBlockHeight,
static_cast<uint32_t>(interval.blocks.size()) - startOffset);
smthChanged = true; smthChanged = true;
} else { } else {
return UpdateConsumersResult::errorOccurred; return UpdateConsumersResult::errorOccurred;
@ -444,57 +478,41 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons
} }
void BlockchainSynchronizer::startPoolSync() { void BlockchainSynchronizer::startPoolSync() {
std::unordered_set<Crypto::Hash> unionPoolHistory;
std::unordered_set<Crypto::Hash> intersectedPoolHistory;
getPoolUnionAndIntersection(unionPoolHistory, intersectedPoolHistory);
GetPoolRequest unionRequest;
unionRequest.knownTxIds.assign(unionPoolHistory.begin(), unionPoolHistory.end());
unionRequest.lastKnownBlock = lastBlockId;
GetPoolResponse unionResponse; GetPoolResponse unionResponse;
GetPoolRequest unionRequest = getUnionPoolHistory();
asyncOperationCompleted = std::promise<std::error_code>();
asyncOperationWaitFuture = asyncOperationCompleted.get_future();
unionResponse.isLastKnownBlockActual = false; unionResponse.isLastKnownBlockActual = false;
m_node.getPoolSymmetricDifference(std::move(unionRequest.knownTxIds), std::move(unionRequest.lastKnownBlock), unionResponse.isLastKnownBlockActual, std::error_code ec = getPoolSymmetricDifferenceSync(std::move(unionRequest), unionResponse);
unionResponse.newTxs, unionResponse.deletedTxIds, std::bind(&BlockchainSynchronizer::onGetPoolChanges, this, std::placeholders::_1));
std::error_code ec = asyncOperationWaitFuture.get();
if (ec) { if (ec) {
setFutureStateIf(State::idle, std::bind( setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
[](State futureState) -> bool { m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec);
return futureState != State::stopped;
}, std::ref(m_futureState)));
m_observerManager.notify(
&IBlockchainSynchronizerObserver::synchronizationCompleted,
ec);
} else { //get union ok } else { //get union ok
if (!unionResponse.isLastKnownBlockActual) { //bc outdated if (!unionResponse.isLastKnownBlockActual) { //bc outdated
setFutureState(State::blockchainSync); setFutureState(State::blockchainSync);
} else { } else {
if (!shouldSyncConsumersPool) { //usual case, start pool processing if (unionPoolHistory == intersectedPoolHistory) { //usual case, start pool processing
m_observerManager.notify( m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, processPoolTxs(unionResponse));
&IBlockchainSynchronizerObserver::synchronizationCompleted, } else {
processPoolTxs(unionResponse)); GetPoolRequest intersectionRequest;
} else {// first launch, we should sync consumers' pools, so let's ask for intersection intersectionRequest.knownTxIds.assign(intersectedPoolHistory.begin(), intersectedPoolHistory.end());
intersectionRequest.lastKnownBlock = lastBlockId;
GetPoolResponse intersectionResponse; GetPoolResponse intersectionResponse;
GetPoolRequest intersectionRequest = getIntersectedPoolHistory();
asyncOperationCompleted = std::promise<std::error_code>();
asyncOperationWaitFuture = asyncOperationCompleted.get_future();
intersectionResponse.isLastKnownBlockActual = false; intersectionResponse.isLastKnownBlockActual = false;
m_node.getPoolSymmetricDifference(std::move(intersectionRequest.knownTxIds), std::move(intersectionRequest.lastKnownBlock), intersectionResponse.isLastKnownBlockActual, std::error_code ec2 = getPoolSymmetricDifferenceSync(std::move(intersectionRequest), intersectionResponse);
intersectionResponse.newTxs, intersectionResponse.deletedTxIds, std::bind(&BlockchainSynchronizer::onGetPoolChanges, this, std::placeholders::_1));
std::error_code ec2 = asyncOperationWaitFuture.get();
if (ec2) { if (ec2) {
setFutureStateIf(State::idle, std::bind( setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
[](State futureState) -> bool { m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec2);
return futureState != State::stopped;
}, std::ref(m_futureState)));
m_observerManager.notify(
&IBlockchainSynchronizerObserver::synchronizationCompleted,
ec2);
} else { //get intersection ok } else { //get intersection ok
if (!intersectionResponse.isLastKnownBlockActual) { //bc outdated if (!intersectionResponse.isLastKnownBlockActual) { //bc outdated
setFutureState(State::blockchainSync); setFutureState(State::blockchainSync);
@ -503,13 +521,7 @@ void BlockchainSynchronizer::startPoolSync() {
std::error_code ec3 = processPoolTxs(intersectionResponse); std::error_code ec3 = processPoolTxs(intersectionResponse);
//notify about error, or success //notify about error, or success
m_observerManager.notify( m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec3);
&IBlockchainSynchronizerObserver::synchronizationCompleted,
ec3);
if (!ec3) {
shouldSyncConsumersPool = false;
}
} }
} }
} }
@ -517,9 +529,22 @@ void BlockchainSynchronizer::startPoolSync() {
} }
} }
void BlockchainSynchronizer::onGetPoolChanges(std::error_code ec) { std::error_code BlockchainSynchronizer::getPoolSymmetricDifferenceSync(GetPoolRequest&& request, GetPoolResponse& response) {
decltype(asyncOperationCompleted) detachedPromise = std::move(asyncOperationCompleted); auto promise = std::promise<std::error_code>();
detachedPromise.set_value(ec); auto future = promise.get_future();
m_node.getPoolSymmetricDifference(
std::move(request.knownTxIds),
std::move(request.lastKnownBlock),
response.isLastKnownBlockActual,
response.newTxs,
response.deletedTxIds,
[&promise](std::error_code ec) {
auto detachedPromise = std::move(promise);
detachedPromise.set_value(ec);
});
return future.get();
} }
std::error_code BlockchainSynchronizer::processPoolTxs(GetPoolResponse& response) { std::error_code BlockchainSynchronizer::processPoolTxs(GetPoolResponse& response) {
@ -541,4 +566,20 @@ std::error_code BlockchainSynchronizer::processPoolTxs(GetPoolResponse& response
return error; return error;
} }
///pre: m_consumersMutex is locked
SynchronizationState* BlockchainSynchronizer::getConsumerSynchronizationState(IBlockchainConsumer* consumer) const {
assert(consumer != nullptr);
if (!(checkIfStopped() && checkIfShouldStop())) {
throw std::runtime_error("Can't get consumer state, because BlockchainSynchronizer isn't stopped");
}
auto it = m_consumers.find(consumer);
if (it == m_consumers.end()) {
return nullptr;
}
return it->second.get();
}
} }

View file

@ -41,7 +41,11 @@ public:
// IBlockchainSynchronizer // IBlockchainSynchronizer
virtual void addConsumer(IBlockchainConsumer* consumer) override; virtual void addConsumer(IBlockchainConsumer* consumer) override;
virtual bool removeConsumer(IBlockchainConsumer* consumer) override; virtual bool removeConsumer(IBlockchainConsumer* consumer) override;
virtual IStreamSerializable* getConsumerState(IBlockchainConsumer* consumer) override; virtual IStreamSerializable* getConsumerState(IBlockchainConsumer* consumer) const override;
virtual std::vector<Crypto::Hash> getConsumerKnownBlocks(IBlockchainConsumer& consumer) const override;
virtual std::future<std::error_code> addUnconfirmedTransaction(const ITransactionReader& transaction) override;
virtual std::future<void> removeUnconfirmedTransaction(const Crypto::Hash& transactionHash) override;
virtual void start() override; virtual void start() override;
virtual void stop() override; virtual void stop() override;
@ -51,6 +55,7 @@ public:
virtual void load(std::istream& in) override; virtual void load(std::istream& in) override;
// INodeObserver // INodeObserver
virtual void localBlockchainUpdated(uint32_t height) override;
virtual void lastKnownBlockHeightUpdated(uint32_t height) override; virtual void lastKnownBlockHeightUpdated(uint32_t height) override;
virtual void poolChanged() override; virtual void poolChanged() override;
@ -81,7 +86,6 @@ private:
Crypto::Hash lastKnownBlock; Crypto::Hash lastKnownBlock;
}; };
enum class State { //prioritized finite states enum class State { //prioritized finite states
idle = 0, //DO idle = 0, //DO
poolSync = 1, //NOT poolSync = 1, //NOT
@ -99,25 +103,26 @@ private:
void startPoolSync(); void startPoolSync();
void startBlockchainSync(); void startBlockchainSync();
void onGetBlocksCompleted(std::error_code ec);
void processBlocks(GetBlocksResponse& response); void processBlocks(GetBlocksResponse& response);
UpdateConsumersResult updateConsumers(const BlockchainInterval& interval, const std::vector<CompleteBlock>& blocks); UpdateConsumersResult updateConsumers(const BlockchainInterval& interval, const std::vector<CompleteBlock>& blocks);
void onGetPoolChanges(std::error_code ec);
std::error_code processPoolTxs(GetPoolResponse& response); std::error_code processPoolTxs(GetPoolResponse& response);
std::error_code getPoolSymmetricDifferenceSync(GetPoolRequest&& request, GetPoolResponse& response);
std::error_code doAddUnconfirmedTransaction(const ITransactionReader& transaction);
void doRemoveUnconfirmedTransaction(const Crypto::Hash& transactionHash);
///second parameter is used only in case of errors returned into callback from INode, such as aborted or connection lost ///second parameter is used only in case of errors returned into callback from INode, such as aborted or connection lost
bool setFutureState(State s); bool setFutureState(State s);
bool setFutureStateIf(State s, std::function<bool(void)>&& pred); bool setFutureStateIf(State s, std::function<bool(void)>&& pred);
void actualizeFutureState(); void actualizeFutureState();
bool checkIfShouldStop(); bool checkIfShouldStop() const;
bool checkIfStopped(); bool checkIfStopped() const;
void workingProcedure(); void workingProcedure();
GetBlocksRequest getCommonHistory(); GetBlocksRequest getCommonHistory();
GetPoolRequest getUnionPoolHistory(); void getPoolUnionAndIntersection(std::unordered_set<Crypto::Hash>& poolUnion, std::unordered_set<Crypto::Hash>& poolIntersection) const;
GetPoolRequest getIntersectedPoolHistory(); SynchronizationState* getConsumerSynchronizationState(IBlockchainConsumer* consumer) const ;
typedef std::map<IBlockchainConsumer*, std::shared_ptr<SynchronizationState>> ConsumersMap; typedef std::map<IBlockchainConsumer*, std::shared_ptr<SynchronizationState>> ConsumersMap;
@ -130,14 +135,12 @@ private:
State m_currentState; State m_currentState;
State m_futureState; State m_futureState;
std::unique_ptr<std::thread> workingThread; std::unique_ptr<std::thread> workingThread;
std::list<std::pair<const ITransactionReader*, std::promise<std::error_code>>> m_addTransactionTasks;
std::list<std::pair<const Crypto::Hash*, std::promise<void>>> m_removeTransactionTasks;
std::future<std::error_code> asyncOperationWaitFuture; mutable std::mutex m_consumersMutex;
std::promise<std::error_code> asyncOperationCompleted; mutable std::mutex m_stateMutex;
std::condition_variable m_hasWork;
std::mutex m_consumersMutex;
std::mutex m_stateMutex;
bool shouldSyncConsumersPool;
}; };
} }

View file

@ -18,7 +18,9 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <future>
#include <system_error> #include <system_error>
#include <unordered_set>
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "CryptoNoteCore/CryptoNoteBasic.h" #include "CryptoNoteCore/CryptoNoteBasic.h"
@ -37,16 +39,29 @@ public:
virtual void synchronizationCompleted(std::error_code result) {} virtual void synchronizationCompleted(std::error_code result) {}
}; };
class IBlockchainConsumer { class IBlockchainConsumerObserver;
class IBlockchainConsumer : public IObservable<IBlockchainConsumerObserver> {
public: public:
virtual ~IBlockchainConsumer() {} virtual ~IBlockchainConsumer() {}
virtual SynchronizationStart getSyncStart() = 0; virtual SynchronizationStart getSyncStart() = 0;
virtual void getKnownPoolTxIds(std::vector<Crypto::Hash>& ids) = 0; virtual const std::unordered_set<Crypto::Hash>& getKnownPoolTxIds() const = 0;
virtual void onBlockchainDetach(uint32_t height) = 0; virtual void onBlockchainDetach(uint32_t height) = 0;
virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) = 0; virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) = 0;
virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Crypto::Hash>& deletedTransactions) = 0; virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Crypto::Hash>& deletedTransactions) = 0;
virtual std::error_code addUnconfirmedTransaction(const ITransactionReader& transaction) = 0;
virtual void removeUnconfirmedTransaction(const Crypto::Hash& transactionHash) = 0;
}; };
class IBlockchainConsumerObserver {
public:
virtual void onBlocksAdded(IBlockchainConsumer* consumer, const std::vector<Crypto::Hash>& blockHashes) {}
virtual void onBlockchainDetach(IBlockchainConsumer* consumer, uint32_t blockIndex) {}
virtual void onTransactionDeleteBegin(IBlockchainConsumer* consumer, Crypto::Hash transactionHash) {}
virtual void onTransactionDeleteEnd(IBlockchainConsumer* consumer, Crypto::Hash transactionHash) {}
virtual void onTransactionUpdated(IBlockchainConsumer* consumer, const Crypto::Hash& transactionHash, const std::vector<ITransfersContainer*>& containers) {}
};
class IBlockchainSynchronizer : class IBlockchainSynchronizer :
public IObservable<IBlockchainSynchronizerObserver>, public IObservable<IBlockchainSynchronizerObserver>,
@ -54,7 +69,11 @@ class IBlockchainSynchronizer :
public: public:
virtual void addConsumer(IBlockchainConsumer* consumer) = 0; virtual void addConsumer(IBlockchainConsumer* consumer) = 0;
virtual bool removeConsumer(IBlockchainConsumer* consumer) = 0; virtual bool removeConsumer(IBlockchainConsumer* consumer) = 0;
virtual IStreamSerializable* getConsumerState(IBlockchainConsumer* consumer) = 0; virtual IStreamSerializable* getConsumerState(IBlockchainConsumer* consumer) const = 0;
virtual std::vector<Crypto::Hash> getConsumerKnownBlocks(IBlockchainConsumer& consumer) const = 0;
virtual std::future<std::error_code> addUnconfirmedTransaction(const ITransactionReader& transaction) = 0;
virtual std::future<void> removeUnconfirmedTransaction(const Crypto::Hash& transactionHash) = 0;
virtual void start() = 0; virtual void start() = 0;
virtual void stop() = 0; virtual void stop() = 0;

View file

@ -103,6 +103,10 @@ uint32_t SynchronizationState::getHeight() const {
return static_cast<uint32_t>(m_blockchain.size()); return static_cast<uint32_t>(m_blockchain.size());
} }
const std::vector<Crypto::Hash>& SynchronizationState::getKnownBlockHashes() const {
return m_blockchain;
}
void SynchronizationState::save(std::ostream& os) { void SynchronizationState::save(std::ostream& os) {
StdOutputStream stream(os); StdOutputStream stream(os);
CryptoNote::BinaryOutputStreamSerializer s(stream); CryptoNote::BinaryOutputStreamSerializer s(stream);

View file

@ -47,6 +47,7 @@ public:
void detach(uint32_t height); void detach(uint32_t height);
void addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count); void addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count);
uint32_t getHeight() const; uint32_t getHeight() const;
const std::vector<Crypto::Hash>& getKnownBlockHashes() const;
// IStreamSerializable // IStreamSerializable
virtual void save(std::ostream& os) override; virtual void save(std::ostream& os) override;

View file

@ -16,13 +16,15 @@
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>. // along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "TransfersConsumer.h" #include "TransfersConsumer.h"
#include "CommonTypes.h"
#include <numeric>
#include "CommonTypes.h"
#include "Common/BlockingQueue.h" #include "Common/BlockingQueue.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h" #include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "CryptoNoteCore/TransactionApi.h" #include "CryptoNoteCore/TransactionApi.h"
#include "IWalletLegacy.h" #include "IWallet.h"
#include "INode.h" #include "INode.h"
#include <future> #include <future>
@ -90,6 +92,17 @@ void findMyOutputs(
} }
} }
std::vector<Crypto::Hash> getBlockHashes(const CryptoNote::CompleteBlock* blocks, size_t count) {
std::vector<Crypto::Hash> result;
result.reserve(count);
for (size_t i = 0; i < count; ++i) {
result.push_back(blocks[i].blockHash);
}
return result;
}
} }
namespace CryptoNote { namespace CryptoNote {
@ -133,6 +146,19 @@ void TransfersConsumer::getSubscriptions(std::vector<AccountPublicAddress>& subs
} }
} }
void TransfersConsumer::initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions) {
for (auto itSubscriptions = m_subscriptions.begin(); itSubscriptions != m_subscriptions.end(); ++itSubscriptions) {
std::vector<Crypto::Hash> unconfirmedTransactions;
itSubscriptions->second->getContainer().getUnconfirmedTransactions(unconfirmedTransactions);
for (auto itTransactions = unconfirmedTransactions.begin(); itTransactions != unconfirmedTransactions.end(); ++itTransactions) {
if (uncommitedTransactions.count(*itTransactions) == 0) {
m_poolTxs.emplace(*itTransactions);
}
}
}
}
void TransfersConsumer::updateSyncStart() { void TransfersConsumer::updateSyncStart() {
SynchronizationStart start; SynchronizationStart start;
@ -153,6 +179,8 @@ SynchronizationStart TransfersConsumer::getSyncStart() {
} }
void TransfersConsumer::onBlockchainDetach(uint32_t height) { void TransfersConsumer::onBlockchainDetach(uint32_t height) {
m_observerManager.notify(&IBlockchainConsumerObserver::onBlockchainDetach, this, height);
for (const auto& kv : m_subscriptions) { for (const auto& kv : m_subscriptions) {
kv.second->onBlockchainDetach(height); kv.second->onBlockchainDetach(height);
} }
@ -253,24 +281,23 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startH
} }
} }
std::vector<Crypto::Hash> blockHashes = getBlockHashes(blocks, count);
if (!processingError) { if (!processingError) {
m_observerManager.notify(&IBlockchainConsumerObserver::onBlocksAdded, this, blockHashes);
// sort by block height and transaction index in block // sort by block height and transaction index in block
std::sort(preprocessedTransactions.begin(), preprocessedTransactions.end(), [](const PreprocessedTx& a, const PreprocessedTx& b) { std::sort(preprocessedTransactions.begin(), preprocessedTransactions.end(), [](const PreprocessedTx& a, const PreprocessedTx& b) {
return std::tie(a.blockInfo.height, a.blockInfo.transactionIndex) < std::tie(b.blockInfo.height, b.blockInfo.transactionIndex); return std::tie(a.blockInfo.height, a.blockInfo.transactionIndex) < std::tie(b.blockInfo.height, b.blockInfo.transactionIndex);
}); });
for (const auto& tx : preprocessedTransactions) { for (const auto& tx : preprocessedTransactions) {
processingError = processTransaction(tx.blockInfo, *tx.tx, tx); processTransaction(tx.blockInfo, *tx.tx, tx);
if (processingError) {
break;
}
} }
} } else {
if (processingError) {
forEachSubscription([&](TransfersSubscription& sub) { forEachSubscription([&](TransfersSubscription& sub) {
sub.onError(processingError, startHeight); sub.onError(processingError, startHeight);
}); });
return false; return false;
} }
@ -285,44 +312,55 @@ bool TransfersConsumer::onNewBlocks(const CompleteBlock* blocks, uint32_t startH
std::error_code TransfersConsumer::onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) { std::error_code TransfersConsumer::onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) {
TransactionBlockInfo unconfirmedBlockInfo; TransactionBlockInfo unconfirmedBlockInfo;
unconfirmedBlockInfo.timestamp = 0; unconfirmedBlockInfo.timestamp = 0;
unconfirmedBlockInfo.height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT; unconfirmedBlockInfo.height = WALLET_UNCONFIRMED_TRANSACTION_HEIGHT;
std::error_code processingError; std::error_code processingError;
for (auto& cryptonoteTransaction : addedTransactions) { for (auto& cryptonoteTransaction : addedTransactions) {
m_poolTxs.emplace(cryptonoteTransaction->getTransactionHash());
processingError = processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get()); processingError = processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get());
if (processingError) { if (processingError) {
break; for (auto& sub : m_subscriptions) {
} sub.second->onError(processingError, WALLET_UNCONFIRMED_TRANSACTION_HEIGHT);
} }
if (processingError) { return processingError;
for (auto& sub : m_subscriptions) {
sub.second->onError(processingError, WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT);
} }
return processingError;
} }
for (auto& deletedTxHash : deletedTransactions) { for (auto& deletedTxHash : deletedTransactions) {
for (auto& sub: m_subscriptions) { m_poolTxs.erase(deletedTxHash);
m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionDeleteBegin, this, deletedTxHash);
for (auto& sub : m_subscriptions) {
sub.second->deleteUnconfirmedTransaction(*reinterpret_cast<const Hash*>(&deletedTxHash)); sub.second->deleteUnconfirmedTransaction(*reinterpret_cast<const Hash*>(&deletedTxHash));
} }
m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionDeleteEnd, this, deletedTxHash);
} }
return std::error_code(); return std::error_code();
} }
void TransfersConsumer::getKnownPoolTxIds(std::vector<Hash>& ids) { const std::unordered_set<Crypto::Hash>& TransfersConsumer::getKnownPoolTxIds() const {
ids.clear(); return m_poolTxs;
std::unordered_set<Hash> knownIds;
for (auto& sub : m_subscriptions) {
std::vector<Hash> subscriptionUnconfirmedTxIds;
sub.second->getContainer().getUnconfirmedTransactions(subscriptionUnconfirmedTxIds);
knownIds.insert(subscriptionUnconfirmedTxIds.begin(), subscriptionUnconfirmedTxIds.end());
}
ids.assign(knownIds.begin(), knownIds.end());
} }
std::error_code TransfersConsumer::addUnconfirmedTransaction(const ITransactionReader& transaction) {
TransactionBlockInfo unconfirmedBlockInfo;
unconfirmedBlockInfo.height = WALLET_UNCONFIRMED_TRANSACTION_HEIGHT;
unconfirmedBlockInfo.timestamp = 0;
unconfirmedBlockInfo.transactionIndex = 0;
return processTransaction(unconfirmedBlockInfo, transaction);
}
void TransfersConsumer::removeUnconfirmedTransaction(const Crypto::Hash& transactionHash) {
m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionDeleteBegin, this, transactionHash);
for (auto& subscription : m_subscriptions) {
subscription.second->deleteUnconfirmedTransaction(transactionHash);
}
m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionDeleteEnd, this, transactionHash);
}
std::error_code createTransfers( std::error_code createTransfers(
const AccountKeys& account, const AccountKeys& account,
@ -353,7 +391,7 @@ std::error_code createTransfers(
info.type = outType; info.type = outType;
info.transactionPublicKey = txPubKey; info.transactionPublicKey = txPubKey;
info.outputInTransaction = idx; info.outputInTransaction = idx;
info.globalOutputIndex = (blockInfo.height == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) ? info.globalOutputIndex = (blockInfo.height == WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) ?
UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx]; UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX : globalIdxs[idx];
if (outType == TransactionTypes::OutputType::Key) { if (outType == TransactionTypes::OutputType::Key) {
@ -399,7 +437,7 @@ std::error_code TransfersConsumer::preprocessOutputs(const TransactionBlockInfo&
std::error_code errorCode; std::error_code errorCode;
auto txHash = tx.getTransactionHash(); auto txHash = tx.getTransactionHash();
if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { if (blockInfo.height != WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) {
errorCode = getGlobalIndices(reinterpret_cast<const Hash&>(txHash), info.globalIdxs); errorCode = getGlobalIndices(reinterpret_cast<const Hash&>(txHash), info.globalIdxs);
if (errorCode) { if (errorCode) {
return errorCode; return errorCode;
@ -427,50 +465,52 @@ std::error_code TransfersConsumer::processTransaction(const TransactionBlockInfo
return ec; return ec;
} }
return processTransaction(blockInfo, tx, info); processTransaction(blockInfo, tx, info);
return std::error_code();
} }
void TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) {
std::error_code TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) {
std::error_code errorCode;
std::vector<TransactionOutputInformationIn> emptyOutputs; std::vector<TransactionOutputInformationIn> emptyOutputs;
std::vector<ITransfersContainer*> transactionContainers;
bool someContainerUpdated = false;
for (auto& kv : m_subscriptions) { for (auto& kv : m_subscriptions) {
auto it = info.outputs.find(kv.first); auto it = info.outputs.find(kv.first);
auto& subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it->second; auto& subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it->second;
errorCode = processOutputs(blockInfo, *kv.second, tx, subscriptionOutputs, info.globalIdxs);
if (errorCode) { bool containerContainsTx;
return errorCode; bool containerUpdated;
processOutputs(blockInfo, *kv.second, tx, subscriptionOutputs, info.globalIdxs, containerContainsTx, containerUpdated);
someContainerUpdated = someContainerUpdated || containerUpdated;
if (containerContainsTx) {
transactionContainers.emplace_back(&kv.second->getContainer());
} }
} }
return std::error_code(); if (someContainerUpdated) {
} m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionUpdated, this, tx.getTransactionHash(), transactionContainers);
std::error_code TransfersConsumer::processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub,
const ITransactionReader& tx, const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs) {
if (blockInfo.height != WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {
TransactionInformation subscribtionTxInfo;
int64_t txBalance;
if (sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo, txBalance)) {
if (subscribtionTxInfo.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {
// pool->blockchain
sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs);
return std::error_code();
} else {
// - Subscription already has this transaction as confirmed, so why are we here?
// - Because, for instance, some another subscription doesn't have this transactions, so it is given to us again.
return std::error_code();
}
}
} }
sub.addTransaction(blockInfo, tx, transfers);
return std::error_code();
} }
void TransfersConsumer::processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfers, const std::vector<uint32_t>& globalIdxs, bool& contains, bool& updated) {
TransactionInformation subscribtionTxInfo;
contains = sub.getContainer().getTransactionInformation(tx.getTransactionHash(), subscribtionTxInfo);
updated = false;
if (contains) {
if (subscribtionTxInfo.blockHeight == WALLET_UNCONFIRMED_TRANSACTION_HEIGHT && blockInfo.height != WALLET_UNCONFIRMED_TRANSACTION_HEIGHT) {
// pool->blockchain
sub.markTransactionConfirmed(blockInfo, tx.getTransactionHash(), globalIdxs);
updated = true;
} else {
assert(subscribtionTxInfo.blockHeight == blockInfo.height);
}
} else {
updated = sub.addTransaction(blockInfo, tx, transfers);
contains = updated;
}
}
std::error_code TransfersConsumer::getGlobalIndices(const Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices) { std::error_code TransfersConsumer::getGlobalIndices(const Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices) {
std::promise<std::error_code> prom; std::promise<std::error_code> prom;

View file

@ -32,8 +32,7 @@ namespace CryptoNote {
class INode; class INode;
class TransfersConsumer : public IBlockchainConsumer { class TransfersConsumer: public IObservableImpl<IBlockchainConsumerObserver, IBlockchainConsumer> {
public: public:
TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const Crypto::SecretKey& viewSecret); TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const Crypto::SecretKey& viewSecret);
@ -43,13 +42,18 @@ public:
bool removeSubscription(const AccountPublicAddress& address); bool removeSubscription(const AccountPublicAddress& address);
ITransfersSubscription* getSubscription(const AccountPublicAddress& acc); ITransfersSubscription* getSubscription(const AccountPublicAddress& acc);
void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions); void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions);
void initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions);
// IBlockchainConsumer // IBlockchainConsumer
virtual SynchronizationStart getSyncStart() override; virtual SynchronizationStart getSyncStart() override;
virtual void onBlockchainDetach(uint32_t height) override; virtual void onBlockchainDetach(uint32_t height) override;
virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) override; virtual bool onNewBlocks(const CompleteBlock* blocks, uint32_t startHeight, uint32_t count) override;
virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Crypto::Hash>& deletedTransactions) override; virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Crypto::Hash>& deletedTransactions) override;
virtual void getKnownPoolTxIds(std::vector<Crypto::Hash>& ids) override; virtual const std::unordered_set<Crypto::Hash>& getKnownPoolTxIds() const override;
virtual std::error_code addUnconfirmedTransaction(const ITransactionReader& transaction) override;
virtual void removeUnconfirmedTransaction(const Crypto::Hash& transactionHash) override;
private: private:
@ -67,9 +71,9 @@ private:
std::error_code preprocessOutputs(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info); std::error_code preprocessOutputs(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, PreprocessInfo& info);
std::error_code processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx); std::error_code processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx);
std::error_code processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info); void processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info);
std::error_code processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx, void processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& outputs, const std::vector<uint32_t>& globalIdxs); const std::vector<TransactionOutputInformationIn>& outputs, const std::vector<uint32_t>& globalIdxs, bool& contains, bool& updated);
std::error_code getGlobalIndices(const Crypto::Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices); std::error_code getGlobalIndices(const Crypto::Hash& transactionHash, std::vector<uint32_t>& outsGlobalIndices);
@ -80,6 +84,7 @@ private:
// map { spend public key -> subscription } // map { spend public key -> subscription }
std::unordered_map<Crypto::PublicKey, std::unique_ptr<TransfersSubscription>> m_subscriptions; std::unordered_map<Crypto::PublicKey, std::unique_ptr<TransfersSubscription>> m_subscriptions;
std::unordered_set<Crypto::PublicKey> m_spendKeys; std::unordered_set<Crypto::PublicKey> m_spendKeys;
std::unordered_set<Crypto::Hash> m_poolTxs;
INode& m_node; INode& m_node;
const CryptoNote::Currency& m_currency; const CryptoNote::Currency& m_currency;

View file

@ -256,11 +256,11 @@ bool TransfersContainer::addTransactionOutputs(const TransactionBlockInfo& block
assert(result.second); assert(result.second);
} else { } else {
if (info.type == TransactionTypes::OutputType::Multisignature) { if (info.type == TransactionTypes::OutputType::Multisignature) {
SpentOutputDescriptor descriptor(transfer); SpentOutputDescriptor descriptor(transfer);
if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 || if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 ||
m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) { m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) {
throw std::runtime_error("Transfer already exists"); throw std::runtime_error("Transfer already exists");
} }
} }
auto result = m_availableTransfers.emplace(std::move(info)); auto result = m_availableTransfers.emplace(std::move(info));
@ -341,10 +341,10 @@ bool TransfersContainer::addTransactionInputs(const TransactionBlockInfo& block,
outputDescriptorIndex.erase(availableOutputIt); outputDescriptorIndex.erase(availableOutputIt);
inputsAdded = true; inputsAdded = true;
} }
} else { } else {
assert(inputType == TransactionTypes::InputType::Generating); assert(inputType == TransactionTypes::InputType::Generating);
} }
} }
return inputsAdded; return inputsAdded;
@ -361,7 +361,7 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas
} else { } else {
deleteTransactionTransfers(it->transactionHash); deleteTransactionTransfers(it->transactionHash);
m_transactions.erase(it); m_transactions.erase(it);
return true; return true;
} }
} }
@ -401,12 +401,12 @@ bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& bl
transfer.globalOutputIndex = globalIndices[transfer.outputInTransaction]; transfer.globalOutputIndex = globalIndices[transfer.outputInTransaction];
if (transfer.type == TransactionTypes::OutputType::Multisignature) { if (transfer.type == TransactionTypes::OutputType::Multisignature) {
SpentOutputDescriptor descriptor(transfer); SpentOutputDescriptor descriptor(transfer);
if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 || if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 ||
m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) { m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) {
// This exception breaks TransfersContainer consistency // This exception breaks TransfersContainer consistency
throw std::runtime_error("Transfer already exists"); throw std::runtime_error("Transfer already exists");
} }
} }
auto result = m_availableTransfers.emplace(std::move(transfer)); auto result = m_availableTransfers.emplace(std::move(transfer));
@ -417,7 +417,7 @@ bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& bl
if (transfer.type == TransactionTypes::OutputType::Key) { if (transfer.type == TransactionTypes::OutputType::Key) {
updateTransfersVisibility(transfer.keyImage); updateTransfersVisibility(transfer.keyImage);
} }
} }
auto& spendingTransactionIndex = m_spentTransfers.get<SpendingTransactionIndex>(); auto& spendingTransactionIndex = m_spentTransfers.get<SpendingTransactionIndex>();
@ -430,7 +430,7 @@ bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& bl
spendingTransactionIndex.replace(transferIt, transfer); spendingTransactionIndex.replace(transferIt, transfer);
} }
return true; return true;
} }
/** /**
@ -472,7 +472,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash)
updateTransfersVisibility(keyImage); updateTransfersVisibility(keyImage);
} else { } else {
it = transactionTransfersIndex.erase(it); it = transactionTransfersIndex.erase(it);
} }
} }
} }
@ -643,7 +643,7 @@ void TransfersContainer::getOutputs(std::vector<TransactionOutputInformation>& t
} }
} }
bool TransfersContainer::getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) const { bool TransfersContainer::getTransactionInformation(const Hash& transactionHash, TransactionInformation& info, uint64_t* amountIn, uint64_t* amountOut) const {
std::lock_guard<std::mutex> lk(m_mutex); std::lock_guard<std::mutex> lk(m_mutex);
auto it = m_transactions.find(transactionHash); auto it = m_transactions.find(transactionHash);
if (it == m_transactions.end()) { if (it == m_transactions.end()) {
@ -652,32 +652,35 @@ bool TransfersContainer::getTransactionInformation(const Hash& transactionHash,
info = *it; info = *it;
int64_t amountOut = 0; if (amountOut != nullptr) {
if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) { *amountOut = 0;
auto unconfirmedOutputsRange = m_unconfirmedTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
for (auto it = unconfirmedOutputsRange.first; it != unconfirmedOutputsRange.second; ++it) {
amountOut += static_cast<int64_t>(it->amount);
}
} else {
auto availableOutputsRange = m_availableTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
for (auto it = availableOutputsRange.first; it != availableOutputsRange.second; ++it) {
amountOut += static_cast<int64_t>(it->amount);
}
auto spentOutputsRange = m_spentTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash); if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {
for (auto it = spentOutputsRange.first; it != spentOutputsRange.second; ++it) { auto unconfirmedOutputsRange = m_unconfirmedTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
amountOut += static_cast<int64_t>(it->amount); for (auto it = unconfirmedOutputsRange.first; it != unconfirmedOutputsRange.second; ++it) {
*amountOut += it->amount;
}
} else {
auto availableOutputsRange = m_availableTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
for (auto it = availableOutputsRange.first; it != availableOutputsRange.second; ++it) {
*amountOut += it->amount;
}
auto spentOutputsRange = m_spentTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
for (auto it = spentOutputsRange.first; it != spentOutputsRange.second; ++it) {
*amountOut += it->amount;
}
} }
} }
int64_t amountIn = 0; if (amountIn != nullptr) {
auto rangeInputs = m_spentTransfers.get<SpendingTransactionIndex>().equal_range(transactionHash); *amountIn = 0;
for (auto it = rangeInputs.first; it != rangeInputs.second; ++it) { auto rangeInputs = m_spentTransfers.get<SpendingTransactionIndex>().equal_range(transactionHash);
amountIn += static_cast<int64_t>(it->amount); for (auto it = rangeInputs.first; it != rangeInputs.second; ++it) {
*amountIn += it->amount;
}
} }
txBalance = amountOut - amountIn;
return true; return true;
} }

View file

@ -165,7 +165,8 @@ public:
virtual size_t transactionsCount() const override; virtual size_t transactionsCount() const override;
virtual uint64_t balance(uint32_t flags) const override; virtual uint64_t balance(uint32_t flags) const override;
virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags) const override; virtual void getOutputs(std::vector<TransactionOutputInformation>& transfers, uint32_t flags) const override;
virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info, int64_t& txBalance) const override; virtual bool getTransactionInformation(const Crypto::Hash& transactionHash, TransactionInformation& info,
uint64_t* amountIn = nullptr, uint64_t* amountOut = nullptr) const override;
virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags) const override; virtual std::vector<TransactionOutputInformation> getTransactionOutputs(const Crypto::Hash& transactionHash, uint32_t flags) const override;
//only type flags are feasible for this function //only type flags are feasible for this function
virtual std::vector<TransactionOutputInformation> getTransactionInputs(const Crypto::Hash& transactionHash, uint32_t flags) const override; virtual std::vector<TransactionOutputInformation> getTransactionInputs(const Crypto::Hash& transactionHash, uint32_t flags) const override;

View file

@ -52,12 +52,14 @@ const AccountKeys& TransfersSubscription::getKeys() const {
return subscription.keys; return subscription.keys;
} }
void TransfersSubscription::addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, bool TransfersSubscription::addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfersList) { const std::vector<TransactionOutputInformationIn>& transfersList) {
bool added = transfers.addTransaction(blockInfo, tx, transfersList); bool added = transfers.addTransaction(blockInfo, tx, transfersList);
if (added) { if (added) {
m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash()); m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash());
} }
return added;
} }
AccountPublicAddress TransfersSubscription::getAddress() { AccountPublicAddress TransfersSubscription::getAddress() {
@ -69,8 +71,9 @@ ITransfersContainer& TransfersSubscription::getContainer() {
} }
void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) { void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) {
transfers.deleteUnconfirmedTransaction(transactionHash); if (transfers.deleteUnconfirmedTransaction(transactionHash)) {
m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash); m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash);
}
} }
void TransfersSubscription::markTransactionConfirmed(const TransactionBlockInfo& block, const Hash& transactionHash, void TransfersSubscription::markTransactionConfirmed(const TransactionBlockInfo& block, const Hash& transactionHash,

View file

@ -32,7 +32,7 @@ public:
void onError(const std::error_code& ec, uint32_t height); void onError(const std::error_code& ec, uint32_t height);
bool advanceHeight(uint32_t height); bool advanceHeight(uint32_t height);
const AccountKeys& getKeys() const; const AccountKeys& getKeys() const;
void addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, bool addTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx,
const std::vector<TransactionOutputInformationIn>& transfers); const std::vector<TransactionOutputInformationIn>& transfers);
void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash); void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash);

View file

@ -41,13 +41,21 @@ TransfersSyncronizer::~TransfersSyncronizer() {
} }
} }
void TransfersSyncronizer::initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions) {
for (auto it = m_consumers.begin(); it != m_consumers.end(); ++it) {
it->second->initTransactionPool(uncommitedTransactions);
}
}
ITransfersSubscription& TransfersSyncronizer::addSubscription(const AccountSubscription& acc) { ITransfersSubscription& TransfersSyncronizer::addSubscription(const AccountSubscription& acc) {
auto it = m_consumers.find(acc.keys.address.viewPublicKey); auto it = m_consumers.find(acc.keys.address.viewPublicKey);
if (it == m_consumers.end()) { if (it == m_consumers.end()) {
std::unique_ptr<TransfersConsumer> consumer( std::unique_ptr<TransfersConsumer> consumer(
new TransfersConsumer(m_currency, m_node, acc.keys.viewSecretKey)); new TransfersConsumer(m_currency, m_node, acc.keys.viewSecretKey));
m_sync.addConsumer(consumer.get()); m_sync.addConsumer(consumer.get());
consumer->addObserver(this);
it = m_consumers.insert(std::make_pair(acc.keys.address.viewPublicKey, std::move(consumer))).first; it = m_consumers.insert(std::make_pair(acc.keys.address.viewPublicKey, std::move(consumer))).first;
} }
@ -62,6 +70,8 @@ bool TransfersSyncronizer::removeSubscription(const AccountPublicAddress& acc) {
if (it->second->removeSubscription(acc)) { if (it->second->removeSubscription(acc)) {
m_sync.removeConsumer(it->second.get()); m_sync.removeConsumer(it->second.get());
m_consumers.erase(it); m_consumers.erase(it);
m_subscribers.erase(acc.viewPublicKey);
} }
return true; return true;
@ -75,7 +85,68 @@ void TransfersSyncronizer::getSubscriptions(std::vector<AccountPublicAddress>& s
ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountPublicAddress& acc) { ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountPublicAddress& acc) {
auto it = m_consumers.find(acc.viewPublicKey); auto it = m_consumers.find(acc.viewPublicKey);
return (it == m_consumers.end()) ? 0 : it->second->getSubscription(acc); return (it == m_consumers.end()) ? nullptr : it->second->getSubscription(acc);
}
std::vector<Crypto::Hash> TransfersSyncronizer::getViewKeyKnownBlocks(const Crypto::PublicKey& publicViewKey) {
auto it = m_consumers.find(publicViewKey);
if (it == m_consumers.end()) {
throw std::invalid_argument("Consumer not found");
}
return m_sync.getConsumerKnownBlocks(*it->second);
}
void TransfersSyncronizer::onBlocksAdded(IBlockchainConsumer* consumer, const std::vector<Crypto::Hash>& blockHashes) {
auto it = findSubscriberForConsumer(consumer);
if (it != m_subscribers.end()) {
it->second->notify(&ITransfersSynchronizerObserver::onBlocksAdded, it->first, blockHashes);
}
}
void TransfersSyncronizer::onBlockchainDetach(IBlockchainConsumer* consumer, uint32_t blockIndex) {
auto it = findSubscriberForConsumer(consumer);
if (it != m_subscribers.end()) {
it->second->notify(&ITransfersSynchronizerObserver::onBlockchainDetach, it->first, blockIndex);
}
}
void TransfersSyncronizer::onTransactionDeleteBegin(IBlockchainConsumer* consumer, Crypto::Hash transactionHash) {
auto it = findSubscriberForConsumer(consumer);
if (it != m_subscribers.end()) {
it->second->notify(&ITransfersSynchronizerObserver::onTransactionDeleteBegin, it->first, transactionHash);
}
}
void TransfersSyncronizer::onTransactionDeleteEnd(IBlockchainConsumer* consumer, Crypto::Hash transactionHash) {
auto it = findSubscriberForConsumer(consumer);
if (it != m_subscribers.end()) {
it->second->notify(&ITransfersSynchronizerObserver::onTransactionDeleteEnd, it->first, transactionHash);
}
}
void TransfersSyncronizer::onTransactionUpdated(IBlockchainConsumer* consumer, const Crypto::Hash& transactionHash,
const std::vector<ITransfersContainer*>& containers) {
auto it = findSubscriberForConsumer(consumer);
if (it != m_subscribers.end()) {
it->second->notify(&ITransfersSynchronizerObserver::onTransactionUpdated, it->first, transactionHash, containers);
}
}
void TransfersSyncronizer::subscribeConsumerNotifications(const Crypto::PublicKey& viewPublicKey, ITransfersSynchronizerObserver* observer) {
auto it = m_subscribers.find(viewPublicKey);
if (it != m_subscribers.end()) {
it->second->add(observer);
return;
}
auto insertedIt = m_subscribers.emplace(viewPublicKey, std::unique_ptr<SubscribersNotifier>(new SubscribersNotifier())).first;
insertedIt->second->add(observer);
}
void TransfersSyncronizer::unsubscribeConsumerNotifications(const Crypto::PublicKey& viewPublicKey, ITransfersSynchronizerObserver* observer) {
m_subscribers.at(viewPublicKey)->remove(observer);
} }
void TransfersSyncronizer::save(std::ostream& os) { void TransfersSyncronizer::save(std::ostream& os) {
@ -233,4 +304,30 @@ void TransfersSyncronizer::load(std::istream& is) {
} }
bool TransfersSyncronizer::findViewKeyForConsumer(IBlockchainConsumer* consumer, Crypto::PublicKey& viewKey) const {
//since we have only couple of consumers linear complexity is fine
auto it = std::find_if(m_consumers.begin(), m_consumers.end(), [consumer] (const ConsumersContainer::value_type& subscription) {
return subscription.second.get() == consumer;
});
if (it == m_consumers.end()) {
return false;
}
viewKey = it->first;
return true;
}
TransfersSyncronizer::SubscribersContainer::const_iterator TransfersSyncronizer::findSubscriberForConsumer(IBlockchainConsumer* consumer) const {
Crypto::PublicKey viewKey;
if (findViewKeyForConsumer(consumer, viewKey)) {
auto it = m_subscribers.find(viewKey);
if (it != m_subscribers.end()) {
return it;
}
}
return m_subscribers.end();
}
} }

View file

@ -17,6 +17,7 @@
#pragma once #pragma once
#include "Common/ObserverManager.h"
#include "ITransfersSynchronizer.h" #include "ITransfersSynchronizer.h"
#include "IBlockchainSynchronizer.h" #include "IBlockchainSynchronizer.h"
#include "TypeHelpers.h" #include "TypeHelpers.h"
@ -34,31 +35,50 @@ namespace CryptoNote {
class TransfersConsumer; class TransfersConsumer;
class INode; class INode;
class TransfersSyncronizer : public ITransfersSynchronizer { class TransfersSyncronizer : public ITransfersSynchronizer, public IBlockchainConsumerObserver {
public: public:
TransfersSyncronizer(const CryptoNote::Currency& currency, IBlockchainSynchronizer& sync, INode& node); TransfersSyncronizer(const CryptoNote::Currency& currency, IBlockchainSynchronizer& sync, INode& node);
~TransfersSyncronizer(); virtual ~TransfersSyncronizer();
void initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions);
// ITransfersSynchronizer // ITransfersSynchronizer
virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) override; virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) override;
virtual bool removeSubscription(const AccountPublicAddress& acc) override; virtual bool removeSubscription(const AccountPublicAddress& acc) override;
virtual void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions) override; virtual void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions) override;
virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) override; virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) override;
virtual std::vector<Crypto::Hash> getViewKeyKnownBlocks(const Crypto::PublicKey& publicViewKey) override;
void subscribeConsumerNotifications(const Crypto::PublicKey& viewPublicKey, ITransfersSynchronizerObserver* observer);
void unsubscribeConsumerNotifications(const Crypto::PublicKey& viewPublicKey, ITransfersSynchronizerObserver* observer);
// IStreamSerializable // IStreamSerializable
virtual void save(std::ostream& os) override; virtual void save(std::ostream& os) override;
virtual void load(std::istream& in) override; virtual void load(std::istream& in) override;
private: private:
// map { view public key -> consumer } // map { view public key -> consumer }
std::unordered_map<Crypto::PublicKey, std::unique_ptr<TransfersConsumer>> m_consumers; typedef std::unordered_map<Crypto::PublicKey, std::unique_ptr<TransfersConsumer>> ConsumersContainer;
ConsumersContainer m_consumers;
typedef Tools::ObserverManager<ITransfersSynchronizerObserver> SubscribersNotifier;
typedef std::unordered_map<Crypto::PublicKey, std::unique_ptr<SubscribersNotifier>> SubscribersContainer;
SubscribersContainer m_subscribers;
// std::unordered_map<AccountAddress, std::unique_ptr<TransfersConsumer>> m_subscriptions; // std::unordered_map<AccountAddress, std::unique_ptr<TransfersConsumer>> m_subscriptions;
IBlockchainSynchronizer& m_sync; IBlockchainSynchronizer& m_sync;
INode& m_node; INode& m_node;
const CryptoNote::Currency& m_currency; const CryptoNote::Currency& m_currency;
virtual void onBlocksAdded(IBlockchainConsumer* consumer, const std::vector<Crypto::Hash>& blockHashes) override;
virtual void onBlockchainDetach(IBlockchainConsumer* consumer, uint32_t blockIndex) override;
virtual void onTransactionDeleteBegin(IBlockchainConsumer* consumer, Crypto::Hash transactionHash) override;
virtual void onTransactionDeleteEnd(IBlockchainConsumer* consumer, Crypto::Hash transactionHash) override;
virtual void onTransactionUpdated(IBlockchainConsumer* consumer, const Crypto::Hash& transactionHash,
const std::vector<ITransfersContainer*>& containers) override;
bool findViewKeyForConsumer(IBlockchainConsumer* consumer, Crypto::PublicKey& viewKey) const;
SubscribersContainer::const_iterator findSubscriberForConsumer(IBlockchainConsumer* consumer) const;
}; };
} }

View file

@ -46,7 +46,11 @@ enum WalletErrorCodes {
INDEX_OUT_OF_RANGE, INDEX_OUT_OF_RANGE,
ADDRESS_ALREADY_EXISTS, ADDRESS_ALREADY_EXISTS,
TRACKING_MODE, TRACKING_MODE,
WRONG_PARAMETERS WRONG_PARAMETERS,
OBJECT_NOT_FOUND,
WALLET_NOT_FOUND,
CHANGE_ADDRESS_REQUIRED,
CHANGE_ADDRESS_NOT_FOUND
}; };
// custom category: // custom category:
@ -85,6 +89,10 @@ public:
case ADDRESS_ALREADY_EXISTS: return "Address already exists"; case ADDRESS_ALREADY_EXISTS: return "Address already exists";
case TRACKING_MODE: return "The wallet is in tracking mode"; case TRACKING_MODE: return "The wallet is in tracking mode";
case WRONG_PARAMETERS: return "Wrong parameters passed"; case WRONG_PARAMETERS: return "Wrong parameters passed";
case OBJECT_NOT_FOUND: return "Object not found";
case WALLET_NOT_FOUND: return "Requested wallet not found";
case CHANGE_ADDRESS_REQUIRED: return "Change address required";
case CHANGE_ADDRESS_NOT_FOUND: return "Change address not found";
default: return "Unknown error"; default: return "Unknown error";
} }
} }
@ -100,3 +108,10 @@ private:
inline std::error_code make_error_code(CryptoNote::error::WalletErrorCodes e) { inline std::error_code make_error_code(CryptoNote::error::WalletErrorCodes e) {
return std::error_code(static_cast<int>(e), CryptoNote::error::WalletErrorCategory::INSTANCE); return std::error_code(static_cast<int>(e), CryptoNote::error::WalletErrorCategory::INSTANCE);
} }
namespace std {
template <>
struct is_error_code_enum<CryptoNote::error::WalletErrorCodes>: public true_type {};
}

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@
#include "IWallet.h" #include "IWallet.h"
#include <queue> #include <queue>
#include <unordered_map>
#include "IFusionManager.h" #include "IFusionManager.h"
#include "WalletIndices.h" #include "WalletIndices.h"
@ -34,6 +35,7 @@ namespace CryptoNote {
class WalletGreen : public IWallet, class WalletGreen : public IWallet,
ITransfersObserver, ITransfersObserver,
IBlockchainSynchronizerObserver, IBlockchainSynchronizerObserver,
ITransfersSynchronizerObserver,
IFusionManager { IFusionManager {
public: public:
WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node, uint32_t transactionSoftLockTime = 1); WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node, uint32_t transactionSoftLockTime = 1);
@ -50,6 +52,7 @@ public:
virtual size_t getAddressCount() const override; virtual size_t getAddressCount() const override;
virtual std::string getAddress(size_t index) const override; virtual std::string getAddress(size_t index) const override;
virtual KeyPair getAddressSpendKey(size_t index) const override; virtual KeyPair getAddressSpendKey(size_t index) const override;
virtual KeyPair getAddressSpendKey(const std::string& address) const override;
virtual KeyPair getViewKey() const override; virtual KeyPair getViewKey() const override;
virtual std::string createAddress() override; virtual std::string createAddress() override;
virtual std::string createAddress(const Crypto::SecretKey& spendSecretKey) override; virtual std::string createAddress(const Crypto::SecretKey& spendSecretKey) override;
@ -66,12 +69,20 @@ public:
virtual size_t getTransactionTransferCount(size_t transactionIndex) const override; virtual size_t getTransactionTransferCount(size_t transactionIndex) const override;
virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const override; virtual WalletTransfer getTransactionTransfer(size_t transactionIndex, size_t transferIndex) const override;
virtual size_t transfer(const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; virtual WalletTransactionWithTransfers getTransaction(const Crypto::Hash& transactionHash) const override;
virtual size_t transfer(const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; virtual std::vector<TransactionsInBlockInfo> getTransactions(const Crypto::Hash& blockHash, size_t count) const override;
virtual size_t transfer(const std::string& sourceAddress, const WalletOrder& destination, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; virtual std::vector<TransactionsInBlockInfo> getTransactions(uint32_t blockIndex, size_t count) const override;
virtual size_t transfer(const std::string& sourceAddress, const std::vector<WalletOrder>& destinations, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "", uint64_t unlockTimestamp = 0) override; virtual std::vector<Crypto::Hash> getBlockHashes(uint32_t blockIndex, size_t count) const override;
virtual uint32_t getBlockCount() const override;
virtual std::vector<WalletTransactionWithTransfers> getUnconfirmedTransactions() const override;
virtual std::vector<size_t> getDelayedTransactionIds() const override;
virtual size_t transfer(const TransactionParameters& sendingTransaction) override; virtual size_t transfer(const TransactionParameters& sendingTransaction) override;
virtual size_t makeTransaction(const TransactionParameters& sendingTransaction) override;
virtual void commitTransaction(size_t) override;
virtual void rollbackUncommitedTransaction(size_t) override;
virtual void start() override; virtual void start() override;
virtual void stop() override; virtual void stop() override;
virtual WalletEvent getEvent() override; virtual WalletEvent getEvent() override;
@ -110,9 +121,26 @@ protected:
std::vector<TransactionOutputInformation> outs; std::vector<TransactionOutputInformation> outs;
}; };
typedef std::pair<WalletTransfers::const_iterator, WalletTransfers::const_iterator> TransfersRange;
struct AddressAmounts {
int64_t input = 0;
int64_t output = 0;
};
struct ContainerAmounts {
ITransfersContainer* container;
AddressAmounts amounts;
};
typedef std::unordered_map<std::string, AddressAmounts> TransfersMap;
virtual void onError(ITransfersSubscription* object, uint32_t height, std::error_code ec) override; virtual void onError(ITransfersSubscription* object, uint32_t height, std::error_code ec) override;
virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override;
void transactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash); virtual void onTransactionUpdated(const Crypto::PublicKey& viewPublicKey, const Crypto::Hash& transactionHash,
const std::vector<ITransfersContainer*>& containers) override;
void transactionUpdated(const TransactionInformation& transactionInfo, const std::vector<ContainerAmounts>& containerAmountsList);
virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override;
void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash); void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash);
@ -123,8 +151,21 @@ protected:
void onSynchronizationProgressUpdated(uint32_t processedBlockCount, uint32_t totalBlockCount); void onSynchronizationProgressUpdated(uint32_t processedBlockCount, uint32_t totalBlockCount);
void onSynchronizationCompleted(); void onSynchronizationCompleted();
virtual void onBlocksAdded(const Crypto::PublicKey& viewPublicKey, const std::vector<Crypto::Hash>& blockHashes) override;
void blocksAdded(const std::vector<Crypto::Hash>& blockHashes);
virtual void onBlockchainDetach(const Crypto::PublicKey& viewPublicKey, uint32_t blockIndex) override;
void blocksRollback(uint32_t blockIndex);
virtual void onTransactionDeleteBegin(const Crypto::PublicKey& viewPublicKey, Crypto::Hash transactionHash) override;
void transactionDeleteBegin(Crypto::Hash transactionHash);
virtual void onTransactionDeleteEnd(const Crypto::PublicKey& viewPublicKey, Crypto::Hash transactionHash) override;
void transactionDeleteEnd(Crypto::Hash transactionHash);
std::vector<WalletOuts> pickWalletsWithMoney() const; std::vector<WalletOuts> pickWalletsWithMoney() const;
WalletOuts pickWallet(const std::string& address); WalletOuts pickWallet(const std::string& address);
std::vector<WalletOuts> pickWallets(const std::vector<std::string>& addresses);
void updateBalance(CryptoNote::ITransfersContainer* container); void updateBalance(CryptoNote::ITransfersContainer* container);
void unlockBalances(uint32_t height); void unlockBalances(uint32_t height);
@ -134,24 +175,31 @@ protected:
const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const; const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const;
CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const; CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const;
void addWallet(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey, uint64_t creationTimestamp); std::string addWallet(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey, uint64_t creationTimestamp);
bool isOutputUsed(const TransactionOutputInformation& out) const;
void markOutputsSpent(const Crypto::Hash& transactionHash, const std::vector<OutputToTransfer>& selectedTransfers);
void deleteSpentOutputs(const Crypto::Hash& transactionHash);
uint64_t countSpentBalance(const WalletRecord* wallet);
void updateUsedWalletsBalances(const std::vector<OutputToTransfer>& selectedTransfers);
AccountKeys makeAccountKeys(const WalletRecord& wallet) const; AccountKeys makeAccountKeys(const WalletRecord& wallet) const;
size_t getTransactionId(const Crypto::Hash& transactionHash) const; size_t getTransactionId(const Crypto::Hash& transactionHash) const;
void pushEvent(const WalletEvent& event); void pushEvent(const WalletEvent& event);
bool isFusionTransaction(const WalletTransaction& walletTx) const; bool isFusionTransaction(const WalletTransaction& walletTx) const;
size_t doTransfer(std::vector<WalletOuts>&& wallets, struct PreparedTransaction {
std::unique_ptr<ITransaction> transaction;
std::vector<WalletTransfer> destinations;
uint64_t neededMoney;
uint64_t changeAmount;
};
void prepareTransaction(std::vector<WalletOuts>&& wallets,
const std::vector<WalletOrder>& orders, const std::vector<WalletOrder>& orders,
uint64_t fee, uint64_t fee,
uint64_t mixIn, uint64_t mixIn,
const std::string& extra, const std::string& extra,
uint64_t unlockTimestamp, uint64_t unlockTimestamp,
const DonationSettings& donation); const DonationSettings& donation,
const CryptoNote::AccountPublicAddress& changeDestinationAddress,
PreparedTransaction& preparedTransaction);
void validateTransactionParameters(const TransactionParameters& transactionParameters);
size_t doTransfer(const TransactionParameters& transactionParameters);
void requestMixinOuts(const std::vector<OutputToTransfer>& selectedTransfers, void requestMixinOuts(const std::vector<OutputToTransfer>& selectedTransfers,
uint64_t mixIn, uint64_t mixIn,
@ -175,23 +223,38 @@ protected:
std::unique_ptr<CryptoNote::ITransaction> makeTransaction(const std::vector<ReceiverAmounts>& decomposedOutputs, std::unique_ptr<CryptoNote::ITransaction> makeTransaction(const std::vector<ReceiverAmounts>& decomposedOutputs,
std::vector<InputInfo>& keysInfo, const std::string& extra, uint64_t unlockTimestamp); std::vector<InputInfo>& keysInfo, const std::string& extra, uint64_t unlockTimestamp);
void sendTransaction(ITransaction* tx); void sendTransaction(const CryptoNote::Transaction& cryptoNoteTransaction);
size_t validateSaveAndSendTransaction(const ITransactionReader& transaction, const std::vector<WalletTransfer>& destinations, bool isFusion, bool send);
size_t insertBlockchainTransactionAndPushEvent(const TransactionInformation& info, int64_t txBalance); size_t insertBlockchainTransaction(const TransactionInformation& info, int64_t txBalance);
size_t insertOutgoingTransactionAndPushEvent(const Crypto::Hash& transactionHash, size_t insertOutgoingTransactionAndPushEvent(const Crypto::Hash& transactionHash, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp);
int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp);
void updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state); void updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state);
void updateWalletTransactionInfoAndPushEvent(size_t transactionId, const CryptoNote::TransactionInformation& info, bool transfersUpdated); bool updateWalletTransactionInfo(size_t transactionId, const CryptoNote::TransactionInformation& info, int64_t totalAmount);
void insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount); bool updateTransactionTransfers(size_t transactionId, const std::vector<ContainerAmounts>& containerAmountsList,
void pushBackOutgoingTransfers(size_t txId, const std::vector<WalletTransfer> &destinations); int64_t allInputsAmount, int64_t allOutputsAmount);
TransfersMap getKnownTransfersMap(size_t transactionId, size_t firstTransferIdx) const;
bool updateAddressTransfers(size_t transactionId, size_t firstTransferIdx, const std::string& address, int64_t knownAmount, int64_t targetAmount);
bool updateUnknownTransfers(size_t transactionId, size_t firstTransferIdx, const std::unordered_set<std::string>& myAddresses,
int64_t knownAmount, int64_t myAmount, int64_t totalAmount, bool isOutput);
void appendTransfer(size_t transactionId, size_t firstTransferIdx, const std::string& address, int64_t amount);
bool adjustTransfer(size_t transactionId, size_t firstTransferIdx, const std::string& address, int64_t amount);
bool eraseTransfers(size_t transactionId, size_t firstTransferIdx, std::function<bool(bool, const std::string&)>&& predicate);
bool eraseTransfersByAddress(size_t transactionId, size_t firstTransferIdx, const std::string& address, bool eraseOutputTransfers);
bool eraseForeignTransfers(size_t transactionId, size_t firstTransferIdx, const std::unordered_set<std::string>& knownAddresses, bool eraseOutputTransfers);
void pushBackOutgoingTransfers(size_t txId, const std::vector<WalletTransfer>& destinations);
void insertUnlockTransactionJob(const Crypto::Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container); void insertUnlockTransactionJob(const Crypto::Hash& transactionHash, uint32_t blockHeight, CryptoNote::ITransfersContainer* container);
void deleteUnlockTransactionJob(const Crypto::Hash& transactionHash); void deleteUnlockTransactionJob(const Crypto::Hash& transactionHash);
void startBlockchainSynchronizer();
void stopBlockchainSynchronizer();
void addUnconfirmedTransaction(const ITransactionReader& transaction);
void removeUnconfirmedTransaction(const Crypto::Hash& transactionHash);
void unsafeLoad(std::istream& source, const std::string& password); void unsafeLoad(std::istream& source, const std::string& password);
void unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache); void unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache);
std::vector<OutputToTransfer> pickRandomFusionInputs(uint64_t threshold, size_t minInputCount, size_t maxInputCount); std::vector<OutputToTransfer> pickRandomFusionInputs(uint64_t threshold, size_t minInputCount, size_t maxInputCount);
ReceiverAmounts decomposeFusionOutputs(uint64_t inputsAmount); ReceiverAmounts decomposeFusionOutputs(uint64_t inputsAmount);
enum class WalletState { enum class WalletState {
INITIALIZED, INITIALIZED,
NOT_INITIALIZED NOT_INITIALIZED
@ -205,21 +268,33 @@ protected:
WalletTrackingMode getTrackingMode() const; WalletTrackingMode getTrackingMode() const;
std::pair<WalletTransfers::const_iterator, WalletTransfers::const_iterator> getTransactionTransfers(size_t transactionIndex) const; TransfersRange getTransactionTransfersRange(size_t transactionIndex) const;
std::vector<TransactionsInBlockInfo> getTransactionsInBlocks(uint32_t blockIndex, size_t count) const;
Crypto::Hash getBlockHashByIndex(uint32_t blockIndex) const;
std::vector<WalletTransfer> getTransactionTransfers(const WalletTransaction& transaction) const;
void filterOutTransactions(WalletTransactions& transactions, WalletTransfers& transfers, std::function<bool (const WalletTransaction&)>&& pred) const;
void getViewKeyKnownBlocks(const Crypto::PublicKey& viewPublicKey);
CryptoNote::AccountPublicAddress getChangeDestination(const std::string& changeDestinationAddress, const std::vector<std::string>& sourceAddresses) const;
bool isMyAddress(const std::string& address) const;
void deleteContainerFromUnlockTransactionJobs(const ITransfersContainer* container);
std::vector<size_t> deleteTransfersForAddress(const std::string& address, std::vector<size_t>& deletedTransactions);
void deleteFromUncommitedTransactions(const std::vector<size_t>& deletedTransactions);
System::Dispatcher& m_dispatcher; System::Dispatcher& m_dispatcher;
const Currency& m_currency; const Currency& m_currency;
INode& m_node; INode& m_node;
bool m_stopped = false; bool m_stopped;
WalletsContainer m_walletsContainer; WalletsContainer m_walletsContainer;
SpentOutputs m_spentOutputs;
UnlockTransactionJobs m_unlockTransactionsJob; UnlockTransactionJobs m_unlockTransactionsJob;
WalletTransactions m_transactions; WalletTransactions m_transactions;
WalletTransfers m_transfers; //sorted WalletTransfers m_transfers; //sorted
TransactionChanges m_change;
mutable std::unordered_map<size_t, bool> m_fusionTxsCache; // txIndex -> isFusion mutable std::unordered_map<size_t, bool> m_fusionTxsCache; // txIndex -> isFusion
UncommitedTransactions m_uncommitedTransactions;
bool m_blockchainSynchronizerStarted;
BlockchainSynchronizer m_blockchainSynchronizer; BlockchainSynchronizer m_blockchainSynchronizer;
TransfersSyncronizer m_synchronizer; TransfersSyncronizer m_synchronizer;
@ -227,18 +302,20 @@ protected:
std::queue<WalletEvent> m_events; std::queue<WalletEvent> m_events;
mutable System::Event m_readyEvent; mutable System::Event m_readyEvent;
WalletState m_state = WalletState::NOT_INITIALIZED; WalletState m_state;
std::string m_password; std::string m_password;
Crypto::PublicKey m_viewPublicKey; Crypto::PublicKey m_viewPublicKey;
Crypto::SecretKey m_viewSecretKey; Crypto::SecretKey m_viewSecretKey;
uint64_t m_actualBalance = 0; uint64_t m_actualBalance;
uint64_t m_pendingBalance = 0; uint64_t m_pendingBalance;
uint64_t m_upperTransactionSizeLimit; uint64_t m_upperTransactionSizeLimit;
uint32_t m_transactionSoftLockTime; uint32_t m_transactionSoftLockTime;
BlockHashesContainer m_blockchain;
}; };
} //namespace CryptoNote } //namespace CryptoNote

View file

@ -17,6 +17,7 @@
#pragma once #pragma once
#include <map>
#include <unordered_map> #include <unordered_map>
#include "ITransfersContainer.h" #include "ITransfersContainer.h"
@ -43,14 +44,6 @@ struct WalletRecord {
time_t creationTimestamp; time_t creationTimestamp;
}; };
struct SpentOutput {
uint64_t amount;
Crypto::Hash transactionHash;
uint32_t outputInTransaction;
const WalletRecord* wallet;
Crypto::Hash spendingTransactionHash;
};
struct RandomAccessIndex {}; struct RandomAccessIndex {};
struct KeysIndex {}; struct KeysIndex {};
struct TransfersContainerIndex {}; struct TransfersContainerIndex {};
@ -61,6 +54,7 @@ struct BlockHeightIndex {};
struct TransactionHashIndex {}; struct TransactionHashIndex {};
struct TransactionIndex {}; struct TransactionIndex {};
struct BlockHashIndex {};
typedef boost::multi_index_container < typedef boost::multi_index_container <
WalletRecord, WalletRecord,
@ -73,27 +67,6 @@ typedef boost::multi_index_container <
> >
> WalletsContainer; > WalletsContainer;
struct OutputIndex: boost::multi_index::composite_key <
SpentOutput,
BOOST_MULTI_INDEX_MEMBER(SpentOutput, Crypto::Hash, transactionHash),
BOOST_MULTI_INDEX_MEMBER(SpentOutput, uint32_t, outputInTransaction)
> {};
typedef boost::multi_index_container <
SpentOutput,
boost::multi_index::indexed_by <
boost::multi_index::hashed_unique < boost::multi_index::tag <TransactionOutputIndex>,
OutputIndex
>,
boost::multi_index::hashed_non_unique < boost::multi_index::tag <TransactionIndex>,
BOOST_MULTI_INDEX_MEMBER(SpentOutput, Crypto::Hash, spendingTransactionHash)
>,
boost::multi_index::hashed_non_unique < boost::multi_index::tag <WalletIndex>,
BOOST_MULTI_INDEX_MEMBER(SpentOutput, const WalletRecord *, wallet)
>
>
> SpentOutputs;
struct UnlockTransactionJob { struct UnlockTransactionJob {
uint32_t blockHeight; uint32_t blockHeight;
CryptoNote::ITransfersContainer* container; CryptoNote::ITransfersContainer* container;
@ -106,7 +79,7 @@ typedef boost::multi_index_container <
boost::multi_index::ordered_non_unique < boost::multi_index::tag <BlockHeightIndex>, boost::multi_index::ordered_non_unique < boost::multi_index::tag <BlockHeightIndex>,
BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, uint32_t, blockHeight) BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, uint32_t, blockHeight)
>, >,
boost::multi_index::hashed_unique < boost::multi_index::tag <TransactionHashIndex>, boost::multi_index::hashed_non_unique < boost::multi_index::tag <TransactionHashIndex>,
BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, Crypto::Hash, transactionHash) BOOST_MULTI_INDEX_MEMBER(UnlockTransactionJob, Crypto::Hash, transactionHash)
> >
> >
@ -118,13 +91,28 @@ typedef boost::multi_index_container <
boost::multi_index::random_access < boost::multi_index::tag <RandomAccessIndex> >, boost::multi_index::random_access < boost::multi_index::tag <RandomAccessIndex> >,
boost::multi_index::hashed_unique < boost::multi_index::tag <TransactionIndex>, boost::multi_index::hashed_unique < boost::multi_index::tag <TransactionIndex>,
boost::multi_index::member<CryptoNote::WalletTransaction, Crypto::Hash, &CryptoNote::WalletTransaction::hash > boost::multi_index::member<CryptoNote::WalletTransaction, Crypto::Hash, &CryptoNote::WalletTransaction::hash >
>,
boost::multi_index::ordered_non_unique < boost::multi_index::tag <BlockHeightIndex>,
boost::multi_index::member<CryptoNote::WalletTransaction, uint32_t, &CryptoNote::WalletTransaction::blockHeight >
> >
> >
> WalletTransactions; > WalletTransactions;
typedef std::unordered_map<Crypto::Hash, uint64_t> TransactionChanges;
typedef std::pair<size_t, CryptoNote::WalletTransfer> TransactionTransferPair; typedef std::pair<size_t, CryptoNote::WalletTransfer> TransactionTransferPair;
typedef std::vector<TransactionTransferPair> WalletTransfers; typedef std::vector<TransactionTransferPair> WalletTransfers;
typedef std::map<size_t, CryptoNote::Transaction> UncommitedTransactions;
typedef boost::multi_index_container<
Crypto::Hash,
boost::multi_index::indexed_by <
boost::multi_index::random_access<
boost::multi_index::tag<BlockHeightIndex>
>,
boost::multi_index::hashed_unique<
boost::multi_index::tag<BlockHashIndex>,
boost::multi_index::identity<Crypto::Hash>
>
>
> BlockHashesContainer;
} }

View file

@ -25,6 +25,7 @@
#include "Common/StdInputStream.h" #include "Common/StdInputStream.h"
#include "Common/StdOutputStream.h" #include "Common/StdOutputStream.h"
#include "CryptoNoteCore/CryptoNoteSerialization.h" #include "CryptoNoteCore/CryptoNoteSerialization.h"
#include "CryptoNoteCore/CryptoNoteTools.h"
#include "Serialization/BinaryOutputStreamSerializer.h" #include "Serialization/BinaryOutputStreamSerializer.h"
#include "Serialization/BinaryInputStreamSerializer.h" #include "Serialization/BinaryInputStreamSerializer.h"
@ -49,7 +50,7 @@ struct WalletRecordDto {
}; };
//DO NOT CHANGE IT //DO NOT CHANGE IT
struct SpentOutputDto { struct ObsoleteSpentOutputDto {
uint64_t amount; uint64_t amount;
Hash transactionHash; Hash transactionHash;
uint32_t outputInTransaction; uint32_t outputInTransaction;
@ -58,7 +59,7 @@ struct SpentOutputDto {
}; };
//DO NOT CHANGE IT //DO NOT CHANGE IT
struct ChangeDto { struct ObsoleteChangeDto {
Hash txHash; Hash txHash;
uint64_t amount; uint64_t amount;
}; };
@ -121,7 +122,7 @@ void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) {
serializer(value.creationTimestamp, "creation_timestamp"); serializer(value.creationTimestamp, "creation_timestamp");
} }
void serialize(SpentOutputDto& value, CryptoNote::ISerializer& serializer) { void serialize(ObsoleteSpentOutputDto& value, CryptoNote::ISerializer& serializer) {
serializer(value.amount, "amount"); serializer(value.amount, "amount");
serializer(value.transactionHash, "transaction_hash"); serializer(value.transactionHash, "transaction_hash");
serializer(value.outputInTransaction, "output_in_transaction"); serializer(value.outputInTransaction, "output_in_transaction");
@ -129,7 +130,7 @@ void serialize(SpentOutputDto& value, CryptoNote::ISerializer& serializer) {
serializer(value.spendingTransactionHash, "spending_transaction_hash"); serializer(value.spendingTransactionHash, "spending_transaction_hash");
} }
void serialize(ChangeDto& value, CryptoNote::ISerializer& serializer) { void serialize(ObsoleteChangeDto& value, CryptoNote::ISerializer& serializer) {
serializer(value.txHash, "transaction_hash"); serializer(value.txHash, "transaction_hash");
serializer(value.amount, "amount"); serializer(value.amount, "amount");
} }
@ -273,7 +274,7 @@ CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) {
namespace CryptoNote { namespace CryptoNote {
const uint32_t WalletSerializer::SERIALIZATION_VERSION = 3; const uint32_t WalletSerializer::SERIALIZATION_VERSION = 5;
void CryptoContext::incIv() { void CryptoContext::incIv() {
uint64_t * i = reinterpret_cast<uint64_t *>(&iv.data[0]); uint64_t * i = reinterpret_cast<uint64_t *>(&iv.data[0]);
@ -288,12 +289,11 @@ WalletSerializer::WalletSerializer(
uint64_t& pendingBalance, uint64_t& pendingBalance,
WalletsContainer& walletsContainer, WalletsContainer& walletsContainer,
TransfersSyncronizer& synchronizer, TransfersSyncronizer& synchronizer,
SpentOutputs& spentOutputs,
UnlockTransactionJobs& unlockTransactions, UnlockTransactionJobs& unlockTransactions,
TransactionChanges& change,
WalletTransactions& transactions, WalletTransactions& transactions,
WalletTransfers& transfers, WalletTransfers& transfers,
uint32_t transactionSoftLockTime uint32_t transactionSoftLockTime,
UncommitedTransactions& uncommitedTransactions
) : ) :
m_transfersObserver(transfersObserver), m_transfersObserver(transfersObserver),
m_viewPublicKey(viewPublicKey), m_viewPublicKey(viewPublicKey),
@ -302,12 +302,11 @@ WalletSerializer::WalletSerializer(
m_pendingBalance(pendingBalance), m_pendingBalance(pendingBalance),
m_walletsContainer(walletsContainer), m_walletsContainer(walletsContainer),
m_synchronizer(synchronizer), m_synchronizer(synchronizer),
m_spentOutputs(spentOutputs),
m_unlockTransactions(unlockTransactions), m_unlockTransactions(unlockTransactions),
m_change(change),
m_transactions(transactions), m_transactions(transactions),
m_transfers(transfers), m_transfers(transfers),
m_transactionSoftLockTime(transactionSoftLockTime) m_transactionSoftLockTime(transactionSoftLockTime),
uncommitedTransactions(uncommitedTransactions)
{ } { }
void WalletSerializer::save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache) { void WalletSerializer::save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache) {
@ -331,9 +330,8 @@ void WalletSerializer::save(const std::string& password, Common::IOutputStream&
if (saveCache) { if (saveCache) {
saveBalances(destination, saveCache, cryptoContext); saveBalances(destination, saveCache, cryptoContext);
saveTransfersSynchronizer(destination, cryptoContext); saveTransfersSynchronizer(destination, cryptoContext);
saveSpentOutputs(destination, cryptoContext);
saveUnlockTransactionsJobs(destination, cryptoContext); saveUnlockTransactionsJobs(destination, cryptoContext);
saveChange(destination, cryptoContext); saveUncommitedTransactions(destination, cryptoContext);
} }
s.endObject(); s.endObject();
@ -426,29 +424,6 @@ void WalletSerializer::saveTransfersSynchronizer(Common::IOutputStream& destinat
cryptoContext.incIv(); cryptoContext.incIv();
} }
void WalletSerializer::saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
auto& index = m_spentOutputs.get<WalletIndex>();
uint64_t outsCount = index.size();
serializeEncrypted(outsCount, "spent_outputs_count", cryptoContext, destination);
cryptoContext.incIv();
for (const auto& o: index) {
auto it = m_walletsContainer.get<RandomAccessIndex>().iterator_to(*o.wallet);
uint64_t walletIndex = std::distance(m_walletsContainer.get<RandomAccessIndex>().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) { void WalletSerializer::saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
auto& index = m_unlockTransactions.get<TransactionHashIndex>(); auto& index = m_unlockTransactions.get<TransactionHashIndex>();
auto& wallets = m_walletsContainer.get<TransfersContainerIndex>(); auto& wallets = m_walletsContainer.get<TransfersContainerIndex>();
@ -476,19 +451,8 @@ void WalletSerializer::saveUnlockTransactionsJobs(Common::IOutputStream& destina
} }
} }
void WalletSerializer::saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext) { void WalletSerializer::saveUncommitedTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
uint64_t count = m_change.size(); serializeEncrypted(uncommitedTransactions, "uncommited_transactions", cryptoContext, destination);
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) { void WalletSerializer::saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
@ -510,6 +474,7 @@ void WalletSerializer::saveTransfers(Common::IOutputStream& destination, CryptoC
for (const auto& kv: m_transfers) { for (const auto& kv: m_transfers) {
uint64_t txId = kv.first; uint64_t txId = kv.first;
WalletTransferDto tr(kv.second, SERIALIZATION_VERSION); WalletTransferDto tr(kv.second, SERIALIZATION_VERSION);
serializeEncrypted(txId, "transaction_id", cryptoContext, destination); serializeEncrypted(txId, "transaction_id", cryptoContext, destination);
@ -559,12 +524,33 @@ void WalletSerializer::loadWallet(Common::IInputStream& source, const std::strin
loadTransfers(source, cryptoContext, version); loadTransfers(source, cryptoContext, version);
} }
if (version < 5) {
updateTransfersSign();
cache = false;
}
if (cache) { if (cache) {
loadBalances(source, cryptoContext); loadBalances(source, cryptoContext);
loadTransfersSynchronizer(source, cryptoContext); loadTransfersSynchronizer(source, cryptoContext);
loadSpentOutputs(source, cryptoContext); if (version < 5) {
loadObsoleteSpentOutputs(source, cryptoContext);
}
loadUnlockTransactionsJobs(source, cryptoContext); loadUnlockTransactionsJobs(source, cryptoContext);
loadChange(source, cryptoContext);
if (version < 5) {
loadObsoleteChange(source, cryptoContext);
}
if (version > 3) {
loadUncommitedTransactions(source, cryptoContext);
if (version >= 5) {
initTransactionPool();
}
}
} else {
resetCachedBalance();
} }
if (details && cache) { if (details && cache) {
@ -762,30 +748,15 @@ void WalletSerializer::loadTransfersSynchronizer(Common::IInputStream& source, C
m_synchronizer.load(stream); m_synchronizer.load(stream);
} }
void WalletSerializer::loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) { void WalletSerializer::loadObsoleteSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) {
auto& index = m_spentOutputs.get<WalletIndex>();
auto& walletsIndex = m_walletsContainer.get<RandomAccessIndex>();
const uint64_t walletsSize = walletsIndex.size();
uint64_t count = 0; uint64_t count = 0;
deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source); deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source);
cryptoContext.incIv(); cryptoContext.incIv();
for (uint64_t i = 0; i < count; ++i) { for (uint64_t i = 0; i < count; ++i) {
SpentOutputDto dto; ObsoleteSpentOutputDto dto;
deserializeEncrypted(dto, "", cryptoContext, source); deserializeEncrypted(dto, "", cryptoContext, source);
cryptoContext.incIv(); 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));
} }
} }
@ -814,17 +785,37 @@ void WalletSerializer::loadUnlockTransactionsJobs(Common::IInputStream& source,
} }
} }
void WalletSerializer::loadChange(Common::IInputStream& source, CryptoContext& cryptoContext) { void WalletSerializer::loadObsoleteChange(Common::IInputStream& source, CryptoContext& cryptoContext) {
uint64_t count = 0; uint64_t count = 0;
deserializeEncrypted(count, "changes_count", cryptoContext, source); deserializeEncrypted(count, "changes_count", cryptoContext, source);
cryptoContext.incIv(); cryptoContext.incIv();
for (uint64_t i = 0; i < count; i++) { for (uint64_t i = 0; i < count; i++) {
ChangeDto dto; ObsoleteChangeDto dto;
deserializeEncrypted(dto, "", cryptoContext, source); deserializeEncrypted(dto, "", cryptoContext, source);
cryptoContext.incIv(); cryptoContext.incIv();
}
}
m_change[dto.txHash] = dto.amount; void WalletSerializer::loadUncommitedTransactions(Common::IInputStream& source, CryptoContext& cryptoContext) {
deserializeEncrypted(uncommitedTransactions, "uncommited_transactions", cryptoContext, source);
}
void WalletSerializer::initTransactionPool() {
std::unordered_set<Crypto::Hash> 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;
});
} }
} }
@ -838,9 +829,8 @@ void WalletSerializer::updateTransactionsBaseStatus() {
auto& wallets = m_walletsContainer.get<RandomAccessIndex>(); auto& wallets = m_walletsContainer.get<RandomAccessIndex>();
TransactionInformation txInfo; TransactionInformation txInfo;
auto it = std::find_if(std::begin(wallets), std::end(wallets), [&](const WalletRecord& rec) { auto it = std::find_if(std::begin(wallets), std::end(wallets), [&](const WalletRecord& rec) {
int64_t id = 0;
assert(rec.container != nullptr); assert(rec.container != nullptr);
return rec.container->getTransactionInformation(tx.hash, txInfo, id); return rec.container->getTransactionInformation(tx.hash, txInfo);
}); });
tx.isBase = it != std::end(wallets) && txInfo.totalAmountIn == 0; tx.isBase = it != std::end(wallets) && txInfo.totalAmountIn == 0;
@ -848,6 +838,18 @@ void WalletSerializer::updateTransactionsBaseStatus() {
} }
} }
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) { void WalletSerializer::loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext) {
uint64_t count = 0; uint64_t count = 0;
deserializeEncrypted(count, "transactions_count", cryptoContext, source); deserializeEncrypted(count, "transactions_count", cryptoContext, source);

View file

@ -45,12 +45,11 @@ public:
uint64_t& pendingBalance, uint64_t& pendingBalance,
WalletsContainer& walletsContainer, WalletsContainer& walletsContainer,
TransfersSyncronizer& synchronizer, TransfersSyncronizer& synchronizer,
SpentOutputs& spentOutputs,
UnlockTransactionJobs& unlockTransactions, UnlockTransactionJobs& unlockTransactions,
TransactionChanges& change,
WalletTransactions& transactions, WalletTransactions& transactions,
WalletTransfers& transfers, WalletTransfers& transfers,
uint32_t transactionSoftLockTime uint32_t transactionSoftLockTime,
UncommitedTransactions& uncommitedTransactions
); );
void save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache); void save(const std::string& password, Common::IOutputStream& destination, bool saveDetails, bool saveCache);
@ -73,9 +72,8 @@ private:
void saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext); void saveWallets(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext);
void saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext); void saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext);
void saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext); void saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext);
void saveSpentOutputs(Common::IOutputStream& destination, CryptoContext& cryptoContext);
void saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext); void saveUnlockTransactionsJobs(Common::IOutputStream& destination, CryptoContext& cryptoContext);
void saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext); void saveUncommitedTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext);
void saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext); void saveTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext);
void saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext); void saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext);
@ -91,16 +89,20 @@ private:
void subscribeWallets(); void subscribeWallets();
void loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext); void loadBalances(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext); void loadTransfersSynchronizer(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext); void loadObsoleteSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext); void loadUnlockTransactionsJobs(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadChange(Common::IInputStream& source, CryptoContext& cryptoContext); void loadObsoleteChange(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadUncommitedTransactions(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext); void loadTransactions(Common::IInputStream& source, CryptoContext& cryptoContext);
void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version); void loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version);
void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer); void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer);
void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer); void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer);
void addWalletV1Details(const std::vector<WalletLegacyTransaction>& txs, const std::vector<WalletLegacyTransfer>& trs); void addWalletV1Details(const std::vector<WalletLegacyTransaction>& txs, const std::vector<WalletLegacyTransfer>& trs);
void initTransactionPool();
void resetCachedBalance();
void updateTransactionsBaseStatus(); void updateTransactionsBaseStatus();
void updateTransfersSign();
ITransfersObserver& m_transfersObserver; ITransfersObserver& m_transfersObserver;
Crypto::PublicKey& m_viewPublicKey; Crypto::PublicKey& m_viewPublicKey;
@ -109,12 +111,11 @@ private:
uint64_t& m_pendingBalance; uint64_t& m_pendingBalance;
WalletsContainer& m_walletsContainer; WalletsContainer& m_walletsContainer;
TransfersSyncronizer& m_synchronizer; TransfersSyncronizer& m_synchronizer;
SpentOutputs& m_spentOutputs;
UnlockTransactionJobs& m_unlockTransactions; UnlockTransactionJobs& m_unlockTransactions;
TransactionChanges& m_change;
WalletTransactions& m_transactions; WalletTransactions& m_transactions;
WalletTransfers& m_transfers; WalletTransfers& m_transfers;
uint32_t m_transactionSoftLockTime; uint32_t m_transactionSoftLockTime;
UncommitedTransactions& uncommitedTransactions;
}; };
} //namespace CryptoNote } //namespace CryptoNote

View file

@ -0,0 +1,29 @@
// 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 <http://www.gnu.org/licenses/>.
#include "WalletUtils.h"
#include "CryptoNote.h"
namespace CryptoNote {
bool validateAddress(const std::string& address, const CryptoNote::Currency& currency) {
CryptoNote::AccountPublicAddress ignore;
return currency.parseAccountAddressString(address, ignore);
}
}

28
src/Wallet/WalletUtils.h Normal file
View file

@ -0,0 +1,28 @@
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include "CryptoNoteCore/Currency.h"
namespace CryptoNote {
bool validateAddress(const std::string& address, const CryptoNote::Currency& currency);
}

View file

@ -536,10 +536,11 @@ void WalletLegacy::onTransactionUpdated(ITransfersSubscription* object, const Ha
std::shared_ptr<WalletLegacyEvent> event; std::shared_ptr<WalletLegacyEvent> event;
TransactionInformation txInfo; TransactionInformation txInfo;
int64_t txBalance; uint64_t amountIn;
if (m_transferDetails->getTransactionInformation(transactionHash, txInfo, txBalance)) { uint64_t amountOut;
if (m_transferDetails->getTransactionInformation(transactionHash, txInfo, &amountIn, &amountOut)) {
std::unique_lock<std::mutex> lock(m_cacheMutex); std::unique_lock<std::mutex> lock(m_cacheMutex);
event = m_transactionsCache.onTransactionUpdated(txInfo, txBalance); event = m_transactionsCache.onTransactionUpdated(txInfo, static_cast<int64_t>(amountOut) - static_cast<int64_t>(amountIn));
} }
if (event.get()) { if (event.get()) {

View file

@ -32,23 +32,23 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) {
size_t i, j; size_t i, j;
size_t cnt = count - 1; size_t cnt = count - 1;
char (*ints)[HASH_SIZE]; char (*ints)[HASH_SIZE];
for (i = 1; i < sizeof(size_t); i <<= 1) { for (i = 1; i < 8 * sizeof(size_t); i <<= 1) {
cnt |= cnt >> i; cnt |= cnt >> i;
} }
cnt &= ~(cnt >> 1); cnt &= ~(cnt >> 1);
ints = alloca(cnt * HASH_SIZE); ints = alloca(cnt * HASH_SIZE);
memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE);
for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) {
cn_fast_hash(hashes[i], 64, ints[j]); cn_fast_hash(hashes[i], 2 * HASH_SIZE, ints[j]);
} }
assert(i == count); assert(i == count);
while (cnt > 2) { while (cnt > 2) {
cnt >>= 1; cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) { for (i = 0, j = 0; j < cnt; i += 2, ++j) {
cn_fast_hash(ints[i], 64, ints[j]); cn_fast_hash(ints[i], 2 * HASH_SIZE, ints[j]);
} }
} }
cn_fast_hash(ints[0], 64, root_hash); cn_fast_hash(ints[0], 2 * HASH_SIZE, root_hash);
} }
} }

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@" #define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0.8.2" #define PROJECT_VERSION "1.0.9"
#define PROJECT_VERSION_BUILD_NO "625" #define PROJECT_VERSION_BUILD_NO "661"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View file

@ -99,7 +99,25 @@ public:
return std::error_code(); return std::error_code();
} }
void getKnownPoolTxIds(std::vector<Crypto::Hash>& ids) override { const std::unordered_set<Crypto::Hash>& getKnownPoolTxIds() const override {
//stub
static std::unordered_set<Crypto::Hash> empty;
return empty;
}
std::error_code addUnconfirmedTransaction(const ITransactionReader& /*transaction*/) override {
throw std::runtime_error("Not implemented");
}
void removeUnconfirmedTransaction(const Crypto::Hash& /*transactionHash*/) override {
throw std::runtime_error("Not implemented");
}
virtual void addObserver(IBlockchainConsumerObserver* observer) override {
//stub
}
virtual void removeObserver(IBlockchainConsumerObserver* observer) override {
//stub //stub
} }

View file

@ -63,7 +63,6 @@ public:
virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) override { return false; } virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) override { return false; }
virtual bool handle_get_objects(CryptoNote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) override { return false; } virtual bool handle_get_objects(CryptoNote::NOTIFY_REQUEST_GET_OBJECTS::request& arg, CryptoNote::NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) override { return false; }
virtual void on_synchronized() override {} virtual void on_synchronized() override {}
virtual bool is_ready() override { return true; }
virtual bool getOutByMSigGIndex(uint64_t amount, uint64_t gindex, CryptoNote::MultisignatureOutput& out) override { return true; } virtual bool getOutByMSigGIndex(uint64_t amount, uint64_t gindex, CryptoNote::MultisignatureOutput& out) override { return true; }
virtual size_t addChain(const std::vector<const CryptoNote::IBlock*>& chain) override; virtual size_t addChain(const std::vector<const CryptoNote::IBlock*>& chain) override;

View file

@ -23,6 +23,7 @@
#include <Logging/ConsoleLogger.h> #include <Logging/ConsoleLogger.h>
#include "PaymentGate/WalletService.h" #include "PaymentGate/WalletService.h"
#include "PaymentGate/WalletFactory.h"
// test helpers // test helpers
#include "INodeStubs.h" #include "INodeStubs.h"
@ -45,7 +46,8 @@ public:
} }
std::unique_ptr<WalletService> createWalletService(const WalletConfiguration& cfg) { std::unique_ptr<WalletService> createWalletService(const WalletConfiguration& cfg) {
std::unique_ptr<WalletService> service(new WalletService(currency, dispatcher, nodeStub, cfg, logger)); wallet.reset(WalletFactory::createWallet(currency, nodeStub, dispatcher));
std::unique_ptr<WalletService> service(new WalletService(currency, dispatcher, nodeStub, *wallet, cfg, logger));
service->init(); service->init();
return service; return service;
} }
@ -61,6 +63,8 @@ protected:
TestBlockchainGenerator generator; TestBlockchainGenerator generator;
INodeTrivialRefreshStub nodeStub; INodeTrivialRefreshStub nodeStub;
System::Dispatcher dispatcher; System::Dispatcher dispatcher;
std::unique_ptr<CryptoNote::IWallet> wallet;
}; };
@ -90,15 +94,9 @@ TEST_F(PaymentGateTest, addTransaction) {
System::Timer(dispatcher).sleep(std::chrono::seconds(2)); System::Timer(dispatcher).sleep(std::chrono::seconds(2));
uint64_t txCount = 0;
ASSERT_TRUE(!service->getTransactionsCount(txCount));
ASSERT_EQ(3, txCount);
uint64_t pending = 0, actual = 0; uint64_t pending = 0, actual = 0;
ASSERT_TRUE(!service->getPendingBalance(pending)); service->getBalance(actual, pending);
ASSERT_TRUE(!service->getActualBalance(actual));
ASSERT_NE(0, pending); ASSERT_NE(0, pending);
ASSERT_NE(0, actual); ASSERT_NE(0, actual);
@ -106,6 +104,7 @@ TEST_F(PaymentGateTest, addTransaction) {
ASSERT_EQ(pending * 2, actual); ASSERT_EQ(pending * 2, actual);
} }
/*
TEST_F(PaymentGateTest, DISABLED_sendTransaction) { TEST_F(PaymentGateTest, DISABLED_sendTransaction) {
auto cfg = createWalletConfiguration(); auto cfg = createWalletConfiguration();
@ -142,16 +141,16 @@ TEST_F(PaymentGateTest, DISABLED_sendTransaction) {
uint64_t txId = 0; uint64_t txId = 0;
{ {
SendTransactionRequest req; SendTransaction::Request req;
SendTransactionResponse res; SendTransaction::Response res;
req.destinations.push_back(TransferDestination{ TEST_AMOUNT, recvAddress }); req.transfers.push_back(WalletRpcOrder{ TEST_AMOUNT, recvAddress });
req.fee = currency.minimumFee(); req.fee = currency.minimumFee();
req.mixin = 1; req.anonymity = 1;
req.unlockTime = 0; req.unlockTime = 0;
req.paymentId = paymentIdStr; req.paymentId = paymentIdStr;
ASSERT_TRUE(!service->sendTransaction(req, res)); ASSERT_TRUE(!service->sendTransaction(req, res.transactionHash));
txId = res.transactionId; txId = res.transactionId;
} }
@ -263,4 +262,4 @@ TEST_F(PaymentGateTest, DISABLED_sendTransaction) {
ASSERT_EQ(1, recvPayment.size()); ASSERT_EQ(1, recvPayment.size());
ASSERT_EQ(TEST_AMOUNT / 2, recvPayment[0].amount); ASSERT_EQ(TEST_AMOUNT / 2, recvPayment[0].amount);
} }
} } */

View file

@ -20,6 +20,7 @@
#include "Transfers/BlockchainSynchronizer.h" #include "Transfers/BlockchainSynchronizer.h"
#include "Transfers/TransfersConsumer.h" #include "Transfers/TransfersConsumer.h"
#include "crypto/hash.h"
#include "CryptoNoteCore/TransactionApi.h" #include "CryptoNoteCore/TransactionApi.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h" #include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "CryptoNoteCore/CryptoNoteTools.h" #include "CryptoNoteCore/CryptoNoteTools.h"
@ -122,11 +123,21 @@ public:
m_blockchain.push_back(genesisBlockHash); m_blockchain.push_back(genesisBlockHash);
} }
void addPoolTransaction(const Crypto::Hash& hash) {
m_pool.emplace(hash);
}
virtual SynchronizationStart getSyncStart() override { virtual SynchronizationStart getSyncStart() override {
SynchronizationStart start = { 0, 0 }; SynchronizationStart start = { 0, 0 };
return start; return start;
} }
virtual void addObserver(IBlockchainConsumerObserver* observer) override {
}
virtual void removeObserver(IBlockchainConsumerObserver* observer) override {
}
virtual void onBlockchainDetach(uint32_t height) override { virtual void onBlockchainDetach(uint32_t height) override {
assert(height < m_blockchain.size()); assert(height < m_blockchain.size());
m_blockchain.resize(height); m_blockchain.resize(height);
@ -145,28 +156,32 @@ public:
return m_blockchain; return m_blockchain;
} }
virtual void getKnownPoolTxIds(std::vector<Hash>& ids) override { virtual const std::unordered_set<Crypto::Hash>& getKnownPoolTxIds() const override {
ids.assign(m_pool.begin(), m_pool.end()); return m_pool;
} }
virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) override { virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) override {
for (const auto& tx: addedTransactions) { for (const auto& tx: addedTransactions) {
Hash hash = tx->getTransactionHash(); m_pool.emplace(tx->getTransactionHash());
m_pool.push_back(reinterpret_cast<const Hash&>(hash));
} }
for (auto& hash : deletedTransactions) { for (auto& hash : deletedTransactions) {
auto pos = std::find(m_pool.begin(), m_pool.end(), hash); m_pool.erase(hash);
if (pos != m_pool.end()) {
m_pool.erase(pos);
}
} }
return std::error_code(); return std::error_code();
} }
std::error_code addUnconfirmedTransaction(const ITransactionReader& /*transaction*/) override {
throw std::runtime_error("Not implemented");
}
void removeUnconfirmedTransaction(const Crypto::Hash& /*transactionHash*/) override {
throw std::runtime_error("Not implemented");
}
private: private:
std::vector<Hash> m_pool; std::unordered_set<Crypto::Hash> m_pool;
std::vector<Hash> m_blockchain; std::vector<Hash> m_blockchain;
}; };
@ -483,18 +498,12 @@ TEST_F(BcSTest, serializationCheck) {
class FunctorialPoolConsumerStub : public ConsumerStub { class FunctorialPoolConsumerStub : public ConsumerStub {
public: public:
FunctorialPoolConsumerStub(const Hash& genesisBlockHash) : ConsumerStub(genesisBlockHash) {} FunctorialPoolConsumerStub(const Hash& genesisBlockHash) : ConsumerStub(genesisBlockHash) {}
virtual void getKnownPoolTxIds(std::vector<Hash>& ids) override {
getKnownPoolTxIdsFunctor(ids);
}
virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) override { virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) override {
return onPoolUpdatedFunctor(addedTransactions, deletedTransactions); return onPoolUpdatedFunctor(addedTransactions, deletedTransactions);
} }
std::function<void(std::vector<Hash>&)> getKnownPoolTxIdsFunctor;
std::function<std::error_code(const std::vector<std::unique_ptr<ITransactionReader>>&, const std::vector<Hash>&)> onPoolUpdatedFunctor; std::function<std::error_code(const std::vector<std::unique_ptr<ITransactionReader>>&, const std::vector<Hash>&)> onPoolUpdatedFunctor;
}; };
@ -511,8 +520,6 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) {
auto tx2hash = getObjectHash(tx2); auto tx2hash = getObjectHash(tx2);
auto tx3hash = getObjectHash(tx3); auto tx3hash = getObjectHash(tx3);
std::vector<Hash> consumer1Pool = { tx1hash, tx2hash };
std::vector<Hash> consumer2Pool = { tx2hash, tx3hash };
std::unordered_set<Hash> firstExpectedPool = { tx1hash, tx2hash, tx3hash }; std::unordered_set<Hash> firstExpectedPool = { tx1hash, tx2hash, tx3hash };
std::unordered_set<Hash> secondExpectedPool = { tx2hash }; std::unordered_set<Hash> secondExpectedPool = { tx2hash };
@ -523,8 +530,11 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) {
FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash());
FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash());
c1.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) { ids.assign(consumer1Pool.begin(), consumer1Pool.end()); }; c1.addPoolTransaction(tx1hash);
c2.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) { ids.assign(consumer2Pool.begin(), consumer2Pool.end()); }; c1.addPoolTransaction(tx2hash);
c2.addPoolTransaction(tx2hash);
c2.addPoolTransaction(tx3hash);
std::vector<Hash> c1ResponseDeletedPool; std::vector<Hash> c1ResponseDeletedPool;
std::vector<Hash> c2ResponseDeletedPool; std::vector<Hash> c2ResponseDeletedPool;
@ -606,6 +616,7 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) {
TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) { TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) {
addConsumers(2); addConsumers(2);
m_consumers.front()->addPoolTransaction(Crypto::rand<Crypto::Hash>());
int requestsCount = 0; int requestsCount = 0;
@ -635,12 +646,12 @@ TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) {
m_sync.removeObserver(&o1); m_sync.removeObserver(&o1);
o1.syncFunc = [](std::error_code) {}; o1.syncFunc = [](std::error_code) {};
EXPECT_EQ(4, requestsCount); EXPECT_EQ(4, requestsCount);
} }
TEST_F(BcSTest, firstPoolSynchronizationCheckGetPoolErr) { TEST_F(BcSTest, firstPoolSynchronizationCheckGetPoolErr) {
addConsumers(2); addConsumers(2);
m_consumers.front()->addPoolTransaction(Crypto::rand<Crypto::Hash>());
int requestsCount = 0; int requestsCount = 0;
@ -895,9 +906,6 @@ TEST_F(BcSTest, poolSynchronizationCheckConsumersNotififcation) {
FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash());
FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash());
c1.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
c2.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
bool c1Notified = false; bool c1Notified = false;
bool c2Notified = false; bool c2Notified = false;
c1.onPoolUpdatedFunctor = [&](const std::vector<std::unique_ptr<ITransactionReader>>& new_txs, const std::vector<Hash>& deleted)->std::error_code { c1.onPoolUpdatedFunctor = [&](const std::vector<std::unique_ptr<ITransactionReader>>& new_txs, const std::vector<Hash>& deleted)->std::error_code {
@ -933,9 +941,6 @@ TEST_F(BcSTest, poolSynchronizationCheckConsumerReturnError) {
FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash());
FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash()); FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash());
c1.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
c2.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
bool c1Notified = false; bool c1Notified = false;
bool c2Notified = false; bool c2Notified = false;
c1.onPoolUpdatedFunctor = [&](const std::vector<std::unique_ptr<ITransactionReader>>& new_txs, const std::vector<Hash>& deleted)->std::error_code { c1.onPoolUpdatedFunctor = [&](const std::vector<std::unique_ptr<ITransactionReader>>& new_txs, const std::vector<Hash>& deleted)->std::error_code {

View file

@ -210,6 +210,10 @@ void TestBlockchainGenerator::addToBlockchain(const CryptoNote::Transaction& tx)
} }
void TestBlockchainGenerator::addToBlockchain(const std::vector<CryptoNote::Transaction>& txs) { void TestBlockchainGenerator::addToBlockchain(const std::vector<CryptoNote::Transaction>& txs) {
addToBlockchain(txs, miner_acc);
}
void TestBlockchainGenerator::addToBlockchain(const std::vector<CryptoNote::Transaction>& txs, const CryptoNote::AccountBase& minerAddress) {
std::list<CryptoNote::Transaction> txsToBlock; std::list<CryptoNote::Transaction> txsToBlock;
for (const auto& tx: txs) { for (const auto& tx: txs) {
@ -222,7 +226,7 @@ void TestBlockchainGenerator::addToBlockchain(const std::vector<CryptoNote::Tran
CryptoNote::Block& prev_block = m_blockchain.back(); CryptoNote::Block& prev_block = m_blockchain.back();
CryptoNote::Block block; CryptoNote::Block block;
generator.constructBlock(block, prev_block, miner_acc, txsToBlock); generator.constructBlock(block, prev_block, minerAddress, txsToBlock);
m_blockchain.push_back(block); m_blockchain.push_back(block);
addTx(block.baseTransaction); addTx(block.baseTransaction);
@ -396,3 +400,9 @@ bool TestBlockchainGenerator::getMultisignatureOutputByGlobalIndex(uint64_t amou
out = boost::get<MultisignatureOutput>(tx.outputs[entry.indexOut].target); out = boost::get<MultisignatureOutput>(tx.outputs[entry.indexOut].target);
return true; return true;
} }
bool TestBlockchainGenerator::generateFromBaseTx(const CryptoNote::AccountBase& address) {
std::unique_lock<std::mutex> lock(m_mutex);
addToBlockchain({}, address);
return true;
}

View file

@ -43,6 +43,7 @@ public:
void addTxToBlockchain(const CryptoNote::Transaction& transaction); void addTxToBlockchain(const CryptoNote::Transaction& transaction);
bool getTransactionByHash(const Crypto::Hash& hash, CryptoNote::Transaction& tx, bool checkTxPool = false); bool getTransactionByHash(const Crypto::Hash& hash, CryptoNote::Transaction& tx, bool checkTxPool = false);
const CryptoNote::AccountBase& getMinerAccount() const; const CryptoNote::AccountBase& getMinerAccount() const;
bool generateFromBaseTx(const CryptoNote::AccountBase& address);
void putTxToPool(const CryptoNote::Transaction& tx); void putTxToPool(const CryptoNote::Transaction& tx);
void getPoolSymmetricDifference(std::vector<Crypto::Hash>&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual, void getPoolSymmetricDifference(std::vector<Crypto::Hash>&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual,
@ -96,6 +97,7 @@ private:
void addToBlockchain(const CryptoNote::Transaction& tx); void addToBlockchain(const CryptoNote::Transaction& tx);
void addToBlockchain(const std::vector<CryptoNote::Transaction>& txs); void addToBlockchain(const std::vector<CryptoNote::Transaction>& txs);
void addToBlockchain(const std::vector<CryptoNote::Transaction>& txs, const CryptoNote::AccountBase& minerAddress);
void addTx(const CryptoNote::Transaction& tx); void addTx(const CryptoNote::Transaction& tx);
bool doGenerateTransactionsInOneBlock(CryptoNote::AccountPublicAddress const &address, size_t n); bool doGenerateTransactionsInOneBlock(CryptoNote::AccountPublicAddress const &address, size_t n);

View file

@ -742,8 +742,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionInformation) {
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 2)); ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 2));
TransactionInformation info; TransactionInformation info;
int64_t balance; ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), info));
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), info, balance));
ASSERT_EQ(tx->getTransactionHash(), info.transactionHash); ASSERT_EQ(tx->getTransactionHash(), info.transactionHash);
ASSERT_EQ(tx->getTransactionPublicKey(), info.publicKey); ASSERT_EQ(tx->getTransactionPublicKey(), info.publicKey);
@ -875,7 +874,7 @@ TEST_F(TransfersConsumerTest, onPoolUpdated_addTransactionDoesNotGetsGlobalIndic
ASSERT_TRUE(m_node.calls_getTransactionOutsGlobalIndices.empty()); ASSERT_TRUE(m_node.calls_getTransactionOutsGlobalIndices.empty());
} }
TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransaction) { TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransactionNotDeleted) {
auto& sub = addSubscription(); auto& sub = addSubscription();
TransfersObserver observer; TransfersObserver observer;
sub.addObserver(&observer); sub.addObserver(&observer);
@ -887,15 +886,42 @@ TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransaction) {
m_consumer.onPoolUpdated({}, deleted); m_consumer.onPoolUpdated({}, deleted);
ASSERT_EQ(0, observer.deleted.size());
}
TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransaction) {
const uint8_t TX_COUNT = 2;
auto& sub = addSubscription();
TransfersObserver observer;
sub.addObserver(&observer);
std::vector<std::unique_ptr<ITransactionReader>> added;
std::vector<Crypto::Hash> deleted;
for (uint8_t i = 0; i < TX_COUNT; ++i) {
// construct tx
TestTransactionBuilder b1;
auto unknownSender = generateAccountKeys();
b1.addTestInput(10000, unknownSender);
auto out = b1.addTestKeyOutput(10000, UNCONFIRMED_TRANSACTION_GLOBAL_OUTPUT_INDEX, m_accountKeys);
auto tx = std::shared_ptr<ITransactionReader>(b1.build().release());
std::unique_ptr<ITransactionReader> prefix = createTransactionPrefix(convertTx(*tx));
added.push_back(std::move(prefix));
deleted.push_back(added.back()->getTransactionHash());
}
m_consumer.onPoolUpdated(added, {});
m_consumer.onPoolUpdated({}, deleted);
ASSERT_EQ(deleted.size(), observer.deleted.size()); ASSERT_EQ(deleted.size(), observer.deleted.size());
ASSERT_EQ(reinterpret_cast<const Hash&>(deleted[0]), observer.deleted[0]); ASSERT_EQ(deleted, observer.deleted);
ASSERT_EQ(reinterpret_cast<const Hash&>(deleted[1]), observer.deleted[1]);
} }
TEST_F(TransfersConsumerTest, getKnownPoolTxIds_empty) { TEST_F(TransfersConsumerTest, getKnownPoolTxIds_empty) {
addSubscription(); addSubscription();
std::vector<Crypto::Hash> ids; const std::unordered_set<Crypto::Hash>& ids = m_consumer.getKnownPoolTxIds();
m_consumer.getKnownPoolTxIds(ids);
ASSERT_TRUE(ids.empty()); ASSERT_TRUE(ids.empty());
} }
@ -926,14 +952,13 @@ TEST_F(TransfersConsumerTest, getKnownPoolTxIds_returnsUnconfirmed) {
v.push_back(createTransactionPrefix(convertTx(*txs[2]))); v.push_back(createTransactionPrefix(convertTx(*txs[2])));
m_consumer.onPoolUpdated(v, {}); m_consumer.onPoolUpdated(v, {});
std::vector<Crypto::Hash> ids; const std::unordered_set<Crypto::Hash>& ids = m_consumer.getKnownPoolTxIds();
m_consumer.getKnownPoolTxIds(ids);
ASSERT_EQ(3, ids.size()); ASSERT_EQ(3, ids.size());
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
auto txhash = txs[i]->getTransactionHash(); auto txhash = txs[i]->getTransactionHash();
ASSERT_TRUE(std::find(ids.begin(), ids.end(), reinterpret_cast<const Crypto::Hash&>(txhash)) != ids.end()); ASSERT_EQ(1, ids.count(txhash));
} }
} }

View file

@ -295,10 +295,12 @@ TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputToKey) {
ASSERT_TRUE(transfers.empty()); ASSERT_TRUE(transfers.empty());
TransactionInformation txInfo; TransactionInformation txInfo;
int64_t txBalance; uint64_t amountIn;
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance)); uint64_t amountOut;
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, &amountIn, &amountOut));
ASSERT_EQ(blockInfo.height, txInfo.blockHeight); ASSERT_EQ(blockInfo.height, txInfo.blockHeight);
ASSERT_EQ(TEST_OUTPUT_AMOUNT, txBalance); ASSERT_EQ(0, amountIn);
ASSERT_EQ(TEST_OUTPUT_AMOUNT, amountOut);
std::vector<Crypto::Hash> unconfirmedTransactions; std::vector<Crypto::Hash> unconfirmedTransactions;
container.getUnconfirmedTransactions(unconfirmedTransactions); container.getUnconfirmedTransactions(unconfirmedTransactions);
@ -339,10 +341,12 @@ TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputToKey) {
ASSERT_EQ(1, transfers.size()); ASSERT_EQ(1, transfers.size());
TransactionInformation txInfo; TransactionInformation txInfo;
int64_t txBalance; uint64_t amountIn;
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance)); uint64_t amountOut;
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, &amountIn, &amountOut));
ASSERT_EQ(blockInfo.height, txInfo.blockHeight); ASSERT_EQ(blockInfo.height, txInfo.blockHeight);
ASSERT_EQ(TEST_OUTPUT_AMOUNT, txBalance); ASSERT_EQ(0, amountIn);
ASSERT_EQ(TEST_OUTPUT_AMOUNT, amountOut);
std::vector<Crypto::Hash> unconfirmedTransactions; std::vector<Crypto::Hash> unconfirmedTransactions;
container.getUnconfirmedTransactions(unconfirmedTransactions); container.getUnconfirmedTransactions(unconfirmedTransactions);
@ -379,8 +383,7 @@ TEST_F(TransfersContainer_addTransaction, addingEmptyTransactionOuptutsDoesNotCh
ASSERT_TRUE(transfers.empty()); ASSERT_TRUE(transfers.empty());
TransactionInformation txInfo; TransactionInformation txInfo;
int64_t txBalance; ASSERT_FALSE(container.getTransactionInformation(tx->getTransactionHash(), txInfo));
ASSERT_FALSE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance));
std::vector<Crypto::Hash> unconfirmedTransactions; std::vector<Crypto::Hash> unconfirmedTransactions;
container.getUnconfirmedTransactions(unconfirmedTransactions); container.getUnconfirmedTransactions(unconfirmedTransactions);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff