Daemon synchronization and SimpleWallet improvements

This commit is contained in:
Antonio Juarez 2015-08-19 18:06:24 +01:00
parent deda499fc9
commit a4b74eaa11
29 changed files with 1090 additions and 222 deletions

View file

@ -37,7 +37,9 @@ enum class WalletTransactionState : uint8_t {
enum WalletEventType { enum WalletEventType {
TRANSACTION_CREATED, TRANSACTION_CREATED,
TRANSACTION_UPDATED, TRANSACTION_UPDATED,
BALANCE_UNLOCKED BALANCE_UNLOCKED,
SYNC_PROGRESS_UPDATED,
SYNC_COMPLETED
}; };
struct WalletTransactionCreatedData { struct WalletTransactionCreatedData {
@ -48,11 +50,17 @@ struct WalletTransactionUpdatedData {
size_t transactionIndex; size_t transactionIndex;
}; };
struct WalletSynchronizationProgressUpdated {
uint32_t processedBlockCount;
uint32_t totalBlockCount;
};
struct WalletEvent { struct WalletEvent {
WalletEventType type; WalletEventType type;
union { union {
WalletTransactionCreatedData transactionCreated; WalletTransactionCreatedData transactionCreated;
WalletTransactionUpdatedData transactionUpdated; WalletTransactionUpdatedData transactionUpdated;
WalletSynchronizationProgressUpdated synchronizationProgressUpdated;
}; };
}; };
@ -78,6 +86,7 @@ public:
virtual ~IWallet() {} virtual ~IWallet() {}
virtual void initialize(const std::string& password) = 0; virtual void initialize(const std::string& password) = 0;
virtual void initializeWithViewKey(const Crypto::SecretKey& viewSecretKey, const std::string& password) = 0;
virtual void load(std::istream& source, const std::string& password) = 0; virtual void load(std::istream& source, const std::string& password) = 0;
virtual void shutdown() = 0; virtual void shutdown() = 0;
@ -86,8 +95,10 @@ public:
virtual size_t getAddressCount() const = 0; virtual size_t getAddressCount() const = 0;
virtual std::string getAddress(size_t index) const = 0; virtual std::string getAddress(size_t index) const = 0;
virtual KeyPair getAddressSpendKey(size_t index) const = 0;
virtual KeyPair getViewKey() const = 0;
virtual std::string createAddress() = 0; virtual std::string createAddress() = 0;
virtual std::string createAddress(const KeyPair& spendKey) = 0; virtual std::string createAddress(const Crypto::SecretKey& spendSecretKey) = 0;
virtual void deleteAddress(const std::string& address) = 0; virtual void deleteAddress(const std::string& address) = 0;
virtual uint64_t getActualBalance() const = 0; virtual uint64_t getActualBalance() const = 0;

View file

@ -161,7 +161,8 @@ const CheckpointData CHECKPOINTS[] = {
{789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"}, {789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"},
{796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"}, {796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"},
{800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"}, {800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"},
{804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"} {804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"},
{810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"}
}; };
} // CryptoNote } // CryptoNote

View file

@ -27,7 +27,9 @@ CoreConfig::CoreConfig() {
} }
void CoreConfig::init(const boost::program_options::variables_map& options) { void CoreConfig::init(const boost::program_options::variables_map& options) {
configFolder = command_line::get_arg(options, command_line::arg_data_dir); 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);
}
} }
void CoreConfig::initOptions(boost::program_options::options_description& desc) { void CoreConfig::initOptions(boost::program_options::options_description& desc) {

View file

@ -30,7 +30,8 @@ enum NodeErrorCodes {
NETWORK_ERROR, NETWORK_ERROR,
NODE_BUSY, NODE_BUSY,
INTERNAL_NODE_ERROR, INTERNAL_NODE_ERROR,
REQUEST_ERROR REQUEST_ERROR,
CONNECT_ERROR
}; };
// custom category: // custom category:
@ -54,6 +55,7 @@ public:
case NODE_BUSY: return "Node is busy"; case NODE_BUSY: return "Node is busy";
case INTERNAL_NODE_ERROR: return "Internal node error"; case INTERNAL_NODE_ERROR: return "Internal node error";
case REQUEST_ERROR: return "Error in request parameters"; case REQUEST_ERROR: return "Error in request parameters";
case CONNECT_ERROR: return "Can't connect to daemon";
default: return "Unknown error"; default: return "Unknown error";
} }
} }

View file

@ -66,7 +66,8 @@ NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort)
m_pullInterval(5000), m_pullInterval(5000),
m_nodeHost(nodeHost), m_nodeHost(nodeHost),
m_nodePort(nodePort), m_nodePort(nodePort),
m_lastLocalBlockTimestamp(0) { m_lastLocalBlockTimestamp(0),
m_connected(true) {
resetInternalState(); resetInternalState();
} }
@ -170,6 +171,8 @@ void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) {
m_context_group = nullptr; m_context_group = nullptr;
m_httpClient = nullptr; m_httpClient = nullptr;
m_httpEvent = nullptr; m_httpEvent = nullptr;
m_connected = false;
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
} }
void NodeRpcProxy::updateNodeStatus() { void NodeRpcProxy::updateNodeStatus() {
@ -200,6 +203,10 @@ void NodeRpcProxy::updateNodeStatus() {
} }
updatePeerCount(); updatePeerCount();
if (m_connected != m_httpClient->isConnected()) {
m_connected = m_httpClient->isConnected();
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
}
} }
void NodeRpcProxy::updatePeerCount() { void NodeRpcProxy::updatePeerCount() {
@ -225,6 +232,14 @@ bool NodeRpcProxy::removeObserver(INodeObserver* observer) {
return m_observerManager.remove(observer); return m_observerManager.remove(observer);
} }
bool NodeRpcProxy::addObserver(CryptoNote::INodeRpcProxyObserver* observer) {
return m_rpcProxyObserverManager.add(observer);
}
bool NodeRpcProxy::removeObserver(CryptoNote::INodeRpcProxyObserver* observer) {
return m_rpcProxyObserverManager.remove(observer);
}
size_t NodeRpcProxy::getPeerCount() const { size_t NodeRpcProxy::getPeerCount() const {
return m_peerCount.load(std::memory_order_relaxed); return m_peerCount.load(std::memory_order_relaxed);
} }
@ -563,6 +578,10 @@ void NodeRpcProxy::scheduleRequest(std::function<std::error_code()>&& procedure,
callback(std::make_error_code(std::errc::operation_canceled)); callback(std::make_error_code(std::errc::operation_canceled));
} else { } else {
std::error_code ec = procedure(); std::error_code ec = procedure();
if (m_connected != m_httpClient->isConnected()) {
m_connected = m_httpClient->isConnected();
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
}
callback(m_stop ? std::make_error_code(std::errc::operation_canceled) : ec); callback(m_stop ? std::make_error_code(std::errc::operation_canceled) : ec);
} }
}, std::move(procedure), std::move(callback))); }, std::move(procedure), std::move(callback)));
@ -577,6 +596,8 @@ std::error_code NodeRpcProxy::binaryCommand(const std::string& url, const Reques
EventLock eventLock(*m_httpEvent); EventLock eventLock(*m_httpEvent);
invokeBinaryCommand(*m_httpClient, url, req, res); invokeBinaryCommand(*m_httpClient, url, req, res);
ec = interpretResponseStatus(res.status); ec = interpretResponseStatus(res.status);
} catch (const ConnectException&) {
ec = make_error_code(error::CONNECT_ERROR);
} catch (const std::exception&) { } catch (const std::exception&) {
ec = make_error_code(error::NETWORK_ERROR); ec = make_error_code(error::NETWORK_ERROR);
} }
@ -592,6 +613,8 @@ std::error_code NodeRpcProxy::jsonCommand(const std::string& url, const Request&
EventLock eventLock(*m_httpEvent); EventLock eventLock(*m_httpEvent);
invokeJsonCommand(*m_httpClient, url, req, res); invokeJsonCommand(*m_httpClient, url, req, res);
ec = interpretResponseStatus(res.status); ec = interpretResponseStatus(res.status);
} catch (const ConnectException&) {
ec = make_error_code(error::CONNECT_ERROR);
} catch (const std::exception&) { } catch (const std::exception&) {
ec = make_error_code(error::NETWORK_ERROR); ec = make_error_code(error::NETWORK_ERROR);
} }
@ -627,6 +650,8 @@ std::error_code NodeRpcProxy::jsonRpcCommand(const std::string& method, const Re
ec = interpretResponseStatus(res.status); ec = interpretResponseStatus(res.status);
} }
} }
} catch (const ConnectException&) {
ec = make_error_code(error::CONNECT_ERROR);
} catch (const std::exception&) { } catch (const std::exception&) {
ec = make_error_code(error::NETWORK_ERROR); ec = make_error_code(error::NETWORK_ERROR);
} }

View file

@ -36,6 +36,12 @@ namespace CryptoNote {
class HttpClient; class HttpClient;
class INodeRpcProxyObserver {
public:
virtual ~INodeRpcProxyObserver() {}
virtual void connectionStatusUpdated(bool connected) {}
};
class NodeRpcProxy : public CryptoNote::INode { class NodeRpcProxy : public CryptoNote::INode {
public: public:
NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort); NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort);
@ -44,6 +50,9 @@ public:
virtual bool addObserver(CryptoNote::INodeObserver* observer); virtual bool addObserver(CryptoNote::INodeObserver* observer);
virtual bool removeObserver(CryptoNote::INodeObserver* observer); virtual bool removeObserver(CryptoNote::INodeObserver* observer);
virtual bool addObserver(CryptoNote::INodeRpcProxyObserver* observer);
virtual bool removeObserver(CryptoNote::INodeRpcProxyObserver* observer);
virtual void init(const Callback& callback); virtual void init(const Callback& callback);
virtual bool shutdown(); virtual bool shutdown();
@ -116,6 +125,7 @@ private:
System::Dispatcher* m_dispatcher = nullptr; System::Dispatcher* m_dispatcher = nullptr;
System::ContextGroup* m_context_group = nullptr; System::ContextGroup* m_context_group = nullptr;
Tools::ObserverManager<CryptoNote::INodeObserver> m_observerManager; Tools::ObserverManager<CryptoNote::INodeObserver> m_observerManager;
Tools::ObserverManager<CryptoNote::INodeRpcProxyObserver> m_rpcProxyObserverManager;
const std::string m_nodeHost; const std::string m_nodeHost;
const unsigned short m_nodePort; const unsigned short m_nodePort;
@ -134,6 +144,8 @@ private:
//protect it with mutex if decided to add worker threads //protect it with mutex if decided to add worker threads
Crypto::Hash m_lastKnowHash; Crypto::Hash m_lastKnowHash;
std::atomic<uint64_t> m_lastLocalBlockTimestamp; std::atomic<uint64_t> m_lastLocalBlockTimestamp;
bool m_connected;
}; };
} }

