aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/dns_utils.cpp29
-rw-r--r--src/common/dns_utils.h21
-rw-r--r--src/crypto/hash-ops.h4
-rw-r--r--src/crypto/rx-slow-hash.c28
-rw-r--r--src/crypto/tree-hash.c151
-rw-r--r--src/cryptonote_basic/CMakeLists.txt2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp19
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h1
-rw-r--r--src/cryptonote_basic/merge_mining.cpp95
-rw-r--r--src/cryptonote_basic/merge_mining.h40
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl9
-rw-r--r--src/daemon/command_parser_executor.cpp62
-rw-r--r--src/daemon/command_server.cpp2
-rw-r--r--src/daemon/rpc_command_executor.cpp4
-rw-r--r--src/daemon/rpc_command_executor.h3
-rw-r--r--src/debug_utilities/cn_deserialize.cpp12
-rw-r--r--src/device_trezor/device_trezor_base.cpp29
-rw-r--r--src/device_trezor/device_trezor_base.hpp2
-rw-r--r--src/device_trezor/trezor/protocol.cpp15
-rw-r--r--src/net/CMakeLists.txt8
-rw-r--r--src/net/error.cpp7
-rw-r--r--src/net/error.h7
-rw-r--r--src/net/resolve.cpp71
-rw-r--r--src/net/resolve.h47
-rw-r--r--src/p2p/net_node.inl15
-rw-r--r--src/p2p/net_peerlist.h9
-rw-r--r--src/rpc/bootstrap_daemon.cpp21
-rw-r--r--src/rpc/bootstrap_daemon.h12
-rw-r--r--src/rpc/core_rpc_server.cpp180
-rw-r--r--src/rpc/core_rpc_server.h13
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h50
-rw-r--r--src/simplewallet/simplewallet.cpp22
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--src/wallet/wallet_rpc_payments.cpp91
-rw-r--r--src/wallet/wallet_rpc_server.cpp36
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h24
39 files changed, 1000 insertions, 147 deletions
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index c31d1dd96..f0b617798 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -37,6 +37,7 @@
#include <boost/thread/mutex.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/optional.hpp>
+#include <boost/utility/string_ref.hpp>
using namespace epee;
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -124,6 +125,7 @@ static const char *get_record_name(int record_type)
case DNS_TYPE_A: return "A";
case DNS_TYPE_TXT: return "TXT";
case DNS_TYPE_AAAA: return "AAAA";
+ case DNS_TYPE_TLSA: return "TLSA";
default: return "unknown";
}
}
@@ -186,6 +188,13 @@ boost::optional<std::string> txt_to_string(const char* src, size_t len)
return std::string(src+1, len-1);
}
+boost::optional<std::string> tlsa_to_string(const char* src, size_t len)
+{
+ if (len < 4)
+ return boost::none;
+ return std::string(src, len);
+}
+
// custom smart pointer.
// TODO: see if std::auto_ptr and the like support custom destructors
template<typename type, void (*freefunc)(type*)>
@@ -326,11 +335,15 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec
// destructor takes care of cleanup
ub_result_ptr result;
+ MDEBUG("Performing DNSSEC " << get_record_name(record_type) << " record query for " << url);
+
// call DNS resolver, blocking. if return value not zero, something went wrong
if (!ub_resolve(m_data->m_ub_context, string_copy(url.c_str()), record_type, DNS_CLASS_IN, &result))
{
dnssec_available = (result->secure || result->bogus);
dnssec_valid = result->secure && !result->bogus;
+ if (dnssec_available && !dnssec_valid)
+ MWARNING("Invalid DNSSEC " << get_record_name(record_type) << " record signature for " << url << ": " << result->why_bogus);
if (result->havedata)
{
for (size_t i=0; result->data[i] != NULL; i++)
@@ -338,8 +351,9 @@ std::vector<std::string> DNSResolver::get_record(const std::string& url, int rec
boost::optional<std::string> res = (*reader)(result->data[i], result->len[i]);
if (res)
{
- MINFO("Found \"" << *res << "\" in " << get_record_name(record_type) << " record for " << url);
- addresses.push_back(*res);
+ // do not dump dns record directly from dns into log
+ MINFO("Found " << get_record_name(record_type) << " record for " << url);
+ addresses.push_back(std::move(*res));
}
}
}
@@ -363,6 +377,17 @@ std::vector<std::string> DNSResolver::get_txt_record(const std::string& url, boo
return get_record(url, DNS_TYPE_TXT, txt_to_string, dnssec_available, dnssec_valid);
}
+std::vector<std::string> DNSResolver::get_tlsa_tcp_record(const boost::string_ref url, const boost::string_ref port, bool& dnssec_available, bool& dnssec_valid)
+{
+ std::string service_addr;
+ service_addr.reserve(url.size() + port.size() + 7);
+ service_addr.push_back('_');
+ service_addr.append(port.data(), port.size());
+ service_addr.append("._tcp.");
+ service_addr.append(url.data(), url.size());
+ return get_record(service_addr, DNS_TYPE_TLSA, tlsa_to_string, dnssec_available, dnssec_valid);
+}
+
std::string DNSResolver::get_dns_format_from_oa_address(const std::string& oa_addr)
{
std::string addr(oa_addr);
diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h
index 30c4cced2..99e91bc54 100644
--- a/src/common/dns_utils.h
+++ b/src/common/dns_utils.h
@@ -31,15 +31,17 @@
#include <string>
#include <functional>
#include <boost/optional/optional_fwd.hpp>
+#include <boost/utility/string_ref_fwd.hpp>
namespace tools
{
// RFC defines for record types and classes for DNS, gleaned from ldns source
-const static int DNS_CLASS_IN = 1;
-const static int DNS_TYPE_A = 1;
-const static int DNS_TYPE_TXT = 16;
-const static int DNS_TYPE_AAAA = 8;
+constexpr const int DNS_CLASS_IN = 1;
+constexpr const int DNS_TYPE_A = 1;
+constexpr const int DNS_TYPE_TXT = 16;
+constexpr const int DNS_TYPE_AAAA = 8;
+constexpr const int DNS_TYPE_TLSA = 52;
struct DNSResolverData;
@@ -106,6 +108,17 @@ public:
std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid);
/**
+ * @brief gets all TLSA TCP records from a DNS query for the supplied URL;
+ * if no TLSA record present returns an empty vector.
+ *
+ * @param url A string containing a URL to query for
+ * @param port The service port number (as string) to query
+ *
+ * @return A vector of strings containing all TLSA records; or an empty vector
+ */
+ std::vector<std::string> get_tlsa_tcp_record(boost::string_ref url, boost::string_ref port, bool& dnssec_available, bool& dnssec_valid);
+
+ /**
* @brief Gets a DNS address from OpenAlias format
*
* If the address looks good, but contains one @ symbol, replace that with a .
diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h
index 7dfc5151d..1cd502994 100644
--- a/src/crypto/hash-ops.h
+++ b/src/crypto/hash-ops.h
@@ -87,6 +87,10 @@ void hash_extra_jh(const void *data, size_t length, char *hash);
void hash_extra_skein(const void *data, size_t length, char *hash);
void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash);
+bool tree_path(size_t count, size_t idx, uint32_t *path);
+bool tree_branch(const char (*hashes)[HASH_SIZE], size_t count, const char *hash, char (*branch)[HASH_SIZE], size_t *depth, uint32_t *path);
+bool tree_branch_hash(const char hash[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path, char root[HASH_SIZE]);
+bool is_branch_in_tree(const char hash[HASH_SIZE], const char root[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path);
#define RX_BLOCK_VERSION 12
void rx_slow_hash_allocate_state(void);
diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c
index fa35a32e2..801987e37 100644
--- a/src/crypto/rx-slow-hash.c
+++ b/src/crypto/rx-slow-hash.c
@@ -264,12 +264,14 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch
cache = rx_sp->rs_cache;
if (cache == NULL) {
- if (cache == NULL) {
+ if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) {
cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
if (cache == NULL) {
mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX cache");
- cache = randomx_alloc_cache(flags);
}
+ }
+ if (cache == NULL) {
+ cache = randomx_alloc_cache(flags);
if (cache == NULL)
local_abort("Couldn't allocate RandomX cache");
}
@@ -291,11 +293,14 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (!rx_dataset_nomem) {
if (rx_dataset == NULL) {
- rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
- if (rx_dataset == NULL) {
- mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
- rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
+ if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) {
+ rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
+ if (rx_dataset == NULL) {
+ mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
+ }
}
+ if (rx_dataset == NULL)
+ rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
if (rx_dataset != NULL)
rx_initdata(rx_sp->rs_cache, miners, seedheight);
}
@@ -311,11 +316,14 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch
}
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
}
- rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset);
- if(rx_vm == NULL) { //large pages failed
- mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM");
- rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
+ if (!(disabled_flags() & RANDOMX_FLAG_LARGE_PAGES)) {
+ rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset);
+ if(rx_vm == NULL) { //large pages failed
+ mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM");
+ }
}
+ if (rx_vm == NULL)
+ rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
if(rx_vm == NULL) {//fallback if everything fails
flags = RANDOMX_FLAG_DEFAULT | (miners ? RANDOMX_FLAG_FULL_MEM : 0);
rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c
index 643e95121..8f3ea3339 100644
--- a/src/crypto/tree-hash.c
+++ b/src/crypto/tree-hash.c
@@ -104,3 +104,154 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) {
free(ints);
}
}
+
+bool tree_path(size_t count, size_t idx, uint32_t *path)
+{
+ if (count == 0)
+ return false;
+
+ if (count == 1) {
+ *path = 0;
+ } else if (count == 2) {
+ *path = idx == 0 ? 0 : 1;
+ } else {
+ size_t i, j;
+
+ *path = 0;
+ size_t cnt = tree_hash_cnt( count );
+
+ for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) {
+ if (idx == i || idx == i+1)
+ {
+ *path = (*path << 1) | (idx == i ? 0 : 1);
+ idx = j;
+ }
+ }
+ assert(i == count);
+
+ while (cnt > 2) {
+ cnt >>= 1;
+ for (i = 0, j = 0; j < cnt; i += 2, ++j) {
+ if (idx == i || idx == i + 1)
+ {
+ *path = (*path << 1) | (idx == i ? 0 : 1);
+ idx = j;
+ }
+ }
+ }
+
+ if (idx == 0 || idx == 1)
+ {
+ *path = (*path << 1) | (idx == 0 ? 0 : 1);
+ idx = 0;
+ }
+ }
+ return true;
+}
+
+bool tree_branch(const char (*hashes)[HASH_SIZE], size_t count, const char *hash, char (*branch)[HASH_SIZE], size_t *depth, uint32_t *path)
+{
+ size_t idx;
+
+ if (count == 0)
+ return false;
+
+ for (idx = 0; idx < count; ++idx)
+ if (!memcmp(hash, hashes[idx], HASH_SIZE))
+ break;
+ if (idx == count)
+ return false;
+
+ assert(count > 0);
+ if (count == 1) {
+ *depth = 0;
+ *path = 0;
+ } else if (count == 2) {
+ *depth = 1;
+ *path = idx == 0 ? 0 : 1;
+ memcpy(branch[0], hashes[idx ^ 1], HASH_SIZE);
+ } else {
+ size_t i, j;
+
+ *depth = 0;
+ *path = 0;
+ size_t cnt = tree_hash_cnt( count );
+
+ char *ints = calloc(cnt, HASH_SIZE); // zero out as extra protection for using uninitialized mem
+ assert(ints);
+
+ memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE);
+
+ for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) {
+ if (idx == i || idx == i+1)
+ {
+ memcpy(branch[*depth], hashes[idx == i ? i + 1 : i], HASH_SIZE);
+ ++*depth;
+ *path = (*path << 1) | (idx == i ? 0 : 1);
+ idx = j;
+ }
+ cn_fast_hash(hashes[i], 64, ints + j * HASH_SIZE);
+ }
+ assert(i == count);
+
+ while (cnt > 2) {
+ cnt >>= 1;
+ for (i = 0, j = 0; j < cnt; i += 2, ++j) {
+ if (idx == i || idx == i + 1)
+ {
+ memcpy(branch[*depth], ints + (idx == i ? i + 1 : i) * HASH_SIZE, HASH_SIZE);
+ ++*depth;
+ *path = (*path << 1) | (idx == i ? 0 : 1);
+ idx = j;
+ }
+ cn_fast_hash(ints + i * HASH_SIZE, 64, ints + j * HASH_SIZE);
+ }
+ }
+
+ if (idx == 0 || idx == 1)
+ {
+ memcpy(branch[*depth], ints + (idx == 0 ? 1 : 0) * HASH_SIZE, HASH_SIZE);
+ ++*depth;
+ *path = (*path << 1) | (idx == 0 ? 0 : 1);
+ idx = 0;
+ }
+
+ free(ints);
+ }
+ return true;
+}
+
+bool tree_branch_hash(const char hash[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path, char root[HASH_SIZE])
+{
+ size_t d;
+ char partial[HASH_SIZE];
+
+ memcpy(partial, hash, HASH_SIZE);
+
+ for (d = 0; d < depth; ++d)
+ {
+ char buffer[2 * HASH_SIZE];
+ if ((path >> (depth - d - 1)) & 1)
+ {
+ memcpy(buffer, branch[d], HASH_SIZE);
+ memcpy(buffer + HASH_SIZE, partial, HASH_SIZE);
+ }
+ else
+ {
+ memcpy(buffer, partial, HASH_SIZE);
+ memcpy(buffer + HASH_SIZE, branch[d], HASH_SIZE);
+ }
+ cn_fast_hash(buffer, 2 * HASH_SIZE, partial);
+ }
+
+ memcpy(root, partial, HASH_SIZE);
+ return true;
+}
+
+bool is_branch_in_tree(const char hash[HASH_SIZE], const char root[HASH_SIZE], const char (*branch)[HASH_SIZE], size_t depth, uint32_t path)
+{
+ char res[HASH_SIZE];
+ if (!tree_branch_hash(hash, branch, depth, path, res))
+ return false;
+ return memcmp(res, root, HASH_SIZE) == 0;
+}
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index 5286256c7..c9fb1433c 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -43,6 +43,7 @@ set(cryptonote_basic_sources
cryptonote_format_utils.cpp
difficulty.cpp
hardfork.cpp
+ merge_mining.cpp
miner.cpp)
set(cryptonote_basic_headers)
@@ -57,6 +58,7 @@ set(cryptonote_basic_private_headers
cryptonote_format_utils.h
difficulty.h
hardfork.h
+ merge_mining.h
miner.h
tx_extra.h
verification_context.h)
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 1ef6590eb..3e4532d4e 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -727,6 +727,25 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ bool add_mm_merkle_root_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::hash& mm_merkle_root, size_t mm_merkle_tree_depth)
+ {
+ CHECK_AND_ASSERT_MES(mm_merkle_tree_depth < 32, false, "merge mining merkle tree depth should be less than 32");
+ size_t start_pos = tx_extra.size();
+ tx_extra.resize(tx_extra.size() + 3 + 32);
+ //write tag
+ tx_extra[start_pos] = TX_EXTRA_MERGE_MINING_TAG;
+ //write data size
+ ++start_pos;
+ tx_extra[start_pos] = 33;
+ //write depth varint (always one byte here)
+ ++start_pos;
+ tx_extra[start_pos] = mm_merkle_tree_depth;
+ //write data
+ ++start_pos;
+ memcpy(&tx_extra[start_pos], &mm_merkle_root, 32);
+ return true;
+ }
+ //---------------------------------------------------------------
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type)
{
if (tx_extra.empty())
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 636a88b9a..b311bd2b2 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -83,6 +83,7 @@ namespace cryptonote
std::vector<crypto::public_key> get_additional_tx_pub_keys_from_extra(const transaction_prefix& tx);
bool add_additional_tx_pub_keys_to_extra(std::vector<uint8_t>& tx_extra, const std::vector<crypto::public_key>& additional_pub_keys);
bool add_extra_nonce_to_tx_extra(std::vector<uint8_t>& tx_extra, const blobdata& extra_nonce);
+ bool add_mm_merkle_root_to_tx_extra(std::vector<uint8_t>& tx_extra, const crypto::hash& mm_merkle_root, size_t mm_merkle_tree_depth);
bool remove_field_from_tx_extra(std::vector<uint8_t>& tx_extra, const std::type_info &type);
void set_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash& payment_id);
void set_encrypted_payment_id_to_tx_extra_nonce(blobdata& extra_nonce, const crypto::hash8& payment_id);
diff --git a/src/cryptonote_basic/merge_mining.cpp b/src/cryptonote_basic/merge_mining.cpp
new file mode 100644
index 000000000..fcc74859f
--- /dev/null
+++ b/src/cryptonote_basic/merge_mining.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) 2020, 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.
+
+#include <string.h>
+#include "misc_log_ex.h"
+#include "int-util.h"
+#include "crypto/crypto.h"
+#include "common/util.h"
+#include "merge_mining.h"
+
+using namespace epee;
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "cn.mm"
+
+using namespace crypto;
+
+namespace cryptonote
+{
+
+//---------------------------------------------------------------
+uint32_t get_aux_slot(const crypto::hash &id, uint32_t nonce, uint32_t n_aux_chains)
+{
+ CHECK_AND_ASSERT_THROW_MES(n_aux_chains > 0, "n_aux_chains is 0");
+
+ uint8_t buf[HASH_SIZE + sizeof(uint32_t) + 1];
+ memcpy(buf, &id, HASH_SIZE);
+ uint32_t v = SWAP32LE(nonce);
+ memcpy(buf + HASH_SIZE, &v, sizeof(uint32_t));
+ buf[HASH_SIZE + sizeof(uint32_t)] = config::HASH_KEY_MM_SLOT;
+
+ crypto::hash res;
+ tools::sha256sum(buf, sizeof(buf), res);
+ v = *((const uint32_t*)&res);
+ return SWAP32LE(v) % n_aux_chains;
+}
+//---------------------------------------------------------------
+uint32_t get_path_from_aux_slot(uint32_t slot, uint32_t n_aux_chains)
+{
+ CHECK_AND_ASSERT_THROW_MES(n_aux_chains > 0, "n_aux_chains is 0");
+ CHECK_AND_ASSERT_THROW_MES(slot < n_aux_chains, "slot >= n_aux_chains");
+
+ uint32_t path = 0;
+ CHECK_AND_ASSERT_THROW_MES(tree_path(n_aux_chains, slot, &path), "Failed to get path from aux slot");
+ return path;
+}
+//---------------------------------------------------------------
+uint32_t encode_mm_depth(uint32_t n_aux_chains, uint32_t nonce)
+{
+ CHECK_AND_ASSERT_THROW_MES(n_aux_chains > 0, "n_aux_chains is 0");
+
+ // how many bits to we need to representing n_aux_chains - 1
+ uint32_t n_bits = 1;
+ while ((1u << n_bits) < n_aux_chains && n_bits < 16)
+ ++n_bits;
+ CHECK_AND_ASSERT_THROW_MES(n_bits <= 16, "Way too many bits required");
+
+ const uint32_t depth = (n_bits - 1) | ((n_aux_chains - 1) << 3) | (nonce << (3 + n_bits));
+ return depth;
+}
+//---------------------------------------------------------------
+bool decode_mm_depth(uint32_t depth, uint32_t &n_aux_chains, uint32_t &nonce)
+{
+ const uint32_t n_bits = 1 + (depth & 7);
+ n_aux_chains = 1 + (depth >> 3 & ((1 << n_bits) - 1));
+ nonce = depth >> (3 + n_bits);
+ return true;
+}
+//---------------------------------------------------------------
+}
diff --git a/src/cryptonote_basic/merge_mining.h b/src/cryptonote_basic/merge_mining.h
new file mode 100644
index 000000000..378438f7c
--- /dev/null
+++ b/src/cryptonote_basic/merge_mining.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020, 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.
+
+#pragma once
+
+#include <stdint.h>
+#include "crypto/crypto.h"
+
+namespace cryptonote
+{
+ uint32_t get_aux_slot(const crypto::hash &id, uint32_t nonce, uint32_t n_aux_chains);
+ uint32_t get_path_from_aux_slot(uint32_t slot, uint32_t n_aux_chains);
+ uint32_t encode_mm_depth(uint32_t n_aux_chains, uint32_t nonce);
+ bool decode_mm_depth(uint32_t depth, uint32_t &n_aux_chains, uint32_t &nonce);
+}
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 6327f7379..915835d1b 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -234,6 +234,7 @@ namespace config
const unsigned char HASH_KEY_CLSAG_AGG_0[] = "CLSAG_agg_0";
const unsigned char HASH_KEY_CLSAG_AGG_1[] = "CLSAG_agg_1";
const char HASH_KEY_MESSAGE_SIGNING[] = "MoneroMessageSignature";
+ const unsigned char HASH_KEY_MM_SLOT = 'm';
namespace testnet
{
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index c798dbcdb..5580998fb 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -2840,15 +2840,12 @@ skip:
epee::string_tools::to_string_hex(context.m_pruning_seed) <<
"), score " << score << ", flush_all_spans " << flush_all_spans);
- m_block_queue.flush_spans(context.m_connection_id, flush_all_spans);
+ if (score > 0)
+ m_p2p->add_host_fail(context.m_remote_address, score);
- // copy since dropping the connection will invalidate the context, and thus the address
- const auto remote_address = context.m_remote_address;
+ m_block_queue.flush_spans(context.m_connection_id, flush_all_spans);
m_p2p->drop_connection(context);
-
- if (score > 0)
- m_p2p->add_host_fail(remote_address, score);
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 5a7560874..be38752da 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -986,17 +986,67 @@ bool t_command_parser_executor::check_blockchain_pruning(const std::vector<std::
bool t_command_parser_executor::set_bootstrap_daemon(const std::vector<std::string>& args)
{
- const size_t args_count = args.size();
- if (args_count < 1 || args_count > 3)
+ struct parsed_t
+ {
+ std::string address;
+ std::string user;
+ std::string password;
+ std::string proxy;
+ };
+
+ boost::optional<parsed_t> parsed = [&args]() -> boost::optional<parsed_t> {
+ const size_t args_count = args.size();
+ if (args_count == 0)
+ {
+ return {};
+ }
+ if (args[0] == "auto")
+ {
+ if (args_count == 1)
+ {
+ return {{args[0], "", "", ""}};
+ }
+ if (args_count == 2)
+ {
+ return {{args[0], "", "", args[1]}};
+ }
+ }
+ else if (args[0] == "none")
+ {
+ if (args_count == 1)
+ {
+ return {{"", "", "", ""}};
+ }
+ }
+ else
+ {
+ if (args_count == 1)
+ {
+ return {{args[0], "", "", ""}};
+ }
+ if (args_count == 2)
+ {
+ return {{args[0], "", "", args[1]}};
+ }
+ if (args_count == 3)
+ {
+ return {{args[0], args[1], args[2], ""}};
+ }
+ if (args_count == 4)
+ {
+ return {{args[0], args[1], args[2], args[3]}};
+ }
+ }
+ return {};
+ }();
+
+ if (!parsed)
{
std::cout << "Invalid syntax: Wrong number of parameters. For more details, use the help command." << std::endl;
return true;
}
- return m_executor.set_bootstrap_daemon(
- args[0] != "none" ? args[0] : std::string(),
- args_count > 1 ? args[1] : std::string(),
- args_count > 2 ? args[2] : std::string());
+ return m_executor.set_bootstrap_daemon(parsed->address, parsed->user, parsed->password, parsed->proxy);
}
bool t_command_parser_executor::flush_cache(const std::vector<std::string>& args)
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 4768bb842..63f44c4cd 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -326,7 +326,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"set_bootstrap_daemon"
, std::bind(&t_command_parser_executor::set_bootstrap_daemon, &m_parser, p::_1)
- , "set_bootstrap_daemon (auto | none | host[:port] [username] [password])"
+ , "set_bootstrap_daemon (auto | none | host[:port] [username] [password]) [proxy_ip:proxy_port]"
, "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced.\n"
"Use 'auto' to enable automatic public nodes discovering and bootstrap daemon switching"
);
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 8194fe642..16e6a304c 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -2405,7 +2405,8 @@ bool t_rpc_command_executor::check_blockchain_pruning()
bool t_rpc_command_executor::set_bootstrap_daemon(
const std::string &address,
const std::string &username,
- const std::string &password)
+ const std::string &password,
+ const std::string &proxy)
{
cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request req;
cryptonote::COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response res;
@@ -2414,6 +2415,7 @@ bool t_rpc_command_executor::set_bootstrap_daemon(
req.address = address;
req.username = username;
req.password = password;
+ req.proxy = proxy;
if (m_is_rpc)
{
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index 6fb5d6903..118f04731 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -168,7 +168,8 @@ public:
bool set_bootstrap_daemon(
const std::string &address,
const std::string &username,
- const std::string &password);
+ const std::string &password,
+ const std::string &proxy);
bool rpc_payments();
diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp
index dd4701e4f..c039b93c5 100644
--- a/src/debug_utilities/cn_deserialize.cpp
+++ b/src/debug_utilities/cn_deserialize.cpp
@@ -133,6 +133,18 @@ int main(int argc, char* argv[])
{
std::cout << "Parsed block:" << std::endl;
std::cout << cryptonote::obj_to_json_str(block) << std::endl;
+ bool parsed = cryptonote::parse_tx_extra(block.miner_tx.extra, fields);
+ if (!parsed)
+ std::cout << "Failed to parse tx_extra" << std::endl;
+
+ if (!fields.empty())
+ {
+ print_extra_fields(fields);
+ }
+ else
+ {
+ std::cout << "No fields were found in tx_extra" << std::endl;
+ }
}
else if (cryptonote::parse_and_validate_tx_from_blob(blob, tx) || cryptonote::parse_and_validate_tx_base_from_blob(blob, tx))
{
diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp
index f59be1573..70dc7f539 100644
--- a/src/device_trezor/device_trezor_base.cpp
+++ b/src/device_trezor/device_trezor_base.cpp
@@ -365,15 +365,14 @@ namespace trezor {
void device_trezor_base::device_state_initialize_unsafe()
{
require_connected();
- std::string tmp_session_id;
auto initMsg = std::make_shared<messages::management::Initialize>();
const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() {
- memwipe(&tmp_session_id[0], tmp_session_id.size());
+ if (initMsg->has_session_id())
+ memwipe(&(*initMsg->mutable_session_id())[0], initMsg->mutable_session_id()->size());
});
if(!m_device_session_id.empty()) {
- tmp_session_id.assign(m_device_session_id.data(), m_device_session_id.size());
- initMsg->set_allocated_session_id(&tmp_session_id);
+ initMsg->set_allocated_session_id(new std::string(m_device_session_id.data(), m_device_session_id.size()));
}
m_features = this->client_exchange<messages::management::Features>(initMsg);
@@ -382,8 +381,6 @@ namespace trezor {
} else {
m_device_session_id.clear();
}
-
- initMsg->release_session_id();
}
void device_trezor_base::device_state_reset()
@@ -453,18 +450,14 @@ namespace trezor {
pin = m_pin;
}
- std::string pin_field;
messages::common::PinMatrixAck m;
if (pin) {
- pin_field.assign(pin->data(), pin->size());
- m.set_allocated_pin(&pin_field);
+ m.set_allocated_pin(new std::string(pin->data(), pin->size()));
}
const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() {
- m.release_pin();
- if (!pin_field.empty()){
- memwipe(&pin_field[0], pin_field.size());
- }
+ if (m.has_pin())
+ memwipe(&(*m.mutable_pin())[0], m.mutable_pin()->size());
});
resp = call_raw(&m);
@@ -499,7 +492,6 @@ namespace trezor {
boost::optional<epee::wipeable_string> passphrase;
TREZOR_CALLBACK_GET(passphrase, on_passphrase_request, on_device);
- std::string passphrase_field;
messages::common::PassphraseAck m;
m.set_on_device(on_device);
if (!on_device) {
@@ -512,16 +504,13 @@ namespace trezor {
}
if (passphrase) {
- passphrase_field.assign(passphrase->data(), passphrase->size());
- m.set_allocated_passphrase(&passphrase_field);
+ m.set_allocated_passphrase(new std::string(passphrase->data(), passphrase->size()));
}
}
const auto data_cleaner = epee::misc_utils::create_scope_leave_handler([&]() {
- m.release_passphrase();
- if (!passphrase_field.empty()){
- memwipe(&passphrase_field[0], passphrase_field.size());
- }
+ if (m.has_passphrase())
+ memwipe(&(m.mutable_passphrase())[0], m.mutable_passphrase()->size());
});
resp = call_raw(&m);
diff --git a/src/device_trezor/device_trezor_base.hpp b/src/device_trezor/device_trezor_base.hpp
index 4db8f0c8e..0162b23df 100644
--- a/src/device_trezor/device_trezor_base.hpp
+++ b/src/device_trezor/device_trezor_base.hpp
@@ -165,7 +165,7 @@ namespace trezor {
// Scoped session closer
BOOST_SCOPE_EXIT_ALL(&, this) {
- if (open_session){
+ if (open_session && this->get_transport()){
this->get_transport()->close();
}
};
diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp
index 288f3ddca..92150b579 100644
--- a/src/device_trezor/trezor/protocol.cpp
+++ b/src/device_trezor/trezor/protocol.cpp
@@ -502,21 +502,9 @@ namespace tx {
}
void Signer::compute_integrated_indices(TsxData * tsx_data){
- if (m_aux_data == nullptr || m_aux_data->tx_recipients.empty()){
- return;
- }
-
auto & chg = tsx_data->change_dts();
std::string change_hash = hash_addr(&chg.addr(), chg.amount(), chg.is_subaddress());
-
std::vector<uint32_t> integrated_indices;
- std::set<std::string> integrated_hashes;
- for (auto & cur : m_aux_data->tx_recipients){
- if (!cur.has_payment_id){
- continue;
- }
- integrated_hashes.emplace(hash_addr(&cur.address.m_spend_public_key, &cur.address.m_view_public_key));
- }
ssize_t idx = -1;
for (auto & cur : tsx_data->outputs()){
@@ -527,8 +515,7 @@ namespace tx {
continue;
}
- c_hash = hash_addr(&cur.addr());
- if (integrated_hashes.find(c_hash) != integrated_hashes.end()){
+ if (cur.is_integrated()){
integrated_indices.push_back((uint32_t)idx);
}
}
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt
index afcd42ef7..e93e27bcd 100644
--- a/src/net/CMakeLists.txt
+++ b/src/net/CMakeLists.txt
@@ -26,10 +26,10 @@
# 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.
-set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp socks.cpp
- socks_connect.cpp tor_address.cpp zmq.cpp)
-set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h socks_connect.h
- tor_address.h zmq.h)
+set(net_sources dandelionpp.cpp error.cpp http.cpp i2p_address.cpp parse.cpp resolve.cpp
+ socks.cpp socks_connect.cpp tor_address.cpp zmq.cpp)
+set(net_headers dandelionpp.h error.h http.cpp i2p_address.h parse.h socks.h resolve.h
+ socks_connect.h tor_address.h zmq.h)
monero_add_library(net ${net_sources} ${net_headers})
target_link_libraries(net common epee ${ZMQ_LIB} ${Boost_ASIO_LIBRARY})
diff --git a/src/net/error.cpp b/src/net/error.cpp
index 037f44d52..d2e713bc5 100644
--- a/src/net/error.cpp
+++ b/src/net/error.cpp
@@ -47,12 +47,18 @@ namespace
{
switch (net::error(value))
{
+ case net::error::bogus_dnssec:
+ return "Invalid response signature from DNSSEC enabled domain";
+ case net::error::dns_query_failure:
+ return "Failed to retrieve desired DNS record";
case net::error::expected_tld:
return "Expected top-level domain";
case net::error::invalid_host:
return "Host value is not valid";
case net::error::invalid_i2p_address:
return "Invalid I2P address";
+ case net::error::invalid_mask:
+ return "CIDR netmask outside of 0-32 range";
case net::error::invalid_port:
return "Invalid port value (expected 0-65535)";
case net::error::invalid_tor_address:
@@ -71,6 +77,7 @@ namespace
switch (net::error(value))
{
case net::error::invalid_port:
+ case net::error::invalid_mask:
return std::errc::result_out_of_range;
case net::error::expected_tld:
case net::error::invalid_tor_address:
diff --git a/src/net/error.h b/src/net/error.h
index 7c852dd20..746eb0ecb 100644
--- a/src/net/error.h
+++ b/src/net/error.h
@@ -37,13 +37,16 @@ namespace net
enum class error : int
{
// 0 reserved for success (as per expect<T>)
- expected_tld = 1, //!< Expected a tld
+ bogus_dnssec = 1, //!< Invalid response signature from DNSSEC enabled domain
+ dns_query_failure, //!< Failed to retrieve desired DNS record
+ expected_tld, //!< Expected a tld
invalid_host, //!< Hostname is not valid
invalid_i2p_address,
+ invalid_mask, //!< Outside of 0-32 range
invalid_port, //!< Outside of 0-65535 range
invalid_tor_address,//!< Invalid base32 or length
unsupported_address,//!< Type not supported by `get_network_address`
- invalid_mask, //!< Outside of 0-32 range
+
};
//! \return `std::error_category` for `net` namespace.
diff --git a/src/net/resolve.cpp b/src/net/resolve.cpp
new file mode 100644
index 000000000..1b43cf6c4
--- /dev/null
+++ b/src/net/resolve.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2020, 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.
+
+#include "net/resolve.h"
+
+#include <boost/utility/string_ref.hpp>
+#include "common/dns_utils.h"
+#include "common/expect.h"
+#include "net/error.h"
+
+namespace net
+{
+namespace dnssec
+{
+ expect<service_response> resolve_hostname(const std::string& addr, const std::string& tlsa_port)
+ {
+ // use basic (blocking) unbound for now, possibly refactor later
+ tools::DNSResolver& resolver = tools::DNSResolver::instance();
+
+ bool dnssec_available = false;
+ bool dnssec_valid = false;
+ std::vector<std::string> ip_records = resolver.get_ipv4(addr, dnssec_available, dnssec_valid);
+
+ if (dnssec_available && !dnssec_valid)
+ return {net::error::bogus_dnssec};
+
+ if (ip_records.empty())
+ {
+ ip_records = resolver.get_ipv6(addr, dnssec_available, dnssec_valid);
+ if (dnssec_available && !dnssec_valid)
+ return {net::error::bogus_dnssec};
+ if (ip_records.empty())
+ return {net::error::dns_query_failure};
+ }
+
+ std::vector<std::string> tlsa{};
+ if (dnssec_available && !tlsa_port.empty())
+ {
+ tlsa = resolver.get_tlsa_tcp_record(addr, tlsa_port, dnssec_available, dnssec_valid);
+ if (!dnssec_valid)
+ return {net::error::bogus_dnssec};
+ }
+ return {{std::move(ip_records), std::move(tlsa)}};
+ }
+} // dnssec
+} // net
diff --git a/src/net/resolve.h b/src/net/resolve.h
new file mode 100644
index 000000000..46bd8e617
--- /dev/null
+++ b/src/net/resolve.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2020, 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.
+
+#include <string>
+#include <vector>
+
+template<typename> class expect;
+
+namespace net
+{
+namespace dnssec
+{
+ struct service_response
+ {
+ std::vector<std::string> ip; //!< IPv4/6 records in dotted or semicolon notation
+ std::vector<std::string> tlsa; //!< DANE/TLSA records
+ };
+
+ //! \return IP + (optionally) DANE/TLSA records, failing if DNSSEC signature is "bogus"
+ expect<service_response> resolve_hostname(const std::string& addr, const std::string& tlsa_port = {});
+} // dnssec
+} // net
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 07b45b9bd..cf0b1db10 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -1203,9 +1203,8 @@ namespace nodetool
if(!handle_remote_peerlist(rsp.local_peerlist_new, context))
{
LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection.");
- const auto remote_address = context.m_remote_address;
m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id );
- add_host_fail(remote_address);
+ add_host_fail(context.m_remote_address);
}
if(!context.m_is_income)
m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
@@ -1369,7 +1368,7 @@ namespace nodetool
if(just_take_peerlist)
{
zone.m_net_server.get_config_object().close(con->m_connection_id);
- MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED.");
+ LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED.");
return true;
}
@@ -1431,7 +1430,7 @@ namespace nodetool
zone.m_net_server.get_config_object().close(con->m_connection_id);
- MDEBUG(na.str() << "CONNECTION HANDSHAKED OK AND CLOSED.");
+ LOG_DEBUG_CC(*con, "CONNECTION HANDSHAKED OK AND CLOSED.");
return true;
}
@@ -2458,14 +2457,12 @@ namespace nodetool
template<class t_payload_net_handler>
int node_server<t_payload_net_handler>::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context)
{
- // copy since dropping the connection will invalidate the context, and thus the address
- const auto remote_address = context.m_remote_address;
-
if(arg.node_data.network_id != m_network_id)
{
+
LOG_INFO_CC(context, "WRONG NETWORK AGENT CONNECTED! id=" << arg.node_data.network_id);
drop_connection(context);
- add_host_fail(remote_address);
+ add_host_fail(context.m_remote_address);
return 1;
}
@@ -2473,7 +2470,7 @@ namespace nodetool
{
LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came not from incoming connection");
drop_connection(context);
- add_host_fail(remote_address);
+ add_host_fail(context.m_remote_address);
return 1;
}
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index c794b0f3b..d8de6abe9 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -110,7 +110,7 @@ namespace nodetool
bool get_gray_peer_by_index(peerlist_entry& p, size_t i);
template<typename F> bool foreach(bool white, const F &f);
void evict_host_from_white_peerlist(const peerlist_entry& pr);
- bool append_with_peer_white(const peerlist_entry& pr);
+ bool append_with_peer_white(const peerlist_entry& pr, bool trust_last_seen = false);
bool append_with_peer_gray(const peerlist_entry& pr);
bool append_with_peer_anchor(const anchor_peerlist_entry& ple);
bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash);
@@ -329,12 +329,12 @@ namespace nodetool
ple.pruning_seed = pruning_seed;
ple.rpc_port = rpc_port;
ple.rpc_credits_per_hash = rpc_credits_per_hash;
- return append_with_peer_white(ple);
+ return append_with_peer_white(ple, true);
CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false);
}
//--------------------------------------------------------------------------------------------------
inline
- bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple)
+ bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple, bool trust_last_seen)
{
TRY_ENTRY();
if(!is_host_allowed(ple.adr))
@@ -357,7 +357,8 @@ namespace nodetool
new_ple.pruning_seed = by_addr_it_wt->pruning_seed;
if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
new_ple.rpc_port = by_addr_it_wt->rpc_port;
- new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
+ if (!trust_last_seen)
+ new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer lists are untrusted
m_peers_white.replace(by_addr_it_wt, new_ple);
}
//remove from gray list, if need
diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp
index 2fdd28406..ffea906d5 100644
--- a/src/rpc/bootstrap_daemon.cpp
+++ b/src/rpc/bootstrap_daemon.cpp
@@ -7,6 +7,7 @@
#include "crypto/crypto.h"
#include "cryptonote_core/cryptonote_core.h"
#include "misc_log_ex.h"
+#include "net/parse.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc.bootstrap_daemon"
@@ -16,19 +17,23 @@ namespace cryptonote
bootstrap_daemon::bootstrap_daemon(
std::function<std::map<std::string, bool>()> get_public_nodes,
- bool rpc_payment_enabled)
+ bool rpc_payment_enabled,
+ const std::string &proxy)
: m_selector(new bootstrap_node::selector_auto(std::move(get_public_nodes)))
, m_rpc_payment_enabled(rpc_payment_enabled)
{
+ set_proxy(proxy);
}
bootstrap_daemon::bootstrap_daemon(
const std::string &address,
boost::optional<epee::net_utils::http::login> credentials,
- bool rpc_payment_enabled)
+ bool rpc_payment_enabled,
+ const std::string &proxy)
: m_selector(nullptr)
, m_rpc_payment_enabled(rpc_payment_enabled)
{
+ set_proxy(proxy);
if (!set_server(address, std::move(credentials)))
{
throw std::runtime_error("invalid bootstrap daemon address or credentials");
@@ -78,6 +83,18 @@ namespace cryptonote
return success;
}
+ void bootstrap_daemon::set_proxy(const std::string &address)
+ {
+ if (!address.empty() && !net::get_tcp_endpoint(address))
+ {
+ throw std::runtime_error("invalid proxy address format");
+ }
+ if (!m_http_client.set_proxy(address))
+ {
+ throw std::runtime_error("failed to set proxy address");
+ }
+ }
+
bool bootstrap_daemon::set_server(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials /* = boost::none */)
{
if (!m_http_client.set_server(address, credentials))
diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h
index d54042b11..1e4477123 100644
--- a/src/rpc/bootstrap_daemon.h
+++ b/src/rpc/bootstrap_daemon.h
@@ -8,7 +8,7 @@
#include <boost/thread/mutex.hpp>
#include <boost/utility/string_ref.hpp>
-#include "net/http_client.h"
+#include "net/http.h"
#include "storages/http_abstract_invoke.h"
#include "bootstrap_node_selector.h"
@@ -21,11 +21,13 @@ namespace cryptonote
public:
bootstrap_daemon(
std::function<std::map<std::string, bool>()> get_public_nodes,
- bool rpc_payment_enabled);
+ bool rpc_payment_enabled,
+ const std::string &proxy);
bootstrap_daemon(
const std::string &address,
boost::optional<epee::net_utils::http::login> credentials,
- bool rpc_payment_enabled);
+ bool rpc_payment_enabled,
+ const std::string &proxy);
std::string address() const noexcept;
boost::optional<std::pair<uint64_t, uint64_t>> get_height();
@@ -72,12 +74,14 @@ namespace cryptonote
return handle_result(result, result_struct.status);
}
+ void set_proxy(const std::string &address);
+
private:
bool set_server(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials = boost::none);
bool switch_server_if_needed();
private:
- epee::net_utils::http::http_simple_client m_http_client;
+ net::http::client m_http_client;
const bool m_rpc_payment_enabled;
const std::unique_ptr<bootstrap_node::selector> m_selector;
boost::mutex m_selector_mutex;
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 2bb4969e9..40c04b4f9 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -44,6 +44,7 @@ using namespace epee;
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_basic/merge_mining.h"
#include "cryptonote_core/tx_sanity_check.h"
#include "misc_language.h"
#include "net/parse.h"
@@ -154,6 +155,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_restricted_rpc);
command_line::add_arg(desc, arg_bootstrap_daemon_address);
command_line::add_arg(desc, arg_bootstrap_daemon_login);
+ command_line::add_arg(desc, arg_bootstrap_daemon_proxy);
cryptonote::rpc_args::init_options(desc, true);
command_line::add_arg(desc, arg_rpc_payment_address);
command_line::add_arg(desc, arg_rpc_payment_difficulty);
@@ -172,7 +174,10 @@ namespace cryptonote
, m_rpc_payment_allow_free_loopback(false)
{}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password)
+ bool core_rpc_server::set_bootstrap_daemon(
+ const std::string &address,
+ const std::string &username_password,
+ const std::string &proxy)
{
boost::optional<epee::net_utils::http::login> credentials;
const auto loc = username_password.find(':');
@@ -180,7 +185,7 @@ namespace cryptonote
{
credentials = epee::net_utils::http::login(username_password.substr(0, loc), username_password.substr(loc + 1));
}
- return set_bootstrap_daemon(address, credentials);
+ return set_bootstrap_daemon(address, credentials, proxy);
}
//------------------------------------------------------------------------------------------------------------------------------
std::map<std::string, bool> core_rpc_server::get_public_nodes(uint32_t credits_per_hash_threshold/* = 0*/)
@@ -217,7 +222,10 @@ namespace cryptonote
return result;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials)
+ bool core_rpc_server::set_bootstrap_daemon(
+ const std::string &address,
+ const boost::optional<epee::net_utils::http::login> &credentials,
+ const std::string &proxy)
{
boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
@@ -233,11 +241,11 @@ namespace cryptonote
auto get_nodes = [this]() {
return get_public_nodes(credits_per_hash_threshold);
};
- m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled));
+ m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled, proxy));
}
else
{
- m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled));
+ m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled, proxy));
}
m_should_use_bootstrap_daemon = m_bootstrap_daemon.get() != nullptr;
@@ -278,6 +286,7 @@ namespace cryptonote
}
}
disable_rpc_ban = rpc_config->disable_rpc_ban;
+ const std::string data_dir{command_line::get_arg(vm, cryptonote::arg_data_dir)};
std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
if (!address.empty() && allow_rpc_payment)
{
@@ -306,7 +315,7 @@ namespace cryptonote
}
m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback);
m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
- m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir));
+ m_rpc_payment->load(data_dir);
m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff));
}
@@ -318,8 +327,10 @@ namespace cryptonote
MWARNING("The RPC server is accessible from the outside, but no RPC payment was setup. RPC access will be free for all.");
}
- if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
- command_line::get_arg(vm, arg_bootstrap_daemon_login)))
+ if (!set_bootstrap_daemon(
+ command_line::get_arg(vm, arg_bootstrap_daemon_address),
+ command_line::get_arg(vm, arg_bootstrap_daemon_login),
+ command_line::get_arg(vm, arg_bootstrap_daemon_proxy)))
{
MFATAL("Failed to parse bootstrap daemon address");
return false;
@@ -333,12 +344,32 @@ namespace cryptonote
if (m_rpc_payment)
m_net_server.add_idle_handler([this](){ return m_rpc_payment->on_idle(); }, 60 * 1000);
+ bool store_ssl_key = !restricted && rpc_config->ssl_options && rpc_config->ssl_options.auth.certificate_path.empty();
+ const auto ssl_base_path = (boost::filesystem::path{data_dir} / "rpc_ssl").string();
+ if (store_ssl_key && boost::filesystem::exists(ssl_base_path + ".crt"))
+ {
+ // load key from previous run, password prompted by OpenSSL
+ store_ssl_key = false;
+ rpc_config->ssl_options.auth =
+ epee::net_utils::ssl_authentication_t{ssl_base_path + ".key", ssl_base_path + ".crt"};
+ }
+
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
- return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
+ const bool inited = epee::http_server_impl_base<core_rpc_server, connection_context>::init(
rng, std::move(port), std::move(bind_ip_str),
std::move(bind_ipv6_str), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
);
+
+ if (store_ssl_key && inited)
+ {
+ // new keys were generated, store for next run
+ const auto error = epee::net_utils::store_ssl_keys(m_net_server.get_ssl_context(), ssl_base_path);
+ if (error)
+ MFATAL("Failed to store HTTP SSL cert/key for " << (restricted ? "restricted " : "") << "RPC server: " << error.message());
+ return !bool(error);
+ }
+ return inited;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::check_payment(const std::string &client_message, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash)
@@ -1595,15 +1626,15 @@ namespace cryptonote
{
credentials = epee::net_utils::http::login(req.username, req.password);
}
-
- if (set_bootstrap_daemon(req.address, credentials))
+
+ if (set_bootstrap_daemon(req.address, credentials, req.proxy))
{
res.status = CORE_RPC_STATUS_OK;
}
else
{
res.status = "Failed to set bootstrap daemon";
- }
+ }
return true;
}
@@ -1826,6 +1857,125 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_add_aux_pow(const COMMAND_RPC_ADD_AUX_POW::request& req, COMMAND_RPC_ADD_AUX_POW::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(add_aux_pow);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ADD_AUX_POW>(invoke_http_mode::JON_RPC, "add_aux_pow", req, res, r))
+ return r;
+
+ if (req.aux_pow.empty())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Empty aux pow hash vector";
+ return false;
+ }
+
+ crypto::hash merkle_root;
+ size_t merkle_tree_depth = 0;
+ std::vector<std::pair<crypto::hash, crypto::hash>> aux_pow;
+ std::vector<crypto::hash> aux_pow_raw;
+ aux_pow.reserve(req.aux_pow.size());
+ aux_pow_raw.reserve(req.aux_pow.size());
+ for (const auto &s: req.aux_pow)
+ {
+ aux_pow.push_back({});
+ if (!epee::string_tools::hex_to_pod(s.id, aux_pow.back().first))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Invalid aux pow id";
+ return false;
+ }
+ if (!epee::string_tools::hex_to_pod(s.hash, aux_pow.back().second))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Invalid aux pow hash";
+ return false;
+ }
+ aux_pow_raw.push_back(aux_pow.back().second);
+ }
+
+ size_t path_domain = 1;
+ while ((1u << path_domain) < aux_pow.size())
+ ++path_domain;
+ uint32_t nonce;
+ const uint32_t max_nonce = 65535;
+ bool collision = true;
+ for (nonce = 0; nonce <= max_nonce; ++nonce)
+ {
+ std::vector<bool> slots(aux_pow.size(), false);
+ collision = false;
+ for (size_t idx = 0; idx < aux_pow.size(); ++idx)
+ {
+ const uint32_t slot = cryptonote::get_aux_slot(aux_pow[idx].first, nonce, aux_pow.size());
+ if (slot >= aux_pow.size())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Computed slot is out of range";
+ return false;
+ }
+ if (slots[slot])
+ {
+ collision = true;
+ break;
+ }
+ slots[slot] = true;
+ }
+ if (!collision)
+ break;
+ }
+ if (collision)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Failed to find a suitable nonce";
+ return false;
+ }
+
+ crypto::tree_hash((const char(*)[crypto::HASH_SIZE])aux_pow_raw.data(), aux_pow_raw.size(), merkle_root.data);
+ res.merkle_root = epee::string_tools::pod_to_hex(merkle_root);
+ res.merkle_tree_depth = cryptonote::encode_mm_depth(aux_pow.size(), nonce);
+
+ blobdata blocktemplate_blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.blocktemplate_blob, blocktemplate_blob))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Invalid blocktemplate_blob";
+ return false;
+ }
+
+ block b;
+ if (!parse_and_validate_block_from_blob(blocktemplate_blob, b))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
+ error_resp.message = "Wrong blocktemplate_blob";
+ return false;
+ }
+
+ if (!remove_field_from_tx_extra(b.miner_tx.extra, typeid(cryptonote::tx_extra_merge_mining_tag)))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Error removing existing merkle root";
+ return false;
+ }
+ if (!add_mm_merkle_root_to_tx_extra(b.miner_tx.extra, merkle_root, merkle_tree_depth))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Error adding merkle root";
+ return false;
+ }
+ b.invalidate_hashes();
+ b.miner_tx.invalidate_hashes();
+
+ const blobdata block_blob = t_serializable_object_to_blob(b);
+ const blobdata hashing_blob = get_block_hashing_blob(b);
+
+ res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
+ res.blockhashing_blob = string_tools::buff_to_hex_nodelimer(hashing_blob);
+ res.aux_pow = req.aux_pow;
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
RPC_TRACKER(submitblock);
@@ -3347,6 +3497,12 @@ namespace cryptonote
, ""
};
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_proxy = {
+ "bootstrap-daemon-proxy"
+ , "<ip>:<port> socks proxy to use for bootstrap daemon connections"
+ , ""
+ };
+
const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_payment_address = {
"rpc-payment-address"
, "Restrict RPC to clients sending micropayment to this address"
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index dcf6b4e4b..b21e43ab0 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -72,6 +72,7 @@ namespace cryptonote
static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert;
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address;
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login;
+ static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_proxy;
static const command_line::arg_descriptor<std::string> arg_rpc_payment_address;
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty;
static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits;
@@ -146,6 +147,7 @@ namespace cryptonote
MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
MAP_JON_RPC_WE("get_block_template", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
MAP_JON_RPC_WE("getblocktemplate", on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE)
+ MAP_JON_RPC_WE("add_aux_pow", on_add_aux_pow, COMMAND_RPC_ADD_AUX_POW)
MAP_JON_RPC_WE("submit_block", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK)
MAP_JON_RPC_WE_IF("generateblocks", on_generateblocks, COMMAND_RPC_GENERATEBLOCKS, !m_restricted)
@@ -226,6 +228,7 @@ namespace cryptonote
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, const connection_context *ctx = NULL);
bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_add_aux_pow(const COMMAND_RPC_ADD_AUX_POW::request& req, COMMAND_RPC_ADD_AUX_POW::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
@@ -268,8 +271,14 @@ private:
uint64_t get_block_reward(const block& blk);
bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
std::map<std::string, bool> get_public_nodes(uint32_t credits_per_hash_threshold = 0);
- bool set_bootstrap_daemon(const std::string &address, const std::string &username_password);
- bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials);
+ bool set_bootstrap_daemon(
+ const std::string &address,
+ const std::string &username_password,
+ const std::string &proxy);
+ bool set_bootstrap_daemon(
+ const std::string &address,
+ const boost::optional<epee::net_utils::http::login> &credentials,
+ const std::string &proxy);
enum invoke_http_mode { JON, BIN, JON_RPC };
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index bbcb27f1c..5ebe4f654 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -88,7 +88,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 3
-#define CORE_RPC_VERSION_MINOR 5
+#define CORE_RPC_VERSION_MINOR 6
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -938,6 +938,52 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_ADD_AUX_POW
+ {
+ struct aux_pow_t
+ {
+ std::string id;
+ std::string hash;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(id)
+ KV_SERIALIZE(hash)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct request_t: public rpc_request_base
+ {
+ blobdata blocktemplate_blob;
+ std::vector<aux_pow_t> aux_pow;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ KV_SERIALIZE(blocktemplate_blob)
+ KV_SERIALIZE(aux_pow)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t: public rpc_response_base
+ {
+ blobdata blocktemplate_blob;
+ blobdata blockhashing_blob;
+ std::string merkle_root;
+ uint32_t merkle_tree_depth;
+ std::vector<aux_pow_t> aux_pow;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(blocktemplate_blob)
+ KV_SERIALIZE(blockhashing_blob)
+ KV_SERIALIZE(merkle_root)
+ KV_SERIALIZE(merkle_tree_depth)
+ KV_SERIALIZE(aux_pow)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_SUBMITBLOCK
{
typedef std::vector<std::string> request;
@@ -1613,11 +1659,13 @@ namespace cryptonote
std::string address;
std::string username;
std::string password;
+ std::string proxy;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(username)
KV_SERIALIZE(password)
+ KV_SERIALIZE(proxy)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 2a3c33f48..2ae091c7e 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -276,7 +276,7 @@ namespace
const char* USAGE_PUBLIC_NODES("public_nodes");
const char* USAGE_WELCOME("welcome");
const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info");
- const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc");
+ const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc [<number_of_threads>]");
const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc");
const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]");
const char* USAGE_VERSION("version");
@@ -2361,6 +2361,24 @@ bool simple_wallet::start_mining_for_rpc(const std::vector<std::string> &args)
if (!try_connect_to_daemon())
return true;
+ bool ok = true;
+ if(args.size() >= 1)
+ {
+ uint16_t num = 0;
+ ok = string_tools::get_xtype_from_string(num, args[0]);
+ m_rpc_payment_threads = num;
+ }
+ else
+ {
+ m_rpc_payment_threads = 0;
+ }
+
+ if (!ok)
+ {
+ PRINT_USAGE(USAGE_START_MINING_FOR_RPC);
+ return true;
+ }
+
LOCK_IDLE_SCOPE();
bool payment_required;
@@ -9327,7 +9345,7 @@ bool simple_wallet::check_rpc_payment()
fail_msg_writer() << tr("Error mining to daemon: ") << error;
m_cmd_binder.print_prompt();
};
- bool ret = m_wallet->search_for_rpc_payment(target, startfunc, contfunc, foundfunc, errorfunc);
+ bool ret = m_wallet->search_for_rpc_payment(target, m_rpc_payment_threads, startfunc, contfunc, foundfunc, errorfunc);
if (!ret)
{
fail_msg_writer() << tr("Failed to start mining for RPC payment");
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index af654e0dd..8780bee1d 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -464,6 +464,7 @@ namespace cryptonote
std::atomic<bool> m_need_payment;
boost::posix_time::ptime m_last_rpc_payment_mining_time;
bool m_rpc_payment_mining_requested;
+ uint32_t m_rpc_payment_threads = 0;
bool m_daemon_rpc_payment_message_displayed;
float m_rpc_payment_hash_rate;
std::atomic<bool> m_suspend_rpc_payment_mining;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index e5a5136a4..4c50c3c22 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1428,7 +1428,7 @@ private:
bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
bool daemon_requires_payment();
bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance);
- bool search_for_rpc_payment(uint64_t credits_target, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc = NULL, const std::function<void(const std::string&)> &errorfunc = NULL);
+ bool search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc = NULL, const std::function<void(const std::string&)> &errorfunc = NULL);
template<typename T> void handle_payment_changes(const T &res, std::true_type) {
if (res.status == CORE_RPC_STATUS_OK || res.status == CORE_RPC_STATUS_PAYMENT_REQUIRED)
m_rpc_payment_state.credits = res.credits;
diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp
index 6527b1384..bf278f695 100644
--- a/src/wallet/wallet_rpc_payments.cpp
+++ b/src/wallet/wallet_rpc_payments.cpp
@@ -42,6 +42,7 @@
#include "cryptonote_basic/blobdatatype.h"
#include "common/i18n.h"
#include "common/util.h"
+#include "common/threadpool.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2.rpc_payments"
@@ -101,7 +102,7 @@ bool wallet2::make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credit
return true;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::search_for_rpc_payment(uint64_t credits_target, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc, const std::function<void(const std::string&)> &errorfunc)
+bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc, const std::function<void(const std::string&)> &errorfunc)
{
bool need_payment = false;
bool payment_required;
@@ -139,49 +140,65 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, const std::functio
continue;
}
- crypto::hash hash;
- const uint32_t local_nonce = nonce++; // wrapping's OK
- *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce);
- const uint8_t major_version = hashing_blob[0];
- if (major_version >= RX_BLOCK_VERSION)
- {
- const int miners = 1;
- crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, miners, 0);
- }
- else
+ if(n_threads == 0)
+ n_threads = boost::thread::hardware_concurrency();
+
+ std::vector<crypto::hash> hash(n_threads);
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter(tpool);
+
+ const uint32_t local_nonce = nonce += n_threads; // wrapping's OK
+ for (size_t i = 0; i < n_threads; i++)
{
- int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
- crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash, cn_variant, height);
+ tpool.submit(&waiter, [&, i] {
+ *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
+ const uint8_t major_version = hashing_blob[0];
+ if (major_version >= RX_BLOCK_VERSION)
+ {
+ const int miners = 1;
+ crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data, miners, 0);
+ }
+ else
+ {
+ int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
+ crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash[i], cn_variant, height);
+ }
+ });
}
- ++n_hashes;
- if (cryptonote::check_hash(hash, diff))
+ waiter.wait();
+ n_hashes += n_threads;
+
+ for(size_t i=0; i < n_threads; i++)
{
- uint64_t credits, balance;
- try
+ if (cryptonote::check_hash(hash[i], diff))
{
- make_rpc_payment(local_nonce, cookie, credits, balance);
- if (credits != credits_per_hash_found)
+ uint64_t credits, balance;
+ try
{
- MERROR("Found nonce, but daemon did not credit us with the expected amount");
+ make_rpc_payment(local_nonce-i, cookie, credits, balance);
+ if (credits != credits_per_hash_found)
+ {
+ MERROR("Found nonce, but daemon did not credit us with the expected amount");
+ if (errorfunc)
+ errorfunc("Found nonce, but daemon did not credit us with the expected amount");
+ return false;
+ }
+ MDEBUG("Found nonce " << local_nonce-i << " at diff " << diff << ", gets us " << credits_per_hash_found << ", now " << balance << " credits");
+ if (!foundfunc(credits))
+ break;
+ }
+ catch (const tools::error::wallet_coded_rpc_error &e)
+ {
+ MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
if (errorfunc)
- errorfunc("Found nonce, but daemon did not credit us with the expected amount");
- return false;
+ errorfunc("Found nonce, but daemon errored out with error " + std::to_string(e.code()) + ": " + e.status() + ", continuing");
+ }
+ catch (const std::exception &e)
+ {
+ MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
+ if (errorfunc)
+ errorfunc("Found nonce, but daemon errored out with: '" + std::string(e.what()) + "', continuing");
}
- MDEBUG("Found nonce " << local_nonce << " at diff " << diff << ", gets us " << credits_per_hash_found << ", now " << balance << " credits");
- if (!foundfunc(credits))
- break;
- }
- catch (const tools::error::wallet_coded_rpc_error &e)
- {
- MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
- if (errorfunc)
- errorfunc("Found nonce, but daemon errored out with error " + std::to_string(e.code()) + ": " + e.status() + ", continuing");
- }
- catch (const std::exception &e)
- {
- MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
- if (errorfunc)
- errorfunc("Found nonce, but daemon errored out with: '" + std::string(e.what()) + "', continuing");
}
}
}
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 327a189ca..b09e24d31 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1887,6 +1887,7 @@ namespace tools
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor};
rpc_transfers.key_image = td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
+ rpc_transfers.pubkey = epee::string_tools::pod_to_hex(td.get_public_key());
rpc_transfers.block_height = td.m_block_height;
rpc_transfers.frozen = td.m_frozen;
rpc_transfers.unlocked = m_wallet->is_transfer_unlocked(td);
@@ -3013,6 +3014,41 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_scan_tx(const wallet_rpc::COMMAND_RPC_SCAN_TX::request& req, wallet_rpc::COMMAND_RPC_SCAN_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ std::vector<crypto::hash> txids;
+ std::list<std::string>::const_iterator i = req.txids.begin();
+ while (i != req.txids.end())
+ {
+ cryptonote::blobdata txid_blob;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
+ er.message = "TX ID has invalid format";
+ return false;
+ }
+
+ crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_blob.data());
+ txids.push_back(txid);
+ }
+
+ try {
+ m_wallet->scan_tx(txids);
+ } catch (const std::exception &e) {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 6e39eca1e..9f9e3c134 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -131,6 +131,7 @@ namespace tools
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH)
+ MAP_JON_RPC_WE("scan_tx", on_scan_tx, wallet_rpc::COMMAND_RPC_SCAN_TX)
MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT)
MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING)
MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING)
@@ -218,6 +219,7 @@ namespace tools
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_scan_tx(const wallet_rpc::COMMAND_RPC_SCAN_TX::request& req, wallet_rpc::COMMAND_RPC_SCAN_TX::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 81f83fb18..abc7702f7 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// 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 20
+#define WALLET_RPC_VERSION_MINOR 21
#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
@@ -1010,6 +1010,7 @@ namespace wallet_rpc
std::string tx_hash;
cryptonote::subaddress_index subaddr_index;
std::string key_image;
+ std::string pubkey; // owned output public key found
uint64_t block_height;
bool frozen;
bool unlocked;
@@ -1021,6 +1022,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(subaddr_index)
KV_SERIALIZE(key_image)
+ KV_SERIALIZE(pubkey);
KV_SERIALIZE(block_height)
KV_SERIALIZE(frozen)
KV_SERIALIZE(unlocked)
@@ -2028,6 +2030,26 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_SCAN_TX
+ {
+ struct request_t
+ {
+ std::list<std::string> txids;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txids)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_START_MINING
{
struct request_t