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 {
|
||||
TRANSACTION_CREATED,
|
||||
TRANSACTION_UPDATED,
|
||||
BALANCE_UNLOCKED
|
||||
BALANCE_UNLOCKED,
|
||||
SYNC_PROGRESS_UPDATED,
|
||||
SYNC_COMPLETED
|
||||
};
|
||||
|
||||
struct WalletTransactionCreatedData {
|
||||
|
@ -48,11 +50,17 @@ struct WalletTransactionUpdatedData {
|
|||
size_t transactionIndex;
|
||||
};
|
||||
|
||||
struct WalletSynchronizationProgressUpdated {
|
||||
uint32_t processedBlockCount;
|
||||
uint32_t totalBlockCount;
|
||||
};
|
||||
|
||||
struct WalletEvent {
|
||||
WalletEventType type;
|
||||
union {
|
||||
WalletTransactionCreatedData transactionCreated;
|
||||
WalletTransactionUpdatedData transactionUpdated;
|
||||
WalletSynchronizationProgressUpdated synchronizationProgressUpdated;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -78,6 +86,7 @@ public:
|
|||
virtual ~IWallet() {}
|
||||
|
||||
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 shutdown() = 0;
|
||||
|
||||
|
@ -86,8 +95,10 @@ public:
|
|||
|
||||
virtual size_t getAddressCount() const = 0;
|
||||
virtual std::string getAddress(size_t index) const = 0;
|
||||
virtual KeyPair getAddressSpendKey(size_t index) const = 0;
|
||||
virtual KeyPair getViewKey() const = 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 uint64_t getActualBalance() const = 0;
|
||||
|
|
|
@ -161,7 +161,8 @@ const CheckpointData CHECKPOINTS[] = {
|
|||
{789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"},
|
||||
{796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"},
|
||||
{800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"},
|
||||
{804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"}
|
||||
{804000, "bcc8b3782499aae508c40d5587d1cc5d68281435ea9bfc6804a262047f7b934d"},
|
||||
{810500, "302b2349f221232820adc3dadafd8a61b035491e33af669c78a687949eb0a381"}
|
||||
};
|
||||
} // CryptoNote
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@ CoreConfig::CoreConfig() {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -30,7 +30,8 @@ enum NodeErrorCodes {
|
|||
NETWORK_ERROR,
|
||||
NODE_BUSY,
|
||||
INTERNAL_NODE_ERROR,
|
||||
REQUEST_ERROR
|
||||
REQUEST_ERROR,
|
||||
CONNECT_ERROR
|
||||
};
|
||||
|
||||
// custom category:
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
case NODE_BUSY: return "Node is busy";
|
||||
case INTERNAL_NODE_ERROR: return "Internal node error";
|
||||
case REQUEST_ERROR: return "Error in request parameters";
|
||||
case CONNECT_ERROR: return "Can't connect to daemon";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,8 @@ NodeRpcProxy::NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort)
|
|||
m_pullInterval(5000),
|
||||
m_nodeHost(nodeHost),
|
||||
m_nodePort(nodePort),
|
||||
m_lastLocalBlockTimestamp(0) {
|
||||
m_lastLocalBlockTimestamp(0),
|
||||
m_connected(true) {
|
||||
resetInternalState();
|
||||
}
|
||||
|
||||
|
@ -170,6 +171,8 @@ void NodeRpcProxy::workerThread(const INode::Callback& initialized_callback) {
|
|||
m_context_group = nullptr;
|
||||
m_httpClient = nullptr;
|
||||
m_httpEvent = nullptr;
|
||||
m_connected = false;
|
||||
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
|
||||
}
|
||||
|
||||
void NodeRpcProxy::updateNodeStatus() {
|
||||
|
@ -200,6 +203,10 @@ void NodeRpcProxy::updateNodeStatus() {
|
|||
}
|
||||
|
||||
updatePeerCount();
|
||||
if (m_connected != m_httpClient->isConnected()) {
|
||||
m_connected = m_httpClient->isConnected();
|
||||
m_rpcProxyObserverManager.notify(&INodeRpcProxyObserver::connectionStatusUpdated, m_connected);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeRpcProxy::updatePeerCount() {
|
||||
|
@ -225,6 +232,14 @@ bool NodeRpcProxy::removeObserver(INodeObserver* 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 {
|
||||
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));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}, 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);
|
||||
invokeBinaryCommand(*m_httpClient, url, req, res);
|
||||
ec = interpretResponseStatus(res.status);
|
||||
} catch (const ConnectException&) {
|
||||
ec = make_error_code(error::CONNECT_ERROR);
|
||||
} catch (const std::exception&) {
|
||||
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);
|
||||
invokeJsonCommand(*m_httpClient, url, req, res);
|
||||
ec = interpretResponseStatus(res.status);
|
||||
} catch (const ConnectException&) {
|
||||
ec = make_error_code(error::CONNECT_ERROR);
|
||||
} catch (const std::exception&) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} catch (const ConnectException&) {
|
||||
ec = make_error_code(error::CONNECT_ERROR);
|
||||
} catch (const std::exception&) {
|
||||
ec = make_error_code(error::NETWORK_ERROR);
|
||||
}
|
||||
|
|
|
@ -36,6 +36,12 @@ namespace CryptoNote {
|
|||
|
||||
class HttpClient;
|
||||
|
||||
class INodeRpcProxyObserver {
|
||||
public:
|
||||
virtual ~INodeRpcProxyObserver() {}
|
||||
virtual void connectionStatusUpdated(bool connected) {}
|
||||
};
|
||||
|
||||
class NodeRpcProxy : public CryptoNote::INode {
|
||||
public:
|
||||
NodeRpcProxy(const std::string& nodeHost, unsigned short nodePort);
|
||||
|
@ -44,6 +50,9 @@ public:
|
|||
virtual bool addObserver(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 bool shutdown();
|
||||
|
||||
|
@ -116,6 +125,7 @@ private:
|
|||
System::Dispatcher* m_dispatcher = nullptr;
|
||||
System::ContextGroup* m_context_group = nullptr;
|
||||
Tools::ObserverManager<CryptoNote::INodeObserver> m_observerManager;
|
||||
Tools::ObserverManager<CryptoNote::INodeRpcProxyObserver> m_rpcProxyObserverManager;
|
||||
|
||||
const std::string m_nodeHost;
|
||||
const unsigned short m_nodePort;
|
||||
|
@ -134,6 +144,8 @@ private:
|
|||
//protect it with mutex if decided to add worker threads
|
||||
Crypto::Hash m_lastKnowHash;
|
||||
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";
|
||||
case CryptoNoteConnectionContext::state_sync_required:
|
||||
return "state_sync_required";
|
||||
case CryptoNoteConnectionContext::state_pool_sync_required:
|
||||
return "state_pool_sync_required";
|
||||
case CryptoNoteConnectionContext::state_shutdown:
|
||||
return "state_shutdown";
|
||||
default:
|
||||
|
|
|
@ -68,13 +68,13 @@ void LevinProtocol::sendMessage(uint32_t command, const BinaryArray& out, bool n
|
|||
stream.writeSome(&head, sizeof(head));
|
||||
stream.writeSome(out.data(), out.size());
|
||||
|
||||
m_conn.write(writeBuffer.data(), writeBuffer.size());
|
||||
writeStrict(writeBuffer.data(), writeBuffer.size());
|
||||
}
|
||||
|
||||
bool LevinProtocol::readCommand(Command& cmd) {
|
||||
bucket_head2 head = { 0 };
|
||||
|
||||
if (!readStrict(&head, sizeof(head))) {
|
||||
if (!readStrict(reinterpret_cast<uint8_t*>(&head), sizeof(head))) {
|
||||
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_return_code = returnCode;
|
||||
|
||||
m_conn.write(reinterpret_cast<const uint8_t*>(&head), sizeof(head));
|
||||
if (out.size() > 0) {
|
||||
m_conn.write(reinterpret_cast<const uint8_t*>(out.data()), out.size());
|
||||
BinaryArray writeBuffer;
|
||||
writeBuffer.reserve(sizeof(head) + 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) {
|
||||
char* pos = reinterpret_cast<char*>(ptr);
|
||||
bool LevinProtocol::readStrict(uint8_t* ptr, size_t size) {
|
||||
size_t offset = 0;
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -105,7 +105,8 @@ public:
|
|||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -731,7 +731,8 @@ namespace CryptoNote
|
|||
});
|
||||
|
||||
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();
|
||||
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>();
|
||||
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
@ -1068,41 +1071,48 @@ namespace CryptoNote
|
|||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
bool NodeServer::try_ping(basic_node_data& node_data, P2pConnectionContext& context)
|
||||
{
|
||||
if(!node_data.my_port)
|
||||
bool NodeServer::try_ping(basic_node_data& node_data, P2pConnectionContext& context) {
|
||||
if(!node_data.my_port) {
|
||||
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;
|
||||
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() {
|
||||
bindIp = "0.0.0.0";
|
||||
bindPort = P2P_DEFAULT_PORT;
|
||||
bindIp = "";
|
||||
bindPort = 0;
|
||||
externalPort = 0;
|
||||
allowLocalIp = false;
|
||||
hideMyPort = false;
|
||||
|
@ -84,23 +84,37 @@ NetNodeConfig::NetNodeConfig() {
|
|||
|
||||
bool NetNodeConfig::init(const boost::program_options::variables_map& vm)
|
||||
{
|
||||
bindIp = command_line::get_arg(vm, arg_p2p_bind_ip);
|
||||
bindPort = command_line::get_arg(vm, arg_p2p_bind_port);
|
||||
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_ip.name) != 0 && (!vm[arg_p2p_bind_ip.name].defaulted() || bindIp.empty())) {
|
||||
bindIp = command_line::get_arg(vm, arg_p2p_bind_ip);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
for(const std::string& pr_str: perrs)
|
||||
{
|
||||
for(const std::string& pr_str: perrs) {
|
||||
PeerlistEntry pe = boost::value_initialized<PeerlistEntry>();
|
||||
pe.id = Crypto::rand<uint64_t>();
|
||||
if (!parsePeerFromString(pe.adr, pr_str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
peers.push_back(pe);
|
||||
}
|
||||
}
|
||||
|
@ -120,8 +134,9 @@ bool NetNodeConfig::init(const boost::program_options::variables_map& vm)
|
|||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ Configuration::Configuration() {
|
|||
testnet = false;
|
||||
printAddresses = false;
|
||||
logLevel = Logging::INFO;
|
||||
bindAddress = "";
|
||||
bindPort = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (options.count("daemon")) {
|
||||
if (options.count("daemon") != 0) {
|
||||
daemonize = true;
|
||||
}
|
||||
|
||||
if (options.count("register-service")) {
|
||||
if (options.count("register-service") != 0) {
|
||||
registerService = true;
|
||||
}
|
||||
|
||||
if (options.count("unregister-service")) {
|
||||
if (options.count("unregister-service") != 0) {
|
||||
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");
|
||||
}
|
||||
|
||||
if (options.count("testnet")) {
|
||||
if (options.count("testnet") != 0) {
|
||||
testnet = true;
|
||||
}
|
||||
|
||||
if (options.count("log-file")) {
|
||||
if (options.count("log-file") != 0) {
|
||||
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>();
|
||||
if (logLevel > Logging::TRACE) {
|
||||
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>();
|
||||
}
|
||||
|
||||
if (options.count("bind-address")) {
|
||||
if (options.count("bind-address") != 0 && (!options["bind-address"].defaulted() || bindAddress.empty())) {
|
||||
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>();
|
||||
}
|
||||
|
||||
if (options.count("wallet-file")) {
|
||||
if (options.count("wallet-file") != 0) {
|
||||
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>();
|
||||
}
|
||||
|
||||
if (options.count("generate-wallet")) {
|
||||
if (options.count("generate-wallet") != 0) {
|
||||
generateNewWallet = true;
|
||||
}
|
||||
|
||||
if (options.count("address")) {
|
||||
if (options.count("address") != 0) {
|
||||
printAddresses = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ namespace PaymentService {
|
|||
namespace po = boost::program_options;
|
||||
|
||||
RpcNodeConfiguration::RpcNodeConfiguration() {
|
||||
daemonHost = "127.0.0.1";
|
||||
daemonPort = 8081;
|
||||
daemonHost = "";
|
||||
daemonPort = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (options.count("daemon-address")) {
|
||||
if (options.count("daemon-address") != 0 && (!options["daemon-address"].defaulted() || daemonHost.empty())) {
|
||||
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>();
|
||||
}
|
||||
}
|
||||
|
|
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() {
|
||||
auto ipAddr = System::Ipv4Resolver(m_dispatcher).resolve(m_address);
|
||||
m_connection = System::TcpConnector(m_dispatcher).connect(ipAddr, m_port);
|
||||
m_streamBuf.reset(new System::TcpStreambuf(m_connection));
|
||||
m_connected = true;
|
||||
try {
|
||||
auto ipAddr = System::Ipv4Resolver(m_dispatcher).resolve(m_address);
|
||||
m_connection = System::TcpConnector(m_dispatcher).connect(ipAddr, m_port);
|
||||
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() {
|
||||
|
@ -75,4 +83,7 @@ void HttpClient::disconnect() {
|
|||
m_connected = false;
|
||||
}
|
||||
|
||||
ConnectException::ConnectException(const std::string& whatArg) : std::runtime_error(whatArg.c_str()) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,11 @@
|
|||
|
||||
namespace CryptoNote {
|
||||
|
||||
class ConnectException : public std::runtime_error {
|
||||
public:
|
||||
ConnectException(const std::string& whatArg);
|
||||
};
|
||||
|
||||
class HttpClient {
|
||||
public:
|
||||
|
||||
|
@ -35,8 +40,9 @@ public:
|
|||
~HttpClient();
|
||||
void request(const HttpRequest& req, HttpResponse& res);
|
||||
|
||||
private:
|
||||
bool isConnected() const;
|
||||
|
||||
private:
|
||||
void connect();
|
||||
void disconnect();
|
||||
|
||||
|
|
|
@ -420,10 +420,24 @@ void printListTransfersItem(LoggerRef& logger, const WalletLegacyTransaction& tx
|
|||
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;
|
||||
ss << "Commands: " << ENDL;
|
||||
std::string usage = m_consoleHandler.getUsage();
|
||||
|
@ -433,8 +447,7 @@ std::string simple_wallet::get_commands_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();
|
||||
return true;
|
||||
}
|
||||
|
@ -451,8 +464,7 @@ simple_wallet::simple_wallet(System::Dispatcher& dispatcher, const CryptoNote::C
|
|||
logManager(log),
|
||||
logger(log, "simplewallet"),
|
||||
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("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");
|
||||
|
@ -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");
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::set_log(const std::vector<std::string> &args)
|
||||
{
|
||||
if (args.size() != 1)
|
||||
{
|
||||
bool simple_wallet::set_log(const std::vector<std::string> &args) {
|
||||
if (args.size() != 1) {
|
||||
fail_msg_writer() << "use: set_log <log_level_number_0-4>";
|
||||
return true;
|
||||
}
|
||||
|
||||
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>";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (l > Logging::TRACE)
|
||||
{
|
||||
if (l > Logging::TRACE) {
|
||||
fail_msg_writer() << "wrong number range, use: set_log <log_level_number_0-4>";
|
||||
return true;
|
||||
}
|
||||
|
@ -497,8 +506,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
|
|||
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);
|
||||
|
||||
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::future<std::error_code> f_error = errorPromise.get_future();
|
||||
auto callback = [&errorPromise](std::error_code e) {errorPromise.set_value(e); };
|
||||
|
||||
m_node->addObserver(static_cast<INodeRpcProxyObserver*>(this));
|
||||
m_node->init(callback);
|
||||
auto error = f_error.get();
|
||||
if (error) {
|
||||
|
@ -589,13 +599,23 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!m_generate_new.empty())
|
||||
{
|
||||
bool r = new_wallet(walletFileName, pwd_container.password());
|
||||
if (!(r)) { logger(ERROR, BRIGHT_RED) << "account creation failed"; return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_generate_new.empty()) {
|
||||
std::string walletAddressFile = prepareWalletAddressFilename(m_generate_new);
|
||||
boost::system::error_code ignore;
|
||||
if (boost::filesystem::exists(walletAddressFile, ignore)) {
|
||||
logger(ERROR, BRIGHT_RED) << "Address file already exists: " + walletAddressFile;
|
||||
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));
|
||||
|
||||
try {
|
||||
|
@ -606,7 +626,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
|||
}
|
||||
|
||||
m_wallet->addObserver(this);
|
||||
m_node->addObserver(this);
|
||||
m_node->addObserver(static_cast<INodeObserver*>(this));
|
||||
|
||||
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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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())
|
||||
return true;
|
||||
|
||||
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_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
|
||||
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);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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.reset(new WalletLegacy(m_currency, *m_node.get()));
|
||||
m_node->addObserver(this);
|
||||
m_node->addObserver(static_cast<INodeObserver*>(this));
|
||||
m_wallet->addObserver(this);
|
||||
try
|
||||
{
|
||||
try {
|
||||
m_initResultPromise.reset(new std::promise<std::error_code>());
|
||||
std::future<std::error_code> f_initError = m_initResultPromise->get_future();
|
||||
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 <<
|
||||
"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();
|
||||
return false;
|
||||
}
|
||||
|
@ -690,9 +709,7 @@ bool simple_wallet::close_wallet()
|
|||
{
|
||||
try {
|
||||
CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
fail_msg_writer() << e.what();
|
||||
return false;
|
||||
}
|
||||
|
@ -706,13 +723,10 @@ bool simple_wallet::close_wallet()
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::save(const std::vector<std::string> &args)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
CryptoNote::WalletHelper::storeWallet(*m_wallet, m_wallet_file);
|
||||
success_msg_writer() << "Wallet data saved";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
} catch (const std::exception& e) {
|
||||
fail_msg_writer() << e.what();
|
||||
}
|
||||
|
||||
|
@ -725,31 +739,24 @@ bool simple_wallet::reset(const std::vector<std::string> &args) {
|
|||
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;
|
||||
req.miner_address = m_wallet->getAddress();
|
||||
|
||||
bool ok = true;
|
||||
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;
|
||||
}
|
||||
else if (1 == args.size())
|
||||
{
|
||||
} else if (1 == args.size()) {
|
||||
uint16_t num = 1;
|
||||
ok = Common::fromString(args[0], num);
|
||||
ok = ok && (1 <= num && num <= max_mining_threads_count);
|
||||
req.threads_count = num;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
if (!ok) {
|
||||
fail_msg_writer() << "invalid arguments. Please use start_mining [<number_of_threads>], " <<
|
||||
"<number_of_threads> should be from 1 to " << max_mining_threads_count;
|
||||
return true;
|
||||
|
@ -769,6 +776,8 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
|
|||
else
|
||||
fail_msg_writer() << "mining has NOT been started: " << err;
|
||||
|
||||
} catch (const ConnectException&) {
|
||||
printConnectionError();
|
||||
} catch (const std::exception& e) {
|
||||
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 {
|
||||
HttpClient httpClient(m_dispatcher, m_daemon_host, m_daemon_port);
|
||||
|
||||
invokeJsonCommand(httpClient, "/stop_mining", req, res);
|
||||
std::string err = interpret_rpc_response(true, res.status);
|
||||
if (err.empty())
|
||||
success_msg_writer() << "Mining stopped in daemon";
|
||||
else
|
||||
fail_msg_writer() << "mining has NOT been stopped: " << err;
|
||||
} catch (const ConnectException&) {
|
||||
printConnectionError();
|
||||
} catch (const std::exception& e) {
|
||||
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);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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;
|
||||
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()) <<
|
||||
", locked amount: " << m_currency.formatAmount(m_wallet->pendingBalance());
|
||||
|
||||
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;
|
||||
size_t transactionsCount = m_wallet->getTransactionCount();
|
||||
for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) {
|
||||
|
@ -888,10 +904,8 @@ bool simple_wallet::listTransfers(const std::vector<std::string>& args) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::show_payments(const std::vector<std::string> &args)
|
||||
{
|
||||
if (args.empty())
|
||||
{
|
||||
bool simple_wallet::show_payments(const std::vector<std::string> &args) {
|
||||
if (args.empty()) {
|
||||
fail_msg_writer() << "expected at least one payment ID";
|
||||
return true;
|
||||
}
|
||||
|
@ -901,11 +915,9 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
|
|||
" height\t amount ";
|
||||
|
||||
bool payments_found = false;
|
||||
for (const std::string& arg: args)
|
||||
{
|
||||
for (const std::string& arg: args) {
|
||||
Crypto::Hash expectedPaymentId;
|
||||
if (CryptoNote::parsePaymentId(arg, expectedPaymentId))
|
||||
{
|
||||
if (CryptoNote::parsePaymentId(arg, expectedPaymentId)) {
|
||||
size_t transactionsCount = m_wallet->getTransactionCount();
|
||||
for (size_t trantransactionNumber = 0; trantransactionNumber < transactionsCount; ++trantransactionNumber) {
|
||||
WalletLegacyTransaction txInfo;
|
||||
|
@ -938,8 +950,7 @@ bool simple_wallet::show_payments(const std::vector<std::string> &args)
|
|||
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 {
|
||||
uint64_t bc_height = m_node->getLastLocalBlockHeight();
|
||||
success_msg_writer() << bc_height;
|
||||
|
@ -950,10 +961,8 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::transfer(const std::vector<std::string> &args)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool simple_wallet::transfer(const std::vector<std::string> &args) {
|
||||
try {
|
||||
TransferCommand cmd(m_currency);
|
||||
|
||||
if (!cmd.parseArguments(logger, args))
|
||||
|
@ -989,17 +998,11 @@ bool simple_wallet::transfer(const std::vector<std::string> &args)
|
|||
fail_msg_writer() << e.what();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::system_error& e)
|
||||
{
|
||||
fail_msg_writer() << "unexpected error: " << e.what();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
fail_msg_writer() << "unexpected error: " << e.what();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
} catch (const std::system_error& e) {
|
||||
fail_msg_writer() << e.what();
|
||||
} catch (const std::exception& e) {
|
||||
fail_msg_writer() << e.what();
|
||||
} catch (...) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
#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)) {
|
||||
//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.";
|
||||
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.";
|
||||
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.";
|
||||
return 1;
|
||||
}
|
||||
|
@ -1179,9 +1184,7 @@ int main(int argc, char* argv[])
|
|||
logger(INFO) << "Storing wallet...";
|
||||
CryptoNote::WalletHelper::storeWallet(*wallet, walletFileName);
|
||||
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();
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,12 @@
|
|||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include "IWalletLegacy.h"
|
||||
#include "INode.h"
|
||||
#include "PasswordContainer.h"
|
||||
|
||||
#include "Common/ConsoleHandler.h"
|
||||
#include "CryptoNoteCore/CryptoNoteBasicImpl.h"
|
||||
#include "CryptoNoteCore/Currency.h"
|
||||
#include "NodeRpcProxy/NodeRpcProxy.h"
|
||||
#include "WalletLegacy/WalletHelper.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:
|
||||
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);
|
||||
}
|
||||
|
||||
Logging::LoggerMessage fail_msg_writer() {
|
||||
Logging::LoggerMessage fail_msg_writer() const {
|
||||
auto msg = logger(Logging::ERROR, Logging::BRIGHT_RED);
|
||||
msg << "Error: ";
|
||||
return msg;
|
||||
|
@ -94,6 +93,8 @@ namespace CryptoNote
|
|||
|
||||
bool ask_wallet_create_if_needed();
|
||||
|
||||
void printConnectionError() const;
|
||||
|
||||
//---------------- IWalletObserver -------------------------
|
||||
virtual void initCompleted(std::error_code result) override;
|
||||
virtual void externalTransactionCreated(CryptoNote::TransactionId transactionId) override;
|
||||
|
@ -103,6 +104,10 @@ namespace CryptoNote
|
|||
virtual void localBlockchainUpdated(uint32_t height) override;
|
||||
//----------------------------------------------------------
|
||||
|
||||
//----------------- INodeRpcProxyObserver --------------------------
|
||||
virtual void connectionStatusUpdated(bool connected) override;
|
||||
//----------------------------------------------------------
|
||||
|
||||
friend class refresh_progress_reporter_t;
|
||||
|
||||
class refresh_progress_reporter_t
|
||||
|
@ -174,7 +179,7 @@ namespace CryptoNote
|
|||
System::Dispatcher& m_dispatcher;
|
||||
Logging::LoggerRef logger;
|
||||
|
||||
std::unique_ptr<CryptoNote::INode> m_node;
|
||||
std::unique_ptr<CryptoNote::NodeRpcProxy> m_node;
|
||||
std::unique_ptr<CryptoNote::IWalletLegacy> m_wallet;
|
||||
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,
|
||||
TX_TRANSFER_IMPOSSIBLE,
|
||||
WRONG_VERSION,
|
||||
FEE_TOO_SMALL
|
||||
FEE_TOO_SMALL,
|
||||
KEY_GENERATION_ERROR
|
||||
};
|
||||
|
||||
// custom category:
|
||||
|
@ -75,6 +76,7 @@ public:
|
|||
case TX_TRANSFER_IMPOSSIBLE: return "Transaction transfer impossible";
|
||||
case WRONG_VERSION: return "Wrong version";
|
||||
case FEE_TOO_SMALL: return "Transaction fee is too small";
|
||||
case KEY_GENERATION_ERROR: return "Cannot generate new key";
|
||||
default: return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -136,6 +136,22 @@ CryptoNote::WalletEvent makeMoneyUnlockedEvent() {
|
|||
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 {
|
||||
|
@ -162,18 +178,20 @@ WalletGreen::~WalletGreen() {
|
|||
}
|
||||
|
||||
void WalletGreen::initialize(const std::string& password) {
|
||||
if (m_state != WalletState::NOT_INITIALIZED) {
|
||||
throw std::system_error(make_error_code(CryptoNote::error::ALREADY_INITIALIZED));
|
||||
Crypto::PublicKey viewPublicKey;
|
||||
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();
|
||||
|
||||
Crypto::generate_keys(m_viewPublicKey, m_viewSecretKey);
|
||||
m_password = password;
|
||||
|
||||
m_blockchainSynchronizer.addObserver(this);
|
||||
|
||||
m_state = WalletState::INITIALIZED;
|
||||
initWithKeys(viewPublicKey, viewSecretKey, password);
|
||||
}
|
||||
|
||||
void WalletGreen::shutdown() {
|
||||
|
@ -210,6 +228,22 @@ void WalletGreen::clearCaches() {
|
|||
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) {
|
||||
throwIfNotInitialized();
|
||||
throwIfStopped();
|
||||
|
@ -318,14 +352,42 @@ std::string WalletGreen::getAddress(size_t index) const {
|
|||
return m_currency.accountAddressAsString({ wallet.spendPublicKey, m_viewPublicKey });
|
||||
}
|
||||
|
||||
std::string WalletGreen::createAddress() {
|
||||
KeyPair spendKey;
|
||||
KeyPair WalletGreen::getAddressSpendKey(size_t index) const {
|
||||
throwIfNotInitialized();
|
||||
throwIfStopped();
|
||||
|
||||
Crypto::generate_keys(spendKey.publicKey, spendKey.secretKey);
|
||||
return createAddress(spendKey);
|
||||
if (index >= m_walletsContainer.get<RandomAccessIndex>().size()) {
|
||||
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();
|
||||
throwIfStopped();
|
||||
|
||||
|
@ -333,22 +395,22 @@ std::string WalletGreen::createAddress(const KeyPair& spendKey) {
|
|||
m_blockchainSynchronizer.stop();
|
||||
}
|
||||
|
||||
addWallet(spendKey);
|
||||
std::string address = m_currency.accountAddressAsString({ spendKey.publicKey, m_viewPublicKey });
|
||||
addWallet(spendPublicKey, spendSecretKey);
|
||||
std::string address = m_currency.accountAddressAsString({ spendPublicKey, m_viewPublicKey });
|
||||
|
||||
m_blockchainSynchronizer.start();
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
void WalletGreen::addWallet(const KeyPair& spendKey) {
|
||||
void WalletGreen::addWallet(const Crypto::PublicKey& spendPublicKey, const Crypto::SecretKey& spendSecretKey) {
|
||||
time_t creationTimestamp = time(nullptr);
|
||||
|
||||
AccountSubscription sub;
|
||||
sub.keys.address.viewPublicKey = m_viewPublicKey;
|
||||
sub.keys.address.spendPublicKey = spendKey.publicKey;
|
||||
sub.keys.address.spendPublicKey = spendPublicKey;
|
||||
sub.keys.viewSecretKey = m_viewSecretKey;
|
||||
sub.keys.spendSecretKey = spendKey.secretKey;
|
||||
sub.keys.spendSecretKey = spendSecretKey;
|
||||
sub.transactionSpendableAge = 10;
|
||||
sub.syncStart.height = 0;
|
||||
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();
|
||||
|
||||
WalletRecord wallet;
|
||||
wallet.spendPublicKey = spendKey.publicKey;
|
||||
wallet.spendSecretKey = spendKey.secretKey;
|
||||
wallet.spendPublicKey = spendPublicKey;
|
||||
wallet.spendSecretKey = spendSecretKey;
|
||||
wallet.container = container;
|
||||
wallet.creationTimestamp = creationTimestamp;
|
||||
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) {
|
||||
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);
|
||||
|
||||
if (m_state == WalletState::NOT_INITIALIZED) {
|
||||
return;
|
||||
}
|
||||
|
||||
pushEvent(makeSyncProgressUpdatedEvent(current, total));
|
||||
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) {
|
||||
auto& index = m_unlockTransactionsJob.get<BlockHeightIndex>();
|
||||
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
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <queue>
|
||||
|
||||
#include "IFusionManager.h"
|
||||
#include "WalletIndices.h"
|
||||
|
||||
#include <System/Dispatcher.h>
|
||||
|
@ -32,12 +33,14 @@ namespace CryptoNote {
|
|||
|
||||
class WalletGreen : public IWallet,
|
||||
ITransfersObserver,
|
||||
IBlockchainSynchronizerObserver {
|
||||
IBlockchainSynchronizerObserver,
|
||||
IFusionManager {
|
||||
public:
|
||||
WalletGreen(System::Dispatcher& dispatcher, const Currency& currency, INode& node);
|
||||
virtual ~WalletGreen();
|
||||
|
||||
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 shutdown() override;
|
||||
|
||||
|
@ -46,8 +49,10 @@ public:
|
|||
|
||||
virtual size_t getAddressCount() const override;
|
||||
virtual std::string getAddress(size_t index) const override;
|
||||
virtual KeyPair getAddressSpendKey(size_t index) const override;
|
||||
virtual KeyPair getViewKey() const 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 uint64_t getActualBalance() const override;
|
||||
|
@ -69,11 +74,17 @@ public:
|
|||
virtual void stop() 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:
|
||||
void throwIfNotInitialized() const;
|
||||
void throwIfStopped() const;
|
||||
void doShutdown();
|
||||
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 {
|
||||
TransactionTypes::InputKeyInfo keyInfo;
|
||||
|
@ -100,10 +111,12 @@ protected:
|
|||
virtual void onTransactionUpdated(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 synchronizationCompleted(std::error_code result) override;
|
||||
|
||||
void transactionUpdated(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();
|
||||
WalletOuts pickWallet(const std::string& address);
|
||||
|
@ -116,7 +129,7 @@ protected:
|
|||
const WalletRecord& getWalletRecord(CryptoNote::ITransfersContainer* container) 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;
|
||||
void markOutputsSpent(const Crypto::Hash& transactionHash, const std::vector<OutputToTransfer>& selectedTransfers);
|
||||
void deleteSpentOutputs(const Crypto::Hash& transactionHash);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#define BUILD_COMMIT_ID "@VERSION@"
|
||||
#define PROJECT_VERSION "1.0.7"
|
||||
#define PROJECT_VERSION_BUILD_NO "564"
|
||||
#define PROJECT_VERSION "1.0.7.1"
|
||||
#define PROJECT_VERSION_BUILD_NO "571"
|
||||
#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; }
|
||||
|
||||
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 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 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);
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "WalletLegacy/WalletLegacySerializer.h"
|
||||
#include <System/Dispatcher.h>
|
||||
#include <System/Timer.h>
|
||||
#include <System/Context.h>
|
||||
|
||||
using namespace Crypto;
|
||||
using namespace Common;
|
||||
|
@ -129,6 +130,8 @@ protected:
|
|||
template<typename T>
|
||||
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(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() {
|
||||
waitActualBalanceUpdated(alice, alice.getActualBalance());
|
||||
}
|
||||
|
@ -1145,3 +1170,93 @@ TEST_F(WalletApi, transferSmallFeeTransactionThrows) {
|
|||
|
||||
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