mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-26 21:03:28 +01:00
json rpc for wallet and bugfix
This commit is contained in:
parent
8efa1313f3
commit
29c2859a3e
17
ReleaseNotes.txt
Normal file
17
ReleaseNotes.txt
Normal 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
|
@ -42,10 +42,10 @@ bool communicate(const std::string url, t_request& req, t_response& rsp, const s
|
||||
if(use_jrpc)
|
||||
{
|
||||
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.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);
|
||||
if(!epee::net_utils::invoke_http_json_remote_command2("/request_json_rpc", req_t, resp_t, http_client))
|
||||
{
|
||||
|
@ -261,6 +261,7 @@ namespace epee
|
||||
continue_handle = false;
|
||||
break;
|
||||
}
|
||||
string_tools::trim(command);
|
||||
|
||||
LOG_PRINT_L2("Read command: " << command);
|
||||
if(0 == command.compare("exit") || 0 == command.compare("q"))
|
||||
@ -281,7 +282,8 @@ namespace epee
|
||||
continue;
|
||||
}
|
||||
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())
|
||||
{
|
||||
continue;
|
||||
|
@ -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_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_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)
|
||||
|
||||
|
@ -801,7 +801,7 @@ using namespace std;
|
||||
}else
|
||||
{ //Apparently there are no signs of the form of transfer, will receive data until the connection is closed
|
||||
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;
|
||||
|
@ -118,15 +118,15 @@ namespace epee
|
||||
template<typename t_param>
|
||||
struct request
|
||||
{
|
||||
std::string version;
|
||||
std::string jsonrpc;
|
||||
std::string method;
|
||||
std::string id;
|
||||
t_param params;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(version)
|
||||
KV_SERIALIZE(method)
|
||||
KV_SERIALIZE(jsonrpc)
|
||||
KV_SERIALIZE(id)
|
||||
KV_SERIALIZE(method)
|
||||
KV_SERIALIZE(params)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
@ -147,21 +147,54 @@ namespace epee
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
template<typename t_param, typename t_error>
|
||||
struct response
|
||||
struct dummy_result
|
||||
{
|
||||
t_param result;
|
||||
t_error error;
|
||||
std::string id;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(result)
|
||||
KV_SERIALIZE(error)
|
||||
KV_SERIALIZE(id)
|
||||
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)) \
|
||||
{ \
|
||||
epee::json_rpc::error_response rsp; \
|
||||
rsp.jsonrpc = "2.0"; \
|
||||
rsp.error.code = -32600; \
|
||||
rsp.error.message = "Invalid Request"; \
|
||||
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
|
||||
@ -201,14 +235,45 @@ namespace epee
|
||||
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)) \
|
||||
{ \
|
||||
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_WERI(method_name, callback_f, command_type) \
|
||||
else if(callback_name == method_name) \
|
||||
{ \
|
||||
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
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(); \
|
||||
@ -229,16 +294,18 @@ namespace epee
|
||||
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; \
|
||||
if(!callback_f(req.params, resp.result, m_conn_context)) \
|
||||
{ \
|
||||
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
|
||||
fail_resp.id = req.id; \
|
||||
fail_resp.error.code = -32603; \
|
||||
fail_resp.error.message = "Internal error"; \
|
||||
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
|
||||
return true; \
|
||||
} \
|
||||
{ \
|
||||
epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
|
||||
fail_resp.jsonrpc = "2.0"; \
|
||||
fail_resp.id = req.id; \
|
||||
fail_resp.error.code = -32603; \
|
||||
fail_resp.error.message = "Internal error"; \
|
||||
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(); \
|
||||
@ -251,6 +318,7 @@ namespace epee
|
||||
|
||||
#define END_JSON_RPC_MAP() \
|
||||
epee::json_rpc::error_response rsp; \
|
||||
rsp.jsonrpc = "2.0"; \
|
||||
rsp.error.code = -32601; \
|
||||
rsp.error.message = "Method not found"; \
|
||||
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
|
||||
|
@ -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_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_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_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_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_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)
|
||||
|
||||
|
@ -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")
|
||||
{
|
||||
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.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);
|
||||
if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method))
|
||||
{
|
||||
|
@ -27,6 +27,7 @@ namespace boost
|
||||
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)
|
||||
{
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for(size_t i = 0; i != s; i++)
|
||||
@ -54,6 +55,7 @@ namespace boost
|
||||
template <class Archive, class hval>
|
||||
inline void load(Archive &a, std::unordered_set<hval> &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
x.clear();
|
||||
size_t s = 0;
|
||||
a >> s;
|
||||
for(size_t i = 0; i != s; i++)
|
||||
|
@ -4,29 +4,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000
|
||||
#define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used!
|
||||
#define CRYPTONOTE_MAX_TX_SIZE 1000000000
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 6 // addresses start with "2"
|
||||
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 10
|
||||
#define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000
|
||||
#define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used!
|
||||
#define CRYPTONOTE_MAX_TX_SIZE 1000000000
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 6 // addresses start with "2"
|
||||
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 10
|
||||
#define CURRENT_TRANSACTION_VERSION 1
|
||||
#define CURRENT_BLOCK_MAJOR_VERSION 1
|
||||
#define CURRENT_BLOCK_MINOR_VERSION 0
|
||||
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||
|
||||
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60
|
||||
|
||||
// MONEY_SUPPLY - total number coins to be generated
|
||||
#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_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_DISPLAY_DECIMAL_POINT 8
|
||||
#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_COINBASE_BLOB_RESERVED_SIZE 600
|
||||
#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
|
||||
@ -39,8 +39,8 @@
|
||||
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG
|
||||
|
||||
|
||||
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
|
||||
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1
|
||||
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
|
||||
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1
|
||||
|
||||
|
||||
#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN DIFFICULTY_TARGET //just alias
|
||||
@ -48,7 +48,7 @@
|
||||
|
||||
#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing
|
||||
#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading
|
||||
#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block
|
||||
#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block
|
||||
|
||||
|
||||
#define P2P_DEFAULT_PORT 8080
|
||||
|
@ -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)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
auto it = m_transactions.find(id);
|
||||
if (it == m_transactions.end())
|
||||
return NULL;
|
||||
@ -65,26 +66,12 @@ bool blockchain_storage::init(const std::string& config_folder)
|
||||
const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME;
|
||||
if(!tools::unserialize_obj_from_file(*this, filename))
|
||||
{
|
||||
const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME;
|
||||
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.");
|
||||
LOG_PRINT_L0("Can't load blockchain storage from file, generating genesis block.");
|
||||
block bl = boost::value_initialized<block>();
|
||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||
generate_genesis_block(bl);
|
||||
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");
|
||||
}
|
||||
}
|
||||
if(!m_blocks.size())
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
struct purge_transaction_visitor: public boost::static_visitor<bool>
|
||||
{
|
||||
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)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
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!!");
|
||||
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;
|
||||
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
bool block_too_big = false;
|
||||
base_reward = get_block_reward(last_blocks_sizes, cumulative_block_size, block_too_big, already_generated_coins);
|
||||
if(block_too_big)
|
||||
if(!get_block_reward(misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward))
|
||||
{
|
||||
LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
size_t txs_cumulative_size = 0;
|
||||
uint64_t fee = 0;
|
||||
size_t comul_sz_limit = 0;
|
||||
std::vector<size_t> sz;
|
||||
size_t median_size;
|
||||
uint64_t already_generated_coins;
|
||||
|
||||
CRITICAL_REGION_BEGIN(m_blockchain_lock);
|
||||
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
|
||||
b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
|
||||
b.minor_version = CURRENT_BLOCK_MINOR_VERSION;
|
||||
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();
|
||||
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();
|
||||
|
||||
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
|
||||
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
|
||||
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");
|
||||
#ifdef _DEBUG
|
||||
std::list<size_t> try_val;
|
||||
try_val.push_back(get_object_blobsize(b.miner_tx));
|
||||
#endif
|
||||
|
||||
size_t cumulative_size = txs_cumulative_size + get_object_blobsize(b.miner_tx);
|
||||
size_t try_count = 0;
|
||||
for(; try_count != 10; ++try_count)
|
||||
{
|
||||
r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, cumulative_size, ex_nonce, 11);
|
||||
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
|
||||
for (size_t try_count = 0; 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);
|
||||
#ifdef _DEBUG
|
||||
try_val.push_back(get_object_blobsize(b.miner_tx));
|
||||
#endif
|
||||
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
|
||||
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
|
||||
if(coinbase_blob_size > cumulative_size - txs_cumulative_size )
|
||||
{
|
||||
cumulative_size = txs_cumulative_size + coinbase_blob_size;
|
||||
if (coinbase_blob_size > cumulative_size - txs_size) {
|
||||
cumulative_size = txs_size + coinbase_blob_size;
|
||||
continue;
|
||||
}else
|
||||
{
|
||||
if(coinbase_blob_size < cumulative_size - txs_cumulative_size )
|
||||
{
|
||||
size_t delta = cumulative_size - txs_cumulative_size - coinbase_blob_size;
|
||||
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.
|
||||
if(cumulative_size != txs_cumulative_size + 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 );
|
||||
if(cumulative_size != txs_cumulative_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
|
||||
LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2);
|
||||
cumulative_size += delta + 1;
|
||||
continue;
|
||||
}
|
||||
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) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (coinbase_blob_size < cumulative_size - txs_size) {
|
||||
size_t delta = cumulative_size - txs_size - coinbase_blob_size;
|
||||
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.
|
||||
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));
|
||||
b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
|
||||
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
|
||||
LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2);
|
||||
cumulative_size += delta - 1;
|
||||
continue;
|
||||
}
|
||||
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_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;
|
||||
}
|
||||
LOG_ERROR("Failed to create_block_template with " << try_count << " tries");
|
||||
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
|
||||
{
|
||||
|
||||
if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
|
||||
return true;
|
||||
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
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());
|
||||
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;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
//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)
|
||||
{
|
||||
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)
|
||||
<< "\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()
|
||||
@ -1605,7 +1575,7 @@ bool blockchain_storage::update_next_comulative_size_limit()
|
||||
if(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;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
@ -50,7 +50,7 @@ namespace cryptonote
|
||||
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()); }
|
||||
@ -175,7 +175,7 @@ namespace cryptonote
|
||||
blocks_by_id_index m_blocks_index; // crypto::hash -> height
|
||||
transactions_container m_transactions;
|
||||
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
|
||||
@ -235,12 +235,12 @@ namespace cryptonote
|
||||
/* */
|
||||
/************************************************************************/
|
||||
|
||||
#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 11
|
||||
#define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 12
|
||||
|
||||
template<class archive_t>
|
||||
void blockchain_storage::serialize(archive_t & ar, const unsigned int version)
|
||||
{
|
||||
if(version < CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER)
|
||||
if(version < 11)
|
||||
return;
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
ar & m_blocks;
|
||||
@ -250,14 +250,54 @@ namespace cryptonote
|
||||
ar & m_alternative_chains;
|
||||
ar & m_outputs;
|
||||
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>
|
||||
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);
|
||||
if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size())
|
||||
return false;
|
||||
|
@ -25,7 +25,7 @@ namespace cryptonote {
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
size_t get_max_block_size()
|
||||
{
|
||||
return CRYPTONOTE_MAX_BLOCK_SIZE;
|
||||
return CRYPTONOTE_MAX_BLOCK_SIZE;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------------------
|
||||
size_t get_max_tx_size()
|
||||
@ -33,46 +33,39 @@ namespace cryptonote {
|
||||
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)
|
||||
{
|
||||
block_too_big = false;
|
||||
|
||||
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward) {
|
||||
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
|
||||
if(med_sz < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE)
|
||||
med_sz = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE;
|
||||
if (median_size < 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;
|
||||
}
|
||||
if (current_block_size <= median_size) {
|
||||
reward = base_reward;
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(med_sz < std::numeric_limits<uint32_t>::max());
|
||||
assert(current_block_size < std::numeric_limits<uint32_t>::max());
|
||||
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;
|
||||
}
|
||||
|
||||
uint64_t product_hi;
|
||||
uint64_t product_lo = mul128(base_reward, current_block_size * (2 * med_sz - current_block_size), &product_hi);
|
||||
assert(median_size < std::numeric_limits<uint32_t>::max());
|
||||
assert(current_block_size < std::numeric_limits<uint32_t>::max());
|
||||
|
||||
uint64_t reward_hi;
|
||||
uint64_t reward_lo;
|
||||
div128_32(product_hi, product_lo, static_cast<uint32_t>(med_sz), &reward_hi, &reward_lo);
|
||||
div128_32(reward_hi, reward_lo, static_cast<uint32_t>(med_sz), &reward_hi, &reward_lo);
|
||||
assert(0 == reward_hi);
|
||||
assert(reward_lo < base_reward);
|
||||
uint64_t product_hi;
|
||||
uint64_t product_lo = mul128(base_reward, current_block_size * (2 * median_size - current_block_size), &product_hi);
|
||||
|
||||
return reward_lo;
|
||||
}else
|
||||
return base_reward;
|
||||
uint64_t reward_hi;
|
||||
uint64_t 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>(median_size), &reward_hi, &reward_lo);
|
||||
assert(0 == reward_hi);
|
||||
assert(reward_lo < base_reward);
|
||||
|
||||
reward = reward_lo;
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
uint8_t get_account_address_checksum(const public_address_outer_blob& bl)
|
||||
|
@ -38,7 +38,7 @@ namespace cryptonote {
|
||||
/************************************************************************/
|
||||
size_t get_max_block_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);
|
||||
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);
|
||||
|
@ -54,13 +54,7 @@ namespace cryptonote
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.extra.clear();
|
||||
@ -74,13 +68,13 @@ namespace cryptonote
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
bool block_too_big = false;
|
||||
uint64_t block_reward = get_block_reward(blocks_sizes, current_block_size, block_too_big, already_generated_coins) + fee;
|
||||
if(block_too_big)
|
||||
uint64_t block_reward;
|
||||
if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward))
|
||||
{
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
}
|
||||
block_reward += fee;
|
||||
|
||||
std::vector<size_t> out_amounts;
|
||||
decompose_amount_into_digits(block_reward, DEFAULT_FEE,
|
||||
@ -120,7 +114,7 @@ namespace cryptonote
|
||||
//lock
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.push_back(in);
|
||||
//LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
|
||||
//LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
|
||||
// << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2);
|
||||
return true;
|
||||
}
|
||||
@ -152,33 +146,37 @@ namespace cryptonote
|
||||
//---------------------------------------------------------------
|
||||
bool parse_amount(uint64_t& amount, const std::string& str_amount_)
|
||||
{
|
||||
std::vector<std::string> pars;
|
||||
std::string str_amount = str_amount_;
|
||||
boost::algorithm::trim(str_amount);
|
||||
if(!str_amount.size())
|
||||
return false;
|
||||
if(str_amount[0] == '-')
|
||||
|
||||
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;
|
||||
str_amount.erase(point_index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fraction_size = 0;
|
||||
}
|
||||
|
||||
if (str_amount.empty())
|
||||
return false;
|
||||
|
||||
boost::split(pars, str_amount, boost::is_any_of("."), boost::token_compress_on );
|
||||
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)
|
||||
if (fraction_size < CRYPTONOTE_DISPLAY_DECIMAL_POINT)
|
||||
{
|
||||
uint64_t right = 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;
|
||||
str_amount.append(CRYPTONOTE_DISPLAY_DECIMAL_POINT - fraction_size, '0');
|
||||
}
|
||||
return true;
|
||||
|
||||
return string_tools::get_xtype_from_string(amount, str_amount);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool get_tx_fee(const transaction& tx, uint64_t & fee)
|
||||
@ -218,7 +216,7 @@ namespace cryptonote
|
||||
tx_pub_key = null_pkey;
|
||||
bool padding_started = false; //let the padding goes only at the end
|
||||
bool tx_extra_tag_pubkey_found = false;
|
||||
bool tx_extra_extra_nonce_found = false;
|
||||
bool tx_extra_extra_nonce_found = false;
|
||||
for(size_t i = 0; i != tx.extra.size();)
|
||||
{
|
||||
if(padding_started)
|
||||
@ -573,7 +571,7 @@ namespace cryptonote
|
||||
blobdata get_block_hashing_blob(const block& b)
|
||||
{
|
||||
blobdata blob = t_serializable_object_to_blob(static_cast<block_header>(b));
|
||||
crypto::hash tree_root_hash = get_tx_tree_hash(b);
|
||||
crypto::hash tree_root_hash = get_tx_tree_hash(b);
|
||||
blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash ));
|
||||
blob.append(tools::get_varint_data(b.tx_hashes.size()+1));
|
||||
return blob;
|
||||
@ -599,7 +597,7 @@ namespace cryptonote
|
||||
|
||||
account_public_address ac = boost::value_initialized<account_public_address>();
|
||||
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);
|
||||
std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb);
|
||||
|
||||
|
@ -17,9 +17,8 @@ namespace cryptonote
|
||||
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
|
||||
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);
|
||||
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(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);
|
||||
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
|
||||
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);
|
||||
|
||||
struct tx_source_entry
|
||||
{
|
||||
|
@ -2,8 +2,10 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "tx_pool.h"
|
||||
#include "cryptonote_format_utils.h"
|
||||
@ -11,6 +13,7 @@
|
||||
#include "cryptonote_config.h"
|
||||
#include "blockchain_storage.h"
|
||||
#include "common/boost_serialization_helper.h"
|
||||
#include "common/int-util.h"
|
||||
#include "misc_language.h"
|
||||
#include "warnings.h"
|
||||
#include "crypto/hash.h"
|
||||
@ -345,31 +348,60 @@ namespace cryptonote
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
std::unordered_set<crypto::key_image> k_images;
|
||||
|
||||
BOOST_FOREACH(transactions_container::value_type& tx, m_transactions)
|
||||
{
|
||||
if(cumulative_sizes + tx.second.blob_size > max_comulative_sz)
|
||||
continue;
|
||||
for (size_t i = 0; i < txs.size(); i++) {
|
||||
txv &tx(*txs[i]);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -49,7 +49,7 @@ namespace cryptonote
|
||||
// load/store operations
|
||||
bool init(const std::string& config_folder);
|
||||
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_transaction(const crypto::hash& h, transaction& tx);
|
||||
size_t get_transactions_count();
|
||||
|
@ -108,7 +108,9 @@ namespace cryptonote
|
||||
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_remote_blockchain_height = hshd.current_height;
|
||||
//let the socket to send response to handshake, but request callback, to let send request data after response
|
||||
|
@ -29,9 +29,6 @@ using namespace epee;
|
||||
|
||||
namespace po = boost::program_options;
|
||||
|
||||
|
||||
BOOST_CLASS_VERSION(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >, 1);
|
||||
|
||||
namespace
|
||||
{
|
||||
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 );
|
||||
#endif
|
||||
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...");
|
||||
|
||||
TRY_ENTRY();
|
||||
|
@ -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)
|
||||
{
|
||||
CHECK_CORE_READY();
|
||||
|
||||
std::string tx_blob;
|
||||
if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob))
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "storages/http_abstract_invoke.h"
|
||||
#include "rpc/core_rpc_server_commands_defs.h"
|
||||
#include "wallet/wallet_rpc_server.h"
|
||||
#include "version.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
@ -37,27 +38,109 @@ 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_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<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< 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);
|
||||
if (color) epee::log_space::set_console_color(epee::log_space::console_color_green, false);
|
||||
std::cout << msg;
|
||||
if (color) epee::log_space::reset_console_color();
|
||||
std::cout << std::endl;
|
||||
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;
|
||||
}
|
||||
|
||||
void print_fail_msg(const std::string& msg)
|
||||
class message_writer
|
||||
{
|
||||
LOG_PRINT_L1("Error:" << msg);
|
||||
epee::log_space::set_console_color(epee::log_space::console_color_red, true);
|
||||
std::cout << "Error: " << msg;
|
||||
epee::log_space::reset_console_color();
|
||||
std::cout << std::endl;
|
||||
public:
|
||||
message_writer(epee::log_space::console_colors color = epee::log_space::console_color_default, bool bright = false,
|
||||
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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>()*/)
|
||||
{
|
||||
std::cout << get_commands_str();
|
||||
success_msg_writer() << get_commands_str();
|
||||
return true;
|
||||
}
|
||||
|
||||
simple_wallet::simple_wallet()
|
||||
: 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("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("show_incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), "Show incoming transfers");
|
||||
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("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("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("balance", boost::bind(&simple_wallet::show_balance, this, _1), "Show current wallet balance");
|
||||
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("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_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), "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("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");
|
||||
@ -99,18 +183,18 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
|
||||
{
|
||||
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;
|
||||
}
|
||||
uint16_t l = 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -122,19 +206,27 @@ bool simple_wallet::init(const boost::program_options::variables_map& 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;
|
||||
if(!m_generate_new.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())
|
||||
m_daemon_host = "localhost";
|
||||
if (!m_daemon_port)
|
||||
m_daemon_port = RPC_DEFAULT_PORT;
|
||||
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;
|
||||
if (command_line::has_arg(vm, arg_password))
|
||||
@ -144,7 +236,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
|
||||
else
|
||||
{
|
||||
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())
|
||||
@ -169,25 +265,22 @@ bool simple_wallet::deinit()
|
||||
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_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
|
||||
m_daemon_address = command_line::get_arg(vm, arg_daemon_address);
|
||||
m_daemon_host = command_line::get_arg(vm, arg_daemon_host);
|
||||
m_daemon_port = command_line::get_arg(vm, arg_daemon_port);
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::try_connect_to_daemon()
|
||||
{
|
||||
if (!m_wallet->check_connection())
|
||||
{
|
||||
std::string msg = "wallet failed to connect to daemon (" + m_daemon_address + "). " +
|
||||
"Daemon either is not started or passed wrong port. " +
|
||||
fail_msg_writer() << "wallet failed to connect to daemon (" << m_daemon_address << "). " <<
|
||||
"Daemon either is not started or passed wrong port. " <<
|
||||
"Please, make sure that daemon is running or restart the wallet with correct daemon address.";
|
||||
print_fail_msg(msg);
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
m_wallet.reset(new tools::wallet2());
|
||||
bool r = m_wallet->generate(wallet_file, password);
|
||||
if(!r)
|
||||
return false;
|
||||
m_wallet->init(m_daemon_address);
|
||||
|
||||
cout << "Generated new wallet" << ENDL;
|
||||
print_address(std::vector<std::string>());
|
||||
r = m_wallet->init(m_daemon_address);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to init wallet");
|
||||
std::cout << "**********************************************************************" << ENDL
|
||||
<< "Your wallet has been generated. " << ENDL
|
||||
<< "To start synchronizing with the daemon use \"refresh\" command." << ENDL
|
||||
<< "Use \"help\" command to see the list of available commands." << ENDL
|
||||
<< "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;
|
||||
success_msg_writer() <<
|
||||
"**********************************************************************\n" <<
|
||||
"Your wallet has been generated.\n" <<
|
||||
"To start synchronizing with the daemon use \"refresh\" command.\n" <<
|
||||
"Use \"help\" command to see the list of available commands.\n" <<
|
||||
"Always use \"exit\" command when closing simplewallet to save\n" <<
|
||||
"current session's state. Otherwise, you will possibly need to synchronize \n" <<
|
||||
"your wallet again. Your wallet key is NOT under risk anyway.\n" <<
|
||||
"**********************************************************************";
|
||||
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.reset(new tools::wallet2());
|
||||
m_wallet->callback(this);
|
||||
|
||||
bool r = m_wallet->load(m_wallet_file, password);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to load wallet " + m_wallet_file);
|
||||
r = m_wallet->init(m_daemon_address);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to init wallet");
|
||||
try
|
||||
{
|
||||
m_wallet->load(m_wallet_file, password);
|
||||
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>());
|
||||
std::cout << "**********************************************************************" << ENDL
|
||||
<< "Use \"help\" command to see the list of available commands." << ENDL
|
||||
<< "**********************************************************************" << ENDL ;
|
||||
success_msg_writer() <<
|
||||
"**********************************************************************\n" <<
|
||||
"Use \"help\" command to see the list of available commands.\n" <<
|
||||
"**********************************************************************";
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::close_wallet()
|
||||
{
|
||||
bool r = m_wallet->deinit();
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to deinit wallet");
|
||||
r = m_wallet->store();
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to store wallet " + m_wallet_file);
|
||||
if (!r)
|
||||
{
|
||||
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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::save(const std::vector<std::string> &args)
|
||||
{
|
||||
bool r = m_wallet->store();
|
||||
if (r)
|
||||
print_success_msg("Wallet data saved");
|
||||
else
|
||||
print_fail_msg("failed to store wallet " + m_wallet_file);
|
||||
try
|
||||
{
|
||||
m_wallet->store();
|
||||
success_msg_writer() << "Wallet data saved";
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
fail_msg_writer() << e.what();
|
||||
}
|
||||
|
||||
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]);
|
||||
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;
|
||||
}
|
||||
req.threads_count = num;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
std::string err = tools::interpret_rpc_response(r, res.status);
|
||||
std::string err = interpret_rpc_response(r, res.status);
|
||||
if (err.empty())
|
||||
print_success_msg("Mining started in daemon");
|
||||
success_msg_writer() << "Mining started in daemon";
|
||||
else
|
||||
print_fail_msg("mining has NOT been started: " + err);
|
||||
fail_msg_writer() << "mining has NOT been started: " << err;
|
||||
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::response res;
|
||||
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())
|
||||
print_success_msg("Mining stopped in daemon");
|
||||
success_msg_writer() << "Mining stopped in daemon";
|
||||
else
|
||||
print_fail_msg("mining has NOT been stopped: " + err);
|
||||
fail_msg_writer() << "mining has NOT been stopped: " << err;
|
||||
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)
|
||||
{
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
||||
std::cout << "Starting refresh..." << endl;
|
||||
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();
|
||||
message_writer() << "Starting refresh...";
|
||||
uint64_t fetched_blocks = 0;
|
||||
bool money_received = false;
|
||||
tools::wallet2::fail_details fd;
|
||||
bool ok = m_wallet->refresh(fetched_blocks, money_received, fd);
|
||||
refresh_is_done = true;
|
||||
th.join();
|
||||
if (ok)
|
||||
bool ok = false;
|
||||
std::ostringstream ss;
|
||||
try
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Refresh done, blocks received: " << fetched_blocks;
|
||||
print_success_msg(ss.str(), true);
|
||||
|
||||
m_wallet->refresh(fetched_blocks);
|
||||
ok = true;
|
||||
// Clear line "Height xxx of xxx"
|
||||
std::cout << "\r \r";
|
||||
success_msg_writer(true) << "Refresh done, blocks received: " << fetched_blocks;
|
||||
show_balance();
|
||||
}
|
||||
else
|
||||
catch (const tools::error::daemon_busy&)
|
||||
{
|
||||
fetched_blocks = m_wallet->get_blockchain_current_height() - initial_height;
|
||||
std::stringstream ss;
|
||||
ss << "refresh failed: " << fd.what() << ". Blocks received: " << fetched_blocks;
|
||||
print_fail_msg(ss.str());
|
||||
ss << "daemon is busy. Please try later";
|
||||
}
|
||||
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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
|
||||
print_success_msg(ss.str());
|
||||
success_msg_writer() << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args)
|
||||
{
|
||||
std::cout << " amount \tspent\tglobal index\t tx id" << std::endl;
|
||||
bool ok = m_wallet->enum_incoming_transfers([](const cryptonote::transaction& tx, uint64_t global_out_index, uint64_t amount, bool spent) {
|
||||
epee::log_space::set_console_color(spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, true);
|
||||
std::cout << std::setw(21) << print_money(amount) << '\t'
|
||||
<< std::setw(3) << (spent ? 'T' : 'F') << " \t"
|
||||
<< std::setw(12) << global_out_index << '\t'
|
||||
<< get_transaction_hash(tx)
|
||||
<< '\n';
|
||||
});
|
||||
epee::log_space::reset_console_color();
|
||||
if (ok)
|
||||
std::cout.flush();
|
||||
else
|
||||
print_fail_msg("No incoming transfers");
|
||||
bool filter = false;
|
||||
bool available = false;
|
||||
if (!args.empty())
|
||||
{
|
||||
if (args[0] == "available")
|
||||
{
|
||||
filter = true;
|
||||
available = true;
|
||||
}
|
||||
else if (args[0] == "unavailable")
|
||||
{
|
||||
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
|
||||
{
|
||||
success_msg_writer() << "No incoming unavailable transfers";
|
||||
}
|
||||
}
|
||||
|
||||
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::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);
|
||||
err = tools::interpret_rpc_response(r, res.status);
|
||||
err = interpret_rpc_response(r, res.status);
|
||||
return res.height;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@ -401,9 +600,9 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
|
||||
std::string err;
|
||||
uint64_t bc_height = get_daemon_blockchain_height(err);
|
||||
if (err.empty())
|
||||
print_success_msg(boost::lexical_cast<std::string>(bc_height));
|
||||
success_msg_writer() << bc_height;
|
||||
else
|
||||
print_fail_msg("failed to get blockchain height: " + err);
|
||||
fail_msg_writer() << "failed to get blockchain height: " << err;
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@ -415,65 +614,132 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
||||
std::vector<std::string> local_args = args_;
|
||||
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;
|
||||
}
|
||||
|
||||
size_t fake_outs_count;
|
||||
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;
|
||||
}
|
||||
local_args.erase(local_args.begin());
|
||||
|
||||
vector<cryptonote::tx_destination_entry> dsts;
|
||||
uint64_t summary_amount = 0;
|
||||
for (size_t i = 0; i < local_args.size(); i += 2)
|
||||
for (size_t i = 1; i < local_args.size(); i += 2)
|
||||
{
|
||||
cryptonote::tx_destination_entry de;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
|
||||
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;
|
||||
}
|
||||
|
||||
summary_amount += de.amount;
|
||||
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()));
|
||||
return true;
|
||||
cryptonote::transaction tx;
|
||||
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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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()
|
||||
@ -482,9 +748,9 @@ void simple_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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@ -495,7 +761,6 @@ bool simple_wallet::process_command(const std::vector<std::string> &args)
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
#ifdef WIN32
|
||||
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
|
||||
#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_command);
|
||||
command_line::add_arg(desc_params, arg_log_level);
|
||||
tools::wallet_rpc_server::init_options(desc_params);
|
||||
|
||||
po::positional_options_description positional_options;
|
||||
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))
|
||||
{
|
||||
std::cout << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]\n";
|
||||
std::cout << desc_all << '\n' << w.get_commands_str() << std::endl;
|
||||
success_msg_writer() << "Usage: simplewallet [--wallet-file=<file>|--generate-new-wallet=<file>] [--daemon-address=<host>:<port>] [<COMMAND>]";
|
||||
success_msg_writer() << desc_all << '\n' << w.get_commands_str();
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -551,33 +817,104 @@ int main(int argc, char* argv[])
|
||||
|
||||
//set up logging options
|
||||
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::get_default_log_file().c_str(),
|
||||
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))
|
||||
{
|
||||
LOG_PRINT_L0("Setting log level = " << 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;
|
||||
}
|
||||
|
||||
r = w.init(vm);
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
|
||||
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);
|
||||
|
||||
std::vector<std::string> command = command_line::get_arg(vm, arg_command);
|
||||
if (!command.empty())
|
||||
w.process_command(command);
|
||||
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([&w] {
|
||||
w.stop();
|
||||
});
|
||||
w.run();
|
||||
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);
|
||||
CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet");
|
||||
|
||||
w.deinit();
|
||||
std::vector<std::string> command = command_line::get_arg(vm, arg_command);
|
||||
if (!command.empty())
|
||||
w.process_command(command);
|
||||
|
||||
tools::signal_handler::install([&w] {
|
||||
w.stop();
|
||||
});
|
||||
w.run();
|
||||
|
||||
w.deinit();
|
||||
}
|
||||
return 1;
|
||||
//CATCH_ENTRY_L0("main", 1);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ namespace cryptonote
|
||||
/************************************************************************/
|
||||
/* */
|
||||
/************************************************************************/
|
||||
class simple_wallet
|
||||
class simple_wallet : public tools::i_wallet2_callback
|
||||
{
|
||||
public:
|
||||
typedef std::vector<std::string> command_type;
|
||||
@ -35,7 +35,7 @@ namespace cryptonote
|
||||
bool process_command(const std::vector<std::string> &args);
|
||||
std::string get_commands_str();
|
||||
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();
|
||||
|
||||
@ -51,13 +51,72 @@ namespace cryptonote
|
||||
bool show_incoming_transfers(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 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 set_log(const std::vector<std::string> &args);
|
||||
|
||||
uint64_t get_daemon_blockchain_height(std::string& err);
|
||||
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_generate_new;
|
||||
std::string m_import_path;
|
||||
@ -70,5 +129,6 @@ namespace cryptonote
|
||||
|
||||
std::unique_ptr<tools::wallet2> m_wallet;
|
||||
net_utils::http::http_simple_client m_http_client;
|
||||
refresh_progress_reporter_t m_refresh_progress_reporter;
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
#define BUILD_COMMIT_ID "@VERSION@"
|
||||
#define PROJECT_VERSION "0.8.2"
|
||||
#define PROJECT_VERSION_BUILD_NO "284"
|
||||
#define PROJECT_VERSION "0.8.3"
|
||||
#define PROJECT_VERSION_BUILD_NO "288"
|
||||
#define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO "(" BUILD_COMMIT_ID ")"
|
||||
|
@ -25,25 +25,25 @@ using namespace cryptonote;
|
||||
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_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;
|
||||
uint64_t tx_money_got_in_outs = 0;
|
||||
crypto::public_key tx_pub_key = null_pkey;
|
||||
bool r = parse_and_validate_tx_extra(tx, tx_pub_key);
|
||||
fd.reason = fail_details::error_to_parse_tx_extra;
|
||||
CHECK_AND_ASSERT_MES(r && tx_pub_key != null_pkey, false, "process_new_transaction failed.");
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::tx_extra_parse_error, tx);
|
||||
|
||||
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_ASSERT_MES(r, false, "call lookup_acc_outs failed");
|
||||
if(outs.size() && tx_money_got_in_outs)
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
|
||||
|
||||
if(!outs.empty() && tx_money_got_in_outs)
|
||||
{
|
||||
//good news - got money! take care about it
|
||||
//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);
|
||||
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);
|
||||
if (!r) fd.reason = fail_details::error_not_connected;
|
||||
else if (CORE_RPC_STATUS_BUSY == res.status) fd.reason = fail_details::error_daemon_is_busy;
|
||||
else if (CORE_RPC_STATUS_OK != res.status) fd.reason = fail_details::error_internal_error;
|
||||
else fd.reason = fail_details::error_ok;
|
||||
if (fail_details::error_ok != fd.reason)
|
||||
{
|
||||
// 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());
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "get_o_indexes.bin");
|
||||
CHECK_AND_THROW_WALLET_EX(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin");
|
||||
CHECK_AND_THROW_WALLET_EX(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status);
|
||||
CHECK_AND_THROW_WALLET_EX(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error,
|
||||
"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()));
|
||||
|
||||
BOOST_FOREACH(size_t o, outs)
|
||||
{
|
||||
fd.reason = fail_details::error_invalid_tx;
|
||||
CHECK_AND_ASSERT_MES(o < tx.vout.size(), false, "wrong out in transaction: internal index=" << o << ", total_outs" << tx.vout.size());
|
||||
CHECK_AND_THROW_WALLET_EX(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
|
||||
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
|
||||
|
||||
m_transfers.push_back(boost::value_initialized<transfer_details>());
|
||||
transfer_details& td = m_transfers.back();
|
||||
td.m_block_height = height;
|
||||
@ -79,12 +72,13 @@ bool wallet2::process_new_transaction(cryptonote::transaction& tx, uint64_t heig
|
||||
td.m_spent = false;
|
||||
cryptonote::keypair in_ephemeral;
|
||||
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_ASSERT_MES(in_ephemeral.pub == boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key,
|
||||
false, "internal error: at key_image generating ephemeral public key not matched with output_key");
|
||||
CHECK_AND_THROW_WALLET_EX(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");
|
||||
|
||||
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_LEVEL_0, epee::log_space::console_color_green);
|
||||
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
|
||||
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)
|
||||
@ -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);
|
||||
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_LEVEL_0, epee::log_space::console_color_magenta);
|
||||
m_transfers[it->second].m_spent = true;
|
||||
LOG_PRINT_L0("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx));
|
||||
transfer_details& td = m_transfers[it->second];
|
||||
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
|
||||
fd.reason = fail_details::error_internal_error;
|
||||
CHECK_AND_ASSERT_MES(height == m_blockchain.size(), false, "internal error: current_index=" << height << ", m_blockchain.size()=" << m_blockchain.size());
|
||||
CHECK_AND_THROW_WALLET_EX(height != m_blockchain.size(), error::wallet_internal_error,
|
||||
"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
|
||||
if(b.timestamp + 60*60*24 > m_account.get_createtime())
|
||||
{
|
||||
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);
|
||||
CHECK_AND_NO_ASSERT_MES(r, false, "failed to process transaction");
|
||||
|
||||
TIME_MEASURE_START(txs_handle_time);
|
||||
BOOST_FOREACH(auto& txblob, bche.txs)
|
||||
{
|
||||
cryptonote::transaction tx;
|
||||
r = parse_and_validate_tx_from_blob(txblob, tx);
|
||||
fd.reason = fail_details::error_to_parse_tx;
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to parse and validate transaction from blob");
|
||||
r = process_new_transaction(tx, height, fd);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to process transaction");
|
||||
bool r = parse_and_validate_tx_from_blob(txblob, tx);
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::tx_parse_error, txblob);
|
||||
process_new_transaction(tx, height);
|
||||
}
|
||||
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");
|
||||
@ -134,16 +134,18 @@ bool wallet2::process_new_blockchain_entry(cryptonote::block& b, cryptonote::blo
|
||||
}
|
||||
m_blockchain.push_back(bl_id);
|
||||
++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 current_multiplier = 1;
|
||||
size_t sz = m_blockchain.size();
|
||||
if(!sz)
|
||||
return true;
|
||||
return;
|
||||
size_t current_back_offset = 1;
|
||||
bool genesis_included = false;
|
||||
while(current_back_offset < sz)
|
||||
@ -162,77 +164,68 @@ bool wallet2::get_short_chain_history(std::list<crypto::hash>& ids)
|
||||
}
|
||||
if(!genesis_included)
|
||||
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;
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
|
||||
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
|
||||
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);
|
||||
if (!r) fd.reason = fail_details::error_not_connected;
|
||||
else if (CORE_RPC_STATUS_BUSY == res.status) fd.reason = fail_details::error_daemon_is_busy;
|
||||
else if (CORE_RPC_STATUS_OK != res.status) fd.reason = fail_details::error_internal_error;
|
||||
else fd.reason = fail_details::error_ok;
|
||||
if (fail_details::error_ok != fd.reason)
|
||||
{
|
||||
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());
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "getblocks.bin");
|
||||
CHECK_AND_THROW_WALLET_EX(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
|
||||
CHECK_AND_THROW_WALLET_EX(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status);
|
||||
CHECK_AND_THROW_WALLET_EX(m_blockchain.size() <= res.start_height, error::wallet_internal_error,
|
||||
"wrong daemon response: m_start_height=" + std::to_string(res.start_height) +
|
||||
" not less than local blockchain size=" + std::to_string(m_blockchain.size()));
|
||||
|
||||
size_t current_index = res.start_height;
|
||||
BOOST_FOREACH(auto& bl_entry, res.blocks)
|
||||
{
|
||||
cryptonote::block bl;
|
||||
r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl);
|
||||
fd.reason = fail_details::error_to_parse_block;
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to parse/validate block");
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::block_parse_error, bl_entry.block);
|
||||
|
||||
crypto::hash bl_id = get_block_hash(bl);
|
||||
if(current_index >= m_blockchain.size())
|
||||
{
|
||||
r = process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, fd);
|
||||
if(!r) return false;
|
||||
process_new_blockchain_entry(bl, bl_entry, bl_id, current_index);
|
||||
++blocks_added;
|
||||
}else
|
||||
{
|
||||
if(bl_id != m_blockchain[current_index])
|
||||
{
|
||||
//split detected here !!!
|
||||
fd.reason = fail_details::error_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)
|
||||
<< "\nnot match with local block id " << string_tools::pod_to_hex(m_blockchain[current_index]));
|
||||
detach_blockchain(current_index);
|
||||
r = process_new_blockchain_entry(bl, bl_entry, bl_id, current_index, fd);
|
||||
if(!r) return false;
|
||||
}
|
||||
}
|
||||
else if(bl_id != m_blockchain[current_index])
|
||||
{
|
||||
//split detected here !!!
|
||||
CHECK_AND_THROW_WALLET_EX(current_index == res.start_height, error::wallet_internal_error,
|
||||
"wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) +
|
||||
" (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);
|
||||
process_new_blockchain_entry(bl, bl_entry, bl_id, current_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_PRINT_L2("Block is already in blockchain: " << string_tools::pod_to_hex(bl_id));
|
||||
}
|
||||
|
||||
++current_index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::refresh(fail_details& fd)
|
||||
void wallet2::refresh()
|
||||
{
|
||||
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;
|
||||
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;
|
||||
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))
|
||||
{
|
||||
bool res = pull_blocks(added_blocks, fd);
|
||||
if(!res)
|
||||
try
|
||||
{
|
||||
pull_blocks(added_blocks);
|
||||
blocks_fetched += added_blocks;
|
||||
if(!added_blocks)
|
||||
break;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
blocks_fetched += added_blocks;
|
||||
if(try_count < 3)
|
||||
{
|
||||
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");
|
||||
++try_count;
|
||||
continue;
|
||||
}else
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_PRINT_L1("pull_blocks failed, try_count=" << try_count);
|
||||
return false;
|
||||
LOG_ERROR("pull_blocks failed, try_count=" << try_count);
|
||||
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))
|
||||
received_money = true;
|
||||
|
||||
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);
|
||||
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++)
|
||||
{
|
||||
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);
|
||||
++transfers_detached;
|
||||
}
|
||||
@ -290,8 +299,6 @@ bool wallet2::detach_blockchain(uint64_t height)
|
||||
m_local_bc_height -= blocks_detached;
|
||||
|
||||
LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached);
|
||||
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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;
|
||||
std::string 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_ASSERT_MES(r, false, "failed to load wallet keys file: " << keys_file_name);
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::file_read_error, 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::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 = 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);
|
||||
if (!r)
|
||||
{
|
||||
LOG_ERROR("invalid password");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::invalid_password);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool wallet2::generate(const std::string& wallet_, const std::string& password)
|
||||
void wallet2::generate(const std::string& wallet_, const std::string& password)
|
||||
{
|
||||
clear();
|
||||
prepare_file_names(wallet_);
|
||||
boost::system::error_code e;
|
||||
if(boost::filesystem::exists(m_wallet_file, e) || boost::filesystem::exists(m_keys_file, e))
|
||||
{
|
||||
LOG_PRINT_RED_L0("failed to generate wallet, file already exist or wrong path: " << wallet_);
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
CHECK_AND_THROW_WALLET_EX(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
|
||||
|
||||
m_account.generate();
|
||||
m_account_public_address = m_account.get_keys().m_account_address;
|
||||
|
||||
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());
|
||||
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)
|
||||
@ -418,51 +420,46 @@ bool wallet2::check_connection()
|
||||
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();
|
||||
prepare_file_names(wallet_);
|
||||
|
||||
boost::system::error_code e;
|
||||
if(!boost::filesystem::exists(m_keys_file, e) || e)
|
||||
{
|
||||
LOG_PRINT_L0("file not found: " << m_keys_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = load_keys(m_keys_file, password);
|
||||
if (!r)
|
||||
return false;
|
||||
bool exists = boost::filesystem::exists(m_keys_file, e);
|
||||
CHECK_AND_THROW_WALLET_EX(e || !exists, error::file_not_found, m_keys_file);
|
||||
|
||||
load_keys(m_keys_file, password);
|
||||
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str());
|
||||
|
||||
//keys loaded ok!
|
||||
//try to load wallet file. but even if we failed, it is not big problem
|
||||
if(!boost::filesystem::exists(m_wallet_file, e) || e)
|
||||
{
|
||||
LOG_PRINT_L0("file not found: " << m_wallet_file << ", starting with empty blockchain");
|
||||
m_account_public_address = m_account.get_keys().m_account_address;
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
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_ASSERT_MES(m_account_public_address.m_spend_public_key == m_account.get_keys().m_account_address.m_spend_public_key &&
|
||||
m_account_public_address.m_view_public_key == m_account.get_keys().m_account_address.m_view_public_key,
|
||||
false, "addresses of wallet keys file and wallet data file are mismatched");
|
||||
if(!m_blockchain.size())
|
||||
bool r = tools::unserialize_obj_from_file(*this, m_wallet_file);
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::file_read_error, m_wallet_file);
|
||||
CHECK_AND_THROW_WALLET_EX(
|
||||
m_account_public_address.m_spend_public_key != m_account.get_keys().m_account_address.m_spend_public_key ||
|
||||
m_account_public_address.m_view_public_key != m_account.get_keys().m_account_address.m_view_public_key,
|
||||
error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
|
||||
|
||||
if(m_blockchain.empty())
|
||||
{
|
||||
cryptonote::block b;
|
||||
cryptonote::generate_genesis_block(b);
|
||||
m_blockchain.push_back(get_block_hash(b));
|
||||
}
|
||||
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);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to save wallet to file " << m_wallet_file);
|
||||
return r;
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::file_save_error, m_wallet_file);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
uint64_t wallet2::unlocked_balance()
|
||||
@ -482,13 +479,16 @@ uint64_t wallet2::balance()
|
||||
if(!td.m_spent)
|
||||
amount += td.amount();
|
||||
|
||||
|
||||
BOOST_FOREACH(auto& utx, m_unconfirmed_txs)
|
||||
amount+= utx.second.m_change;
|
||||
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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)
|
||||
void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount)
|
||||
{
|
||||
return transfer(dsts, fake_outputs_count, unlock_time, fee, detail::digit_split_strategy, tx_dust_policy(fee), tx);
|
||||
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;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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,
|
||||
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, tfd);
|
||||
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,
|
||||
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;
|
||||
return transfer(dsts, fake_outputs_count, unlock_time, fee, tx);
|
||||
transfer(dsts, fake_outputs_count, unlock_time, fee, tx);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <boost/serialization/list.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include "include_base_utils.h"
|
||||
#include "cryptonote_core/account.h"
|
||||
#include "cryptonote_core/account_boost_serialization.h"
|
||||
@ -20,38 +21,40 @@
|
||||
#include "crypto/chacha8.h"
|
||||
#include "crypto/hash.h"
|
||||
|
||||
#include "wallet_errors.h"
|
||||
|
||||
#define DEFAULT_TX_SPENDABLE_AGE 10
|
||||
#define WALLET_RCP_CONNECTION_TIMEOUT 200000
|
||||
|
||||
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;
|
||||
}
|
||||
public:
|
||||
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) {}
|
||||
};
|
||||
|
||||
struct tx_dust_policy
|
||||
{
|
||||
uint64_t dust_threshold;
|
||||
bool add_to_fee;
|
||||
cryptonote::account_public_address addr_for_dust;
|
||||
|
||||
tx_dust_policy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address())
|
||||
: dust_threshold(a_dust_threshold)
|
||||
, add_to_fee(an_add_to_fee)
|
||||
, addr_for_dust(an_addr_for_dust)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class wallet2
|
||||
{
|
||||
wallet2(const wallet2&) : m_run(true) {};
|
||||
wallet2(const wallet2&) : m_run(true), m_callback(0) {};
|
||||
public:
|
||||
wallet2() : m_run(true) {};
|
||||
wallet2() : m_run(true), m_callback(0) {};
|
||||
struct transfer_details
|
||||
{
|
||||
uint64_t m_block_height;
|
||||
@ -63,22 +66,16 @@ namespace tools
|
||||
|
||||
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 unconfirmed_transfer_details
|
||||
{
|
||||
uint64_t dust_threshold;
|
||||
bool add_to_fee;
|
||||
cryptonote::account_public_address addr_for_dust;
|
||||
|
||||
tx_dust_policy(uint64_t a_dust_threshold = 0, bool an_add_to_fee = true, cryptonote::account_public_address an_addr_for_dust = cryptonote::account_public_address())
|
||||
: dust_threshold(a_dust_threshold)
|
||||
, add_to_fee(an_add_to_fee)
|
||||
, addr_for_dust(an_addr_for_dust)
|
||||
{
|
||||
}
|
||||
cryptonote::transaction m_tx;
|
||||
uint64_t m_change;
|
||||
time_t m_sent_time;
|
||||
};
|
||||
|
||||
typedef std::vector<transfer_details> transfer_container;
|
||||
|
||||
struct keys_file_data
|
||||
{
|
||||
crypto::chacha8_iv iv;
|
||||
@ -90,77 +87,34 @@ namespace tools
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct fail_details
|
||||
{
|
||||
enum fail_reason
|
||||
{
|
||||
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();
|
||||
void generate(const std::string& wallet, const std::string& password);
|
||||
void load(const std::string& wallet, const std::string& password);
|
||||
void store();
|
||||
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);
|
||||
|
||||
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);
|
||||
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 deinit();
|
||||
|
||||
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 unlocked_balance();
|
||||
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>
|
||||
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);
|
||||
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, fail_details& tfd);
|
||||
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);
|
||||
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);
|
||||
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee);
|
||||
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);
|
||||
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; }
|
||||
template <class t_archive>
|
||||
inline void serialize(t_archive &a, const unsigned int ver)
|
||||
@ -171,21 +125,26 @@ namespace tools
|
||||
a & m_transfers;
|
||||
a & m_account_public_address;
|
||||
a & m_key_images;
|
||||
if(ver < 6)
|
||||
return;
|
||||
a & m_unconfirmed_txs;
|
||||
}
|
||||
|
||||
private:
|
||||
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);
|
||||
bool process_new_transaction(cryptonote::transaction& tx, uint64_t height, fail_details& fd);
|
||||
bool process_new_blockchain_entry(cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height, fail_details& fd);
|
||||
bool detach_blockchain(uint64_t height);
|
||||
bool get_short_chain_history(std::list<crypto::hash>& ids);
|
||||
void load_keys(const std::string& keys_file_name, const std::string& password);
|
||||
void process_new_transaction(const cryptonote::transaction& tx, uint64_t height);
|
||||
void process_new_blockchain_entry(const cryptonote::block& b, cryptonote::block_complete_entry& bche, crypto::hash& bl_id, uint64_t height);
|
||||
void detach_blockchain(uint64_t height);
|
||||
void get_short_chain_history(std::list<crypto::hash>& ids);
|
||||
bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
|
||||
bool is_transfer_unlocked(const transfer_details& td) const;
|
||||
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);
|
||||
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;
|
||||
std::string m_daemon_address;
|
||||
@ -194,6 +153,7 @@ namespace tools
|
||||
epee::net_utils::http::http_simple_client m_http_client;
|
||||
std::vector<crypto::hash> m_blockchain;
|
||||
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;
|
||||
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
|
||||
|
||||
std::atomic<bool> m_run;
|
||||
|
||||
i_wallet2_callback* m_callback;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 5)
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 6)
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@ -219,6 +181,16 @@ namespace boost
|
||||
a & x.m_spent;
|
||||
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>
|
||||
bool wallet2::enum_incoming_transfers(const T& handler) const
|
||||
{
|
||||
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,
|
||||
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;
|
||||
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>
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
uint64_t needed_money = fee;
|
||||
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;
|
||||
CHECK_AND_THROW_WALLET_EX(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee);
|
||||
}
|
||||
|
||||
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);
|
||||
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 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
|
||||
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 = "
|
||||
<< it->m_internal_output_index << " more than " << it->m_tx.vout.size());
|
||||
CHECK_AND_THROW_WALLET_EX(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
|
||||
"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());
|
||||
}
|
||||
|
||||
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;
|
||||
else if (CORE_RPC_STATUS_BUSY == daemon_resp.status) tfd.reason = fail_details::error_daemon_is_busy;
|
||||
else if (CORE_RPC_STATUS_OK != daemon_resp.status) tfd.reason = fail_details::error_internal_error;
|
||||
else tfd.reason = fail_details::error_ok;
|
||||
if (fail_details::error_ok != tfd.reason)
|
||||
{
|
||||
LOG_PRINT_L0("failed to invoke getrandom_outs.bin: " << interpret_rpc_response(r, daemon_resp.status));
|
||||
return false;
|
||||
}
|
||||
CHECK_AND_THROW_WALLET_EX(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
CHECK_AND_THROW_WALLET_EX(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
CHECK_AND_THROW_WALLET_EX(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
|
||||
CHECK_AND_THROW_WALLET_EX(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error,
|
||||
"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()));
|
||||
|
||||
tfd.reason = fail_details::error_internal_error;
|
||||
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;
|
||||
std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_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;
|
||||
LOG_PRINT_L0("not enough outputs to mix output " << print_money(amount_outs.amount) << ", requested " <<
|
||||
fake_outputs_count << ", found " << amount_outs.outs.size());
|
||||
scanty_outs.push_back(amount_outs);
|
||||
}
|
||||
}
|
||||
if (fail_details::error_ok != tfd.reason)
|
||||
return false;
|
||||
CHECK_AND_THROW_WALLET_EX(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
|
||||
}
|
||||
tfd.reason = fail_details::error_ok;
|
||||
|
||||
//prepare inputs
|
||||
size_t i = 0;
|
||||
@ -443,60 +373,36 @@ namespace tools
|
||||
uint64_t dust = 0;
|
||||
std::vector<cryptonote::tx_destination_entry> splitted_dsts;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Transaction construction failed");
|
||||
|
||||
//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;
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
key_images += boost::to_string(in.k_image) + " ";
|
||||
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 << "]");
|
||||
|
||||
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
|
||||
@ -507,8 +413,5 @@ namespace tools
|
||||
<< "Balance: " << print_money(balance()) << ENDL
|
||||
<< "Unlocked: " << print_money(unlocked_balance()) << ENDL
|
||||
<< "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
611
src/wallet/wallet_errors.h
Normal 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__); \
|
||||
}
|
132
src/wallet/wallet_rpc_server.cpp
Normal file
132
src/wallet/wallet_rpc_server.cpp
Normal 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;
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
}
|
55
src/wallet/wallet_rpc_server.h
Normal file
55
src/wallet/wallet_rpc_server.h
Normal 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;
|
||||
};
|
||||
}
|
91
src/wallet/wallet_rpc_server_commans_defs.h
Normal file
91
src/wallet/wallet_rpc_server_commans_defs.h
Normal 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()
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
11
src/wallet/wallet_rpc_server_error_codes.h
Normal file
11
src/wallet/wallet_rpc_server_error_codes.h
Normal 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
|
@ -16,7 +16,7 @@ namespace
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
size_t current_size = get_object_blobsize(miner_tx);
|
||||
|
@ -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)
|
||||
{
|
||||
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx);
|
||||
bool block_too_big;
|
||||
uint64_t block_reward = get_block_reward(block_sizes, block_size, block_too_big, already_generated_coins);
|
||||
uint64_t block_reward;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
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;
|
||||
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -496,14 +496,13 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
|
||||
tx.vin.push_back(in);
|
||||
|
||||
// This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
|
||||
std::vector<size_t> block_sizes;
|
||||
bool block_too_big = false;
|
||||
uint64_t block_reward = get_block_reward(block_sizes, 0, block_too_big, already_generated_coins) + fee;
|
||||
if (block_too_big)
|
||||
uint64_t block_reward;
|
||||
if (!get_block_reward(0, 0, already_generated_coins, block_reward))
|
||||
{
|
||||
LOG_PRINT_L0("Block is too big");
|
||||
return false;
|
||||
}
|
||||
block_reward += fee;
|
||||
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "cryptonote_core/cryptonote_basic_impl.h"
|
||||
#include "cryptonote_core/account.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
#include "misc_language.h"
|
||||
|
||||
using namespace cryptonote;
|
||||
|
||||
@ -35,19 +36,18 @@ bool test_transaction_generation_and_ring_signature()
|
||||
rv_acc.generate();
|
||||
account_base rv_acc2;
|
||||
rv_acc2.generate();
|
||||
std::vector<size_t> b;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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
|
||||
typedef tx_source_entry::output_entry tx_output_entry;
|
||||
@ -111,7 +111,7 @@ bool test_transaction_generation_and_ring_signature()
|
||||
|
||||
std::vector<size_t> outs;
|
||||
uint64_t money = 0;
|
||||
|
||||
|
||||
r = lookup_acc_outs(rv_acc.get_keys(), tx_rc1, get_tx_pub_key_from_extra(tx_rc1), outs, money);
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to lookup_acc_outs");
|
||||
CHECK_AND_ASSERT_MES(td.amount == money, false, "wrong money amount in new transaction");
|
||||
@ -130,7 +130,7 @@ bool test_block_creation()
|
||||
bool r = get_account_address_from_str(adr, "0099be99c70ef10fd534c43c88e9d13d1c8853213df7e362afbec0e4ee6fec4948d0c190b58f4b356cd7feaf8d9d0a76e7c7e5a9a0a497a6b1faf7a765882dd08ac2");
|
||||
CHECK_AND_ASSERT_MES(r, false, "failed to import");
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "cryptonote_config.h"
|
||||
#include "cryptonote_core/difficulty.h"
|
||||
|
@ -50,7 +50,15 @@ bool do_send_money(tools::wallet2& w1, tools::wallet2& w2, size_t mix_in_factor,
|
||||
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)
|
||||
@ -84,36 +92,30 @@ bool transactions_flow_test(std::string& working_folder,
|
||||
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;
|
||||
}
|
||||
|
||||
if(!w2.generate(working_folder + "/" + path_terget_wallet, ""))
|
||||
{
|
||||
LOG_ERROR( "failed to generate target load wallet from " << path_source_wallet );
|
||||
return false;
|
||||
}
|
||||
w1.init(daemon_addr_a);
|
||||
|
||||
if(!w1.init(daemon_addr_a))
|
||||
{
|
||||
LOG_ERROR("failed to init source wallet from " << daemon_addr_a );
|
||||
return false;
|
||||
}
|
||||
size_t blocks_fetched = 0;
|
||||
tools::wallet2::fail_details fd;
|
||||
if(!w1.refresh(blocks_fetched, fd))
|
||||
bool received_money;
|
||||
bool ok;
|
||||
if(!w1.refresh(blocks_fetched, received_money, ok))
|
||||
{
|
||||
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!w2.init(daemon_addr_b))
|
||||
{
|
||||
LOG_ERROR( "failed to init target wallet from " << daemon_addr_b );
|
||||
return false;
|
||||
}
|
||||
w2.init(daemon_addr_b);
|
||||
|
||||
LOG_PRINT_GREEN("Using wallets: " << 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);
|
||||
@ -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);
|
||||
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::response daemon_rsp = AUTO_VAL_INIT(daemon_rsp);
|
||||
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");
|
||||
|
||||
//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)
|
||||
{
|
||||
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
|
||||
@ -166,7 +167,7 @@ bool transactions_flow_test(std::string& working_folder,
|
||||
}else
|
||||
{
|
||||
misc_utils::sleep_no_w(1000);
|
||||
w1.refresh(fd);
|
||||
w1.refresh(blocks_fetched, received_money, ok);
|
||||
}
|
||||
}
|
||||
//do actual transfer
|
||||
@ -188,7 +189,7 @@ bool transactions_flow_test(std::string& working_folder,
|
||||
{
|
||||
misc_utils::sleep_no_w(1000);
|
||||
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;
|
||||
@ -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))
|
||||
{
|
||||
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))
|
||||
{
|
||||
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
|
||||
LOG_PRINT_L0( "refreshing...");
|
||||
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
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ namespace
|
||||
if (predicate())
|
||||
return true;
|
||||
//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;
|
||||
}
|
||||
|
@ -29,8 +29,7 @@ public:
|
||||
{
|
||||
m_miners[i].generate();
|
||||
|
||||
std::vector<size_t> block_sizes;
|
||||
if (!construct_miner_tx(0, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i], 0, block_sizes, 2))
|
||||
if (!construct_miner_tx(0, 0, 0, 2, 0, m_miners[i].get_keys().m_account_address, m_miner_txs[i]))
|
||||
return false;
|
||||
|
||||
txout_to_key tx_out = boost::get<txout_to_key>(m_miner_txs[i].vout[0].target);
|
||||
|
@ -17,8 +17,7 @@ public:
|
||||
|
||||
m_bob.generate();
|
||||
|
||||
std::vector<size_t> block_sizes;
|
||||
if (!construct_miner_tx(0, 0, m_bob.get_keys().m_account_address, m_tx, 0, block_sizes, 2))
|
||||
if (!construct_miner_tx(0, 0, 0, 2, 0, m_bob.get_keys().m_account_address, m_tx))
|
||||
return false;
|
||||
|
||||
m_tx_pub_key = get_tx_pub_key_from_extra(m_tx);
|
||||
|
@ -16,14 +16,13 @@ namespace
|
||||
protected:
|
||||
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_too_big;
|
||||
bool m_block_not_too_big;
|
||||
uint64_t m_block_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); \
|
||||
ASSERT_FALSE(m_block_too_big); \
|
||||
#define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \
|
||||
m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward); \
|
||||
ASSERT_TRUE(m_block_not_too_big); \
|
||||
ASSERT_EQ(m_block_reward, UINT64_C(expected_reward));
|
||||
|
||||
TEST_F(block_reward_and_already_generated_coins, handles_first_values)
|
||||
@ -53,72 +52,70 @@ namespace
|
||||
protected:
|
||||
virtual void SetUp()
|
||||
{
|
||||
m_standard_block_reward = get_block_reward(m_last_block_sizes, 0, m_block_too_big, already_generated_coins);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
m_block_not_too_big = get_block_reward(0, 0, already_generated_coins, m_standard_block_reward);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
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;
|
||||
|
||||
std::vector<size_t> m_last_block_sizes;
|
||||
bool m_block_too_big;
|
||||
bool m_block_not_too_big;
|
||||
uint64_t m_block_reward;
|
||||
uint64_t m_standard_block_reward;
|
||||
};
|
||||
|
||||
TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level)
|
||||
{
|
||||
do_test(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_EQ(m_block_reward, m_standard_block_reward);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level)
|
||||
{
|
||||
do_test(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_EQ(m_block_reward, m_standard_block_reward);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level)
|
||||
{
|
||||
do_test(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_LT(m_block_reward, m_standard_block_reward);
|
||||
}
|
||||
|
||||
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);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE - 1);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_LT(m_block_reward, m_standard_block_reward);
|
||||
ASSERT_LT(0, m_block_reward);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level)
|
||||
{
|
||||
do_test(2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE);
|
||||
ASSERT_TRUE(m_block_not_too_big);
|
||||
ASSERT_EQ(0, m_block_reward);
|
||||
}
|
||||
|
||||
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);
|
||||
ASSERT_TRUE(m_block_too_big);
|
||||
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE + 1);
|
||||
ASSERT_FALSE(m_block_not_too_big);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size)
|
||||
{
|
||||
#if !defined(NDEBUG)
|
||||
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 + 1), "");
|
||||
ASSERT_DEATH(do_test(huge_size, huge_size + 1), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -126,8 +123,7 @@ namespace
|
||||
{
|
||||
#if !defined(NDEBUG)
|
||||
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), "");
|
||||
ASSERT_DEATH(do_test(huge_size - 2, huge_size), "");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -145,21 +141,21 @@ namespace
|
||||
|
||||
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);
|
||||
ASSERT_FALSE(m_block_too_big);
|
||||
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_TRUE(m_block_not_too_big);
|
||||
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE, m_standard_block_reward);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
std::vector<size_t> m_last_block_sizes;
|
||||
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_standard_block_reward;
|
||||
};
|
||||
@ -167,28 +163,28 @@ namespace
|
||||
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_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);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians)
|
||||
{
|
||||
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(0, m_block_reward);
|
||||
}
|
||||
@ -196,14 +192,14 @@ namespace
|
||||
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians)
|
||||
{
|
||||
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)
|
||||
@ -211,16 +207,16 @@ namespace
|
||||
ASSERT_EQ(0, m_last_block_sizes_median % 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);
|
||||
|
||||
// 3/2 = 12/8
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ TEST_pos(uint16_t, 0, "0");
|
||||
TEST_pos(uint16_t, 1, "1");
|
||||
TEST_pos(uint16_t, 65535, "65535");
|
||||
|
||||
TEST_neg(uint16_t, "");
|
||||
TEST_neg(uint16_t, "+0");
|
||||
TEST_neg(uint16_t, "+1");
|
||||
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, 4294967295, "4294967295");
|
||||
|
||||
TEST_neg(uint32_t, "");
|
||||
TEST_neg(uint32_t, "+0");
|
||||
TEST_neg(uint32_t, "+1");
|
||||
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, 18446744073709551615ULL, "18446744073709551615");
|
||||
|
||||
TEST_neg(uint64_t, "");
|
||||
TEST_neg(uint64_t, "+0");
|
||||
TEST_neg(uint64_t, "+1");
|
||||
TEST_neg(uint64_t, "+18446744073709551615");
|
||||
|
124
tests/unit_tests/parse_amount.cpp
Normal file
124
tests/unit_tests/parse_amount.cpp
Normal 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);
|
@ -7,30 +7,28 @@
|
||||
#include "common/util.h"
|
||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||
|
||||
TEST(parse_and_validate_tx_extra, is_correct_parse_and_validate_tx_extra)
|
||||
TEST(parse_and_validate_tx_extra, is_correct_parse_and_validate_tx_extra)
|
||||
{
|
||||
cryptonote::transaction tx = AUTO_VAL_INIT(tx);
|
||||
cryptonote::account_base acc;
|
||||
acc.generate();
|
||||
std::vector<size_t> bss;
|
||||
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);
|
||||
crypto::public_key tx_pub_key;
|
||||
r = cryptonote::parse_and_validate_tx_extra(tx, tx_pub_key);
|
||||
ASSERT_TRUE(r);
|
||||
}
|
||||
TEST(parse_and_validate_tx_extra, is_correct_extranonce_too_big)
|
||||
TEST(parse_and_validate_tx_extra, is_correct_extranonce_too_big)
|
||||
{
|
||||
cryptonote::transaction tx = AUTO_VAL_INIT(tx);
|
||||
cryptonote::account_base acc;
|
||||
acc.generate();
|
||||
std::vector<size_t> bss;
|
||||
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);
|
||||
}
|
||||
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)
|
||||
{
|
||||
cryptonote::transaction tx = AUTO_VAL_INIT(tx);
|
||||
tx.extra.resize(20, 0);
|
||||
@ -75,7 +73,7 @@ TEST(validate_parse_amount_case, validate_parse_amount)
|
||||
r = cryptonote::parse_amount(res, " 100.0001 ");
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(res, 10000010000);
|
||||
|
||||
|
||||
r = cryptonote::parse_amount(res, " 100.0000 ");
|
||||
ASSERT_TRUE(r);
|
||||
ASSERT_EQ(res, 10000000000);
|
||||
@ -88,10 +86,10 @@ TEST(validate_parse_amount_case, validate_parse_amount)
|
||||
|
||||
r = cryptonote::parse_amount(res, "100 . 0000");
|
||||
ASSERT_FALSE(r);
|
||||
|
||||
|
||||
r = cryptonote::parse_amount(res, "100.00 00");
|
||||
ASSERT_FALSE(r);
|
||||
|
||||
|
||||
r = cryptonote::parse_amount(res, "1 00.00 00");
|
||||
ASSERT_FALSE(r);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user