View file

@ -64,6 +64,8 @@ inline std::string get_protocol_state_string(CryptoNoteConnectionContext::state
return "state_normal"; return "state_normal";
case CryptoNoteConnectionContext::state_sync_required: case CryptoNoteConnectionContext::state_sync_required:
return "state_sync_required"; return "state_sync_required";
case CryptoNoteConnectionContext::state_pool_sync_required:
return "state_pool_sync_required";
case CryptoNoteConnectionContext::state_shutdown: case CryptoNoteConnectionContext::state_shutdown:
return "state_shutdown"; return "state_shutdown";
default: default:

View file

@ -68,13 +68,13 @@ void LevinProtocol::sendMessage(uint32_t command, const BinaryArray& out, bool n
stream.writeSome(&head, sizeof(head)); stream.writeSome(&head, sizeof(head));
stream.writeSome(out.data(), out.size()); stream.writeSome(out.data(), out.size());
m_conn.write(writeBuffer.data(), writeBuffer.size()); writeStrict(writeBuffer.data(), writeBuffer.size());
} }
bool LevinProtocol::readCommand(Command& cmd) { bool LevinProtocol::readCommand(Command& cmd) {
bucket_head2 head = { 0 }; bucket_head2 head = { 0 };
if (!readStrict(&head, sizeof(head))) { if (!readStrict(reinterpret_cast<uint8_t*>(&head), sizeof(head))) {
return false; return false;
} }
@ -113,18 +113,27 @@ void LevinProtocol::sendReply(uint32_t command, const BinaryArray& out, int32_t
head.m_flags = LEVIN_PACKET_RESPONSE; head.m_flags = LEVIN_PACKET_RESPONSE;
head.m_return_code = returnCode; head.m_return_code = returnCode;
m_conn.write(reinterpret_cast<const uint8_t*>(&head), sizeof(head)); BinaryArray writeBuffer;
if (out.size() > 0) { writeBuffer.reserve(sizeof(head) + out.size());
m_conn.write(reinterpret_cast<const uint8_t*>(out.data()), out.size());
Common::VectorOutputStream stream(writeBuffer);
stream.writeSome(&head, sizeof(head));
stream.writeSome(out.data(), out.size());
writeStrict(writeBuffer.data(), writeBuffer.size());
}
void LevinProtocol::writeStrict(const uint8_t* ptr, size_t size) {
size_t offset = 0;
while (offset < size) {
offset += m_conn.write(ptr + offset, size - offset);
} }
} }
bool LevinProtocol::readStrict(void* ptr, size_t size) { bool LevinProtocol::readStrict(uint8_t* ptr, size_t size) {
char* pos = reinterpret_cast<char*>(ptr);
size_t offset = 0; size_t offset = 0;
while (offset < size) { while (offset < size) {
size_t read = m_conn.read(reinterpret_cast<uint8_t*>(pos + offset), size - offset); size_t read = m_conn.read(ptr + offset, size - offset);
if (read == 0) { if (read == 0) {
return false; return false;
} }

View file

@ -105,7 +105,8 @@ public:
private: private:
bool readStrict(void* ptr, size_t size); bool readStrict(uint8_t* ptr, size_t size);
void writeStrict(const uint8_t* ptr, size_t size);
System::TcpConnection& m_conn; System::TcpConnection& m_conn;
}; };

View file

@ -731,7 +731,8 @@ namespace CryptoNote
}); });
System::Context<> timeoutContext(m_dispatcher, [&] { System::Context<> timeoutContext(m_dispatcher, [&] {
System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout)); // Here we use connection_timeout * 3, one for this handshake, and two for back ping from peer.
System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout * 3));
handshakeContext.interrupt(); handshakeContext.interrupt();
logger(DEBUGGING) << "Handshake with " << na << " timed out, interrupt it"; logger(DEBUGGING) << "Handshake with " << na << " timed out, interrupt it";
}); });
@ -1049,7 +1050,9 @@ namespace CryptoNote
net_connection_id excludeId = excludeConnection ? *excludeConnection : boost::value_initialized<net_connection_id>(); net_connection_id excludeId = excludeConnection ? *excludeConnection : boost::value_initialized<net_connection_id>();
forEachConnection([&](P2pConnectionContext& conn) { forEachConnection([&](P2pConnectionContext& conn) {
if (conn.peerId && conn.m_connection_id != excludeId) { if (conn.peerId && conn.m_connection_id != excludeId &&
(conn.m_state == CryptoNoteConnectionContext::state_normal ||
conn.m_state == CryptoNoteConnectionContext::state_synchronizing)) {
conn.pushMessage(P2pMessage(P2pMessage::NOTIFY, command, data_buff)); conn.pushMessage(P2pMessage(P2pMessage::NOTIFY, command, data_buff));
} }
}); });
@ -1068,41 +1071,48 @@ namespace CryptoNote
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
bool NodeServer::try_ping(basic_node_data& node_data, P2pConnectionContext& context) bool NodeServer::try_ping(basic_node_data& node_data, P2pConnectionContext& context) {
{ if(!node_data.my_port) {
if(!node_data.my_port)
return false; return false;
uint32_t actual_ip = context.m_remote_ip;
if(!m_peerlist.is_ip_allowed(actual_ip))
return false;
std::string ip = Common::ipAddressToString(actual_ip);
auto port = node_data.my_port;
PeerIdType pr = node_data.peer_id;
try {
System::TcpConnector connector(m_dispatcher);
System::TcpConnection conn = connector.connect(System::Ipv4Address(ip), static_cast<uint16_t>(port));
LevinProtocol proto(conn);
COMMAND_PING::request req;
COMMAND_PING::response rsp;
proto.invoke(COMMAND_PING::ID, req, rsp);
if (rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) {
logger(Logging::DEBUGGING) << context << "back ping invoke wrong response \"" << rsp.status << "\" from" << ip << ":" << port << ", hsh_peer_id=" << pr << ", rsp.peer_id=" << rsp.peer_id;
return false;
}
return true;
} catch (std::exception& e) {
logger(Logging::DEBUGGING) << context << "back ping to " << ip << ":" << port << " failed: " << e.what();
} }
return false; uint32_t actual_ip = context.m_remote_ip;
if(!m_peerlist.is_ip_allowed(actual_ip)) {
return false;
}
auto ip = Common::ipAddressToString(actual_ip);
auto port = node_data.my_port;
auto peerId = node_data.peer_id;
try {
COMMAND_PING::request req;
COMMAND_PING::response rsp;
System::Context<> pingContext(m_dispatcher, [&] {
System::TcpConnector connector(m_dispatcher);
auto connection = connector.connect(System::Ipv4Address(ip), static_cast<uint16_t>(port));
LevinProtocol(connection).invoke(COMMAND_PING::ID, req, rsp);
});
System::Context<> timeoutContext(m_dispatcher, [&] {
System::Timer(m_dispatcher).sleep(std::chrono::milliseconds(m_config.m_net_config.connection_timeout * 2));
logger(DEBUGGING) << context << "Back ping timed out" << ip << ":" << port;
pingContext.interrupt();
});
pingContext.get();
if (rsp.status != PING_OK_RESPONSE_STATUS_TEXT || peerId != rsp.peer_id) {
logger(DEBUGGING) << context << "Back ping invoke wrong response \"" << rsp.status << "\" from" << ip
<< ":" << port << ", hsh_peer_id=" << peerId << ", rsp.peer_id=" << rsp.peer_id;
return false;
}
} catch (std::exception& e) {
logger(DEBUGGING) << context << "Back ping connection to " << ip << ":" << port << " failed: " << e.what();
return false;
}
return true;
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
@ -1166,7 +1176,7 @@ namespace CryptoNote
pe.id = peer_id_l; pe.id = peer_id_l;
m_peerlist.append_with_peer_white(pe); m_peerlist.append_with_peer_white(pe);
logger(Logging::TRACE) << context << "PING SUCCESS " << Common::ipAddressToString(context.m_remote_ip) << ":" << port_l; logger(Logging::TRACE) << context << "BACK PING SUCCESS, " << Common::ipAddressToString(context.m_remote_ip) << ":" << port_l << " added to whitelist";
} }
} }

View file

