Daemon synchronization and SimpleWallet improvements
This commit is contained in:
parent
deda499fc9
commit
a4b74eaa11
29 changed files with 1090 additions and 222 deletions
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/Platform/Linux/System/Future.h
Normal file
34
src/Platform/Linux/System/Future.h
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
170
src/Platform/OSX/System/Future.h
Normal file
170
src/Platform/OSX/System/Future.h
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
src/Platform/Windows/System/Future.h
Normal file
35
src/Platform/Windows/System/Future.h
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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()) {
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
102
src/System/RemoteContext.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
39
src/Wallet/IFusionManager.h
Normal file
39
src/Wallet/IFusionManager.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 ")"
|
||||||
|
|
146
tests/System/RemoteContextTests.cpp
Normal file
146
tests/System/RemoteContextTests.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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)));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue