Bytecoin v.1.0.9 release

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

View file

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

View file

@ -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;

View file

@ -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;
};
}

View file

@ -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;

View file

@ -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;

View file

@ -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")

View file

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

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

@ -0,0 +1,37 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "ScopeExit.h"
namespace Tools {
ScopeExit::ScopeExit(std::function<void()>&& handler) :
m_handler(std::move(handler)),
m_cancelled(false) {
}
ScopeExit::~ScopeExit() {
if (!m_cancelled) {
m_handler();
}
}
void ScopeExit::cancel() {
m_cancelled = true;
}
}

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

@ -0,0 +1,41 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <functional>
namespace Tools {
class ScopeExit {
public:
ScopeExit(std::function<void()>&& handler);
~ScopeExit();
ScopeExit(const ScopeExit&) = delete;
ScopeExit(ScopeExit&&) = delete;
ScopeExit& operator=(const ScopeExit&) = delete;
ScopeExit& operator=(ScopeExit&&) = delete;
void cancel();
private:
std::function<void()> m_handler;
bool m_cancelled;
};
}

View file

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

View file

@ -26,4 +26,5 @@ namespace Tools
std::string get_os_version_string();
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);
}

View file

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

View file

@ -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) {

View file

@ -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;

View file

@ -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();
}

View file

@ -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);

View file

@ -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();
}
}

View file

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

View file

@ -68,7 +68,6 @@ public:
virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) = 0;
virtual bool handle_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;

View file

@ -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;
}

View file

@ -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);

View file

@ -0,0 +1,105 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "BlockchainMonitor.h"
#include "Common/StringTools.h"
#include <System/EventLock.h>
#include <System/Timer.h>
#include <System/InterruptedException.h>
#include "Rpc/CoreRpcServerCommandsDefinitions.h"
#include "Rpc/JsonRpc.h"
#include "Rpc/HttpClient.h"
BlockchainMonitor::BlockchainMonitor(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, size_t pollingInterval, Logging::ILogger& logger):
m_dispatcher(dispatcher),
m_daemonHost(daemonHost),
m_daemonPort(daemonPort),
m_pollingInterval(pollingInterval),
m_stopped(false),
m_httpEvent(dispatcher),
m_sleepingContext(dispatcher),
m_logger(logger, "BlockchainMonitor") {
m_httpEvent.set();
}
void BlockchainMonitor::waitBlockchainUpdate() {
m_logger(Logging::DEBUGGING) << "Waiting for blockchain updates";
m_stopped = false;
Crypto::Hash lastBlockHash = requestLastBlockHash();
while(!m_stopped) {
m_sleepingContext.spawn([this] () {
System::Timer timer(m_dispatcher);
timer.sleep(std::chrono::seconds(m_pollingInterval));
});
m_sleepingContext.wait();
if (lastBlockHash != requestLastBlockHash()) {
m_logger(Logging::DEBUGGING) << "Blockchain has been updated";
break;
}
}
if (m_stopped) {
m_logger(Logging::DEBUGGING) << "Blockchain monitor has been stopped";
throw System::InterruptedException();
}
}
void BlockchainMonitor::stop() {
m_logger(Logging::DEBUGGING) << "Sending stop signal to blockchain monitor";
m_stopped = true;
m_sleepingContext.interrupt();
m_sleepingContext.wait();
}
Crypto::Hash BlockchainMonitor::requestLastBlockHash() {
m_logger(Logging::DEBUGGING) << "Requesting last block hash";
try {
CryptoNote::HttpClient client(m_dispatcher, m_daemonHost, m_daemonPort);
CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::request request;
CryptoNote::COMMAND_RPC_GET_LAST_BLOCK_HEADER::response response;
System::EventLock lk(m_httpEvent);
CryptoNote::JsonRpc::invokeJsonRpcCommand(client, "getlastblockheader", request, response);
if (response.status != CORE_RPC_STATUS_OK) {
throw std::runtime_error("Core responded with wrong status: " + response.status);
}
Crypto::Hash blockHash;
if (!Common::podFromHex(response.block_header.hash, blockHash)) {
throw std::runtime_error("Couldn't parse block hash: " + response.block_header.hash);
}
m_logger(Logging::DEBUGGING) << "Last block hash: " << Common::podToHex(blockHash);
return blockHash;
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Failed to request last block hash: " << e.what();
throw;
}
}

