Dev v0.4 #123

Merged
jwinterm merged 74 commits from dev-v0.4 into master 6 years ago

@ -95,7 +95,8 @@ Dates are provided in the format YYYY-MM-DD.
| ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- |
| 1 | 2018-04-01 | v7 | v0.1.0.0 | v0.1.0.0 | Cryptonight variant 1, ringsize >= 8, sorted inputs
| 6969 | 2018-04-24 | v8 | v0.2.0.0 | v0.2.0.0 | Bulletproofs, LWMA difficulty algorithm, ringsize >= 10, reduce unlock to 4
| 53666 | 2018-10-06 | v9 | v0.3.0.0 | v0.3.0.1 | Cryptonight variant 2, LWMA v2, ringsize = 22, XXX
| 53666 | 2018-10-06 | v9 | v0.3.0.0 | v0.3.1.3 | Cryptonight variant 2, LWMA v2, ringsize = 22, MMS
| 63469 | 2018-11-11 | v10 | v0.4.0.0 | v0.4.0.0 | LWMA v4
X's indicate that these details have not been determined as of commit date.

@ -122,6 +122,7 @@
if(!ps.load_from_json(query_info.m_body)) \
{ \
boost::value_initialized<epee::json_rpc::error_response> rsp; \
static_cast<epee::json_rpc::error_response&>(rsp).jsonrpc = "2.0"; \
static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \
static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \

@ -35,6 +35,11 @@
namespace epee
{
namespace
{
template<class C> void hint_resize(C &container, size_t size) {}
template<class C> void hint_resize(std::vector<C> &container, size_t size) { container.reserve(size); }
}
namespace serialization
{
@ -158,6 +163,7 @@ namespace epee
false,
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type));
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
hint_resize(container, count);
for(size_t i = 0; i < count; i++)
container.insert(container.end(), *(pelem++));
}

@ -28,6 +28,8 @@
#pragma once
#include <algorithm>
namespace epee
{
namespace misc_utils
@ -36,8 +38,12 @@ namespace misc_utils
{
inline std::string transform_to_escape_sequence(const std::string& src)
{
//std::stringstream res;
static const char escaped[] = "\b\f\n\r\t\v\"\\/";
if (std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)) == src.end())
return src;
std::string res;
res.reserve(2 * src.size());
for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it)
{
switch(*it)
@ -84,6 +90,7 @@ namespace misc_utils
inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
{
val.clear();
val.reserve(std::distance(star_end_string, buf_end));
bool escape_mode = false;
std::string::const_iterator it = star_end_string;
++it;

Binary file not shown.

@ -185,7 +185,29 @@ namespace cryptonote
ADD_CHECKPOINT(20000, "52cc7edcb49eb02f28a653b824089a726f4050eb210263ee6f4180d388a1e5cc");
ADD_CHECKPOINT(30000, "d22fde5dd240ade16d3250eb0aa5d1c16dc7cb51c20484e05eb274911032b3fa");
ADD_CHECKPOINT(40000, "aee0d642322542ba069cb1c58ab2acd3560f108d4682c3dc3cb15a54d442d91f");
ADD_CHECKPOINT(50000, "5286ac2a0f39b3aefcba363cd71f2760bd1e0d763cbc81026ebdc3f80a86541f");
ADD_CHECKPOINT(50000, "5286ac2a0f39b3aefcba363cd71f2760bd1e0d763cbc81026ebdc3f80a86541f");
ADD_CHECKPOINT(53666, "3f43f56f66ef0c43cf2fd14d0d28fa2aae0ef8f40716773511345750770f1255"); //Hard fork to v9
ADD_CHECKPOINT(54500, "8ed3078b389c2b44add007803d741b58d3fbed2e1ba4139bda702152d8773c9b");
ADD_CHECKPOINT(55000, "4b662ceccefc3247edb4d654dd610b8fb496e85b88a5de43cc2bdd28171b15ff");
ADD_CHECKPOINT(57000, "08a79f09f12bb5d230b63963356a760d51618e526cfc636047a6f3798217c177");
ADD_CHECKPOINT(59000, "180b51ee2c5fbcd4362eb7a29df9422481310dd77d10bccdf8930724c31e007e");
ADD_CHECKPOINT(59900, "18cc0653ef39cb304c68045dba5eb6b885f936281cd939dea04d0e6c9cd4ae2e");
ADD_CHECKPOINT(60000, "0f02aa57a63f79f63dafed9063abe228a37cb19f00430dc3168b8a8f4ae8016c");
ADD_CHECKPOINT(61000, "509aca8c54eb5fe44623768757b6e890ae39d512478c75f614cbff3d91809350");
ADD_CHECKPOINT(62000, "7fe91ad256c08dbd961e04738968be22fb481093fbfa7959bde7796ccceba0e2");
ADD_CHECKPOINT(62150, "1a7c75f8ebeda0e20eb5877181eafd7db0fc887e3fed43e0b27ab2e7bccafd10");
ADD_CHECKPOINT(62269, "4969555d60742afb93925fd96d83ac28f45e6e3c0e583c9fb3c92d9b2100d38f");
ADD_CHECKPOINT(62405, "4d0ae890cf9f875f231c7069508ad28dc429d14814b52db114dfab7519a27584");
ADD_CHECKPOINT(62419, "bd8bf5ac4c4fb07ab4d0d492bd1699def5c095ab6943ad3b63a89d1d8b1ce748");
ADD_CHECKPOINT(62425, "41a922dba6f3906871b2ccaf31ec9c91033470c503959093dae796deda8940ea");
ADD_CHECKPOINT(62479, "a2e8ff4205ba2980eb70921b0b21b5fc656ee273664ea94b860c68ca069b60dd");
ADD_CHECKPOINT(62503, "25fa115962988b4b8f8cfd22744a3e653b22ead8c8468e64caf334fc75a97d08");
ADD_CHECKPOINT(62550, "bde522a8a81c392c98c979434aa1dd9d20b4ca52230ba6ae0362872757808a48");
ADD_CHECKPOINT(62629, "8368e1ce1d421f1fc969364558433e2b2363d0ffcb5f2d946633095e3e6734f5");
ADD_CHECKPOINT(62720, "f871cddd75951e2fe24c282d2bd28396fc922ea519b354ace992a0162cb333ff");
ADD_CHECKPOINT(62733, "8331dbeeaf23173d2235a062373a437befadb6492cceb7640127bf18653a9e61");
ADD_CHECKPOINT(62877, "62d44adc05d7d4fd9d15239c5575612207beab0bcf2da49158bf89e365441ca1");
return true;
}

@ -201,15 +201,25 @@ namespace cryptonote
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation));
}
std::vector<crypto::key_derivation> additional_recv_derivations;
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
{
crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation);
r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
additional_recv_derivations.push_back(additional_recv_derivation);
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
}
else
{
additional_recv_derivations.push_back(additional_recv_derivation);
}
}
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);