@ -74,8 +74,8 @@ void NetNodeConfig::initOptions(boost::program_options::options_description& des
} }
NetNodeConfig::NetNodeConfig() { NetNodeConfig::NetNodeConfig() {
bindIp = "0.0.0.0"; bindIp = "";
bindPort = P2P_DEFAULT_PORT; bindPort = 0;
externalPort = 0; externalPort = 0;
allowLocalIp = false; allowLocalIp = false;
hideMyPort = false; hideMyPort = false;
@ -84,23 +84,37 @@ NetNodeConfig::NetNodeConfig() {
bool NetNodeConfig::init(const boost::program_options::variables_map& vm) bool NetNodeConfig::init(const boost::program_options::variables_map& vm)
{ {
bindIp = command_line::get_arg(vm, arg_p2p_bind_ip); if (vm.count(arg_p2p_bind_ip.name) != 0 && (!vm[arg_p2p_bind_ip.name].defaulted() || bindIp.empty())) {
bindPort = command_line::get_arg(vm, arg_p2p_bind_port); bindIp = command_line::get_arg(vm, arg_p2p_bind_ip);
externalPort = command_line::get_arg(vm, arg_p2p_external_port); }
allowLocalIp = command_line::get_arg(vm, arg_p2p_allow_local_ip);
configFolder = command_line::get_arg(vm, command_line::arg_data_dir); if (vm.count(arg_p2p_bind_port.name) != 0 && (!vm[arg_p2p_bind_port.name].defaulted() || bindPort == 0)) {
bindPort = command_line::get_arg(vm, arg_p2p_bind_port);
}
if (vm.count(arg_p2p_external_port.name) != 0 && (!vm[arg_p2p_external_port.name].defaulted() || externalPort == 0)) {
externalPort = command_line::get_arg(vm, arg_p2p_external_port);
}
if (vm.count(arg_p2p_allow_local_ip.name) != 0 && (!vm[arg_p2p_allow_local_ip.name].defaulted() || !allowLocalIp)) {
allowLocalIp = command_line::get_arg(vm, arg_p2p_allow_local_ip);
}
if (vm.count(command_line::arg_data_dir.name) != 0 && (!vm[command_line::arg_data_dir.name].defaulted() || configFolder == Tools::getDefaultDataDirectory())) {
configFolder = command_line::get_arg(vm, command_line::arg_data_dir);
}
p2pStateFilename = CryptoNote::parameters::P2P_NET_DATA_FILENAME; p2pStateFilename = CryptoNote::parameters::P2P_NET_DATA_FILENAME;
if (command_line::has_arg(vm, arg_p2p_add_peer)) if (command_line::has_arg(vm, arg_p2p_add_peer)) {
{
std::vector<std::string> perrs = command_line::get_arg(vm, arg_p2p_add_peer); std::vector<std::string> perrs = command_line::get_arg(vm, arg_p2p_add_peer);
for(const std::string& pr_str: perrs) for(const std::string& pr_str: perrs) {
{
PeerlistEntry pe = boost::value_initialized<PeerlistEntry>(); PeerlistEntry pe = boost::value_initialized<PeerlistEntry>();
pe.id = Crypto::rand<uint64_t>(); pe.id = Crypto::rand<uint64_t>();
if (!parsePeerFromString(pe.adr, pr_str)) { if (!parsePeerFromString(pe.adr, pr_str)) {
return false; return false;
} }
peers.push_back(pe); peers.push_back(pe);
} }
} }
@ -120,8 +134,9 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm)
return false; return false;
} }
if(command_line::has_arg(vm, arg_p2p_hide_my_port)) if (command_line::has_arg(vm, arg_p2p_hide_my_port)) {
hideMyPort = true; hideMyPort = true;
}
return true; return true;
} }

View file