View file

@ -0,0 +1,46 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include "CryptoTypes.h"
#include <System/ContextGroup.h>
#include <System/Dispatcher.h>
#include <System/Event.h>
#include "Logging/LoggerRef.h"
class BlockchainMonitor {
public:
BlockchainMonitor(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, size_t pollingInterval, Logging::ILogger& logger);
void waitBlockchainUpdate();
void stop();
private:
System::Dispatcher& m_dispatcher;
std::string m_daemonHost;
uint16_t m_daemonPort;
size_t m_pollingInterval;
bool m_stopped;
System::Event m_httpEvent;
System::ContextGroup m_sleepingContext;
Logging::LoggerRef m_logger;
Crypto::Hash requestLastBlockHash();
};

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

@ -0,0 +1,157 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "Miner.h"
#include <functional>
#include "crypto/crypto.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include <System/InterruptedException.h>
namespace CryptoNote {
Miner::Miner(System::Dispatcher& dispatcher, Logging::ILogger& logger) :
m_dispatcher(dispatcher),
m_miningStopped(dispatcher),
m_state(MiningState::MINING_STOPPED),
m_logger(logger, "Miner") {
}
Miner::~Miner() {
assert(m_state != MiningState::MINING_IN_PROGRESS);
}
Block Miner::mine(const BlockMiningParameters& blockMiningParameters, size_t threadCount) {
if (threadCount == 0) {
throw std::runtime_error("Miner requires at least one thread");
}
if (m_state == MiningState::MINING_IN_PROGRESS) {
throw std::runtime_error("Mining is already in progress");
}
m_state = MiningState::MINING_IN_PROGRESS;
m_miningStopped.clear();
runWorkers(blockMiningParameters, threadCount);
assert(m_state != MiningState::MINING_IN_PROGRESS);
if (m_state == MiningState::MINING_STOPPED) {
m_logger(Logging::DEBUGGING) << "Mining has been stopped";
throw System::InterruptedException();
}
assert(m_state == MiningState::BLOCK_FOUND);
return m_block;
}
void Miner::stop() {
MiningState state = MiningState::MINING_IN_PROGRESS;
if (m_state.compare_exchange_weak(state, MiningState::MINING_STOPPED)) {
m_miningStopped.wait();
m_miningStopped.clear();
}
}
void Miner::runWorkers(BlockMiningParameters blockMiningParameters, size_t threadCount) {
assert(threadCount > 0);
m_logger(Logging::INFO) << "Starting mining for difficulty " << blockMiningParameters.difficulty;
try {
blockMiningParameters.blockTemplate.nonce = Crypto::rand<uint32_t>();
for (size_t i = 0; i < threadCount; ++i) {
m_workers.emplace_back(std::unique_ptr<System::RemoteContext<void>> (
new System::RemoteContext<void>(m_dispatcher, std::bind(&Miner::workerFunc, this, blockMiningParameters.blockTemplate, blockMiningParameters.difficulty, threadCount)))
);
blockMiningParameters.blockTemplate.nonce++;
}
m_workers.clear();
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Error occured during mining: " << e.what();
m_state = MiningState::MINING_STOPPED;
}
m_miningStopped.set();
}
void Miner::workerFunc(const Block& blockTemplate, difficulty_type difficulty, uint32_t nonceStep) {
try {
Block block = blockTemplate;
Crypto::cn_context cryptoContext;
while (m_state == MiningState::MINING_IN_PROGRESS) {
Crypto::Hash hash;
if (!get_block_longhash(cryptoContext, block, hash)) {
//error occured
m_logger(Logging::DEBUGGING) << "calculating long hash error occured";
m_state = MiningState::MINING_STOPPED;
return;
}
if (check_hash(hash, difficulty)) {
m_logger(Logging::INFO) << "Found block for difficulty " << difficulty;
if (!setStateBlockFound()) {
m_logger(Logging::DEBUGGING) << "block is already found or mining stopped";
return;
}
m_block = block;
return;
}
block.nonce += nonceStep;
}
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Miner got error: " << e.what();
m_state = MiningState::MINING_STOPPED;
}
}
bool Miner::setStateBlockFound() {
auto state = m_state.load();
for (;;) {
switch (state) {
case MiningState::BLOCK_FOUND:
return false;
case MiningState::MINING_IN_PROGRESS:
if (m_state.compare_exchange_weak(state, MiningState::BLOCK_FOUND)) {
return true;
}
break;
case MiningState::MINING_STOPPED:
return false;
default:
assert(false);
return false;
}
}
}
} //namespace CryptoNote

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

@ -0,0 +1,67 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <atomic>
#include <thread>
#include <System/Dispatcher.h>
#include <System/Event.h>
#include <System/RemoteContext.h>
#include "CryptoNote.h"
#include "CryptoNoteCore/Difficulty.h"
#include "Logging/LoggerRef.h"
namespace CryptoNote {
struct BlockMiningParameters {
Block blockTemplate;
difficulty_type difficulty;
};
class Miner {
public:
Miner(System::Dispatcher& dispatcher, Logging::ILogger& logger);
~Miner();
Block mine(const BlockMiningParameters& blockMiningParameters, size_t threadCount);
//NOTE! this is blocking method
void stop();
private:
System::Dispatcher& m_dispatcher;
System::Event m_miningStopped;
enum class MiningState : uint8_t { MINING_STOPPED, BLOCK_FOUND, MINING_IN_PROGRESS};
std::atomic<MiningState> m_state;
std::vector<std::unique_ptr<System::RemoteContext<void>>> m_workers;
Block m_block;
Logging::LoggerRef m_logger;
void runWorkers(BlockMiningParameters blockMiningParameters, size_t threadCount);
void workerFunc(const Block& blockTemplate, difficulty_type difficulty, uint32_t nonceStep);
bool setStateBlockFound();
};
} //namespace CryptoNote

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

@ -0,0 +1,31 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
namespace Miner {
enum class MinerEventType: uint8_t {
BLOCK_MINED,
BLOCKCHAIN_UPDATED,
};
struct MinerEvent {
MinerEventType type;
};
} //namespace Miner

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