@ -256,13 +256,9 @@ namespace cryptonote {
assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= static_cast<uint64_t>(N+1) );
if ( cryptonote::MAINNET && height <= DIFFICULTY_HEIGHT ){
/*if ( height <= DIFFICULTY_HEIGHT ){
return static_cast<uint64_t>(DIFFICULTY_GUESS);
}
if ( cryptonote::TESTNET && height <= DIFFICULTY_TESTNET_HEIGHT ){
return static_cast<uint64_t>(DIFFICULTY_TESTNET_GUESS);
}
}*/
for ( int64_t i = 1; i <= N; i++ ) {
ST = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i-1]);
@ -281,14 +277,79 @@ namespace cryptonote {
next_D = std::max(next_D,(prev_D*108)/100);
}
if ( cryptonote::MAINNET && next_D < DIFFICULTY_MINIMUM ) {
if ( next_D < DIFFICULTY_MINIMUM ) {
return static_cast<uint64_t>(DIFFICULTY_MINIMUM);
}
else if ( cryptonote::TESTNET && next_D < DIFFICULTY_TESTNET_MINIMUM ) {
return static_cast<uint64_t>(DIFFICULTY_TESTNET_MINIMUM);
}
else {
return static_cast<uint64_t>(next_D);
}
}
// LWMA-4 difficulty algorithm
// Copyright (c) 2017-2018 Zawy, MIT License
// https://github.com/zawy12/difficulty-algorithms/issues/3
difficulty_type next_difficulty_v4(std::vector<uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t height) {
uint64_t T = DIFFICULTY_TARGET_V2;
uint64_t N = DIFFICULTY_WINDOW_V2; // N=45, 60, and 90 for T=600, 120, 60.
uint64_t L(0), ST(0), next_D, prev_D, avg_D, i;
assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= N+1 );
if ( height <= DIFFICULTY_HEIGHT + 1 ) { return DIFFICULTY_GUESS; }
// Safely convert out-of-sequence timestamps into > 0 solvetimes.
std::vector<uint64_t>TS(N+1);
TS[0] = timestamps[0];
for ( i = 1; i <= N; i++) {
if ( timestamps[i] > TS[i-1] ) { TS[i] = timestamps[i]; }
else { TS[i] = TS[i-1]; }
}
for ( i = 1; i <= N; i++) {
// Temper long solvetime drops if they were preceded by 3 or 6 fast solves.
if ( i > 4 && TS[i]-TS[i-1] > 5*T && TS[i-1] - TS[i-4] < (14*T)/10 ) { ST = 2*T; }
else if ( i > 7 && TS[i]-TS[i-1] > 5*T && TS[i-1] - TS[i-7] < 4*T ) { ST = 2*T; }
else { // Assume normal conditions, so get ST.
// LWMA drops too much from long ST, so limit drops with a 5*T limit
ST = std::min(5*T ,TS[i] - TS[i-1]);
}
L += ST * i ;
}
if (L < N*N*T/20 ) { L = N*N*T/20; }
avg_D = ( cumulative_difficulties[N] - cumulative_difficulties[0] )/ N;
// Prevent round off error for small D and overflow for large D.
if (avg_D > 2000000*N*N*T) {
next_D = (avg_D/(200*L))*(N*(N+1)*T*97);
}
else { next_D = (avg_D*N*(N+1)*T*97)/(200*L); }
prev_D = cumulative_difficulties[N] - cumulative_difficulties[N-1] ;
// Apply 10% jump rule.
if ( ( TS[N] - TS[N-1] < (2*T)/10 ) ||
( TS[N] - TS[N-2] < (5*T)/10 ) ||
( TS[N] - TS[N-3] < (8*T)/10 ) )
{
next_D = std::max( next_D, std::min( (prev_D*110)/100, (105*avg_D)/100 ) );
}
// Make all insignificant digits zero for easy reading.
i = 1000000000;
while (i > 1) {
if ( next_D > i*100 ) { next_D = ((next_D+i/2)/i)*i; break; }
else { i /= 10; }
}
// Make least 3 digits equal avg of past 10 solvetimes.
if ( next_D > 100000 ) {
next_D = ((next_D+500)/1000)*1000 + std::min(static_cast<uint64_t>(999), (TS[N]-TS[N-10])/10);
}
if ( next_D < DIFFICULTY_MINIMUM ) {
return static_cast<uint64_t>(DIFFICULTY_MINIMUM);
}
else {
return static_cast<uint64_t>(next_D);
}
}
}

@ -55,4 +55,5 @@ namespace cryptonote
difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v2(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v3(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t height);
difficulty_type next_difficulty_v4(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t height);
}

@ -48,6 +48,7 @@
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 4
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 11
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60
// MONEY_SUPPLY - total number coins to be generated
@ -81,12 +82,9 @@
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
#define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + 1 // added +1 to make N=N
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG
#define DIFFICULTY_HEIGHT 53666 // v9 fork height
#define DIFFICULTY_GUESS 40000000 // difficulty at fork 40m
#define DIFFICULTY_MINIMUM 10000000 // minimum difficulty set to 10m
#define DIFFICULTY_TESTNET_HEIGHT 100
#define DIFFICULTY_TESTNET_GUESS 5069
#define DIFFICULTY_TESTNET_MINIMUM 4069
#define DIFFICULTY_HEIGHT 63469 // v10 fork height
#define DIFFICULTY_GUESS 100000069 // difficulty at fork 100m
#define DIFFICULTY_MINIMUM 40000069 // minimum difficulty set to 40m
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 DIFFICULTY_TARGET_V1 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 DIFFICULTY_TARGET_V2 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS

@ -93,6 +93,7 @@ static const struct {
{ 7, 1, 0, 1519605000 },
{ 8, 6969, 0, 1524214739 },
{ 9, 53666, 0, 1538689773 },
{ 10, 63469, 0, 1541700352 },
};
static const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)(0));
@ -838,8 +839,11 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
else if (version == 8) {
return next_difficulty_v2(timestamps, difficulties, target);
}
else {
else if (version == 9) {
return next_difficulty_v3(timestamps, difficulties, height);
}
else {
return next_difficulty_v4(timestamps, difficulties, height);
}
}
//------------------------------------------------------------------
@ -1056,9 +1060,12 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
}
else if (version == 8) {
return next_difficulty_v2(timestamps, cumulative_difficulties, target);
}
else {
}
else if (version == 9) {
return next_difficulty_v3(timestamps, cumulative_difficulties, height);
}
else {
return next_difficulty_v4(timestamps, cumulative_difficulties, height);
}
}
@ -1350,12 +1357,13 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
{
LOG_PRINT_L3("Blockchain::" << __func__);
if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
const uint8_t hf_version = m_hardfork->get_current_version();
size_t blockchain_timestamp_check_window = hf_version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
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();
size_t need_elements = blockchain_timestamp_check_window - timestamps.size();
CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height());
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0;
while (start_top_height != stop_offset)
@ -3203,10 +3211,12 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
{
LOG_PRINT_L3("Blockchain::" << __func__);
median_ts = epee::misc_utils::median(timestamps);
const uint8_t hf_version = m_hardfork->get_current_version();
size_t blockchain_timestamp_check_window = hf_version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
if(b.timestamp < median_ts)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts);
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << blockchain_timestamp_check_window << " blocks, " << median_ts);
return false;
}
@ -3224,6 +3234,8 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
{
LOG_PRINT_L3("Blockchain::" << __func__);
uint64_t cryptonote_block_future_time_limit = get_current_hard_fork_version() >= 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT;
const uint8_t hf_version = m_hardfork->get_current_version();
size_t blockchain_timestamp_check_window = hf_version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
if(b.timestamp > get_adjusted_time() + cryptonote_block_future_time_limit)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 10 minutes");
@ -3231,7 +3243,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
}
// if not enough blocks, no proper median yet, return true
if(m_db->height() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
if(m_db->height() < blockchain_timestamp_check_window)
{
return true;
}
@ -3240,7 +3252,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
auto h = m_db->height();
// need most recent 60 blocks, get index of first of those
size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
size_t offset = h - blockchain_timestamp_check_window;
for(;offset < h; ++offset)
{
timestamps.push_back(m_db->get_block_timestamp(offset));
@ -4441,7 +4453,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "16cb7e839284c1910925f8d9f8a752f38ad908ae9deec7afdfbbc4a777f5ef2e";
static const char expected_block_hashes_hash[] = "e06ddfd59a891d5e63e80795d8e0494a7c1e3b01a1faa4ed91b015ecfc38c003";
void Blockchain::load_compiled_in_block_hashes()
{
const bool testnet = m_nettype == TESTNET;

@ -1344,16 +1344,24 @@ namespace cryptonote
main_message = "The daemon is running offline and will not attempt to sync to the Monero network.";
else
main_message = "The daemon will start synchronizing with the network. This may take a long time to complete.";
MGINFO_BLUE(ENDL <<
MGINFO_GREEN(ENDL <<
"\n \n"
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░▄█▄░░░░████▄░████▄░█░░░░░░░░▄█▄░░░░██░░░░▄▀░░▄███▄░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░█▀ ▀▄░░█░░░█░█░░░█░█░░░░░░░░█▀ ▀▄░░█ █░░▄▀░░░█▀░░░▀░░░░░░░░░░░\n"
"░░░░░░░░░░░░░█░░░░░░█░░░█░█░░░█░█░░░░░░░░█░░░░░░█▄▄█ █░▀▄░██▄▄░░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░█▄░░▄▀ ▀████░▀████░███▄░░░░░█▄░░▄▀ █░░█ █░░░█░█▄░░░▄▀░░░░░░░░░\n"
"░░░░░░░░░░░░░▀███▀░░░░░░░░░░░░░░░░░░▀░░░░▀███▀░░░░░█░░███░░▀███▀░░░░░░░░░░░\n"
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█░░░░░░░░░░░░░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▀░░░░░░░░░░░░░░░░░░░░░░░░░" << ENDL);
" \n"
" | __/| \n"
" | @ @ WoW! \n"
" | <> _ \n"
" | _/|------____ ((| |)) \n"
" | `--' | \n"
" ____|_ ___| |___.' \n"
" /_/_____/____/_______| \n"
"########################################################\n"
"### ____ ############ _ #### ____ ######################\n"
"###| _ | __ _ _ __ | | __ | _ | ___ __ _ ___ ###\n"
"###| | | |/ _` | '_ || |/ / | | | |/ _ | / _` |/ _ | ###\n"
"###| |_| | (_| | | | | < | |_| | (_) | (_| | __/ ###\n"
"###|____/ |__,_|_| |_|_||_| |____/ |___/ __, ||___| ###\n"
"#########################################|___/##########\n"
"########################################################"<< ENDL);
MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL
<< main_message << ENDL
<< ENDL

@ -1095,7 +1095,7 @@ namespace cryptonote
LockedTXN lock(m_blockchain);
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
{
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
@ -1109,7 +1109,6 @@ namespace cryptonote
if (max_total_size < total_size + meta.blob_size)
{
LOG_PRINT_L2(" would exceed maximum block size");
sorted_it++;
continue;
}
@ -1122,14 +1121,12 @@ namespace cryptonote
if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version))
{
LOG_PRINT_L2(" would exceed maximum block size");
sorted_it++;
continue;
}
coinbase = block_reward + fee + meta.fee;
if (coinbase < template_accept_threshold(best_coinbase))
{
LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase));
sorted_it++;
continue;
}
}
@ -1173,13 +1170,11 @@ namespace cryptonote
if (!ready)
{
LOG_PRINT_L2(" not ready to go");
sorted_it++;
continue;
}
if (have_key_images(k_images, tx))
{
LOG_PRINT_L2(" key images already seen");
sorted_it++;
continue;
}
@ -1188,7 +1183,6 @@ namespace cryptonote
fee += meta.fee;
best_coinbase = coinbase;
append_key_images(k_images, tx);
sorted_it++;
LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase));
}
@ -1284,6 +1278,7 @@ namespace cryptonote
{
MWARNING("Failed to parse tx from txpool, removing");
remove.push_back(txid);
return true;
}
if (!insert_key_images(tx, meta.kept_by_block))
{

@ -1167,8 +1167,20 @@ skip:
+ " blocks/sec), " + std::to_string(m_block_queue.get_data_size() / 1048576.f) + " MB queued";
if (ELPP->vRegistry()->allowed(el::Level::Debug, "sync-info"))
timing_message += std::string(": ") + m_block_queue.get_overview();
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
if(m_core.get_target_blockchain_height() == 0){
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
} else {
const int completition_percent = (m_core.get_current_blockchain_height() * 100 / m_core.get_target_blockchain_height());
if(completition_percent < 99) {//printing completion percent only if % is < of 99 cause for 99 >= this is useless
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< " (" << completition_percent << "% " << (m_core.get_target_blockchain_height() - m_core.get_current_blockchain_height())
<< " blocks remaining)" << timing_message);
} else {
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
}
}
}
}
}
@ -1752,3 +1764,4 @@ skip:
m_core.stop();
}
} // namespace

@ -438,7 +438,8 @@ bool t_rpc_command_executor::show_status() {
}
}
tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
std::stringstream str;
str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections")
% (unsigned long long)ires.height
% (unsigned long long)net_height
% get_sync_percentage(ires)
@ -451,12 +452,21 @@ bool t_rpc_command_executor::show_status() {
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
% (unsigned)ires.outgoing_connections_count
% (unsigned)ires.incoming_connections_count
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
% (unsigned int)fmod(uptime, 60.0)
;
// restricted RPC does not disclose start time
if (ires.start_time)
{
str << boost::format(", uptime %ud %uh %um %us")
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
% (unsigned int)fmod(uptime, 60.0)
;
}
tools::success_msg_writer() << str.str();
return true;
}
@ -554,7 +564,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
if (!first)
std::cout << std::endl;
std::cout
<< "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty
<< "height: " << header.height << ", timestamp: " << header.timestamp
<< ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl

@ -863,7 +863,13 @@ namespace cryptonote
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
cryptonote::miner &miner= m_core.get_miner();
if (miner.is_mining())
{
res.status = "Already mining";
return true;
}
if(!miner.start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);
@ -992,6 +998,8 @@ namespace cryptonote
return r;
m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted);
for (tx_info& txi : res.transactions)
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
res.status = CORE_RPC_STATUS_OK;
return true;
}

File diff suppressed because it is too large Load Diff

@ -91,13 +91,13 @@ namespace cryptonote
//! \return Prompts user for password and verifies against local file. Logs on error and returns `none`
boost::optional<tools::password_container> get_and_verify_password() const;
bool new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
bool recover, bool two_random, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
bool new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const std::string &multisig_keys, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@ -121,7 +121,7 @@ namespace cryptonote
bool set_store_tx_info(const std::vector<std::string> &args = std::vector<std::string>());
bool set_auto_refresh(const std::vector<std::string> &args = std::vector<std::string>());
bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>());
bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>());
bool set_confirm_subaddress(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ask_password(const std::vector<std::string> &args = std::vector<std::string>());
bool set_unit(const std::vector<std::string> &args = std::vector<std::string>());
bool set_min_output_count(const std::vector<std::string> &args = std::vector<std::string>());
@ -167,7 +167,6 @@ namespace cryptonote
void print_accounts();
void print_accounts(const std::string& tag);
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
bool save(const std::vector<std::string> &args);
bool save_watch_only(const std::vector<std::string> &args);
@ -211,6 +210,7 @@ namespace cryptonote
bool sign_multisig(const std::vector<std::string>& args);
bool submit_multisig(const std::vector<std::string>& args);
bool export_raw_multisig(const std::vector<std::string>& args);
bool mms(const std::vector<std::string>& args);
bool print_ring(const std::vector<std::string>& args);
bool set_ring(const std::vector<std::string>& args);
bool save_known_rings(const std::vector<std::string>& args);
@ -352,5 +352,38 @@ namespace cryptonote
bool m_auto_refresh_refreshing;
std::atomic<bool> m_in_manual_refresh;
uint32_t m_current_subaddress_account;
// MMS
mms::message_store& get_message_store() const { return m_wallet->get_message_store(); };
mms::multisig_wallet_state get_multisig_wallet_state() const { return m_wallet->get_multisig_wallet_state(); };
bool mms_active() const { return get_message_store().get_active(); };
bool choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice);
void list_mms_messages(const std::vector<mms::message> &messages);
void show_message(const mms::message &m);
void ask_send_all_ready_messages();
void check_for_messages();
bool user_confirms(const std::string &question);
bool get_message_from_arg(const std::string &arg, mms::message &m);
bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound);
void mms_init(const std::vector<std::string> &args);
void mms_info(const std::vector<std::string> &args);
void mms_member(const std::vector<std::string> &args);
void mms_list(const std::vector<std::string> &args);
void mms_next(const std::vector<std::string> &args);
void mms_sync(const std::vector<std::string> &args);
void mms_transfer(const std::vector<std::string> &args);
void mms_delete(const std::vector<std::string> &args);
void mms_send(const std::vector<std::string> &args);
void mms_receive(const std::vector<std::string> &args);
void mms_note(const std::vector<std::string> &args);
void mms_show(const std::vector<std::string> &args);
void mms_set(const std::vector<std::string> &args);
void mms_help(const std::vector<std::string> &args);
void mms_debug(const std::vector<std::string> &args);
bool m_called_by_mms = false;
bool called_by_mms();
bool m_command_successful;
};
}

@ -1,6 +1,6 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
#define DEF_MONERO_VERSION "0.3.0.1-master"
#define DEF_MONERO_RELEASE_NAME "Cool Cage"
#define DEF_MONERO_VERSION "0.4.0.0"
#define DEF_MONERO_RELEASE_NAME "Dank Doge"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#include "version.h"

@ -34,7 +34,10 @@ set(wallet_sources
wallet2.cpp
wallet_args.cpp
ringdb.cpp
node_rpc_proxy.cpp)
node_rpc_proxy.cpp
message_store.cpp
message_transporter.cpp
)
set(wallet_private_headers
wallet2.h
@ -44,7 +47,9 @@ set(wallet_private_headers
wallet_rpc_server_commands_defs.h
wallet_rpc_server_error_codes.h
ringdb.h
node_rpc_proxy.h)
node_rpc_proxy.h
message_store.h
message_transporter.h)
monero_private_headers(wallet
${wallet_private_headers})
@ -102,7 +107,6 @@ set_property(TARGET wallet_rpc_server
OUTPUT_NAME "wownero-wallet-rpc")
install(TARGETS wallet_rpc_server DESTINATION bin)
# build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS)
set(libs_to_merge

@ -744,14 +744,6 @@ std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) co
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
}
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
{
crypto::hash8 pid;
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
return "";
}
return m_wallet->get_integrated_address_as_str(pid);
}
std::string WalletImpl::secretViewKey() const
{

@ -88,7 +88,6 @@ public:
std::string errorString() const;
bool setPassword(const std::string &password);
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const;
std::string integratedAddress(const std::string &payment_id) const;
std::string secretViewKey() const;
std::string publicViewKey() const;
std::string secretSpendKey() const;

@ -373,16 +373,6 @@ struct Wallet
virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//! check if hard fork rules should be used
virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
/*!
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.
* if passed "payment_id" param is an empty string or not-valid payment id string
* (16 characters hexadecimal string) - random payment_id will be generated
*
* \param payment_id - 16 characters hexadecimal string or empty string if new random payment id needs to be
* generated
* \return - 106 characters string representing integrated address
*/
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
/*!
* \brief secretViewKey - returns secret view key

File diff suppressed because it is too large Load Diff

@ -0,0 +1,357 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <cstdlib>
#include <string>
#include <vector>
#include "crypto/hash.h"
#include <boost/serialization/vector.hpp>
#include "serialization/serialization.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "common/i18n.h"
#include "message_transporter.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms"
namespace mms
{
enum class message_type
{
key_set,
finalizing_key_set,
multisig_sync_data,
partially_signed_tx,
fully_signed_tx,
note
};
enum class message_direction
{
in,
out
};
enum class message_state
{
ready_to_send,
sent,
waiting,
processed,
cancelled
};
enum class message_processing
{
prepare_multisig,
make_multisig,
finalize_multisig,
create_sync_data,
process_sync_data,
sign_tx,
send_tx,
submit_tx
};
struct message
{
uint32_t id;
message_type type;
message_direction direction;
std::string content;
uint64_t created;
uint64_t modified;
uint64_t sent;
uint32_t member_index;
crypto::hash hash;
message_state state;
uint32_t wallet_height;
uint32_t round;
uint32_t signature_count;
std::string transport_id;
};
// "wallet_height" (for lack of a short name that would describe what it is about)
// is the number of transfers present in the wallet at the time of message
// construction; used to coordinate generation of sync info (which depends
// on the content of the wallet at time of generation)
struct coalition_member
{
std::string label;
std::string transport_address;
bool monero_address_known;
cryptonote::account_public_address monero_address;
bool me;
uint32_t index;
};
struct processing_data
{
message_processing processing;
std::vector<uint32_t> message_ids;
uint32_t receiving_member_index = 0;
};
struct file_transport_message
{
cryptonote::account_public_address sender_address;
crypto::chacha_iv iv;
crypto::public_key encryption_public_key;
message internal_message;
};
// Overal .mms file structure, with the "message_store" object serialized to and
// encrypted in "encrypted_data"
struct file_data
{
std::string magic_string;
uint32_t file_version;
crypto::chacha_iv iv;
std::string encrypted_data;
};
// The following struct provides info about the current state of a "wallet2" object
// at the time of a "message_store" method call that those methods need. See on the
// one hand a first parameter of this type for several of those methods, and on the
// other hand the method "wallet2::get_multisig_wallet_state" which clients like the
// CLI wallet can use to get that info.
//
// Note that in the case of a wallet that is already multisig "address" is NOT the
// multisig address, but the "original" wallet address at creation time. Likewise
// "view_secret_key" is the original view secret key then.
//
// This struct definition is here and not in "wallet2.h" to avoid circular imports.
struct multisig_wallet_state
{
cryptonote::account_public_address address;
cryptonote::network_type nettype;
crypto::secret_key view_secret_key;
bool multisig;
bool multisig_is_ready;
bool has_multisig_partial_key_images;
size_t num_transfer_details;
std::string mms_file;
~multisig_wallet_state()
{
view_secret_key = crypto::null_skey;
}
};
class message_store
{
public:
message_store();
// Initialize and start to use the MMS, set the first member, this wallet itself
// Filename, if not null and not empty, is used to create the ".mms" file
// reset it if already used, with deletion of all members and messages
void init(const multisig_wallet_state &state, const std::string &own_label,
const std::string &own_transport_address, uint32_t coalition_size, uint32_t threshold);
void set_active(bool active) { m_active = active; };
void set_auto_send(bool auto_send) { m_auto_send = auto_send; };
void set_options(const boost::program_options::variables_map& vm);
void set_options(const std::string &bitmessage_address, const std::string &bitmessage_login);
bool get_active() const { return m_active; };
bool get_auto_send() const { return m_auto_send; };
uint32_t get_threshold() const { return m_threshold; };
uint32_t get_coalition_size() const { return m_coalition_size; };
void set_member(const multisig_wallet_state &state,
uint32_t index,
const boost::optional<std::string> &label,
const boost::optional<std::string> &transport_address,
const boost::optional<cryptonote::account_public_address> monero_address);
const coalition_member &get_member(uint32_t index) const;
bool get_member_index_by_monero_address(const cryptonote::account_public_address &monero_address, uint32_t &index) const;
bool get_member_index_by_label(const std::string label, uint32_t &index) const;
const std::vector<coalition_member> &get_all_members() const { return m_members; };
bool member_info_complete() const;
// Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command
// Creates the resulting messages to the right members
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content);
// Go through all the messages, look at the "ready to process" ones, and check whether any single one
// or any group of them can be processed, because they are processable as single messages (like a tx
// that is fully signed and thus ready for submit to the net) or because they form a complete group
// (e.g. key sets from all coalition members to make the wallet multisig). If there are multiple
// candidates, e.g. in 2/3 multisig sending to one OR the other member to sign, there will be more
// than 1 element in 'data' for the user to choose. If nothing is ready "false" is returned.
// The method mostly ignores the order in which the messages were received because messages may be delayed
// (e.g. sync data from a member arrives AFTER a transaction to submit) or because message time stamps
// may be wrong so it's not possible to order them reliably.
// Messages also may be ready by themselves but the wallet not yet ready for them (e.g. sync data already
// arriving when the wallet is not yet multisig because key sets were delayed or were lost altogether.)
// If nothing is ready 'wait_reason' may contain further info about the reason why.
bool get_processable_messages(const multisig_wallet_state &state,
bool force_sync,
std::vector<processing_data> &data_list,
std::string &wait_reason);
void set_messages_processed(const processing_data &data);
uint32_t add_message(const multisig_wallet_state &state,
uint32_t member_index, message_type type, message_direction direction,
const std::string &content);
const std::vector<message> &get_all_messages() const { return m_messages; };
bool get_message_by_id(uint32_t id, message &m) const;
message get_message_by_id(uint32_t id) const;
void set_message_processed_or_sent(uint32_t id);
void delete_message(uint32_t id);
void delete_all_messages();
void send_message(const multisig_wallet_state &state, uint32_t id);
bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages);
void stop() { m_run.store(false, std::memory_order_relaxed); m_transporter.stop(); }
void write_to_file(const multisig_wallet_state &state, const std::string &filename);
void read_from_file(const multisig_wallet_state &state, const std::string &filename);
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
a & m_active;
a & m_coalition_size;
a & m_nettype;
a & m_threshold;
a & m_members;
a & m_messages;
a & m_next_message_id;
a & m_auto_send;
}
const char* message_type_to_string(message_type type);
const char* message_direction_to_string(message_direction direction);
const char* message_state_to_string(message_state state);
std::string member_to_string(const coalition_member &member, uint32_t max_width);
static const char *tr(const char *str) { return i18n_translate(str, "tools::mms"); }
static void init_options(boost::program_options::options_description& desc_params);
private:
bool m_active;
uint32_t m_coalition_size;
uint32_t m_threshold;
bool m_auto_send;
cryptonote::network_type m_nettype;
std::vector<coalition_member> m_members;
std::vector<message> m_messages;
uint32_t m_next_message_id;
std::string m_filename;
message_transporter m_transporter;
std::atomic<bool> m_run;
bool get_message_index_by_id(uint32_t id, uint32_t &index) const;
uint32_t get_message_index_by_id(uint32_t id) const;
bool any_message_of_type(message_type type, message_direction direction) const;
bool any_message_with_hash(const crypto::hash &hash) const;
bool message_ids_complete(const std::vector<uint32_t> ids) const;
void encrypt(uint32_t member_index, const std::string &plaintext,
std::string &ciphertext, crypto::public_key &encryption_public_key, crypto::chacha_iv &iv);
void decrypt(const std::string &ciphertext, const crypto::public_key &encryption_public_key, const crypto::chacha_iv &iv,
const crypto::secret_key &view_secret_key, std::string &plaintext);
void delete_transport_message(uint32_t id);
std::string account_address_to_string(const cryptonote::account_public_address &account_address) const;
void save(const multisig_wallet_state &state);
};
}
BOOST_CLASS_VERSION(mms::file_data, 0)
BOOST_CLASS_VERSION(mms::message_store, 0)
BOOST_CLASS_VERSION(mms::message, 0)
BOOST_CLASS_VERSION(mms::file_transport_message, 0)
BOOST_CLASS_VERSION(mms::coalition_member, 0)
namespace boost
{
namespace serialization
{
template <class Archive>
inline void serialize(Archive &a, mms::file_data &x, const boost::serialization::version_type ver)
{
a & x.magic_string;
a & x.file_version;
a & x.iv;
a & x.encrypted_data;
}
template <class Archive>
inline void serialize(Archive &a, mms::message &x, const boost::serialization::version_type ver)
{
a & x.id;
a & x.type;
a & x.direction;
a & x.content;
a & x.created;
a & x.modified;
a & x.sent;
a & x.member_index;
a & x.hash;
a & x.state;
a & x.wallet_height;
a & x.round;
a & x.signature_count;
a & x.transport_id;
}
template <class Archive>
inline void serialize(Archive &a, mms::coalition_member &x, const boost::serialization::version_type ver)
{
a & x.label;
a & x.transport_address;
a & x.monero_address_known;
a & x.monero_address;
a & x.me;
a & x.index;
}
template <class Archive>
inline void serialize(Archive &a, mms::file_transport_message &x, const boost::serialization::version_type ver)
{
a & x.sender_address;
a & x.iv;
a & x.encryption_public_key;
a & x.internal_message;
}
template <class Archive>
inline void serialize(Archive &a, crypto::chacha_iv &x, const boost::serialization::version_type ver)
{
a & x.data;
}
}
}

@ -0,0 +1,287 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "message_transporter.h"
#include "string_coding.h"
#include <boost/format.hpp>
#include "wallet_errors.h"
#include "net/http_client.h"
#include "net/net_parse_helpers.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms"
#define PYBITMESSAGE_DEFAULT_API_PORT 8442
namespace mms
{
namespace bitmessage_rpc
{
struct message_info
{
uint32_t encodingType;
std::string toAddress;
uint32_t read;
std::string msgid;
std::string message;
std::string fromAddress;
std::string receivedTime;
std::string subject;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(encodingType)
KV_SERIALIZE(toAddress)
KV_SERIALIZE(read)
KV_SERIALIZE(msgid)
KV_SERIALIZE(message);
KV_SERIALIZE(fromAddress)
KV_SERIALIZE(receivedTime)
KV_SERIALIZE(subject)
END_KV_SERIALIZE_MAP()
};
struct inbox_messages_response
{
std::vector<message_info> inboxMessages;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(inboxMessages)
END_KV_SERIALIZE_MAP()
};
}
message_transporter::message_transporter()
{
m_run = true;
}
void message_transporter::set_options(const std::string &bitmessage_address, const std::string &bitmessage_login) {
m_bitmessage_url = bitmessage_address;
epee::net_utils::http::url_content address_parts{};
epee::net_utils::parse_url(m_bitmessage_url, address_parts);
if (address_parts.port == 0)
{
address_parts.port = PYBITMESSAGE_DEFAULT_API_PORT;
}
auto pos = bitmessage_login.find(":");
if (pos == std::string::npos)
{
m_bitmessage_user = bitmessage_login;
m_bitmessage_password.clear();
}
else
{
m_bitmessage_user = bitmessage_login.substr(0, pos);
m_bitmessage_password = bitmessage_login.substr(pos + 1);
}
boost::optional<epee::net_utils::http::login> login{};
login.emplace(m_bitmessage_user, m_bitmessage_password);
m_http_client.set_server(address_parts.host, std::to_string(address_parts.port), login);
}
bool message_transporter::receive_messages(const cryptonote::account_public_address &destination_monero_address,
const std::string &destination_transport_address,
std::vector<transport_message> &messages)
{
// The message body of the Bitmessage message is basically the transport message, as JSON (and nothing more).
// Weeding out other, non-MMS messages is done in a simple way: If it deserializes without error, it's an MMS message
// That JSON is Base64-encoded by the MMS because the Monero epee JSON serializer does not escape anything and happily
// includes even 0 (NUL) in strings, which might confuse Bitmessage or at least display confusingly in the client.
// There is yet another Base64-encoding of course as part of the Bitmessage API for the message body parameter
// The Bitmessage API call "getAllInboxMessages" gives back a JSON array with all the messages (despite using
// XML-RPC for the calls, and not JSON-RPC ...)
m_run.store(true, std::memory_order_relaxed);
std::string request;
start_xml_rpc_cmd(request, "getAllInboxMessages");
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
std::string json = get_str_between_tags(answer, "<string>", "</string>");
bitmessage_rpc::inbox_messages_response bitmessage_res;
epee::serialization::load_t_from_json(bitmessage_res, json);
size_t size = bitmessage_res.inboxMessages.size();
messages.clear();
for (size_t i = 0; i < size; ++i)
{
if (!m_run.load(std::memory_order_relaxed))
{
// Stop was called, don't waste time processing any more messages
return false;
}
bitmessage_rpc::message_info message_info = bitmessage_res.inboxMessages[i];
transport_message message;
bool is_mms_message = false;
try
{
// First Base64-decoding: The message body is Base64 in the Bitmessage API
std::string message_body = epee::string_encoding::base64_decode(message_info.message);
// Second Base64-decoding: The MMS uses Base64 to hide non-textual data in its JSON from Bitmessage
json = epee::string_encoding::base64_decode(message_body);
epee::serialization::load_t_from_json(message, json);
is_mms_message = true;
}
catch(const std::exception& e)
{
}
if (is_mms_message)
{
if (message.destination_monero_address == destination_monero_address)
{
message.transport_id = message_info.msgid;
messages.push_back(message);
}
}
}
return true;
}
bool message_transporter::send_message(const transport_message &message)
{
// <toAddress> <fromAddress> <subject> <message> [encodingType [TTL]]
std::string request;
start_xml_rpc_cmd(request, "sendMessage");
add_xml_rpc_string_param(request, message.destination_transport_address);
add_xml_rpc_string_param(request, message.source_transport_address);
add_xml_rpc_base64_param(request, message.subject);
std::string json = epee::serialization::store_t_to_json(message);
std::string message_body = epee::string_encoding::base64_encode(json); // See comment in "receive_message" about reason for (double-)Base64 encoding
add_xml_rpc_base64_param(request, message_body);
add_xml_rpc_integer_param(request, 2);
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
return true;
}
bool message_transporter::delete_message(const std::string &transport_id)
{
std::string request;
start_xml_rpc_cmd(request, "trashMessage");
add_xml_rpc_string_param(request, transport_id);
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
return true;
}
bool message_transporter::post_request(const std::string &request, std::string &answer)
{
// Somehow things do not work out if one tries to connect "m_http_client" to Bitmessage
// and keep it connected over the course of several calls. But with a new connection per
// call and disconnecting after the call there is no problem (despite perhaps a small
// slowdown)
epee::net_utils::http::fields_list additional_params;
// Basic access authentication according to RFC 7617 (which the epee HTTP classes do not seem to support?)
std::string user_password(m_bitmessage_user + ":" + m_bitmessage_password);
std::string auth_string = epee::string_encoding::base64_encode(user_password);
auth_string.insert(0, "Basic ");
additional_params.push_back(std::make_pair("Authorization", auth_string));
additional_params.push_back(std::make_pair("Content-Type", "application/xml; charset=utf-8"));
const epee::net_utils::http::http_response_info* response = NULL;
std::chrono::milliseconds timeout = std::chrono::seconds(15);
bool r = m_http_client.invoke("/", "POST", request, timeout, std::addressof(response), std::move(additional_params));
if (r)
{
answer = response->m_body;
}
else
{
LOG_ERROR("POST request to Bitmessage failed: " << request.substr(0, 300));
THROW_WALLET_EXCEPTION(tools::error::no_connection_to_bitmessage, m_bitmessage_url);
}
m_http_client.disconnect(); // see comment above
std::string string_value = get_str_between_tags(answer, "<string>", "</string>");
if ((string_value.find("API Error") == 0) || (string_value.find("RPC ") == 0))
{
THROW_WALLET_EXCEPTION(tools::error::bitmessage_api_error, string_value);
}
return r;
}
// Pick some string between two delimiters
// When parsing the XML returned by PyBitmessage, don't bother to fully parse it but as a little hack rely on the
// fact that e.g. a single string returned will be, however deeply nested in "<params><param><value>...", delivered
// between the very first "<string>" and "</string>" tags to be found in the XML
std::string message_transporter::get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim)
{
size_t first_delim_pos = s.find(start_delim);
if (first_delim_pos != std::string::npos)
{
size_t end_pos_of_first_delim = first_delim_pos + start_delim.length();
size_t last_delim_pos = s.find(stop_delim);
if (last_delim_pos != std::string::npos)
{
return s.substr(end_pos_of_first_delim, last_delim_pos - end_pos_of_first_delim);
}
}
return std::string();
}
void message_transporter::start_xml_rpc_cmd(std::string &xml, const std::string &method_name)
{
xml = (boost::format("<?xml version=\"1.0\"?><methodCall><methodName>%s</methodName><params>") % method_name).str();
}
void message_transporter::add_xml_rpc_string_param(std::string &xml, const std::string &param)
{
xml += (boost::format("<param><value><string>%s</string></value></param>") % param).str();
}
void message_transporter::add_xml_rpc_base64_param(std::string &xml, const std::string &param)
{
// Bitmessage expects some arguments Base64-encoded, but it wants them as parameters of type "string", not "base64" that is also part of XML-RPC
std::string encoded_param = epee::string_encoding::base64_encode(param);
xml += (boost::format("<param><value><string>%s</string></value></param>") % encoded_param).str();
}
void message_transporter::add_xml_rpc_integer_param(std::string &xml, const int32_t &param)
{
xml += (boost::format("<param><value><int>%i</int></value></param>") % param).str();
}
void message_transporter::end_xml_rpc_cmd(std::string &xml)
{
xml += "</params></methodCall>";
}
}

@ -0,0 +1,113 @@
// Copyright (c) 2014-2018, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "serialization/keyvalue_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "net/http_server_impl_base.h"
#include "net/http_client.h"
#include "common/util.h"
#include "serialization/keyvalue_serialization.h"
#define PYBITMESSAGE_API_PORT 8442
namespace mms
{
struct transport_message
{
cryptonote::account_public_address source_monero_address;
std::string source_transport_address;
cryptonote::account_public_address destination_monero_address;
std::string destination_transport_address;
crypto::chacha_iv iv;
crypto::public_key encryption_public_key;
uint64_t timestamp;
uint32_t type;
std::string subject;
std::string content;
crypto::hash hash;
crypto::signature signature;
uint32_t round;
uint32_t signature_count;
std::string transport_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(source_monero_address)
KV_SERIALIZE(source_transport_address)
KV_SERIALIZE(destination_monero_address)
KV_SERIALIZE(destination_transport_address)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(iv)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(encryption_public_key)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(type)
KV_SERIALIZE(subject)
KV_SERIALIZE(content)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(hash)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(signature)
KV_SERIALIZE(round)
KV_SERIALIZE(signature_count)
KV_SERIALIZE(transport_id)
END_KV_SERIALIZE_MAP()
};
class message_transporter {
public:
message_transporter();
void set_options(const std::string &bitmessage_address, const std::string &bitmessage_login);
bool send_message(const transport_message &message);
bool receive_messages(const cryptonote::account_public_address &destination_monero_address,
const std::string &destination_transport_address,
std::vector<transport_message> &messages);
bool delete_message(const std::string &transport_id);
void stop() { m_run.store(false, std::memory_order_relaxed); }
private:
epee::net_utils::http::http_simple_client m_http_client;
std::string m_bitmessage_url;
std::string m_bitmessage_user;
std::string m_bitmessage_password;
std::atomic<bool> m_run;
bool post_request(const std::string &request, std::string &answer);
std::string get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim);
void start_xml_rpc_cmd(std::string &xml, const std::string &method_name);
void add_xml_rpc_string_param(std::string &xml, const std::string &param);
void add_xml_rpc_base64_param(std::string &xml, const std::string &param);
void add_xml_rpc_integer_param(std::string &xml, const int32_t &param);
void end_xml_rpc_cmd(std::string &xml);
};
}

@ -146,7 +146,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status");
m_earliest_height[version] = resp_t.enabled ? resp_t.earliest_height : std::numeric_limits<uint64_t>::max();
m_earliest_height[version] = resp_t.earliest_height;
}
earliest_height = m_earliest_height[version];

@ -153,7 +153,7 @@ struct options {
};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
{
keys_file = file_path;
wallet_file = file_path;
@ -165,6 +165,7 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
{//provided wallet file name
keys_file += ".keys";
}
mms_file = file_path + ".mms";
}
uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
@ -230,6 +231,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
wallet->init(std::move(daemon_address), std::move(login));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->get_message_store().set_options(vm);
return wallet;
}
@ -651,7 +653,7 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_auto_refresh(true),
m_refresh_from_block_height(0),
m_explicit_refresh_from_block_height(true),
m_confirm_missing_payment_id(true),
m_confirm_subaddress(true),
m_confirm_non_default_ring_size(true),
m_ask_password(true),
m_min_output_count(0),
@ -676,6 +678,8 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_light_wallet_connected(false),
m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0),
m_original_keys_available(false),
m_message_store(),
m_key_on_device(false),
m_ring_history_saved(false),
m_ringdb()
@ -709,6 +713,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.restricted);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
mms::message_store::init_options(desc_params);
}
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@ -887,6 +892,14 @@ cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::sub
return hwdev.get_subaddress(m_account.get_keys(), index);
}
//----------------------------------------------------------------------------------------------------
boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
{
auto index = m_subaddresses.find(address.m_spend_public_key);
if (index == m_subaddresses.end())
return boost::none;
return index->second;
}
//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const
{
hw::device &hwdev = m_account.get_device();
@ -898,6 +911,7 @@ std::string wallet2::get_subaddress_as_str(const cryptonote::subaddress_index& i
cryptonote::account_public_address address = get_subaddress(index);
return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const
{
@ -1105,7 +1119,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
std::vector<size_t> outs;
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
@ -1123,6 +1136,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t total_received_1 = 0;
while (!tx.vout.empty())
{
std::vector<size_t> outs;
// if tx.vout is not empty, we loop through all tx pubkeys
tx_extra_pub_key pub_key_field;
@ -2572,6 +2586,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
rapidjson::Document json;
json.SetObject();
rapidjson::Value value(rapidjson::kStringType);
value.SetString(account_data.c_str(), account_data.length());
json.AddMember("key_data", value, json.GetAllocator());
if (!seed_language.empty())
@ -2623,8 +2638,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint64(m_refresh_from_block_height);
json.AddMember("refresh_height", value2, json.GetAllocator());
value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
value2.SetInt(m_confirm_subaddress ? 1 :0);
json.AddMember("confirm_subaddress", value2, json.GetAllocator());
value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
@ -2674,6 +2689,22 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_subaddress_lookahead_minor);
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
value2.SetInt(m_original_keys_available ? 1 : 0);
json.AddMember("original_keys_available", value2, json.GetAllocator());
std::string original_address;
std::string original_view_secret_key;
if (m_original_keys_available)
{
original_address = get_account_address_as_str(m_nettype, false, m_original_address);
value.SetString(original_address.c_str(), original_address.length());
json.AddMember("original_address", value, json.GetAllocator());
original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key);
value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
json.AddMember("original_view_secret_key", value, json.GetAllocator());
}
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@ -2734,7 +2765,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_default_priority = 0;
m_auto_refresh = true;
m_refresh_type = RefreshType::RefreshDefault;
m_confirm_missing_payment_id = true;
m_confirm_subaddress = true;
m_confirm_non_default_ring_size = true;
m_ask_password = true;
m_min_output_count = 0;
@ -2749,6 +2780,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = 0;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_original_keys_available = false;
m_key_on_device = false;
}
else if(json.IsObject())
@ -2837,8 +2869,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
m_refresh_from_block_height = field_refresh_height;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_subaddress, int, Int, false, true);
m_confirm_subaddress = field_confirm_subaddress;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
@ -2875,6 +2907,44 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
if (json.HasMember("original_keys_available"))
{
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false);
m_original_keys_available = field_original_keys_available;
if (m_original_keys_available)
{
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, false, std::string());
if (!field_original_address_found)
{
LOG_ERROR("Field original_address not found in JSON");
return false;
}
address_parse_info info;
bool ok = get_account_address_from_str(info, m_nettype, field_original_address);
if (!ok)
{
LOG_ERROR("Failed to parse original_address from JSON");
return false;
}
m_original_address = info.address;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, false, std::string());
if (!field_original_view_secret_key_found)
{
LOG_ERROR("Field original_view_secret_key not found in JSON");
return false;
}
ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key);
if (!ok)
{
LOG_ERROR("Failed to parse original_view_secret_key from JSON");
}
}
}
else
{
m_original_keys_available = false;
}
}
else
{
@ -3042,6 +3112,10 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig_signers = multisig_signers;
m_key_on_device = false;
// Not possible to restore a multisig wallet that is able to activate the MMS
// (because the original keys are not (yet) part of the restore info)
m_original_keys_available = false;
if (!wallet_.empty())
{
bool r = store_keys(m_keys_file, password, false);
@ -3092,6 +3166,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false;
// calculate a starting refresh height
@ -3190,6 +3265,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false;
if (!wallet_.empty())
@ -3240,6 +3316,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false;
if (!wallet_.empty())
@ -3286,6 +3363,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
if (!wallet_.empty()) {
bool r = store_keys(m_keys_file, password, false);
@ -3360,6 +3438,15 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case");
}
if (!m_original_keys_available)
{
// Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
// (making a wallet multisig overwrites those keys, see account_base::make_multisig)
m_original_address = m_account.get_keys().m_account_address;
m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
m_original_keys_available = true;
}
// the multisig view key is shared by all, make one all can derive
MINFO("Creating view key...");
crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
@ -3687,8 +3774,8 @@ void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee
//----------------------------------------------------------------------------------------------------
void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
{
std::string keys_file, wallet_file;
do_prepare_file_names(file_path, keys_file, wallet_file);
std::string keys_file, wallet_file, mms_file;
do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
boost::system::error_code ignore;
keys_file_exists = boost::filesystem::exists(keys_file, ignore);
@ -3742,7 +3829,7 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path)
{
do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
return true;
}
//----------------------------------------------------------------------------------------------------
@ -3913,6 +4000,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
MERROR("Failed to save rings, will try again next time");
}
m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
}
//----------------------------------------------------------------------------------------------------
void wallet2::trim_hashchain()
@ -4020,6 +4109,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string old_file = m_wallet_file;
const std::string old_keys_file = m_keys_file;
const std::string old_address_file = m_wallet_file + ".address.txt";
const std::string old_mms_file = m_mms_file;
// save keys to the new file
// if we here, main wallet file is saved and we only need to save keys and address files
@ -4049,6 +4139,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
if (!r) {
LOG_ERROR("error removing file: " << old_address_file);
}
// remove old message store file
if (boost::filesystem::exists(old_mms_file))
{
r = boost::filesystem::remove(old_mms_file);
if (!r) {
LOG_ERROR("error removing file: " << old_mms_file);
}
}
} else {
// save to new file
#ifdef WIN32
@ -4074,6 +4172,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
std::error_code e = tools::replace_file(new_file, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
}
if (m_message_store.get_active())
{
// While the "m_message_store" object of course always exist, a file for the message
// store should only exist if the MMS is really active
m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
}
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::balance(uint32_t index_major) const
@ -5914,6 +6020,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
bool is_after_segregation_fork = height >= segregation_fork_height;
// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
@ -5985,6 +6092,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
size_t num_selected_transfers = 0;
for(size_t idx: selected_transfers)
{
@ -8124,7 +8232,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
bool close_enough = height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand
if (close_enough)
LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
@ -8907,6 +9015,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
if (account_minreserve)
{
THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
// minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
{ return m_transfers[a].amount() > m_transfers[b].amount(); });
@ -9572,6 +9681,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
// was created by sweep_all, so we can't know the spent height and other detailed info.
std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
for (const transfer_details &td: m_transfers)
{
for (const cryptonote::txin_v& in : td.m_tx.vin)
{
if (in.type() == typeid(cryptonote::txin_to_key))
spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
}
}
for(size_t i = 0; i < signed_key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
@ -9585,28 +9705,11 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
{
bool is_spent_tx_found = false;
for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
{
bool is_spent_tx = false;
for(const cryptonote::txin_v& in : it->m_tx.vin)
{
if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
{
is_spent_tx = true;
break;
}
}
if (is_spent_tx)
{
is_spent_tx_found = true;
spent_txids.insert(it->m_txid);
break;
}
}
if (!is_spent_tx_found)
const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
if (skii == spent_key_images.end())
swept_transfers.push_back(i);
else
spent_txids.insert(skii->second);
}
}
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
@ -9729,11 +9832,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
{
const transfer_details& td = m_transfers[n];
confirmed_transfer_details pd;
pd.m_change = (uint64_t)-1; // cahnge is unknown
pd.m_change = (uint64_t)-1; // change is unknown
pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
std::string err;
pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
pd.m_block_height = 0; // spent block height is unknown
const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
}
}
@ -9828,18 +9930,20 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key),
error::wallet_internal_error, "Unsupported output type");
const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true;
td.m_key_image_partial = false;
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key,
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i));
m_key_images[td.m_key_image] = m_transfers.size();
m_pub_keys[td.get_public_key()] = m_transfers.size();
m_transfers.push_back(td);
m_transfers.push_back(std::move(td));
}
return m_transfers.size();
@ -10554,4 +10658,28 @@ void wallet2::generate_genesis(cryptonote::block& b) const {
cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
}
}
//----------------------------------------------------------------------------------------------------
mms::multisig_wallet_state wallet2::get_multisig_wallet_state()
{
mms::multisig_wallet_state state;
state.nettype = m_nettype;
state.multisig = multisig(&state.multisig_is_ready);
state.has_multisig_partial_key_images = has_multisig_partial_key_images();
state.num_transfer_details = m_transfers.size();
if (state.multisig)
{
THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Monero address not available");
state.address = m_original_address;
state.view_secret_key = m_original_view_secret_key;
}
else
{
state.address = m_account.get_keys().m_account_address;
state.view_secret_key = m_account.get_keys().m_view_secret_key;
}
state.mms_file=m_mms_file;
return state;
}
}

@ -58,6 +58,7 @@
#include "wallet_errors.h"
#include "common/password.h"
#include "node_rpc_proxy.h"
#include "message_store.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@ -454,6 +455,7 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@ -593,7 +595,7 @@ namespace tools
bool init(std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
void stop() { m_run.store(false, std::memory_order_relaxed); }
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
i_wallet2_callback* callback() const { return m_callback; }
void callback(i_wallet2_callback* callback) { m_callback = callback; }
@ -624,6 +626,7 @@ namespace tools
// Subaddress scheme
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const;
cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); }
boost::optional<cryptonote::subaddress_index> get_subaddress_index(const cryptonote::account_public_address& address) const;
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
@ -851,8 +854,8 @@ namespace tools
void set_default_priority(uint32_t p) { m_default_priority = p; }
bool auto_refresh() const { return m_auto_refresh; }
void auto_refresh(bool r) { m_auto_refresh = r; }
bool confirm_missing_payment_id() const { return m_confirm_missing_payment_id; }
void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
bool confirm_subaddress() const { return m_confirm_subaddress; }
void confirm_subaddress(bool always) { m_confirm_subaddress = always; }
bool ask_password() const { return m_ask_password; }
void ask_password(bool always) { m_ask_password = always; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
@ -1076,6 +1079,10 @@ namespace tools
bool unblackball_output(const crypto::public_key &output);
bool is_output_blackballed(const crypto::public_key &output) const;
// MMS -------------------------------------------------------------------------------------------------
mms::message_store& get_message_store() { return m_message_store; };
mms::multisig_wallet_state get_multisig_wallet_state();
private:
/*!
* \brief Stores wallet information to wallet file.
@ -1147,6 +1154,7 @@ namespace tools
std::string m_daemon_address;
std::string m_wallet_file;
std::string m_keys_file;
std::string m_mms_file;
epee::net_utils::http::http_simple_client m_http_client;
hashchain m_blockchain;
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
@ -1196,7 +1204,7 @@ namespace tools
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
// m_refresh_from_block_height was defaulted to zero.*/
bool m_explicit_refresh_from_block_height;
bool m_confirm_missing_payment_id;
bool m_confirm_subaddress;
bool m_confirm_non_default_ring_size;
bool m_ask_password;
uint32_t m_min_output_count;
@ -1231,6 +1239,11 @@ namespace tools
std::string m_ring_database;
bool m_ring_history_saved;
std::unique_ptr<ringdb> m_ringdb;
mms::message_store m_message_store;
bool m_original_keys_available;
cryptonote::account_public_address m_original_address;
crypto::secret_key m_original_view_secret_key;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 24)

