json rpc for wallet and bugfix

This commit is contained in:
Antonio Juarez 2014-04-02 17:00:17 +01:00
parent 8efa1313f3
commit 29c2859a3e
43 changed files with 2299 additions and 845 deletions

17
ReleaseNotes.txt Normal file
View File

@ -0,0 +1,17 @@
Release notes 0.8.3
- JSON RPC for wallet
- fixed bug with blockchain storing
- fixed bug with correct display of transfer's change
- bug fix in simple wallet
Release notes 0.8.2
- now wallet is still working when daemon stores blockchain and can't serve clients;
- no-console option for a daemon;
- fixed problem with network synchronization;
- major bug fix in simple wallet.
Release notes 0.8.1
Bytecoin project is moved to GitHub

View File

@ -42,10 +42,10 @@ bool communicate(const std::string url, t_request& req, t_response& rsp, const s
if(use_jrpc) if(use_jrpc)
{ {
epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t); epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t);
req_t.params = req; req_t.jsonrpc = "2.0";
req_t.id = "10"; req_t.id = "10";
req_t.method = "command_example_1"; req_t.method = "command_example_1";
req_t.version = "1.1"; req_t.params = req;
epee::json_rpc::response<t_response, std::string> resp_t = AUTO_VAL_INIT(resp_t); epee::json_rpc::response<t_response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
if(!epee::net_utils::invoke_http_json_remote_command2("/request_json_rpc", req_t, resp_t, http_client)) if(!epee::net_utils::invoke_http_json_remote_command2("/request_json_rpc", req_t, resp_t, http_client))
{ {

View File

@ -261,6 +261,7 @@ namespace epee
continue_handle = false; continue_handle = false;
break; break;
} }
string_tools::trim(command);
LOG_PRINT_L2("Read command: " << command); LOG_PRINT_L2("Read command: " << command);
if(0 == command.compare("exit") || 0 == command.compare("q")) if(0 == command.compare("exit") || 0 == command.compare("q"))
@ -281,7 +282,8 @@ namespace epee
continue; continue;
} }
log_space::get_set_log_detalisation_level(true, n); log_space::get_set_log_detalisation_level(true, n);
LOG_PRINT_L0("New log level set " << n); std::cout << "New log level set " << n;
LOG_PRINT_L2("New log level set " << n);
}else if (command.empty()) }else if (command.empty())
{ {
continue; continue;

View File

@ -1380,6 +1380,9 @@ POP_WARNINGS
#define LOG_PRINT_RED(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_red) #define LOG_PRINT_RED(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_red)
#define LOG_PRINT_GREEN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_green) #define LOG_PRINT_GREEN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_green)
#define LOG_PRINT_BLUE(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_blue) #define LOG_PRINT_BLUE(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_blue)
#define LOG_PRINT_YELLOW(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_yellow)
#define LOG_PRINT_CYAN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_cyan)
#define LOG_PRINT_MAGENTA(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_magenta)
#define LOG_PRINT_RED_L0(mess) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, LOG_LEVEL_0, epee::log_space::console_color_red) #define LOG_PRINT_RED_L0(mess) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, LOG_LEVEL_0, epee::log_space::console_color_red)

View File

@ -801,7 +801,7 @@ using namespace std;
}else }else
{ //Apparently there are no signs of the form of transfer, will receive data until the connection is closed { //Apparently there are no signs of the form of transfer, will receive data until the connection is closed
m_state = reciev_machine_state_error; m_state = reciev_machine_state_error;
LOG_PRINT("Undefinded transfer type, consider http_body_transfer_connection_close method.", LOG_LEVEL_2); LOG_PRINT("Undefinded transfer type, consider http_body_transfer_connection_close method. header: " << m_header_cache, LOG_LEVEL_2);
return false; return false;
} }
return false; return false;

View File

@ -118,15 +118,15 @@ namespace epee
template<typename t_param> template<typename t_param>
struct request struct request
{ {
std::string version; std::string jsonrpc;
std::string method; std::string method;
std::string id; std::string id;
t_param params; t_param params;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(version) KV_SERIALIZE(jsonrpc)
KV_SERIALIZE(method)
KV_SERIALIZE(id) KV_SERIALIZE(id)
KV_SERIALIZE(method)
KV_SERIALIZE(params) KV_SERIALIZE(params)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
@ -147,21 +147,54 @@ namespace epee
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
template<typename t_param, typename t_error> struct dummy_result
struct response
{ {
t_param result;
t_error error;
std::string id;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(result)
KV_SERIALIZE(error)
KV_SERIALIZE(id)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };
typedef response<std::string, error> error_response; template<typename t_param, typename t_error>
struct response
{
std::string jsonrpc;
t_param result;
std::string id;
t_error error;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
KV_SERIALIZE(id)
KV_SERIALIZE(result)
KV_SERIALIZE(error)
END_KV_SERIALIZE_MAP()
};
template<typename t_param>
struct response<t_param, dummy_error>
{
std::string jsonrpc;
t_param result;
std::string id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
KV_SERIALIZE(id)
KV_SERIALIZE(result)
END_KV_SERIALIZE_MAP()
};
template<typename t_error>
struct response<dummy_result, t_error>
{
std::string jsonrpc;
t_error error;
std::string id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(jsonrpc)
KV_SERIALIZE(id)
KV_SERIALIZE(error)
END_KV_SERIALIZE_MAP()
};
typedef response<dummy_result, error> error_response;
} }
} }
@ -184,6 +217,7 @@ namespace epee
if(!ps.get_value("method", callback_name, nullptr)) \ if(!ps.get_value("method", callback_name, nullptr)) \
{ \ { \
epee::json_rpc::error_response rsp; \ epee::json_rpc::error_response rsp; \
rsp.jsonrpc = "2.0"; \
rsp.error.code = -32600; \ rsp.error.code = -32600; \
rsp.error.message = "Invalid Request"; \ rsp.error.message = "Invalid Request"; \
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
@ -201,8 +235,10 @@ namespace epee
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \ boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \ epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
resp.jsonrpc = "2.0"; \
resp.id = req.id; \ resp.id = req.id; \
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
fail_resp.jsonrpc = "2.0"; \
fail_resp.id = req.id; \ fail_resp.id = req.id; \
if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context)) \ if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context)) \
{ \ { \
@ -218,6 +254,35 @@ namespace epee
return true;\ return true;\
} }
#define MAP_JON_RPC_WERI(method_name, callback_f, command_type) \
else if(callback_name == method_name) \
{ \
handled = true; \
boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \
epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\
req.load(ps); \
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
resp.jsonrpc = "2.0"; \
resp.id = req.id; \
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
fail_resp.jsonrpc = "2.0"; \
fail_resp.id = req.id; \
if(!callback_f(req.params, resp.result, fail_resp.error, m_conn_context, response_info)) \
{ \
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
return true; \
} \
uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
epee::serialization::store_t_to_json(resp, response_info.m_body); \
uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
response_info.m_mime_tipe = "application/json"; \
response_info.m_header_info.m_content_type = " application/json"; \
LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
return true;\
}
#define MAP_JON_RPC(method_name, callback_f, command_type) \ #define MAP_JON_RPC(method_name, callback_f, command_type) \
else if(callback_name == method_name) \ else if(callback_name == method_name) \
@ -229,10 +294,12 @@ namespace epee
uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \ boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \ epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
resp.jsonrpc = "2.0"; \
resp.id = req.id; \ resp.id = req.id; \
if(!callback_f(req.params, resp.result, m_conn_context)) \ if(!callback_f(req.params, resp.result, m_conn_context)) \
{ \ { \
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
fail_resp.jsonrpc = "2.0"; \
fail_resp.id = req.id; \ fail_resp.id = req.id; \
fail_resp.error.code = -32603; \ fail_resp.error.code = -32603; \
fail_resp.error.message = "Internal error"; \ fail_resp.error.message = "Internal error"; \
@ -251,6 +318,7 @@ namespace epee
#define END_JSON_RPC_MAP() \ #define END_JSON_RPC_MAP() \
epee::json_rpc::error_response rsp; \ epee::json_rpc::error_response rsp; \
rsp.jsonrpc = "2.0"; \
rsp.error.code = -32601; \ rsp.error.code = -32601; \
rsp.error.message = "Method not found"; \ rsp.error.message = "Method not found"; \
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \

View File

@ -137,6 +137,9 @@ namespace net_utils
#define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) #define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
#define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) #define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
#define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) #define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
#define LOG_PRINT_CC_YELLOW(ct, message, log_level) LOG_PRINT_YELLOW("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
#define LOG_PRINT_CC_CYAN(ct, message, log_level) LOG_PRINT_CYAN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
#define LOG_PRINT_CC_MAGENTA(ct, message, log_level) LOG_PRINT_MAGENTA("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
#define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) #define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) #define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
@ -154,6 +157,9 @@ namespace net_utils
#define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level) #define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level)
#define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level) #define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level)
#define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level) #define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level)
#define LOG_PRINT_CCONTEXT_YELLOW(message, log_level) LOG_PRINT_CC_YELLOW(context, message, log_level)
#define LOG_PRINT_CCONTEXT_CYAN(message, log_level) LOG_PRINT_CC_CYAN(context, message, log_level)
#define LOG_PRINT_CCONTEXT_MAGENTA(message, log_level) LOG_PRINT_CC_MAGENTA(context, message, log_level)
#define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message) #define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message)

View File

@ -98,10 +98,10 @@ namespace epee
bool invoke_http_json_rpc(const std::string& url, const std::string& method_name, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") bool invoke_http_json_rpc(const std::string& url, const std::string& method_name, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0")
{ {
epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t); epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t);
req_t.params = out_struct; req_t.jsonrpc = "2.0";
req_t.id = req_id; req_t.id = req_id;
req_t.method = method_name; req_t.method = method_name;
req_t.version = "2.0"; req_t.params = out_struct;
epee::json_rpc::response<t_response, epee::json_rpc::error> resp_t = AUTO_VAL_INIT(resp_t); epee::json_rpc::response<t_response, epee::json_rpc::error> resp_t = AUTO_VAL_INIT(resp_t);
if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method)) if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method))
{ {

View File

@ -27,6 +27,7 @@ namespace boost
template <class Archive, class h_key, class hval> template <class Archive, class h_key, class hval>
inline void load(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver) inline void load(Archive &a, std::unordered_map<h_key, hval> &x, const boost::serialization::version_type ver)
{ {
x.clear();
size_t s = 0; size_t s = 0;
a >> s; a >> s;
for(size_t i = 0; i != s; i++) for(size_t i = 0; i != s; i++)
@ -54,6 +55,7 @@ namespace boost
template <class Archive, class hval> template <class Archive, class hval>
inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver) inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver)
{ {
x.clear();
size_t s = 0; size_t s = 0;
a >> s; a >> s;
for(size_t i = 0; i != s; i++) for(size_t i = 0; i != s; i++)

View File

@ -19,14 +19,14 @@
// MONEY_SUPPLY - total number coins to be generated // MONEY_SUPPLY - total number coins to be generated
#define MONEY_SUPPLY ((uint64_t)(-1)) #define MONEY_SUPPLY ((uint64_t)(-1))
// COIN - number of smallest units in one coin
#define COIN ((uint64_t)1000000000000) // pow(10, 12)
#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6)
#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8
// COIN - number of smallest units in one coin
#define COIN ((uint64_t)100000000) // pow(10, 8)
#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6)
#define ORPHANED_BLOCKS_MAX_COUNT 100 #define ORPHANED_BLOCKS_MAX_COUNT 100

View File

@ -44,6 +44,7 @@ bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im
//------------------------------------------------------------------ //------------------------------------------------------------------
transaction *blockchain_storage::get_tx(const crypto::hash &id) transaction *blockchain_storage::get_tx(const crypto::hash &id)
{ {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto it = m_transactions.find(id); auto it = m_transactions.find(id);
if (it == m_transactions.end()) if (it == m_transactions.end())
return NULL; return NULL;
@ -65,27 +66,13 @@ bool blockchain_storage::init(const std::string& config_folder)
const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
if(!tools::unserialize_obj_from_file(*this, filename)) if(!tools::unserialize_obj_from_file(*this, filename))
{ {
const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block.");
if(tools::unserialize_obj_from_file(*this, temp_filename))
{
LOG_PRINT_L0("Blockchain storage loaded from temporary file");
std::error_code ec = tools::replace_file(temp_filename, filename);
if (ec)
{
LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value());
return false;
}
}
else
{
LOG_PRINT_L0("Blockchain storage file not found, generating genesis block.");
block bl = boost::value_initialized<block>(); block bl = boost::value_initialized<block>();
block_verification_context bvc = boost::value_initialized<block_verification_context>(); block_verification_context bvc = boost::value_initialized<block_verification_context>();
generate_genesis_block(bl); generate_genesis_block(bl);
add_new_block(bl, bvc); add_new_block(bl, bvc);
CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain"); CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain");
} }
}
if(!m_blocks.size()) if(!m_blocks.size())
{ {
LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); LOG_PRINT_L0("Blockchain not loaded, generating genesis block.");
@ -177,6 +164,7 @@ bool blockchain_storage::reset_and_set_genesis_block(const block& b)
//------------------------------------------------------------------ //------------------------------------------------------------------
bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check) bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check)
{ {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
struct purge_transaction_visitor: public boost::static_visitor<bool> struct purge_transaction_visitor: public boost::static_visitor<bool>
{ {
key_images_container& m_spent_keys; key_images_container& m_spent_keys;
@ -221,6 +209,7 @@ bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const trans
//------------------------------------------------------------------ //------------------------------------------------------------------
bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id) bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id)
{ {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto tx_index_it = m_transactions.find(tx_id); auto tx_index_it = m_transactions.find(tx_id);
CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!"); CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!");
transaction& tx = tx_index_it->second.tx; transaction& tx = tx_index_it->second.tx;
@ -525,9 +514,7 @@ bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumul
std::vector<size_t> last_blocks_sizes; std::vector<size_t> last_blocks_sizes;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
bool block_too_big = false; if(!get_block_reward(misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward))
base_reward = get_block_reward(last_blocks_sizes, cumulative_block_size, block_too_big, already_generated_coins);
if(block_too_big)
{ {
LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
return false; return false;
@ -568,18 +555,15 @@ bool blockchain_storage::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t blockchain_storage::get_current_comulative_blocksize_limit() uint64_t blockchain_storage::get_current_comulative_blocksize_limit()
{ {
return m_current_block_comul_sz_limit; return m_current_block_cumul_sz_limit;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce)
{ {
size_t txs_cumulative_size = 0; size_t median_size;
uint64_t fee = 0; uint64_t already_generated_coins;
size_t comul_sz_limit = 0;
std::vector<size_t> sz;
CRITICAL_REGION_BEGIN(m_blockchain_lock); CRITICAL_REGION_BEGIN(m_blockchain_lock);
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
b.major_version = CURRENT_BLOCK_MAJOR_VERSION; b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
b.minor_version = CURRENT_BLOCK_MINOR_VERSION; b.minor_version = CURRENT_BLOCK_MINOR_VERSION;
b.prev_id = get_tail_id(); b.prev_id = get_tail_id();
@ -588,72 +572,73 @@ bool blockchain_storage::create_block_template(block& b, const account_public_ad
diffic = get_difficulty_for_next_block(); diffic = get_difficulty_for_next_block();
CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead."); CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead.");
comul_sz_limit = m_current_block_comul_sz_limit - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; median_size = m_current_block_cumul_sz_limit;
already_generated_coins = m_blocks.back().already_generated_coins;
CRITICAL_REGION_END(); CRITICAL_REGION_END();
m_tx_pool.fill_block_template(b, txs_cumulative_size, comul_sz_limit, fee); size_t txs_size;
uint64_t fee;
if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee)) {
return false;
}
/* /*
two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
*/ */
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
bool r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, txs_cumulative_size, ex_nonce, 11); bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
#ifdef _DEBUG #ifdef _DEBUG
std::list<size_t> try_val; std::list<size_t> try_val;
try_val.push_back(get_object_blobsize(b.miner_tx)); try_val.push_back(get_object_blobsize(b.miner_tx));
#endif #endif
size_t cumulative_size = txs_cumulative_size + get_object_blobsize(b.miner_tx); size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
size_t try_count = 0; for (size_t try_count = 0; try_count != 10; ++try_count) {
for(; try_count != 10; ++try_count) r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11);
{
r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, cumulative_size, ex_nonce, 11);
#ifdef _DEBUG #ifdef _DEBUG
try_val.push_back(get_object_blobsize(b.miner_tx)); try_val.push_back(get_object_blobsize(b.miner_tx));
#endif #endif
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
if(coinbase_blob_size > cumulative_size - txs_cumulative_size ) if (coinbase_blob_size > cumulative_size - txs_size) {
{ cumulative_size = txs_size + coinbase_blob_size;
cumulative_size = txs_cumulative_size + coinbase_blob_size;
continue; continue;
}else }
{
if(coinbase_blob_size < cumulative_size - txs_cumulative_size ) if (coinbase_blob_size < cumulative_size - txs_size) {
{ size_t delta = cumulative_size - txs_size - coinbase_blob_size;
size_t delta = cumulative_size - txs_cumulative_size - coinbase_blob_size;
b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0);
//here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
if(cumulative_size != txs_cumulative_size + get_object_blobsize(b.miner_tx)) if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
{ CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
CHECK_AND_ASSERT_MES(cumulative_size + 1== txs_cumulative_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_cumulative_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx) );
b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
if(cumulative_size != txs_cumulative_size + get_object_blobsize(b.miner_tx)) if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) {
{//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size
LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2); LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2);
cumulative_size += delta + 1; cumulative_size += delta - 1;
continue; continue;
} }
LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1);
} }
} }
CHECK_AND_ASSERT_MES(cumulative_size == txs_cumulative_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_cumulative_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx) ); CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx));
return true; return true;
} }
} LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
LOG_ERROR("Failed to create_block_template with " << try_count << " tries");
return false; return false;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
{ {
if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
return true; return true;
CRITICAL_REGION_LOCAL(m_blockchain_lock);
size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size();
CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size());
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0; size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0;
@ -947,22 +932,6 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
//bool blockchain_storage::get_outs_for_amounts(uint64_t amount, std::vector<std::pair<crypto::hash, size_t> >& keys, std::map<crypto::hash, transaction>& txs)
//{
// auto it = m_outputs.find(amount);
// if(it == m_outputs.end())
// return false;
// keys = it->second;
// typedef std::pair<crypto::hash, size_t> pair;
// BOOST_FOREACH(pair& pr, keys)
// {
// auto it = m_transactions.find(pr.first);
// CHECK_AND_ASSERT_MES(it != m_transactions.end(), false, "internal error: transaction with id " << pr.first << " not found in internal index, but have refference for amount " << amount);
// txs[pr.first] = it->second.tx;
// }
// return true;
//}
//------------------------------------------------------------------
bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) bool blockchain_storage::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset)
{ {
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
@ -1036,7 +1005,8 @@ void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_ind
<< "\nid\t\t" << get_block_hash(m_blocks[i].bl) << "\nid\t\t" << get_block_hash(m_blocks[i].bl)
<< "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL;
} }
LOG_PRINT_L0("Current blockchain:" << ENDL << ss.str()); LOG_PRINT_L1("Current blockchain:" << ENDL << ss.str());
LOG_PRINT_L0("Blockchain printed with log level 1");
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
void blockchain_storage::print_blockchain_index() void blockchain_storage::print_blockchain_index()
@ -1605,7 +1575,7 @@ bool blockchain_storage::update_next_comulative_size_limit()
if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE)
median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE;
m_current_block_comul_sz_limit = median*2; m_current_block_cumul_sz_limit = median*2;
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------

View File