@ -0,0 +1,276 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "MinerManager.h"
#include <System/EventLock.h>
#include <System/InterruptedException.h>
#include <System/Timer.h>
#include "Common/StringTools.h"
#include "CryptoNoteConfig.h"
#include "CryptoNoteCore/CryptoNoteTools.h"
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "CryptoNoteCore/TransactionExtra.h"
#include "Rpc/HttpClient.h"
#include "Rpc/CoreRpcServerCommandsDefinitions.h"
#include "Rpc/JsonRpc.h"
using namespace CryptoNote;
namespace Miner {
namespace {
MinerEvent BlockMinedEvent() {
MinerEvent event;
event.type = MinerEventType::BLOCK_MINED;
return event;
}
MinerEvent BlockchainUpdatedEvent() {
MinerEvent event;
event.type = MinerEventType::BLOCKCHAIN_UPDATED;
return event;
}
void adjustMergeMiningTag(Block& blockTemplate) {
if (blockTemplate.majorVersion == BLOCK_MAJOR_VERSION_2) {
CryptoNote::TransactionExtraMergeMiningTag mmTag;
mmTag.depth = 0;
if (!CryptoNote::get_aux_block_header_hash(blockTemplate, mmTag.merkleRoot)) {
throw std::runtime_error("Couldn't get block header hash");
}
blockTemplate.parentBlock.baseTransaction.extra.clear();
if (!CryptoNote::appendMergeMiningTagToExtra(blockTemplate.parentBlock.baseTransaction.extra, mmTag)) {
throw std::runtime_error("Couldn't append merge mining tag");
}
}
}
}
MinerManager::MinerManager(System::Dispatcher& dispatcher, const CryptoNote::MiningConfig& config, Logging::ILogger& logger) :
m_dispatcher(dispatcher),
m_logger(logger, "MinerManager"),
m_contextGroup(dispatcher),
m_config(config),
m_miner(dispatcher, logger),
m_blockchainMonitor(dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.scanPeriod, logger),
m_eventOccurred(dispatcher),
m_httpEvent(dispatcher),
m_lastBlockTimestamp(0) {
m_httpEvent.set();
}
MinerManager::~MinerManager() {
}
void MinerManager::start() {
m_logger(Logging::DEBUGGING) << "starting";
BlockMiningParameters params;
for (;;) {
m_logger(Logging::INFO) << "requesting mining parameters";
try {
params = requestMiningParameters(m_dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.miningAddress);
} catch (ConnectException& e) {
m_logger(Logging::WARNING) << "Couldn't connect to daemon: " << e.what();
System::Timer timer(m_dispatcher);
timer.sleep(std::chrono::seconds(m_config.scanPeriod));
continue;
}
adjustBlockTemplate(params.blockTemplate);
break;
}
startBlockchainMonitoring();
startMining(params);
eventLoop();
}
void MinerManager::eventLoop() {
size_t blocksMined = 0;
for (;;) {
m_logger(Logging::DEBUGGING) << "waiting for event";
MinerEvent event = waitEvent();
switch (event.type) {
case MinerEventType::BLOCK_MINED: {
m_logger(Logging::DEBUGGING) << "got BLOCK_MINED event";
stopBlockchainMonitoring();
if (submitBlock(m_minedBlock, m_config.daemonHost, m_config.daemonPort)) {
m_lastBlockTimestamp = m_minedBlock.timestamp;
if (m_config.blocksLimit != 0 && ++blocksMined == m_config.blocksLimit) {
m_logger(Logging::INFO) << "Miner mined requested " << m_config.blocksLimit << " blocks. Quitting";
return;
}
}
BlockMiningParameters params = requestMiningParameters(m_dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.miningAddress);
adjustBlockTemplate(params.blockTemplate);
startBlockchainMonitoring();
startMining(params);
break;
}
case MinerEventType::BLOCKCHAIN_UPDATED: {
m_logger(Logging::DEBUGGING) << "got BLOCKCHAIN_UPDATED event";
stopMining();
stopBlockchainMonitoring();
BlockMiningParameters params = requestMiningParameters(m_dispatcher, m_config.daemonHost, m_config.daemonPort, m_config.miningAddress);
adjustBlockTemplate(params.blockTemplate);
startBlockchainMonitoring();
startMining(params);
break;
}
default:
assert(false);
return;
}
}
}
MinerEvent MinerManager::waitEvent() {
while(m_events.empty()) {
m_eventOccurred.wait();
m_eventOccurred.clear();
}
MinerEvent event = std::move(m_events.front());
m_events.pop();
return event;
}
void MinerManager::pushEvent(MinerEvent&& event) {
m_events.push(std::move(event));
m_eventOccurred.set();
}
void MinerManager::startMining(const CryptoNote::BlockMiningParameters& params) {
m_contextGroup.spawn([this, params] () {
try {
m_minedBlock = m_miner.mine(params, m_config.threadCount);
pushEvent(BlockMinedEvent());
} catch (System::InterruptedException&) {
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "Miner context unexpectedly finished: " << e.what();
}
});
}
void MinerManager::stopMining() {
m_miner.stop();
}
void MinerManager::startBlockchainMonitoring() {
m_contextGroup.spawn([this] () {
try {
m_blockchainMonitor.waitBlockchainUpdate();
pushEvent(BlockchainUpdatedEvent());
} catch (System::InterruptedException&) {
} catch (std::exception& e) {
m_logger(Logging::ERROR) << "BlockchainMonitor context unexpectedly finished: " << e.what();
}
});
}
void MinerManager::stopBlockchainMonitoring() {
m_blockchainMonitor.stop();
}
bool MinerManager::submitBlock(const Block& minedBlock, const std::string& daemonHost, uint16_t daemonPort) {
try {
HttpClient client(m_dispatcher, daemonHost, daemonPort);
COMMAND_RPC_SUBMITBLOCK::request request;
request.emplace_back(Common::toHex(toBinaryArray(minedBlock)));
COMMAND_RPC_SUBMITBLOCK::response response;
System::EventLock lk(m_httpEvent);
JsonRpc::invokeJsonRpcCommand(client, "submitblock", request, response);
m_logger(Logging::INFO) << "Block has been successfully submitted. Block hash: " << Common::podToHex(get_block_hash(minedBlock));
return true;
} catch (std::exception& e) {
m_logger(Logging::WARNING) << "Couldn't submit block: " << Common::podToHex(get_block_hash(minedBlock)) << ", reason: " << e.what();
return false;
}
}
BlockMiningParameters MinerManager::requestMiningParameters(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, const std::string& miningAddress) {
try {
HttpClient client(dispatcher, daemonHost, daemonPort);
COMMAND_RPC_GETBLOCKTEMPLATE::request request;
request.wallet_address = miningAddress;
request.reserve_size = 0;
COMMAND_RPC_GETBLOCKTEMPLATE::response response;
System::EventLock lk(m_httpEvent);
JsonRpc::invokeJsonRpcCommand(client, "getblocktemplate", request, response);
if (response.status != CORE_RPC_STATUS_OK) {
throw std::runtime_error("Core responded with wrong status: " + response.status);
}
BlockMiningParameters params;
params.difficulty = response.difficulty;
if(!fromBinaryArray(params.blockTemplate, Common::fromHex(response.blocktemplate_blob))) {
throw std::runtime_error("Couldn't deserialize block template");
}
m_logger(Logging::DEBUGGING) << "Requested block template with previous block hash: " << Common::podToHex(params.blockTemplate.previousBlockHash);
return params;
} catch (std::exception& e) {
m_logger(Logging::WARNING) << "Couldn't get block template: " << e.what();
throw;
}
}
void MinerManager::adjustBlockTemplate(CryptoNote::Block& blockTemplate) const {
adjustMergeMiningTag(blockTemplate);
if (m_config.firstBlockTimestamp == 0) {
//no need to fix timestamp
return;
}
if (m_lastBlockTimestamp == 0) {
blockTemplate.timestamp = m_config.firstBlockTimestamp;
} else if (m_lastBlockTimestamp != 0 && m_config.blockTimestampInterval != 0) {
blockTemplate.timestamp = m_lastBlockTimestamp + m_config.blockTimestampInterval;
}
}
} //namespace Miner

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