@ -784,6 +784,31 @@ namespace tools
std::string m_wallet_file;
};
//----------------------------------------------------------------------------------------------------
struct mms_error : public wallet_logic_error
{
protected:
explicit mms_error(std::string&& loc, const std::string& message)
: wallet_logic_error(std::move(loc), message)
{
}
};
//----------------------------------------------------------------------------------------------------
struct no_connection_to_bitmessage : public mms_error
{
explicit no_connection_to_bitmessage(std::string&& loc, const std::string& address)
: mms_error(std::move(loc), "no connection to PyBitmessage at address " + address)
{
}
};
//----------------------------------------------------------------------------------------------------
struct bitmessage_api_error : public mms_error
{
explicit bitmessage_api_error(std::string&& loc, const std::string& error_string)
: mms_error(std::move(loc), "PyBitmessage returned " + error_string)
{
}
};
//----------------------------------------------------------------------------------------------------
#if !defined(_MSC_VER)

@ -336,14 +336,20 @@ namespace tools
std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index);
std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
std::set<uint32_t> address_indices = req.address_indices;
if (address_indices.empty())
{
for (const auto& i : balance_per_subaddress)
address_indices.insert(i.first);
}
for (uint32_t i : address_indices)
{
wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info;
info.address_index = i.first;
info.address_index = i;
cryptonote::subaddress_index index = {req.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.balance = i.second;
info.unlocked_balance = unlocked_balance_per_subaddress[i.first];
info.balance = balance_per_subaddress[i];
info.unlocked_balance = unlocked_balance_per_subaddress[i];
info.label = m_wallet->get_subaddress_label(index);
info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; });
res.per_subaddress.push_back(info);
@ -397,6 +403,27 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Invalid address";
return false;
}
auto index = m_wallet->get_subaddress_index(info.address);
if (!index)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Address doesn't belong to the wallet";
return false;
}
res.index = *index;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@ -1938,8 +1965,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
}
}
@ -1948,8 +1975,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@ -1958,8 +1985,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@ -1970,11 +1997,17 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
if (i->second.m_pd.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
if (!res.transfers.empty())
{
res.transfer = res.transfers.front(); // backward compat
return true;
}
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction not found.";
return false;
@ -3007,6 +3040,7 @@ just_dir:
wrpc.send_stop_signal();
});
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
LOG_PRINT_L0(tools::wallet_rpc_server::tr("WARNING: Payment IDs/integrated addresses will be deprecated soon. Use sub-addresses."));
try
{
wrpc.run();

@ -69,6 +69,7 @@ namespace tools
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX)
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
@ -141,6 +142,7 @@ namespace tools
//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);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er);
bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);

@ -39,6 +39,17 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
// When making *any* change here, bump minor
// If the change is incompatible, then bump major and set minor to 0
// This ensures WALLET_RPC_VERSION always increases, that every change
// has its own version, and that clients can just test major to see
// whether they can talk to a given wallet without having to know in
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
#define WALLET_RPC_VERSION_MINOR 4
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
{
namespace wallet_rpc
@ -51,8 +62,10 @@ namespace wallet_rpc
struct request
{
uint32_t account_index;
std::set<uint32_t> address_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(address_indices)
END_KV_SERIALIZE_MAP()
};
@ -130,6 +143,25 @@ namespace wallet_rpc
};
};
struct COMMAND_RPC_GET_ADDRESS_INDEX
{
struct request
{
std::string address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};
struct response
{
cryptonote::subaddress_index index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(index)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_CREATE_ADDRESS
{
struct request
@ -1311,9 +1343,11 @@ namespace wallet_rpc
struct response
{
transfer_entry transfer;
std::list<transfer_entry> transfers;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer);
KV_SERIALIZE(transfers);
END_KV_SERIALIZE_MAP()
};
};

Loading…
Cancel
Save