IWallet implementation improvements

This commit is contained in:
Antonio Juarez 2015-08-05 14:09:05 +01:00
parent 50cdbfa424
commit 49572fc425
50 changed files with 702 additions and 266 deletions

4
.gitignore vendored
View file

@ -2,3 +2,7 @@
/build
/tags
.idea
.ycm_extra_conf.py
.ycm_extra_conf.pyc
Release
Debug

View file

@ -83,8 +83,8 @@ else()
set(RELEASE_FLAGS "-Ofast -DNDEBUG -Wno-unused-variable")
if(NOT APPLE)
# There is a clang bug that does not allow to compile code that uses AES-NI intrinsics if -flto is enabled
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux"
AND ${CMAKE_BUILD_TYPE} STREQUAL "Release" AND ((CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.9) OR (CMAKE_C_COMPILER_VERSION VERSION_EQUAL 4.9)))
if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME STREQUAL "Linux"
AND CMAKE_BUILD_TYPE STREQUAL "Release" AND ((CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.9) OR (CMAKE_C_COMPILER_VERSION VERSION_EQUAL 4.9)))
# On linux, to build in lto mode, check that ld.gold linker is used: 'update-alternatives --install /usr/bin/ld ld /usr/bin/ld.gold HIGHEST_PRIORITY'
set(CMAKE_AR gcc-ar)
set(CMAKE_RANLIB gcc-ranlib)

View file

@ -155,7 +155,8 @@ const CheckpointData CHECKPOINTS[] = {
{780000, "8dd55a9bae429e3685b90317281e633917023d3512eb7f37372209d1a5fc1070"},
{785500, "de1a487d70964d25ed6f7de196866f357a293e867ee81313e7fd0352d0126bdd"},
{789000, "acef490bbccce3b7b7ae8554a414f55413fbf4ca1472c6359b126a4439bd9f01"},
{796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"}
{796000, "04e387a00d35db21d4d93d04040b31f22573972a7e61d72cc07d0ab69bcb9c44"},
{800000, "d7fa4eea02e5ce60b949136569c0ea7ac71ea46e0065311054072ac415560b86"}
};
} // CryptoNote

View file