@ -50,7 +50,7 @@ namespace cryptonote
uint64_t already_generated_coins; uint64_t already_generated_coins;
}; };
blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_comul_sz_limit(0), m_is_in_checkpoint_zone(false) blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false)
{}; {};
bool init() { return init(tools::get_default_data_dir()); } bool init() { return init(tools::get_default_data_dir()); }
@ -175,7 +175,7 @@ namespace cryptonote
blocks_by_id_index m_blocks_index; // crypto::hash -> height blocks_by_id_index m_blocks_index; // crypto::hash -> height
transactions_container m_transactions; transactions_container m_transactions;
key_images_container m_spent_keys; key_images_container m_spent_keys;
size_t m_current_block_comul_sz_limit; size_t m_current_block_cumul_sz_limit;
// all alternative chains // all alternative chains
@ -235,12 +235,12 @@ namespace cryptonote
/* */ /* */
/************************************************************************/ /************************************************************************/
#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 11 #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12
template<class archive_t> template<class archive_t>
void blockchain_storage::serialize(archive_t & ar, const unsigned int version) void blockchain_storage::serialize(archive_t & ar, const unsigned int version)
{ {
if(version < CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) if(version < 11)
return; return;
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
ar & m_blocks; ar & m_blocks;
@ -250,14 +250,54 @@ namespace cryptonote
ar & m_alternative_chains; ar & m_alternative_chains;
ar & m_outputs; ar & m_outputs;
ar & m_invalid_blocks; ar & m_invalid_blocks;
ar & m_current_block_comul_sz_limit; ar & m_current_block_cumul_sz_limit;
/*serialization bug workaround*/
if(version > 11)
{
uint64_t total_check_count = m_blocks.size() + m_blocks_index.size() + m_transactions.size() + m_spent_keys.size() + m_alternative_chains.size() + m_outputs.size() + m_invalid_blocks.size() + m_current_block_cumul_sz_limit;
if(archive_t::is_saving::value)
{
ar & total_check_count;
}else
{
uint64_t total_check_count_loaded = 0;
ar & total_check_count_loaded;
if(total_check_count != total_check_count_loaded)
{
LOG_ERROR("Blockchain storage data corruption detected. total_count loaded from file = " << total_check_count_loaded << ", expected = " << total_check_count);
LOG_PRINT_L0("Blockchain storage:" << ENDL <<
"m_blocks: " << m_blocks.size() << ENDL <<
"m_blocks_index: " << m_blocks_index.size() << ENDL <<
"m_transactions: " << m_transactions.size() << ENDL <<
"m_spent_keys: " << m_spent_keys.size() << ENDL <<
"m_alternative_chains: " << m_alternative_chains.size() << ENDL <<
"m_outputs: " << m_outputs.size() << ENDL <<
"m_invalid_blocks: " << m_invalid_blocks.size() << ENDL <<
"m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
throw std::runtime_error("Blockchain data corruption");
}
}
}
LOG_PRINT_L2("Blockchain storage:" << ENDL <<
"m_blocks: " << m_blocks.size() << ENDL <<
"m_blocks_index: " << m_blocks_index.size() << ENDL <<
"m_transactions: " << m_transactions.size() << ENDL <<
"m_spent_keys: " << m_spent_keys.size() << ENDL <<
"m_alternative_chains: " << m_alternative_chains.size() << ENDL <<
"m_outputs: " << m_outputs.size() << ENDL <<
"m_invalid_blocks: " << m_invalid_blocks.size() << ENDL <<
"m_current_block_cumul_sz_limit: " << m_current_block_cumul_sz_limit);
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
template<class visitor_t> template<class visitor_t>
bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height)
{ {
CRITICAL_REGION_LOCAL(m_blockchain_lock);
auto it = m_outputs.find(tx_in_to_key.amount); auto it = m_outputs.find(tx_in_to_key.amount);
if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size())
return false; return false;

View File

@ -33,46 +33,39 @@ namespace cryptonote {
return CRYPTONOTE_MAX_TX_SIZE; return CRYPTONOTE_MAX_TX_SIZE;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
uint64_t get_block_reward(std::vector<size_t>& last_blocks_sizes, size_t current_block_size, bool& block_too_big, uint64_t already_generated_coins) bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) {
{
block_too_big = false;
uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> 18; uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> 18;
if(current_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE)
return base_reward;
size_t med_sz = misc_utils::median(last_blocks_sizes);
//make it soft //make it soft
if(med_sz < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) if (median_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) {
med_sz = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; median_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE;
if(current_block_size > med_sz)
{
if(current_block_size > med_sz*2)
{
LOG_PRINT_L0("Block cumulative size is too big: " << current_block_size << ", expected less than " << med_sz*2);
block_too_big = true;
return 0;
} }
assert(med_sz < std::numeric_limits<uint32_t>::max()); if (current_block_size <= median_size) {
reward = base_reward;
return true;
}
if(current_block_size > 2 * median_size) {
LOG_PRINT_L4("Block cumulative size is too big: " << current_block_size << ", expected less than " << 2 * median_size);
return false;
}
assert(median_size < std::numeric_limits<uint32_t>::max());
assert(current_block_size < std::numeric_limits<uint32_t>::max()); assert(current_block_size < std::numeric_limits<uint32_t>::max());
uint64_t product_hi; uint64_t product_hi;
uint64_t product_lo = mul128(base_reward, current_block_size * (2 * med_sz - current_block_size), &product_hi); uint64_t product_lo = mul128(base_reward, current_block_size * (2 * median_size - current_block_size), &product_hi);
uint64_t reward_hi; uint64_t reward_hi;
uint64_t reward_lo; uint64_t reward_lo;
div128_32(product_hi, product_lo, static_cast<uint32_t>(med_sz), &reward_hi, &reward_lo); div128_32(product_hi, product_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo);
div128_32(reward_hi, reward_lo, static_cast<uint32_t>(med_sz), &reward_hi, &reward_lo); div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo);
assert(0 == reward_hi); assert(0 == reward_hi);
assert(reward_lo < base_reward); assert(reward_lo < base_reward);
return reward_lo; reward = reward_lo;
}else return true;
return base_reward;
} }
//------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------
uint8_t get_account_address_checksum(const public_address_outer_blob& bl) uint8_t get_account_address_checksum(const public_address_outer_blob& bl)

View File

@ -38,7 +38,7 @@ namespace cryptonote {
/************************************************************************/ /************************************************************************/
size_t get_max_block_size(); size_t get_max_block_size();
size_t get_max_tx_size(); size_t get_max_tx_size();
uint64_t get_block_reward(std::vector<size_t>& last_blocks_sizes, size_t current_block_size, bool& block_too_big, uint64_t already_generated_coins); bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward);
uint8_t get_account_address_checksum(const public_address_outer_blob& bl); uint8_t get_account_address_checksum(const public_address_outer_blob& bl);
std::string get_account_address_as_str(const account_public_address& adr); std::string get_account_address_as_str(const account_public_address& adr);
bool get_account_address_from_str(account_public_address& adr, const std::string& str); bool get_account_address_from_str(account_public_address& adr, const std::string& str);

View File

@ -54,13 +54,7 @@ namespace cryptonote
return true; return true;
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, size_t max_outs) bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs) {
{
return construct_miner_tx(height, already_generated_coins, miner_address, tx, fee, blocks_sizes, current_block_size, blobdata(), max_outs);
}
//---------------------------------------------------------------
bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, const blobdata& extra_nonce, size_t max_outs)
{
tx.vin.clear(); tx.vin.clear();
tx.vout.clear(); tx.vout.clear();
tx.extra.clear(); tx.extra.clear();
@ -74,13 +68,13 @@ namespace cryptonote
txin_gen in; txin_gen in;
in.height = height; in.height = height;
bool block_too_big = false; uint64_t block_reward;
uint64_t block_reward = get_block_reward(blocks_sizes, current_block_size, block_too_big, already_generated_coins) + fee; if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward))
if(block_too_big)
{ {
LOG_PRINT_L0("Block is too big"); LOG_PRINT_L0("Block is too big");
return false; return false;
} }
block_reward += fee;
std::vector<size_t> out_amounts; std::vector<size_t> out_amounts;
decompose_amount_into_digits(block_reward, DEFAULT_FEE, decompose_amount_into_digits(block_reward, DEFAULT_FEE,
@ -152,33 +146,37 @@ namespace cryptonote
//--------------------------------------------------------------- //---------------------------------------------------------------
bool parse_amount(uint64_t& amount, const std::string& str_amount_) bool parse_amount(uint64_t& amount, const std::string& str_amount_)
{ {
std::vector<std::string> pars;
std::string str_amount = str_amount_; std::string str_amount = str_amount_;
boost::algorithm::trim(str_amount); boost::algorithm::trim(str_amount);
if(!str_amount.size())
size_t point_index = str_amount.find_first_of('.');
size_t fraction_size;
if (std::string::npos != point_index)
{
fraction_size = str_amount.size() - point_index - 1;
while (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back())
{
str_amount.erase(str_amount.size() - 1, 1);
--fraction_size;
}
if (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size)
return false; return false;
if(str_amount[0] == '-') str_amount.erase(point_index, 1);
}
else
{
fraction_size = 0;
}
if (str_amount.empty())
return false; return false;
boost::split(pars, str_amount, boost::is_any_of("."), boost::token_compress_on ); if (fraction_size < CRYPTONOTE_DISPLAY_DECIMAL_POINT)
if(pars.size() > 2 || pars.size() < 1)
return false;
uint64_t left = 0;
if(!string_tools::get_xtype_from_string(left, pars[0]))
return false;
amount = left * power_integral(10, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
if(pars.size() == 2)
{ {
uint64_t right = 0; str_amount.append(CRYPTONOTE_DISPLAY_DECIMAL_POINT - fraction_size, '0');
if(pars[1].size() > 8 )
return false;
if(pars[1].size() < 8 )
pars[1].append(8 - pars[1].size(), '0');
if(!string_tools::get_xtype_from_string(right, pars[1]))
return false;
amount += right;
} }
return true;
return string_tools::get_xtype_from_string(amount, str_amount);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool get_tx_fee(const transaction& tx, uint64_t & fee) bool get_tx_fee(const transaction& tx, uint64_t & fee)
@ -599,7 +597,7 @@ namespace cryptonote
account_public_address ac = boost::value_initialized<account_public_address>(); account_public_address ac = boost::value_initialized<account_public_address>();
std::vector<size_t> sz; std::vector<size_t> sz;
construct_miner_tx(0, 0, ac, bl.miner_tx, 0, sz, 0, 1); // zero fee in genesis construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis
blobdata txb = tx_to_blob(bl.miner_tx); blobdata txb = tx_to_blob(bl.miner_tx);
std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb);

View File

@ -18,8 +18,7 @@ namespace cryptonote
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, const blobdata& extra_nonce, size_t max_outs = 1); bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 1);
bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector<size_t>& blocks_sizes, size_t current_block_size, size_t max_outs = 1);
struct tx_source_entry struct tx_source_entry
{ {

View File

@ -2,8 +2,10 @@
// Distributed under the MIT/X11 software license, see the accompanying // Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <algorithm>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <unordered_set> #include <unordered_set>
#include <vector>
#include "tx_pool.h" #include "tx_pool.h"
#include "cryptonote_format_utils.h" #include "cryptonote_format_utils.h"
@ -11,6 +13,7 @@
#include "cryptonote_config.h" #include "cryptonote_config.h"
#include "blockchain_storage.h" #include "blockchain_storage.h"
#include "common/boost_serialization_helper.h" #include "common/boost_serialization_helper.h"
#include "common/int-util.h"
#include "misc_language.h" #include "misc_language.h"
#include "warnings.h" #include "warnings.h"
#include "crypto/hash.h" #include "crypto/hash.h"
@ -345,33 +348,62 @@ namespace cryptonote
return ss.str(); return ss.str();
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::fill_block_template(block& bl, size_t& cumulative_sizes, size_t max_comulative_sz, uint64_t& fee) bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee) {
{ typedef transactions_container::value_type txv;
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
std::vector<txv *> txs(m_transactions.size());
std::transform(m_transactions.begin(), m_transactions.end(), txs.begin(), [](txv &a) -> txv * { return &a; });
std::sort(txs.begin(), txs.end(), [](txv *a, txv *b) -> bool {
uint64_t a_hi, a_lo = mul128(a->second.fee, b->second.blob_size, &a_hi);
uint64_t b_hi, b_lo = mul128(b->second.fee, a->second.blob_size, &b_hi);
return a_hi > b_hi || (a_hi == b_hi && a_lo > b_lo);
});
size_t current_size = 0;
uint64_t current_fee = 0;
uint64_t best_money;
if (!get_block_reward(median_size, CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE, already_generated_coins, best_money)) {
LOG_ERROR("Block with just a miner transaction is already too large!");
return false;
}
size_t best_position = 0;
total_size = 0;
fee = 0; fee = 0;
std::unordered_set<crypto::key_image> k_images; std::unordered_set<crypto::key_image> k_images;
BOOST_FOREACH(transactions_container::value_type& tx, m_transactions) for (size_t i = 0; i < txs.size(); i++) {
{ txv &tx(*txs[i]);
if(cumulative_sizes + tx.second.blob_size > max_comulative_sz)
continue;
if(!is_transaction_ready_to_go(tx.second)) if(!is_transaction_ready_to_go(tx.second) || have_key_images(k_images, tx.second.tx)) {
txs[i] = NULL;
continue; continue;
}
if(have_key_images(k_images, tx.second.tx))
continue;
bl.tx_hashes.push_back(tx.first);
cumulative_sizes += tx.second.blob_size;
fee += tx.second.fee;
append_key_images(k_images, tx.second.tx); append_key_images(k_images, tx.second.tx);
if(cumulative_sizes >= max_comulative_sz) current_size += tx.second.blob_size;
current_fee += tx.second.fee;
uint64_t current_reward;
if (!get_block_reward(median_size, current_size + CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE, already_generated_coins, current_reward)) {
break; break;
} }
if (best_money < current_reward + current_fee) {
best_money = current_reward + current_fee;
best_position = i + 1;
total_size = current_size;
fee = current_fee;
}
}
for (size_t i = 0; i < best_position; i++) {
if (txs[i]) {
bl.tx_hashes.push_back(txs[i]->first);
}
}
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------

View File

@ -49,7 +49,7 @@ namespace cryptonote
// load/store operations // load/store operations
bool init(const std::string& config_folder); bool init(const std::string& config_folder);
bool deinit(); bool deinit();
bool fill_block_template(block& bl, size_t& cumulative_sizes, size_t max_comulative_sz, uint64_t& fee); bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee);
bool get_transactions(std::list<transaction>& txs); bool get_transactions(std::list<transaction>& txs);
bool get_transaction(const crypto::hash& h, transaction& tx); bool get_transaction(const crypto::hash& h, transaction& tx);
size_t get_transactions_count(); size_t get_transactions_count();

View File

@ -108,7 +108,9 @@ namespace cryptonote
return true; return true;
} }
LOG_PRINT_CCONTEXT_BLUE("Sync data returned unknown top block " << "["<< m_core.get_current_blockchain_height() << "->" << hshd.current_height << "] " << hshd.top_id << ", set SYNCHRONIZATION mode", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1)); LOG_PRINT_CCONTEXT_YELLOW("Sync data returned unknown top block: " << m_core.get_current_blockchain_height() << "->" << hshd.current_height
<< "[" << static_cast<int64_t>(hshd.current_height - m_core.get_current_blockchain_height()) << " blocks(" << (hshd.current_height - m_core.get_current_blockchain_height()) /720 << " days) behind] " << ENDL
<< "remote top: " << hshd.top_id << "[" << hshd.current_height << "]" << ", set SYNCHRONIZATION mode", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1));
context.m_state = cryptonote_connection_context::state_synchronizing; context.m_state = cryptonote_connection_context::state_synchronizing;
context.m_remote_blockchain_height = hshd.current_height; context.m_remote_blockchain_height = hshd.current_height;
//let the socket to send response to handshake, but request callback, to let send request data after response //let the socket to send response to handshake, but request callback, to let send request data after response

View File

@ -29,9 +29,6 @@ using namespace epee;
namespace po = boost::program_options; namespace po = boost::program_options;
BOOST_CLASS_VERSION(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >, 1);
namespace namespace
{ {
const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", "Specify configuration file", std::string(CRYPTONOTE_NAME ".conf")}; const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", "Specify configuration file", std::string(CRYPTONOTE_NAME ".conf")};
@ -51,7 +48,7 @@ int main(int argc, char* argv[])
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif #endif
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0);
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, 0);
LOG_PRINT_L0("Starting..."); LOG_PRINT_L0("Starting...");
TRY_ENTRY(); TRY_ENTRY();

View File

@ -199,6 +199,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx) bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, connection_context& cntx)
{ {
CHECK_CORE_READY();
std::string tx_blob; std::string tx_blob;
if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob)) if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob))
{ {

View File

@ -15,6 +15,7 @@
#include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_format_utils.h"
#include "storages/http_abstract_invoke.h" #include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.h" #include "rpc/core_rpc_server_commands_defs.h"
#include "wallet/wallet_rpc_server.h"
#include "version.h" #include "version.h"
#if defined(WIN32) #if defined(WIN32)
@ -37,30 +38,112 @@ namespace
const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", "Use daemon instance at <host>:<port>", ""}; const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", "Use daemon instance at <host>:<port>", ""};
const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", "Use daemon instance at host <arg> instead of localhost", ""}; const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", "Use daemon instance at host <arg> instead of localhost", ""};
const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true}; const command_line::arg_descriptor<std::string> arg_password = {"password", "Wallet password", "", true};
const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of 8080", 0}; const command_line::arg_descriptor<int> arg_daemon_port = {"daemon-port", "Use daemon instance at port <arg> instead of 8081", 0};
const command_line::arg_descriptor<uint32_t> arg_log_level = {"set_log", "", 0, true}; const command_line::arg_descriptor<uint32_t> arg_log_level = {"set_log", "", 0, true};
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""}; const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
void print_success_msg(const std::string& msg, bool color = false) inline std::string interpret_rpc_response(bool ok, const std::string& status)
{ {
LOG_PRINT_L4(msg); std::string err;
if (color) epee::log_space::set_console_color(epee::log_space::console_color_green, false); if (ok)
std::cout << msg; {
if (color) epee::log_space::reset_console_color(); if (status == CORE_RPC_STATUS_BUSY)
std::cout << std::endl; {
err = "daemon is busy. Please try later";
}
else if (status != CORE_RPC_STATUS_OK)
{
err = status;
}
}
else
{
err = "possible lost connection to daemon";
}
return err;
} }
void print_fail_msg(const std::string& msg) class message_writer
{ {
LOG_PRINT_L1("Error:" << msg); public:
epee::log_space::set_console_color(epee::log_space::console_color_red, true); message_writer(epee::log_space::console_colors color = epee::log_space::console_color_default, bool bright = false,
std::cout << "Error: " << msg; std::string&& prefix = std::string(), int log_level = LOG_LEVEL_2)
: m_flush(true)
, m_color(color)
, m_bright(bright)
, m_log_level(log_level)
{
m_oss << prefix;
}
message_writer(message_writer&& rhs)
: m_flush(std::move(rhs.m_flush))
#if defined(_MSC_VER)
, m_oss(std::move(rhs.m_oss))
#else
// GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316
, m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate)
#endif
, m_color(std::move(rhs.m_color))
, m_log_level(std::move(rhs.m_log_level))
{
rhs.m_flush = false;
}
template<typename T>
message_writer& operator<<(const T& val)
{
m_oss << val;
return *this;
}
~message_writer()
{
if (m_flush)
{
m_flush = false;
LOG_PRINT(m_oss.str(), m_log_level)
if (epee::log_space::console_color_default == m_color)
{
std::cout << m_oss.str();
}
else
{
epee::log_space::set_console_color(m_color, m_bright);
std::cout << m_oss.str();
epee::log_space::reset_console_color(); epee::log_space::reset_console_color();
}
std::cout << std::endl; std::cout << std::endl;
} }
} }
private:
message_writer(message_writer& rhs);
message_writer& operator=(message_writer& rhs);
message_writer& operator=(message_writer&& rhs);
private:
bool m_flush;
std::stringstream m_oss;
epee::log_space::console_colors m_color;
bool m_bright;
int m_log_level;
};
message_writer success_msg_writer(bool color = false)
{
return message_writer(color ? epee::log_space::console_color_green : epee::log_space::console_color_default, false, std::string(), LOG_LEVEL_2);
}
message_writer fail_msg_writer()
{
return message_writer(epee::log_space::console_color_red, true, "Error: ", LOG_LEVEL_0);
}
}
std::string simple_wallet::get_commands_str() std::string simple_wallet::get_commands_str()
{ {
@ -75,21 +158,22 @@ std::string simple_wallet::get_commands_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>()*/)
{ {
std::cout << get_commands_str(); success_msg_writer() << get_commands_str();
return true; return true;
} }
simple_wallet::simple_wallet() simple_wallet::simple_wallet()
: m_daemon_port(0) : m_daemon_port(0)
, m_refresh_progress_reporter(*this)
{ {
m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "Start mining in daemon, start_mining <threads_count>"); m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), "start_mining <threads_count> - Start mining in daemon");
m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon"); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), "Stop mining in daemon");
m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance"); m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), "Resynchronize transactions and balance");
m_cmd_binder.set_handler("show_balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance"); m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance");
m_cmd_binder.set_handler("show_incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers"); m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "incoming_transfers [available|unavailable] - Show incoming transfers - all of them or filter them by availability");
m_cmd_binder.set_handler("show_bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height"); m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height");
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> {<addr> <amount>} Transfer <amount> to <address>. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)"); m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer <mixin_count> <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)");
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "Change current log detalization level, <level> is a number 0-4"); m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4");
m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address"); m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address");
m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data"); m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data");
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help"); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), "Show this help");
@ -99,18 +183,18 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
{ {
if(args.size() != 1) if(args.size() != 1)
{ {
std::cout << "use: set_log <log_level_number_0-4>" << ENDL; fail_msg_writer() << "use: set_log <log_level_number_0-4>";
return true; return true;
} }
uint16_t l = 0; uint16_t l = 0;
if(!string_tools::get_xtype_from_string(l, args[0])) if(!string_tools::get_xtype_from_string(l, args[0]))
{ {
std::cout << "wrong number format, use: set_log <log_level_number_0-4>" << ENDL; fail_msg_writer() << "wrong number format, use: set_log <log_level_number_0-4>";
return true; return true;
} }
if(LOG_LEVEL_4 < l) if(LOG_LEVEL_4 < l)
{ {
std::cout << "wrong number range, use: set_log <log_level_number_0-4>" << ENDL; fail_msg_writer() << "wrong number range, use: set_log <log_level_number_0-4>";
return true; return true;
} }
@ -122,19 +206,27 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{ {
handle_command_line(vm); handle_command_line(vm);
CHECK_AND_ASSERT_MES(m_daemon_address.empty() || (m_daemon_host.empty() && !m_daemon_port), false, "you can't specify daemon host or port several times"); if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port)
{
fail_msg_writer() << "you can't specify daemon host or port several times";
return false;
}
size_t c = 0; size_t c = 0;
if(!m_generate_new.empty()) ++c; if(!m_generate_new.empty()) ++c;
if(!m_wallet_file.empty()) ++c; if(!m_wallet_file.empty()) ++c;
CHECK_AND_ASSERT_MES(c == 1, false, "you must specify --wallet-file or --generate-new-wallet params"); if (1 != c)
{
fail_msg_writer() << "you must specify --wallet-file or --generate-new-wallet params";
return false;
}
if (m_daemon_host.empty()) if (m_daemon_host.empty())
m_daemon_host = "localhost"; m_daemon_host = "localhost";
if (!m_daemon_port) if (!m_daemon_port)
m_daemon_port = RPC_DEFAULT_PORT; m_daemon_port = RPC_DEFAULT_PORT;
if (m_daemon_address.empty()) if (m_daemon_address.empty())
m_daemon_address = string("http://") + m_daemon_host + ":" + lexical_cast<string>(m_daemon_port); m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port);
tools::password_container pwd_container; tools::password_container pwd_container;
if (command_line::has_arg(vm, arg_password)) if (command_line::has_arg(vm, arg_password))
@ -144,7 +236,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
else else
{ {
bool r = pwd_container.read_password(); bool r = pwd_container.read_password();
CHECK_AND_ASSERT_MES(r, false, "failed to read wallet password"); if (!r)
{
fail_msg_writer() << "failed to read wallet password";
return false;
}
} }
if (!m_generate_new.empty()) if (!m_generate_new.empty())
@ -169,25 +265,22 @@ bool simple_wallet::deinit()
return close_wallet(); return close_wallet();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool 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 = command_line::get_arg(vm, arg_wallet_file); m_wallet_file = command_line::get_arg(vm, arg_wallet_file);
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet); m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_daemon_address = command_line::get_arg(vm, arg_daemon_address); m_daemon_address = command_line::get_arg(vm, arg_daemon_address);
m_daemon_host = command_line::get_arg(vm, arg_daemon_host); m_daemon_host = command_line::get_arg(vm, arg_daemon_host);
m_daemon_port = command_line::get_arg(vm, arg_daemon_port); m_daemon_port = command_line::get_arg(vm, arg_daemon_port);
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::try_connect_to_daemon() bool simple_wallet::try_connect_to_daemon()
{ {
if (!m_wallet->check_connection()) if (!m_wallet->check_connection())
{ {
std::string msg = "wallet failed to connect to daemon (" + m_daemon_address + "). " + fail_msg_writer() << "wallet failed to connect to daemon (" << m_daemon_address << "). " <<
"Daemon either is not started or passed wrong port. " + "Daemon either is not started or passed wrong port. " <<
"Please, make sure that daemon is running or restart the wallet with correct daemon address."; "Please, make sure that daemon is running or restart the wallet with correct daemon address.";
print_fail_msg(msg);
return false; return false;
} }
return true; return true;
@ -196,29 +289,31 @@ bool simple_wallet::try_connect_to_daemon()
bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password) bool simple_wallet::new_wallet(const string &wallet_file, const std::string& password)
{ {
m_wallet_file = wallet_file; m_wallet_file = wallet_file;
if(boost::filesystem::exists(wallet_file))
m_wallet.reset(new tools::wallet2());
m_wallet->callback(this);
try
{ {
std::cout << "wallet creation failed, file " << wallet_file << " already exists" << std::endl; m_wallet->generate(wallet_file, password);
message_writer(epee::log_space::console_color_white, true) << "Generated new wallet: " << m_wallet->get_account().get_public_address_str();
}
catch (const std::exception& e)
{
fail_msg_writer() << "failed to generate new wallet: " << e.what();
return false; return false;
} }
m_wallet.reset(new tools::wallet2()); m_wallet->init(m_daemon_address);
bool r = m_wallet->generate(wallet_file, password);
if(!r)
return false;
cout << "Generated new wallet" << ENDL; success_msg_writer() <<
print_address(std::vector<std::string>()); "**********************************************************************\n" <<
r = m_wallet->init(m_daemon_address); "Your wallet has been generated.\n" <<
CHECK_AND_ASSERT_MES(r, false, "failed to init wallet"); "To start synchronizing with the daemon use \"refresh\" command.\n" <<
std::cout << "**********************************************************************" << ENDL "Use \"help\" command to see the list of available commands.\n" <<
<< "Your wallet has been generated. " << ENDL "Always use \"exit\" command when closing simplewallet to save\n" <<
<< "To start synchronizing with the daemon use \"refresh\" command." << ENDL "current session's state. Otherwise, you will possibly need to synchronize \n" <<
<< "Use \"help\" command to see the list of available commands." << ENDL "your wallet again. Your wallet key is NOT under risk anyway.\n" <<
<< "Always use \"exit\" command when closing simplewallet to save " "**********************************************************************";
<< "current session's state. Otherwise, you will possibly need to synchronize " << ENDL
<< "your wallet again. Your wallet key is NOT under risk anyway." << ENDL
<< "**********************************************************************" << ENDL;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -226,35 +321,63 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
{ {
m_wallet_file = wallet_file; m_wallet_file = wallet_file;
m_wallet.reset(new tools::wallet2()); m_wallet.reset(new tools::wallet2());
m_wallet->callback(this);
bool r = m_wallet->load(m_wallet_file, password); try
CHECK_AND_ASSERT_MES(r, false, "failed to load wallet " + m_wallet_file); {
r = m_wallet->init(m_daemon_address); m_wallet->load(m_wallet_file, password);
CHECK_AND_ASSERT_MES(r, false, "failed to init wallet"); message_writer(epee::log_space::console_color_white, true) << "Opened wallet: " << m_wallet->get_account().get_public_address_str();
}
catch (const std::exception& e)
{
fail_msg_writer() << "failed to load wallet: " << e.what();
return false;
}
m_wallet->init(m_daemon_address);
refresh(std::vector<std::string>()); refresh(std::vector<std::string>());
std::cout << "**********************************************************************" << ENDL success_msg_writer() <<
<< "Use \"help\" command to see the list of available commands." << ENDL "**********************************************************************\n" <<
<< "**********************************************************************" << ENDL ; "Use \"help\" command to see the list of available commands.\n" <<
"**********************************************************************";
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::close_wallet() bool simple_wallet::close_wallet()
{ {
bool r = m_wallet->deinit(); bool r = m_wallet->deinit();
CHECK_AND_ASSERT_MES(r, false, "failed to deinit wallet"); if (!r)
r = m_wallet->store(); {
CHECK_AND_ASSERT_MES(r, false, "failed to store wallet " + m_wallet_file); fail_msg_writer() << "failed to deinit wallet";
return false;
}
try
{
m_wallet->store();
}
catch (const std::exception& e)
{
fail_msg_writer() << e.what();
return false;
}
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::save(const std::vector<std::string> &args) bool simple_wallet::save(const std::vector<std::string> &args)
{ {
bool r = m_wallet->store(); try
if (r) {
print_success_msg("Wallet data saved"); m_wallet->store();
else success_msg_writer() << "Wallet data saved";
print_fail_msg("failed to store wallet " + m_wallet_file); }
catch (const std::exception& e)
{
fail_msg_writer() << e.what();
}
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -276,24 +399,24 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
bool ok = string_tools::get_xtype_from_string(num, args[0]); bool ok = string_tools::get_xtype_from_string(num, args[0]);
if(!ok || 0 == num) if(!ok || 0 == num)
{ {
print_fail_msg("wrong number of mining threads: \"" + args[0] + "\""); fail_msg_writer() << "wrong number of mining threads: \"" << args[0] << "\"";
return true; return true;
} }
req.threads_count = num; req.threads_count = num;
} }
else else
{ {
print_fail_msg("wrong number of arguments, expected the number of mining threads"); fail_msg_writer() << "wrong number of arguments, expected the number of mining threads";
return true; return true;
} }
COMMAND_RPC_START_MINING::response res; COMMAND_RPC_START_MINING::response res;
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client); bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client);
std::string err = tools::interpret_rpc_response(r, res.status); std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) if (err.empty())
print_success_msg("Mining started in daemon"); success_msg_writer() << "Mining started in daemon";
else else
print_fail_msg("mining has NOT been started: " + err); fail_msg_writer() << "mining has NOT been started: " << err;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -305,82 +428,158 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
COMMAND_RPC_STOP_MINING::request req; COMMAND_RPC_STOP_MINING::request req;
COMMAND_RPC_STOP_MINING::response res; COMMAND_RPC_STOP_MINING::response res;
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client); bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client);
std::string err = tools::interpret_rpc_response(r, res.status); std::string err = interpret_rpc_response(r, res.status);
if (err.empty()) if (err.empty())
print_success_msg("Mining stopped in daemon"); success_msg_writer() << "Mining stopped in daemon";
else else
print_fail_msg("mining has NOT been stopped: " + err); fail_msg_writer() << "mining has NOT been stopped: " << err;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block)
{
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index)
{
message_writer(epee::log_space::console_color_green, false) <<
"Height " << height <<
", transaction " << get_transaction_hash(tx) <<
", received " << print_money(tx.vout[out_index].amount);
m_refresh_progress_reporter.update(height, true);
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx)
{
message_writer(epee::log_space::console_color_magenta, false) <<
"Height " << height <<
", transaction " << get_transaction_hash(spend_tx) <<
", spent " << print_money(in_tx.vout[out_index].amount);
m_refresh_progress_reporter.update(height, true);
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::refresh(const std::vector<std::string>& args) bool simple_wallet::refresh(const std::vector<std::string>& args)
{ {
if (!try_connect_to_daemon()) if (!try_connect_to_daemon())
return true; return true;
std::cout << "Starting refresh..." << endl; message_writer() << "Starting refresh...";
std::atomic<bool> refresh_is_done(false);
std::thread th([&]()
{
epee::misc_utils::sleep_no_w(1000);
while(!refresh_is_done)
{
std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err);
if (err.empty())
cout << "Height " << m_wallet->get_blockchain_current_height() << " of " << bc_height << endl;
epee::misc_utils::sleep_no_w(1000);
}
});
uint64_t initial_height = m_wallet->get_blockchain_current_height();
uint64_t fetched_blocks = 0; uint64_t fetched_blocks = 0;
bool money_received = false; bool ok = false;
tools::wallet2::fail_details fd; std::ostringstream ss;
bool ok = m_wallet->refresh(fetched_blocks, money_received, fd); try
refresh_is_done = true;
th.join();
if (ok)
{ {
std::stringstream ss; m_wallet->refresh(fetched_blocks);
ss << "Refresh done, blocks received: " << fetched_blocks; ok = true;
print_success_msg(ss.str(), true); // Clear line "Height xxx of xxx"
std::cout << "\r \r";
success_msg_writer(true) << "Refresh done, blocks received: " << fetched_blocks;
show_balance(); show_balance();
} }
else catch (const tools::error::daemon_busy&)
{ {
fetched_blocks = m_wallet->get_blockchain_current_height() - initial_height; ss << "daemon is busy. Please try later";
std::stringstream ss;
ss << "refresh failed: " << fd.what() << ". Blocks received: " << fetched_blocks;
print_fail_msg(ss.str());
} }
catch (const tools::error::no_connection_to_daemon&)
{
ss << "no connection to daemon. Please, make sure daemon is running";
}
catch (const tools::error::wallet_rpc_error& e)
{
LOG_ERROR("Unknown RPC error: " << e.to_string());
ss << "RPC error \"" << e.what() << '"';
}
catch (const tools::error::refresh_error& e)
{
LOG_ERROR("refresh error: " << e.to_string());
ss << e.what();
}
catch (const tools::error::wallet_internal_error& e)
{
LOG_ERROR("internal error: " << e.to_string());
ss << "internal error: " << e.what();
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
ss << "unexpected error: " << e.what();
}
catch (...)
{
LOG_ERROR("Unknown error");
ss << "unknown error";
}
if (!ok)
{
fail_msg_writer() << "refresh failed: " << ss.str() << ". Blocks received: " << fetched_blocks;
}
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
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>()*/)
{ {
std::stringstream ss; success_msg_writer() << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
ss << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
print_success_msg(ss.str());
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args) bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args)
{ {
std::cout << " amount \tspent\tglobal index\t tx id" << std::endl; bool filter = false;
bool ok = m_wallet->enum_incoming_transfers([](const cryptonote::transaction& tx, uint64_t global_out_index, uint64_t amount, bool spent) { bool available = false;
epee::log_space::set_console_color(spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, true); if (!args.empty())
std::cout << std::setw(21) << print_money(amount) << '\t' {
<< std::setw(3) << (spent ? 'T' : 'F') << " \t" if (args[0] == "available")
<< std::setw(12) << global_out_index << '\t' {
<< get_transaction_hash(tx) filter = true;
<< '\n'; available = true;
}); }
epee::log_space::reset_console_color(); else if (args[0] == "unavailable")
if (ok) {
std::cout.flush(); filter = true;
available = false;
}
}
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
bool transfers_found = false;
for (const auto& td : transfers)
{
if (!filter || available != td.m_spent)
{
if (!transfers_found)
{
message_writer() << " amount \tspent\tglobal index\t tx id";
transfers_found = true;
}
message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
std::setw(21) << print_money(td.amount()) << '\t' <<
std::setw(3) << (td.m_spent ? 'T' : 'F') << " \t" <<
std::setw(12) << td.m_global_output_index << '\t' <<
get_transaction_hash(td.m_tx);
}
}
if (!transfers_found)
{
if (!filter)
{
success_msg_writer() << "No incoming transfers";
}
else if (available)
{
success_msg_writer() << "No incoming available transfers";
}
else else
print_fail_msg("No incoming transfers"); {
success_msg_writer() << "No incoming unavailable transfers";
}
}
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -389,7 +588,7 @@ uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err)
COMMAND_RPC_GET_HEIGHT::request req; COMMAND_RPC_GET_HEIGHT::request req;
COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>(); COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized<COMMAND_RPC_GET_HEIGHT::response>();
bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client); bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client);
err = tools::interpret_rpc_response(r, res.status); err = interpret_rpc_response(r, res.status);
return res.height; return res.height;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -401,9 +600,9 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
std::string err; std::string err;
uint64_t bc_height = get_daemon_blockchain_height(err); uint64_t bc_height = get_daemon_blockchain_height(err);
if (err.empty()) if (err.empty())
print_success_msg(boost::lexical_cast<std::string>(bc_height)); success_msg_writer() << bc_height;
else else
print_fail_msg("failed to get blockchain height: " + err); fail_msg_writer() << "failed to get blockchain height: " << err;
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -415,65 +614,132 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
std::vector<std::string> local_args = args_; std::vector<std::string> local_args = args_;
if(local_args.size() < 3) if(local_args.size() < 3)
{ {
print_fail_msg("wrong number of arguments, expected at least 3, got " + boost::lexical_cast<std::string>(local_args.size())); fail_msg_writer() << "wrong number of arguments, expected at least 3, got " << local_args.size();
return true; return true;
} }
size_t fake_outs_count; size_t fake_outs_count;
if(!string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) if(!string_tools::get_xtype_from_string(fake_outs_count, local_args[0]))
{ {
print_fail_msg("mixin_count should be non-negative integer, got " + local_args[0]); fail_msg_writer() << "mixin_count should be non-negative integer, got " << local_args[0];
return true; return true;
} }
local_args.erase(local_args.begin());
vector<cryptonote::tx_destination_entry> dsts; vector<cryptonote::tx_destination_entry> dsts;
uint64_t summary_amount = 0; for (size_t i = 1; i < local_args.size(); i += 2)
for (size_t i = 0; i < local_args.size(); i += 2)
{ {
cryptonote::tx_destination_entry de; cryptonote::tx_destination_entry de;
if(!get_account_address_from_str(de.addr, local_args[i])) if(!get_account_address_from_str(de.addr, local_args[i]))
{ {
print_fail_msg("wrong address: " + local_args[i]); fail_msg_writer() << "wrong address: " << local_args[i];
return true; return true;
} }
if (local_args.size() <= i + 1) if (local_args.size() <= i + 1)
{ {
print_fail_msg("amount for the last address " + local_args[i] + " is not specified"); fail_msg_writer() << "amount for the last address " << local_args[i] << " is not specified";
return true; return true;
} }
bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]); bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
if(!ok || 0 == de.amount) if(!ok || 0 == de.amount)
{ {
print_fail_msg("amount is wrong: " + local_args[i] + " " + local_args[i + 1]); fail_msg_writer() << "amount is wrong: " << local_args[i] << ' ' << local_args[i + 1] <<
", expected number from 0 to " << print_money(std::numeric_limits<uint64_t>::max());
return true; return true;
} }
summary_amount += de.amount;
dsts.push_back(de); dsts.push_back(de);
} }
if(summary_amount > m_wallet->unlocked_balance()) try
{ {
print_fail_msg("not enough money to transfer " + print_money(summary_amount) + ", available (unlocked) only " + print_money(m_wallet->unlocked_balance())); cryptonote::transaction tx;
return true; m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, tx);
success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx);
}
catch (const tools::error::daemon_busy&)
{
fail_msg_writer() << "daemon is busy. Please try later";
}
catch (const tools::error::no_connection_to_daemon&)
{
fail_msg_writer() << "no connection to daemon. Please, make sure daemon is running.";
}
catch (const tools::error::wallet_rpc_error& e)
{
LOG_ERROR("Unknown RPC error: " << e.to_string());
fail_msg_writer() << "RPC error \"" << e.what() << '"';
}
catch (const tools::error::get_random_outs_error&)
{
fail_msg_writer() << "failed to get random outputs to mix";
}
catch (const tools::error::not_enough_money& e)
{
fail_msg_writer() << "not enough money to transfer, available only " << print_money(e.available()) <<
", transaction amount " << print_money(e.tx_amount() + e.fee()) << " = " << print_money(e.tx_amount()) <<
" + " << print_money(e.fee()) << " (fee)";
}
catch (const tools::error::not_enough_outs_to_mix& e)
{
auto writer = fail_msg_writer();
writer << "not enough outputs for specified mixin_count = " << e.mixin_count() << ":";
for (const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& outs_for_amount : e.scanty_outs())
{
writer << "\noutput amount = " << print_money(outs_for_amount.amount) << ", fount outputs to mix = " << outs_for_amount.outs.size();
}
}
catch (const tools::error::tx_not_constructed&)
{
fail_msg_writer() << "transaction was not constructed";
}
catch (const tools::error::tx_rejected& e)
{
fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " was rejected by daemon with status \"" << e.status() << '"';
}
catch (const tools::error::tx_sum_overflow& e)
{
fail_msg_writer() << e.what();
}
catch (const tools::error::tx_too_big& e)
{
cryptonote::transaction tx = e.tx();
fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " is too big. Transaction size: " <<
get_object_blobsize(e.tx()) << " bytes, transaction size limit: " << e.tx_size_limit() << " bytes";
}
catch (const tools::error::zero_destination&)
{
fail_msg_writer() << "one of destinations is zero";
}
catch (const tools::error::transfer_error& e)
{
LOG_ERROR("unknown transfer error: " << e.to_string());
fail_msg_writer() << "unknown transfer error: " << e.what();
}
catch (const tools::error::wallet_internal_error& e)
{
LOG_ERROR("internal error: " << e.to_string());
fail_msg_writer() << "internal error: " << e.what();
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
fail_msg_writer() << "unexpected error: " << e.what();
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << "unknown error";
} }
cryptonote::transaction tx;
tools::wallet2::fail_details tfd;
bool ok = m_wallet->transfer(dsts, fake_outs_count, 0, DEFAULT_FEE, tx, tfd);
if (ok)
print_success_msg("Money successfully sent", true);
else
print_fail_msg("failed to transfer money: " + tfd.what());
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::run() bool simple_wallet::run()
{ {
return m_cmd_binder.run_handling("[wallet]# ", ""); std::string addr_start = m_wallet->get_account().get_public_address_str().substr(0, 6);
return m_cmd_binder.run_handling("[wallet " + addr_start + "]: ", "");
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void simple_wallet::stop() void simple_wallet::stop()
@ -482,9 +748,9 @@ void simple_wallet::stop()
m_wallet->stop(); m_wallet->stop();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool simple_wallet::print_address(const std::vector<std::string> &args) bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{ {
print_success_msg(m_wallet->get_account().get_public_address_str()); success_msg_writer() << m_wallet->get_account().get_public_address_str();
return true; return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -495,7 +761,6 @@ bool simple_wallet::process_command(const std::vector<std::string> &args)
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
#ifdef WIN32 #ifdef WIN32
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif #endif
@ -517,6 +782,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_daemon_port); command_line::add_arg(desc_params, arg_daemon_port);
command_line::add_arg(desc_params, arg_command); command_line::add_arg(desc_params, arg_command);
command_line::add_arg(desc_params, arg_log_level); command_line::add_arg(desc_params, arg_log_level);
tools::wallet_rpc_server::init_options(desc_params);
po::positional_options_description positional_options; po::positional_options_description positional_options;
positional_options.add(arg_command.name, -1); positional_options.add(arg_command.name, -1);
@ -531,13 +797,13 @@ int main(int argc, char* argv[])
if (command_line::get_arg(vm, command_line::arg_help)) if (command_line::get_arg(vm, command_line::arg_help))
{ {
std::cout << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]\n"; success_msg_writer() << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]";
std::cout << desc_all << '\n' << w.get_commands_str() << std::endl; success_msg_writer() << desc_all << '\n' << w.get_commands_str();
return false; return false;
} }
else if (command_line::get_arg(vm, command_line::arg_version)) else if (command_line::get_arg(vm, command_line::arg_version))
{ {
std::cout << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG << ENDL; success_msg_writer() << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG;
return false; return false;
} }
@ -551,12 +817,12 @@ int main(int argc, char* argv[])
//set up logging options //set up logging options
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0); //log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0);
log_space::log_singletone::add_logger(LOGGER_FILE, log_space::log_singletone::add_logger(LOGGER_FILE,
log_space::log_singletone::get_default_log_file().c_str(), log_space::log_singletone::get_default_log_file().c_str(),
log_space::log_singletone::get_default_log_folder().c_str(), LOG_LEVEL_4); log_space::log_singletone::get_default_log_folder().c_str(), LOG_LEVEL_4);
LOG_PRINT_L0(CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG); message_writer(epee::log_space::console_color_white, true) << CRYPTONOTE_NAME << " wallet v" << PROJECT_VERSION_LONG;
if(command_line::has_arg(vm, arg_log_level)) if(command_line::has_arg(vm, arg_log_level))
{ {
@ -564,6 +830,77 @@ int main(int argc, char* argv[])
log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level));
} }
if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port))
{
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2);
//runs wallet with rpc interface
if(!command_line::has_arg(vm, arg_wallet_file) )
{
LOG_ERROR("Wallet file not set.");
return 1;
}
if(!command_line::has_arg(vm, arg_daemon_address) )
{
LOG_ERROR("Daemon address not set.");
return 1;
}
if(!command_line::has_arg(vm, arg_password) )
{
LOG_ERROR("Wallet password not set.");
return 1;
}
std::string wallet_file = command_line::get_arg(vm, arg_wallet_file);
std::string wallet_password = command_line::get_arg(vm, arg_password);
std::string daemon_address = command_line::get_arg(vm, arg_daemon_address);
std::string daemon_host = command_line::get_arg(vm, arg_daemon_host);
int daemon_port = command_line::get_arg(vm, arg_daemon_port);
if (daemon_host.empty())
daemon_host = "localhost";
if (!daemon_port)
daemon_port = RPC_DEFAULT_PORT;
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
tools::wallet2 wal;
try
{
LOG_PRINT_L0("Loading wallet...");
wal.load(wallet_file, wallet_password);
wal.init(daemon_address);
wal.refresh();
LOG_PRINT_GREEN("Loaded ok", LOG_LEVEL_0);
}
catch (const std::exception& e)
{
LOG_ERROR("Wallet initialize failed: " << e.what());
return 1;
}
tools::wallet_rpc_server wrpc(wal);
bool r = wrpc.init(vm);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server");
tools::signal_handler::install([&wrpc, &wal] {
wrpc.send_stop_signal();
wal.store();
});
LOG_PRINT_L0("Starting wallet rpc server");
wrpc.run();
LOG_PRINT_L0("Stopped wallet rpc server");
try
{
LOG_PRINT_L0("Storing wallet...");
wal.store();
LOG_PRINT_GREEN("Stored ok", LOG_LEVEL_0);
}
catch (const std::exception& e)
{
LOG_ERROR("Failed to store wallet: " << e.what());
return 1;
}
}else
{
//runs wallet with console interface
r = w.init(vm); r = w.init(vm);
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet"); CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
@ -577,7 +914,7 @@ int main(int argc, char* argv[])
w.run(); w.run();
w.deinit(); w.deinit();
}
return 1; return 1;
//CATCH_ENTRY_L0("main", 1); //CATCH_ENTRY_L0("main", 1);
} }

View File

@ -20,7 +20,7 @@ namespace cryptonote
/************************************************************************/ /************************************************************************/
/* */ /* */
/************************************************************************/ /************************************************************************/
class simple_wallet class simple_wallet : public tools::i_wallet2_callback
{ {
public: public:
typedef std::vector<std::string> command_type; typedef std::vector<std::string> command_type;
@ -35,7 +35,7 @@ namespace cryptonote
bool process_command(const std::vector<std::string> &args); bool process_command(const std::vector<std::string> &args);
std::string get_commands_str(); std::string get_commands_str();
private: private:
bool handle_command_line(const boost::program_options::variables_map& vm); void handle_command_line(const boost::program_options::variables_map& vm);
bool run_console_handler(); bool run_console_handler();
@ -51,13 +51,72 @@ namespace cryptonote
bool show_incoming_transfers(const std::vector<std::string> &args); bool show_incoming_transfers(const std::vector<std::string> &args);
bool show_blockchain_height(const std::vector<std::string> &args); bool show_blockchain_height(const std::vector<std::string> &args);
bool transfer(const std::vector<std::string> &args); bool transfer(const std::vector<std::string> &args);
bool print_address(const std::vector<std::string> &args); bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool save(const std::vector<std::string> &args); bool save(const std::vector<std::string> &args);
bool set_log(const std::vector<std::string> &args); bool set_log(const std::vector<std::string> &args);
uint64_t get_daemon_blockchain_height(std::string& err); uint64_t get_daemon_blockchain_height(std::string& err);
bool try_connect_to_daemon(); bool try_connect_to_daemon();
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index);
virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx);
//----------------------------------------------------------
friend class refresh_progress_reporter_t;
class refresh_progress_reporter_t
{
public:
refresh_progress_reporter_t(cryptonote::simple_wallet& simple_wallet)
: m_simple_wallet(simple_wallet)
, m_blockchain_height(0)
, m_blockchain_height_update_time()
, m_print_time()
{
}
void update(uint64_t height, bool force = false)
{
auto current_time = std::chrono::system_clock::now();
if (std::chrono::seconds(DIFFICULTY_TARGET / 2) < current_time - m_blockchain_height_update_time || m_blockchain_height <= height)
{
update_blockchain_height();
m_blockchain_height = (std::max)(m_blockchain_height, height);
}
if (std::chrono::milliseconds(1) < current_time - m_print_time || force)
{
std::cout << "Height " << height << " of " << m_blockchain_height << '\r';
m_print_time = current_time;
}
}
private:
void update_blockchain_height()
{
std::string err;
uint64_t blockchain_height = m_simple_wallet.get_daemon_blockchain_height(err);
if (err.empty())
{
m_blockchain_height = blockchain_height;
m_blockchain_height_update_time = std::chrono::system_clock::now();
}
else
{
LOG_ERROR("Failed to get current blockchain height: " << err);
}
}
private:
cryptonote::simple_wallet& m_simple_wallet;
uint64_t m_blockchain_height;
std::chrono::system_clock::time_point m_blockchain_height_update_time;
std::chrono::system_clock::time_point m_print_time;
};
private:
std::string m_wallet_file; std::string m_wallet_file;
std::string m_generate_new; std::string m_generate_new;
std::string m_import_path; std::string m_import_path;
@ -70,5 +129,6 @@ namespace cryptonote
std::unique_ptr<tools::wallet2> m_wallet; std::unique_ptr<tools::wallet2> m_wallet;
net_utils::http::http_simple_client m_http_client; net_utils::http::http_simple_client m_http_client;
refresh_progress_reporter_t m_refresh_progress_reporter;
}; };
} }

View File

@ -1,4 +1,4 @@
#define BUILD_COMMIT_ID "@VERSION@" #define BUILD_COMMIT_ID "@VERSION@"
#define PROJECT_VERSION "0.8.2" #define PROJECT_VERSION "0.8.3"
#define PROJECT_VERSION_BUILD_NO "284" #define PROJECT_VERSION_BUILD_NO "288"
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")" #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"

View File