@ -36,6 +36,8 @@ Configuration::Configuration() {
testnet = false; testnet = false;
printAddresses = false; printAddresses = false;
logLevel = Logging::INFO; logLevel = Logging::INFO;
bindAddress = "";
bindPort = 0;
} }
void Configuration::initOptions(boost::program_options::options_description& desc) { void Configuration::initOptions(boost::program_options::options_description& desc) {
@ -57,15 +59,15 @@ void Configuration::initOptions(boost::program_options::options_description& des
} }
void Configuration::init(const boost::program_options::variables_map& options) { void Configuration::init(const boost::program_options::variables_map& options) {
if (options.count("daemon")) { if (options.count("daemon") != 0) {
daemonize = true; daemonize = true;
} }
if (options.count("register-service")) { if (options.count("register-service") != 0) {
registerService = true; registerService = true;
} }
if (options.count("unregister-service")) { if (options.count("unregister-service") != 0) {
unregisterService = true; unregisterService = true;
} }
@ -73,15 +75,15 @@ void Configuration::init(const boost::program_options::variables_map& options) {
throw ConfigurationError("It's impossible to use both \"register-service\" and \"unregister-service\" at the same time"); throw ConfigurationError("It's impossible to use both \"register-service\" and \"unregister-service\" at the same time");
} }
if (options.count("testnet")) { if (options.count("testnet") != 0) {
testnet = true; testnet = true;
} }
if (options.count("log-file")) { if (options.count("log-file") != 0) {
logFile = options["log-file"].as<std::string>(); logFile = options["log-file"].as<std::string>();
} }
if (options.count("log-level")) { if (options.count("log-level") != 0) {
logLevel = options["log-level"].as<size_t>(); logLevel = options["log-level"].as<size_t>();
if (logLevel > Logging::TRACE) { if (logLevel > Logging::TRACE) {
std::string error = "log-level option must be in " + std::to_string(Logging::FATAL) + ".." + std::to_string(Logging::TRACE) + " interval"; std::string error = "log-level option must be in " + std::to_string(Logging::FATAL) + ".." + std::to_string(Logging::TRACE) + " interval";
@ -89,31 +91,31 @@ void Configuration::init(const boost::program_options::variables_map& options) {
} }
} }
if (options.count("server-root")) { if (options.count("server-root") != 0) {
serverRoot = options["server-root"].as<std::string>(); serverRoot = options["server-root"].as<std::string>();
} }
if (options.count("bind-address")) { if (options.count("bind-address") != 0 && (!options["bind-address"].defaulted() || bindAddress.empty())) {
bindAddress = options["bind-address"].as<std::string>(); bindAddress = options["bind-address"].as<std::string>();
} }
if (options.count("bind-port")) { if (options.count("bind-port") != 0 && (!options["bind-port"].defaulted() || bindPort == 0)) {
bindPort = options["bind-port"].as<uint16_t>(); bindPort = options["bind-port"].as<uint16_t>();
} }
if (options.count("wallet-file")) { if (options.count("wallet-file") != 0) {
walletFile = options["wallet-file"].as<std::string>(); walletFile = options["wallet-file"].as<std::string>();
} }
if (options.count("wallet-password")) { if (options.count("wallet-password") != 0) {
walletPassword = options["wallet-password"].as<std::string>(); walletPassword = options["wallet-password"].as<std::string>();
} }
if (options.count("generate-wallet")) { if (options.count("generate-wallet") != 0) {
generateNewWallet = true; generateNewWallet = true;
} }
if (options.count("address")) { if (options.count("address") != 0) {
printAddresses = true; printAddresses = true;
} }

View file

@ -22,8 +22,8 @@ namespace PaymentService {
namespace po = boost::program_options; namespace po = boost::program_options;
RpcNodeConfiguration::RpcNodeConfiguration() { RpcNodeConfiguration::RpcNodeConfiguration() {
daemonHost = "127.0.0.1"; daemonHost = "";
daemonPort = 8081; daemonPort = 0;
} }
void RpcNodeConfiguration::initOptions(boost::program_options::options_description& desc) { void RpcNodeConfiguration::initOptions(boost::program_options::options_description& desc) {
@ -33,11 +33,11 @@ void RpcNodeConfiguration::initOptions(boost::program_options::options_descripti
} }
void RpcNodeConfiguration::init(const boost::program_options::variables_map& options) { void RpcNodeConfiguration::init(const boost::program_options::variables_map& options) {
if (options.count("daemon-address")) { if (options.count("daemon-address") != 0 && (!options["daemon-address"].defaulted() || daemonHost.empty())) {
daemonHost = options["daemon-address"].as<std::string>(); daemonHost = options["daemon-address"].as<std::string>();
} }
if (options.count("daemon-port")) { if (options.count("daemon-port") != 0 && (!options["daemon-port"].defaulted() || daemonPort == 0)) {
daemonPort = options["daemon-port"].as<uint16_t>(); daemonPort = options["daemon-port"].as<uint16_t>();
} }
} }

View file

@ -0,0 +1,34 @@
// 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 <future>
namespace System {
namespace Detail {
template<class T> using Future = std::future<T>;
template<class T> Future<T> async(std::function<T()>&& operation) {
return std::async(std::launch::async, std::move(operation));
}
}
}

View file

@ -0,0 +1,170 @@
// 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 <condition_variable>
#include <mutex>
#include <thread>
namespace System {
namespace Detail {
namespace {
enum class State : unsigned {
STARTED,
COMPLETED,
CONSUMED
};
}
// Simplest possible future implementation. The reason why this class even exist is because currenty std future has a
// memory corrupting bug on OSX. Spawn a new thread, execute procedure in it, get result, and wait thread to shut down.
// Actualy, this is what libstdc++'s std::future is doing.
template<class T> class Future {
public:
// Create a new thread, and run `operation` in it.
explicit Future(std::function<T()>&& operation) : procedure(std::move(operation)), state(State::STARTED), worker{[this] { asyncOp(); }} {
}
// Wait for async op to complete, then if thread wasn't detached, join it.
~Future() {
wait();
if (worker.joinable()) {
worker.join();
}
}
// Get result of async operation. UB if called more than once.
T get() const {
assert(state != State::CONSUMED);
wait();
state = State::CONSUMED;
if (currentException != nullptr) {
std::rethrow_exception(currentException);
}
return std::move(result);
}
// Wait for async operation to complete, if op is already completed, return immediately.
void wait() const {
std::unique_lock<std::mutex> guard(operationMutex);
while (state == State::STARTED) {
operationCondition.wait(guard);
}
}
bool valid() const {
std::unique_lock<std::mutex> guard(operationMutex);
return state != State::CONSUMED;
}
private:
// This function is executed in a separate thread.
void asyncOp() {
try {
assert(procedure != nullptr);
result = procedure();
} catch (...) {
currentException = std::current_exception();
}
std::unique_lock<std::mutex> guard(operationMutex);
state = State::COMPLETED;
operationCondition.notify_one();
}
mutable T result;
std::function<T()> procedure;
std::exception_ptr currentException;
mutable std::mutex operationMutex;
mutable std::condition_variable operationCondition;
mutable State state;
std::thread worker;
};
template<> class Future<void> {
public:
// Create a new thread, and run `operation` in it.
explicit Future(std::function<void()>&& operation) : procedure(std::move(operation)), state(State::STARTED), worker{[this] { asyncOp(); }} {
}
// Wait for async op to complete, then if thread wasn't detached, join it.
~Future() {
wait();
if (worker.joinable()) {
worker.join();
}
}
// Get result of async operation. UB if called more than once.
void get() const {
assert(state != State::CONSUMED);
wait();
state = State::CONSUMED;
if (currentException != nullptr) {
std::rethrow_exception(currentException);
}
}
// Wait for async operation to complete, if op is already completed, return immediately.
void wait() const {
std::unique_lock<std::mutex> guard(operationMutex);
while (state == State::STARTED) {
operationCondition.wait(guard);
}
}
bool valid() const {
std::unique_lock<std::mutex> guard(operationMutex);
return state != State::CONSUMED;
}
private:
// This function is executed in a separate thread.
void asyncOp() {
try {
assert(procedure != nullptr);
procedure();
} catch (...) {
currentException = std::current_exception();
}
std::unique_lock<std::mutex> guard(operationMutex);
state = State::COMPLETED;
operationCondition.notify_one();
}
std::function<void()> procedure;
std::exception_ptr currentException;
mutable std::mutex operationMutex;
mutable std::condition_variable operationCondition;
mutable State state;
std::thread worker;
};
template<class T> std::function<T()> async(std::function<T()>&& operation) {
return operation;
}
}
}

View file

@ -0,0 +1,35 @@
// 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 <future>
namespace System {
namespace Detail {
template<class T> using Future = std::future<T>;
template<class T> Future<T> async(std::function<T()>&& operation) {
return std::async(std::launch::async, std::move(operation));
}
}
}

View file

@ -52,10 +52,18 @@ void HttpClient::request(const HttpRequest &req, HttpResponse &res) {
} }
void HttpClient::connect() { void HttpClient::connect() {
auto ipAddr = System::Ipv4Resolver(m_dispatcher).resolve(m_address); try {
m_connection = System::TcpConnector(m_dispatcher).connect(ipAddr, m_port); auto ipAddr = System::Ipv4Resolver(m_dispatcher).resolve(m_address);
m_streamBuf.reset(new System::TcpStreambuf(m_connection)); m_connection = System::TcpConnector(m_dispatcher).connect(ipAddr, m_port);
m_connected = true; m_streamBuf.reset(new System::TcpStreambuf(m_connection));
m_connected = true;
} catch (const std::exception& e) {
throw ConnectException(e.what());
}
}
bool HttpClient::isConnected() const {
return m_connected;
} }
void HttpClient::disconnect() { void HttpClient::disconnect() {
@ -75,4 +83,7 @@ void HttpClient::disconnect() {
m_connected = false; m_connected = false;
} }
ConnectException::ConnectException(const std::string& whatArg) : std::runtime_error(whatArg.c_str()) {
}
} }

View file

@ -28,6 +28,11 @@
namespace CryptoNote { namespace CryptoNote {
class ConnectException : public std::runtime_error {
public:
ConnectException(const std::string& whatArg);
};
class HttpClient { class HttpClient {
public: public:
@ -35,8 +40,9 @@ public:
~HttpClient(); ~HttpClient();
void request(const HttpRequest& req, HttpResponse& res); void request(const HttpRequest& req, HttpResponse& res);
private: bool isConnected() const;
private:
void connect(); void connect();
void disconnect(); void disconnect();

View file

@ -420,10 +420,24 @@ void printListTransfersItem(LoggerRef& logger, const WalletLegacyTransaction& tx
logger(INFO, rowColor) << " "; //just to make logger print one endline logger(INFO, rowColor) << " "; //just to make logger print one endline
} }
std::string prepareWalletAddressFilename(const std::string& walletBaseName) {
return walletBaseName + ".address";
} }
std::string simple_wallet::get_commands_str() bool writeAddressFile(const std::string& addressFilename, const std::string& address) {
{ std::ofstream addressFile(addressFilename, std::ios::out | std::ios::trunc | std::ios::binary);
if (!addressFile.good()) {
return false;
}
addressFile << address;
return true;
}
}
std::string simple_wallet::get_commands_str() {
std::stringstream ss; std::stringstream ss;
ss << "Commands: " << ENDL; ss << "Commands: " << ENDL;
std::string usage = m_consoleHandler.getUsage(); std::string usage = m_consoleHandler.getUsage();
@ -433,8 +447,7 @@ std::string simple_wallet::get_commands_str()
return ss.str(); return ss.str();
} }
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/) bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/) {
{
success_msg_writer() << get_commands_str(); success_msg_writer() << get_commands_str();
return true; return true;
} }
@ -451,8 +464,7 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C
logManager(log), logManager(log),
logger(log, "simplewallet"), logger(log, "simplewallet"),
m_refresh_progress_reporter(*this), m_refresh_progress_reporter(*this),
m_initResultPromise(nullptr) m_initResultPromise(nullptr) {
{
m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [<number_of_threads>] - Start mining in daemon"); m_consoleHandler.setHandler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining [<number_of_threads>] - Start mining in daemon");
m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); m_consoleHandler.setHandler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon");
//m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); //m_consoleHandler.setHandler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance");
@ -473,22 +485,19 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C
m_consoleHandler.setHandler("exit", boost::bind(&simple_wallet::exit, this, _1), "Close wallet"); m_consoleHandler.setHandler("exit", boost::bind(&simple_wallet::exit, this, _1), "Close wallet");
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::set_log(const std::vector<std::string> &args) bool simple_wallet::set_log(const std::vector<std::string> &args) {
{ if (args.size() != 1) {
if (args.size() != 1)
{
fail_msg_writer() << "use: set_log <log_level_number_0-4>"; fail_msg_writer() << "use: set_log <log_level_number_0-4>";
return true; return true;
} }
uint16_t l = 0; uint16_t l = 0;
if (!Common::fromString(args[0], l)) if (!Common::fromString(args[0], l)) {
{
fail_msg_writer() << "wrong number format, use: set_log <log_level_number_0-4>"; fail_msg_writer() << "wrong number format, use: set_log <log_level_number_0-4>";
return true; return true;
} }
if (l > Logging::TRACE) if (l > Logging::TRACE) {
{
fail_msg_writer() << "wrong number range, use: set_log <log_level_number_0-4>"; fail_msg_writer() << "wrong number range, use: set_log <log_level_number_0-4>";
return true; return true;
} }
@ -497,8 +506,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm) bool simple_wallet::init(const boost::program_options::variables_map& vm) {
{
handle_command_line(vm); handle_command_line(vm);
if (!m_daemon_address.empty() && (!m_daemon_host.empty() || 0 != m_daemon_port)) { if (!m_daemon_address.empty() && (!m_daemon_host.empty() || 0 != m_daemon_port)) {
@ -582,6 +590,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
std::promise<std::error_code> errorPromise; std::promise<std::error_code> errorPromise;
std::future<std::error_code> f_error = errorPromise.get_future(); std::future<std::error_code> f_error = errorPromise.get_future();
auto callback = [&errorPromise](std::error_code e) {errorPromise.set_value(e); }; auto callback = [&errorPromise](std::error_code e) {errorPromise.set_value(e); };
m_node->addObserver(static_cast<INodeRpcProxyObserver*>(this));
m_node->init(callback); m_node->init(callback);
auto error = f_error.get(); auto error = f_error.get();
if (error) { if (error) {
@ -589,13 +599,23 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false; return false;
} }
if (!m_generate_new.empty()) if (!m_generate_new.empty()) {
{ std::string walletAddressFile = prepareWalletAddressFilename(m_generate_new);
bool r = new_wallet(walletFileName, pwd_container.password()); boost::system::error_code ignore;
if (!(r)) { logger(ERROR, BRIGHT_RED) << "account creation failed"; return false; } if (boost::filesystem::exists(walletAddressFile, ignore)) {
} logger(ERROR, BRIGHT_RED) << "Address file already exists: " + walletAddressFile;
else return false;
{ }
if (!new_wallet(walletFileName, pwd_container.password())) {
logger(ERROR, BRIGHT_RED) << "account creation failed";
return false;
}
if (!writeAddressFile(walletAddressFile, m_wallet->getAddress())) {
logger(WARNING, BRIGHT_RED) << "Couldn't write wallet address file: " + walletAddressFile;
}
} else {
m_wallet.reset(new WalletLegacy(m_currency, *m_node)); m_wallet.reset(new WalletLegacy(m_currency, *m_node));
try { try {
@ -606,7 +626,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
} }
m_wallet->addObserver(this); m_wallet->addObserver(this);
m_node->addObserver(this); m_node->addObserver(static_cast<INodeObserver*>(this));
logger(INFO, BRIGHT_WHITE) << "Opened wallet: " << m_wallet->getAddress(); logger(INFO, BRIGHT_WHITE) << "Opened wallet: " << m_wallet->getAddress();
@ -619,16 +639,18 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::deinit() bool simple_wallet::deinit() {
{ m_wallet->removeObserver(this);
m_node->removeObserver(static_cast<INodeObserver*>(this));
m_node->removeObserver(static_cast<INodeRpcProxyObserver*>(this));
if (!m_wallet.get()) if (!m_wallet.get())
return true; return true;
return close_wallet(); return close_wallet();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm) void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm) {
{
m_wallet_file_arg = command_line::get_arg(vm, arg_wallet_file); m_wallet_file_arg = command_line::get_arg(vm, arg_wallet_file);
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_daemon_address = command_line::get_arg(vm, arg_daemon_address); m_daemon_address = command_line::get_arg(vm, arg_daemon_address);
@ -636,15 +658,13 @@ void simple_wallet::handle_command_line(const boost::program_options::variables_
m_daemon_port = command_line::get_arg(vm, arg_daemon_port); m_daemon_port = command_line::get_arg(vm, arg_daemon_port);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password) bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string& password) {
{
m_wallet_file = wallet_file; m_wallet_file = wallet_file;
m_wallet.reset(new WalletLegacy(m_currency, *m_node.get())); m_wallet.reset(new WalletLegacy(m_currency, *m_node.get()));
m_node->addObserver(this); m_node->addObserver(static_cast<INodeObserver*>(this));
m_wallet->addObserver(this); m_wallet->addObserver(this);
try try {
{
m_initResultPromise.reset(new std::promise<std::error_code>()); m_initResultPromise.reset(new std::promise<std::error_code>());
std::future<std::error_code> f_initError = m_initResultPromise->get_future(); std::future<std::error_code> f_initError = m_initResultPromise->get_future();
m_wallet->initAndGenerate(password); m_wallet->initAndGenerate(password);
@ -669,8 +689,7 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string
"Generated new wallet: " << m_wallet->getAddress() << std::endl << "Generated new wallet: " << m_wallet->getAddress() << std::endl <<
"view key: " << Common::podToHex(keys.viewSecretKey); "view key: " << Common::podToHex(keys.viewSecretKey);
} }
catch (const std::exception& e) catch (const std::exception& e) {
{
fail_msg_writer() << "failed to generate new wallet: " << e.what(); fail_msg_writer() << "failed to generate new wallet: " << e.what();
return false; return false;
} }
@ -690,9 +709,7 @@ bool simple_wallet::close_wallet()
{ {
try { try {
CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file);
} } catch (const std::exception& e) {
catch (const std::exception& e)
{
fail_msg_writer() << e.what(); fail_msg_writer() << e.what();
return false; return false;
} }
@ -706,13 +723,10 @@ bool simple_wallet::close_wallet()
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::save(const std::vector<std::string> &args) bool simple_wallet::save(const std::vector<std::string> &args)
{ {
try try {
{
CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file); CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file);
success_msg_writer() << "Wallet data saved"; success_msg_writer() << "Wallet data saved";
} } catch (const std::exception& e) {
catch (const std::exception& e)
{
fail_msg_writer() << e.what(); fail_msg_writer() << e.what();
} }
@ -725,31 +739,24 @@ bool simple_wallet::reset(const std::vector<std::string> &args) {
return true; return true;
} }
bool simple_wallet::start_mining(const std::vector<std::string>& args) bool simple_wallet::start_mining(const std::vector<std::string>& args) {
{
COMMAND_RPC_START_MINING::request req; COMMAND_RPC_START_MINING::request req;
req.miner_address = m_wallet->getAddress(); req.miner_address = m_wallet->getAddress();
bool ok = true; bool ok = true;
size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast<unsigned>(2)); size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast<unsigned>(2));
if (0 == args.size()) if (0 == args.size()) {
{
req.threads_count = 1; req.threads_count = 1;
} } else if (1 == args.size()) {
else if (1 == args.size())
{
uint16_t num = 1; uint16_t num = 1;
ok = Common::fromString(args[0], num); ok = Common::fromString(args[0], num);
ok = ok && (1 <= num && num <= max_mining_threads_count); ok = ok && (1 <= num && num <= max_mining_threads_count);
req.threads_count = num; req.threads_count = num;
} } else {
else
{
ok = false; ok = false;
} }
if (!ok) if (!ok) {
{
fail_msg_writer() << "invalid arguments. Please use start_mining [<number_of_threads>], " << fail_msg_writer() << "invalid arguments. Please use start_mining [<number_of_threads>], " <<
"<number_of_threads> should be from 1 to " << max_mining_threads_count; "<number_of_threads> should be from 1 to " << max_mining_threads_count;
return true; return true;
@ -769,6 +776,8 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
else else
fail_msg_writer() << "mining has NOT been started: " << err; fail_msg_writer() << "mining has NOT been started: " << err;
} catch (const ConnectException&) {
printConnectionError();
} catch (const std::exception& e) { } catch (const std::exception& e) {
fail_msg_writer() << "Failed to invoke rpc method: " << e.what(); fail_msg_writer() << "Failed to invoke rpc method: " << e.what();
} }
@ -783,12 +792,15 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
try { try {
HttpClient httpClient(m_dispatcher, m_daemon_host, m_daemon_port); HttpClient httpClient(m_dispatcher, m_daemon_host, m_daemon_port);
invokeJsonCommand(httpClient, "/stop_mining", req, res); invokeJsonCommand(httpClient, "/stop_mining", req, res);
std::string err = interpret_rpc_response(true, res.status); std::string err = interpret_rpc_response(true, res.status);
if (err.empty()) if (err.empty())
success_msg_writer() << "Mining stopped in daemon"; success_msg_writer() << "Mining stopped in daemon";
else else
fail_msg_writer() << "mining has NOT been stopped: " << err; fail_msg_writer() << "mining has NOT been stopped: " << err;
} catch (const ConnectException&) {
printConnectionError();
} catch (const std::exception& e) { } catch (const std::exception& e) {
fail_msg_writer() << "Failed to invoke rpc method: " << e.what(); fail_msg_writer() << "Failed to invoke rpc method: " << e.what();
} }
@ -802,13 +814,19 @@ void simple_wallet::initCompleted(std::error_code result) {
} }
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::localBlockchainUpdated(uint32_t height) void simple_wallet::localBlockchainUpdated(uint32_t height) {
{
m_refresh_progress_reporter.update(height, false); m_refresh_progress_reporter.update(height, false);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transactionId) void simple_wallet::connectionStatusUpdated(bool connected) {
{ if (connected) {
logger(INFO, GREEN) << "Wallet connected to daemon.";
} else {
printConnectionError();
}
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transactionId) {
WalletLegacyTransaction txInfo; WalletLegacyTransaction txInfo;
m_wallet->getTransaction(transactionId, txInfo); m_wallet->getTransaction(transactionId, txInfo);
@ -836,16 +854,14 @@ void simple_wallet::externalTransactionCreated(CryptoNote::TransactionId transac
} }
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/) bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/) {
{
success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) << success_msg_writer() << "available balance: " << m_currency.formatAmount(m_wallet->actualBalance()) <<
", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance()); ", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance());
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args) bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args) {
{
bool hasTransfers = false; bool hasTransfers = false;
size_t transactionsCount = m_wallet->getTransactionCount(); size_t transactionsCount = m_wallet->getTransactionCount();
for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) {
@ -888,10 +904,8 @@ bool simple_wallet::listTransfers(const std::vector<std::string>& args) {
return true; return true;
} }
bool simple_wallet::show_payments(const std::vector<std::string> &args) bool simple_wallet::show_payments(const std::vector<std::string> &args) {
{ if (args.empty()) {
if (args.empty())
{
fail_msg_writer() << "expected at least one payment ID"; fail_msg_writer() << "expected at least one payment ID";
return true; return true;
} }
@ -901,11 +915,9 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
" height\t amount "; " height\t amount ";
bool payments_found = false; bool payments_found = false;
for (const std::string& arg: args) for (const std::string& arg: args) {
{
Crypto::Hash expectedPaymentId; Crypto::Hash expectedPaymentId;
if (CryptoNote::parsePaymentId(arg, expectedPaymentId)) if (CryptoNote::parsePaymentId(arg, expectedPaymentId)) {
{
size_t transactionsCount = m_wallet->getTransactionCount(); size_t transactionsCount = m_wallet->getTransactionCount();
for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) { for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) {
WalletLegacyTransaction txInfo; WalletLegacyTransaction txInfo;
@ -938,8 +950,7 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args) bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args) {
{
try { try {
uint64_t bc_height = m_node->getLastLocalBlockHeight(); uint64_t bc_height = m_node->getLastLocalBlockHeight();
success_msg_writer() << bc_height; success_msg_writer() << bc_height;
@ -950,10 +961,8 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer(const std::vector<std::string> &args) bool simple_wallet::transfer(const std::vector<std::string> &args) {
{ try {
try
{
TransferCommand cmd(m_currency); TransferCommand cmd(m_currency);
if (!cmd.parseArguments(logger, args)) if (!cmd.parseArguments(logger, args))
@ -989,17 +998,11 @@ bool simple_wallet::transfer(const std::vector<std::string> &args)
fail_msg_writer() << e.what(); fail_msg_writer() << e.what();
return true; return true;
} }
} } catch (const std::system_error& e) {
catch (const std::system_error& e) fail_msg_writer() << e.what();
{ } catch (const std::exception& e) {
fail_msg_writer() << "unexpected error: " << e.what(); fail_msg_writer() << e.what();
} } catch (...) {
catch (const std::exception& e)
{
fail_msg_writer() << "unexpected error: " << e.what();
}
catch (...)
{
fail_msg_writer() << "unknown error"; fail_msg_writer() << "unknown error";
} }
@ -1025,9 +1028,12 @@ bool simple_wallet::process_command(const std::vector<std::string> &args) {
return m_consoleHandler.runCommand(args); return m_consoleHandler.runCommand(args);
} }
void simple_wallet::printConnectionError() const {
fail_msg_writer() << "wallet failed to connect to daemon (" << m_daemon_address << ").";
}
int main(int argc, char* argv[])
{ int main(int argc, char* argv[]) {
#ifdef WIN32 #ifdef WIN32
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif #endif
@ -1101,18 +1107,17 @@ int main(int argc, char* argv[])
if (command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_bind_port)) { if (command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_bind_port)) {
//runs wallet with rpc interface //runs wallet with rpc interface
if (!command_line::has_arg(vm, arg_wallet_file)) if (!command_line::has_arg(vm, arg_wallet_file)) {
{
logger(ERROR, BRIGHT_RED) << "Wallet file not set."; logger(ERROR, BRIGHT_RED) << "Wallet file not set.";
return 1; return 1;
} }
if (!command_line::has_arg(vm, arg_daemon_address))
{ if (!command_line::has_arg(vm, arg_daemon_address)) {
logger(ERROR, BRIGHT_RED) << "Daemon address not set."; logger(ERROR, BRIGHT_RED) << "Daemon address not set.";
return 1; return 1;
} }
if (!command_line::has_arg(vm, arg_password))
{ if (!command_line::has_arg(vm, arg_password)) {
logger(ERROR, BRIGHT_RED) << "Wallet password not set."; logger(ERROR, BRIGHT_RED) << "Wallet password not set.";
return 1; return 1;
} }
@ -1179,9 +1184,7 @@ int main(int argc, char* argv[])
logger(INFO) << "Storing wallet..."; logger(INFO) << "Storing wallet...";
CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName); CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName);
logger(INFO, BRIGHT_GREEN) << "Stored ok"; logger(INFO, BRIGHT_GREEN) << "Stored ok";
} } catch (const std::exception& e) {
catch (const std::exception& e)
{
logger(ERROR, BRIGHT_RED) << "Failed to store wallet: " << e.what(); logger(ERROR, BRIGHT_RED) << "Failed to store wallet: " << e.what();
return 1; return 1;
} }

View file

@ -23,12 +23,12 @@
#include <boost/program_options/variables_map.hpp> #include <boost/program_options/variables_map.hpp>
#include "IWalletLegacy.h" #include "IWalletLegacy.h"
#include "INode.h"
#include "PasswordContainer.h" #include "PasswordContainer.h"
#include "Common/ConsoleHandler.h" #include "Common/ConsoleHandler.h"
#include "CryptoNoteCore/CryptoNoteBasicImpl.h" #include "CryptoNoteCore/CryptoNoteBasicImpl.h"
#include "CryptoNoteCore/Currency.h" #include "CryptoNoteCore/Currency.h"
#include "NodeRpcProxy/NodeRpcProxy.h"
#include "WalletLegacy/WalletHelper.h" #include "WalletLegacy/WalletHelper.h"
#include <Logging/LoggerRef.h> #include <Logging/LoggerRef.h>
@ -42,8 +42,7 @@ namespace CryptoNote
/************************************************************************/ /************************************************************************/
/* */ /* */
/************************************************************************/ /************************************************************************/
class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletLegacyObserver class simple_wallet : public CryptoNote::INodeObserver, public CryptoNote::IWalletLegacyObserver, public CryptoNote::INodeRpcProxyObserver {
{
public: public:
simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log); simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::Currency& currency, Logging::LoggerManager& log);
@ -63,7 +62,7 @@ namespace CryptoNote
return logger(Logging::INFO, color ? Logging::GREEN : Logging::DEFAULT); return logger(Logging::INFO, color ? Logging::GREEN : Logging::DEFAULT);
} }
Logging::LoggerMessage fail_msg_writer() { Logging::LoggerMessage fail_msg_writer() const {
auto msg = logger(Logging::ERROR, Logging::BRIGHT_RED); auto msg = logger(Logging::ERROR, Logging::BRIGHT_RED);
msg << "Error: "; msg << "Error: ";
return msg; return msg;
@ -94,6 +93,8 @@ namespace CryptoNote
bool ask_wallet_create_if_needed(); bool ask_wallet_create_if_needed();
void printConnectionError() const;
//---------------- IWalletObserver ------------------------- //---------------- IWalletObserver -------------------------
virtual void initCompleted(std::error_code result) override; virtual void initCompleted(std::error_code result) override;
virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override; virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override;
@ -103,6 +104,10 @@ namespace CryptoNote
virtual void localBlockchainUpdated(uint32_t height) override; virtual void localBlockchainUpdated(uint32_t height) override;
//---------------------------------------------------------- //----------------------------------------------------------
//----------------- INodeRpcProxyObserver --------------------------
virtual void connectionStatusUpdated(bool connected) override;
//----------------------------------------------------------
friend class refresh_progress_reporter_t; friend class refresh_progress_reporter_t;
class refresh_progress_reporter_t class refresh_progress_reporter_t
@ -174,7 +179,7 @@ namespace CryptoNote
System::Dispatcher& m_dispatcher; System::Dispatcher& m_dispatcher;
Logging::LoggerRef logger; Logging::LoggerRef logger;
std::unique_ptr<CryptoNote::INode> m_node; std::unique_ptr<CryptoNote::NodeRpcProxy> m_node;
std::unique_ptr<CryptoNote::IWalletLegacy> m_wallet; std::unique_ptr<CryptoNote::IWalletLegacy> m_wallet;
refresh_progress_reporter_t m_refresh_progress_reporter; refresh_progress_reporter_t m_refresh_progress_reporter;
}; };

102
src/System/RemoteContext.h Normal file
View file

@ -0,0 +1,102 @@
// 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 <cassert>
#include <System/Dispatcher.h>
#include <System/Event.h>
#include <System/Future.h>
#include <System/InterruptedException.h>
namespace System {
template<class T = void> class RemoteContext {
public:
// Start a thread, execute operation in it, continue execution of current context.
RemoteContext(Dispatcher& d, std::function<T()>&& operation)
: dispatcher(d), event(d), procedure(std::move(operation)), future(System::Detail::async<T>([this] { return asyncProcedure(); })), interrupted(false) {
}
// Run other task on dispatcher until future is ready, then return lambda's result, or rethrow exception. UB if called more than once.
T get() const {
wait();
return future.get();
}
// Run other task on dispatcher until future is ready.
void wait() const {
while (!event.get()) {
try {
event.wait();
} catch (InterruptedException&) {
interrupted = true;
}
}
if (interrupted) {
dispatcher.interrupt();
}
}
// Wait future to complete.
~RemoteContext() {
try {
wait();
} catch (std::exception&) {
}
try {
// windows future implementation doesn't wait for completion on destruction
if (future.valid()) {
future.wait();
}
} catch (std::exception&) {
}
}
private:
struct NotifyOnDestruction {
NotifyOnDestruction(Dispatcher& d, Event& e) : dispatcher(d), event(e) {
}
~NotifyOnDestruction() {
// make a local copy; event reference will be dead when function is called
auto localEvent = &event;
// die if this throws...
dispatcher.remoteSpawn([=] { localEvent->set(); });
}
Dispatcher& dispatcher;
Event& event;
};
// This function is executed in future object
T asyncProcedure() {
NotifyOnDestruction guard(dispatcher, event);
assert(procedure != nullptr);
return procedure();
}
Dispatcher& dispatcher;
mutable Event event;
std::function<T()> procedure;
mutable System::Detail::Future<T> future;
mutable bool interrupted;
};
}

View file

@ -0,0 +1,39 @@
// 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 <utility>
namespace CryptoNote {
class IFusionManager {
public:
struct EstimateResult {
size_t belowThresholdCount;
size_t totalOutputCount;
};
virtual ~IFusionManager() {}
virtual size_t createFusionTransaction(uint64_t threshold, uint64_t mixin) = 0;
virtual bool isFusionTransaction(size_t transactionId) const = 0;
virtual EstimateResult estimate(uint64_t threshold) const = 0;
};
}

View file

@ -41,7 +41,8 @@ enum WalletErrorCodes {
OPERATION_CANCELLED, OPERATION_CANCELLED,
TX_TRANSFER_IMPOSSIBLE, TX_TRANSFER_IMPOSSIBLE,
WRONG_VERSION, WRONG_VERSION,
FEE_TOO_SMALL FEE_TOO_SMALL,
KEY_GENERATION_ERROR
}; };
// custom category: // custom category:
@ -75,6 +76,7 @@ public:
case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible"; case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible";
case WRONG_VERSION: return "Wrong version"; case WRONG_VERSION: return "Wrong version";
case FEE_TOO_SMALL: return "Transaction fee is too small"; case FEE_TOO_SMALL: return "Transaction fee is too small";
case KEY_GENERATION_ERROR: return "Cannot generate new key";
default: return "Unknown error"; default: return "Unknown error";
} }
} }

View file

@ -136,6 +136,22 @@ CryptoNote::WalletEvent makeMoneyUnlockedEvent() {
return event; return event;
} }
CryptoNote::WalletEvent makeSyncProgressUpdatedEvent(uint32_t current, uint32_t total) {
CryptoNote::WalletEvent event;
event.type = CryptoNote::WalletEventType::SYNC_PROGRESS_UPDATED;
event.synchronizationProgressUpdated.processedBlockCount = current;
event.synchronizationProgressUpdated.totalBlockCount = total;
return event;
}
CryptoNote::WalletEvent makeSyncCompletedEvent() {
CryptoNote::WalletEvent event;
event.type = CryptoNote::WalletEventType::SYNC_COMPLETED;
return event;
}
} }
namespace CryptoNote { namespace CryptoNote {
@ -162,18 +178,20 @@ WalletGreen::~WalletGreen() {
} }
void WalletGreen::initialize(const std::string& password) { void WalletGreen::initialize(const std::string& password) {
if (m_state != WalletState::NOT_INITIALIZED) { Crypto::PublicKey viewPublicKey;
throw std::system_error(make_error_code(CryptoNote::error::ALREADY_INITIALIZED)); Crypto::SecretKey viewSecretKey;
Crypto::generate_keys(viewPublicKey, viewSecretKey);
initWithKeys(viewPublicKey, viewSecretKey, password);
}
void WalletGreen::initializeWithViewKey(const Crypto::SecretKey& viewSecretKey, const std::string& password) {
Crypto::PublicKey viewPublicKey;
if (!Crypto::secret_key_to_public_key(viewSecretKey, viewPublicKey)) {
throw std::system_error(make_error_code(CryptoNote::error::KEY_GENERATION_ERROR));
} }
throwIfStopped(); initWithKeys(viewPublicKey, viewSecretKey, password);
Crypto::generate_keys(m_viewPublicKey, m_viewSecretKey);
m_password = password;
m_blockchainSynchronizer.addObserver(this);
m_state = WalletState::INITIALIZED;
} }
void WalletGreen::shutdown() { void WalletGreen::shutdown() {
@ -210,6 +228,22 @@ void WalletGreen::clearCaches() {
m_pendingBalance = 0; m_pendingBalance = 0;
} }
void WalletGreen::initWithKeys(const Crypto::PublicKey& viewPublicKey, const Crypto::SecretKey& viewSecretKey, const std::string& password) {
if (m_state != WalletState::NOT_INITIALIZED) {
throw std::system_error(make_error_code(CryptoNote::error::ALREADY_INITIALIZED));
}
throwIfStopped();
m_viewPublicKey = viewPublicKey;
m_viewSecretKey = viewSecretKey;
m_password = password;
m_blockchainSynchronizer.addObserver(this);
m_state = WalletState::INITIALIZED;
}
void WalletGreen::save(std::ostream& destination, bool saveDetails, bool saveCache) { void WalletGreen::save(std::ostream& destination, bool saveDetails, bool saveCache) {
throwIfNotInitialized(); throwIfNotInitialized();
throwIfStopped(); throwIfStopped();
@ -318,14 +352,42 @@ std::string WalletGreen::getAddress(size_t index) const {
return m_currency.accountAddressAsString({ wallet.spendPublicKey, m_viewPublicKey }); return m_currency.accountAddressAsString({ wallet.spendPublicKey, m_viewPublicKey });
} }
std::string WalletGreen::createAddress() { KeyPair WalletGreen::getAddressSpendKey(size_t index) const {
KeyPair spendKey; throwIfNotInitialized();
throwIfStopped();
Crypto::generate_keys(spendKey.publicKey, spendKey.secretKey); if (index >= m_walletsContainer.get<RandomAccessIndex>().size()) {
return createAddress(spendKey); throw std::system_error(std::make_error_code(std::errc::invalid_argument));
}
const WalletRecord& wallet = m_walletsContainer.get<RandomAccessIndex>()[index];
return {wallet.spendPublicKey, wallet.spendSecretKey};
} }
std::string WalletGreen::createAddress(const KeyPair& spendKey) { KeyPair WalletGreen::getViewKey() const {
throwIfNotInitialized();
throwIfStopped();
return {m_viewPublicKey, m_viewSecretKey};
}
std::string WalletGreen::createAddress() {
KeyPair spendKey;
Crypto::generate_keys(spendKey.publicKey, spendKey.secretKey);
return doCreateAddress(spendKey.publicKey, spendKey.secretKey);
}
std::string WalletGreen::createAddress(const Crypto::SecretKey& spendSecretKey) {
Crypto::PublicKey spendPublicKey;
if (!Crypto::secret_key_to_public_key(spendSecretKey, spendPublicKey) ) {
throw std::system_error(make_error_code(CryptoNote::error::KEY_GENERATION_ERROR));
}
return doCreateAddress(spendPublicKey, spendSecretKey);
}
std::string WalletGreen::doCreateAddress(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey) {
throwIfNotInitialized(); throwIfNotInitialized();
throwIfStopped(); throwIfStopped();
@ -333,22 +395,22 @@ std::string WalletGreen::createAddress(const KeyPair& spendKey) {
m_blockchainSynchronizer.stop(); m_blockchainSynchronizer.stop();
} }
addWallet(spendKey); addWallet(spendPublicKey, spendSecretKey);
std::string address = m_currency.accountAddressAsString({ spendKey.publicKey, m_viewPublicKey }); std::string address = m_currency.accountAddressAsString({ spendPublicKey, m_viewPublicKey });
m_blockchainSynchronizer.start(); m_blockchainSynchronizer.start();
return address; return address;
} }
void WalletGreen::addWallet(const KeyPair& spendKey) { void WalletGreen::addWallet(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey) {
time_t creationTimestamp = time(nullptr); time_t creationTimestamp = time(nullptr);
AccountSubscription sub; AccountSubscription sub;
sub.keys.address.viewPublicKey = m_viewPublicKey; sub.keys.address.viewPublicKey = m_viewPublicKey;
sub.keys.address.spendPublicKey = spendKey.publicKey; sub.keys.address.spendPublicKey = spendPublicKey;
sub.keys.viewSecretKey = m_viewSecretKey; sub.keys.viewSecretKey = m_viewSecretKey;
sub.keys.spendSecretKey = spendKey.secretKey; sub.keys.spendSecretKey = spendSecretKey;
sub.transactionSpendableAge = 10; sub.transactionSpendableAge = 10;
sub.syncStart.height = 0; sub.syncStart.height = 0;
sub.syncStart.timestamp = static_cast<uint64_t>(creationTimestamp) - (60 * 60 * 24); sub.syncStart.timestamp = static_cast<uint64_t>(creationTimestamp) - (60 * 60 * 24);
@ -357,8 +419,8 @@ void WalletGreen::addWallet(const KeyPair& spendKey) {
ITransfersContainer* container = &trSubscription.getContainer(); ITransfersContainer* container = &trSubscription.getContainer();
WalletRecord wallet; WalletRecord wallet;
wallet.spendPublicKey = spendKey.publicKey; wallet.spendPublicKey = spendPublicKey;
wallet.spendSecretKey = spendKey.secretKey; wallet.spendSecretKey = spendSecretKey;
wallet.container = container; wallet.container = container;
wallet.creationTimestamp = creationTimestamp; wallet.creationTimestamp = creationTimestamp;
trSubscription.addObserver(this); trSubscription.addObserver(this);
@ -967,19 +1029,34 @@ void WalletGreen::onError(ITransfersSubscription* object, uint32_t height, std::
} }
void WalletGreen::synchronizationProgressUpdated(uint32_t current, uint32_t total) { void WalletGreen::synchronizationProgressUpdated(uint32_t current, uint32_t total) {
m_dispatcher.remoteSpawn( [current, this] () { this->onSynchronizationProgressUpdated(current); } ); m_dispatcher.remoteSpawn( [current, total, this] () { onSynchronizationProgressUpdated(current, total); } );
} }
void WalletGreen::onSynchronizationProgressUpdated(uint32_t current) { void WalletGreen::synchronizationCompleted(std::error_code result) {
m_dispatcher.remoteSpawn([this] () { onSynchronizationCompleted(); } );
}
void WalletGreen::onSynchronizationProgressUpdated(uint32_t current, uint32_t total) {
System::EventLock lk(m_readyEvent); System::EventLock lk(m_readyEvent);
if (m_state == WalletState::NOT_INITIALIZED) { if (m_state == WalletState::NOT_INITIALIZED) {
return; return;
} }
pushEvent(makeSyncProgressUpdatedEvent(current, total));
unlockBalances(current); unlockBalances(current);
} }
void WalletGreen::onSynchronizationCompleted() {
System::EventLock lk(m_readyEvent);
if (m_state == WalletState::NOT_INITIALIZED) {
return;
}
pushEvent(makeSyncCompletedEvent());
}
void WalletGreen::unlockBalances(uint32_t height) { void WalletGreen::unlockBalances(uint32_t height) {
auto& index = m_unlockTransactionsJob.get<BlockHeightIndex>(); auto& index = m_unlockTransactionsJob.get<BlockHeightIndex>();
auto upper = index.upper_bound(height); auto upper = index.upper_bound(height);
@ -1219,4 +1296,19 @@ void WalletGreen::throwIfStopped() const {
} }
} }
size_t WalletGreen::createFusionTransaction(uint64_t threshold, uint64_t mixin) {
// TODO NOT IMPLEMENTED
throw std::runtime_error("WalletGreen::createFusionTransaction not implemented.");
}
bool WalletGreen::isFusionTransaction(size_t transactionId) const {
// TODO NOT IMPLEMENTED
throw std::runtime_error("WalletGreen::isFusionTransaction not implemented.");
}
IFusionManager::EstimateResult WalletGreen::estimate(uint64_t threshold) const {
// TODO NOT IMPLEMENTED
throw std::runtime_error("WalletGreen::estimate not implemented.");
}
} //namespace CryptoNote } //namespace CryptoNote

View file

@ -21,6 +21,7 @@
#include <queue> #include <queue>
#include "IFusionManager.h"
#include "WalletIndices.h" #include "WalletIndices.h"
#include <System/Dispatcher.h> #include <System/Dispatcher.h>
@ -32,12 +33,14 @@ namespace CryptoNote {
class WalletGreen : public IWallet, class WalletGreen : public IWallet,
ITransfersObserver, ITransfersObserver,
IBlockchainSynchronizerObserver { IBlockchainSynchronizerObserver,
IFusionManager {
public: public:
WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node); WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node);
virtual ~WalletGreen(); virtual ~WalletGreen();
virtual void initialize(const std::string& password) override; virtual void initialize(const std::string& password) override;
virtual void initializeWithViewKey(const Crypto::SecretKey& viewSecretKey, const std::string& password) override;
virtual void load(std::istream& source, const std::string& password) override; virtual void load(std::istream& source, const std::string& password) override;
virtual void shutdown() override; virtual void shutdown() override;
@ -46,8 +49,10 @@ public:
virtual size_t getAddressCount() const override; virtual size_t getAddressCount() const override;
virtual std::string getAddress(size_t index) const override; virtual std::string getAddress(size_t index) const override;
virtual KeyPair getAddressSpendKey(size_t index) const override;
virtual KeyPair getViewKey() const override;
virtual std::string createAddress() override; virtual std::string createAddress() override;
virtual std::string createAddress(const KeyPair& spendKey) override; virtual std::string createAddress(const Crypto::SecretKey& spendSecretKey) override;
virtual void deleteAddress(const std::string& address) override; virtual void deleteAddress(const std::string& address) override;
virtual uint64_t getActualBalance() const override; virtual uint64_t getActualBalance() const override;
@ -69,11 +74,17 @@ public:
virtual void stop() override; virtual void stop() override;
virtual WalletEvent getEvent() override; virtual WalletEvent getEvent() override;
virtual size_t createFusionTransaction(uint64_t threshold, uint64_t mixin) override;
virtual bool isFusionTransaction(size_t transactionId) const override;
virtual IFusionManager::EstimateResult estimate(uint64_t threshold) const override;
protected: protected:
void throwIfNotInitialized() const; void throwIfNotInitialized() const;
void throwIfStopped() const; void throwIfStopped() const;
void doShutdown(); void doShutdown();
void clearCaches(); void clearCaches();
void initWithKeys(const Crypto::PublicKey& viewPublicKey, const Crypto::SecretKey& viewSecretKey, const std::string& password);
std::string doCreateAddress(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey);
struct InputInfo { struct InputInfo {
TransactionTypes::InputKeyInfo keyInfo; TransactionTypes::InputKeyInfo keyInfo;
@ -100,10 +111,12 @@ protected:
virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; virtual void onTransactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override;
virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override; virtual void onTransactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash) override;
virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override; virtual void synchronizationProgressUpdated(uint32_t current, uint32_t total) override;
virtual void synchronizationCompleted(std::error_code result) override;
void transactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash); void transactionUpdated(ITransfersSubscription* object, const Crypto::Hash& transactionHash);
void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash); void transactionDeleted(ITransfersSubscription* object, const Crypto::Hash& transactionHash);
void onSynchronizationProgressUpdated(uint32_t current); void onSynchronizationProgressUpdated(uint32_t current, uint32_t total);
void onSynchronizationCompleted();
std::vector<WalletOuts> pickWalletsWithMoney(); std::vector<WalletOuts> pickWalletsWithMoney();
WalletOuts pickWallet(const std::string& address); WalletOuts pickWallet(const std::string& address);
@ -116,7 +129,7 @@ protected:
const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const; const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) const;
CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const; CryptoNote::AccountPublicAddress parseAddress(const std::string& address) const;
void addWallet(const KeyPair& spendKey); void addWallet(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey);
bool isOutputUsed(const TransactionOutputInformation& out) const; bool isOutputUsed(const TransactionOutputInformation& out) const;
void markOutputsSpent(const Crypto::Hash& transactionHash, const std::vector<OutputToTransfer>& selectedTransfers); void markOutputsSpent(const Crypto::Hash& transactionHash, const std::vector<OutputToTransfer>& selectedTransfers);
void deleteSpentOutputs(const Crypto::Hash& transactionHash); void deleteSpentOutputs(const Crypto::Hash& transactionHash);

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@" #define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0.7" #define PROJECT_VERSION "1.0.7.1"
#define PROJECT_VERSION_BUILD_NO "564" #define PROJECT_VERSION_BUILD_NO "571"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View file

@ -0,0 +1,146 @@
// 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 <System/RemoteContext.h>
#include <System/Dispatcher.h>
#include <System/ContextGroup.h>
#include <System/Event.h>
#include <System/InterruptedException.h>
#include <System/Timer.h>
#include <gtest/gtest.h>
using namespace System;
class RemoteContextTests : public testing::Test {
public:
Dispatcher dispatcher;
};
TEST_F(RemoteContextTests, getReturnsResult) {
RemoteContext<int> context(dispatcher, [&] {
return 2;
});
ASSERT_EQ(2, context.get());
}
TEST_F(RemoteContextTests, getRethrowsException) {
RemoteContext<> context(dispatcher, [&] {
throw std::string("Hi there!");
});
ASSERT_THROW(context.get(), std::string);
}
TEST_F(RemoteContextTests, destructorIgnoresException) {
ASSERT_NO_THROW(RemoteContext<>(dispatcher, [&] {
throw std::string("Hi there!");
}));
}
TEST_F(RemoteContextTests, canBeUsedWithoutObject) {
ASSERT_EQ(42, RemoteContext<int>(dispatcher, [&] { return 42; }).get());
}
TEST_F(RemoteContextTests, interruptIsInterruptingWait) {
ContextGroup cg(dispatcher);
cg.spawn([&] {
RemoteContext<> context(dispatcher, [&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
ASSERT_NO_THROW(context.wait());
ASSERT_TRUE(dispatcher.interrupted());
});
cg.interrupt();
cg.wait();
}
TEST_F(RemoteContextTests, interruptIsInterruptingGet) {
ContextGroup cg(dispatcher);
cg.spawn([&] {
RemoteContext<> context(dispatcher, [&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
ASSERT_NO_THROW(context.wait());
ASSERT_TRUE(dispatcher.interrupted());
});
cg.interrupt();
cg.wait();
}
TEST_F(RemoteContextTests, destructorIgnoresInterrupt) {
ContextGroup cg(dispatcher);
cg.spawn([&] {
ASSERT_NO_THROW(RemoteContext<>(dispatcher, [&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}));
});
cg.interrupt();
cg.wait();
}
TEST_F(RemoteContextTests, canExecuteOtherContextsWhileWaiting) {
auto start = std::chrono::high_resolution_clock::now();
ContextGroup cg(dispatcher);
cg.spawn([&] {
RemoteContext<> context(dispatcher, [&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
});
cg.spawn([&] {
System::Timer(dispatcher).sleep(std::chrono::milliseconds(5));
auto end = std::chrono::high_resolution_clock::now();
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(), 5);
ASSERT_LE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(), 9);
});
cg.wait();
}
TEST_F(RemoteContextTests, waitMethodWaitsForContexCompletion) {
auto start = std::chrono::high_resolution_clock::now();
ContextGroup cg(dispatcher);
cg.spawn([&] {
RemoteContext<> context(dispatcher, [&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
});
cg.wait();
auto end = std::chrono::high_resolution_clock::now();
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(), 10);
}
TEST_F(RemoteContextTests, waitMethodWaitsForContexCompletionOnInterrupt) {
auto start = std::chrono::high_resolution_clock::now();
ContextGroup cg(dispatcher);
cg.spawn([&] {
RemoteContext<> context(dispatcher, [&] {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
});
});
cg.interrupt();
cg.wait();
auto end = std::chrono::high_resolution_clock::now();
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(), 10);
}

View file

@ -79,8 +79,11 @@ public:
void setGetNewBlocksLimit(size_t maxBlocks) { m_getMaxBlocks = maxBlocks; } void setGetNewBlocksLimit(size_t maxBlocks) { m_getMaxBlocks = maxBlocks; }
virtual uint32_t getLastLocalBlockHeight() const { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }; virtual uint32_t getLastLocalBlockHeight() const { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }
virtual uint32_t getLastKnownBlockHeight() const { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }; virtual uint32_t getLastKnownBlockHeight() const { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size() - 1); }
virtual uint32_t getLocalBlockCount() const override { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size()); }
virtual uint32_t getKnownBlockCount() const override { return static_cast<uint32_t>(m_blockchainGenerator.getBlockchain().size()); }
virtual void getNewBlocks(std::vector<Crypto::Hash>&& knownBlockIds, std::vector<CryptoNote::block_complete_entry>& newBlocks, uint32_t& startHeight, const Callback& callback); virtual void getNewBlocks(std::vector<Crypto::Hash>&& knownBlockIds, std::vector<CryptoNote::block_complete_entry>& newBlocks, uint32_t& startHeight, const Callback& callback);

View file

@ -34,6 +34,7 @@
#include "WalletLegacy/WalletLegacySerializer.h" #include "WalletLegacy/WalletLegacySerializer.h"
#include <System/Dispatcher.h> #include <System/Dispatcher.h>
#include <System/Timer.h> #include <System/Timer.h>
#include <System/Context.h>
using namespace Crypto; using namespace Crypto;
using namespace Common; using namespace Common;
@ -129,6 +130,8 @@ protected:
template<typename T> template<typename T>
void waitForValue(CryptoNote::WalletGreen& wallet, T value, std::function<T ()>&& f); void waitForValue(CryptoNote::WalletGreen& wallet, T value, std::function<T ()>&& f);
bool waitForWalletEvent(CryptoNote::WalletGreen& wallet, CryptoNote::WalletEventType eventType, std::chrono::nanoseconds timeout);
void waitActualBalanceUpdated(); void waitActualBalanceUpdated();
void waitActualBalanceUpdated(uint64_t prev); void waitActualBalanceUpdated(uint64_t prev);
void waitActualBalanceUpdated(CryptoNote::WalletGreen& wallet, uint64_t prev); void waitActualBalanceUpdated(CryptoNote::WalletGreen& wallet, uint64_t prev);
@ -223,6 +226,28 @@ void WalletApi::waitForValue(CryptoNote::WalletGreen& wallet, T value, std::func
} }
} }
bool WalletApi::waitForWalletEvent(CryptoNote::WalletGreen& wallet, CryptoNote::WalletEventType eventType, std::chrono::nanoseconds timeout) {
System::Context<> eventContext(dispatcher, [&wallet, eventType] () {
CryptoNote::WalletEvent event;
do {
event = wallet.getEvent();
} while(event.type != eventType);
});
System::Context<> timeoutContext(dispatcher, [timeout, &eventContext, this] {
System::Timer(dispatcher).sleep(timeout);
eventContext.interrupt();
});
try {
eventContext.get();
return true;
} catch (System::InterruptedException&) {
return false;
}
}
void WalletApi::waitActualBalanceUpdated() { void WalletApi::waitActualBalanceUpdated() {
waitActualBalanceUpdated(alice, alice.getActualBalance()); waitActualBalanceUpdated(alice, alice.getActualBalance());
} }
@ -1145,3 +1170,93 @@ TEST_F(WalletApi, transferSmallFeeTransactionThrows) {
ASSERT_ANY_THROW(sendMoneyToRandomAddressFrom(alice.getAddress(0), SENT, currency.minimumFee() - 1)); ASSERT_ANY_THROW(sendMoneyToRandomAddressFrom(alice.getAddress(0), SENT, currency.minimumFee() - 1));
} }
TEST_F(WalletApi, initializeWithKeysSucceded) {
CryptoNote::WalletGreen wallet(dispatcher, currency, node);
CryptoNote::KeyPair viewKeys;
Crypto::generate_keys(viewKeys.publicKey, viewKeys.secretKey);
ASSERT_NO_THROW(wallet.initializeWithViewKey(viewKeys.secretKey, "pass"));
wallet.shutdown();
}
TEST_F(WalletApi, initializeWithKeysThrowsIfAlreadInitialized) {
CryptoNote::KeyPair viewKeys;
Crypto::generate_keys(viewKeys.publicKey, viewKeys.secretKey);
ASSERT_ANY_THROW(alice.initializeWithViewKey(viewKeys.secretKey, "pass"));
}
TEST_F(WalletApi, initializeWithKeysThrowsIfStopped) {
CryptoNote::WalletGreen wallet(dispatcher, currency, node);
wallet.stop();
CryptoNote::KeyPair viewKeys;
Crypto::generate_keys(viewKeys.publicKey, viewKeys.secretKey);
ASSERT_ANY_THROW(wallet.initializeWithViewKey(viewKeys.secretKey, "pass"));
}
TEST_F(WalletApi, getViewKeyReturnsProperKey) {
CryptoNote::WalletGreen wallet(dispatcher, currency, node);
CryptoNote::KeyPair viewKeys;
Crypto::generate_keys(viewKeys.publicKey, viewKeys.secretKey);
wallet.initializeWithViewKey(viewKeys.secretKey, "pass");
CryptoNote::KeyPair retrievedKeys = wallet.getViewKey();
ASSERT_EQ(viewKeys.publicKey, retrievedKeys.publicKey);
ASSERT_EQ(viewKeys.secretKey, retrievedKeys.secretKey);
wallet.shutdown();
}
TEST_F(WalletApi, getViewKeyThrowsIfNotInitialized) {
CryptoNote::WalletGreen wallet(dispatcher, currency, node);
ASSERT_ANY_THROW(wallet.getViewKey());
}
TEST_F(WalletApi, getViewKeyThrowsIfStopped) {
alice.stop();
ASSERT_ANY_THROW(alice.getViewKey());
}
TEST_F(WalletApi, getAddressSpendKeyReturnsProperKey) {
CryptoNote::KeyPair spendKeys;
Crypto::generate_keys(spendKeys.publicKey, spendKeys.secretKey);
alice.createAddress(spendKeys.secretKey);
CryptoNote::KeyPair retrievedKeys = alice.getAddressSpendKey(1);
ASSERT_EQ(spendKeys.publicKey, retrievedKeys.publicKey);
ASSERT_EQ(spendKeys.secretKey, retrievedKeys.secretKey);
}
TEST_F(WalletApi, getAddressSpendKeyThrowsForWrongAddressIndex) {
ASSERT_ANY_THROW(alice.getAddressSpendKey(1));
}
TEST_F(WalletApi, getAddressSpendKeyThrowsIfNotInitialized) {
CryptoNote::WalletGreen wallet(dispatcher, currency, node);
ASSERT_ANY_THROW(wallet.getAddressSpendKey(0));
}
TEST_F(WalletApi, getAddressSpendKeyThrowsIfStopped) {
alice.stop();
ASSERT_ANY_THROW(alice.getAddressSpendKey(0));
}
TEST_F(WalletApi, walletGetsSyncCompletedEvent) {
generator.generateEmptyBlocks(1);
node.updateObservers();
ASSERT_TRUE(waitForWalletEvent(alice, CryptoNote::SYNC_COMPLETED, std::chrono::seconds(5)));
}
TEST_F(WalletApi, walletGetsSyncProgressUpdatedEvent) {
generator.generateEmptyBlocks(1);
node.updateObservers();
ASSERT_TRUE(waitForWalletEvent(alice, CryptoNote::SYNC_PROGRESS_UPDATED, std::chrono::seconds(5)));
}