@ -0,0 +1,76 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <queue>
#include <System/ContextGroup.h>
#include <System/Event.h>
#include "BlockchainMonitor.h"
#include "Logging/LoggerRef.h"
#include "Miner.h"
#include "MinerEvent.h"
#include "MiningConfig.h"
namespace System {
class Dispatcher;
}
namespace Miner {
class MinerManager {
public:
MinerManager(System::Dispatcher& dispatcher, const CryptoNote::MiningConfig& config, Logging::ILogger& logger);
~MinerManager();
void start();
private:
System::Dispatcher& m_dispatcher;
Logging::LoggerRef m_logger;
System::ContextGroup m_contextGroup;
CryptoNote::MiningConfig m_config;
CryptoNote::Miner m_miner;
BlockchainMonitor m_blockchainMonitor;
System::Event m_eventOccurred;
System::Event m_httpEvent;
std::queue<MinerEvent> m_events;
CryptoNote::Block m_minedBlock;
uint64_t m_lastBlockTimestamp;
void eventLoop();
MinerEvent waitEvent();
void pushEvent(MinerEvent&& event);
void startMining(const CryptoNote::BlockMiningParameters& params);
void stopMining();
void startBlockchainMonitoring();
void stopBlockchainMonitoring();
bool submitBlock(const CryptoNote::Block& minedBlock, const std::string& daemonHost, uint16_t daemonPort);
CryptoNote::BlockMiningParameters requestMiningParameters(System::Dispatcher& dispatcher, const std::string& daemonHost, uint16_t daemonPort, const std::string& miningAddress);
void adjustBlockTemplate(CryptoNote::Block& blockTemplate) const;
};
} //namespace Miner

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