@ -25,25 +25,25 @@ using namespace cryptonote;
namespace tools namespace tools
{ {
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit)
{ {
m_upper_transaction_size_limit = upper_transaction_size_limit; m_upper_transaction_size_limit = upper_transaction_size_limit;
m_daemon_address = daemon_address; m_daemon_address = daemon_address;
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::process_new_transaction(cryptonote::transaction& tx, uint64_t height, fail_details& fd) void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height)
{ {
process_unconfirmed(tx);
std::vector<size_t> outs; std::vector<size_t> outs;
uint64_t tx_money_got_in_outs = 0; uint64_t tx_money_got_in_outs = 0;
crypto::public_key tx_pub_key = null_pkey; crypto::public_key tx_pub_key = null_pkey;
bool r = parse_and_validate_tx_extra(tx, tx_pub_key); bool r = parse_and_validate_tx_extra(tx, tx_pub_key);
fd.reason = fail_details::error_to_parse_tx_extra; CHECK_AND_THROW_WALLET_EX(!r, error::tx_extra_parse_error, tx);
CHECK_AND_ASSERT_MES(r && tx_pub_key != null_pkey, false, "process_new_transaction failed.");
r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs); r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs);
fd.reason = fail_details::error_invalid_tx; CHECK_AND_THROW_WALLET_EX(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
CHECK_AND_ASSERT_MES(r, false, "call lookup_acc_outs failed");
if(outs.size() && tx_money_got_in_outs) if(!outs.empty() && tx_money_got_in_outs)
{ {
//good news - got money! take care about it //good news - got money! take care about it
//usually we have only one transfer for user in transaction //usually we have only one transfer for user in transaction
@ -51,25 +51,18 @@ bool wallet2::process_new_transaction(cryptonote::transaction& tx, uint64_t heig
cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res);
req.txid = get_transaction_hash(tx); req.txid = get_transaction_hash(tx);
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
if (!r) fd.reason = fail_details::error_not_connected; CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "get_o_indexes.bin");
else if (CORE_RPC_STATUS_BUSY == res.status) fd.reason = fail_details::error_daemon_is_busy; CHECK_AND_THROW_WALLET_EX(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin");
else if (CORE_RPC_STATUS_OK != res.status) fd.reason = fail_details::error_internal_error; CHECK_AND_THROW_WALLET_EX(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status);
else fd.reason = fail_details::error_ok; CHECK_AND_THROW_WALLET_EX(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error,
if (fail_details::error_ok != fd.reason) "transactions outputs size=" + std::to_string(tx.vout.size()) +
{ " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));
// in case of split while lookup_acc_outs, transaction could be lost (especially if it is coinbase tx)
LOG_PRINT_L0("failed to invoke get_o_indexes.bin: " << interpret_rpc_response(r, res.status));
return false;
}
fd.reason = fail_details::error_internal_error;
CHECK_AND_ASSERT_MES(res.o_indexes.size() == tx.vout.size(), false, "internal error: transactions outputs size=" << tx.vout.size()
<< " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" << res.o_indexes.size());
BOOST_FOREACH(size_t o, outs) BOOST_FOREACH(size_t o, outs)
{ {
fd.reason = fail_details::error_invalid_tx; CHECK_AND_THROW_WALLET_EX(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
CHECK_AND_ASSERT_MES(o < tx.vout.size(), false, "wrong out in transaction: internal index=" << o << ", total_outs" << tx.vout.size()); std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
m_transfers.push_back(boost::value_initialized<transfer_details>()); m_transfers.push_back(boost::value_initialized<transfer_details>());
transfer_details& td = m_transfers.back(); transfer_details& td = m_transfers.back();
td.m_block_height = height; td.m_block_height = height;
@ -79,12 +72,13 @@ bool wallet2::process_new_transaction(cryptonote::transaction& tx, uint64_t heig
td.m_spent = false; td.m_spent = false;
cryptonote::keypair in_ephemeral; cryptonote::keypair in_ephemeral;
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.m_key_image); cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.m_key_image);
fd.reason = fail_details::error_internal_error; CHECK_AND_THROW_WALLET_EX(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key,
CHECK_AND_ASSERT_MES(in_ephemeral.pub == boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
false, "internal error: at key_image generating ephemeral public key not matched with output_key");
m_key_images[td.m_key_image] = m_transfers.size()-1; m_key_images[td.m_key_image] = m_transfers.size()-1;
LOG_PRINT_COLOR("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx), LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
LOG_LEVEL_0, epee::log_space::console_color_green); if (0 != m_callback)
m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
} }
} }
// check all outputs for spending (compare key images) // check all outputs for spending (compare key images)
@ -95,36 +89,42 @@ bool wallet2::process_new_transaction(cryptonote::transaction& tx, uint64_t heig
auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image); auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
if(it != m_key_images.end()) if(it != m_key_images.end())
{ {
LOG_PRINT_COLOR("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx), LOG_PRINT_L0("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx));
LOG_LEVEL_0, epee::log_space::console_color_magenta); transfer_details& td = m_transfers[it->second];
m_transfers[it->second].m_spent = true; td.m_spent = true;
if (0 != m_callback)
m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx);
} }
} }
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::process_new_blockchain_entry(cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height, fail_details& fd) void wallet2::process_unconfirmed(const cryptonote::transaction& tx)
{
auto unconf_it = m_unconfirmed_txs.find(get_transaction_hash(tx));
if(unconf_it != m_unconfirmed_txs.end())
m_unconfirmed_txs.erase(unconf_it);
}
//----------------------------------------------------------------------------------------------------
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height)
{ {
//handle transactions from new block //handle transactions from new block
fd.reason = fail_details::error_internal_error; CHECK_AND_THROW_WALLET_EX(height != m_blockchain.size(), error::wallet_internal_error,
CHECK_AND_ASSERT_MES(height == m_blockchain.size(), false, "internal error: current_index=" << height << ", m_blockchain.size()=" << m_blockchain.size()); "current_index=" + std::to_string(height) + ", m_blockchain.size()=" + std::to_string(m_blockchain.size()));
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup //optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
if(b.timestamp + 60*60*24 > m_account.get_createtime()) if(b.timestamp + 60*60*24 > m_account.get_createtime())
{ {
TIME_MEASURE_START(miner_tx_handle_time); TIME_MEASURE_START(miner_tx_handle_time);
bool r = process_new_transaction(b.miner_tx, height, fd); process_new_transaction(b.miner_tx, height);
TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_FINISH(miner_tx_handle_time);
CHECK_AND_NO_ASSERT_MES(r, false, "failed to process transaction");
TIME_MEASURE_START(txs_handle_time); TIME_MEASURE_START(txs_handle_time);
BOOST_FOREACH(auto& txblob, bche.txs) BOOST_FOREACH(auto& txblob, bche.txs)
{ {
cryptonote::transaction tx; cryptonote::transaction tx;
r = parse_and_validate_tx_from_blob(txblob, tx); bool r = parse_and_validate_tx_from_blob(txblob, tx);
fd.reason = fail_details::error_to_parse_tx; CHECK_AND_THROW_WALLET_EX(!r, error::tx_parse_error, txblob);
CHECK_AND_ASSERT_MES(r, false, "failed to parse and validate transaction from blob"); process_new_transaction(tx, height);
r = process_new_transaction(tx, height, fd);
CHECK_AND_ASSERT_MES(r, false, "failed to process transaction");
} }
TIME_MEASURE_FINISH(txs_handle_time); TIME_MEASURE_FINISH(txs_handle_time);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms"); LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
@ -134,16 +134,18 @@ bool wallet2::process_new_blockchain_entry(cryptonote::block& b, cryptonote::blo
} }
m_blockchain.push_back(bl_id); m_blockchain.push_back(bl_id);
++m_local_bc_height; ++m_local_bc_height;
return true;
if (0 != m_callback)
m_callback->on_new_block(height, b);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::get_short_chain_history(std::list<crypto::hash>& ids) void wallet2::get_short_chain_history(std::list<crypto::hash>& ids)
{ {
size_t i = 0; size_t i = 0;
size_t current_multiplier = 1; size_t current_multiplier = 1;
size_t sz = m_blockchain.size(); size_t sz = m_blockchain.size();
if(!sz) if(!sz)
return true; return;
size_t current_back_offset = 1; size_t current_back_offset = 1;
bool genesis_included = false; bool genesis_included = false;
while(current_back_offset < sz) while(current_back_offset < sz)
@ -162,77 +164,68 @@ bool wallet2::get_short_chain_history(std::list<crypto::hash>& ids)
} }
if(!genesis_included) if(!genesis_included)
ids.push_back(m_blockchain[0]); ids.push_back(m_blockchain[0]);
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::pull_blocks(size_t& blocks_added, fail_details& fd) void wallet2::pull_blocks(size_t& blocks_added)
{ {
blocks_added = 0; blocks_added = 0;
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
get_short_chain_history(req.block_ids); get_short_chain_history(req.block_ids);
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT);
if (!r) fd.reason = fail_details::error_not_connected; CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "getblocks.bin");
else if (CORE_RPC_STATUS_BUSY == res.status) fd.reason = fail_details::error_daemon_is_busy; CHECK_AND_THROW_WALLET_EX(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
else if (CORE_RPC_STATUS_OK != res.status) fd.reason = fail_details::error_internal_error; CHECK_AND_THROW_WALLET_EX(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status);
else fd.reason = fail_details::error_ok; CHECK_AND_THROW_WALLET_EX(m_blockchain.size() <= res.start_height, error::wallet_internal_error,
if (fail_details::error_ok != fd.reason) "wrong daemon response: m_start_height=" + std::to_string(res.start_height) +
{ " not less than local blockchain size=" + std::to_string(m_blockchain.size()));
LOG_PRINT_L0("failed to get blocks: " << interpret_rpc_response(r, res.status));
return false;
}
//find split position, if split happened
fd.reason = fail_details::error_internal_error;
CHECK_AND_ASSERT_MES(res.start_height < m_blockchain.size(), false, "wrong daemon response: m_start_height="
<< res.start_height << " not less than local blockchain size=" << m_blockchain.size());
size_t current_index = res.start_height; size_t current_index = res.start_height;
BOOST_FOREACH(auto& bl_entry, res.blocks) BOOST_FOREACH(auto& bl_entry, res.blocks)
{ {
cryptonote::block bl; cryptonote::block bl;
r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl);
fd.reason = fail_details::error_to_parse_block; CHECK_AND_THROW_WALLET_EX(!r, error::block_parse_error, bl_entry.block);
CHECK_AND_ASSERT_MES(r, false, "failed to parse/validate block");
crypto::hash bl_id = get_block_hash(bl); crypto::hash bl_id = get_block_hash(bl);
if(current_index >= m_blockchain.size()) if(current_index >= m_blockchain.size())
{ {
r = process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, fd); process_new_blockchain_entry(bl, bl_entry, bl_id, current_index);
if(!r) return false;
++blocks_added; ++blocks_added;
}else }
{ else if(bl_id != m_blockchain[current_index])
if(bl_id != m_blockchain[current_index])
{ {
//split detected here !!! //split detected here !!!
fd.reason = fail_details::error_internal_error; CHECK_AND_THROW_WALLET_EX(current_index == res.start_height, error::wallet_internal_error,
CHECK_AND_ASSERT_MES(current_index != res.start_height, false, "wrong daemon response: first block in response " << string_tools::pod_to_hex(bl_id) "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
<< "\nnot match with local block id " << string_tools::pod_to_hex(m_blockchain[current_index])); " (height " + std::to_string(res.start_height) + "), local block id at this height: " +
string_tools::pod_to_hex(m_blockchain[current_index]));
detach_blockchain(current_index); detach_blockchain(current_index);
r = process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, fd); process_new_blockchain_entry(bl, bl_entry, bl_id, current_index);
if(!r) return false;
} }
else
{
LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
} }
++current_index; ++current_index;
} }
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::refresh(fail_details& fd) void wallet2::refresh()
{ {
size_t blocks_fetched = 0; size_t blocks_fetched = 0;
return refresh(blocks_fetched, fd); refresh(blocks_fetched);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::refresh(size_t & blocks_fetched, fail_details& fd) void wallet2::refresh(size_t & blocks_fetched)
{ {
bool received_money = false; bool received_money = false;
return refresh(blocks_fetched, received_money, fd); refresh(blocks_fetched, received_money);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::refresh(size_t & blocks_fetched, bool& received_money, fail_details& fd) void wallet2::refresh(size_t & blocks_fetched, bool& received_money)
{ {
received_money = false; received_money = false;
blocks_fetched = 0; blocks_fetched = 0;
@ -242,33 +235,49 @@ bool wallet2::refresh(size_t & blocks_fetched, bool& received_money, fail_detail
while(m_run.load(std::memory_order_relaxed)) while(m_run.load(std::memory_order_relaxed))
{ {
bool res = pull_blocks(added_blocks, fd); try
if(!res)
{ {
pull_blocks(added_blocks);
blocks_fetched += added_blocks;
if(!added_blocks)
break;
}
catch (const std::exception&)
{
blocks_fetched += added_blocks;
if(try_count < 3) if(try_count < 3)
{ {
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")..."); LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
++try_count; ++try_count;
continue; }
}else else
{ {
LOG_PRINT_L1("pull_blocks failed, try_count=" << try_count); LOG_ERROR("pull_blocks failed, try_count=" << try_count);
return false; throw;
} }
} }
blocks_fetched+=added_blocks;
if(!added_blocks)
break;
} }
if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash)) if(last_tx_hash_id != (m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash))
received_money = true; received_money = true;
LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance: " << print_money(balance()) << ", unlocked: " << print_money(unlocked_balance())); LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance: " << print_money(balance()) << ", unlocked: " << print_money(unlocked_balance()));
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::detach_blockchain(uint64_t height) bool wallet2::refresh(size_t & blocks_fetched, bool& received_money, bool& ok)
{
try
{
refresh(blocks_fetched, received_money);
ok = true;
}
catch (...)
{
ok = false;
}
return ok;
}
//----------------------------------------------------------------------------------------------------
void wallet2::detach_blockchain(uint64_t height)
{ {
LOG_PRINT_L0("Detaching blockchain on height " << height); LOG_PRINT_L0("Detaching blockchain on height " << height);
size_t transfers_detached = 0; size_t transfers_detached = 0;
@ -279,7 +288,7 @@ bool wallet2::detach_blockchain(uint64_t height)
for(size_t i = i_start; i!= m_transfers.size();i++) for(size_t i = i_start; i!= m_transfers.size();i++)
{ {
auto it_ki = m_key_images.find(m_transfers[i].m_key_image); auto it_ki = m_key_images.find(m_transfers[i].m_key_image);
CHECK_AND_ASSERT_MES(it_ki != m_key_images.end(), false, "key image not found"); CHECK_AND_THROW_WALLET_EX(it_ki == m_key_images.end(), error::wallet_internal_error, "key image not found");
m_key_images.erase(it_ki); m_key_images.erase(it_ki);
++transfers_detached; ++transfers_detached;
} }
@ -290,8 +299,6 @@ bool wallet2::detach_blockchain(uint64_t height)
m_local_bc_height -= blocks_detached; m_local_bc_height -= blocks_detached;
LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached);
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::deinit() bool wallet2::deinit()
@ -343,13 +350,14 @@ namespace
} }
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::load_keys(const std::string& keys_file_name, const std::string& password) void wallet2::load_keys(const std::string& keys_file_name, const std::string& password)
{ {
wallet2::keys_file_data keys_file_data; wallet2::keys_file_data keys_file_data;
std::string buf; std::string buf;
bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf);
r &= ::serialization::parse_binary(buf, keys_file_data); CHECK_AND_THROW_WALLET_EX(!r, error::file_read_error, keys_file_name);
CHECK_AND_ASSERT_MES(r, false, "failed to load wallet keys file: " << keys_file_name); r = ::serialization::parse_binary(buf, keys_file_data);
CHECK_AND_THROW_WALLET_EX(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"');
crypto::chacha8_key key; crypto::chacha8_key key;
crypto::generate_chacha8_key(password, key); crypto::generate_chacha8_key(password, key);
@ -361,34 +369,28 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
r = epee::serialization::load_t_from_binary(m_account, account_data); r = epee::serialization::load_t_from_binary(m_account, account_data);
r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key);
r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
if (!r) CHECK_AND_THROW_WALLET_EX(!r, error::invalid_password);
{
LOG_ERROR("invalid password");
return false;
}
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::generate(const std::string& wallet_, const std::string& password) void wallet2::generate(const std::string& wallet_, const std::string& password)
{ {
clear(); clear();
prepare_file_names(wallet_); prepare_file_names(wallet_);
boost::system::error_code e;
if(boost::filesystem::exists(m_wallet_file, e) || boost::filesystem::exists(m_keys_file, e)) boost::system::error_code ignored_ec;
{ CHECK_AND_THROW_WALLET_EX(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
LOG_PRINT_RED_L0("failed to generate wallet, file already exist or wrong path: " << wallet_); CHECK_AND_THROW_WALLET_EX(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
return false;
}
m_account.generate(); m_account.generate();
m_account_public_address = m_account.get_keys().m_account_address; m_account_public_address = m_account.get_keys().m_account_address;
bool r = store_keys(m_keys_file, password); bool r = store_keys(m_keys_file, password);
CHECK_AND_ASSERT_MES(r, false, "Failed to store wallet key files!"); CHECK_AND_THROW_WALLET_EX(!r, error::file_save_error, m_keys_file);
r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str()); r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str());
if(!r) LOG_PRINT_RED_L0("String with address text not saved"); if(!r) LOG_PRINT_RED_L0("String with address text not saved");
return store();
store();
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path) bool wallet2::prepare_file_names(const std::string& file_path)
@ -418,51 +420,46 @@ bool wallet2::check_connection()
return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::load(const std::string& wallet_, const std::string& password) void wallet2::load(const std::string& wallet_, const std::string& password)
{ {
clear(); clear();
prepare_file_names(wallet_); prepare_file_names(wallet_);
boost::system::error_code e; boost::system::error_code e;
if(!boost::filesystem::exists(m_keys_file, e) || e) bool exists = boost::filesystem::exists(m_keys_file, e);
{ CHECK_AND_THROW_WALLET_EX(e || !exists, error::file_not_found, m_keys_file);
LOG_PRINT_L0("file not found: " << m_keys_file);
return false;
}
bool r = load_keys(m_keys_file, password);
if (!r)
return false;
load_keys(m_keys_file, password);
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str()); LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str());
//keys loaded ok! //keys loaded ok!
//try to load wallet file. but even if we failed, it is not big problem //try to load wallet file. but even if we failed, it is not big problem
if(!boost::filesystem::exists(m_wallet_file, e) || e) if(!boost::filesystem::exists(m_wallet_file, e) || e)
{ {
LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain"); LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
m_account_public_address = m_account.get_keys().m_account_address; m_account_public_address = m_account.get_keys().m_account_address;
return true; return;
} }
r = tools::unserialize_obj_from_file(*this, m_wallet_file); bool r = tools::unserialize_obj_from_file(*this, m_wallet_file);
CHECK_AND_ASSERT_MES(r, false, "failed to load wallet from file " << m_wallet_file); CHECK_AND_THROW_WALLET_EX(!r, error::file_read_error, m_wallet_file);
CHECK_AND_ASSERT_MES(m_account_public_address.m_spend_public_key == m_account.get_keys().m_account_address.m_spend_public_key && CHECK_AND_THROW_WALLET_EX(
m_account_public_address.m_view_public_key == m_account.get_keys().m_account_address.m_view_public_key, m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key ||
false, "addresses of wallet keys file and wallet data file are mismatched"); m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key,
if(!m_blockchain.size()) error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
if(m_blockchain.empty())
{ {
cryptonote::block b; cryptonote::block b;
cryptonote::generate_genesis_block(b); cryptonote::generate_genesis_block(b);
m_blockchain.push_back(get_block_hash(b)); m_blockchain.push_back(get_block_hash(b));
} }
m_local_bc_height = m_blockchain.size(); m_local_bc_height = m_blockchain.size();
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::store() void wallet2::store()
{ {
bool r = tools::serialize_obj_to_file(*this, m_wallet_file); bool r = tools::serialize_obj_to_file(*this, m_wallet_file);
CHECK_AND_ASSERT_MES(r, false, "failed to save wallet to file " << m_wallet_file); CHECK_AND_THROW_WALLET_EX(!r, error::file_save_error, m_wallet_file);
return r;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::unlocked_balance() uint64_t wallet2::unlocked_balance()
@ -482,13 +479,16 @@ uint64_t wallet2::balance()
if(!td.m_spent) if(!td.m_spent)
amount += td.amount(); amount += td.amount();
BOOST_FOREACH(auto& utx, m_unconfirmed_txs)
amount+= utx.second.m_change;
return amount; return amount;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) void wallet2::get_transfers(wallet2::transfer_container& incoming_transfers) const
{ {
incoming_transfers = m_transfers; incoming_transfers = m_transfers;
return true;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td) const bool wallet2::is_transfer_unlocked(const transfer_details& td) const
@ -581,22 +581,25 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_
return found_money; return found_money;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount)
{
unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
utd.m_change = change_amount;
utd.m_sent_time = time(NULL);
utd.m_tx = tx;
}
//----------------------------------------------------------------------------------------------------
void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx) uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx)
{ {
return transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx); transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx, fail_details& tfd) void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
{
return transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx, tfd);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee) uint64_t unlock_time, uint64_t fee)
{ {
cryptonote::transaction tx; cryptonote::transaction tx;
return transfer(dsts, fake_outputs_count, unlock_time, fee, tx); transfer(dsts, fake_outputs_count, unlock_time, fee, tx);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
} }

View File

@ -8,6 +8,7 @@
#include <boost/serialization/list.hpp> #include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp> #include <boost/serialization/vector.hpp>
#include <atomic> #include <atomic>
#include "include_base_utils.h" #include "include_base_utils.h"
#include "cryptonote_core/account.h" #include "cryptonote_core/account.h"
#include "cryptonote_core/account_boost_serialization.h" #include "cryptonote_core/account_boost_serialization.h"
@ -20,50 +21,20 @@
#include "crypto/chacha8.h" #include "crypto/chacha8.h"
#include "crypto/hash.h" #include "crypto/hash.h"
#include "wallet_errors.h"
#define DEFAULT_TX_SPENDABLE_AGE 10 #define DEFAULT_TX_SPENDABLE_AGE 10
#define WALLET_RCP_CONNECTION_TIMEOUT 200000 #define WALLET_RCP_CONNECTION_TIMEOUT 200000
namespace tools namespace tools
{ {
inline std::string interpret_rpc_response(bool ok, const std::string& status) class i_wallet2_callback
{ {
std::string err;
if (ok)
{
if (status == CORE_RPC_STATUS_BUSY)
{
err = "daemon is busy. Please try later";
}
else if (status != CORE_RPC_STATUS_OK)
{
err = status;
}
}
else
{
err = "possible lost connection to daemon";
}
return err;
}
class wallet2
{
wallet2(const wallet2&) : m_run(true) {};
public: public:
wallet2() : m_run(true) {}; virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
struct transfer_details virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) {}
{ virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) {}
uint64_t m_block_height;
cryptonote::transaction m_tx;
size_t m_internal_output_index;
uint64_t m_global_output_index;
bool m_spent;
crypto::key_image m_key_image; //TODO: key_image stored twice :(
uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; }
}; };
typedef std::vector<transfer_details> transfer_container;
struct tx_dust_policy struct tx_dust_policy
{ {
@ -79,6 +50,32 @@ namespace tools
} }
}; };
class wallet2
{
wallet2(const wallet2&) : m_run(true), m_callback(0) {};
public:
wallet2() : m_run(true), m_callback(0) {};
struct transfer_details
{
uint64_t m_block_height;
cryptonote::transaction m_tx;
size_t m_internal_output_index;
uint64_t m_global_output_index;
bool m_spent;
crypto::key_image m_key_image; //TODO: key_image stored twice :(
uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; }
};
struct unconfirmed_transfer_details
{
cryptonote::transaction m_tx;
uint64_t m_change;
time_t m_sent_time;
};
typedef std::vector<transfer_details> transfer_container;
struct keys_file_data struct keys_file_data
{ {
crypto::chacha8_iv iv; crypto::chacha8_iv iv;
@ -90,77 +87,34 @@ namespace tools
END_SERIALIZE() END_SERIALIZE()
}; };
struct fail_details void generate(const std::string& wallet, const std::string& password);
{ void load(const std::string& wallet, const std::string& password);
enum fail_reason void store();
{
error_ok = 0,
error_not_connected,
error_daemon_is_busy,
error_rejected_by_daemon,
error_too_big_transaction,
error_not_enough_money,
error_too_big_mixin,
error_to_parse_block,
error_to_parse_tx,
error_to_parse_tx_extra,
error_invalid_tx,
error_internal_error
};
fail_reason reason;
uint64_t tx_blob_size;
uint64_t max_expected_tx_blob_size;
std::string what() const
{
switch (reason)
{
case error_ok: return "OK";
case error_not_connected: return "not connected";
case error_daemon_is_busy: return "daemon is busy. Please try later";
case error_rejected_by_daemon: return "rejected by daemon";
case error_too_big_transaction: return "transaction size is too big";
case error_not_enough_money: return "not enough money";
case error_too_big_mixin: return "not enough outputs for specified mixin_count";
case error_to_parse_block: return "failed to parse/validate block";
case error_to_parse_tx: return "failed to parse/validate tx";
case error_to_parse_tx_extra: return "failed to parse/validate tx extra";
case error_invalid_tx: return "wrong tx";
case error_internal_error: return "internal error";
default: return "unknown error";
}
}
};
bool generate(const std::string& wallet, const std::string& password);
bool load(const std::string& wallet, const std::string& password);
bool store();
cryptonote::account_base& get_account(){return m_account;} cryptonote::account_base& get_account(){return m_account;}
bool init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE*2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
bool refresh(fail_details& fd);
bool refresh(size_t & blocks_fetched, fail_details& fd);
bool refresh(size_t & blocks_fetched, bool& received_money, fail_details& fd);
bool deinit(); bool deinit();
void stop() { m_run.store(false, std::memory_order_relaxed); } void stop() { m_run.store(false, std::memory_order_relaxed); }
i_wallet2_callback* callback() const { return m_callback; }
void callback(i_wallet2_callback* callback) { m_callback = callback; }
void refresh();
void refresh(size_t & blocks_fetched);
void refresh(size_t & blocks_fetched, bool& received_money);
bool refresh(size_t & blocks_fetched, bool& received_money, bool& ok);
uint64_t balance(); uint64_t balance();
uint64_t unlocked_balance(); uint64_t unlocked_balance();
template<typename T> template<typename T>
bool enum_incoming_transfers(const T& handler) const; void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy);
template<typename T> template<typename T>
bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy); void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx);
template<typename T> void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee);
bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx, fail_details& tfd); void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx);
template<typename T>
bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx);
bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee);
bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx);
bool transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, cryptonote::transaction& tx, fail_details& tfd);
bool check_connection(); bool check_connection();
bool get_transfers(wallet2::transfer_container& incoming_transfers); void get_transfers(wallet2::transfer_container& incoming_transfers) const;
uint64_t get_blockchain_current_height() const { return m_local_bc_height; } uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
template <class t_archive> template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver) inline void serialize(t_archive &a, const unsigned int ver)
@ -171,21 +125,26 @@ namespace tools
a & m_transfers; a & m_transfers;
a & m_account_public_address; a & m_account_public_address;
a & m_key_images; a & m_key_images;
if(ver < 6)
return;
a & m_unconfirmed_txs;
} }
private: private:
bool store_keys(const std::string& keys_file_name, const std::string& password); bool store_keys(const std::string& keys_file_name, const std::string& password);
bool load_keys(const std::string& keys_file_name, const std::string& password); void load_keys(const std::string& keys_file_name, const std::string& password);
bool process_new_transaction(cryptonote::transaction& tx, uint64_t height, fail_details& fd); void process_new_transaction(const cryptonote::transaction& tx, uint64_t height);
bool process_new_blockchain_entry(cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height, fail_details& fd); void process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height);
bool detach_blockchain(uint64_t height); void detach_blockchain(uint64_t height);
bool get_short_chain_history(std::list<crypto::hash>& ids); void get_short_chain_history(std::list<crypto::hash>& ids);
bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
bool is_transfer_unlocked(const transfer_details& td) const; bool is_transfer_unlocked(const transfer_details& td) const;
bool clear(); bool clear();
bool pull_blocks(size_t& blocks_added, fail_details& fd); void pull_blocks(size_t& blocks_added);
uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers); uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers);
bool prepare_file_names(const std::string& file_path); bool prepare_file_names(const std::string& file_path);
void process_unconfirmed(const cryptonote::transaction& tx);
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount);
cryptonote::account_base m_account; cryptonote::account_base m_account;
std::string m_daemon_address; std::string m_daemon_address;
@ -194,6 +153,7 @@ namespace tools
epee::net_utils::http::http_simple_client m_http_client; epee::net_utils::http::http_simple_client m_http_client;
std::vector<crypto::hash> m_blockchain; std::vector<crypto::hash> m_blockchain;
std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
transfer_container m_transfers; transfer_container m_transfers;
std::unordered_map<crypto::key_image, size_t> m_key_images; std::unordered_map<crypto::key_image, size_t> m_key_images;
@ -201,9 +161,11 @@ namespace tools
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
std::atomic<bool> m_run; std::atomic<bool> m_run;
i_wallet2_callback* m_callback;
}; };
} }
BOOST_CLASS_VERSION(tools::wallet2, 5) BOOST_CLASS_VERSION(tools::wallet2, 6)
namespace boost namespace boost
{ {
@ -219,6 +181,16 @@ namespace boost
a & x.m_spent; a & x.m_spent;
a & x.m_key_image; a & x.m_key_image;
} }
template <class Archive>
inline void serialize(Archive &a, tools::wallet2::unconfirmed_transfer_details &x, const boost::serialization::version_type ver)
{
a & x.m_change;
a & x.m_sent_time;
a & x.m_tx;
}
} }
} }
@ -288,62 +260,31 @@ namespace tools
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
template<typename T> template<typename T>
bool wallet2::enum_incoming_transfers(const T& handler) const void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
{
if(!m_transfers.empty())
{
BOOST_FOREACH(const transfer_details& td, m_transfers)
{
handler(td.m_tx, td.m_global_output_index, td.amount(), td.m_spent);
}
return true;
}
else
{
return false;
}
}
//----------------------------------------------------------------------------------------------------
template<typename T>
bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy) uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy)
{ {
cryptonote::transaction tx; cryptonote::transaction tx;
return transfer(dsts, fake_outputs_count, unlock_time, fee, destination_split_strategy, dust_policy, tx); transfer(dsts, fake_outputs_count, unlock_time, fee, destination_split_strategy, dust_policy, tx);
} }
template<typename T> template<typename T>
bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx) uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx)
{
fail_details stub = AUTO_VAL_INIT(stub);
return transfer(dsts, fake_outputs_count, unlock_time, fee, destination_split_strategy, dust_policy, tx, stub);
}
template<typename T>
bool wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction &tx, fail_details& tfd)
{ {
using namespace cryptonote; using namespace cryptonote;
uint64_t needed_money = fee; uint64_t needed_money = fee;
BOOST_FOREACH(auto& dt, dsts) BOOST_FOREACH(auto& dt, dsts)
{ {
CHECK_AND_ASSERT_MES(dt.amount > 0, false, "Wrong destination amount value: " << dt.amount); CHECK_AND_THROW_WALLET_EX(0 == dt.amount, error::zero_destination);
needed_money += dt.amount; needed_money += dt.amount;
CHECK_AND_THROW_WALLET_EX(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee);
} }
std::list<transfer_container::iterator> selected_transfers; std::list<transfer_container::iterator> selected_transfers;
uint64_t found_money = select_transfers(needed_money, 0 == fake_outputs_count, dust_policy.dust_threshold, selected_transfers); uint64_t found_money = select_transfers(needed_money, 0 == fake_outputs_count, dust_policy.dust_threshold, selected_transfers);
CHECK_AND_THROW_WALLET_EX(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
if(found_money < needed_money)
{
LOG_ERROR("not enough money, available only " << print_money(found_money) << ", transaction amount " <<
print_money(needed_money) << " = " << print_money(needed_money - fee) << " + " << print_money(fee) << " (fee)");
tfd.reason = fail_details::error_not_enough_money;
return false;
}
//typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount;
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
typedef cryptonote::tx_source_entry::output_entry tx_output_entry; typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
@ -354,41 +295,30 @@ namespace tools
req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key
BOOST_FOREACH(transfer_container::iterator it, selected_transfers) BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
{ {
CHECK_AND_ASSERT_MES(it->m_tx.vout.size() > it->m_internal_output_index, false, "internal error: m_internal_output_index = " CHECK_AND_THROW_WALLET_EX(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
<< it->m_internal_output_index << " more than " << it->m_tx.vout.size()); "m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
" is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
req.amounts.push_back(it->amount()); req.amounts.push_back(it->amount());
} }
bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000);
if (!r) tfd.reason = fail_details::error_not_connected; CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
else if (CORE_RPC_STATUS_BUSY == daemon_resp.status) tfd.reason = fail_details::error_daemon_is_busy; CHECK_AND_THROW_WALLET_EX(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
else if (CORE_RPC_STATUS_OK != daemon_resp.status) tfd.reason = fail_details::error_internal_error; CHECK_AND_THROW_WALLET_EX(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
else tfd.reason = fail_details::error_ok; CHECK_AND_THROW_WALLET_EX(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
if (fail_details::error_ok != tfd.reason) "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
{ std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));
LOG_PRINT_L0("failed to invoke getrandom_outs.bin: " << interpret_rpc_response(r, daemon_resp.status));
return false;
}
tfd.reason = fail_details::error_internal_error; std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_outs;
CHECK_AND_ASSERT_MES(daemon_resp.outs.size() == selected_transfers.size(), false,
"internal error: daemon returned wrong response for getrandom_outs.bin, wrong amounts count = "
<< daemon_resp.outs.size() << ", expected " << selected_transfers.size());
tfd.reason = fail_details::error_ok;
BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs) BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs)
{ {
if (amount_outs.outs.size() != fake_outputs_count) if (amount_outs.outs.size() < fake_outputs_count)
{ {
tfd.reason = fail_details::error_too_big_mixin; scanty_outs.push_back(amount_outs);
LOG_PRINT_L0("not enough outputs to mix output " << print_money(amount_outs.amount) << ", requested " <<
fake_outputs_count << ", found " << amount_outs.outs.size());
} }
} }
if (fail_details::error_ok != tfd.reason) CHECK_AND_THROW_WALLET_EX(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
return false;
} }
tfd.reason = fail_details::error_ok;
//prepare inputs //prepare inputs
size_t i = 0; size_t i = 0;
@ -443,60 +373,36 @@ namespace tools
uint64_t dust = 0; uint64_t dust = 0;
std::vector<cryptonote::tx_destination_entry> splitted_dsts; std::vector<cryptonote::tx_destination_entry> splitted_dsts;
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust); destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust);
CHECK_AND_ASSERT_MES(dust <= dust_policy.dust_threshold, false, "internal error: invalid dust value"); CHECK_AND_THROW_WALLET_EX(dust_policy.dust_threshold < dust, error::wallet_internal_error, "invalid dust value: dust = " +
std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
if (0 != dust && !dust_policy.add_to_fee) if (0 != dust && !dust_policy.add_to_fee)
{ {
splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust)); splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust));
} }
tfd.reason = fail_details::error_internal_error;
bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, tx, unlock_time); bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, tx, unlock_time);
CHECK_AND_ASSERT_MES(r, false, "Transaction construction failed"); CHECK_AND_THROW_WALLET_EX(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time);
CHECK_AND_THROW_WALLET_EX(m_upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_limit);
//check transaction size
if(get_object_blobsize(tx) >= m_upper_transaction_size_limit)
{
LOG_PRINT_L0("Transaction size is too big: " << get_object_blobsize(tx) << ", expected size < " << m_upper_transaction_size_limit);
tfd.reason = fail_details::error_too_big_transaction;
tfd.tx_blob_size = get_object_blobsize(tx);
tfd.max_expected_tx_blob_size = m_upper_transaction_size_limit;
return false;
}
COMMAND_RPC_SEND_RAW_TX::request req;
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx));
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000);
if (!r)
{
tfd.reason = fail_details::error_not_connected;
LOG_PRINT_L0("failed to send transaction: " << interpret_rpc_response(r, daemon_send_resp.status));
return false;
}
else if (CORE_RPC_STATUS_BUSY == daemon_send_resp.status)
{
tfd.reason = fail_details::error_daemon_is_busy;
LOG_PRINT_L0("failed to send transaction: " << interpret_rpc_response(r, daemon_send_resp.status));
return false;
}
else if (CORE_RPC_STATUS_OK != daemon_send_resp.status)
{
tfd.reason = fail_details::error_rejected_by_daemon;
LOG_ERROR("daemon failed to accept generated transaction, id: " << get_transaction_hash(tx));
return false;
}
else
{
tfd.reason = fail_details::error_ok;
}
std::string key_images; std::string key_images;
std::for_each(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
{ {
CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false); CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
key_images += boost::to_string(in.k_image) + " "; key_images += boost::to_string(in.k_image) + " ";
return true; return true;
}); });
CHECK_AND_THROW_WALLET_EX(!all_are_txin_to_key, error::unexpected_txin_type, tx);
COMMAND_RPC_SEND_RAW_TX::request req;
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(tx));
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000);
CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "sendrawtransaction");
CHECK_AND_THROW_WALLET_EX(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
CHECK_AND_THROW_WALLET_EX(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, tx, daemon_send_resp.status);
add_unconfirmed_tx(tx, change_dts.amount);
LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]"); LOG_PRINT_L2("transaction " << get_transaction_hash(tx) << " generated ok and sent to daemon, key_images: [" << key_images << "]");
BOOST_FOREACH(transfer_container::iterator it, selected_transfers) BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
@ -507,8 +413,5 @@ namespace tools
<< "Balance: " << print_money(balance()) << ENDL << "Balance: " << print_money(balance()) << ENDL
<< "Unlocked: " << print_money(unlocked_balance()) << ENDL << "Unlocked: " << print_money(unlocked_balance()) << ENDL
<< "Please, wait for confirmation for your balance to be unlocked."); << "Please, wait for confirmation for your balance to be unlocked.");
tfd.reason = fail_details::error_ok;
return true;
} }
} }