@ -261,7 +261,7 @@ bool core::check_tx_semantic(const Transaction& tx, bool keeped_by_block) {
get_inputs_money_amount(tx, amount_in);
uint64_t amount_out = get_outs_money_amount(tx);
if (amount_in <= amount_out) {
if (amount_in < amount_out) {
logger(ERROR) << "tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << getObjectHash(tx);
return false;
}

View file

@ -126,7 +126,7 @@ namespace CryptoNote {
uint64_t outputs_amount = get_outs_money_amount(tx);
if (outputs_amount >= inputs_amount) {
if (outputs_amount > inputs_amount) {
logger(INFO) << "transaction use more money then it has: use " << m_currency.formatAmount(outputs_amount) <<
", have " << m_currency.formatAmount(inputs_amount);
tvc.m_verifivation_failed = true;

View file

@ -23,15 +23,7 @@
#include <sstream>
#include <unordered_set>
#ifdef WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <unistd.h>
#include <stdio.h>
#endif
#include <boost/filesystem/operations.hpp>
#include <System/Timer.h>
#include <System/InterruptedException.h>
@ -114,11 +106,8 @@ std::string createTemporaryFile(const std::string& path, std::fstream& tempFile)
//returns true on success
bool deleteFile(const std::string& filename) {
#ifdef WIN32
return DeleteFile(filename.c_str()) != 0;
#else
return unlink(filename.c_str()) == 0;
#endif
boost::system::error_code err;
return boost::filesystem::remove(filename, err) && !err;
}
void replaceWalletFiles(const std::string &path, const std::string &tempFilePath) {

View file

@ -25,6 +25,7 @@
#include <string.h>
#include <ucontext.h>
#include <unistd.h>
#include "ErrorMessage.h"
namespace System {
@ -40,7 +41,7 @@ public:
MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) {
auto ret = pthread_mutex_lock(&mutex);
if (ret != 0) {
throw std::runtime_error("failed to acquire mutex, errno=" + std::to_string(ret) + ": " + strerror(ret));
throw std::runtime_error("pthread_mutex_lock failed, " + errorMessage(ret));
}
}
@ -62,15 +63,15 @@ Dispatcher::Dispatcher() {
std::string message;
epoll = ::epoll_create1(0);
if (epoll == -1) {
message = "epoll_create1() fail errno=" + std::to_string(errno);
message = "epoll_create1 failed, " + lastErrorMessage();
} else {
mainContext.ucontext = new ucontext_t;
if (getcontext(reinterpret_cast<ucontext_t*>(mainContext.ucontext)) == -1) {
message = "getcontext() fail errno=" + std::to_string(errno);
message = "getcontext failed, " + lastErrorMessage();
} else {
remoteSpawnEvent = eventfd(0, O_NONBLOCK);
if(remoteSpawnEvent == -1) {
message = "eventfd() fail errno=" + std::to_string(errno);
message = "eventfd failed, " + lastErrorMessage();
} else {
remoteSpawnEventContext.writeContext = nullptr;
remoteSpawnEventContext.readContext = nullptr;
@ -80,7 +81,7 @@ Dispatcher::Dispatcher() {
remoteSpawnEventEpollEvent.data.ptr = &remoteSpawnEventContext;
if (epoll_ctl(epoll, EPOLL_CTL_ADD, remoteSpawnEvent, &remoteSpawnEventEpollEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
*reinterpret_cast<pthread_mutex_t*>(this->mutex) = pthread_mutex_t(PTHREAD_MUTEX_INITIALIZER);
@ -155,7 +156,7 @@ void Dispatcher::clear() {
while (!timers.empty()) {
int result = ::close(timers.top());
if (result == -1) {
throw std::runtime_error("Dispatcher::clear, close failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::clear, close failed, " + lastErrorMessage());
}
timers.pop();
@ -179,7 +180,7 @@ void Dispatcher::dispatch() {
uint64_t buf;
auto transferred = read(remoteSpawnEvent, &buf, sizeof buf);
if(transferred == -1) {
throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, read(remoteSpawnEvent) failed, " + lastErrorMessage());
}
MutextGuard guard(*reinterpret_cast<pthread_mutex_t*>(this->mutex));
@ -206,7 +207,7 @@ void Dispatcher::dispatch() {
}
if (errno != EINTR) {
throw std::runtime_error("Dispatcher::dispatch(), epoll_wait() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, epoll_wait failed, " + lastErrorMessage());
}
}
@ -214,7 +215,7 @@ void Dispatcher::dispatch() {
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext->ucontext);
currentContext = context;
if (swapcontext(oldContext, static_cast<ucontext_t *>(context->ucontext)) == -1) {
throw std::runtime_error("Dispatcher::dispatch() swapcontext() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, swapcontext failed, " + lastErrorMessage());
}
}
}
@ -269,7 +270,7 @@ void Dispatcher::remoteSpawn(std::function<void()>&& procedure) {
uint64_t one = 1;
auto transferred = write(remoteSpawnEvent, &one, sizeof one);
if(transferred == - 1) {
throw std::runtime_error("Dispatcher::remoteSpawn, write() failed errno = " + std::to_string(errno));
throw std::runtime_error("Dispatcher::remoteSpawn, write failed, " + lastErrorMessage());
}
}
@ -308,7 +309,7 @@ void Dispatcher::yield() {
uint64_t buf;
auto transferred = read(remoteSpawnEvent, &buf, sizeof buf);
if(transferred == -1) {
throw std::runtime_error("Dispatcher::dispatch() read(remoteSpawnEvent) fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, read(remoteSpawnEvent) failed, " + lastErrorMessage());
}
MutextGuard guard(*reinterpret_cast<pthread_mutex_t*>(this->mutex));
@ -334,7 +335,7 @@ void Dispatcher::yield() {
}
} else {
if (errno != EINTR) {
throw std::runtime_error("Dispatcher::dispatch(), epoll_wait() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, epoll_wait failed, " + lastErrorMessage());
}
}
}
@ -353,7 +354,7 @@ NativeContext& Dispatcher::getReusableContext() {
if(firstReusableContext == nullptr) {
ucontext_t* newlyCreatedContext = new ucontext_t;
if (getcontext(newlyCreatedContext) == -1) { //makecontext precondition
throw std::runtime_error("Dispatcher::getReusableContext(), getcontext() fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::getReusableContext, getcontext failed, " + lastErrorMessage());
}
auto stackPointer = new uint8_t[STACK_SIZE];
@ -365,7 +366,7 @@ NativeContext& Dispatcher::getReusableContext() {
ucontext_t* oldContext = static_cast<ucontext_t*>(currentContext->ucontext);
if (swapcontext(oldContext, newlyCreatedContext) == -1) {
throw std::runtime_error("Dispatcher::getReusableContext() swapcontext() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::getReusableContext, swapcontext failed, " + lastErrorMessage());
}
assert(firstReusableContext != nullptr);
@ -393,7 +394,7 @@ int Dispatcher::getTimer() {
timerEvent.data.ptr = nullptr;
if (epoll_ctl(getEpoll(), EPOLL_CTL_ADD, timer, &timerEvent) == -1) {
throw std::runtime_error("Dispatcher::getTimer(), epoll_ctl() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::getTimer, epoll_ctl failed, " + lastErrorMessage());
}
} else {
timer = timers.top();
@ -416,7 +417,7 @@ void Dispatcher::contextProcedure(void* ucontext) {
firstReusableContext = &context;
ucontext_t* oldContext = static_cast<ucontext_t*>(context.ucontext);
if (swapcontext(oldContext, static_cast<ucontext_t*>(currentContext->ucontext)) == -1) {
throw std::runtime_error("Dispatcher::contextProcedure() swapcontext() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::contextProcedure, swapcontext failed, " + lastErrorMessage());
}
for (;;) {

View file

@ -0,0 +1,32 @@
// 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 "ErrorMessage.h"
#include <cerrno>
#include <cstring>
namespace System {
std::string lastErrorMessage() {
return errorMessage(errno);
}
std::string errorMessage(int err) {
return "result=" + std::to_string(err) + ", " + std::strerror(err);
}
}

View file

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

View file

@ -23,6 +23,7 @@
#include <netdb.h>
#include <System/Dispatcher.h>
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -62,7 +63,7 @@ Ipv4Address Ipv4Resolver::resolve(const std::string& host) {
addrinfo* addressInfos;
int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos);
if (result != 0) {
throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result));
throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, " + errorMessage(result));
}
std::size_t count = 0;

View file

@ -22,6 +22,7 @@
#include <sys/epoll.h>
#include <unistd.h>
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -54,7 +55,7 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
assert(contextPair.readContext == nullptr);
assert(contextPair.writeContext == nullptr);
if (close(connection) == -1) {
throw std::runtime_error("TcpConnection::operator=, close() failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::operator=, close failed, " + lastErrorMessage());
}
}
@ -81,7 +82,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
message = "recv failed, errno=" + std::to_string(errno);
message = "recv failed, " + lastErrorMessage();
} else {
epoll_event connectionEvent;
OperationContext operationContext;
@ -97,7 +98,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
}
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
dispatcher->getCurrentContext()->interruptProcedure = [&]() {
assert(dispatcher != nullptr);
@ -107,7 +108,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail, errno=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::stop, epoll_ctl failed, " + lastErrorMessage());
}
contextPair.readContext->interrupted = true;
@ -132,7 +133,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
connectionEvent.data.ptr = &contextPair;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
throw std::runtime_error("TcpConnection::read");
}
}
@ -143,7 +144,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
message = "recv failed, errno=" + std::to_string(errno);
message = "recv failed, " + lastErrorMessage();
} else {
assert(transferred <= static_cast<ssize_t>(size));
return transferred;
@ -168,7 +169,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) {
std::string message;
if(size == 0) {
if(shutdown(connection, SHUT_WR) == -1) {
throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::write, shutdown failed, " + lastErrorMessage());
}
return 0;
@ -177,7 +178,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) {
ssize_t transferred = ::send(connection, (void *)data, size, MSG_NOSIGNAL);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
message = "send failed, result=" + std::to_string(errno);
message = "send failed, " + lastErrorMessage();
} else {
epoll_event connectionEvent;
OperationContext operationContext;
@ -193,7 +194,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) {
}
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
dispatcher->getCurrentContext()->interruptProcedure = [&]() {
assert(dispatcher != nullptr);
@ -203,7 +204,7 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) {
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
throw std::runtime_error("TcpConnection::stop, epoll_ctl() fail" + std::to_string(errno));
throw std::runtime_error("TcpConnection::stop, epoll_ctl failed, " + lastErrorMessage());
}
contextPair.writeContext->interrupted = true;
@ -228,18 +229,18 @@ std::size_t TcpConnection::write(const uint8_t* data, size_t size) {
connectionEvent.data.ptr = &contextPair;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, connection, &connectionEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
throw std::runtime_error("TcpConnection::write, " + message);
}
}
if((operationContext.events & (EPOLLERR | EPOLLHUP)) != 0) {
throw std::runtime_error("TcpConnection::write: events & (EPOLLERR | EPOLLHUP) != 0");
throw std::runtime_error("TcpConnection::write, events & (EPOLLERR | EPOLLHUP) != 0");
}
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
message = "send failed, errno=" + std::to_string(errno);
message = "send failed, " + lastErrorMessage();
} else {
assert(transferred <= static_cast<ssize_t>(size));
return transferred;
@ -258,7 +259,7 @@ std::pair<Ipv4Address, uint16_t> TcpConnection::getPeerAddressAndPort() const {
sockaddr_in addr;
socklen_t size = sizeof(addr);
if (getpeername(connection, reinterpret_cast<sockaddr*>(&addr), &size) != 0) {
throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, " + lastErrorMessage());
}
assert(size == sizeof(sockaddr_in));
@ -273,7 +274,7 @@ TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&d
connectionEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, socket, &connectionEvent) == -1) {
throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl() fail, errno=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::TcpConnection, epoll_ctl failed, " + lastErrorMessage());
}
}

View file

@ -27,6 +27,7 @@
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
#include "Dispatcher.h"
#include "ErrorMessage.h"
#include "TcpConnection.h"
namespace System {
@ -77,18 +78,18 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
std::string message;
int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == -1) {
message = "socket() failed, errno=" + std::to_string(errno);
message = "socket failed, " + lastErrorMessage();
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
message = "bind failed, errno=" + std::to_string(errno);
message = "bind failed, " + lastErrorMessage();
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
message = "fcntl() failed errno=" + std::to_string(errno);
message = "fcntl failed, " + lastErrorMessage();
} else {
sockaddr_in addressData;
addressData.sin_family = AF_INET;
@ -111,14 +112,14 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
connectEvent.events = EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT;
connectEvent.data.ptr = &contextPair;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_ADD, connection, &connectEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
context = &connectorContext;
dispatcher->getCurrentContext()->interruptProcedure = [&] {
TcpConnectorContextExt* connectorContext1 = static_cast<TcpConnectorContextExt*>(context);
if (!connectorContext1->interrupted) {
if (close(connectorContext1->connection) == -1) {
throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::stop, close failed, " + lastErrorMessage());
}
connectorContext1->interrupted = true;
@ -139,7 +140,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
}
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_DEL, connection, NULL) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
if((connectorContext.events & (EPOLLERR | EPOLLHUP)) != 0) {
int result = close(connection);
@ -152,10 +153,10 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
message = "getsockopt() failed, errno=" + std::to_string(errno);
message = "getsockopt failed, " + lastErrorMessage();
} else {
if (retval != 0) {
message = "connect failed; getsockopt retval =" + std::to_string(errno);
message = "getsockopt failed, " + lastErrorMessage();
} else {
return TcpConnection(*dispatcher, connection);
}

View file

@ -27,6 +27,7 @@
#include "Dispatcher.h"
#include "TcpConnection.h"
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -39,31 +40,31 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16
std::string message;
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
message = "socket() failed, errno=" + std::to_string(errno) + ": " + strerror(errno);
message = "socket failed, " + lastErrorMessage();
} else {
int flags = fcntl(listener, F_GETFL, 0);
if (flags == -1 || fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1) {
message = "fcntl() failed errno=" + std::to_string(errno) + ": " + strerror(errno);
message = "fcntl failed, " + lastErrorMessage();
} else {
int on = 1;
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) {
message = "setsockopt failed, errno=" + std::to_string(errno) + ": " + strerror(errno);
message = "setsockopt failed, " + lastErrorMessage();
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = htonl( addr.getValue());
if (bind(listener, reinterpret_cast<sockaddr *>(&address), sizeof address) != 0) {
message = "bind failed, errno=" + std::to_string(errno) + ": " + strerror(errno);
message = "bind failed, " + lastErrorMessage();
} else if (listen(listener, SOMAXCONN) != 0) {
message = "listen failed, errno=" + std::to_string(errno) + ": " + strerror(errno);
message = "listen failed, " + lastErrorMessage();
} else {
epoll_event listenEvent;
listenEvent.events = 0;
listenEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher.getEpoll(), EPOLL_CTL_ADD, listener, &listenEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno) + ": " + strerror(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
context = nullptr;
return;
@ -100,7 +101,7 @@ TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
throw std::runtime_error("TcpListener::operator=, close failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::operator=, close failed, " + lastErrorMessage());
}
}
@ -135,7 +136,7 @@ TcpConnection TcpListener::accept() {
listenEvent.data.ptr = &contextPair;
std::string message;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) {
message = "epoll_ctl() failed, errno=" + std::to_string(errno);
message = "epoll_ctl failed, " + lastErrorMessage();
} else {
context = &listenerContext;
dispatcher->getCurrentContext()->interruptProcedure = [&]() {
@ -148,7 +149,7 @@ TcpConnection TcpListener::accept() {
listenEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, listener, &listenEvent) == -1) {
throw std::runtime_error("TcpListener::stop, epoll_ctl() failed, errno=" + std::to_string(errno) );
throw std::runtime_error("TcpListener::stop, epoll_ctl failed, " + lastErrorMessage() );
}
listenerContext->interrupted = true;
@ -176,11 +177,11 @@ TcpConnection TcpListener::accept() {
socklen_t inLen = sizeof(inAddr);
int connection = ::accept(listener, &inAddr, &inLen);
if (connection == -1) {
message = "accept() failed, errno=" + std::to_string(errno);
message = "accept failed, " + lastErrorMessage();
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
message = "fcntl() failed errno=" + std::to_string(errno);
message = "fcntl failed, " + lastErrorMessage();
} else {
return TcpConnection(*dispatcher, connection);
}

View file

@ -24,6 +24,7 @@
#include <unistd.h>
#include "Dispatcher.h"
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
namespace System {
@ -92,7 +93,7 @@ void Timer::sleep(std::chrono::nanoseconds duration) {
timerEvent.data.ptr = &contextPair;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) {
throw std::runtime_error("Timer::sleep, epoll_ctl() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Timer::sleep, epoll_ctl failed, " + lastErrorMessage());
}
dispatcher->getCurrentContext()->interruptProcedure = [&]() {
assert(dispatcher != nullptr);
@ -105,7 +106,7 @@ void Timer::sleep(std::chrono::nanoseconds duration) {
timerContext->interrupted = true;
dispatcher->pushContext(timerContext->context);
} else {
throw std::runtime_error("Timer::interrupt, read failed, errno=" + std::to_string(errno));
throw std::runtime_error("Timer::interrupt, read failed, " + lastErrorMessage());
}
} else {
assert(value>0);
@ -117,7 +118,7 @@ void Timer::sleep(std::chrono::nanoseconds duration) {
timerEvent.data.ptr = nullptr;
if (epoll_ctl(dispatcher->getEpoll(), EPOLL_CTL_MOD, timer, &timerEvent) == -1) {
throw std::runtime_error("Timer::interrupt epoll_ctl() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Timer::interrupt, epoll_ctl failed, " + lastErrorMessage());
}
}
};

View file

@ -27,6 +27,7 @@
#include <stdio.h>
#include <unistd.h>
#include "Context.h"
#include "ErrorMessage.h"
namespace System {
@ -42,7 +43,7 @@ public:
MutextGuard(pthread_mutex_t& _mutex) : mutex(_mutex) {
auto ret = pthread_mutex_lock(&mutex);
if (ret != 0) {
throw std::runtime_error("failed to acquire mutex, errno=" + std::to_string(ret) + ": " + strerror(ret));
throw std::runtime_error("MutextGuard::MutextGuard, pthread_mutex_lock failed, " + errorMessage(ret));
}
}
@ -64,19 +65,19 @@ Dispatcher::Dispatcher() : lastCreatedTimer(0) {
std::string message;
kqueue = ::kqueue();
if (kqueue == -1) {
message = "kqueue() fail errno=" + std::to_string(errno);
message = "kqueue failed, " + lastErrorMessage();
} else {
mainContext.uctx = new uctx;
if (getcontext(static_cast<uctx*>(mainContext.uctx)) == -1) {
message = "getcontext() fail errno=" + std::to_string(errno);
message = "getcontext failed, " + lastErrorMessage();
} else {
struct kevent event;
EV_SET(&event, 0, EVFILT_USER, EV_ADD, NOTE_FFNOP, 0, NULL);
if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() fail errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
if(pthread_mutex_init(reinterpret_cast<pthread_mutex_t*>(this->mutex), NULL) == -1) {
message = "pthread_mutex_init() fail errno=" + std::to_string(errno);
message = "pthread_mutex_init failed, " + lastErrorMessage();
} else {
remoteSpawned = false;
@ -165,7 +166,7 @@ void Dispatcher::dispatch() {
struct kevent event;
EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_DISABLE, NOTE_FFNOP, 0, NULL);
if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("kevent() fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, kevent failed, " + lastErrorMessage());
}
continue;
@ -176,7 +177,7 @@ void Dispatcher::dispatch() {
}
if (errno != EINTR) {
throw std::runtime_error("Dispatcher::dispatch(), kqueue() fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, kqueue failed, " + lastErrorMessage());
} else {
MutextGuard guard(*reinterpret_cast<pthread_mutex_t*>(this->mutex));
while (!remoteSpawningProcedures.empty()) {
@ -191,7 +192,7 @@ void Dispatcher::dispatch() {
uctx* oldContext = static_cast<uctx*>(currentContext->uctx);
currentContext = context;
if (swapcontext(oldContext,static_cast<uctx*>(currentContext->uctx)) == -1) {
throw std::runtime_error("Dispatcher::dispatch(), swapcontext() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, swapcontext failed, " + lastErrorMessage());
}
}
}
@ -246,7 +247,7 @@ void Dispatcher::remoteSpawn(std::function<void()>&& procedure) {
struct kevent event;
EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_ENABLE, NOTE_FFCOPY | NOTE_TRIGGER, 0, NULL);
if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("Dispatcher::remoteSpawn(), kevent() fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::remoteSpawn, kevent failed, " + lastErrorMessage());
};
}
}
@ -286,7 +287,7 @@ void Dispatcher::yield() {
struct kevent event;
EV_SET(&event, 0, EVFILT_USER, EV_ADD | EV_DISABLE, NOTE_FFNOP, 0, NULL);
if (kevent(kqueue, &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("kevent() fail errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::yield, kevent failed, " + lastErrorMessage());
}
MutextGuard guard(*reinterpret_cast<pthread_mutex_t*>(this->mutex));
@ -304,7 +305,7 @@ void Dispatcher::yield() {
}
} else {
if (errno != EINTR) {
throw std::runtime_error("Dispatcher::dispatch(), epoll_wait() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::dispatch, kevent failed, " + lastErrorMessage());
}
}
}
@ -331,7 +332,7 @@ NativeContext& Dispatcher::getReusableContext() {
uctx* oldContext = static_cast<uctx*>(currentContext->uctx);
if (swapcontext(oldContext, newlyCreatedContext) == -1) {
throw std::runtime_error("Dispatcher::getReusableContext(), swapcontext() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::getReusableContext, swapcontext failed, " + lastErrorMessage());
}
assert(firstReusableContext != nullptr);
@ -375,7 +376,7 @@ void Dispatcher::contextProcedure(void* ucontext) {
firstReusableContext = &context;
uctx* oldContext = static_cast<uctx*>(context.uctx);
if (swapcontext(oldContext, static_cast<uctx*>(currentContext->uctx)) == -1) {
throw std::runtime_error("Dispatcher::contextProcedure() swapcontext() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Dispatcher::contextProcedure, swapcontext failed, " + lastErrorMessage());
}
for (;;) {

View file

@ -0,0 +1,32 @@
// 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 "ErrorMessage.h"
#include <cerrno>
#include <cstring>
namespace System {
std::string lastErrorMessage() {
return errorMessage(errno);
}
std::string errorMessage(int err) {
return "result=" + std::to_string(err) + ", " + std::strerror(err);
}
}

View file

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

View file

@ -23,6 +23,7 @@
#include <netdb.h>
#include <System/Dispatcher.h>
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -62,7 +63,7 @@ Ipv4Address Ipv4Resolver::resolve(const std::string& host) {
addrinfo* addressInfos;
int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos);
if (result != 0) {
throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result));
throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, " + errorMessage(result));
}
std::size_t count = 0;

View file

@ -25,6 +25,7 @@
#include <unistd.h>
#include "Dispatcher.h"
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -58,7 +59,7 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
assert(readContext == nullptr);
assert(writeContext == nullptr);
if (close(connection) == -1) {
throw std::runtime_error("TcpConnection::operator=, close() failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::operator=, close failed, " + lastErrorMessage());
}
}
@ -86,7 +87,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
message = "recv failed, errno=" + std::to_string(errno);
message = "recv failed, " + lastErrorMessage();
} else {
OperationContext context;
context.context = dispatcher->getCurrentContext();
@ -94,7 +95,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
struct kevent event;
EV_SET(&event, connection, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT, 0, 0, &context);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() failed, errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
readContext = &context;
dispatcher->getCurrentContext()->interruptProcedure = [&] {
@ -106,7 +107,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
EV_SET(&event, connection, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("TcpListener::interruptionProcedure, kevent() failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::interruptionProcedure, kevent failed, " + lastErrorMessage());
}
context->interrupted = true;
@ -127,7 +128,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
ssize_t transferred = ::recv(connection, (void *)data, size, 0);
if (transferred == -1) {
message = "recv failed, errno=" + std::to_string(errno);
message = "recv failed, " + lastErrorMessage();
} else {
assert(transferred <= static_cast<ssize_t>(size));
return transferred;
@ -152,7 +153,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
std::string message;
if (size == 0) {
if (shutdown(connection, SHUT_WR) == -1) {
throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::write, shutdown failed, " + lastErrorMessage());
}
return 0;
@ -161,7 +162,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
message = "send failed, result=" + std::to_string(errno);
message = "send failed, " + lastErrorMessage();
} else {
OperationContext context;
context.context = dispatcher->getCurrentContext();
@ -169,7 +170,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, &context);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() failed, errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
writeContext = &context;
dispatcher->getCurrentContext()->interruptProcedure = [&] {
@ -181,7 +182,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
EV_SET(&event, connection, EVFILT_WRITE, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::stop, kevent failed, " + lastErrorMessage());
}
context->interrupted = true;
@ -202,7 +203,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
ssize_t transferred = ::send(connection, (void *)data, size, 0);
if (transferred == -1) {
message = "send failed, errno=" + std::to_string(errno);
message = "send failed, " + lastErrorMessage();
} else {
assert(transferred <= static_cast<ssize_t>(size));
return transferred;
@ -221,7 +222,7 @@ std::pair<Ipv4Address, uint16_t> TcpConnection::getPeerAddressAndPort() const {
sockaddr_in addr;
socklen_t size = sizeof(addr);
if (getpeername(connection, reinterpret_cast<sockaddr*>(&addr), &size) != 0) {
throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, " + lastErrorMessage());
}
assert(size == sizeof(sockaddr_in));
@ -231,7 +232,7 @@ std::pair<Ipv4Address, uint16_t> TcpConnection::getPeerAddressAndPort() const {
TcpConnection::TcpConnection(Dispatcher& dispatcher, int socket) : dispatcher(&dispatcher), connection(socket), readContext(nullptr), writeContext(nullptr) {
int val = 1;
if (setsockopt(connection, SOL_SOCKET, SO_NOSIGPIPE, (void*)&val, sizeof val) == -1) {
throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, result=" + std::to_string(errno));
throw std::runtime_error("TcpConnection::TcpConnection, setsockopt failed, " + lastErrorMessage());
}
}

View file

@ -29,6 +29,7 @@
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
#include "Dispatcher.h"
#include "ErrorMessage.h"
#include "TcpConnection.h"
namespace System {
@ -80,18 +81,18 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
std::string message;
int connection = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == -1) {
message = "socket() failed, errno=" + std::to_string(errno);
message = "socket failed, " + lastErrorMessage();
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
message = "bind failed, errno=" + std::to_string(errno);
message = "bind failed, " + lastErrorMessage();
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
message = "fcntl() failed errno=" + std::to_string(errno);
message = "fcntl failed, " + lastErrorMessage();
} else {
sockaddr_in addressData;
addressData.sin_family = AF_INET;
@ -108,7 +109,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
struct kevent event;
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT | EV_CLEAR, 0, 0, &connectorContext);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() failed, errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
context = &connectorContext;
dispatcher->getCurrentContext()->interruptProcedure = [&] {
@ -117,7 +118,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
ConnectorContext* connectorContext = static_cast<ConnectorContext*>(context);
if (!connectorContext->interrupted) {
if (close(connectorContext->connection) == -1) {
throw std::runtime_error("TcpListener::stop, close failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::stop, close failed, " + lastErrorMessage());
}
dispatcher->pushContext(connectorContext->context);
@ -140,16 +141,16 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
EV_SET(&event, connection, EVFILT_WRITE, EV_ADD | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() failed, errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
int retval = -1;
socklen_t retValLen = sizeof(retval);
int s = getsockopt(connection, SOL_SOCKET, SO_ERROR, &retval, &retValLen);
if (s == -1) {
message = "getsockopt() failed, errno=" + std::to_string(errno);
message = "getsockopt failed, " + lastErrorMessage();
} else {
if (retval != 0) {
message = "connect failed; getsockopt retval =" + std::to_string(errno);
message = "getsockopt failed, " + lastErrorMessage();
} else {
return TcpConnection(*dispatcher, connection);
}

View file

@ -29,6 +29,7 @@
#include "Dispatcher.h"
#include "TcpConnection.h"
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -41,30 +42,30 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16
std::string message;
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == -1) {
message = "socket() failed, errno=" + std::to_string(errno);
message = "socket failed, " + lastErrorMessage();
} else {
int flags = fcntl(listener, F_GETFL, 0);
if (flags == -1 || (fcntl(listener, F_SETFL, flags | O_NONBLOCK) == -1)) {
message = "fcntl() failed errno=" + std::to_string(errno);
message = "fcntl failed, " + lastErrorMessage();
} else {
int on = 1;
if (setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) == -1) {
message = "setsockopt failed, errno=" + std::to_string(errno);
message = "setsockopt failed, " + lastErrorMessage();
} else {
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = htonl(addr.getValue());
if (bind(listener, reinterpret_cast<sockaddr*>(&address), sizeof address) != 0) {
message = "bind failed, errno=" + std::to_string(errno);
message = "bind failed, " + lastErrorMessage();
} else if (listen(listener, SOMAXCONN) != 0) {
message = "listen failed, errno=" + std::to_string(errno);
message = "listen failed, " + lastErrorMessage();
} else {
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_DISABLE | EV_CLEAR, 0, SOMAXCONN, NULL);
if (kevent(dispatcher.getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() failed, errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
context = nullptr;
return;
@ -74,7 +75,7 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& addr, uint16
}
if (close(listener) == -1) {
message = "close failed, errno=" + std::to_string(errno);
message = "close failed, " + lastErrorMessage();
}
}
@ -102,7 +103,7 @@ TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (close(listener) == -1) {
throw std::runtime_error("TcpListener::operator=, close failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::operator=, close failed, " + lastErrorMessage());
}
}
@ -131,7 +132,7 @@ TcpConnection TcpListener::accept() {
struct kevent event;
EV_SET(&event, listener, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR , 0, SOMAXCONN, &listenerContext);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
message = "kevent() failed, errno=" + std::to_string(errno);
message = "kevent failed, " + lastErrorMessage();
} else {
context = &listenerContext;
dispatcher->getCurrentContext()->interruptProcedure = [&] {
@ -144,7 +145,7 @@ TcpConnection TcpListener::accept() {
EV_SET(&event, listener, EVFILT_READ, EV_DELETE | EV_DISABLE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("TcpListener::stop, kevent() failed, errno=" + std::to_string(errno));
throw std::runtime_error("TcpListener::stop, kevent failed, " + lastErrorMessage());
}
listenerContext->interrupted = true;
@ -167,11 +168,11 @@ TcpConnection TcpListener::accept() {
socklen_t inLen = sizeof(inAddr);
int connection = ::accept(listener, &inAddr, &inLen);
if (connection == -1) {
message = "accept() failed, errno=" + std::to_string(errno);
message = "accept failed, " + lastErrorMessage();
} else {
int flags = fcntl(connection, F_GETFL, 0);
if (flags == -1 || fcntl(connection, F_SETFL, flags | O_NONBLOCK) == -1) {
message = "fcntl() failed errno=" + std::to_string(errno);
message = "fcntl failed, " + lastErrorMessage();
} else {
return TcpConnection(*dispatcher, connection);
}

View file

@ -26,6 +26,7 @@
#include <unistd.h>
#include "Dispatcher.h"
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
namespace System {
@ -79,7 +80,7 @@ void Timer::sleep(std::chrono::nanoseconds duration) {
EV_SET(&event, timer, EVFILT_TIMER, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_NSECONDS, duration.count(), &timerContext);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Timer::stop, kevent failed, " + lastErrorMessage());
}
context = &timerContext;
@ -92,7 +93,7 @@ void Timer::sleep(std::chrono::nanoseconds duration) {
EV_SET(&event, timer, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
if (kevent(dispatcher->getKqueue(), &event, 1, NULL, 0, NULL) == -1) {
throw std::runtime_error("Timer::stop, kevent() failed, errno=" + std::to_string(errno));
throw std::runtime_error("Timer::stop, kevent failed, " + lastErrorMessage());
}
dispatcher->pushContext(timerContext->context);

View file

@ -25,6 +25,7 @@
#define NOMINMAX
#endif
#include <winsock2.h>
#include "ErrorMessage.h"
namespace System {
@ -44,16 +45,16 @@ Dispatcher::Dispatcher() {
assert(result != FALSE);
std::string message;
if (ConvertThreadToFiberEx(NULL, 0) == NULL) {
message = "ConvertThreadToFiberEx failed, result=" + std::to_string(GetLastError());
message = "ConvertThreadToFiberEx failed, " + lastErrorMessage();
} else {
completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
if (completionPort == NULL) {
message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError());
message = "CreateIoCompletionPort failed, " + lastErrorMessage();
} else {
WSADATA wsaData;
int wsaResult = WSAStartup(0x0202, &wsaData);
if (wsaResult != 0) {
message = "WSAStartup failed, result=" + std::to_string(wsaResult);
message = "WSAStartup failed, " + errorMessage(wsaResult);
} else {
remoteNotificationSent = false;
reinterpret_cast<LPOVERLAPPED>(remoteSpawnOverlapped)->hEvent = NULL;
@ -179,7 +180,7 @@ void Dispatcher::dispatch() {
}
if (lastError != WAIT_IO_COMPLETION) {
throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError));
throw std::runtime_error("Dispatcher::dispatch, GetQueuedCompletionStatusEx failed, " + errorMessage(lastError));
}
}
@ -241,7 +242,7 @@ void Dispatcher::remoteSpawn(std::function<void()>&& procedure) {
remoteNotificationSent = true;
if (PostQueuedCompletionStatus(completionPort, 0, 0, reinterpret_cast<LPOVERLAPPED>(remoteSpawnOverlapped)) == NULL) {
LeaveCriticalSection(reinterpret_cast<LPCRITICAL_SECTION>(criticalSection));
throw std::runtime_error("Dispatcher::remoteSpawn, PostQueuedCompletionStatus failed, result=" + std::to_string(GetLastError()));
throw std::runtime_error("Dispatcher::remoteSpawn, PostQueuedCompletionStatus failed, " + lastErrorMessage());
};
}
@ -313,7 +314,7 @@ void Dispatcher::yield() {
if (lastError == WAIT_TIMEOUT) {
break;
} else if (lastError != WAIT_IO_COMPLETION) {
throw std::runtime_error("Dispatcher::yield, GetQueuedCompletionStatusEx failed, result=" + std::to_string(lastError));
throw std::runtime_error("Dispatcher::yield, GetQueuedCompletionStatusEx failed, " + errorMessage(lastError));
}
}
}
@ -337,7 +338,7 @@ NativeContext& Dispatcher::getReusableContext() {
if (firstReusableContext == nullptr) {
void* fiber = CreateFiberEx(STACK_SIZE, RESERVE_STACK_SIZE, 0, contextProcedureStatic, this);
if (fiber == NULL) {
throw std::runtime_error("Dispatcher::getReusableContext, CreateFiberEx failed, result=" + std::to_string(GetLastError()));
throw std::runtime_error("Dispatcher::getReusableContext, CreateFiberEx failed, " + lastErrorMessage());
}
SwitchToFiber(fiber);

View file

@ -0,0 +1,45 @@
// 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 "ErrorMessage.h"
#include <cstddef>
#include <windows.h>
namespace System {
std::string lastErrorMessage() {
return errorMessage(GetLastError());
}
std::string errorMessage(int error) {
struct Buffer {
~Buffer() {
if (pointer != nullptr) {
LocalFree(pointer);
}
}
LPTSTR pointer = nullptr;
} buffer;
auto size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPTSTR>(&buffer.pointer), 0, nullptr);
return "result=" + std::to_string(error) + ", " + std::string(buffer.pointer, size);
}
}

View file

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

View file

@ -23,6 +23,7 @@
#endif
#include <ws2tcpip.h>
#include <System/Dispatcher.h>
#include <System/ErrorMessage.h>
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
@ -62,7 +63,7 @@ Ipv4Address Ipv4Resolver::resolve(const std::string& host) {
addrinfo* addressInfos;
int result = getaddrinfo(host.c_str(), NULL, &hints, &addressInfos);
if (result != 0) {
throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, result=" + std::to_string(result));
throw std::runtime_error("Ipv4Resolver::resolve, getaddrinfo failed, " + errorMessage(result));
}
size_t count = 0;

View file

@ -25,6 +25,7 @@
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
#include "Dispatcher.h"
#include "ErrorMessage.h"
namespace System {
@ -65,7 +66,7 @@ TcpConnection& TcpConnection::operator=(TcpConnection&& other) {
assert(readContext == nullptr);
assert(writeContext == nullptr);
if (closesocket(connection) != 0) {
throw std::runtime_error("TcpConnection::operator=, closesocket failed, result=" + std::to_string(WSAGetLastError()));
throw std::runtime_error("TcpConnection::operator=, closesocket failed, " + errorMessage(WSAGetLastError()));
}
}
@ -96,7 +97,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
if (WSARecv(connection, &buf, 1, NULL, &flags, &context, NULL) != 0) {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
throw std::runtime_error("TcpConnection::read, WSARecv failed, result=" + std::to_string(lastError));
throw std::runtime_error("TcpConnection::read, WSARecv failed, " + errorMessage(lastError));
}
}
@ -112,7 +113,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
if (CancelIoEx(reinterpret_cast<HANDLE>(connection), context) != TRUE) {
DWORD lastError = GetLastError();
if (lastError != ERROR_NOT_FOUND) {
throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError()));
throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, " + lastErrorMessage());
}
context->context->interrupted = true;
@ -132,7 +133,7 @@ size_t TcpConnection::read(uint8_t* data, size_t size) {
if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) {
int lastError = WSAGetLastError();
if (lastError != ERROR_OPERATION_ABORTED) {
throw std::runtime_error("TcpConnection::read, WSAGetOverlappedResult failed, result=" + std::to_string(lastError));
throw std::runtime_error("TcpConnection::read, WSAGetOverlappedResult failed, " + errorMessage(lastError));
}
assert(context.interrupted);
@ -153,7 +154,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
if (size == 0) {
if (shutdown(connection, SD_SEND) != 0) {
throw std::runtime_error("TcpConnection::write, shutdown failed, result=" + std::to_string(WSAGetLastError()));
throw std::runtime_error("TcpConnection::write, shutdown failed, " + errorMessage(WSAGetLastError()));
}
return 0;
@ -165,7 +166,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
if (WSASend(connection, &buf, 1, NULL, 0, &context, NULL) != 0) {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
throw std::runtime_error("TcpConnection::write, WSASend failed, result=" + std::to_string(lastError));
throw std::runtime_error("TcpConnection::write, WSASend failed, " + errorMessage(lastError));
}
}
@ -180,7 +181,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
if (CancelIoEx(reinterpret_cast<HANDLE>(connection), context) != TRUE) {
DWORD lastError = GetLastError();
if (lastError != ERROR_NOT_FOUND) {
throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, result=" + std::to_string(GetLastError()));
throw std::runtime_error("TcpConnection::stop, CancelIoEx failed, " + lastErrorMessage());
}
context->context->interrupted = true;
@ -201,7 +202,7 @@ size_t TcpConnection::write(const uint8_t* data, size_t size) {
if (WSAGetOverlappedResult(connection, &context, &transferred, FALSE, &flags) != TRUE) {
int lastError = WSAGetLastError();
if (lastError != ERROR_OPERATION_ABORTED) {
throw std::runtime_error("TcpConnection::write, WSAGetOverlappedResult failed, result=" + std::to_string(lastError));
throw std::runtime_error("TcpConnection::write, WSAGetOverlappedResult failed, " + errorMessage(lastError));
}
assert(context.interrupted);
@ -217,7 +218,7 @@ std::pair<Ipv4Address, uint16_t> TcpConnection::getPeerAddressAndPort() const {
sockaddr_in address;
int size = sizeof(address);
if (getpeername(connection, reinterpret_cast<sockaddr*>(&address), &size) != 0) {
throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, result=" + std::to_string(WSAGetLastError()));
throw std::runtime_error("TcpConnection::getPeerAddress, getpeername failed, " + errorMessage(WSAGetLastError()));
}
assert(size == sizeof(sockaddr_in));

View file

@ -25,6 +25,7 @@
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
#include "Dispatcher.h"
#include "ErrorMessage.h"
#include "TcpConnection.h"
namespace System {
@ -81,23 +82,23 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
std::string message;
SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == INVALID_SOCKET) {
message = "socket failed, result=" + std::to_string(WSAGetLastError());
message = "socket failed, " + errorMessage(WSAGetLastError());
} else {
sockaddr_in bindAddress;
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = 0;
bindAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(connection, reinterpret_cast<sockaddr*>(&bindAddress), sizeof bindAddress) != 0) {
message = "bind failed, result=" + std::to_string(WSAGetLastError());
message = "bind failed, " + errorMessage(WSAGetLastError());
} else {
GUID guidConnectEx = WSAID_CONNECTEX;
DWORD read = sizeof connectEx;
if (connectEx == nullptr && WSAIoctl(connection, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidConnectEx, sizeof guidConnectEx, &connectEx, sizeof connectEx, &read, NULL, NULL) != 0) {
message = "WSAIoctl failed, result=" + std::to_string(WSAGetLastError());
message = "WSAIoctl failed, " + errorMessage(WSAGetLastError());
} else {
assert(read == sizeof connectEx);
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) {
message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError());
message = "CreateIoCompletionPort failed, " + lastErrorMessage();
} else {
sockaddr_in addressData;
addressData.sin_family = AF_INET;
@ -110,7 +111,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
} else {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
message = "ConnectEx failed, result=" + std::to_string(lastError);
message = "ConnectEx failed, " + errorMessage(lastError);
} else {
context2.context = dispatcher->getCurrentContext();
context2.connection = connection;
@ -124,7 +125,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
if (CancelIoEx(reinterpret_cast<HANDLE>(context2->connection), context2) != TRUE) {
DWORD lastError = GetLastError();
if (lastError != ERROR_NOT_FOUND) {
throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, result=" + std::to_string(GetLastError()));
throw std::runtime_error("TcpConnector::stop, CancelIoEx failed, " + lastErrorMessage());
}
context2->context->interrupted = true;
@ -146,11 +147,11 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
if (WSAGetOverlappedResult(connection, &context2, &transferred, FALSE, &flags) != TRUE) {
lastError = WSAGetLastError();
if (lastError != ERROR_OPERATION_ABORTED) {
message = "ConnectEx failed, result=" + std::to_string(lastError);
message = "ConnectEx failed, " + errorMessage(lastError);
} else {
assert(context2.interrupted);
if (closesocket(connection) != 0) {
throw std::runtime_error("TcpConnector::connect, closesocket failed, result=" + std::to_string(WSAGetLastError()));
throw std::runtime_error("TcpConnector::connect, closesocket failed, " + errorMessage(WSAGetLastError()));
} else {
throw InterruptedException();
}
@ -160,7 +161,7 @@ TcpConnection TcpConnector::connect(const Ipv4Address& address, uint16_t port) {
assert(flags == 0);
DWORD value = 1;
if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, reinterpret_cast<char*>(&value), sizeof(value)) != 0) {
message = "setsockopt failed, result=" + std::to_string(WSAGetLastError());
message = "setsockopt failed, " + errorMessage(WSAGetLastError());
} else {
return TcpConnection(*dispatcher, connection);
}

View file

@ -25,6 +25,7 @@
#include <System/InterruptedException.h>
#include <System/Ipv4Address.h>
#include "Dispatcher.h"
#include "ErrorMessage.h"
#include "TcpConnection.h"
namespace System {
@ -47,25 +48,25 @@ TcpListener::TcpListener(Dispatcher& dispatcher, const Ipv4Address& address, uin
std::string message;
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == INVALID_SOCKET) {
message = "socket failed, result=" + std::to_string(WSAGetLastError());
message = "socket failed, " + errorMessage(WSAGetLastError());
} else {
sockaddr_in addressData;
addressData.sin_family = AF_INET;
addressData.sin_port = htons(port);
addressData.sin_addr.S_un.S_addr = htonl(address.getValue());
if (bind(listener, reinterpret_cast<sockaddr*>(&addressData), sizeof(addressData)) != 0) {
message = "bind failed, result=" + std::to_string(WSAGetLastError());
message = "bind failed, " + errorMessage(WSAGetLastError());
} else if (listen(listener, SOMAXCONN) != 0) {
message = "listen failed, result=" + std::to_string(WSAGetLastError());
message = "listen failed, " + errorMessage(WSAGetLastError());
} else {
GUID guidAcceptEx = WSAID_ACCEPTEX;
DWORD read = sizeof acceptEx;
if (acceptEx == nullptr && WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guidAcceptEx, sizeof guidAcceptEx, &acceptEx, sizeof acceptEx, &read, NULL, NULL) != 0) {
message = "WSAIoctl failed, result=" + std::to_string(WSAGetLastError());
message = "WSAIoctl failed, " + errorMessage(WSAGetLastError());
} else {
assert(read == sizeof acceptEx);
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(listener), dispatcher.getCompletionPort(), 0, 0) != dispatcher.getCompletionPort()) {
message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError());
message = "CreateIoCompletionPort failed, " + lastErrorMessage();
} else {
context = nullptr;
return;
@ -101,7 +102,7 @@ TcpListener& TcpListener::operator=(TcpListener&& other) {
if (dispatcher != nullptr) {
assert(context == nullptr);
if (closesocket(listener) != 0) {
throw std::runtime_error("TcpListener::operator=, closesocket failed, result=" + std::to_string(WSAGetLastError()));
throw std::runtime_error("TcpListener::operator=, closesocket failed, " + errorMessage(WSAGetLastError()));
}
}
@ -126,7 +127,7 @@ TcpConnection TcpListener::accept() {
std::string message;
SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (connection == INVALID_SOCKET) {
message = "socket failed, result=" + std::to_string(WSAGetLastError());
message = "socket failed, " + errorMessage(WSAGetLastError());
} else {
uint8_t addresses[sizeof sockaddr_in * 2 + 32];
DWORD received;
@ -137,7 +138,7 @@ TcpConnection TcpListener::accept() {
} else {
int lastError = WSAGetLastError();
if (lastError != WSA_IO_PENDING) {
message = "AcceptEx failed, result=" + std::to_string(lastError);
message = "AcceptEx failed, " + errorMessage(lastError);
} else {
context2.context = dispatcher->getCurrentContext();
context2.interrupted = false;
@ -150,7 +151,7 @@ TcpConnection TcpListener::accept() {
if (CancelIoEx(reinterpret_cast<HANDLE>(listener), context2) != TRUE) {
DWORD lastError = GetLastError();
if (lastError != ERROR_NOT_FOUND) {
throw std::runtime_error("TcpListener::stop, CancelIoEx failed, result=" + std::to_string(GetLastError()));
throw std::runtime_error("TcpListener::stop, CancelIoEx failed, " + lastErrorMessage());
}
context2->context->interrupted = true;
@ -171,11 +172,11 @@ TcpConnection TcpListener::accept() {
if (WSAGetOverlappedResult(listener, &context2, &transferred, FALSE, &flags) != TRUE) {
lastError = WSAGetLastError();
if (lastError != ERROR_OPERATION_ABORTED) {
message = "AcceptEx failed, result=" + std::to_string(lastError);
message = "AcceptEx failed, " + errorMessage(lastError);
} else {
assert(context2.interrupted);
if (closesocket(connection) != 0) {
throw std::runtime_error("TcpListener::accept, closesocket failed, result=" + std::to_string(WSAGetLastError()));
throw std::runtime_error("TcpListener::accept, closesocket failed, " + errorMessage(WSAGetLastError()));
} else {
throw InterruptedException();
}
@ -184,10 +185,10 @@ TcpConnection TcpListener::accept() {
assert(transferred == 0);
assert(flags == 0);
if (setsockopt(connection, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, reinterpret_cast<char*>(&listener), sizeof listener) != 0) {
message = "setsockopt failed, result=" + std::to_string(WSAGetLastError());
message = "setsockopt failed, " + errorMessage(WSAGetLastError());
} else {
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(connection), dispatcher->getCompletionPort(), 0, 0) != dispatcher->getCompletionPort()) {
message = "CreateIoCompletionPort failed, result=" + std::to_string(GetLastError());
message = "CreateIoCompletionPort failed, " + lastErrorMessage();
} else {
return TcpConnection(*dispatcher, connection);
}

View file

@ -0,0 +1,45 @@
// 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 "Serialization/SerializationOverloads.h"
#include <limits>
namespace CryptoNote {
void serializeBlockHeight(ISerializer& s, uint32_t& blockHeight, Common::StringView name) {
if (s.type() == ISerializer::INPUT) {
uint64_t height;
s(height, name);
if (height == std::numeric_limits<uint64_t>::max()) {
blockHeight = std::numeric_limits<uint32_t>::max();
} else if (height > std::numeric_limits<uint32_t>::max() && height < std::numeric_limits<uint64_t>::max()) {
throw std::runtime_error("Deserialization error: wrong value");
} else {
blockHeight = static_cast<uint32_t>(height);
}
} else {
s(blockHeight, name);
}
}
void serializeGlobalOutputIndex(ISerializer& s, uint32_t& globalOutputIndex, Common::StringView name) {
serializeBlockHeight(s, globalOutputIndex, name);
}
} //namespace CryptoNote

View file

@ -249,4 +249,10 @@ void readSequence(Iterator outputIterator, Common::StringView name, ISerializer&
s.endArray();
}
//convinience function since we change block height type
void serializeBlockHeight(ISerializer& s, uint32_t& blockHeight, Common::StringView name);
//convinience function since we change global output index type
void serializeGlobalOutputIndex(ISerializer& s, uint32_t& globalOutputIndex, Common::StringView name);
}

View file

@ -22,6 +22,7 @@
#include "CryptoNoteCore/CryptoNoteFormatUtils.h"
#include "Serialization/BinaryInputStreamSerializer.h"
#include "Serialization/BinaryOutputStreamSerializer.h"
#include "Serialization/SerializationOverloads.h"
using namespace Common;
using namespace Crypto;
@ -31,7 +32,7 @@ namespace CryptoNote {
void serialize(TransactionInformation& ti, CryptoNote::ISerializer& s) {
s(ti.transactionHash, "");
s(ti.publicKey, "");
s(ti.blockHeight, "");
serializeBlockHeight(s, ti.blockHeight, "");
s(ti.timestamp, "");
s(ti.unlockTime, "");
s(ti.totalAmountIn, "");

View file

@ -89,12 +89,12 @@ struct TransactionOutputInformationEx : public TransactionOutputInformationIn {
void serialize(CryptoNote::ISerializer& s) {
s(reinterpret_cast<uint8_t&>(type), "type");
s(amount, "");
s(globalOutputIndex, "");
serializeGlobalOutputIndex(s, globalOutputIndex, "");
s(outputInTransaction, "");
s(transactionPublicKey, "");
s(keyImage, "");
s(unlockTime, "");
s(blockHeight, "");
serializeBlockHeight(s, blockHeight, "");
s(transactionIndex, "");
s(transactionHash, "");
s(visible, "");
@ -113,7 +113,7 @@ struct BlockInfo {
uint32_t transactionIndex;
void serialize(ISerializer& s) {
s(height, "height");
serializeBlockHeight(s, height, "height");
s(timestamp, "timestamp");
s(transactionIndex, "transactionIndex");
}

View file

@ -144,7 +144,7 @@ void serialize(WalletTransactionDto& value, CryptoNote::ISerializer& serializer)
value.state = static_cast<CryptoNote::WalletTransactionState>(state);
serializer(value.timestamp, "timestamp");
serializer(value.blockHeight, "block_height");
CryptoNote::serializeBlockHeight(serializer, value.blockHeight, "block_height");
serializer(value.hash, "hash");
serializer(value.totalAmount, "total_amount");
serializer(value.fee, "fee");

View file

@ -113,6 +113,7 @@ WalletLegacy::WalletLegacy(const CryptoNote::Currency& currency, INode& node) :
m_blockchainSync(node, currency.genesisBlockHash()),
m_transfersSync(currency, m_blockchainSync, node),
m_transferDetails(nullptr),
m_transactionsCache(m_currency.mempoolTxLiveTime()),
m_sender(nullptr),
m_onInitSyncStarter(new SyncStarter(m_blockchainSync))
{
@ -501,9 +502,15 @@ std::error_code WalletLegacy::cancelTransaction(size_t transactionId) {
}
void WalletLegacy::synchronizationProgressUpdated(uint32_t current, uint32_t total) {
auto deletedTransactions = deleteOutdatedUnconfirmedTransactions();
// forward notification
m_observerManager.notify(&IWalletLegacyObserver::synchronizationProgressUpdated, current, total);
for (auto transactionId: deletedTransactions) {
m_observerManager.notify(&IWalletLegacyObserver::transactionUpdated, transactionId);
}
// check if balance has changed and notify client
notifyIfBalanceChanged();
}
@ -513,9 +520,16 @@ void WalletLegacy::synchronizationCompleted(std::error_code result) {
m_observerManager.notify(&IWalletLegacyObserver::synchronizationCompleted, result);
}
if (!result) {
notifyIfBalanceChanged();
if (result) {
return;
}
auto deletedTransactions = deleteOutdatedUnconfirmedTransactions();
std::for_each(deletedTransactions.begin(), deletedTransactions.end(), [&] (TransactionId transactionId) {
m_observerManager.notify(&IWalletLegacyObserver::transactionUpdated, transactionId);
});
notifyIfBalanceChanged();
}
void WalletLegacy::onTransactionUpdated(ITransfersSubscription* object, const Hash& transactionHash) {
@ -586,4 +600,9 @@ void WalletLegacy::getAccountKeys(AccountKeys& keys) {
keys = m_account.getAccountKeys();
}
std::vector<TransactionId> WalletLegacy::deleteOutdatedUnconfirmedTransactions() {
std::lock_guard<std::mutex> lock(m_cacheMutex);
return m_transactionsCache.deleteOutdatedTransactions();
}
} //namespace CryptoNote

View file

@ -106,6 +106,8 @@ private:
void notifyClients(std::deque<std::shared_ptr<WalletLegacyEvent> >& events);
void notifyIfBalanceChanged();
std::vector<TransactionId> deleteOutdatedUnconfirmedTransactions();
enum WalletState
{
NOT_INITIALIZED = 0,

View file

@ -52,18 +52,7 @@ void serialize(WalletLegacyTransaction& txi, CryptoNote::ISerializer& serializer
serializer(txi.hash, "hash");
serializer(txi.isCoinbase, "is_coinbase");
if (serializer.type() == ISerializer::INPUT) {
uint64_t height = 0;
serializer(height, "block_height");
if (height == std::numeric_limits<uint64_t>::max()) {
txi.blockHeight = WALLET_LEGACY_UNCONFIRMED_TRANSACTION_HEIGHT;
} else {
txi.blockHeight = static_cast<uint32_t>(height);
}
} else {
serializer(txi.blockHeight, "block_height");
}
CryptoNote::serializeBlockHeight(serializer, txi.blockHeight, "block_height");
serializer(txi.timestamp, "timestamp");
serializer(txi.unlockTime, "unlock_time");

View file

@ -30,6 +30,11 @@ inline TransactionOutputId getOutputId(const TransactionOutputInformation& out)
return std::make_pair(out.transactionPublicKey, out.outputInTransaction);
}
WalletUnconfirmedTransactions::WalletUnconfirmedTransactions(uint64_t uncofirmedTransactionsLiveTime):
m_uncofirmedTransactionsLiveTime(uncofirmedTransactionsLiveTime) {
}
bool WalletUnconfirmedTransactions::serialize(ISerializer& s) {
s(m_unconfirmedTxs, "transactions");
if (s.type() == ISerializer::INPUT) {
@ -54,9 +59,7 @@ void WalletUnconfirmedTransactions::erase(const Hash& hash) {
return;
}
for (const auto& o : it->second.usedOutputs) {
m_usedOutputs.erase(o);
}
deleteUsedOutputs(it->second.usedOutputs);
m_unconfirmedTxs.erase(it);
}
@ -125,4 +128,29 @@ void WalletUnconfirmedTransactions::reset() {
m_usedOutputs.clear();
}
void WalletUnconfirmedTransactions::deleteUsedOutputs(const std::vector<TransactionOutputId>& usedOutputs) {
for (const auto& output: usedOutputs) {
m_usedOutputs.erase(output);
}
}
std::vector<TransactionId> WalletUnconfirmedTransactions::deleteOutdatedTransactions() {
std::vector<TransactionId> deletedTransactions;
uint64_t now = static_cast<uint64_t>(time(nullptr));
assert(now >= m_uncofirmedTransactionsLiveTime);
for (auto it = m_unconfirmedTxs.begin(); it != m_unconfirmedTxs.end();) {
if (static_cast<uint64_t>(it->second.sentTime) <= now - m_uncofirmedTransactionsLiveTime) {
deleteUsedOutputs(it->second.usedOutputs);
deletedTransactions.push_back(it->second.transactionId);
it = m_unconfirmedTxs.erase(it);
} else {
++it;
}
}
return deletedTransactions;
}
} /* namespace CryptoNote */

View file

@ -65,6 +65,8 @@ class WalletUnconfirmedTransactions
{
public:
explicit WalletUnconfirmedTransactions(uint64_t uncofirmedTransactionsLiveTime);
bool serialize(CryptoNote::ISerializer& s);
bool findTransactionId(const Crypto::Hash& hash, TransactionId& id);
@ -78,15 +80,19 @@ public:
bool isUsed(const TransactionOutputInformation& out) const;
void reset();
std::vector<TransactionId> deleteOutdatedTransactions();
private:
void collectUsedOutputs();
void deleteUsedOutputs(const std::vector<TransactionOutputId>& usedOutputs);
typedef std::unordered_map<Crypto::Hash, UnconfirmedTransferDetails, boost::hash<Crypto::Hash>> UnconfirmedTxsContainer;
typedef std::unordered_set<TransactionOutputId> UsedOutputsContainer;
UnconfirmedTxsContainer m_unconfirmedTxs;
UsedOutputsContainer m_usedOutputs;
uint64_t m_uncofirmedTransactionsLiveTime;
};
} // namespace CryptoNote

View file

@ -28,12 +28,18 @@
using namespace Crypto;
namespace CryptoNote {
WalletUserTransactionsCache::WalletUserTransactionsCache(uint64_t mempoolTxLiveTime) : m_unconfirmedTransactions(mempoolTxLiveTime) {
}
bool WalletUserTransactionsCache::serialize(CryptoNote::ISerializer& s) {
if (s.type() == CryptoNote::ISerializer::INPUT) {
s(m_transactions, "transactions");
s(m_transfers, "transfers");
s(m_unconfirmedTransactions, "unconfirmed");
updateUnconfirmedTransactions();
deleteOutdatedTransactions();
} else {
UserTransactions txsToSave;
UserTransfers transfersToSave;
@ -298,4 +304,15 @@ void WalletUserTransactionsCache::reset() {
m_unconfirmedTransactions.reset();
}
std::vector<TransactionId> WalletUserTransactionsCache::deleteOutdatedTransactions() {
auto deletedTransactions = m_unconfirmedTransactions.deleteOutdatedTransactions();
for (auto id: deletedTransactions) {
assert(id < m_transactions.size());
m_transactions[id].state = WalletLegacyTransactionState::Deleted;
}
return deletedTransactions;
}
} //namespace CryptoNote

View file

@ -33,7 +33,7 @@ namespace CryptoNote {
class WalletUserTransactionsCache
{
public:
WalletUserTransactionsCache() {}
explicit WalletUserTransactionsCache(uint64_t mempoolTxLiveTime = 60 * 60 * 24);
bool serialize(CryptoNote::ISerializer& serializer);
@ -59,6 +59,8 @@ public:
bool isUsed(const TransactionOutputInformation& out) const;
void reset();
std::vector<TransactionId> deleteOutdatedTransactions();
private:
TransactionId findTransactionByHash(const Crypto::Hash& hash);

View file

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "1.0.6"
#define PROJECT_VERSION_BUILD_NO "542"
#define PROJECT_VERSION "1.0.6.1"
#define PROJECT_VERSION_BUILD_NO "550"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View file

@ -17,90 +17,47 @@
#pragma once
#ifdef _WIN32
#include <io.h>
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#include <fstream>
#include <memory>
#include <string>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
namespace Tools
{
template<class t_object>
bool serialize_obj_to_file(t_object& obj, const std::string& file_path)
{
try {
#ifdef _WIN32
// Need to know HANDLE of file to call FlushFileBuffers
HANDLE data_file_handle = ::CreateFile(file_path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == data_file_handle)
return false;
namespace Tools {
int data_file_descriptor = _open_osfhandle((intptr_t)data_file_handle, 0);
if (-1 == data_file_descriptor) {
::CloseHandle(data_file_handle);
return false;
}
FILE* data_file_file = _fdopen(data_file_descriptor, "wb");
if (0 == data_file_file) {
// Call CloseHandle is not necessary
_close(data_file_descriptor);
return false;
}
// HACK: undocumented constructor, this code may not compile
std::ofstream data_file(data_file_file);
if (data_file.fail()) {
// Call CloseHandle and _close are not necessary
fclose(data_file_file);
return false;
}
#else
std::ofstream data_file;
data_file.open(file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc);
if (data_file.fail())
return false;
#endif
boost::archive::binary_oarchive a(data_file);
a << obj;
if (data_file.fail())
return false;
data_file.flush();
#ifdef _WIN32
// To make sure the file is fully stored on disk
::FlushFileBuffers(data_file_handle);
fclose(data_file_file);
#endif
return true;
} catch (std::exception&) {
template <class t_object>
bool serialize_obj_to_file(t_object& obj, const std::string& file_path) {
try {
std::ofstream file(file_path);
boost::archive::binary_oarchive a(file);
a << obj;
if (file.fail()) {
return false;
}
}
template<class t_object>
bool unserialize_obj_from_file(t_object& obj, const std::string& file_path)
{
try {
std::ifstream data_file;
data_file.open( file_path, std::ios_base::binary | std::ios_base::in);
if(data_file.fail())
return false;
boost::archive::binary_iarchive a(data_file);
a >> obj;
return !data_file.fail();
} catch (std::exception&) {
return false;
}
file.flush();
return true;
} catch (std::exception&) {
return false;
}
}
template <class t_object>
bool unserialize_obj_from_file(t_object& obj, const std::string& file_path) {
try {
std::ifstream dataFile;
dataFile.open(file_path, std::ios_base::binary | std::ios_base::in);
if (dataFile.fail()) {
return false;
}
boost::archive::binary_iarchive a(dataFile);
a >> obj;
return !dataFile.fail();
} catch (std::exception&) {
return false;
}
}
}

View file

@ -150,6 +150,8 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_tx_txout_to_key_has_invalid_key);
GENERATE_AND_PLAY(gen_tx_output_with_zero_amount);
GENERATE_AND_PLAY(gen_tx_signatures_are_invalid);
GENERATE_AND_PLAY_EX(GenerateTransactionWithZeroFee(false));
GENERATE_AND_PLAY_EX(GenerateTransactionWithZeroFee(true));
// multisignature output
GENERATE_AND_PLAY_EX(MultiSigTx_OutputSignatures(1, 1, true));
@ -187,7 +189,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendInTx(false));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendInTx(true));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendSameBlock(false));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendSameBlock(true));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendSameBlock(true));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendDifferentBlocks(false));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendDifferentBlocks(true));
GENERATE_AND_PLAY_EX(MultiSigTx_DoubleSpendAltChainSameBlock(false));

View file

@ -682,6 +682,34 @@ bool gen_tx_signatures_are_invalid::generate(std::vector<test_event_entry>& even
return true;
}
GenerateTransactionWithZeroFee::GenerateTransactionWithZeroFee(bool keptByBlock) : m_keptByBlock(keptByBlock) {
}
bool GenerateTransactionWithZeroFee::generate(std::vector<test_event_entry>& events) const {
uint64_t ts_start = 1338224400;
GENERATE_ACCOUNT(alice_account);
GENERATE_ACCOUNT(bob_account);
MAKE_GENESIS_BLOCK(events, blk_0, alice_account, ts_start);
REWIND_BLOCKS(events, blk_0r, blk_0, alice_account);
CryptoNote::Transaction tx;
construct_tx_to_key(m_logger, events, tx, blk_0, alice_account, bob_account, MK_COINS(1), 0, 0);
if (!m_keptByBlock) {
DO_CALLBACK(events, "mark_invalid_tx");
} else {
event_visitor_settings settings;
settings.txs_keeped_by_block = true;
settings.valid_mask = 1;
events.push_back(settings);
}
events.push_back(tx);
return true;
}
MultiSigTx_OutputSignatures::MultiSigTx_OutputSignatures(size_t givenKeys, uint32_t requiredSignatures, bool shouldSucceed) :
m_givenKeys(givenKeys), m_requiredSignatures(requiredSignatures), m_shouldSucceed(shouldSucceed) {

View file

@ -141,6 +141,14 @@ struct gen_tx_signatures_are_invalid : public get_tx_validation_base
bool generate(std::vector<test_event_entry>& events) const;
};
struct GenerateTransactionWithZeroFee : public get_tx_validation_base
{
explicit GenerateTransactionWithZeroFee(bool keptByBlock);
bool generate(std::vector<test_event_entry>& events) const;
bool m_keptByBlock;
};
// MultiSignature
class TestGenerator;

View file

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

View file

@ -291,6 +291,10 @@ void INodeTrivialRefreshStub::setNextTransactionToPool() {
m_nextTxToPool = true;
}
void INodeTrivialRefreshStub::cleanTransactionPool() {
m_blockchainGenerator.clearTxPool();
}
void INodeTrivialRefreshStub::getPoolSymmetricDifference(std::vector<Crypto::Hash>&& known_pool_tx_ids, Crypto::Hash known_block_id, bool& is_bc_actual,
std::vector<std::unique_ptr<ITransactionReader>>& new_txs, std::vector<Crypto::Hash>& deleted_tx_ids, const Callback& callback)
{

View file

@ -104,6 +104,7 @@ public:
virtual void startAlternativeChain(uint32_t height);
void setNextTransactionError();
void setNextTransactionToPool();
void cleanTransactionPool();
void setMaxMixinCount(uint64_t maxMixin);
void includeTransactionsFromPoolToBlock();

View file

@ -61,8 +61,8 @@ public:
}
virtual void synchronizationCompleted(std::error_code result) override {
synced.notify();
}
synced.notify();
}
virtual void sendTransactionCompleted(CryptoNote::TransactionId transactionId, std::error_code result) override {
sendResult = result;
@ -166,6 +166,9 @@ protected:
void prepareCarolWallet();
void GetOneBlockReward(CryptoNote::WalletLegacy& wallet);
void GetOneBlockReward(CryptoNote::WalletLegacy& wallet, const CryptoNote::Currency& currency, TestBlockchainGenerator& blockchainGenerator);
void GetOneBlockRewardAndUnlock(CryptoNote::WalletLegacy& wallet, TrivialWalletObserver& observer, INodeTrivialRefreshStub& node,
const CryptoNote::Currency& currency, TestBlockchainGenerator& blockchainGenerator);
void TestSendMoney(int64_t transferAmount, uint64_t fee, uint64_t mixIn = 0, const std::string& extra = "");
void performTransferWithErrorTx(const std::array<int64_t, 5>& amounts, uint64_t fee);
@ -221,9 +224,21 @@ void WalletLegacyApi::prepareCarolWallet() {
}
void WalletLegacyApi::GetOneBlockReward(CryptoNote::WalletLegacy& wallet) {
GetOneBlockReward(wallet, m_currency, generator);
}
void WalletLegacyApi::GetOneBlockReward(CryptoNote::WalletLegacy& wallet, const CryptoNote::Currency& currency, TestBlockchainGenerator& blockchainGenerator) {
CryptoNote::AccountPublicAddress address;
ASSERT_TRUE(m_currency.parseAccountAddressString(wallet.getAddress(), address));
generator.getBlockRewardForAddress(address);
ASSERT_TRUE(currency.parseAccountAddressString(wallet.getAddress(), address));
blockchainGenerator.getBlockRewardForAddress(address);
}
void WalletLegacyApi::GetOneBlockRewardAndUnlock(CryptoNote::WalletLegacy& wallet, TrivialWalletObserver& observer, INodeTrivialRefreshStub& node,
const CryptoNote::Currency& currency, TestBlockchainGenerator& blockchainGenerator) {
GetOneBlockReward(wallet, currency, blockchainGenerator);
blockchainGenerator.generateEmptyBlocks(10);
node.updateObservers();
WaitWalletSync(&observer);
}
void WalletLegacyApi::performTransferWithErrorTx(const std::array<int64_t, 5>& amounts, uint64_t fee) {
@ -1720,3 +1735,86 @@ TEST_F(WalletLegacyApi, resetAndSyncDoNotRestoreTransfers) {
alice->shutdown();
}
void generateWallet(CryptoNote::IWalletLegacy& wallet, TrivialWalletObserver& observer, const std::string& pass) {
wallet.initAndGenerate(pass);
WaitWalletSync(&observer);
}
TEST_F(WalletLegacyApi, outdatedUnconfirmedTransactionDeletedOnNewBlock) {
const uint64_t TRANSACTION_MEMPOOL_TIME = 1;
CryptoNote::Currency currency(CryptoNote::CurrencyBuilder(m_logger).mempoolTxLiveTime(TRANSACTION_MEMPOOL_TIME).currency());
TestBlockchainGenerator blockchainGenerator(currency);
INodeTrivialRefreshStub node(blockchainGenerator);
CryptoNote::WalletLegacy wallet(currency, node);
TrivialWalletObserver walletObserver;
wallet.addObserver(&walletObserver);
wallet.initAndGenerate("pass");
WaitWalletSync(&walletObserver);
GetOneBlockRewardAndUnlock(wallet, walletObserver, node, currency, blockchainGenerator);
const std::string ADDRESS = "2634US2FAz86jZT73YmM8u5GPCknT2Wxj8bUCKivYKpThFhF2xsjygMGxbxZzM42zXhKUhym6Yy6qHHgkuWtruqiGkDpX6m";
node.setNextTransactionToPool();
auto id = wallet.sendTransaction({ADDRESS, static_cast<int64_t>(TEST_BLOCK_REWARD - m_currency.minimumFee())}, m_currency.minimumFee());
WaitWalletSend(&walletObserver);
node.cleanTransactionPool();
std::this_thread::sleep_for(std::chrono::seconds(TRANSACTION_MEMPOOL_TIME));
blockchainGenerator.generateEmptyBlocks(1);
node.updateObservers();
WaitWalletSync(&walletObserver);
ASSERT_EQ(TEST_BLOCK_REWARD, wallet.actualBalance());
CryptoNote::WalletLegacyTransaction transaction;
ASSERT_TRUE(wallet.getTransaction(id, transaction));
EXPECT_EQ(CryptoNote::WalletLegacyTransactionState::Deleted, transaction.state);
wallet.removeObserver(&walletObserver);
wallet.shutdown();
}
TEST_F(WalletLegacyApi, outdatedUnconfirmedTransactionDeletedOnLoad) {
const uint64_t TRANSACTION_MEMPOOL_TIME = 1;
CryptoNote::Currency currency(CryptoNote::CurrencyBuilder(m_logger).mempoolTxLiveTime(TRANSACTION_MEMPOOL_TIME).currency());
TestBlockchainGenerator blockchainGenerator(currency);
INodeTrivialRefreshStub node(blockchainGenerator);
CryptoNote::WalletLegacy wallet(currency, node);
TrivialWalletObserver walletObserver;
wallet.addObserver(&walletObserver);
wallet.initAndGenerate("pass");
WaitWalletSync(&walletObserver);
GetOneBlockRewardAndUnlock(wallet, walletObserver, node, currency, blockchainGenerator);
const std::string ADDRESS = "2634US2FAz86jZT73YmM8u5GPCknT2Wxj8bUCKivYKpThFhF2xsjygMGxbxZzM42zXhKUhym6Yy6qHHgkuWtruqiGkDpX6m";
node.setNextTransactionToPool();
auto id = wallet.sendTransaction({ADDRESS, static_cast<int64_t>(TEST_BLOCK_REWARD - m_currency.minimumFee())}, m_currency.minimumFee());
WaitWalletSend(&walletObserver);
node.cleanTransactionPool();
std::stringstream data;
wallet.save(data);
WaitWalletSave(&walletObserver);
wallet.shutdown();
std::this_thread::sleep_for(std::chrono::seconds(TRANSACTION_MEMPOOL_TIME));
wallet.initAndLoad(data, "pass");
WaitWalletSync(&walletObserver);
ASSERT_EQ(TEST_BLOCK_REWARD, wallet.actualBalance());
CryptoNote::WalletLegacyTransaction transaction;
ASSERT_TRUE(wallet.getTransaction(id, transaction));
EXPECT_EQ(CryptoNote::WalletLegacyTransactionState::Deleted, transaction.state);
wallet.removeObserver(&walletObserver);
wallet.shutdown();
}