@ -0,0 +1,138 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "MiningConfig.h"
#include <iostream>
#include <thread>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
#include "CryptoNoteConfig.h"
#include "Logging/ILogger.h"
namespace po = boost::program_options;
namespace CryptoNote {
namespace {
const size_t DEFAULT_SCANT_PERIOD = 30;
const char* DEFAULT_DAEMON_HOST = "127.0.0.1";
const size_t CONCURRENCY_LEVEL = std::thread::hardware_concurrency();
po::options_description cmdOptions;
void parseDaemonAddress(const std::string& daemonAddress, std::string& daemonHost, uint16_t& daemonPort) {
std::vector<std::string> splittedAddress;
boost::algorithm::split(splittedAddress, daemonAddress, boost::algorithm::is_any_of(":"));
if (splittedAddress.size() != 2) {
throw std::runtime_error("Wrong daemon address format");
}
if (splittedAddress[0].empty() || splittedAddress[1].empty()) {
throw std::runtime_error("Wrong daemon address format");
}
daemonHost = splittedAddress[0];
try {
daemonPort = boost::lexical_cast<uint16_t>(splittedAddress[1]);
} catch (std::exception&) {
throw std::runtime_error("Wrong daemon address format");
}
}
}
MiningConfig::MiningConfig(): help(false) {
cmdOptions.add_options()
("help,h", "produce this help message and exit")
("address", po::value<std::string>(), "Valid cryptonote miner's address")
("daemon-host", po::value<std::string>()->default_value(DEFAULT_DAEMON_HOST), "Daemon host")
("daemon-rpc-port", po::value<uint16_t>()->default_value(static_cast<uint16_t>(RPC_DEFAULT_PORT)), "Daemon's RPC port")
("daemon-address", po::value<std::string>(), "Daemon host:port. If you use this option you must not use --daemon-host and --daemon-port options")
("threads", po::value<size_t>()->default_value(CONCURRENCY_LEVEL), "Mining threads count. Must not be greater than you concurrency level. Default value is your hardware concurrency level")
("scan-time", po::value<size_t>()->default_value(DEFAULT_SCANT_PERIOD), "Blockchain polling interval (seconds). How often miner will check blockchain for updates")
("log-level", po::value<int>()->default_value(1), "Log level. Must be 0..5")
("limit", po::value<size_t>()->default_value(0), "Mine exact quantity of blocks. 0 means no limit")
("first-block-timestamp", po::value<uint64_t>()->default_value(0), "Set timestamp to the first mined block. 0 means leave timestamp unchanged")
("block-timestamp-interval", po::value<int64_t>()->default_value(0), "Timestamp step for each subsequent block. May be set only if --first-block-timestamp has been set."
" If not set blocks' timestamps remain unchanged");
}
void MiningConfig::parse(int argc, char** argv) {
po::variables_map options;
po::store(po::parse_command_line(argc, argv, cmdOptions), options);
po::notify(options);
if (options.count("help") != 0) {
help = true;
return;
}
if (options.count("address") == 0) {
throw std::runtime_error("Specify --address option");
}
miningAddress = options["address"].as<std::string>();
if (!options["daemon-address"].empty()) {
if (!options["daemon-host"].defaulted() || !options["daemon-rpc-port"].defaulted()) {
throw std::runtime_error("Either --daemon-host or --daemon-rpc-port is already specified. You must not specify --daemon-address");
}
parseDaemonAddress(options["daemon-address"].as<std::string>(), daemonHost, daemonPort);
} else {
daemonHost = options["daemon-host"].as<std::string>();
daemonPort = options["daemon-rpc-port"].as<uint16_t>();
}
threadCount = options["threads"].as<size_t>();
if (threadCount == 0 || threadCount > CONCURRENCY_LEVEL) {
throw std::runtime_error("--threads option must be 1.." + std::to_string(CONCURRENCY_LEVEL));
}
scanPeriod = options["scan-time"].as<size_t>();
if (scanPeriod == 0) {
throw std::runtime_error("--scan-time must not be zero");
}
logLevel = static_cast<uint8_t>(options["log-level"].as<int>());
if (logLevel > static_cast<uint8_t>(Logging::TRACE)) {
throw std::runtime_error("--log-level value is too big");
}
blocksLimit = options["limit"].as<size_t>();
if (!options["block-timestamp-interval"].defaulted() && options["first-block-timestamp"].defaulted()) {
throw std::runtime_error("If you specify --block-timestamp-interval you must specify --first-block-timestamp either");
}
firstBlockTimestamp = options["first-block-timestamp"].as<uint64_t>();
blockTimestampInterval = options["block-timestamp-interval"].as<int64_t>();
}
void MiningConfig::printHelp() {
std::cout << cmdOptions << std::endl;
}
}

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

