mirror of
https://git.wownero.com/wownero/wownero.git
synced 2025-01-06 20:38:53 +00:00
commit
59ac413283
@ -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
|
||||
|
1003
src/wallet/message_store.cpp
Normal file
1003
src/wallet/message_store.cpp
Normal file
File diff suppressed because it is too large
Load Diff
357
src/wallet/message_store.h
Normal file
357
src/wallet/message_store.h
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
287
src/wallet/message_transporter.cpp
Normal file
287
src/wallet/message_transporter.cpp
Normal file
@ -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 ¶m)
|
||||
{
|
||||
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 ¶m)
|
||||
{
|
||||
// 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 ¶m)
|
||||
{
|
||||
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>";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
113
src/wallet/message_transporter.h
Normal file
113
src/wallet/message_transporter.h
Normal file
@ -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 ¶m);
|
||||
void add_xml_rpc_base64_param(std::string &xml, const std::string ¶m);
|
||||
void add_xml_rpc_integer_param(std::string &xml, const int32_t ¶m);
|
||||
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…
Reference in New Issue
Block a user