611
src/wallet/wallet_errors.h Normal file
View File

@ -0,0 +1,611 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <stdexcept>
#include <string>
#include <vector>
#include "cryptonote_core/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "include_base_utils.h"
namespace tools
{
namespace error
{
// std::exception
// std::runtime_error
// wallet_runtime_error *
// wallet_internal_error
// unexpected_txin_type
// std::logic_error
// wallet_logic_error *
// file_exists
// file_not_found
// file_read_error
// file_save_error
// invalid_password
// refresh_error *
// acc_outs_lookup_error
// block_parse_error
// get_blocks_error
// get_out_indexes_error
// tx_extra_parse_error
// tx_parse_error
// transfer_error *
// get_random_outs_general_error
// not_enough_money
// not_enough_outs_to_mix
// tx_not_constructed
// tx_rejected
// tx_sum_overflow
// tx_too_big
// zero_destination
// wallet_rpc_error *
// daemon_busy
// no_connection_to_daemon
// wallet_files_doesnt_correspond
//
// * - class with protected ctor
//----------------------------------------------------------------------------------------------------
template<typename Base>
struct wallet_error_base : public Base
{
const std::string& location() const { return m_loc; }
std::string to_string() const
{
std::ostringstream ss;
ss << m_loc << ':' << typeid(*this).name() << ": " << Base::what();
return ss.str();
}
protected:
wallet_error_base(std::string&& loc, const std::string& message)
: Base(message)
, m_loc(loc)
{
}
private:
std::string m_loc;
};
//----------------------------------------------------------------------------------------------------
const char* const failed_rpc_request_messages[] = {
"failed to get blocks",
"failed to get out indices",
"failed to get random outs"
};
enum failed_rpc_request_message_indices
{
get_blocks_error_message_index,
get_out_indices_error_message_index,
get_random_outs_error_message_index
};
template<typename Base, int msg_index>
struct failed_rpc_request : public Base
{
explicit failed_rpc_request(std::string&& loc, const std::string& status)
: Base(std::move(loc), failed_rpc_request_messages[msg_index])
, m_status(status)
{
}
const std::string& status() const { return m_status; }
std::string to_string() const
{
std::ostringstream ss;
ss << Base::to_string() << ", status = " << status();
return ss.str();
}
private:
std::string m_status;
};
//----------------------------------------------------------------------------------------------------
typedef wallet_error_base<std::logic_error> wallet_logic_error;
typedef wallet_error_base<std::runtime_error> wallet_runtime_error;
//----------------------------------------------------------------------------------------------------
struct wallet_internal_error : public wallet_runtime_error
{
explicit wallet_internal_error(std::string&& loc, const std::string& message)
: wallet_runtime_error(std::move(loc), message)
{
}
};
//----------------------------------------------------------------------------------------------------
struct unexpected_txin_type : public wallet_internal_error
{
explicit unexpected_txin_type(std::string&& loc, const cryptonote::transaction& tx)
: wallet_internal_error(std::move(loc), "one of tx inputs has unexpected type")
, m_tx(tx)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
std::string to_string() const
{
std::ostringstream ss;
cryptonote::transaction tx = m_tx;
ss << wallet_internal_error::to_string() << ", tx:\n" << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
cryptonote::transaction m_tx;
};
//----------------------------------------------------------------------------------------------------
const char* const file_error_messages[] = {
"file already exists",
"file not found",
"failed to read file",
"failed to save file"
};
enum file_error_message_indices
{
file_exists_message_index,
file_not_found_message_index,
file_read_error_message_index,
file_save_error_message_index
};
template<int msg_index>
struct file_error_base : public wallet_logic_error
{
explicit file_error_base(std::string&& loc, const std::string& file)
: wallet_logic_error(std::move(loc), std::string(file_error_messages[msg_index]) + " \"" + file + '\"')
, m_file(file)
{
}
const std::string& file() const { return m_file; }
std::string to_string() const { return wallet_logic_error::to_string(); }
private:
std::string m_file;
};
//----------------------------------------------------------------------------------------------------
typedef file_error_base<file_exists_message_index> file_exists;
typedef file_error_base<file_not_found_message_index> file_not_found;
typedef file_error_base<file_not_found_message_index> file_not_found;
typedef file_error_base<file_read_error_message_index> file_read_error;
typedef file_error_base<file_save_error_message_index> file_save_error;
//----------------------------------------------------------------------------------------------------
struct invalid_password : public wallet_logic_error
{
explicit invalid_password(std::string&& loc)
: wallet_logic_error(std::move(loc), "invalid password")
{
}
std::string to_string() const { return wallet_logic_error::to_string(); }
};
//----------------------------------------------------------------------------------------------------
struct refresh_error : public wallet_logic_error
{
protected:
refresh_error(std::string&& loc, const std::string& message)
: wallet_logic_error(std::move(loc), message)
{
}
};
//----------------------------------------------------------------------------------------------------
struct acc_outs_lookup_error : public refresh_error
{
explicit acc_outs_lookup_error(std::string&& loc, const cryptonote::transaction& tx,
const crypto::public_key& tx_pub_key, const cryptonote::account_keys& acc_keys)
: refresh_error(std::move(loc), "account outs lookup error")
, m_tx(tx)
, m_tx_pub_key(tx_pub_key)
, m_acc_keys(acc_keys)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
const crypto::public_key& tx_pub_key() const { return m_tx_pub_key; }
const cryptonote::account_keys& acc_keys() const { return m_acc_keys; }
std::string to_string() const
{
std::ostringstream ss;
cryptonote::transaction tx = m_tx;
ss << refresh_error::to_string() << ", tx: " << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
const cryptonote::transaction m_tx;
const crypto::public_key m_tx_pub_key;
const cryptonote::account_keys m_acc_keys;
};
//----------------------------------------------------------------------------------------------------
struct block_parse_error : public refresh_error
{
explicit block_parse_error(std::string&& loc, const cryptonote::blobdata& block_data)
: refresh_error(std::move(loc), "block parse error")
, m_block_blob(block_data)
{
}
const cryptonote::blobdata& block_blob() const { return m_block_blob; }
std::string to_string() const { return refresh_error::to_string(); }
private:
cryptonote::blobdata m_block_blob;
};
//----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<refresh_error, get_blocks_error_message_index> get_blocks_error;
//----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<refresh_error, get_out_indices_error_message_index> get_out_indices_error;
//----------------------------------------------------------------------------------------------------
struct tx_extra_parse_error : public refresh_error
{
explicit tx_extra_parse_error(std::string&& loc, const cryptonote::transaction& tx)
: refresh_error(std::move(loc), "transaction extra parse error")
, m_tx(tx)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
std::string to_string() const
{
std::ostringstream ss;
cryptonote::transaction tx = m_tx;
ss << refresh_error::to_string() << ", tx: " << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
const cryptonote::transaction m_tx;
};
//----------------------------------------------------------------------------------------------------
struct tx_parse_error : public refresh_error
{
explicit tx_parse_error(std::string&& loc, const cryptonote::blobdata& tx_blob)
: refresh_error(std::move(loc), "transaction parse error")
, m_tx_blob(tx_blob)
{
}
const cryptonote::blobdata& tx_blob() const { return m_tx_blob; }
std::string to_string() const { return refresh_error::to_string(); }
private:
cryptonote::blobdata m_tx_blob;
};
//----------------------------------------------------------------------------------------------------
struct transfer_error : public wallet_logic_error
{
protected:
transfer_error(std::string&& loc, const std::string& message)
: wallet_logic_error(std::move(loc), message)
{
}
};
//----------------------------------------------------------------------------------------------------
typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error;
//----------------------------------------------------------------------------------------------------
struct not_enough_money : public transfer_error
{
not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee)
: transfer_error(std::move(loc), "not enough money")
, m_available(availbable)
, m_tx_amount(tx_amount)
, m_fee(fee)
{
}
uint64_t available() const { return m_available; }
uint64_t tx_amount() const { return m_tx_amount; }
uint64_t fee() const { return m_fee; }
std::string to_string() const
{
std::ostringstream ss;
ss << transfer_error::to_string() <<
", available = " << cryptonote::print_money(m_available) <<
", tx_amount = " << cryptonote::print_money(m_tx_amount) <<
", fee = " << cryptonote::print_money(m_fee);
return ss.str();
}
private:
uint64_t m_available;
uint64_t m_tx_amount;
uint64_t m_fee;
};
//----------------------------------------------------------------------------------------------------
struct not_enough_outs_to_mix : public transfer_error
{
typedef std::vector<cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_outs_t;
explicit not_enough_outs_to_mix(std::string&& loc, const scanty_outs_t& scanty_outs, size_t mixin_count)
: transfer_error(std::move(loc), "not enough outputs to mix")
, m_scanty_outs(scanty_outs)
, m_mixin_count(mixin_count)
{
}
const scanty_outs_t& scanty_outs() const { return m_scanty_outs; }
size_t mixin_count() const { return m_mixin_count; }
std::string to_string() const
{
std::ostringstream ss;
ss << transfer_error::to_string() << ", mixin_count = " << m_mixin_count << ", scanty_outs:";
for (const auto& outs_for_amount : m_scanty_outs)
{
ss << '\n' << cryptonote::print_money(outs_for_amount.amount) << " - " << outs_for_amount.outs.size();
}
return ss.str();
}
private:
scanty_outs_t m_scanty_outs;
size_t m_mixin_count;
};
//----------------------------------------------------------------------------------------------------
struct tx_not_constructed : public transfer_error
{
typedef std::vector<cryptonote::tx_source_entry> sources_t;
typedef std::vector<cryptonote::tx_destination_entry> destinations_t;
explicit tx_not_constructed(std::string&& loc, const sources_t& sources, const destinations_t& destinations, uint64_t unlock_time)
: transfer_error(std::move(loc), "transaction was not constructed")
, m_sources(sources)
, m_destinations(destinations)
, m_unlock_time(unlock_time)
{
}
const sources_t& sources() const { return m_sources; }
const destinations_t& destinations() const { return m_destinations; }
uint64_t unlock_time() const { return m_unlock_time; }
std::string to_string() const
{
std::ostringstream ss;
ss << transfer_error::to_string();
ss << "\nSources:";
for (size_t i = 0; i < m_sources.size(); ++i)
{
const cryptonote::tx_source_entry& src = m_sources[i];
ss << "\n source " << i << ":";
ss << "\n amount: " << cryptonote::print_money(src.amount);
// It's not good, if logs will contain such much data
//ss << "\n real_output: " << src.real_output;
//ss << "\n real_output_in_tx_index: " << src.real_output_in_tx_index;
//ss << "\n real_out_tx_key: " << epee::string_tools::pod_to_hex(src.real_out_tx_key);
//ss << "\n outputs:";
//for (size_t j = 0; j < src.outputs.size(); ++j)
//{
// const cryptonote::tx_source_entry::output_entry& out = src.outputs[j];
// ss << "\n " << j << ": " << out.first << ", " << epee::string_tools::pod_to_hex(out.second);
//}
}
ss << "\nDestinations:";
for (size_t i = 0; i < m_destinations.size(); ++i)
{
const cryptonote::tx_destination_entry& dst = m_destinations[i];
ss << "\n " << i << ": " << cryptonote::get_account_address_as_str(dst.addr) << " " <<
cryptonote::print_money(dst.amount);
}
ss << "\nunlock_time: " << m_unlock_time;
return ss.str();
}
private:
sources_t m_sources;
destinations_t m_destinations;
uint64_t m_unlock_time;
};
//----------------------------------------------------------------------------------------------------
struct tx_rejected : public transfer_error
{
explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, const std::string& status)
: transfer_error(std::move(loc), "transaction was rejected by daemon")
, m_tx(tx)
, m_status(status)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
const std::string& status() const { return m_status; }
std::string to_string() const
{
std::ostringstream ss;
ss << transfer_error::to_string() << ", status = " << m_status << ", tx:\n";
cryptonote::transaction tx = m_tx;
ss << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
cryptonote::transaction m_tx;
std::string m_status;
};
//----------------------------------------------------------------------------------------------------
struct tx_sum_overflow : public transfer_error
{
tx_sum_overflow(std::string&& loc, const std::vector<cryptonote::tx_destination_entry>& destinations, uint64_t fee)
: transfer_error(std::move(loc), "transaction sum + fee exceeds " + cryptonote::print_money(std::numeric_limits<uint64_t>::max()))
, m_destinations(destinations)
, m_fee(fee)
{
}
const std::vector<cryptonote::tx_destination_entry>& destinations() const { return m_destinations; }
uint64_t fee() const { return m_fee; }
std::string to_string() const
{
std::ostringstream ss;
ss << transfer_error::to_string() <<
", fee = " << cryptonote::print_money(m_fee) <<
", destinations:";
for (const auto& dst : m_destinations)
{
ss << '\n' << cryptonote::print_money(dst.amount) << " -> " << cryptonote::get_account_address_as_str(dst.addr);
}
return ss.str();
}
private:
std::vector<cryptonote::tx_destination_entry> m_destinations;
uint64_t m_fee;
};
//----------------------------------------------------------------------------------------------------
struct tx_too_big : public transfer_error
{
explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_size_limit)
: transfer_error(std::move(loc), "transaction is too big")
, m_tx(tx)
, m_tx_size_limit(tx_size_limit)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
uint64_t tx_size_limit() const { return m_tx_size_limit; }
std::string to_string() const
{
std::ostringstream ss;
cryptonote::transaction tx = m_tx;
ss << transfer_error::to_string() <<
", tx_size_limit = " << m_tx_size_limit <<
", tx size = " << get_object_blobsize(m_tx) <<
", tx:\n" << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
cryptonote::transaction m_tx;
uint64_t m_tx_size_limit;
};
//----------------------------------------------------------------------------------------------------
struct zero_destination : public transfer_error
{
explicit zero_destination(std::string&& loc)
: transfer_error(std::move(loc), "destination amount is zero")
{
}
};
//----------------------------------------------------------------------------------------------------
struct wallet_rpc_error : public wallet_logic_error
{
const std::string& request() const { return m_request; }
std::string to_string() const
{
std::ostringstream ss;
ss << wallet_logic_error::to_string() << ", request = " << m_request;
return ss.str();
}
protected:
wallet_rpc_error(std::string&& loc, const std::string& message, const std::string& request)
: wallet_logic_error(std::move(loc), message)
, m_request(request)
{
}
private:
std::string m_request;
};
//----------------------------------------------------------------------------------------------------
struct daemon_busy : public wallet_rpc_error
{
explicit daemon_busy(std::string&& loc, const std::string& request)
: wallet_rpc_error(std::move(loc), "daemon is busy", request)
{
}
};
//----------------------------------------------------------------------------------------------------
struct no_connection_to_daemon : public wallet_rpc_error
{
explicit no_connection_to_daemon(std::string&& loc, const std::string& request)
: wallet_rpc_error(std::move(loc), "no connection to daemon", request)
{
}
};
//----------------------------------------------------------------------------------------------------
struct wallet_files_doesnt_correspond : public wallet_logic_error
{
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)
: wallet_logic_error(std::move(loc), "file " + wallet_file + " does not correspond to " + keys_file)
{
}
const std::string& keys_file() const { return m_keys_file; }
const std::string& wallet_file() const { return m_wallet_file; }
std::string to_string() const { return wallet_logic_error::to_string(); }
private:
std::string m_keys_file;
std::string m_wallet_file;
};
//----------------------------------------------------------------------------------------------------
#if !defined(_MSC_VER)
template<typename TException, typename... TArgs>
void throw_wallet_ex(std::string&& loc, const TArgs&... args)
{
TException e(std::move(loc), args...);
LOG_PRINT_L0(e.to_string());
throw e;
}
#else
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/repeat_from_to.hpp>
template<typename TException>
void throw_wallet_ex(std::string&& loc)
{
TException e(std::move(loc));
LOG_PRINT_L0(e.to_string());
throw e;
}
#define GEN_throw_wallet_ex(z, n, data) \
template<typename TException, BOOST_PP_ENUM_PARAMS(n, typename TArg)> \
void throw_wallet_ex(std::string&& loc, BOOST_PP_ENUM_BINARY_PARAMS(n, const TArg, &arg)) \
{ \
TException e(std::move(loc), BOOST_PP_ENUM_PARAMS(n, arg)); \
LOG_PRINT_L0(e.to_string()); \
throw e; \
}
BOOST_PP_REPEAT_FROM_TO(1, 6, GEN_throw_wallet_ex, ~)
#endif
}
}
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
#define CHECK_AND_THROW_WALLET_EX(cond, err_type, ...) \
if (cond) \
{ \
LOG_ERROR(#cond << ". THROW EXCEPTION: " << #err_type); \
tools::error::throw_wallet_ex<err_type>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ## __VA_ARGS__); \
}

View File

@ -0,0 +1,132 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "include_base_utils.h"
using namespace epee;
#include "wallet_rpc_server.h"
#include "common/command_line.h"
#include "cryptonote_core/cryptonote_format_utils.h"
#include "cryptonote_core/account.h"
#include "misc_language.h"
#include "crypto/hash.h"
namespace tools
{
//-----------------------------------------------------------------------------------
const command_line::arg_descriptor<std::string> wallet_rpc_server::arg_rpc_bind_port = {"rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", "", true};
const command_line::arg_descriptor<std::string> wallet_rpc_server::arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"};
void wallet_rpc_server::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_rpc_bind_ip);
command_line::add_arg(desc, arg_rpc_bind_port);
}
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w)
{}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::run()
{
m_net_server.add_idle_handler([this](){
m_wallet.refresh();
return true;
}, 20000);
//DO NOT START THIS SERVER IN MORE THEN 1 THREADS WITHOUT REFACTORING
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::run(1, true);
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm)
{
m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip);
m_port = command_line::get_arg(vm, arg_rpc_bind_port);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::init(const boost::program_options::variables_map& vm)
{
m_net_server.set_threads_prefix("RPC");
bool r = handle_command_line(vm);
CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server");
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(m_port, m_bind_ip);
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
try
{
res.balance = m_wallet.balance();
res.unlocked_balance = m_wallet.unlocked_balance();
}
catch (std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
std::vector<cryptonote::tx_destination_entry> dsts;
for (auto it = req.destinations.begin(); it != req.destinations.end(); it++)
{
cryptonote::tx_destination_entry de;
if(!get_account_address_from_str(de.addr, it->address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
return false;
}
de.amount = it->amount;
dsts.push_back(de);
}
try
{
cryptonote::transaction tx;
m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, tx);
res.tx_hash = boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(tx));
return true;
}
catch (const tools::error::daemon_busy& e)
{
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
er.message = e.what();
return false;
}
catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
er.message = e.what();
return false;
}
catch (...)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
try
{
m_wallet.store();
}
catch (std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
}

View File

@ -0,0 +1,55 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include "net/http_server_impl_base.h"
#include "wallet_rpc_server_commans_defs.h"
#include "wallet2.h"
#include "common/command_line.h"
namespace tools
{
/************************************************************************/
/* */
/************************************************************************/
class wallet_rpc_server: public epee::http_server_impl_base<wallet_rpc_server>
{
public:
typedef epee::net_utils::connection_context_base connection_context;
wallet_rpc_server(wallet2& cr);
const static command_line::arg_descriptor<std::string> arg_rpc_bind_port;
const static command_line::arg_descriptor<std::string> arg_rpc_bind_ip;
static void init_options(boost::program_options::options_description& desc);
bool init(const boost::program_options::variables_map& vm);
bool run();
private:
CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
BEGIN_URI_MAP2()
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
END_JSON_RPC_MAP()
END_URI_MAP2()
//json_rpc
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool handle_command_line(const boost::program_options::variables_map& vm);
wallet2& m_wallet;
std::string m_port;
std::string m_bind_ip;
};
}

View File

@ -0,0 +1,91 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_core/cryptonote_basic.h"
#include "crypto/hash.h"
#include "wallet_rpc_server_error_codes.h"
namespace tools
{
namespace wallet_rpc
{
#define WALLET_RPC_STATUS_OK "OK"
#define WALLET_RPC_STATUS_BUSY "BUSY"
struct COMMAND_RPC_GET_BALANCE
{
struct request
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct response
{
uint64_t balance;
uint64_t unlocked_balance;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(balance)
KV_SERIALIZE(unlocked_balance)
END_KV_SERIALIZE_MAP()
};
};
struct trnsfer_destination
{
uint64_t amount;
std::string address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_TRANSFER
{
struct request
{
std::list<trnsfer_destination> destinations;
uint64_t fee;
uint64_t mixin;
uint64_t unlock_time;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
KV_SERIALIZE(fee)
KV_SERIALIZE(mixin)
KV_SERIALIZE(unlock_time)
END_KV_SERIALIZE_MAP()
};
struct response
{
std::string tx_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_STORE
{
struct request
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
struct response
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
};
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#define WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR -1
#define WALLET_RPC_ERROR_CODE_WRONG_ADDRESS -2
#define WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY -3
#define WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR -4

View File

@ -16,7 +16,7 @@ namespace
const account_public_address& miner_address, std::vector<size_t>& block_sizes, size_t target_tx_size, const account_public_address& miner_address, std::vector<size_t>& block_sizes, size_t target_tx_size,
size_t target_block_size, uint64_t fee = 0) size_t target_block_size, uint64_t fee = 0)
{ {
if (!construct_miner_tx(height, already_generated_coins, miner_address, miner_tx, fee, block_sizes, target_block_size)) if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, fee, miner_address, miner_tx))
return false; return false;
size_t current_size = get_object_blobsize(miner_tx); size_t current_size = get_object_blobsize(miner_tx);

View File

@ -71,8 +71,8 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl
void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins) void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins)
{ {
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx);
bool block_too_big; uint64_t block_reward;
uint64_t block_reward = get_block_reward(block_sizes, block_size, block_too_big, already_generated_coins); get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward);
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size);
} }
@ -108,7 +108,7 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx); size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx);
while (true) while (true)
{ {
if (!construct_miner_tx(height, already_generated_coins, miner_acc.get_keys().m_account_address, blk.miner_tx, total_fee, block_sizes, target_block_size, 10)) if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10))
return false; return false;
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
@ -209,7 +209,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
{ {
size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx); size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx);
// TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE // TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
if (!construct_miner_tx(height, already_generated_coins, miner_acc.get_keys().m_account_address, blk.miner_tx, 0, block_sizes, current_block_size, 1)) if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 1))
return false; return false;
} }
@ -496,14 +496,13 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
tx.vin.push_back(in); tx.vin.push_back(in);
// This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE // This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
std::vector<size_t> block_sizes; uint64_t block_reward;
bool block_too_big = false; if (!get_block_reward(0, 0, already_generated_coins, block_reward))
uint64_t block_reward = get_block_reward(block_sizes, 0, block_too_big, already_generated_coins) + fee;
if (block_too_big)
{ {
LOG_PRINT_L0("Block is too big"); LOG_PRINT_L0("Block is too big");
return false; return false;
} }
block_reward += fee;
crypto::key_derivation derivation; crypto::key_derivation derivation;
crypto::public_key out_eph_public_key; crypto::public_key out_eph_public_key;

View File

@ -7,6 +7,7 @@
#include "cryptonote_core/cryptonote_basic_impl.h" #include "cryptonote_core/cryptonote_basic_impl.h"
#include "cryptonote_core/account.h" #include "cryptonote_core/account.h"
#include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_format_utils.h"
#include "misc_language.h"
using namespace cryptonote; using namespace cryptonote;
@ -35,19 +36,18 @@ bool test_transaction_generation_and_ring_signature()
rv_acc.generate(); rv_acc.generate();
account_base rv_acc2; account_base rv_acc2;
rv_acc2.generate(); rv_acc2.generate();
std::vector<size_t> b;
transaction tx_mine_1; transaction tx_mine_1;
construct_miner_tx(0, 0, miner_acc1.get_keys().m_account_address, tx_mine_1, 0, b, 10); construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_mine_1);
transaction tx_mine_2; transaction tx_mine_2;
construct_miner_tx(0, 0, miner_acc2.get_keys().m_account_address, tx_mine_2, 0, b, 0); construct_miner_tx(0, 0, 0, 0, 0, miner_acc2.get_keys().m_account_address, tx_mine_2);
transaction tx_mine_3; transaction tx_mine_3;
construct_miner_tx(0, 0, miner_acc3.get_keys().m_account_address, tx_mine_3, 0, b, 0); construct_miner_tx(0, 0, 0, 0, 0, miner_acc3.get_keys().m_account_address, tx_mine_3);
transaction tx_mine_4; transaction tx_mine_4;
construct_miner_tx(0, 0, miner_acc4.get_keys().m_account_address, tx_mine_4, 0, b, 0); construct_miner_tx(0, 0, 0, 0, 0, miner_acc4.get_keys().m_account_address, tx_mine_4);
transaction tx_mine_5; transaction tx_mine_5;
construct_miner_tx(0, 0, miner_acc5.get_keys().m_account_address, tx_mine_5, 0, b, 0); construct_miner_tx(0, 0, 0, 0, 0, miner_acc5.get_keys().m_account_address, tx_mine_5);
transaction tx_mine_6; transaction tx_mine_6;
construct_miner_tx(0, 0, miner_acc6.get_keys().m_account_address, tx_mine_6, 0, b, 0); construct_miner_tx(0, 0, 0, 0, 0, miner_acc6.get_keys().m_account_address, tx_mine_6);
//fill inputs entry //fill inputs entry
typedef tx_source_entry::output_entry tx_output_entry; typedef tx_source_entry::output_entry tx_output_entry;
@ -130,7 +130,7 @@ bool test_block_creation()
bool r = get_account_address_from_str(adr, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2"); bool r = get_account_address_from_str(adr, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2");
CHECK_AND_ASSERT_MES(r, false, "failed to import"); CHECK_AND_ASSERT_MES(r, false, "failed to import");
block b; block b;
r = construct_miner_tx(90, 3553616528562147, adr, b.miner_tx, 10000000, szs, 33094, 11); r = construct_miner_tx(90, epee::misc_utils::median(szs), 3553616528562147, 33094, 10000000, adr, b.miner_tx, blobdata(), 11);
return r; return r;
} }

View File

@ -7,6 +7,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <algorithm>
#include "cryptonote_config.h" #include "cryptonote_config.h"
#include "cryptonote_core/difficulty.h" #include "cryptonote_core/difficulty.h"

View File

@ -50,7 +50,15 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor,
dsts.push_back(de); dsts.push_back(de);
} }
return w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, tools::detail::null_split_strategy, tools::wallet2::tx_dust_policy(DEFAULT_FEE), tx); try
{
w1.transfer(dsts, mix_in_factor, 0, DEFAULT_FEE, tools::detail::null_split_strategy, tools::tx_dust_policy(DEFAULT_FEE), tx);
return true;
}
catch (const std::exception&)
{
return false;
}
} }
uint64_t get_money_in_first_transfers(const tools::wallet2::transfer_container& incoming_transfers, size_t n_transfers) uint64_t get_money_in_first_transfers(const tools::wallet2::transfer_container& incoming_transfers, size_t n_transfers)
@ -84,36 +92,30 @@ bool transactions_flow_test(std::string& working_folder,
path_terget_wallet = generate_random_wallet_name(); path_terget_wallet = generate_random_wallet_name();
if(!w1.generate(working_folder + "/" + path_source_wallet, "")) try
{ {
LOG_ERROR("failed to load source wallet from " << path_source_wallet); w1.generate(working_folder + "/" + path_source_wallet, "");
w2.generate(working_folder + "/" + path_terget_wallet, "");
}
catch (const std::exception& e)
{
LOG_ERROR("failed to generate wallet: " << e.what());
return false; return false;
} }
if(!w2.generate(working_folder + "/" + path_terget_wallet, "")) w1.init(daemon_addr_a);
{
LOG_ERROR( "failed to generate target load wallet from " << path_source_wallet );
return false;
}
if(!w1.init(daemon_addr_a))
{
LOG_ERROR("failed to init source wallet from " << daemon_addr_a );
return false;
}
size_t blocks_fetched = 0; size_t blocks_fetched = 0;
tools::wallet2::fail_details fd; bool received_money;
if(!w1.refresh(blocks_fetched, fd)) bool ok;
if(!w1.refresh(blocks_fetched, received_money, ok))
{ {
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a ); LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
return false; return false;
} }
if(!w2.init(daemon_addr_b)) w2.init(daemon_addr_b);
{
LOG_ERROR( "failed to init target wallet from " << daemon_addr_b );
return false;
}
LOG_PRINT_GREEN("Using wallets: " << ENDL LOG_PRINT_GREEN("Using wallets: " << ENDL
<< "Source: " << w1.get_account().get_public_address_str() << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL << "Source: " << w1.get_account().get_public_address_str() << ENDL << "Path: " << working_folder + "/" + path_source_wallet << ENDL
<< "Target: " << w2.get_account().get_public_address_str() << ENDL << "Path: " << working_folder + "/" + path_terget_wallet, LOG_LEVEL_1); << "Target: " << w2.get_account().get_public_address_str() << ENDL << "Path: " << working_folder + "/" + path_terget_wallet, LOG_LEVEL_1);
@ -125,7 +127,6 @@ bool transactions_flow_test(std::string& working_folder,
bool r = net_utils::invoke_http_json_remote_command2(daemon_addr_a + "/stop_mine", daemon1_req, daemon1_rsp, http_client, 10000); bool r = net_utils::invoke_http_json_remote_command2(daemon_addr_a + "/stop_mine", daemon1_req, daemon1_rsp, http_client, 10000);
CHECK_AND_ASSERT_MES(r, false, "failed to stop mining"); CHECK_AND_ASSERT_MES(r, false, "failed to stop mining");
COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req); COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req);
COMMAND_RPC_START_MINING::response daemon_rsp = AUTO_VAL_INIT(daemon_rsp); COMMAND_RPC_START_MINING::response daemon_rsp = AUTO_VAL_INIT(daemon_rsp);
daemon_req.miner_address = w1.get_account().get_public_address_str(); daemon_req.miner_address = w1.get_account().get_public_address_str();
@ -135,11 +136,11 @@ bool transactions_flow_test(std::string& working_folder,
CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin"); CHECK_AND_ASSERT_MES(daemon_rsp.status == CORE_RPC_STATUS_OK, false, "failed to getrandom_outs.bin");
//wait for money, until balance will have enough money //wait for money, until balance will have enough money
w1.refresh(blocks_fetched, fd); w1.refresh(blocks_fetched, received_money, ok);
while(w1.unlocked_balance() < amount_to_transfer) while(w1.unlocked_balance() < amount_to_transfer)
{ {
misc_utils::sleep_no_w(1000); misc_utils::sleep_no_w(1000);
w1.refresh(blocks_fetched, fd); w1.refresh(blocks_fetched, received_money, ok);
} }
//lets make a lot of small outs to ourselves //lets make a lot of small outs to ourselves
@ -166,7 +167,7 @@ bool transactions_flow_test(std::string& working_folder,
}else }else
{ {
misc_utils::sleep_no_w(1000); misc_utils::sleep_no_w(1000);
w1.refresh(fd); w1.refresh(blocks_fetched, received_money, ok);
} }
} }
//do actual transfer //do actual transfer
@ -188,7 +189,7 @@ bool transactions_flow_test(std::string& working_folder,
{ {
misc_utils::sleep_no_w(1000); misc_utils::sleep_no_w(1000);
LOG_PRINT_L0("not enough money, waiting for cashback or mining"); LOG_PRINT_L0("not enough money, waiting for cashback or mining");
w1.refresh(blocks_fetched, fd); w1.refresh(blocks_fetched, received_money, ok);
} }
transaction tx; transaction tx;
@ -203,7 +204,7 @@ bool transactions_flow_test(std::string& working_folder,
if(!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx)) if(!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx))
{ {
LOG_PRINT_L0("failed to transfer money, tx: " << get_transaction_hash(tx) << ", refresh and try again" ); LOG_PRINT_L0("failed to transfer money, tx: " << get_transaction_hash(tx) << ", refresh and try again" );
w1.refresh(blocks_fetched, fd); w1.refresh(blocks_fetched, received_money, ok);
if(!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx)) if(!do_send_money(w1, w2, mix_in_factor, amount_to_tx, tx))
{ {
LOG_PRINT_L0( "failed to transfer money, second chance. tx: " << get_transaction_hash(tx) << ", exit" ); LOG_PRINT_L0( "failed to transfer money, second chance. tx: " << get_transaction_hash(tx) << ", exit" );
@ -228,7 +229,7 @@ bool transactions_flow_test(std::string& working_folder,
misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*20*1000);//wait two blocks before sync on another wallet on another daemon misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*20*1000);//wait two blocks before sync on another wallet on another daemon
LOG_PRINT_L0( "refreshing..."); LOG_PRINT_L0( "refreshing...");
bool recvd_money = false; bool recvd_money = false;
while(w2.refresh(blocks_fetched, recvd_money, fd) && ( (blocks_fetched && recvd_money) || !blocks_fetched ) ) while(w2.refresh(blocks_fetched, recvd_money, ok) && ( (blocks_fetched && recvd_money) || !blocks_fetched ) )
{ {
misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon misc_utils::sleep_no_w(DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*1000);//wait two blocks before sync on another wallet on another daemon
} }

View File

@ -35,7 +35,7 @@ namespace
if (predicate()) if (predicate())
return true; return true;
//std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); //std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
epee::misc_utils::sleep_no_w(sleep_ms); epee::misc_utils::sleep_no_w(static_cast<long>(sleep_ms));
} }
return false; return false;
} }

View File

@ -29,8 +29,7 @@ public:
{ {
m_miners[i].generate(); m_miners[i].generate();
std::vector<size_t> block_sizes; if (!construct_miner_tx(0, 0, 0, 2, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i]))
if (!construct_miner_tx(0, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i], 0, block_sizes, 2))
return false; return false;
txout_to_key tx_out = boost::get<txout_to_key>(m_miner_txs[i].vout[0].target); txout_to_key tx_out = boost::get<txout_to_key>(m_miner_txs[i].vout[0].target);

View File

@ -17,8 +17,7 @@ public:
m_bob.generate(); m_bob.generate();
std::vector<size_t> block_sizes; if (!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().m_account_address, m_tx))
if (!construct_miner_tx(0, 0, m_bob.get_keys().m_account_address, m_tx, 0, block_sizes, 2))
return false; return false;
m_tx_pub_key = get_tx_pub_key_from_extra(m_tx); m_tx_pub_key = get_tx_pub_key_from_extra(m_tx);

View File

@ -16,14 +16,13 @@ namespace
protected: protected:
static const size_t current_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE / 2; static const size_t current_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE / 2;
std::vector<size_t> m_last_block_sizes; bool m_block_not_too_big;
bool m_block_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
}; };
#define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \ #define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \
m_block_reward = get_block_reward(m_last_block_sizes, current_block_size, m_block_too_big, already_generated_coins); \ m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward); \
ASSERT_FALSE(m_block_too_big); \ ASSERT_TRUE(m_block_not_too_big); \
ASSERT_EQ(m_block_reward, UINT64_C(expected_reward)); ASSERT_EQ(m_block_reward, UINT64_C(expected_reward));
TEST_F(block_reward_and_already_generated_coins, handles_first_values) TEST_F(block_reward_and_already_generated_coins, handles_first_values)
@ -53,72 +52,70 @@ namespace
protected: protected:
virtual void SetUp() virtual void SetUp()
{ {
m_standard_block_reward = get_block_reward(m_last_block_sizes, 0, m_block_too_big, already_generated_coins); m_block_not_too_big = get_block_reward(0, 0, already_generated_coins, m_standard_block_reward);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward);
} }
void do_test(size_t current_block_size) void do_test(size_t median_block_size, size_t current_block_size)
{ {
m_block_reward = get_block_reward(m_last_block_sizes, current_block_size, m_block_too_big, already_generated_coins); m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward);
} }
static const uint64_t already_generated_coins = 0; static const uint64_t already_generated_coins = 0;
std::vector<size_t> m_last_block_sizes; bool m_block_not_too_big;
bool m_block_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
uint64_t m_standard_block_reward; uint64_t m_standard_block_reward;
}; };
TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level) TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level)
{ {
do_test(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level) TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level)
{ {
do_test(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level) TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level)
{ {
do_test(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_less_2_relevance_level) TEST_F(block_reward_and_current_block_size, handles_block_size_less_2_relevance_level)
{ {
do_test(2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
ASSERT_LT(0, m_block_reward); ASSERT_LT(0, m_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level) TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level)
{ {
do_test(2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(0, m_block_reward); ASSERT_EQ(0, m_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_2_relevance_level) TEST_F(block_reward_and_current_block_size, handles_block_size_gt_2_relevance_level)
{ {
do_test(2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1);
ASSERT_TRUE(m_block_too_big); ASSERT_FALSE(m_block_not_too_big);
} }
TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size) TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size)
{ {
#if !defined(NDEBUG) #if !defined(NDEBUG)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2); size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
m_last_block_sizes.push_back(huge_size); ASSERT_DEATH(do_test(huge_size, huge_size + 1), "");
ASSERT_DEATH(do_test(huge_size + 1), "");
#endif #endif
} }
@ -126,8 +123,7 @@ namespace
{ {
#if !defined(NDEBUG) #if !defined(NDEBUG)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2); size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
m_last_block_sizes.push_back(huge_size - 2); ASSERT_DEATH(do_test(huge_size - 2, huge_size), "");
ASSERT_DEATH(do_test(huge_size), "");
#endif #endif
} }
@ -145,21 +141,21 @@ namespace
m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE;
m_standard_block_reward = get_block_reward(m_last_block_sizes, 0, m_block_too_big, already_generated_coins); m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward);
} }
void do_test(size_t current_block_size) void do_test(size_t current_block_size)
{ {
m_block_reward = get_block_reward(m_last_block_sizes, current_block_size, m_block_too_big, already_generated_coins); m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), current_block_size, already_generated_coins, m_block_reward);
} }
static const uint64_t already_generated_coins = 0; static const uint64_t already_generated_coins = 0;
std::vector<size_t> m_last_block_sizes; std::vector<size_t> m_last_block_sizes;
uint64_t m_last_block_sizes_median; uint64_t m_last_block_sizes_median;
bool m_block_too_big; bool m_block_not_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
uint64_t m_standard_block_reward; uint64_t m_standard_block_reward;
}; };
@ -167,28 +163,28 @@ namespace
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median) TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median)
{ {
do_test(m_last_block_sizes_median - 1); do_test(m_last_block_sizes_median - 1);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_median) TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_median)
{ {
do_test(m_last_block_sizes_median); do_test(m_last_block_sizes_median);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median) TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median)
{ {
do_test(m_last_block_sizes_median + 1); do_test(m_last_block_sizes_median + 1);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians) TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians)
{ {
do_test(2 * m_last_block_sizes_median - 1); do_test(2 * m_last_block_sizes_median - 1);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
ASSERT_LT(0, m_block_reward); ASSERT_LT(0, m_block_reward);
} }
@ -196,14 +192,14 @@ namespace
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians) TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians)
{ {
do_test(2 * m_last_block_sizes_median); do_test(2 * m_last_block_sizes_median);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(0, m_block_reward); ASSERT_EQ(0, m_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians) TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians)
{ {
do_test(2 * m_last_block_sizes_median + 1); do_test(2 * m_last_block_sizes_median + 1);
ASSERT_TRUE(m_block_too_big); ASSERT_FALSE(m_block_not_too_big);
} }
TEST_F(block_reward_and_last_block_sizes, calculates_correctly) TEST_F(block_reward_and_last_block_sizes, calculates_correctly)
@ -211,16 +207,16 @@ namespace
ASSERT_EQ(0, m_last_block_sizes_median % 8); ASSERT_EQ(0, m_last_block_sizes_median % 8);
do_test(m_last_block_sizes_median * 9 / 8); do_test(m_last_block_sizes_median * 9 / 8);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64); ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64);
// 3/2 = 12/8 // 3/2 = 12/8
do_test(m_last_block_sizes_median * 3 / 2); do_test(m_last_block_sizes_median * 3 / 2);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4); ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4);
do_test(m_last_block_sizes_median * 15 / 8); do_test(m_last_block_sizes_median * 15 / 8);
ASSERT_FALSE(m_block_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64); ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64);
} }
} }

View File

@ -45,6 +45,7 @@ TEST_pos(uint16_t, 0, "0");
TEST_pos(uint16_t, 1, "1"); TEST_pos(uint16_t, 1, "1");
TEST_pos(uint16_t, 65535, "65535"); TEST_pos(uint16_t, 65535, "65535");
TEST_neg(uint16_t, "");
TEST_neg(uint16_t, "+0"); TEST_neg(uint16_t, "+0");
TEST_neg(uint16_t, "+1"); TEST_neg(uint16_t, "+1");
TEST_neg(uint16_t, "+65535"); TEST_neg(uint16_t, "+65535");
@ -77,6 +78,7 @@ TEST_pos(uint32_t, 0, "0");
TEST_pos(uint32_t, 1, "1"); TEST_pos(uint32_t, 1, "1");
TEST_pos(uint32_t, 4294967295, "4294967295"); TEST_pos(uint32_t, 4294967295, "4294967295");
TEST_neg(uint32_t, "");
TEST_neg(uint32_t, "+0"); TEST_neg(uint32_t, "+0");
TEST_neg(uint32_t, "+1"); TEST_neg(uint32_t, "+1");
TEST_neg(uint32_t, "+4294967295"); TEST_neg(uint32_t, "+4294967295");
@ -107,6 +109,7 @@ TEST_pos(uint64_t, 0, "0");
TEST_pos(uint64_t, 1, "1"); TEST_pos(uint64_t, 1, "1");
TEST_pos(uint64_t, 18446744073709551615ULL, "18446744073709551615"); TEST_pos(uint64_t, 18446744073709551615ULL, "18446744073709551615");
TEST_neg(uint64_t, "");
TEST_neg(uint64_t, "+0"); TEST_neg(uint64_t, "+0");
TEST_neg(uint64_t, "+1"); TEST_neg(uint64_t, "+1");
TEST_neg(uint64_t, "+18446744073709551615"); TEST_neg(uint64_t, "+18446744073709551615");

View File

@ -0,0 +1,124 @@
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "gtest/gtest.h"
#include "cryptonote_core/cryptonote_format_utils.h"
using namespace cryptonote;
namespace
{
void do_pos_test(uint64_t expected, const std::string& str)
{
uint64_t val;
std::string number_str = str;
std::replace(number_str.begin(), number_str.end(), '_', '.');
number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end());
ASSERT_TRUE(parse_amount(val, number_str));
ASSERT_EQ(expected, val);
}
void do_neg_test(const std::string& str)
{
uint64_t val;
std::string number_str = str;
std::replace(number_str.begin(), number_str.end(), '_', '.');
number_str.erase(std::remove(number_str.begin(), number_str.end(), '~'), number_str.end());
ASSERT_FALSE(parse_amount(val, number_str));
}
}
#define TEST_pos(expected, str) \
TEST(parse_amount, handles_pos_ ## str) \
{ \
do_pos_test(UINT64_C(expected), #str); \
}
#define TEST_neg(str) \
TEST(parse_amount, handles_neg_ ## str) \
{ \
do_neg_test(#str); \
}
#define TEST_neg_n(str, name) \
TEST(parse_amount, handles_neg_ ## name) \
{ \
do_neg_test(#str); \
}
TEST_pos(0, 0);
TEST_pos(0, 00);
TEST_pos(0, 00000000);
TEST_pos(0, 000000000);
TEST_pos(0, 00000000000000000000000000000000);
TEST_pos(0, _0);
TEST_pos(0, _00);
TEST_pos(0, _00000000);
TEST_pos(0, _000000000);
TEST_pos(0, _00000000000000000000000000000000);
TEST_pos(0, 00000000_);
TEST_pos(0, 000000000_);
TEST_pos(0, 00000000000000000000000000000000_);
TEST_pos(0, 0_);
TEST_pos(0, 0_0);
TEST_pos(0, 0_00);
TEST_pos(0, 0_00000000);
TEST_pos(0, 0_000000000);
TEST_pos(0, 0_00000000000000000000000000000000);
TEST_pos(0, 00_);
TEST_pos(0, 00_0);
TEST_pos(0, 00_00);
TEST_pos(0, 00_00000000);
TEST_pos(0, 00_000000000);
TEST_pos(0, 00_00000000000000000000000000000000);
TEST_pos(1, 0_00000001);
TEST_pos(1, 0_000000010);
TEST_pos(1, 0_000000010000000000000000000000000);
TEST_pos(9, 0_00000009);
TEST_pos(9, 0_000000090);
TEST_pos(9, 0_000000090000000000000000000000000);
TEST_pos( 100000000, 1);
TEST_pos( 6553500000000, 65535);
TEST_pos( 429496729500000000, 4294967295);
TEST_pos(18446744073700000000, 184467440737_);
TEST_pos(18446744073700000000, 184467440737_0);
TEST_pos(18446744073700000000, 184467440737_00000000);
TEST_pos(18446744073700000000, 184467440737_000000000);
TEST_pos(18446744073700000000, 184467440737_0000000000000000000);
TEST_pos(18446744073709551615, 184467440737_09551615);
// Invalid numbers
TEST_neg_n(~, empty_string);
TEST_neg_n(-0, minus_0);
TEST_neg_n(+0, plus_0);
TEST_neg_n(-1, minus_1);
TEST_neg_n(+1, plus_1);
TEST_neg_n(_, only_point);
// A lot of fraction digits
TEST_neg(0_000000001);
TEST_neg(0_000000009);
TEST_neg(184467440737_000000001);
// Overflow
TEST_neg(184467440737_09551616);
TEST_neg(184467440738);
TEST_neg(18446744073709551616);
// Two or more points
TEST_neg(__);
TEST_neg(0__);
TEST_neg(__0);
TEST_neg(0__0);
TEST_neg(0_0_);
TEST_neg(_0_0);
TEST_neg(0_0_0);

View File

@ -12,9 +12,8 @@ TEST(parse_and_validate_tx_extra, is_correct_parse_and_validate_tx_extra)
cryptonote::transaction tx = AUTO_VAL_INIT(tx); cryptonote::transaction tx = AUTO_VAL_INIT(tx);
cryptonote::account_base acc; cryptonote::account_base acc;
acc.generate(); acc.generate();
std::vector<size_t> bss;
cryptonote::blobdata b = "dsdsdfsdfsf"; cryptonote::blobdata b = "dsdsdfsdfsf";
bool r = cryptonote::construct_miner_tx(0, 10000000000000, acc.get_keys().m_account_address, tx, DEFAULT_FEE, bss, 1000, b, 1); bool r = cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1);
ASSERT_TRUE(r); ASSERT_TRUE(r);
crypto::public_key tx_pub_key; crypto::public_key tx_pub_key;
r = cryptonote::parse_and_validate_tx_extra(tx, tx_pub_key); r = cryptonote::parse_and_validate_tx_extra(tx, tx_pub_key);
@ -25,9 +24,8 @@ TEST(parse_and_validate_tx_extra, is_correct_extranonce_too_big)
cryptonote::transaction tx = AUTO_VAL_INIT(tx); cryptonote::transaction tx = AUTO_VAL_INIT(tx);
cryptonote::account_base acc; cryptonote::account_base acc;
acc.generate(); acc.generate();
std::vector<size_t> bss;
cryptonote::blobdata b(260, 0); cryptonote::blobdata b(260, 0);
bool r = cryptonote::construct_miner_tx(0, 10000000000000, acc.get_keys().m_account_address, tx, DEFAULT_FEE, bss, 1000, b, 1); bool r = cryptonote::construct_miner_tx(0, 0, 10000000000000, 1000, DEFAULT_FEE, acc.get_keys().m_account_address, tx, b, 1);
ASSERT_FALSE(r); ASSERT_FALSE(r);
} }
TEST(parse_and_validate_tx_extra, is_correct_wrong_extra_couner_too_big) TEST(parse_and_validate_tx_extra, is_correct_wrong_extra_couner_too_big)