@ -0,0 +1,43 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <cstdint>
#include <string>
namespace CryptoNote {
struct MiningConfig {
MiningConfig();
void parse(int argc, char** argv);
void printHelp();
std::string miningAddress;
std::string daemonHost;
uint16_t daemonPort;
size_t threadCount;
size_t scanPeriod;
uint8_t logLevel;
size_t blocksLimit;
uint64_t firstBlockTimestamp;
int64_t blockTimestampInterval;
bool help;
};
} //namespace CryptoNote

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

@ -0,0 +1,52 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "Common/SignalHandler.h"
#include "Logging/LoggerGroup.h"
#include "Logging/ConsoleLogger.h"
#include "Logging/LoggerRef.h"
#include "MinerManager.h"
#include <System/Dispatcher.h>
int main(int argc, char** argv) {
try {
CryptoNote::MiningConfig config;
config.parse(argc, argv);
if (config.help) {
config.printHelp();
return 0;
}
Logging::LoggerGroup loggerGroup;
Logging::ConsoleLogger consoleLogger(static_cast<Logging::Level>(config.logLevel));
loggerGroup.addLogger(consoleLogger);
System::Dispatcher dispatcher;
Miner::MinerManager app(dispatcher, config, loggerGroup);
app.start();
} catch (std::exception& e) {
std::cerr << "Fatal: " << e.what() << std::endl;
return 1;
}
return 0;
}

View file

@ -225,36 +225,36 @@ void NodeRpcProxy::updateBlockchainStatus() {
m_lastKnowHash = blockHash;
m_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 {

View file

@ -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);

View file

@ -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) {
}
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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

View file

@ -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 &currency, 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

View file

@ -0,0 +1,26 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "WalletServiceErrorCategory.h"
namespace CryptoNote {
namespace error {
WalletServiceErrorCategory WalletServiceErrorCategory::INSTANCE;
}
}

View file

@ -0,0 +1,75 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <system_error>
namespace CryptoNote {
namespace error {
enum class WalletServiceErrorCode {
WRONG_KEY_FORMAT = 1,
WRONG_PAYMENT_ID_FORMAT,
WRONG_HASH_FORMAT,
OBJECT_NOT_FOUND
};
// custom category:
class WalletServiceErrorCategory : public std::error_category {
public:
static WalletServiceErrorCategory INSTANCE;
virtual const char* name() const throw() {
return "WalletServiceErrorCategory";
}
virtual std::error_condition default_error_condition(int ev) const throw() {
return std::error_condition(ev, *this);
}
virtual std::string message(int ev) const {
WalletServiceErrorCode code = static_cast<WalletServiceErrorCode>(ev);
switch (code) {
case WalletServiceErrorCode::WRONG_KEY_FORMAT: return "Wrong key format";
case WalletServiceErrorCode::WRONG_PAYMENT_ID_FORMAT: return "Wrong payment id format";
case WalletServiceErrorCode::WRONG_HASH_FORMAT: return "Wrong block id format";
case WalletServiceErrorCode::OBJECT_NOT_FOUND: return "Requested object not found";
default: return "Unknown error";
}
}
private:
WalletServiceErrorCategory() {
}
};
} //namespace error
} //namespace CryptoNote
inline std::error_code make_error_code(CryptoNote::error::WalletServiceErrorCode e) {
return std::error_code(static_cast<int>(e), CryptoNote::error::WalletServiceErrorCategory::INSTANCE);
}
namespace std {
template <>
struct is_error_code_enum<CryptoNote::error::WalletServiceErrorCode>: public true_type {};
}

View file

@ -28,6 +28,7 @@
#include "CryptoNoteCore/Core.h"
#include "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();
}
}
}

View file

@ -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");
}
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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)
}
};
};

View file

@ -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;

View file

@ -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;
};
}

View file

@ -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();
}
}

View file

@ -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;
};
}

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -41,13 +41,21 @@ TransfersSyncronizer::~TransfersSyncronizer() {
}
}
void TransfersSyncronizer::initTransactionPool(const std::unordered_set<Crypto::Hash>& uncommitedTransactions) {
for (auto it = m_consumers.begin(); it != m_consumers.end(); ++it) {
it->second->initTransactionPool(uncommitedTransactions);
}
}
ITransfersSubscription& TransfersSyncronizer::addSubscription(const AccountSubscription& acc) {
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();
}
}

View file

@ -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;
};
}

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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);

View file

@ -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

View file

@ -0,0 +1,29 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#include "WalletUtils.h"
#include "CryptoNote.h"
namespace CryptoNote {
bool validateAddress(const std::string& address, const CryptoNote::Currency& currency) {
CryptoNote::AccountPublicAddress ignore;
return currency.parseAccountAddressString(address, ignore);
}
}

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

@ -0,0 +1,28 @@
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include "CryptoNoteCore/Currency.h"
namespace CryptoNote {
bool validateAddress(const std::string& address, const CryptoNote::Currency& currency);
}

View file

@ -536,10 +536,11 @@ void WalletLegacy::onTransactionUpdated(ITransfersSubscription* object, const Ha
std::shared_ptr<WalletLegacyEvent> event;
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()) {

View file

@ -32,23 +32,23 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) {
size_t i, j;
size_t 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);
}
}

View file

@ -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 ")"

View file

@ -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
}

View file

@ -63,7 +63,6 @@ public:
virtual bool handle_incoming_block_blob(const CryptoNote::BinaryArray& block_blob, CryptoNote::block_verification_context& bvc, bool control_miner, bool relay_block) override { return false; }
virtual bool handle_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;

View file

@ -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);
}
}
} */

View file

@ -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 {

View file

@ -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;
}

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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

File diff suppressed because it is too large Load diff