Bytecoin v.1.0.9 release
This commit is contained in:
parent
00915e6d34
commit
6d4e1d1ea3
82 changed files with 7459 additions and 1943 deletions
|
@ -1,3 +1,8 @@
|
|||
Release notes 1.0.9
|
||||
|
||||
- New API for Bytecoin RPC Wallet
|
||||
- Various improvements
|
||||
|
||||
Release notes 1.0.8
|
||||
|
||||
- Fusion transactions for Bytecoin Wallet
|
||||
|
|
|
@ -97,7 +97,8 @@ public:
|
|||
virtual size_t transactionsCount() 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 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;
|
||||
//only type flags are feasible for this function
|
||||
virtual std::vector<TransactionOutputInformation> getTransactionInputs(const Crypto::Hash& transactionHash, uint32_t flags) const = 0;
|
||||
|
|
|
@ -62,6 +62,16 @@ public:
|
|||
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 {
|
||||
public:
|
||||
virtual ~ITransfersSynchronizer() {}
|
||||
|
@ -71,6 +81,7 @@ public:
|
|||
virtual void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions) = 0;
|
||||
// returns nullptr if address is not found
|
||||
virtual ITransfersSubscription* getSubscription(const AccountPublicAddress& acc) = 0;
|
||||
virtual std::vector<Crypto::Hash> getViewKeyKnownBlocks(const Crypto::PublicKey& publicViewKey) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ enum class WalletTransactionState : uint8_t {
|
|||
SUCCEEDED = 0,
|
||||
FAILED,
|
||||
CANCELLED,
|
||||
CREATED
|
||||
CREATED,
|
||||
DELETED
|
||||
};
|
||||
|
||||
enum WalletEventType {
|
||||
|
@ -40,7 +41,7 @@ enum WalletEventType {
|
|||
TRANSACTION_UPDATED,
|
||||
BALANCE_UNLOCKED,
|
||||
SYNC_PROGRESS_UPDATED,
|
||||
SYNC_COMPLETED
|
||||
SYNC_COMPLETED,
|
||||
};
|
||||
|
||||
struct WalletTransactionCreatedData {
|
||||
|
@ -80,7 +81,8 @@ struct WalletTransaction {
|
|||
|
||||
enum class WalletTransferType : uint8_t {
|
||||
USUAL = 0,
|
||||
DONATION
|
||||
DONATION,
|
||||
CHANGE
|
||||
};
|
||||
|
||||
struct WalletOrder {
|
||||
|
@ -100,13 +102,24 @@ struct DonationSettings {
|
|||
};
|
||||
|
||||
struct TransactionParameters {
|
||||
std::string sourceAddress;
|
||||
std::vector<std::string> sourceAddresses;
|
||||
std::vector<WalletOrder> destinations;
|
||||
uint64_t fee = 0;
|
||||
uint64_t mixIn = 0;
|
||||
std::string extra;
|
||||
uint64_t unlockTimestamp = 0;
|
||||
DonationSettings donation;
|
||||
std::string changeDestination;
|
||||
};
|
||||
|
||||
struct WalletTransactionWithTransfers {
|
||||
WalletTransaction transaction;
|
||||
std::vector<WalletTransfer> transfers;
|
||||
};
|
||||
|
||||
struct TransactionsInBlockInfo {
|
||||
Crypto::Hash blockHash;
|
||||
std::vector<WalletTransactionWithTransfers> transactions;
|
||||
};
|
||||
|
||||
class IWallet {
|
||||
|
@ -124,6 +137,7 @@ public:
|
|||
virtual size_t getAddressCount() const = 0;
|
||||
virtual std::string getAddress(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 std::string createAddress() = 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 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 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 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 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 WalletTransactionWithTransfers getTransaction(const Crypto::Hash& transactionHash) const = 0;
|
||||
virtual std::vector<TransactionsInBlockInfo> getTransactions(const Crypto::Hash& blockHash, size_t count) const = 0;
|
||||
virtual std::vector<TransactionsInBlockInfo> getTransactions(uint32_t blockIndex, size_t count) const = 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 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 stop() = 0;
|
||||
|
||||
|
|
|
@ -87,12 +87,16 @@ bool BlockchainExplorer::PoolUpdateGuard::beginUpdate() {
|
|||
for (;;) {
|
||||
switch (state) {
|
||||
case State::NONE:
|
||||
return true;
|
||||
if (m_state.compare_exchange_weak(state, State::UPDATING)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::UPDATING:
|
||||
if (m_state.compare_exchange_weak(state, State::UPDATE_REQUIRED)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case State::UPDATE_REQUIRED:
|
||||
return false;
|
||||
|
|
|
@ -30,6 +30,7 @@ file(GLOB_RECURSE JsonRpcServer JsonRpcServer/*)
|
|||
|
||||
file(GLOB_RECURSE PaymentGate PaymentGate/*)
|
||||
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})
|
||||
|
||||
|
@ -54,17 +55,17 @@ add_executable(ConnectivityTool ${ConnectivityTool})
|
|||
add_executable(Daemon ${Daemon})
|
||||
add_executable(SimpleWallet ${SimpleWallet})
|
||||
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(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(PaymentGateService PaymentGate JsonRpcServer Wallet NodeRpcProxy Transfers CryptoNoteCore Crypto P2P Rpc Http Serialization System Logging Common InProcessNode upnpc-static BlockchainExplorer ${Boost_LIBRARIES})
|
||||
|
||||
if (MSVC)
|
||||
target_link_libraries(ConnectivityTool ws2_32)
|
||||
target_link_libraries(SimpleWallet ws2_32)
|
||||
endif ()
|
||||
target_link_libraries(Miner CryptoNoteCore Rpc Serialization System Http Logging Common Crypto ${Boost_LIBRARIES})
|
||||
|
||||
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 SimpleWallet PROPERTY OUTPUT_NAME "simplewallet")
|
||||
set_property(TARGET PaymentGateService PROPERTY OUTPUT_NAME "walletd")
|
||||
set_property(TARGET Miner PROPERTY OUTPUT_NAME "miner")
|
||||
|
|
|
@ -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
|
||||
|
||||
template<typename F, typename... Args>
|
||||
|
|
37
src/Common/ScopeExit.cpp
Normal file
37
src/Common/ScopeExit.cpp
Normal 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
41
src/Common/ScopeExit.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -359,4 +359,9 @@ std::string get_nix_version_display_string()
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,4 +26,5 @@ namespace Tools
|
|||
std::string get_os_version_string();
|
||||
bool create_directories_if_necessary(const std::string& path);
|
||||
std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name);
|
||||
bool directoryExists(const std::string& path);
|
||||
}
|
||||
|
|
|
@ -165,7 +165,8 @@ const CheckpointData CHECKPOINTS[] = {
|
|||
{810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"},
|
||||
{816000, "32b7fdd4e4d715db81f8f09f4ba5e5c78e8113f2804d61a57378baee479ce745"},
|
||||
{822000, "a3c9603c6813a0dc0efc40db288c356d1a7f02d1d2e47bee04346e73715f8984"},
|
||||
{841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"}
|
||||
{841000, "2cffb6504ee38f708a6256a63585f9382b3b426e64b4504236c70678bd160dce"},
|
||||
{890000, "a7132932ea31236ce6b8775cd1380edf90b5e536ee4202c77b69a3d62445fcd2"}
|
||||
};
|
||||
} // CryptoNote
|
||||
|
||||
|
|
|
@ -319,7 +319,6 @@ m_currency(currency),
|
|||
m_tx_pool(tx_pool),
|
||||
m_current_block_cumul_sz_limit(0),
|
||||
m_is_in_checkpoint_zone(false),
|
||||
m_is_blockchain_storing(false),
|
||||
m_upgradeDetector(currency, m_blocks, BLOCK_MAJOR_VERSION_2, logger),
|
||||
m_checkpoints(logger) {
|
||||
|
||||
|
|
|
@ -107,7 +107,6 @@ namespace CryptoNote {
|
|||
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);
|
||||
uint64_t getCurrentCumulativeBlocksizeLimit();
|
||||
bool isStoringBlockchain(){return m_is_blockchain_storing;}
|
||||
uint64_t blockDifficulty(size_t i);
|
||||
bool getBlockContainingTransaction(const Crypto::Hash& txId, Crypto::Hash& blockId, uint32_t& blockHeight);
|
||||
bool getAlreadyGeneratedCoins(const Crypto::Hash& hash, uint64_t& generatedCoins);
|
||||
|
@ -248,7 +247,6 @@ namespace CryptoNote {
|
|||
std::string m_config_folder;
|
||||
Checkpoints m_checkpoints;
|
||||
std::atomic<bool> m_is_in_checkpoint_zone;
|
||||
std::atomic<bool> m_is_blockchain_storing;
|
||||
|
||||
typedef SwappedVector<BlockEntry> Blocks;
|
||||
typedef std::unordered_map<Crypto::Hash, uint32_t> BlockMap;
|
||||
|
|
|
@ -99,11 +99,6 @@ bool core::handle_command_line(const boost::program_options::variables_map& vm)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool core::is_ready() {
|
||||
return !m_blockchain.isStoringBlockchain();
|
||||
}
|
||||
|
||||
|
||||
uint32_t core::get_current_blockchain_height() {
|
||||
return m_blockchain.getCurrentBlockchainHeight();
|
||||
}
|
||||
|
|
|
@ -97,7 +97,6 @@ namespace CryptoNote {
|
|||
std::vector<Crypto::Hash> buildSparseChain() override;
|
||||
std::vector<Crypto::Hash> buildSparseChain(const Crypto::Hash& startBlockId) override;
|
||||
void on_synchronized() override;
|
||||
bool is_ready() 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);
|
||||
|
|
|
@ -29,6 +29,7 @@ CoreConfig::CoreConfig() {
|
|||
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())) {
|
||||
configFolder = command_line::get_arg(options, command_line::arg_data_dir);
|
||||
configFolderDefaulted = options[command_line::arg_data_dir.name].defaulted();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
void init(const boost::program_options::variables_map& options);
|
||||
|
||||
std::string configFolder;
|
||||
bool configFolderDefaulted = true;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
||||
|
|
|
@ -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_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 bool is_ready() = 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;
|
||||
|
|
|
@ -108,6 +108,9 @@ bool CryptoNoteProtocolHandler::start_sync(CryptoNoteConnectionContext& context)
|
|||
logger(Logging::TRACE) << context << "Starting synchronization";
|
||||
|
||||
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>();
|
||||
r.block_ids = m_core.buildSparseChain();
|
||||
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()) {
|
||||
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";
|
||||
context.m_state = CryptoNoteConnectionContext::state_shutdown;
|
||||
return 1;
|
||||
}
|
||||
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";
|
||||
context.m_state = CryptoNoteConnectionContext::state_shutdown;
|
||||
return 1;
|
||||
|
@ -437,6 +441,8 @@ int CryptoNoteProtocolHandler::processObjects(CryptoNoteConnectionContext& conte
|
|||
} else if (bvc.m_already_exists) {
|
||||
logger(Logging::DEBUGGING) << context << "Block already exists, switching to idle state";
|
||||
context.m_state = CryptoNoteConnectionContext::state_idle;
|
||||
context.m_needed_objects.clear();
|
||||
context.m_requested_objects.clear();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -202,11 +202,21 @@ int main(int argc, char* argv[])
|
|||
RpcServerConfig rpcConfig;
|
||||
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;
|
||||
|
||||
CryptoNote::CryptoNoteProtocolHandler cprotocol(currency, dispatcher, ccore, nullptr, 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);
|
||||
ccore.set_cryptonote_protocol(&cprotocol);
|
||||
|
|
105
src/Miner/BlockchainMonitor.cpp
Normal file
105
src/Miner/BlockchainMonitor.cpp
Normal 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;
|
||||
}
|
||||
}
|
46
src/Miner/BlockchainMonitor.h
Normal file
46
src/Miner/BlockchainMonitor.h
Normal 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
157
src/Miner/Miner.cpp
Normal 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
67
src/Miner/Miner.h
Normal 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
31
src/Miner/MinerEvent.h
Normal 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
276
src/Miner/MinerManager.cpp
Normal 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
76
src/Miner/MinerManager.h
Normal 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
138
src/Miner/MiningConfig.cpp
Normal 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
43
src/Miner/MiningConfig.h
Normal 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
52
src/Miner/main.cpp
Normal 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;
|
||||
}
|
|
@ -225,36 +225,36 @@ void NodeRpcProxy::updateBlockchainStatus() {
|
|||
m_lastKnowHash = blockHash;
|
||||
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);
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
m_connected = m_httpClient->isConnected();
|
||||
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRpcProxy::updatePeerCount() {
|
||||
CryptoNote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req);
|
||||
CryptoNote::COMMAND_RPC_GET_INFO::response rsp = AUTO_VAL_INIT(rsp);
|
||||
|
||||
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));
|
||||
}
|
||||
void NodeRpcProxy::updatePeerCount(size_t peerCount) {
|
||||
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 {
|
||||
return m_networkHeight.load(std::memory_order_relaxed);
|
||||
return m_networkHeight.load(std::memory_order_relaxed) + 1;
|
||||
}
|
||||
|
||||
uint64_t NodeRpcProxy::getLastLocalBlockTimestamp() const {
|
||||
|
|
|
@ -92,7 +92,7 @@ private:
|
|||
void updateNodeStatus();
|
||||
void updateBlockchainStatus();
|
||||
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);
|
||||
|
||||
std::error_code doRelayTransaction(const CryptoNote::Transaction& transaction);
|
||||
|
|
|
@ -20,138 +20,114 @@
|
|||
|
||||
namespace PaymentService {
|
||||
|
||||
void TransferDestination::serialize(CryptoNote::ISerializer& serializer) {
|
||||
bool r = serializer(amount, "amount");
|
||||
r &= serializer(address, "address");
|
||||
void Reset::Request::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(viewSecretKey, "viewSecretKey");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void SendTransactionRequest::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) {
|
||||
void CreateAddress::Response::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(address, "address");
|
||||
}
|
||||
|
||||
void GetAddressCountResponse::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(count, "count");
|
||||
}
|
||||
|
||||
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 DeleteAddress::Request::serialize(CryptoNote::ISerializer& serializer) {
|
||||
if (!serializer(address, "address")) {
|
||||
throw RequestSerializationError();
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
void GetActualBalanceResponse::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(actualBalance, "actual_balance");
|
||||
void GetBalance::Response::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(availableBalance, "availableBalance");
|
||||
serializer(lockedAmount, "lockedAmount");
|
||||
}
|
||||
|
||||
void GetPendingBalanceResponse::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(pendingBalance, "pending_balance");
|
||||
}
|
||||
|
||||
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");
|
||||
void GetBlockHashes::Request::serialize(CryptoNote::ISerializer& serializer) {
|
||||
bool r = serializer(firstBlockIndex, "firstBlockIndex");
|
||||
r &= serializer(blockCount, "blockCount");
|
||||
|
||||
if (!r) {
|
||||
throw RequestSerializationError();
|
||||
}
|
||||
}
|
||||
|
||||
void GetTransactionIdByTransferIdResponse::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(transactionid, "transaction_id");
|
||||
void GetBlockHashes::Response::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(blockHashes, "blockHashes");
|
||||
}
|
||||
|
||||
void GetTransactionRequest::serialize(CryptoNote::ISerializer& serializer) {
|
||||
bool r = serializer(transactionId, "transaction_id");
|
||||
void TransactionHashesInBlockRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(firstTransferId, "first_transfer_id");
|
||||
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");
|
||||
if (!serializer(blockCount, "blockCount")) {
|
||||
throw RequestSerializationError();
|
||||
}
|
||||
|
||||
serializer(paymentId, "paymentId");
|
||||
}
|
||||
|
||||
void ListTransactionsRequest::serialize(CryptoNote::ISerializer& serializer) {
|
||||
bool r = serializer(startingTransactionId, "starting_transaction_id");
|
||||
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 GetTransactionHashes::Response::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(items, "items");
|
||||
}
|
||||
|
||||
void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
|
||||
|
@ -160,43 +136,155 @@ void TransferRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
|
|||
serializer(amount, "amount");
|
||||
}
|
||||
|
||||
void GetTransferRequest::serialize(CryptoNote::ISerializer& serializer) {
|
||||
bool r = serializer(transferId, "transfer_id");
|
||||
|
||||
if (!r) {
|
||||
throw RequestSerializationError();
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
void TransactionRpcInfo::serialize(CryptoNote::ISerializer& serializer) {
|
||||
serializer(state, "state");
|
||||
serializer(transactionHash, "transactionHash");
|
||||
serializer(blockIndex, "blockIndex");
|
||||
serializer(timestamp, "timestamp");
|
||||
serializer(isBase, "isBase");
|
||||
serializer(unlockTime, "unlockTime");
|
||||
serializer(amount, "amount");
|
||||
serializer(blockHeight, "block_height");
|
||||
serializer(unlockTime, "unlock_time");
|
||||
serializer(fee, "fee");
|
||||
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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,122 +18,166 @@
|
|||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "Serialization/ISerializer.h"
|
||||
|
||||
namespace PaymentService {
|
||||
|
||||
const uint32_t DEFAULT_ANONYMITY_LEVEL = 6;
|
||||
|
||||
class RequestSerializationError: public std::exception {
|
||||
public:
|
||||
virtual const char* what() const throw() override { return "Request error"; }
|
||||
};
|
||||
|
||||
struct TransferDestination {
|
||||
uint64_t amount;
|
||||
std::string address;
|
||||
struct Reset {
|
||||
struct Request {
|
||||
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);
|
||||
};
|
||||
|
||||
struct SendTransactionRequest {
|
||||
SendTransactionRequest() : unlockTime(0) {}
|
||||
struct GetTransactionHashes {
|
||||
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;
|
||||
uint64_t fee;
|
||||
uint64_t mixin;
|
||||
uint64_t unlockTime;
|
||||
std::string paymentId;
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
struct Response {
|
||||
std::vector<TransactionHashesInBlockRpcInfo> items;
|
||||
|
||||
struct SendTransactionResponse {
|
||||
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);
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
};
|
||||
|
||||
struct TransferRpcInfo {
|
||||
|
@ -145,80 +189,157 @@ struct TransferRpcInfo {
|
|||
};
|
||||
|
||||
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;
|
||||
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::string extra;
|
||||
std::string paymentId;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
struct GetTransactionResponse {
|
||||
bool found;
|
||||
TransactionRpcInfo transactionInfo;
|
||||
struct GetTransaction {
|
||||
struct Request {
|
||||
std::string transactionHash;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
struct Response {
|
||||
TransactionRpcInfo transaction;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
};
|
||||
|
||||
struct ListTransactionsRequest {
|
||||
uint32_t startingTransactionId;
|
||||
uint32_t maxTransactionCount;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
struct ListTransactionsResponse {
|
||||
struct TransactionsInBlockRpcInfo {
|
||||
std::string blockHash;
|
||||
std::vector<TransactionRpcInfo> transactions;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
struct GetTransferRequest {
|
||||
uint64_t transferId;
|
||||
struct GetTransactions {
|
||||
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 {
|
||||
bool found;
|
||||
TransferRpcInfo transferInfo;
|
||||
struct GetUnconfirmedTransactionHashes {
|
||||
struct Request {
|
||||
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 {
|
||||
std::vector<std::string> payments;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
struct PaymentDetails
|
||||
{
|
||||
std::string txHash;
|
||||
struct WalletRpcOrder {
|
||||
std::string address;
|
||||
uint64_t amount;
|
||||
uint64_t blockHeight;
|
||||
uint64_t unlockTime;
|
||||
|
||||
void serialize(CryptoNote::ISerializer& serializer);
|
||||
};
|
||||
|
||||
struct PaymentsById {
|
||||
std::string id;
|
||||
std::vector<PaymentDetails> payments;
|
||||
struct SendTransaction {
|
||||
struct Request {
|
||||
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 {
|
||||
std::vector<PaymentsById> payments;
|
||||
struct CreateDelayedTransaction {
|
||||
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
|
||||
|
|
|
@ -17,10 +17,11 @@
|
|||
|
||||
#include "PaymentServiceJsonRpcServer.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "PaymentServiceJsonRpcMessages.h"
|
||||
#include "WalletService.h"
|
||||
|
||||
#include "Common/JsonValue.h"
|
||||
#include "Serialization/JsonInputValueSerializer.h"
|
||||
#include "Serialization/JsonOutputStreamSerializer.h"
|
||||
|
||||
|
@ -31,299 +32,157 @@ PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys
|
|||
, service(service)
|
||||
, 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) {
|
||||
try {
|
||||
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();
|
||||
|
||||
CryptoNote::JsonOutputStreamSerializer outputSerializer;
|
||||
|
||||
if (method == "send_transaction") {
|
||||
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;
|
||||
auto it = handlers.find(method);
|
||||
if (it == handlers.end()) {
|
||||
logger(Logging::WARNING) << "Requested method not found: " << method;
|
||||
makeMethodNotFoundResponse(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
fillJsonResponse(outputSerializer.getValue(), resp);
|
||||
logger(Logging::DEBUGGING) << method << " request came";
|
||||
|
||||
} catch (RequestSerializationError&) {
|
||||
logger(Logging::WARNING) << "Wrong request came";
|
||||
makeGenericErrorReponse(resp, "Invalid Request", -32600);
|
||||
Common::JsonValue params(Common::JsonValue::OBJECT);
|
||||
if (req.contains("params")) {
|
||||
params = req("params");
|
||||
}
|
||||
|
||||
it->second(params, resp);
|
||||
} catch (std::exception& e) {
|
||||
logger(Logging::WARNING) << "Error occurred while processing JsonRpc request: " << 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "Common/JsonValue.h"
|
||||
#include "JsonRpcServer/JsonRpcServer.h"
|
||||
#include "PaymentServiceJsonRpcMessages.h"
|
||||
#include "Serialization/JsonInputValueSerializer.h"
|
||||
#include "Serialization/JsonOutputStreamSerializer.h"
|
||||
|
||||
namespace PaymentService {
|
||||
|
||||
|
@ -34,6 +40,56 @@ protected:
|
|||
private:
|
||||
WalletService& service;
|
||||
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
|
@ -35,12 +35,6 @@
|
|||
|
||||
namespace PaymentService {
|
||||
|
||||
struct SendTransactionRequest;
|
||||
struct SendTransactionResponse;
|
||||
struct TransferDestination;
|
||||
struct TransactionRpcInfo;
|
||||
struct TransferRpcInfo;
|
||||
|
||||
struct WalletConfiguration {
|
||||
std::string walletFile;
|
||||
std::string walletPassword;
|
||||
|
@ -48,71 +42,74 @@ struct WalletConfiguration {
|
|||
|
||||
void generateNewWallet(const CryptoNote::Currency ¤cy, const WalletConfiguration &conf, Logging::ILogger &logger, System::Dispatcher& dispatcher);
|
||||
|
||||
struct TransactionsInBlockInfoFilter;
|
||||
|
||||
class WalletService {
|
||||
public:
|
||||
typedef std::map<std::string, std::vector<PaymentDetails> > IncomingPayments;
|
||||
|
||||
explicit WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, const WalletConfiguration& conf, Logging::ILogger& logger);
|
||||
WalletService(const CryptoNote::Currency& currency, System::Dispatcher& sys, CryptoNote::INode& node, CryptoNote::IWallet& wallet, const WalletConfiguration& conf, Logging::ILogger& logger);
|
||||
virtual ~WalletService();
|
||||
|
||||
void init();
|
||||
void saveWallet();
|
||||
|
||||
std::error_code sendTransaction(const SendTransactionRequest& req, SendTransactionResponse& resp);
|
||||
std::error_code getIncomingPayments(const std::vector<std::string>& payments, IncomingPayments& result);
|
||||
std::error_code getAddress(size_t index, std::string& address);
|
||||
std::error_code getAddressCount(size_t& count);
|
||||
std::error_code resetWallet();
|
||||
std::error_code replaceWithNewWallet(const std::string& viewSecretKey);
|
||||
std::error_code createAddress(const std::string& spendSecretKeyText, 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 getActualBalance(const std::string& address, uint64_t& actualBalance);
|
||||
std::error_code getPendingBalance(const std::string& address, uint64_t& pendingBalance);
|
||||
std::error_code getActualBalance(uint64_t& actualBalance);
|
||||
std::error_code getPendingBalance(uint64_t& pendingBalance);
|
||||
std::error_code getTransactionsCount(uint64_t& txCount);
|
||||
std::error_code getTransfersCount(uint64_t& trCount);
|
||||
std::error_code getTransactionByTransferId(size_t transfer, size_t& transaction);
|
||||
std::error_code getTransaction(size_t txId, bool& found, TransactionRpcInfo& rpcInfo);
|
||||
std::error_code listTransactions(size_t startingTxId, uint32_t maxTxCount, std::vector<TransactionRpcInfo>& txsRpcInfo);
|
||||
std::error_code getTransfer(size_t txId, bool& found, TransferRpcInfo& rpcInfo);
|
||||
std::error_code getSpendkeys(const std::string& address, std::string& publicSpendKeyText, std::string& secretSpendKeyText);
|
||||
std::error_code getBalance(const std::string& address, uint64_t& availableBalance, uint64_t& lockedAmount);
|
||||
std::error_code getBalance(uint64_t& availableBalance, uint64_t& lockedAmount);
|
||||
std::error_code getBlockHashes(uint32_t firstBlockIndex, uint32_t blockCount, std::vector<std::string>& blockHashes);
|
||||
std::error_code getViewKey(std::string& viewSecretKey);
|
||||
std::error_code getTransactionHashes(const std::vector<std::string>& addresses, const std::string& blockHash,
|
||||
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionHashesInBlockRpcInfo>& transactionHashes);
|
||||
std::error_code getTransactionHashes(const std::vector<std::string>& addresses, uint32_t firstBlockIndex,
|
||||
uint32_t blockCount, const std::string& paymentId, std::vector<TransactionHashesInBlockRpcInfo>& transactionHashes);
|
||||
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:
|
||||
void refresh();
|
||||
void reset();
|
||||
|
||||
void loadWallet();
|
||||
void loadPaymentsCacheAndTransferIndices();
|
||||
void insertTransaction(size_t id, const Crypto::Hash& paymentIdBin, bool confirmed);
|
||||
void loadTransactionIdIndex();
|
||||
|
||||
void fillTransactionRpcInfo(size_t txId, const CryptoNote::WalletTransaction& tx, TransactionRpcInfo& rpcInfo);
|
||||
void makeOrders(const std::vector<TransferDestination>& destinations, std::vector<CryptoNote::WalletOrder>& transfers);
|
||||
void replaceWithNewWallet(const Crypto::SecretKey& viewSecretKey);
|
||||
|
||||
struct PaymentItem {
|
||||
std::string paymentId;
|
||||
size_t transactionId;
|
||||
bool confirmed;
|
||||
};
|
||||
std::vector<CryptoNote::TransactionsInBlockInfo> getTransactions(const Crypto::Hash& blockHash, size_t blockCount) const;
|
||||
std::vector<CryptoNote::TransactionsInBlockInfo> getTransactions(uint32_t firstBlockIndex, size_t blockCount) const;
|
||||
|
||||
typedef boost::multi_index::hashed_unique<BOOST_MULTI_INDEX_MEMBER(PaymentItem, size_t, transactionId)> TxIdIndex;
|
||||
typedef boost::multi_index::hashed_non_unique<BOOST_MULTI_INDEX_MEMBER(PaymentItem, std::string, paymentId)> PaymentIndex;
|
||||
typedef boost::multi_index::multi_index_container<
|
||||
PaymentItem,
|
||||
boost::multi_index::indexed_by<
|
||||
TxIdIndex,
|
||||
PaymentIndex
|
||||
>
|
||||
> PaymentsContainer;
|
||||
std::vector<TransactionHashesInBlockRpcInfo> getRpcTransactionHashes(const Crypto::Hash& blockHash, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
|
||||
std::vector<TransactionHashesInBlockRpcInfo> getRpcTransactionHashes(uint32_t firstBlockIndex, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
|
||||
|
||||
std::unique_ptr<CryptoNote::IWallet > wallet;
|
||||
CryptoNote::INode* node;
|
||||
std::vector<TransactionsInBlockRpcInfo> getRpcTransactions(const Crypto::Hash& blockHash, size_t blockCount, const TransactionsInBlockInfoFilter& filter) const;
|
||||
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;
|
||||
bool inited;
|
||||
Logging::LoggerRef logger;
|
||||
std::vector<size_t> transfersIndices;
|
||||
System::Dispatcher& dispatcher;
|
||||
System::Event readyEvent;
|
||||
System::ContextGroup refreshContext;
|
||||
|
||||
PaymentsContainer paymentsCache;
|
||||
PaymentsContainer::nth_index<0>::type& txIdIndex;
|
||||
PaymentsContainer::nth_index<1>::type& paymentIdIndex;
|
||||
std::map<std::string, size_t> transactionIdIndex;
|
||||
};
|
||||
|
||||
} //namespace PaymentService
|
||||
|
|
26
src/PaymentGate/WalletServiceErrorCategory.cpp
Normal file
26
src/PaymentGate/WalletServiceErrorCategory.cpp
Normal 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;
|
||||
|
||||
}
|
||||
}
|
75
src/PaymentGate/WalletServiceErrorCategory.h
Normal file
75
src/PaymentGate/WalletServiceErrorCategory.h
Normal 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 {};
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
#include "CryptoNoteCore/Core.h"
|
||||
#include "CryptoNoteProtocol/CryptoNoteProtocolHandler.h"
|
||||
#include "P2p/NetNode.h"
|
||||
#include "PaymentGate/WalletFactory.h"
|
||||
#include <System/Context.h>
|
||||
|
||||
#ifdef ERROR
|
||||
|
@ -86,8 +87,8 @@ bool PaymentGateService::init(int argc, char** argv) {
|
|||
|
||||
WalletConfiguration PaymentGateService::getWalletConfig() const {
|
||||
return WalletConfiguration{
|
||||
config.gateConfiguration.walletFile,
|
||||
config.gateConfiguration.walletPassword
|
||||
config.gateConfiguration.containerFile,
|
||||
config.gateConfiguration.containerPassword
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -132,6 +133,16 @@ void PaymentGateService::stop() {
|
|||
}
|
||||
|
||||
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";
|
||||
|
||||
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) {
|
||||
PaymentService::WalletConfiguration walletConfiguration{
|
||||
config.gateConfiguration.walletFile,
|
||||
config.gateConfiguration.walletPassword
|
||||
config.gateConfiguration.containerFile,
|
||||
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);
|
||||
try {
|
||||
service->init();
|
||||
|
@ -221,13 +234,10 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency,
|
|||
|
||||
if (config.gateConfiguration.printAddresses) {
|
||||
// print addresses and exit
|
||||
size_t addressCount = 0;
|
||||
service->getAddressCount(addressCount);
|
||||
for (size_t i = 0; i < addressCount; ++i) {
|
||||
std::string address;
|
||||
if (service->getAddress(i, address) == std::error_code()) {
|
||||
std::cout << "Address: " << address << std::endl;
|
||||
}
|
||||
std::vector<std::string> addresses;
|
||||
service->getAddresses(addresses);
|
||||
for (const auto& address: addresses) {
|
||||
std::cout << "Address: " << address << std::endl;
|
||||
}
|
||||
} else {
|
||||
PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger);
|
||||
|
@ -236,7 +246,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency,
|
|||
try {
|
||||
service->saveWallet();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace po = boost::program_options;
|
|||
namespace PaymentService {
|
||||
|
||||
Configuration::Configuration() {
|
||||
generateNewWallet = false;
|
||||
generateNewContainer = false;
|
||||
daemonize = false;
|
||||
registerService = false;
|
||||
unregisterService = false;
|
||||
|
@ -44,9 +44,9 @@ void Configuration::initOptions(boost::program_options::options_description& des
|
|||
desc.add_options()
|
||||
("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")
|
||||
("wallet-file,w", po::value<std::string>(), "wallet file")
|
||||
("wallet-password,p", po::value<std::string>(), "wallet password")
|
||||
("generate-wallet,g", "generate new wallet file and exit")
|
||||
("container-file,w", po::value<std::string>(), "container file")
|
||||
("container-password,p", po::value<std::string>(), "container password")
|
||||
("generate-container,g", "generate new container file with one wallet and exit")
|
||||
("daemon,d", "run as daemon in Unix or as service in Windows")
|
||||
#ifdef _WIN32
|
||||
("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>();
|
||||
}
|
||||
|
||||
if (options.count("wallet-file") != 0) {
|
||||
walletFile = options["wallet-file"].as<std::string>();
|
||||
if (options.count("container-file") != 0) {
|
||||
containerFile = options["container-file"].as<std::string>();
|
||||
}
|
||||
|
||||
if (options.count("wallet-password") != 0) {
|
||||
walletPassword = options["wallet-password"].as<std::string>();
|
||||
if (options.count("container-password") != 0) {
|
||||
containerPassword = options["container-password"].as<std::string>();
|
||||
}
|
||||
|
||||
if (options.count("generate-wallet") != 0) {
|
||||
generateNewWallet = true;
|
||||
if (options.count("generate-container") != 0) {
|
||||
generateNewContainer = true;
|
||||
}
|
||||
|
||||
if (options.count("address") != 0) {
|
||||
|
@ -120,8 +120,8 @@ void Configuration::init(const boost::program_options::variables_map& options) {
|
|||
}
|
||||
|
||||
if (!registerService && !unregisterService) {
|
||||
if (walletFile.empty() || walletPassword.empty()) {
|
||||
throw ConfigurationError("Both wallet-file and wallet-password parameters are required");
|
||||
if (containerFile.empty() || containerPassword.empty()) {
|
||||
throw ConfigurationError("Both container-file and container-password parameters are required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,12 +39,12 @@ struct Configuration {
|
|||
std::string bindAddress;
|
||||
uint16_t bindPort;
|
||||
|
||||
std::string walletFile;
|
||||
std::string walletPassword;
|
||||
std::string containerFile;
|
||||
std::string containerPassword;
|
||||
std::string logFile;
|
||||
std::string serverRoot;
|
||||
|
||||
bool generateNewWallet;
|
||||
bool generateNewContainer;
|
||||
bool daemonize;
|
||||
bool registerService;
|
||||
bool unregisterService;
|
||||
|
|
|
@ -305,7 +305,7 @@ int main(int argc, char** argv) {
|
|||
|
||||
const auto& config = pg.getConfig();
|
||||
|
||||
if (config.gateConfiguration.generateNewWallet) {
|
||||
if (config.gateConfiguration.generateNewContainer) {
|
||||
System::Dispatcher d;
|
||||
generateNewWallet(pg.getCurrency(), pg.getWalletConfig(), pg.getLogger(), d);
|
||||
return 0;
|
||||
|
|
|
@ -277,6 +277,7 @@ struct COMMAND_RPC_GET_INFO {
|
|||
uint64_t incoming_connections_count;
|
||||
uint64_t white_peerlist_size;
|
||||
uint64_t grey_peerlist_size;
|
||||
uint32_t last_known_block_index;
|
||||
|
||||
void serialize(ISerializer &s) {
|
||||
KV_MEMBER(status)
|
||||
|
@ -289,6 +290,7 @@ struct COMMAND_RPC_GET_INFO {
|
|||
KV_MEMBER(incoming_connections_count)
|
||||
KV_MEMBER(white_peerlist_size)
|
||||
KV_MEMBER(grey_peerlist_size)
|
||||
KV_MEMBER(last_known_block_index)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#include "CryptoNoteCore/IBlock.h"
|
||||
#include "CryptoNoteCore/Miner.h"
|
||||
#include "CryptoNoteCore/TransactionExtra.h"
|
||||
|
||||
#include "CryptoNoteProtocol/ICryptoNoteProtocolQuery.h"
|
||||
|
||||
#include "P2p/NetNode.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
|
||||
{ "/getblocks.bin", binMethod<COMMAND_RPC_GET_BLOCKS_FAST>(&RpcServer::on_get_blocks) },
|
||||
{ "/queryblocks.bin", binMethod<COMMAND_RPC_QUERY_BLOCKS>(&RpcServer::on_query_blocks) },
|
||||
{ "/queryblockslite.bin", binMethod<COMMAND_RPC_QUERY_BLOCKS_LITE>(&RpcServer::on_query_blocks_lite) },
|
||||
{ "/get_o_indexes.bin", binMethod<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(&RpcServer::on_get_indexes) },
|
||||
{ "/getrandom_outs.bin", binMethod<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS>(&RpcServer::on_get_random_outs) },
|
||||
{ "/get_pool_changes.bin", binMethod<COMMAND_RPC_GET_POOL_CHANGES>(&RpcServer::onGetPoolChanges) },
|
||||
{ "/get_pool_changes_lite.bin", binMethod<COMMAND_RPC_GET_POOL_CHANGES_LITE>(&RpcServer::onGetPoolChangesLite) },
|
||||
{ "/getblocks.bin", { binMethod<COMMAND_RPC_GET_BLOCKS_FAST>(&RpcServer::on_get_blocks), false } },
|
||||
{ "/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), false } },
|
||||
{ "/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), false } },
|
||||
{ "/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), false } },
|
||||
|
||||
// json handlers
|
||||
{ "/getinfo", jsonMethod<COMMAND_RPC_GET_INFO>(&RpcServer::on_get_info) },
|
||||
{ "/getheight", jsonMethod<COMMAND_RPC_GET_HEIGHT>(&RpcServer::on_get_height) },
|
||||
{ "/gettransactions", jsonMethod<COMMAND_RPC_GET_TRANSACTIONS>(&RpcServer::on_get_transactions)},
|
||||
{ "/sendrawtransaction", jsonMethod<COMMAND_RPC_SEND_RAW_TX>(&RpcServer::on_send_raw_tx) },
|
||||
{ "/start_mining", jsonMethod<COMMAND_RPC_START_MINING>(&RpcServer::on_start_mining) },
|
||||
{ "/stop_mining", jsonMethod<COMMAND_RPC_STOP_MINING>(&RpcServer::on_stop_mining) },
|
||||
{ "/stop_daemon", jsonMethod<COMMAND_RPC_STOP_DAEMON>(&RpcServer::on_stop_daemon) },
|
||||
{ "/getinfo", { jsonMethod<COMMAND_RPC_GET_INFO>(&RpcServer::on_get_info), true } },
|
||||
{ "/getheight", { jsonMethod<COMMAND_RPC_GET_HEIGHT>(&RpcServer::on_get_height), true } },
|
||||
{ "/gettransactions", { jsonMethod<COMMAND_RPC_GET_TRANSACTIONS>(&RpcServer::on_get_transactions), false } },
|
||||
{ "/sendrawtransaction", { jsonMethod<COMMAND_RPC_SEND_RAW_TX>(&RpcServer::on_send_raw_tx), false } },
|
||||
{ "/start_mining", { jsonMethod<COMMAND_RPC_START_MINING>(&RpcServer::on_start_mining), false } },
|
||||
{ "/stop_mining", { jsonMethod<COMMAND_RPC_STOP_MINING>(&RpcServer::on_stop_mining), false } },
|
||||
{ "/stop_daemon", { jsonMethod<COMMAND_RPC_STOP_DAEMON>(&RpcServer::on_stop_daemon), true } },
|
||||
|
||||
// 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) :
|
||||
HttpServer(dispatcher, log), logger(log, "RpcServer"), m_core(c), m_p2p(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), m_protocolQuery(protocolQuery) {
|
||||
}
|
||||
|
||||
void RpcServer::processRequest(const HttpRequest& request, HttpResponse& response) {
|
||||
auto url = request.getUrl();
|
||||
|
||||
|
||||
auto it = s_handlers.find(url);
|
||||
if (it == s_handlers.end()) {
|
||||
response.setStatus(HttpResponse::STATUS_404);
|
||||
return;
|
||||
}
|
||||
|
||||
if (url != "/json_rpc" && !checkCoreReady()) {
|
||||
if (!it->second.allowBusyCore && !isCoreReady()) {
|
||||
response.setStatus(HttpResponse::STATUS_500);
|
||||
response.setBody("Core is busy");
|
||||
return;
|
||||
}
|
||||
|
||||
it->second(this, request, response);
|
||||
it->second.handler(this, request, response);
|
||||
}
|
||||
|
||||
bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse& response) {
|
||||
|
@ -138,15 +141,15 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
|
|||
jsonRequest.parseRequest(request.getBody());
|
||||
jsonResponse.setId(jsonRequest.getId()); // copy id
|
||||
|
||||
static std::unordered_map<std::string, JsonMemberMethod> jsonRpcHandlers = {
|
||||
{ "getblockcount", makeMemberMethod(&RpcServer::on_getblockcount) },
|
||||
{ "on_getblockhash", makeMemberMethod(&RpcServer::on_getblockhash) },
|
||||
{ "getblocktemplate", makeMemberMethod(&RpcServer::on_getblocktemplate) },
|
||||
{ "getcurrencyid", makeMemberMethod(&RpcServer::on_get_currency_id) },
|
||||
{ "submitblock", makeMemberMethod(&RpcServer::on_submitblock) },
|
||||
{ "getlastblockheader", makeMemberMethod(&RpcServer::on_get_last_block_header) },
|
||||
{ "getblockheaderbyhash", makeMemberMethod(&RpcServer::on_get_block_header_by_hash) },
|
||||
{ "getblockheaderbyheight", makeMemberMethod(&RpcServer::on_get_block_header_by_height) }
|
||||
static std::unordered_map<std::string, RpcServer::RpcHandler<JsonMemberMethod>> jsonRpcHandlers = {
|
||||
{ "getblockcount", { makeMemberMethod(&RpcServer::on_getblockcount), true } },
|
||||
{ "on_getblockhash", { makeMemberMethod(&RpcServer::on_getblockhash), false } },
|
||||
{ "getblocktemplate", { makeMemberMethod(&RpcServer::on_getblocktemplate), false } },
|
||||
{ "getcurrencyid", { makeMemberMethod(&RpcServer::on_get_currency_id), true } },
|
||||
{ "submitblock", { makeMemberMethod(&RpcServer::on_submitblock), false } },
|
||||
{ "getlastblockheader", { makeMemberMethod(&RpcServer::on_get_last_block_header), false } },
|
||||
{ "getblockheaderbyhash", { makeMemberMethod(&RpcServer::on_get_block_header_by_hash), false } },
|
||||
{ "getblockheaderbyheight", { makeMemberMethod(&RpcServer::on_get_block_header_by_height), false } }
|
||||
};
|
||||
|
||||
auto it = jsonRpcHandlers.find(jsonRequest.getMethod());
|
||||
|
@ -154,11 +157,11 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
|
|||
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");
|
||||
}
|
||||
|
||||
it->second(this, jsonRequest, jsonResponse);
|
||||
it->second.handler(this, jsonRequest, jsonResponse);
|
||||
|
||||
} catch (const JsonRpcError& err) {
|
||||
jsonResponse.setError(err);
|
||||
|
@ -171,10 +174,8 @@ bool RpcServer::processJsonRpcRequest(const HttpRequest& request, HttpResponse&
|
|||
return true;
|
||||
}
|
||||
|
||||
#define CHECK_CORE_READY()
|
||||
|
||||
bool RpcServer::checkCoreReady() {
|
||||
return m_core.is_ready() && m_p2p.get_payload_object().isSynchronized();
|
||||
bool RpcServer::isCoreReady() {
|
||||
return m_core.currency().isTestnet() || 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) {
|
||||
CHECK_CORE_READY();
|
||||
|
||||
uint32_t startHeight;
|
||||
uint32_t currentHeight;
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
|
||||
uint32_t startHeight;
|
||||
uint32_t currentHeight;
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
|
||||
std::vector<uint32_t> outputIndexes;
|
||||
if (!m_core.get_tx_outputs_gindexs(req.txid, outputIndexes)) {
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
res.status = "Failed";
|
||||
if (!m_core.get_random_outs_for_amounts(req, res)) {
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
|
||||
rsp.status = CORE_RPC_STATUS_OK;
|
||||
std::vector<CryptoNote::Transaction> addedTransactions;
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
|
||||
rsp.status = CORE_RPC_STATUS_OK;
|
||||
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.white_peerlist_size = m_p2p.getPeerlistManager().get_white_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;
|
||||
return true;
|
||||
}
|
||||
|
||||
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.status = CORE_RPC_STATUS_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
for (const auto& tx_hex_str : req.txs_hashes) {
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
|
||||
BinaryArray 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) {
|
||||
CHECK_CORE_READY();
|
||||
AccountPublicAddress adr;
|
||||
if (!m_core.currency().parseAccountAddressString(req.miner_address, adr)) {
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
if (!m_core.get_miner().stop()) {
|
||||
res.status = "Failed, mining not stopped";
|
||||
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) {
|
||||
CHECK_CORE_READY();
|
||||
if (m_core.currency().isTestnet()) {
|
||||
m_p2p.sendStopSignal();
|
||||
res.status = CORE_RPC_STATUS_OK;
|
||||
|
|
|
@ -27,21 +27,28 @@ namespace CryptoNote {
|
|||
|
||||
class core;
|
||||
class NodeServer;
|
||||
class ICryptoNoteProtocolQuery;
|
||||
|
||||
class RpcServer : public HttpServer {
|
||||
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;
|
||||
|
||||
private:
|
||||
|
||||
template <class Handler>
|
||||
struct RpcHandler {
|
||||
const Handler handler;
|
||||
const bool allowBusyCore;
|
||||
};
|
||||
|
||||
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;
|
||||
bool processJsonRpcRequest(const HttpRequest& request, HttpResponse& response);
|
||||
bool checkCoreReady();
|
||||
bool isCoreReady();
|
||||
|
||||
// binary handlers
|
||||
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;
|
||||
core& m_core;
|
||||
NodeServer& m_p2p;
|
||||
const ICryptoNoteProtocolQuery& m_protocolQuery;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,10 @@ inline std::vector<uint8_t> stringToVector(const std::string& s) {
|
|||
namespace CryptoNote {
|
||||
|
||||
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() {
|
||||
|
@ -57,7 +60,6 @@ void BlockchainSynchronizer::addConsumer(IBlockchainConsumer* consumer) {
|
|||
}
|
||||
|
||||
m_consumers.insert(std::make_pair(consumer, std::make_shared<SynchronizationState>(m_genesisBlockHash)));
|
||||
shouldSyncConsumersPool = true;
|
||||
}
|
||||
|
||||
bool BlockchainSynchronizer::removeConsumer(IBlockchainConsumer* consumer) {
|
||||
|
@ -70,23 +72,81 @@ bool BlockchainSynchronizer::removeConsumer(IBlockchainConsumer* consumer) {
|
|||
return m_consumers.erase(consumer) > 0;
|
||||
}
|
||||
|
||||
IStreamSerializable* BlockchainSynchronizer::getConsumerState(IBlockchainConsumer* consumer) {
|
||||
assert(consumer != nullptr);
|
||||
|
||||
if (!(checkIfStopped() && checkIfShouldStop())) {
|
||||
throw std::runtime_error("Can't get consumer state, because BlockchainSynchronizer isn't stopped");
|
||||
}
|
||||
|
||||
IStreamSerializable* BlockchainSynchronizer::getConsumerState(IBlockchainConsumer* consumer) const {
|
||||
std::unique_lock<std::mutex> lk(m_consumersMutex);
|
||||
|
||||
auto it = m_consumers.find(consumer);
|
||||
if (it == m_consumers.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return it->second.get();
|
||||
return getConsumerSynchronizationState(consumer);
|
||||
}
|
||||
|
||||
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) {
|
||||
os.write(reinterpret_cast<const char*>(&m_genesisBlockHash), sizeof(m_genesisBlockHash));
|
||||
|
@ -103,16 +163,14 @@ void BlockchainSynchronizer::load(std::istream& in) {
|
|||
//--------------------------- FSM ------------------------------------
|
||||
|
||||
bool BlockchainSynchronizer::setFutureState(State s) {
|
||||
return setFutureStateIf(s, std::bind(
|
||||
[](State futureState, State s) -> bool {
|
||||
return s > futureState;
|
||||
}, std::ref(m_futureState), s));
|
||||
return setFutureStateIf(s, [this, s] { return s > m_futureState; });
|
||||
}
|
||||
|
||||
bool BlockchainSynchronizer::setFutureStateIf(State s, std::function<bool(void)>&& pred) {
|
||||
std::unique_lock<std::mutex> lk(m_stateMutex);
|
||||
if (pred()) {
|
||||
m_futureState = s;
|
||||
m_hasWork.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -129,10 +187,37 @@ void BlockchainSynchronizer::actualizeFutureState() {
|
|||
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;
|
||||
switch (m_futureState) {
|
||||
case State::stopped:
|
||||
m_futureState = State::stopped;
|
||||
break;
|
||||
case State::blockchainSync:
|
||||
m_futureState = State::poolSync;
|
||||
|
@ -145,21 +230,22 @@ void BlockchainSynchronizer::actualizeFutureState() {
|
|||
startPoolSync();
|
||||
break;
|
||||
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();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockchainSynchronizer::checkIfShouldStop() {
|
||||
bool BlockchainSynchronizer::checkIfShouldStop() const {
|
||||
std::unique_lock<std::mutex> lk(m_stateMutex);
|
||||
return m_futureState == State::stopped;
|
||||
}
|
||||
|
||||
bool BlockchainSynchronizer::checkIfStopped() {
|
||||
bool BlockchainSynchronizer::checkIfStopped() const {
|
||||
std::unique_lock<std::mutex> lk(m_stateMutex);
|
||||
return m_currentState == State::stopped;
|
||||
}
|
||||
|
@ -178,16 +264,11 @@ void BlockchainSynchronizer::start() {
|
|||
throw std::runtime_error("Can't start, because BlockchainSynchronizer has no consumers");
|
||||
}
|
||||
|
||||
if (!setFutureStateIf(State::blockchainSync, std::bind(
|
||||
[](State currentState, State futureState) -> bool {
|
||||
return currentState == State::stopped && futureState == State::stopped;
|
||||
}, std::ref(m_currentState), std::ref(m_futureState)))) {
|
||||
if (!setFutureStateIf(State::blockchainSync, [this] { return m_currentState == State::stopped && m_futureState == State::stopped; })) {
|
||||
throw std::runtime_error("BlockchainSynchronizer already started");
|
||||
}
|
||||
|
||||
shouldSyncConsumersPool = true;
|
||||
|
||||
workingThread.reset(new std::thread([this] {this->workingProcedure(); }));
|
||||
workingThread.reset(new std::thread([this] { workingProcedure(); }));
|
||||
}
|
||||
|
||||
void BlockchainSynchronizer::stop() {
|
||||
|
@ -201,7 +282,11 @@ void BlockchainSynchronizer::stop() {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -210,52 +295,27 @@ void BlockchainSynchronizer::poolChanged() {
|
|||
}
|
||||
//--------------------------- FSM END ------------------------------------
|
||||
|
||||
BlockchainSynchronizer::GetPoolRequest BlockchainSynchronizer::getUnionPoolHistory() {
|
||||
GetPoolRequest request;
|
||||
std::unordered_set<Hash> unionHistory;
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(m_consumersMutex);
|
||||
for (auto& consumer : m_consumers) {
|
||||
std::vector<Hash> consumerKnownIds;
|
||||
consumer.first->getKnownPoolTxIds(consumerKnownIds);
|
||||
for (auto& txId : consumerKnownIds) {
|
||||
unionHistory.insert(txId);
|
||||
void BlockchainSynchronizer::getPoolUnionAndIntersection(std::unordered_set<Crypto::Hash>& poolUnion, std::unordered_set<Crypto::Hash>& poolIntersection) const {
|
||||
std::unique_lock<std::mutex> lk(m_consumersMutex);
|
||||
|
||||
auto itConsumers = m_consumers.begin();
|
||||
poolUnion = itConsumers->first->getKnownPoolTxIds();
|
||||
poolIntersection = itConsumers->first->getKnownPoolTxIds();
|
||||
++itConsumers;
|
||||
|
||||
for (; itConsumers != m_consumers.end(); ++itConsumers) {
|
||||
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() {
|
||||
|
@ -290,42 +350,34 @@ void BlockchainSynchronizer::startBlockchainSync() {
|
|||
|
||||
try {
|
||||
if (!req.knownBlocks.empty()) {
|
||||
asyncOperationCompleted = std::promise<std::error_code>();
|
||||
asyncOperationWaitFuture = asyncOperationCompleted.get_future();
|
||||
auto queryBlocksCompleted = std::promise<std::error_code>();
|
||||
auto queryBlocksWaitFuture = queryBlocksCompleted.get_future();
|
||||
|
||||
m_node.queryBlocks
|
||||
(std::move(req.knownBlocks), req.syncStart.timestamp, response.newBlocks, response.startHeight,
|
||||
std::bind(&BlockchainSynchronizer::onGetBlocksCompleted, this, std::placeholders::_1));
|
||||
m_node.queryBlocks(
|
||||
std::move(req.knownBlocks),
|
||||
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) {
|
||||
setFutureStateIf(State::idle, std::bind(
|
||||
[](State futureState) -> bool {
|
||||
return futureState != State::stopped;
|
||||
}, std::ref(m_futureState)));
|
||||
setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec);
|
||||
} else {
|
||||
processBlocks(response);
|
||||
}
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
setFutureStateIf(State::idle, std::bind(
|
||||
[](State futureState) -> bool {
|
||||
return futureState != State::stopped;
|
||||
}, std::ref(m_futureState)));
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
std::make_error_code(std::errc::invalid_argument));
|
||||
} catch (std::exception&) {
|
||||
setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
|
||||
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) {
|
||||
BlockchainInterval interval;
|
||||
interval.startHeight = response.startHeight;
|
||||
|
@ -335,6 +387,7 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
|
|||
if (checkIfShouldStop()) {
|
||||
break;
|
||||
}
|
||||
|
||||
CompleteBlock completeBlock;
|
||||
completeBlock.blockHash = block.blockHash;
|
||||
interval.blocks.push_back(completeBlock.blockHash);
|
||||
|
@ -344,19 +397,11 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
|
|||
|
||||
try {
|
||||
for (const auto& txShortInfo : block.txsShortInfo) {
|
||||
completeBlock.transactions.push_back(createTransactionPrefix(txShortInfo.txPrefix,
|
||||
reinterpret_cast<const Hash&>(txShortInfo.txId)));
|
||||
completeBlock.transactions.push_back(createTransactionPrefix(txShortInfo.txPrefix, reinterpret_cast<const Hash&>(txShortInfo.txId)));
|
||||
}
|
||||
} catch (std::exception&) {
|
||||
setFutureStateIf(State::idle, std::bind(
|
||||
[](State futureState) -> bool {
|
||||
return futureState != State::stopped;
|
||||
}, std::ref(m_futureState)));
|
||||
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
std::make_error_code(std::errc::invalid_argument));
|
||||
|
||||
setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -373,22 +418,18 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
|
|||
|
||||
switch (result) {
|
||||
case UpdateConsumersResult::errorOccurred:
|
||||
if (setFutureStateIf(State::idle, std::bind(
|
||||
[](State futureState) -> bool {
|
||||
return futureState != State::stopped;
|
||||
}, std::ref(m_futureState)))) {
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
std::make_error_code(std::errc::invalid_argument));
|
||||
if (setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; })) {
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, std::make_error_code(std::errc::invalid_argument));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case UpdateConsumersResult::nothingChanged:
|
||||
if (m_node.getLastKnownBlockHeight() != m_node.getLastLocalBlockHeight()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
case UpdateConsumersResult::addedNewBlocks:
|
||||
setFutureState(State::blockchainSync);
|
||||
m_observerManager.notify(
|
||||
|
@ -404,12 +445,11 @@ void BlockchainSynchronizer::processBlocks(GetBlocksResponse& response) {
|
|||
}
|
||||
|
||||
if (checkIfShouldStop()) { //Sic!
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
std::make_error_code(std::errc::interrupted));
|
||||
m_observerManager.notify(&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) {
|
||||
bool smthChanged = false;
|
||||
|
||||
|
@ -424,15 +464,9 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons
|
|||
if (result.hasNewBlocks) {
|
||||
uint32_t startOffset = result.newBlockHeight - interval.startHeight;
|
||||
// update consumer
|
||||
if (kv.first->onNewBlocks(
|
||||
blocks.data() + startOffset,
|
||||
result.newBlockHeight,
|
||||
static_cast<uint32_t>(blocks.size()) - startOffset)) {
|
||||
if (kv.first->onNewBlocks(blocks.data() + startOffset, result.newBlockHeight, static_cast<uint32_t>(blocks.size()) - startOffset)) {
|
||||
// update state if consumer succeeded
|
||||
kv.second->addBlocks(
|
||||
interval.blocks.data() + startOffset,
|
||||
result.newBlockHeight,
|
||||
static_cast<uint32_t>(interval.blocks.size()) - startOffset);
|
||||
kv.second->addBlocks(interval.blocks.data() + startOffset, result.newBlockHeight, static_cast<uint32_t>(interval.blocks.size()) - startOffset);
|
||||
smthChanged = true;
|
||||
} else {
|
||||
return UpdateConsumersResult::errorOccurred;
|
||||
|
@ -444,57 +478,41 @@ BlockchainSynchronizer::UpdateConsumersResult BlockchainSynchronizer::updateCons
|
|||
}
|
||||
|
||||
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;
|
||||
GetPoolRequest unionRequest = getUnionPoolHistory();
|
||||
|
||||
asyncOperationCompleted = std::promise<std::error_code>();
|
||||
asyncOperationWaitFuture = asyncOperationCompleted.get_future();
|
||||
|
||||
unionResponse.isLastKnownBlockActual = false;
|
||||
|
||||
m_node.getPoolSymmetricDifference(std::move(unionRequest.knownTxIds), std::move(unionRequest.lastKnownBlock), unionResponse.isLastKnownBlockActual,
|
||||
unionResponse.newTxs, unionResponse.deletedTxIds, std::bind(&BlockchainSynchronizer::onGetPoolChanges, this, std::placeholders::_1));
|
||||
|
||||
std::error_code ec = asyncOperationWaitFuture.get();
|
||||
std::error_code ec = getPoolSymmetricDifferenceSync(std::move(unionRequest), unionResponse);
|
||||
|
||||
if (ec) {
|
||||
setFutureStateIf(State::idle, std::bind(
|
||||
[](State futureState) -> bool {
|
||||
return futureState != State::stopped;
|
||||
}, std::ref(m_futureState)));
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
ec);
|
||||
setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec);
|
||||
} else { //get union ok
|
||||
if (!unionResponse.isLastKnownBlockActual) { //bc outdated
|
||||
setFutureState(State::blockchainSync);
|
||||
} else {
|
||||
if (!shouldSyncConsumersPool) { //usual case, start pool processing
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
processPoolTxs(unionResponse));
|
||||
} else {// first launch, we should sync consumers' pools, so let's ask for intersection
|
||||
if (unionPoolHistory == intersectedPoolHistory) { //usual case, start pool processing
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, processPoolTxs(unionResponse));
|
||||
} else {
|
||||
GetPoolRequest intersectionRequest;
|
||||
intersectionRequest.knownTxIds.assign(intersectedPoolHistory.begin(), intersectedPoolHistory.end());
|
||||
intersectionRequest.lastKnownBlock = lastBlockId;
|
||||
|
||||
GetPoolResponse intersectionResponse;
|
||||
GetPoolRequest intersectionRequest = getIntersectedPoolHistory();
|
||||
|
||||
asyncOperationCompleted = std::promise<std::error_code>();
|
||||
asyncOperationWaitFuture = asyncOperationCompleted.get_future();
|
||||
|
||||
intersectionResponse.isLastKnownBlockActual = false;
|
||||
|
||||
m_node.getPoolSymmetricDifference(std::move(intersectionRequest.knownTxIds), std::move(intersectionRequest.lastKnownBlock), intersectionResponse.isLastKnownBlockActual,
|
||||
intersectionResponse.newTxs, intersectionResponse.deletedTxIds, std::bind(&BlockchainSynchronizer::onGetPoolChanges, this, std::placeholders::_1));
|
||||
|
||||
std::error_code ec2 = asyncOperationWaitFuture.get();
|
||||
std::error_code ec2 = getPoolSymmetricDifferenceSync(std::move(intersectionRequest), intersectionResponse);
|
||||
|
||||
if (ec2) {
|
||||
setFutureStateIf(State::idle, std::bind(
|
||||
[](State futureState) -> bool {
|
||||
return futureState != State::stopped;
|
||||
}, std::ref(m_futureState)));
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
ec2);
|
||||
setFutureStateIf(State::idle, [this] { return m_futureState != State::stopped; });
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec2);
|
||||
} else { //get intersection ok
|
||||
if (!intersectionResponse.isLastKnownBlockActual) { //bc outdated
|
||||
setFutureState(State::blockchainSync);
|
||||
|
@ -503,13 +521,7 @@ void BlockchainSynchronizer::startPoolSync() {
|
|||
std::error_code ec3 = processPoolTxs(intersectionResponse);
|
||||
|
||||
//notify about error, or success
|
||||
m_observerManager.notify(
|
||||
&IBlockchainSynchronizerObserver::synchronizationCompleted,
|
||||
ec3);
|
||||
|
||||
if (!ec3) {
|
||||
shouldSyncConsumersPool = false;
|
||||
}
|
||||
m_observerManager.notify(&IBlockchainSynchronizerObserver::synchronizationCompleted, ec3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -517,9 +529,22 @@ void BlockchainSynchronizer::startPoolSync() {
|
|||
}
|
||||
}
|
||||
|
||||
void BlockchainSynchronizer::onGetPoolChanges(std::error_code ec) {
|
||||
decltype(asyncOperationCompleted) detachedPromise = std::move(asyncOperationCompleted);
|
||||
detachedPromise.set_value(ec);
|
||||
std::error_code BlockchainSynchronizer::getPoolSymmetricDifferenceSync(GetPoolRequest&& request, GetPoolResponse& response) {
|
||||
auto promise = std::promise<std::error_code>();
|
||||
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) {
|
||||
|
@ -541,4 +566,20 @@ std::error_code BlockchainSynchronizer::processPoolTxs(GetPoolResponse& response
|
|||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,7 +41,11 @@ public:
|
|||
// IBlockchainSynchronizer
|
||||
virtual void addConsumer(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 stop() override;
|
||||
|
@ -51,6 +55,7 @@ public:
|
|||
virtual void load(std::istream& in) override;
|
||||
|
||||
// INodeObserver
|
||||
virtual void localBlockchainUpdated(uint32_t height) override;
|
||||
virtual void lastKnownBlockHeightUpdated(uint32_t height) override;
|
||||
virtual void poolChanged() override;
|
||||
|
||||
|
@ -81,7 +86,6 @@ private:
|
|||
Crypto::Hash lastKnownBlock;
|
||||
};
|
||||
|
||||
|
||||
enum class State { //prioritized finite states
|
||||
idle = 0, //DO
|
||||
poolSync = 1, //NOT
|
||||
|
@ -99,25 +103,26 @@ private:
|
|||
void startPoolSync();
|
||||
void startBlockchainSync();
|
||||
|
||||
void onGetBlocksCompleted(std::error_code ec);
|
||||
void processBlocks(GetBlocksResponse& response);
|
||||
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 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
|
||||
bool setFutureState(State s);
|
||||
bool setFutureStateIf(State s, std::function<bool(void)>&& pred);
|
||||
|
||||
void actualizeFutureState();
|
||||
bool checkIfShouldStop();
|
||||
bool checkIfStopped();
|
||||
bool checkIfShouldStop() const;
|
||||
bool checkIfStopped() const;
|
||||
|
||||
void workingProcedure();
|
||||
|
||||
GetBlocksRequest getCommonHistory();
|
||||
GetPoolRequest getUnionPoolHistory();
|
||||
GetPoolRequest getIntersectedPoolHistory();
|
||||
void getPoolUnionAndIntersection(std::unordered_set<Crypto::Hash>& poolUnion, std::unordered_set<Crypto::Hash>& poolIntersection) const;
|
||||
SynchronizationState* getConsumerSynchronizationState(IBlockchainConsumer* consumer) const ;
|
||||
|
||||
typedef std::map<IBlockchainConsumer*, std::shared_ptr<SynchronizationState>> ConsumersMap;
|
||||
|
||||
|
@ -130,14 +135,12 @@ private:
|
|||
State m_currentState;
|
||||
State m_futureState;
|
||||
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;
|
||||
std::promise<std::error_code> asyncOperationCompleted;
|
||||
|
||||
std::mutex m_consumersMutex;
|
||||
std::mutex m_stateMutex;
|
||||
|
||||
bool shouldSyncConsumersPool;
|
||||
mutable std::mutex m_consumersMutex;
|
||||
mutable std::mutex m_stateMutex;
|
||||
std::condition_variable m_hasWork;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <system_error>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "crypto/crypto.h"
|
||||
#include "CryptoNoteCore/CryptoNoteBasic.h"
|
||||
|
@ -37,16 +39,29 @@ public:
|
|||
virtual void synchronizationCompleted(std::error_code result) {}
|
||||
};
|
||||
|
||||
class IBlockchainConsumer {
|
||||
class IBlockchainConsumerObserver;
|
||||
|
||||
class IBlockchainConsumer : public IObservable<IBlockchainConsumerObserver> {
|
||||
public:
|
||||
virtual ~IBlockchainConsumer() {}
|
||||
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 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 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 :
|
||||
public IObservable<IBlockchainSynchronizerObserver>,
|
||||
|
@ -54,7 +69,11 @@ class IBlockchainSynchronizer :
|
|||
public:
|
||||
virtual void addConsumer(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 stop() = 0;
|
||||
|
|
|
@ -103,6 +103,10 @@ uint32_t SynchronizationState::getHeight() const {
|
|||
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) {
|
||||
StdOutputStream stream(os);
|
||||
CryptoNote::BinaryOutputStreamSerializer s(stream);
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
void detach(uint32_t height);
|
||||
void addBlocks(const Crypto::Hash* blockHashes, uint32_t height, uint32_t count);
|
||||
uint32_t getHeight() const;
|
||||
const std::vector<Crypto::Hash>& getKnownBlockHashes() const;
|
||||
|
||||
// IStreamSerializable
|
||||
virtual void save(std::ostream& os) override;
|
||||
|
|
|
@ -16,13 +16,15 @@
|
|||
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "TransfersConsumer.h"
|
||||
#include "CommonTypes.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include "CommonTypes.h"
|
||||
#include "Common/BlockingQueue.h"
|
||||
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
|
||||
#include "CryptoNoteCore/TransactionApi.h"
|
||||
|
||||
#include "IWalletLegacy.h"
|
||||
#include "IWallet.h"
|
||||
#include "INode.h"
|
||||
#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 {
|
||||
|
@ -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() {
|
||||
SynchronizationStart start;
|
||||
|
||||
|
@ -153,6 +179,8 @@ SynchronizationStart TransfersConsumer::getSyncStart() {
|
|||
}
|
||||
|
||||
void TransfersConsumer::onBlockchainDetach(uint32_t height) {
|
||||
m_observerManager.notify(&IBlockchainConsumerObserver::onBlockchainDetach, this, height);
|
||||
|
||||
for (const auto& kv : m_subscriptions) {
|
||||
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) {
|
||||
m_observerManager.notify(&IBlockchainConsumerObserver::onBlocksAdded, this, blockHashes);
|
||||
|
||||
// sort by block height and transaction index in block
|
||||
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);
|
||||
});
|
||||
|
||||
for (const auto& tx : preprocessedTransactions) {
|
||||
processingError = processTransaction(tx.blockInfo, *tx.tx, tx);
|
||||
if (processingError) {
|
||||
break;
|
||||
}
|
||||
processTransaction(tx.blockInfo, *tx.tx, tx);
|
||||
}
|
||||
}
|
||||
|
||||
if (processingError) {
|
||||
} else {
|
||||
forEachSubscription([&](TransfersSubscription& sub) {
|
||||
sub.onError(processingError, startHeight);
|
||||
});
|
||||
|
||||
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) {
|
||||
TransactionBlockInfo unconfirmedBlockInfo;
|
||||
unconfirmedBlockInfo.timestamp = 0;
|
||||
unconfirmedBlockInfo.height = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT;
|
||||
unconfirmedBlockInfo.height = WALLET_UNCONFIRMED_TRANSACTION_HEIGHT;
|
||||
|
||||
std::error_code processingError;
|
||||
for (auto& cryptonoteTransaction : addedTransactions) {
|
||||
m_poolTxs.emplace(cryptonoteTransaction->getTransactionHash());
|
||||
processingError = processTransaction(unconfirmedBlockInfo, *cryptonoteTransaction.get());
|
||||
if (processingError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto& sub : m_subscriptions) {
|
||||
sub.second->onError(processingError, WALLET_UNCONFIRMED_TRANSACTION_HEIGHT);
|
||||
}
|
||||
|
||||
if (processingError) {
|
||||
for (auto& sub : m_subscriptions) {
|
||||
sub.second->onError(processingError, WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT);
|
||||
return processingError;
|
||||
}
|
||||
|
||||
return processingError;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionDeleteEnd, this, deletedTxHash);
|
||||
}
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
void TransfersConsumer::getKnownPoolTxIds(std::vector<Hash>& ids) {
|
||||
ids.clear();
|
||||
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());
|
||||
const std::unordered_set<Crypto::Hash>& TransfersConsumer::getKnownPoolTxIds() const {
|
||||
return m_poolTxs;
|
||||
}
|
||||
|
||||
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(
|
||||
const AccountKeys& account,
|
||||
|
@ -353,7 +391,7 @@ std::error_code createTransfers(
|
|||
info.type = outType;
|
||||
info.transactionPublicKey = txPubKey;
|
||||
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];
|
||||
|
||||
if (outType == TransactionTypes::OutputType::Key) {
|
||||
|
@ -399,7 +437,7 @@ std::error_code TransfersConsumer::preprocessOutputs(const TransactionBlockInfo&
|
|||
|
||||
std::error_code errorCode;
|
||||
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);
|
||||
if (errorCode) {
|
||||
return errorCode;
|
||||
|
@ -427,50 +465,52 @@ std::error_code TransfersConsumer::processTransaction(const TransactionBlockInfo
|
|||
return ec;
|
||||
}
|
||||
|
||||
return processTransaction(blockInfo, tx, info);
|
||||
processTransaction(blockInfo, tx, info);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
|
||||
std::error_code TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) {
|
||||
std::error_code errorCode;
|
||||
void TransfersConsumer::processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info) {
|
||||
std::vector<TransactionOutputInformationIn> emptyOutputs;
|
||||
std::vector<ITransfersContainer*> transactionContainers;
|
||||
bool someContainerUpdated = false;
|
||||
for (auto& kv : m_subscriptions) {
|
||||
auto it = info.outputs.find(kv.first);
|
||||
auto& subscriptionOutputs = (it == info.outputs.end()) ? emptyOutputs : it->second;
|
||||
errorCode = processOutputs(blockInfo, *kv.second, tx, subscriptionOutputs, info.globalIdxs);
|
||||
if (errorCode) {
|
||||
return errorCode;
|
||||
|
||||
bool containerContainsTx;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
if (someContainerUpdated) {
|
||||
m_observerManager.notify(&IBlockchainConsumerObserver::onTransactionUpdated, this, tx.getTransactionHash(), transactionContainers);
|
||||
}
|
||||
|
||||
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::promise<std::error_code> prom;
|
||||
|
|
|
@ -32,8 +32,7 @@ namespace CryptoNote {
|
|||
|
||||
class INode;
|
||||
|
||||
class TransfersConsumer : public IBlockchainConsumer {
|
||||
|
||||
class TransfersConsumer: public IObservableImpl<IBlockchainConsumerObserver, IBlockchainConsumer> {
|
||||
public:
|
||||
|
||||
TransfersConsumer(const CryptoNote::Currency& currency, INode& node, const Crypto::SecretKey& viewSecret);
|
||||
|
@ -43,13 +42,18 @@ public:
|
|||
bool removeSubscription(const AccountPublicAddress& address);
|
||||
ITransfersSubscription* getSubscription(const AccountPublicAddress& acc);
|
||||
void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions);
|
||||
|
||||
void initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions);
|
||||
|
||||
// IBlockchainConsumer
|
||||
virtual SynchronizationStart getSyncStart() override;
|
||||
virtual void onBlockchainDetach(uint32_t height) 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 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:
|
||||
|
||||
|
@ -67,9 +71,9 @@ private:
|
|||
|
||||
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, const PreprocessInfo& info);
|
||||
std::error_code processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx,
|
||||
const std::vector<TransactionOutputInformationIn>& outputs, const std::vector<uint32_t>& globalIdxs);
|
||||
void processTransaction(const TransactionBlockInfo& blockInfo, const ITransactionReader& tx, const PreprocessInfo& info);
|
||||
void processOutputs(const TransactionBlockInfo& blockInfo, TransfersSubscription& sub, const ITransactionReader& tx,
|
||||
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);
|
||||
|
||||
|
@ -80,6 +84,7 @@ private:
|
|||
// map { spend public key -> subscription }
|
||||
std::unordered_map<Crypto::PublicKey, std::unique_ptr<TransfersSubscription>> m_subscriptions;
|
||||
std::unordered_set<Crypto::PublicKey> m_spendKeys;
|
||||
std::unordered_set<Crypto::Hash> m_poolTxs;
|
||||
|
||||
INode& m_node;
|
||||
const CryptoNote::Currency& m_currency;
|
||||
|
|
|
@ -256,11 +256,11 @@ bool TransfersContainer::addTransactionOutputs(const TransactionBlockInfo& block
|
|||
assert(result.second);
|
||||
} else {
|
||||
if (info.type == TransactionTypes::OutputType::Multisignature) {
|
||||
SpentOutputDescriptor descriptor(transfer);
|
||||
if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 ||
|
||||
m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) {
|
||||
throw std::runtime_error("Transfer already exists");
|
||||
}
|
||||
SpentOutputDescriptor descriptor(transfer);
|
||||
if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 ||
|
||||
m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) {
|
||||
throw std::runtime_error("Transfer already exists");
|
||||
}
|
||||
}
|
||||
|
||||
auto result = m_availableTransfers.emplace(std::move(info));
|
||||
|
@ -341,10 +341,10 @@ bool TransfersContainer::addTransactionInputs(const TransactionBlockInfo& block,
|
|||
outputDescriptorIndex.erase(availableOutputIt);
|
||||
|
||||
inputsAdded = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(inputType == TransactionTypes::InputType::Generating);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputsAdded;
|
||||
|
@ -361,7 +361,7 @@ bool TransfersContainer::deleteUnconfirmedTransaction(const Hash& transactionHas
|
|||
} else {
|
||||
deleteTransactionTransfers(it->transactionHash);
|
||||
m_transactions.erase(it);
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,12 +401,12 @@ bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& bl
|
|||
transfer.globalOutputIndex = globalIndices[transfer.outputInTransaction];
|
||||
|
||||
if (transfer.type == TransactionTypes::OutputType::Multisignature) {
|
||||
SpentOutputDescriptor descriptor(transfer);
|
||||
if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 ||
|
||||
m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) {
|
||||
// This exception breaks TransfersContainer consistency
|
||||
throw std::runtime_error("Transfer already exists");
|
||||
}
|
||||
SpentOutputDescriptor descriptor(transfer);
|
||||
if (m_availableTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0 ||
|
||||
m_spentTransfers.get<SpentOutputDescriptorIndex>().count(descriptor) > 0) {
|
||||
// This exception breaks TransfersContainer consistency
|
||||
throw std::runtime_error("Transfer already exists");
|
||||
}
|
||||
}
|
||||
|
||||
auto result = m_availableTransfers.emplace(std::move(transfer));
|
||||
|
@ -417,7 +417,7 @@ bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& bl
|
|||
|
||||
if (transfer.type == TransactionTypes::OutputType::Key) {
|
||||
updateTransfersVisibility(transfer.keyImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& spendingTransactionIndex = m_spentTransfers.get<SpendingTransactionIndex>();
|
||||
|
@ -430,7 +430,7 @@ bool TransfersContainer::markTransactionConfirmed(const TransactionBlockInfo& bl
|
|||
spendingTransactionIndex.replace(transferIt, transfer);
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -472,7 +472,7 @@ void TransfersContainer::deleteTransactionTransfers(const Hash& transactionHash)
|
|||
updateTransfersVisibility(keyImage);
|
||||
} else {
|
||||
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);
|
||||
auto it = m_transactions.find(transactionHash);
|
||||
if (it == m_transactions.end()) {
|
||||
|
@ -652,32 +652,35 @@ bool TransfersContainer::getTransactionInformation(const Hash& transactionHash,
|
|||
|
||||
info = *it;
|
||||
|
||||
int64_t amountOut = 0;
|
||||
if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {
|
||||
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);
|
||||
}
|
||||
if (amountOut != nullptr) {
|
||||
*amountOut = 0;
|
||||
|
||||
auto spentOutputsRange = m_spentTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
|
||||
for (auto it = spentOutputsRange.first; it != spentOutputsRange.second; ++it) {
|
||||
amountOut += static_cast<int64_t>(it->amount);
|
||||
if (info.blockHeight == WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT) {
|
||||
auto unconfirmedOutputsRange = m_unconfirmedTransfers.get<ContainingTransactionIndex>().equal_range(transactionHash);
|
||||
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;
|
||||
auto rangeInputs = m_spentTransfers.get<SpendingTransactionIndex>().equal_range(transactionHash);
|
||||
for (auto it = rangeInputs.first; it != rangeInputs.second; ++it) {
|
||||
amountIn += static_cast<int64_t>(it->amount);
|
||||
if (amountIn != nullptr) {
|
||||
*amountIn = 0;
|
||||
auto rangeInputs = m_spentTransfers.get<SpendingTransactionIndex>().equal_range(transactionHash);
|
||||
for (auto it = rangeInputs.first; it != rangeInputs.second; ++it) {
|
||||
*amountIn += it->amount;
|
||||
}
|
||||
}
|
||||
|
||||
txBalance = amountOut - amountIn;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -165,7 +165,8 @@ public:
|
|||
virtual size_t transactionsCount() const override;
|
||||
virtual uint64_t balance(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;
|
||||
//only type flags are feasible for this function
|
||||
virtual std::vector<TransactionOutputInformation> getTransactionInputs(const Crypto::Hash& transactionHash, uint32_t flags) const override;
|
||||
|
|
|
@ -52,12 +52,14 @@ const AccountKeys& TransfersSubscription::getKeys() const {
|
|||
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) {
|
||||
bool added = transfers.addTransaction(blockInfo, tx, transfersList);
|
||||
if (added) {
|
||||
m_observerManager.notify(&ITransfersObserver::onTransactionUpdated, this, tx.getTransactionHash());
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
AccountPublicAddress TransfersSubscription::getAddress() {
|
||||
|
@ -69,8 +71,9 @@ ITransfersContainer& TransfersSubscription::getContainer() {
|
|||
}
|
||||
|
||||
void TransfersSubscription::deleteUnconfirmedTransaction(const Hash& transactionHash) {
|
||||
transfers.deleteUnconfirmedTransaction(transactionHash);
|
||||
m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash);
|
||||
if (transfers.deleteUnconfirmedTransaction(transactionHash)) {
|
||||
m_observerManager.notify(&ITransfersObserver::onTransactionDeleted, this, transactionHash);
|
||||
}
|
||||
}
|
||||
|
||||
void TransfersSubscription::markTransactionConfirmed(const TransactionBlockInfo& block, const Hash& transactionHash,
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
void onError(const std::error_code& ec, uint32_t height);
|
||||
bool advanceHeight(uint32_t height);
|
||||
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);
|
||||
|
||||
void deleteUnconfirmedTransaction(const Crypto::Hash& transactionHash);
|
||||
|
|
|
@ -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) {
|
||||
auto it = m_consumers.find(acc.keys.address.viewPublicKey);
|
||||
|
||||
if (it == m_consumers.end()) {
|
||||
std::unique_ptr<TransfersConsumer> consumer(
|
||||
new TransfersConsumer(m_currency, m_node, acc.keys.viewSecretKey));
|
||||
|
||||
m_sync.addConsumer(consumer.get());
|
||||
consumer->addObserver(this);
|
||||
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)) {
|
||||
m_sync.removeConsumer(it->second.get());
|
||||
m_consumers.erase(it);
|
||||
|
||||
m_subscribers.erase(acc.viewPublicKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -75,7 +85,68 @@ void TransfersSyncronizer::getSubscriptions(std::vector<AccountPublicAddress>& s
|
|||
|
||||
ITransfersSubscription* TransfersSyncronizer::getSubscription(const AccountPublicAddress& acc) {
|
||||
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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Common/ObserverManager.h"
|
||||
#include "ITransfersSynchronizer.h"
|
||||
#include "IBlockchainSynchronizer.h"
|
||||
#include "TypeHelpers.h"
|
||||
|
@ -34,31 +35,50 @@ namespace CryptoNote {
|
|||
class TransfersConsumer;
|
||||
class INode;
|
||||
|
||||
class TransfersSyncronizer : public ITransfersSynchronizer {
|
||||
class TransfersSyncronizer : public ITransfersSynchronizer, public IBlockchainConsumerObserver {
|
||||
public:
|
||||
|
||||
TransfersSyncronizer(const CryptoNote::Currency& currency, IBlockchainSynchronizer& sync, INode& node);
|
||||
~TransfersSyncronizer();
|
||||
virtual ~TransfersSyncronizer();
|
||||
|
||||
void initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions);
|
||||
|
||||
// ITransfersSynchronizer
|
||||
virtual ITransfersSubscription& addSubscription(const AccountSubscription& acc) override;
|
||||
virtual bool removeSubscription(const AccountPublicAddress& acc) override;
|
||||
virtual void getSubscriptions(std::vector<AccountPublicAddress>& subscriptions) 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
|
||||
virtual void save(std::ostream& os) override;
|
||||
virtual void load(std::istream& in) override;
|
||||
|
||||
private:
|
||||
|
||||
// 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;
|
||||
IBlockchainSynchronizer& m_sync;
|
||||
INode& m_node;
|
||||
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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -46,7 +46,11 @@ enum WalletErrorCodes {
|
|||
INDEX_OUT_OF_RANGE,
|
||||
ADDRESS_ALREADY_EXISTS,
|
||||
TRACKING_MODE,
|
||||
WRONG_PARAMETERS
|
||||
WRONG_PARAMETERS,
|
||||
OBJECT_NOT_FOUND,
|
||||
WALLET_NOT_FOUND,
|
||||
CHANGE_ADDRESS_REQUIRED,
|
||||
CHANGE_ADDRESS_NOT_FOUND
|
||||
};
|
||||
|
||||
// custom category:
|
||||
|
@ -85,6 +89,10 @@ public:
|
|||
case ADDRESS_ALREADY_EXISTS: return "Address already exists";
|
||||
case TRACKING_MODE: return "The wallet is in tracking mode";
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
@ -100,3 +108,10 @@ private:
|
|||
inline std::error_code make_error_code(CryptoNote::error::WalletErrorCodes e) {
|
||||
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
|
@ -20,6 +20,7 @@
|
|||
#include "IWallet.h"
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "IFusionManager.h"
|
||||
#include "WalletIndices.h"
|
||||
|
@ -34,6 +35,7 @@ namespace CryptoNote {
|
|||
class WalletGreen : public IWallet,
|
||||
ITransfersObserver,
|
||||
IBlockchainSynchronizerObserver,
|
||||
ITransfersSynchronizerObserver,
|
||||
IFusionManager {
|
||||
public:
|
||||
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 std::string getAddress(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 std::string createAddress() 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 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 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 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 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 WalletTransactionWithTransfers getTransaction(const Crypto::Hash& transactionHash) const override;
|
||||
virtual std::vector<TransactionsInBlockInfo> getTransactions(const Crypto::Hash& blockHash, size_t count) const override;
|
||||
virtual std::vector<TransactionsInBlockInfo> getTransactions(uint32_t blockIndex, size_t count) const 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 makeTransaction(const TransactionParameters& sendingTransaction) override;
|
||||
virtual void commitTransaction(size_t) override;
|
||||
virtual void rollbackUncommitedTransaction(size_t) override;
|
||||
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
virtual WalletEvent getEvent() override;
|
||||
|
@ -110,9 +121,26 @@ protected:
|
|||
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 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;
|
||||
void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash);
|
||||
|
@ -123,8 +151,21 @@ protected:
|
|||
void onSynchronizationProgressUpdated(uint32_t processedBlockCount, uint32_t totalBlockCount);
|
||||
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;
|
||||
WalletOuts pickWallet(const std::string& address);
|
||||
std::vector<WalletOuts> pickWallets(const std::vector<std::string>& addresses);
|
||||
|
||||
void updateBalance(CryptoNote::ITransfersContainer* container);
|
||||
void unlockBalances(uint32_t height);
|
||||
|
@ -134,24 +175,31 @@ protected:
|
|||
const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const;
|
||||
|
||||
CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const;
|
||||
void 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);
|
||||
std::string addWallet(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey, uint64_t creationTimestamp);
|
||||
AccountKeys makeAccountKeys(const WalletRecord& wallet) const;
|
||||
size_t getTransactionId(const Crypto::Hash& transactionHash) const;
|
||||
void pushEvent(const WalletEvent& event);
|
||||
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,
|
||||
uint64_t fee,
|
||||
uint64_t mixIn,
|
||||
const std::string& extra,
|
||||
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,
|
||||
uint64_t mixIn,
|
||||
|
@ -175,23 +223,38 @@ protected:
|
|||
std::unique_ptr<CryptoNote::ITransaction> makeTransaction(const std::vector<ReceiverAmounts>& decomposedOutputs,
|
||||
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 insertOutgoingTransactionAndPushEvent(const Crypto::Hash& transactionHash,
|
||||
int64_t totalAmount, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp);
|
||||
size_t insertBlockchainTransaction(const TransactionInformation& info, int64_t txBalance);
|
||||
size_t insertOutgoingTransactionAndPushEvent(const Crypto::Hash& transactionHash, uint64_t fee, const BinaryArray& extra, uint64_t unlockTimestamp);
|
||||
void updateTransactionStateAndPushEvent(size_t transactionId, WalletTransactionState state);
|
||||
void updateWalletTransactionInfoAndPushEvent(size_t transactionId, const CryptoNote::TransactionInformation& info, bool transfersUpdated);
|
||||
void insertIncomingTransfer(size_t txId, const std::string& address, int64_t amount);
|
||||
void pushBackOutgoingTransfers(size_t txId, const std::vector<WalletTransfer> &destinations);
|
||||
bool updateWalletTransactionInfo(size_t transactionId, const CryptoNote::TransactionInformation& info, int64_t totalAmount);
|
||||
bool updateTransactionTransfers(size_t transactionId, const std::vector<ContainerAmounts>& containerAmountsList,
|
||||
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 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 unsafeSave(std::ostream& destination, bool saveDetails, bool saveCache);
|
||||
|
||||
std::vector<OutputToTransfer> pickRandomFusionInputs(uint64_t threshold, size_t minInputCount, size_t maxInputCount);
|
||||
ReceiverAmounts decomposeFusionOutputs(uint64_t inputsAmount);
|
||||
|
||||
enum class WalletState {
|
||||
INITIALIZED,
|
||||
NOT_INITIALIZED
|
||||
|
@ -205,21 +268,33 @@ protected:
|
|||
|
||||
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;
|
||||
const Currency& m_currency;
|
||||
INode& m_node;
|
||||
bool m_stopped = false;
|
||||
bool m_stopped;
|
||||
|
||||
WalletsContainer m_walletsContainer;
|
||||
SpentOutputs m_spentOutputs;
|
||||
UnlockTransactionJobs m_unlockTransactionsJob;
|
||||
WalletTransactions m_transactions;
|
||||
WalletTransfers m_transfers; //sorted
|
||||
TransactionChanges m_change;
|
||||
mutable std::unordered_map<size_t, bool> m_fusionTxsCache; // txIndex -> isFusion
|
||||
UncommitedTransactions m_uncommitedTransactions;
|
||||
|
||||
bool m_blockchainSynchronizerStarted;
|
||||
BlockchainSynchronizer m_blockchainSynchronizer;
|
||||
TransfersSyncronizer m_synchronizer;
|
||||
|
||||
|
@ -227,18 +302,20 @@ protected:
|
|||
std::queue<WalletEvent> m_events;
|
||||
mutable System::Event m_readyEvent;
|
||||
|
||||
WalletState m_state = WalletState::NOT_INITIALIZED;
|
||||
WalletState m_state;
|
||||
|
||||
std::string m_password;
|
||||
|
||||
Crypto::PublicKey m_viewPublicKey;
|
||||
Crypto::SecretKey m_viewSecretKey;
|
||||
|
||||
uint64_t m_actualBalance = 0;
|
||||
uint64_t m_pendingBalance = 0;
|
||||
uint64_t m_actualBalance;
|
||||
uint64_t m_pendingBalance;
|
||||
|
||||
uint64_t m_upperTransactionSizeLimit;
|
||||
uint32_t m_transactionSoftLockTime;
|
||||
|
||||
BlockHashesContainer m_blockchain;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ITransfersContainer.h"
|
||||
|
@ -43,14 +44,6 @@ struct WalletRecord {
|
|||
time_t creationTimestamp;
|
||||
};
|
||||
|
||||
struct SpentOutput {
|
||||
uint64_t amount;
|
||||
Crypto::Hash transactionHash;
|
||||
uint32_t outputInTransaction;
|
||||
const WalletRecord* wallet;
|
||||
Crypto::Hash spendingTransactionHash;
|
||||
};
|
||||
|
||||
struct RandomAccessIndex {};
|
||||
struct KeysIndex {};
|
||||
struct TransfersContainerIndex {};
|
||||
|
@ -61,6 +54,7 @@ struct BlockHeightIndex {};
|
|||
|
||||
struct TransactionHashIndex {};
|
||||
struct TransactionIndex {};
|
||||
struct BlockHashIndex {};
|
||||
|
||||
typedef boost::multi_index_container <
|
||||
WalletRecord,
|
||||
|
@ -73,27 +67,6 @@ typedef boost::multi_index_container <
|
|||
>
|
||||
> 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 {
|
||||
uint32_t blockHeight;
|
||||
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_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)
|
||||
>
|
||||
>
|
||||
|
@ -118,13 +91,28 @@ typedef boost::multi_index_container <
|
|||
boost::multi_index::random_access < boost::multi_index::tag <RandomAccessIndex> >,
|
||||
boost::multi_index::hashed_unique < boost::multi_index::tag <TransactionIndex>,
|
||||
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;
|
||||
|
||||
typedef std::unordered_map<Crypto::Hash, uint64_t> TransactionChanges;
|
||||
|
||||
typedef std::pair<size_t, CryptoNote::WalletTransfer> TransactionTransferPair;
|
||||
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;
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "Common/StdInputStream.h"
|
||||
#include "Common/StdOutputStream.h"
|
||||
#include "CryptoNoteCore/CryptoNoteSerialization.h"
|
||||
#include "CryptoNoteCore/CryptoNoteTools.h"
|
||||
|
||||
#include "Serialization/BinaryOutputStreamSerializer.h"
|
||||
#include "Serialization/BinaryInputStreamSerializer.h"
|
||||
|
@ -49,7 +50,7 @@ struct WalletRecordDto {
|
|||
};
|
||||
|
||||
//DO NOT CHANGE IT
|
||||
struct SpentOutputDto {
|
||||
struct ObsoleteSpentOutputDto {
|
||||
uint64_t amount;
|
||||
Hash transactionHash;
|
||||
uint32_t outputInTransaction;
|
||||
|
@ -58,7 +59,7 @@ struct SpentOutputDto {
|
|||
};
|
||||
|
||||
//DO NOT CHANGE IT
|
||||
struct ChangeDto {
|
||||
struct ObsoleteChangeDto {
|
||||
Hash txHash;
|
||||
uint64_t amount;
|
||||
};
|
||||
|
@ -121,7 +122,7 @@ void serialize(WalletRecordDto& value, CryptoNote::ISerializer& serializer) {
|
|||
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.transactionHash, "transaction_hash");
|
||||
serializer(value.outputInTransaction, "output_in_transaction");
|
||||
|
@ -129,7 +130,7 @@ void serialize(SpentOutputDto& value, CryptoNote::ISerializer& serializer) {
|
|||
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.amount, "amount");
|
||||
}
|
||||
|
@ -273,7 +274,7 @@ CryptoNote::WalletTransfer convert(const CryptoNote::WalletLegacyTransfer& tr) {
|
|||
|
||||
namespace CryptoNote {
|
||||
|
||||
const uint32_t WalletSerializer::SERIALIZATION_VERSION = 3;
|
||||
const uint32_t WalletSerializer::SERIALIZATION_VERSION = 5;
|
||||
|
||||
void CryptoContext::incIv() {
|
||||
uint64_t * i = reinterpret_cast<uint64_t *>(&iv.data[0]);
|
||||
|
@ -288,12 +289,11 @@ WalletSerializer::WalletSerializer(
|
|||
uint64_t& pendingBalance,
|
||||
WalletsContainer& walletsContainer,
|
||||
TransfersSyncronizer& synchronizer,
|
||||
SpentOutputs& spentOutputs,
|
||||
UnlockTransactionJobs& unlockTransactions,
|
||||
TransactionChanges& change,
|
||||
WalletTransactions& transactions,
|
||||
WalletTransfers& transfers,
|
||||
uint32_t transactionSoftLockTime
|
||||
uint32_t transactionSoftLockTime,
|
||||
UncommitedTransactions& uncommitedTransactions
|
||||
) :
|
||||
m_transfersObserver(transfersObserver),
|
||||
m_viewPublicKey(viewPublicKey),
|
||||
|
@ -302,12 +302,11 @@ WalletSerializer::WalletSerializer(
|
|||
m_pendingBalance(pendingBalance),
|
||||
m_walletsContainer(walletsContainer),
|
||||
m_synchronizer(synchronizer),
|
||||
m_spentOutputs(spentOutputs),
|
||||
m_unlockTransactions(unlockTransactions),
|
||||
m_change(change),
|
||||
m_transactions(transactions),
|
||||
m_transfers(transfers),
|
||||
m_transactionSoftLockTime(transactionSoftLockTime)
|
||||
m_transactionSoftLockTime(transactionSoftLockTime),
|
||||
uncommitedTransactions(uncommitedTransactions)
|
||||
{ }
|
||||
|
||||
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) {
|
||||
saveBalances(destination, saveCache, cryptoContext);
|
||||
saveTransfersSynchronizer(destination, cryptoContext);
|
||||
saveSpentOutputs(destination, cryptoContext);
|
||||
saveUnlockTransactionsJobs(destination, cryptoContext);
|
||||
saveChange(destination, cryptoContext);
|
||||
saveUncommitedTransactions(destination, cryptoContext);
|
||||
}
|
||||
|
||||
s.endObject();
|
||||
|
@ -426,29 +424,6 @@ void WalletSerializer::saveTransfersSynchronizer(Common::IOutputStream& destinat
|
|||
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) {
|
||||
auto& index = m_unlockTransactions.get<TransactionHashIndex>();
|
||||
auto& wallets = m_walletsContainer.get<TransfersContainerIndex>();
|
||||
|
@ -476,19 +451,8 @@ void WalletSerializer::saveUnlockTransactionsJobs(Common::IOutputStream& destina
|
|||
}
|
||||
}
|
||||
|
||||
void WalletSerializer::saveChange(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
|
||||
uint64_t count = m_change.size();
|
||||
serializeEncrypted(count, "changes_count", cryptoContext, destination);
|
||||
cryptoContext.incIv();
|
||||
|
||||
for (const auto& kv: m_change) {
|
||||
ChangeDto dto;
|
||||
dto.txHash = kv.first;
|
||||
dto.amount = kv.second;
|
||||
|
||||
serializeEncrypted(dto, "", cryptoContext, destination);
|
||||
cryptoContext.incIv();
|
||||
}
|
||||
void WalletSerializer::saveUncommitedTransactions(Common::IOutputStream& destination, CryptoContext& cryptoContext) {
|
||||
serializeEncrypted(uncommitedTransactions, "uncommited_transactions", cryptoContext, destination);
|
||||
}
|
||||
|
||||
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) {
|
||||
uint64_t txId = kv.first;
|
||||
|
||||
WalletTransferDto tr(kv.second, SERIALIZATION_VERSION);
|
||||
|
||||
serializeEncrypted(txId, "transaction_id", cryptoContext, destination);
|
||||
|
@ -559,12 +524,33 @@ void WalletSerializer::loadWallet(Common::IInputStream& source, const std::strin
|
|||
loadTransfers(source, cryptoContext, version);
|
||||
}
|
||||
|
||||
if (version < 5) {
|
||||
updateTransfersSign();
|
||||
cache = false;
|
||||
}
|
||||
|
||||
if (cache) {
|
||||
loadBalances(source, cryptoContext);
|
||||
loadTransfersSynchronizer(source, cryptoContext);
|
||||
loadSpentOutputs(source, cryptoContext);
|
||||
if (version < 5) {
|
||||
loadObsoleteSpentOutputs(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) {
|
||||
|
@ -762,30 +748,15 @@ void WalletSerializer::loadTransfersSynchronizer(Common::IInputStream& source, C
|
|||
m_synchronizer.load(stream);
|
||||
}
|
||||
|
||||
void WalletSerializer::loadSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) {
|
||||
auto& index = m_spentOutputs.get<WalletIndex>();
|
||||
auto& walletsIndex = m_walletsContainer.get<RandomAccessIndex>();
|
||||
const uint64_t walletsSize = walletsIndex.size();
|
||||
|
||||
void WalletSerializer::loadObsoleteSpentOutputs(Common::IInputStream& source, CryptoContext& cryptoContext) {
|
||||
uint64_t count = 0;
|
||||
deserializeEncrypted(count, "spent_outputs_count", cryptoContext, source);
|
||||
cryptoContext.incIv();
|
||||
|
||||
for (uint64_t i = 0; i < count; ++i) {
|
||||
SpentOutputDto dto;
|
||||
ObsoleteSpentOutputDto dto;
|
||||
deserializeEncrypted(dto, "", cryptoContext, source);
|
||||
cryptoContext.incIv();
|
||||
|
||||
assert(dto.walletIndex < walletsSize);
|
||||
|
||||
SpentOutput output;
|
||||
output.amount = dto.amount;
|
||||
output.transactionHash = dto.transactionHash;
|
||||
output.outputInTransaction = dto.outputInTransaction;
|
||||
output.spendingTransactionHash = dto.spendingTransactionHash;
|
||||
output.wallet = &walletsIndex[dto.walletIndex];
|
||||
|
||||
index.insert(std::move(output));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
deserializeEncrypted(count, "changes_count", cryptoContext, source);
|
||||
cryptoContext.incIv();
|
||||
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
ChangeDto dto;
|
||||
ObsoleteChangeDto dto;
|
||||
deserializeEncrypted(dto, "", cryptoContext, source);
|
||||
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>();
|
||||
TransactionInformation txInfo;
|
||||
auto it = std::find_if(std::begin(wallets), std::end(wallets), [&](const WalletRecord& rec) {
|
||||
int64_t id = 0;
|
||||
assert(rec.container != nullptr);
|
||||
return rec.container->getTransactionInformation(tx.hash, txInfo, id);
|
||||
return rec.container->getTransactionInformation(tx.hash, txInfo);
|
||||
});
|
||||
|
||||
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) {
|
||||
uint64_t count = 0;
|
||||
deserializeEncrypted(count, "transactions_count", cryptoContext, source);
|
||||
|
|
|
@ -45,12 +45,11 @@ public:
|
|||
uint64_t& pendingBalance,
|
||||
WalletsContainer& walletsContainer,
|
||||
TransfersSyncronizer& synchronizer,
|
||||
SpentOutputs& spentOutputs,
|
||||
UnlockTransactionJobs& unlockTransactions,
|
||||
TransactionChanges& change,
|
||||
WalletTransactions& transactions,
|
||||
WalletTransfers& transfers,
|
||||
uint32_t transactionSoftLockTime
|
||||
uint32_t transactionSoftLockTime,
|
||||
UncommitedTransactions& uncommitedTransactions
|
||||
);
|
||||
|
||||
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 saveBalances(Common::IOutputStream& destination, bool saveCache, CryptoContext& cryptoContext);
|
||||
void saveTransfersSynchronizer(Common::IOutputStream& destination, CryptoContext& cryptoContext);
|
||||
void saveSpentOutputs(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 saveTransfers(Common::IOutputStream& destination, CryptoContext& cryptoContext);
|
||||
|
||||
|
@ -91,16 +89,20 @@ private:
|
|||
void subscribeWallets();
|
||||
void loadBalances(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 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 loadTransfers(Common::IInputStream& source, CryptoContext& cryptoContext, uint32_t version);
|
||||
|
||||
void loadWalletV1Keys(CryptoNote::BinaryInputStreamSerializer& serializer);
|
||||
void loadWalletV1Details(CryptoNote::BinaryInputStreamSerializer& serializer);
|
||||
void addWalletV1Details(const std::vector<WalletLegacyTransaction>& txs, const std::vector<WalletLegacyTransfer>& trs);
|
||||
void initTransactionPool();
|
||||
void resetCachedBalance();
|
||||
void updateTransactionsBaseStatus();
|
||||
void updateTransfersSign();
|
||||
|
||||
ITransfersObserver& m_transfersObserver;
|
||||
Crypto::PublicKey& m_viewPublicKey;
|
||||
|
@ -109,12 +111,11 @@ private:
|
|||
uint64_t& m_pendingBalance;
|
||||
WalletsContainer& m_walletsContainer;
|
||||
TransfersSyncronizer& m_synchronizer;
|
||||
SpentOutputs& m_spentOutputs;
|
||||
UnlockTransactionJobs& m_unlockTransactions;
|
||||
TransactionChanges& m_change;
|
||||
WalletTransactions& m_transactions;
|
||||
WalletTransfers& m_transfers;
|
||||
uint32_t m_transactionSoftLockTime;
|
||||
UncommitedTransactions& uncommitedTransactions;
|
||||
};
|
||||
|
||||
} //namespace CryptoNote
|
||||
|
|
29
src/Wallet/WalletUtils.cpp
Normal file
29
src/Wallet/WalletUtils.cpp
Normal 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
28
src/Wallet/WalletUtils.h
Normal 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);
|
||||
|
||||
}
|
|
@ -536,10 +536,11 @@ void WalletLegacy::onTransactionUpdated(ITransfersSubscription* object, const Ha
|
|||
std::shared_ptr<WalletLegacyEvent> event;
|
||||
|
||||
TransactionInformation txInfo;
|
||||
int64_t txBalance;
|
||||
if (m_transferDetails->getTransactionInformation(transactionHash, txInfo, txBalance)) {
|
||||
uint64_t amountIn;
|
||||
uint64_t amountOut;
|
||||
if (m_transferDetails->getTransactionInformation(transactionHash, txInfo, &amountIn, &amountOut)) {
|
||||
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()) {
|
||||
|
|
|
@ -32,23 +32,23 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) {
|
|||
size_t i, j;
|
||||
size_t cnt = count - 1;
|
||||
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 >> 1);
|
||||
ints = alloca(cnt * HASH_SIZE);
|
||||
memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE);
|
||||
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);
|
||||
while (cnt > 2) {
|
||||
cnt >>= 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define BUILD_COMMIT_ID "@VERSION@"
|
||||
#define PROJECT_VERSION "1.0.8.2"
|
||||
#define PROJECT_VERSION_BUILD_NO "625"
|
||||
#define PROJECT_VERSION "1.0.9"
|
||||
#define PROJECT_VERSION_BUILD_NO "661"
|
||||
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"
|
||||
|
|
|
@ -99,7 +99,25 @@ public:
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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_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 bool is_ready() 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;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <Logging/ConsoleLogger.h>
|
||||
|
||||
#include "PaymentGate/WalletService.h"
|
||||
#include "PaymentGate/WalletFactory.h"
|
||||
|
||||
// test helpers
|
||||
#include "INodeStubs.h"
|
||||
|
@ -45,7 +46,8 @@ public:
|
|||
}
|
||||
|
||||
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();
|
||||
return service;
|
||||
}
|
||||
|
@ -61,6 +63,8 @@ protected:
|
|||
TestBlockchainGenerator generator;
|
||||
INodeTrivialRefreshStub nodeStub;
|
||||
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));
|
||||
|
||||
uint64_t txCount = 0;
|
||||
|
||||
ASSERT_TRUE(!service->getTransactionsCount(txCount));
|
||||
ASSERT_EQ(3, txCount);
|
||||
|
||||
uint64_t pending = 0, actual = 0;
|
||||
|
||||
ASSERT_TRUE(!service->getPendingBalance(pending));
|
||||
ASSERT_TRUE(!service->getActualBalance(actual));
|
||||
service->getBalance(actual, pending);
|
||||
|
||||
ASSERT_NE(0, pending);
|
||||
ASSERT_NE(0, actual);
|
||||
|
@ -106,6 +104,7 @@ TEST_F(PaymentGateTest, addTransaction) {
|
|||
ASSERT_EQ(pending * 2, actual);
|
||||
}
|
||||
|
||||
/*
|
||||
TEST_F(PaymentGateTest, DISABLED_sendTransaction) {
|
||||
|
||||
auto cfg = createWalletConfiguration();
|
||||
|
@ -142,16 +141,16 @@ TEST_F(PaymentGateTest, DISABLED_sendTransaction) {
|
|||
uint64_t txId = 0;
|
||||
|
||||
{
|
||||
SendTransactionRequest req;
|
||||
SendTransactionResponse res;
|
||||
SendTransaction::Request req;
|
||||
SendTransaction::Response res;
|
||||
|
||||
req.destinations.push_back(TransferDestination{ TEST_AMOUNT, recvAddress });
|
||||
req.transfers.push_back(WalletRpcOrder{ TEST_AMOUNT, recvAddress });
|
||||
req.fee = currency.minimumFee();
|
||||
req.mixin = 1;
|
||||
req.anonymity = 1;
|
||||
req.unlockTime = 0;
|
||||
req.paymentId = paymentIdStr;
|
||||
|
||||
ASSERT_TRUE(!service->sendTransaction(req, res));
|
||||
ASSERT_TRUE(!service->sendTransaction(req, res.transactionHash));
|
||||
|
||||
txId = res.transactionId;
|
||||
}
|
||||
|
@ -263,4 +262,4 @@ TEST_F(PaymentGateTest, DISABLED_sendTransaction) {
|
|||
ASSERT_EQ(1, recvPayment.size());
|
||||
ASSERT_EQ(TEST_AMOUNT / 2, recvPayment[0].amount);
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "Transfers/BlockchainSynchronizer.h"
|
||||
#include "Transfers/TransfersConsumer.h"
|
||||
|
||||
#include "crypto/hash.h"
|
||||
#include "CryptoNoteCore/TransactionApi.h"
|
||||
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
|
||||
#include "CryptoNoteCore/CryptoNoteTools.h"
|
||||
|
@ -122,11 +123,21 @@ public:
|
|||
m_blockchain.push_back(genesisBlockHash);
|
||||
}
|
||||
|
||||
void addPoolTransaction(const Crypto::Hash& hash) {
|
||||
m_pool.emplace(hash);
|
||||
}
|
||||
|
||||
virtual SynchronizationStart getSyncStart() override {
|
||||
SynchronizationStart start = { 0, 0 };
|
||||
return start;
|
||||
}
|
||||
|
||||
virtual void addObserver(IBlockchainConsumerObserver* observer) override {
|
||||
}
|
||||
|
||||
virtual void removeObserver(IBlockchainConsumerObserver* observer) override {
|
||||
}
|
||||
|
||||
virtual void onBlockchainDetach(uint32_t height) override {
|
||||
assert(height < m_blockchain.size());
|
||||
m_blockchain.resize(height);
|
||||
|
@ -145,28 +156,32 @@ public:
|
|||
return m_blockchain;
|
||||
}
|
||||
|
||||
virtual void getKnownPoolTxIds(std::vector<Hash>& ids) override {
|
||||
ids.assign(m_pool.begin(), m_pool.end());
|
||||
virtual const std::unordered_set<Crypto::Hash>& getKnownPoolTxIds() const override {
|
||||
return m_pool;
|
||||
}
|
||||
|
||||
virtual std::error_code onPoolUpdated(const std::vector<std::unique_ptr<ITransactionReader>>& addedTransactions, const std::vector<Hash>& deletedTransactions) override {
|
||||
for (const auto& tx: addedTransactions) {
|
||||
Hash hash = tx->getTransactionHash();
|
||||
m_pool.push_back(reinterpret_cast<const Hash&>(hash));
|
||||
m_pool.emplace(tx->getTransactionHash());
|
||||
}
|
||||
|
||||
for (auto& hash : deletedTransactions) {
|
||||
auto pos = std::find(m_pool.begin(), m_pool.end(), hash);
|
||||
if (pos != m_pool.end()) {
|
||||
m_pool.erase(pos);
|
||||
}
|
||||
m_pool.erase(hash);
|
||||
}
|
||||
|
||||
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:
|
||||
std::vector<Hash> m_pool;
|
||||
std::unordered_set<Crypto::Hash> m_pool;
|
||||
std::vector<Hash> m_blockchain;
|
||||
};
|
||||
|
||||
|
@ -483,18 +498,12 @@ TEST_F(BcSTest, serializationCheck) {
|
|||
|
||||
class FunctorialPoolConsumerStub : public ConsumerStub {
|
||||
public:
|
||||
|
||||
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 {
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -511,8 +520,6 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) {
|
|||
auto tx2hash = getObjectHash(tx2);
|
||||
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> secondExpectedPool = { tx2hash };
|
||||
|
||||
|
@ -523,8 +530,11 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) {
|
|||
FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash());
|
||||
FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash());
|
||||
|
||||
c1.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) { ids.assign(consumer1Pool.begin(), consumer1Pool.end()); };
|
||||
c2.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) { ids.assign(consumer2Pool.begin(), consumer2Pool.end()); };
|
||||
c1.addPoolTransaction(tx1hash);
|
||||
c1.addPoolTransaction(tx2hash);
|
||||
|
||||
c2.addPoolTransaction(tx2hash);
|
||||
c2.addPoolTransaction(tx3hash);
|
||||
|
||||
std::vector<Hash> c1ResponseDeletedPool;
|
||||
std::vector<Hash> c2ResponseDeletedPool;
|
||||
|
@ -606,6 +616,7 @@ TEST_F(BcSTest, firstPoolSynchronizationCheck) {
|
|||
|
||||
TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) {
|
||||
addConsumers(2);
|
||||
m_consumers.front()->addPoolTransaction(Crypto::rand<Crypto::Hash>());
|
||||
|
||||
int requestsCount = 0;
|
||||
|
||||
|
@ -635,12 +646,12 @@ TEST_F(BcSTest, firstPoolSynchronizationCheckNonActual) {
|
|||
m_sync.removeObserver(&o1);
|
||||
o1.syncFunc = [](std::error_code) {};
|
||||
|
||||
|
||||
EXPECT_EQ(4, requestsCount);
|
||||
}
|
||||
|
||||
TEST_F(BcSTest, firstPoolSynchronizationCheckGetPoolErr) {
|
||||
addConsumers(2);
|
||||
m_consumers.front()->addPoolTransaction(Crypto::rand<Crypto::Hash>());
|
||||
|
||||
int requestsCount = 0;
|
||||
|
||||
|
@ -895,9 +906,6 @@ TEST_F(BcSTest, poolSynchronizationCheckConsumersNotififcation) {
|
|||
FunctorialPoolConsumerStub c1(m_currency.genesisBlockHash());
|
||||
FunctorialPoolConsumerStub c2(m_currency.genesisBlockHash());
|
||||
|
||||
c1.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
|
||||
c2.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
|
||||
|
||||
bool c1Notified = false;
|
||||
bool c2Notified = false;
|
||||
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 c2(m_currency.genesisBlockHash());
|
||||
|
||||
c1.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
|
||||
c2.getKnownPoolTxIdsFunctor = [&](std::vector<Hash>& ids) {};
|
||||
|
||||
bool c1Notified = false;
|
||||
bool c2Notified = false;
|
||||
c1.onPoolUpdatedFunctor = [&](const std::vector<std::unique_ptr<ITransactionReader>>& new_txs, const std::vector<Hash>& deleted)->std::error_code {
|
||||
|
|
|
@ -210,6 +210,10 @@ void TestBlockchainGenerator::addToBlockchain(const CryptoNote::Transaction& tx)
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
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 block;
|
||||
|
||||
generator.constructBlock(block, prev_block, miner_acc, txsToBlock);
|
||||
generator.constructBlock(block, prev_block, minerAddress, txsToBlock);
|
||||
m_blockchain.push_back(block);
|
||||
addTx(block.baseTransaction);
|
||||
|
||||
|
@ -396,3 +400,9 @@ bool TestBlockchainGenerator::getMultisignatureOutputByGlobalIndex(uint64_t amou
|
|||
out = boost::get<MultisignatureOutput>(tx.outputs[entry.indexOut].target);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestBlockchainGenerator::generateFromBaseTx(const CryptoNote::AccountBase& address) {
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
addToBlockchain({}, address);
|
||||
return true;
|
||||
}
|
|
@ -43,6 +43,7 @@ public:
|
|||
void addTxToBlockchain(const CryptoNote::Transaction& transaction);
|
||||
bool getTransactionByHash(const Crypto::Hash& hash, CryptoNote::Transaction& tx, bool checkTxPool = false);
|
||||
const CryptoNote::AccountBase& getMinerAccount() const;
|
||||
bool generateFromBaseTx(const CryptoNote::AccountBase& address);
|
||||
|
||||
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,
|
||||
|
@ -96,6 +97,7 @@ private:
|
|||
|
||||
void addToBlockchain(const CryptoNote::Transaction& tx);
|
||||
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);
|
||||
|
||||
bool doGenerateTransactionsInOneBlock(CryptoNote::AccountPublicAddress const &address, size_t n);
|
||||
|
|
|
@ -742,8 +742,7 @@ TEST_F(TransfersConsumerTest, onNewBlocks_checkTransactionInformation) {
|
|||
ASSERT_TRUE(m_consumer.onNewBlocks(&blocks[0], 0, 2));
|
||||
|
||||
TransactionInformation info;
|
||||
int64_t balance;
|
||||
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), info, balance));
|
||||
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), info));
|
||||
|
||||
ASSERT_EQ(tx->getTransactionHash(), info.transactionHash);
|
||||
ASSERT_EQ(tx->getTransactionPublicKey(), info.publicKey);
|
||||
|
@ -875,7 +874,7 @@ TEST_F(TransfersConsumerTest, onPoolUpdated_addTransactionDoesNotGetsGlobalIndic
|
|||
ASSERT_TRUE(m_node.calls_getTransactionOutsGlobalIndices.empty());
|
||||
}
|
||||
|
||||
TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransaction) {
|
||||
TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransactionNotDeleted) {
|
||||
auto& sub = addSubscription();
|
||||
TransfersObserver observer;
|
||||
sub.addObserver(&observer);
|
||||
|
@ -887,15 +886,42 @@ TEST_F(TransfersConsumerTest, onPoolUpdated_deleteTransaction) {
|
|||
|
||||
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(reinterpret_cast<const Hash&>(deleted[0]), observer.deleted[0]);
|
||||
ASSERT_EQ(reinterpret_cast<const Hash&>(deleted[1]), observer.deleted[1]);
|
||||
ASSERT_EQ(deleted, observer.deleted);
|
||||
}
|
||||
|
||||
TEST_F(TransfersConsumerTest, getKnownPoolTxIds_empty) {
|
||||
addSubscription();
|
||||
std::vector<Crypto::Hash> ids;
|
||||
m_consumer.getKnownPoolTxIds(ids);
|
||||
const std::unordered_set<Crypto::Hash>& ids = m_consumer.getKnownPoolTxIds();
|
||||
ASSERT_TRUE(ids.empty());
|
||||
}
|
||||
|
||||
|
@ -926,14 +952,13 @@ TEST_F(TransfersConsumerTest, getKnownPoolTxIds_returnsUnconfirmed) {
|
|||
v.push_back(createTransactionPrefix(convertTx(*txs[2])));
|
||||
m_consumer.onPoolUpdated(v, {});
|
||||
|
||||
std::vector<Crypto::Hash> ids;
|
||||
m_consumer.getKnownPoolTxIds(ids);
|
||||
const std::unordered_set<Crypto::Hash>& ids = m_consumer.getKnownPoolTxIds();
|
||||
|
||||
ASSERT_EQ(3, ids.size());
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -295,10 +295,12 @@ TEST_F(TransfersContainer_addTransaction, handlesAddingUnconfirmedOutputToKey) {
|
|||
ASSERT_TRUE(transfers.empty());
|
||||
|
||||
TransactionInformation txInfo;
|
||||
int64_t txBalance;
|
||||
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance));
|
||||
uint64_t amountIn;
|
||||
uint64_t amountOut;
|
||||
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, &amountIn, &amountOut));
|
||||
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;
|
||||
container.getUnconfirmedTransactions(unconfirmedTransactions);
|
||||
|
@ -339,10 +341,12 @@ TEST_F(TransfersContainer_addTransaction, handlesAddingConfirmedOutputToKey) {
|
|||
ASSERT_EQ(1, transfers.size());
|
||||
|
||||
TransactionInformation txInfo;
|
||||
int64_t txBalance;
|
||||
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance));
|
||||
uint64_t amountIn;
|
||||
uint64_t amountOut;
|
||||
ASSERT_TRUE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, &amountIn, &amountOut));
|
||||
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;
|
||||
container.getUnconfirmedTransactions(unconfirmedTransactions);
|
||||
|
@ -379,8 +383,7 @@ TEST_F(TransfersContainer_addTransaction, addingEmptyTransactionOuptutsDoesNotCh
|
|||
ASSERT_TRUE(transfers.empty());
|
||||
|
||||
TransactionInformation txInfo;
|
||||
int64_t txBalance;
|
||||
ASSERT_FALSE(container.getTransactionInformation(tx->getTransactionHash(), txInfo, txBalance));
|
||||
ASSERT_FALSE(container.getTransactionInformation(tx->getTransactionHash(), txInfo));
|
||||
|
||||
std::vector<Crypto::Hash> unconfirmedTransactions;
|
||||
container.getUnconfirmedTransactions(unconfirmedTransactions);
|
||||
|
|
File diff suppressed because it is too large
Load diff
1001
tests/UnitTests/TestWalletService.cpp
Normal file
1001
tests/UnitTests/TestWalletService.cpp
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue