aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.h2
-rw-r--r--src/blockchain_utilities/CMakeLists.txt20
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp8
-rw-r--r--src/blocks/CMakeLists.txt35
-rw-r--r--src/blocks/blockexports.c87
-rw-r--r--src/blocks/blocks.cpp31
-rw-r--r--src/blocks/blocks.dat0
-rw-r--r--src/blocks/blocks.h14
-rw-r--r--src/common/util.cpp25
-rw-r--r--src/cryptonote_basic/account.cpp1
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h1
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp85
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_basic/miner.cpp2
-rw-r--r--src/cryptonote_config.h1
-rw-r--r--src/cryptonote_core/CMakeLists.txt7
-rw-r--r--src/cryptonote_core/blockchain.cpp38
-rw-r--r--src/cryptonote_core/blockchain.h23
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp51
-rw-r--r--src/cryptonote_core/cryptonote_core.h11
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp6
-rw-r--r--src/daemon/CMakeLists.txt21
-rw-r--r--src/daemon/core.h8
-rw-r--r--src/daemon/rpc_command_executor.cpp2
-rw-r--r--src/device/CMakeLists.txt7
-rw-r--r--src/device/device_io_hid.cpp15
-rw-r--r--src/p2p/net_node.inl16
-rw-r--r--src/rpc/core_rpc_server.cpp31
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h3
-rw-r--r--src/rpc/message.h2
-rw-r--r--src/simplewallet/simplewallet.cpp10
-rw-r--r--src/wallet/api/wallet.cpp26
-rw-r--r--src/wallet/api/wallet.h3
-rw-r--r--src/wallet/api/wallet2_api.h11
-rw-r--r--src/wallet/wallet2.cpp10
-rw-r--r--src/wallet/wallet_errors.h9
-rw-r--r--src/wallet/wallet_rpc_server.cpp172
-rw-r--r--src/wallet/wallet_rpc_server.h2
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h67
39 files changed, 636 insertions, 229 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 396ae7544..71c46d76b 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -544,7 +544,7 @@ public:
/**
* @brief An empty constructor.
*/
- BlockchainDB(): m_open(false) { }
+ BlockchainDB(): m_hardfork(NULL), m_open(false) { }
/**
* @brief An empty destructor.
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index 24a750eb0..6e6e4c6f1 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -26,20 +26,6 @@
# 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(blocksdat "")
-if(PER_BLOCK_CHECKPOINT)
- if(APPLE AND DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(APPLE AND NOT DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(LINUX_32)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- else()
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- endif()
- set(blocksdat "blocksdat.o")
-endif()
-
set(blockchain_import_sources
blockchain_import.cpp
bootstrap_file.cpp
@@ -119,8 +105,7 @@ monero_private_headers(blockchain_depth
monero_add_executable(blockchain_import
${blockchain_import_sources}
- ${blockchain_import_private_headers}
- ${blocksdat})
+ ${blockchain_import_private_headers})
target_link_libraries(blockchain_import
PRIVATE
@@ -132,7 +117,8 @@ target_link_libraries(blockchain_import
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
- ${EXTRA_LIBRARIES})
+ ${EXTRA_LIBRARIES}
+ ${Blocks})
if(ARCH_WIDTH)
target_compile_definitions(blockchain_import
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 9ec768d26..7f92ecd87 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -37,6 +37,7 @@
#include "misc_log_ex.h"
#include "bootstrap_file.h"
#include "bootstrap_serialization.h"
+#include "blocks/blocks.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
#include "serialization/json_utils.h" // dump_json()
@@ -758,7 +759,12 @@ int main(int argc, char* argv[])
{
core.disable_dns_checkpoints(true);
- if (!core.init(vm, NULL))
+#if defined(PER_BLOCK_CHECKPOINT)
+ const GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
+#else
+ const GetCheckpointsCallback& get_checkpoints = nullptr;
+#endif
+ if (!core.init(vm, nullptr, nullptr, get_checkpoints))
{
std::cerr << "Failed to initialize core" << ENDL;
return 1;
diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt
index ebb5408cc..30d85adbf 100644
--- a/src/blocks/CMakeLists.txt
+++ b/src/blocks/CMakeLists.txt
@@ -26,20 +26,23 @@
# 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.
-if(APPLE)
- add_library(blocks STATIC blockexports.c)
- set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
-else()
- if(LINUX_32)
- add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
- add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
- add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
- else()
- add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
- add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
- add_custom_command(OUTPUT stagenet_blocks.o MAIN_DEPENDENCY stagenet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/stagenet_blocks.o stagenet_blocks.dat)
- endif()
- add_library(blocks STATIC blocks.o testnet_blocks.o stagenet_blocks.o blockexports.c)
- set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
-endif()
+set(GENERATED_SOURCES "")
+foreach(BLOB_NAME checkpoints testnet_blocks stagenet_blocks)
+ set(OUTPUT_C_SOURCE "generated_${BLOB_NAME}.c")
+ list(APPEND GENERATED_SOURCES ${OUTPUT_C_SOURCE})
+ set(INPUT_DAT_FILE "${BLOB_NAME}.dat")
+ add_custom_command(
+ OUTPUT ${OUTPUT_C_SOURCE}
+ MAIN_DEPENDENCY ${INPUT_DAT_FILE}
+ COMMAND
+ cd ${CMAKE_CURRENT_BINARY_DIR} &&
+ echo "'#include\t<stddef.h>'" > ${OUTPUT_C_SOURCE} &&
+ echo "'const\tunsigned\tchar\t${BLOB_NAME}[]={'" >> ${OUTPUT_C_SOURCE} &&
+ od -v -An -tu1 ${CMAKE_CURRENT_SOURCE_DIR}/${INPUT_DAT_FILE} | sed -e "'s/[0-9]\\{1,\\}/&,/g'" -e "'$$s/.$$//'" >> ${OUTPUT_C_SOURCE} &&
+ echo "'};'" >> ${OUTPUT_C_SOURCE} &&
+ echo "'const\tsize_t\t${BLOB_NAME}_len\t=\tsizeof(${BLOB_NAME});'" >> ${OUTPUT_C_SOURCE}
+ )
+endforeach()
+
+add_library(blocks STATIC blocks.cpp ${GENERATED_SOURCES})
diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c
deleted file mode 100644
index 0154b0413..000000000
--- a/src/blocks/blockexports.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include <stddef.h>
-
-#if defined(__APPLE__)
-#include <mach-o/getsect.h>
-#ifdef BUILD_SHARED_LIBS
-#if !defined(__LP64__)
-const struct mach_header _mh_execute_header;
-#else
-const struct mach_header_64 _mh_execute_header;
-#endif
-#else
-#if !defined(__LP64__)
-extern const struct mach_header _mh_execute_header;
-#else
-extern const struct mach_header_64 _mh_execute_header;
-#endif
-#endif
-
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet)
-{
- size_t size;
- if (testnet)
- return getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size);
- else if (stagenet)
- return getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size);
- else
- return getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size);
-}
-
-size_t get_blocks_dat_size(int testnet, int stagenet)
-{
- size_t size;
- if (testnet)
- getsectiondata(&_mh_execute_header, "__DATA", "__testnet_blocks_dat", &size);
- else if (stagenet)
- getsectiondata(&_mh_execute_header, "__DATA", "__stagenet_blocks_dat", &size);
- else
- getsectiondata(&_mh_execute_header, "__DATA", "__blocks_dat", &size);
- return size;
-}
-
-#else
-
-#if defined(_WIN32) && !defined(_WIN64)
-#define _binary_blocks_start binary_blocks_dat_start
-#define _binary_blocks_end binary_blocks_dat_end
-#define _binary_testnet_blocks_start binary_testnet_blocks_dat_start
-#define _binary_testnet_blocks_end binary_testnet_blocks_dat_end
-#define _binary_stagenet_blocks_start binary_stagenet_blocks_dat_start
-#define _binary_stagenet_blocks_end binary_stagenet_blocks_dat_end
-#else
-#define _binary_blocks_start _binary_blocks_dat_start
-#define _binary_blocks_end _binary_blocks_dat_end
-#define _binary_testnet_blocks_start _binary_testnet_blocks_dat_start
-#define _binary_testnet_blocks_end _binary_testnet_blocks_dat_end
-#define _binary_stagenet_blocks_start _binary_stagenet_blocks_dat_start
-#define _binary_stagenet_blocks_end _binary_stagenet_blocks_dat_end
-#endif
-
-extern const unsigned char _binary_blocks_start[];
-extern const unsigned char _binary_blocks_end[];
-extern const unsigned char _binary_testnet_blocks_start[];
-extern const unsigned char _binary_testnet_blocks_end[];
-extern const unsigned char _binary_stagenet_blocks_start[];
-extern const unsigned char _binary_stagenet_blocks_end[];
-
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet)
-{
- if (testnet)
- return _binary_testnet_blocks_start;
- else if (stagenet)
- return _binary_stagenet_blocks_start;
- else
- return _binary_blocks_start;
-}
-
-size_t get_blocks_dat_size(int testnet, int stagenet)
-{
- if (testnet)
- return (size_t) (_binary_testnet_blocks_end - _binary_testnet_blocks_start);
- else if (stagenet)
- return (size_t) (_binary_stagenet_blocks_end - _binary_stagenet_blocks_start);
- else
- return (size_t) (_binary_blocks_end - _binary_blocks_start);
-}
-
-#endif
diff --git a/src/blocks/blocks.cpp b/src/blocks/blocks.cpp
new file mode 100644
index 000000000..0661f8448
--- /dev/null
+++ b/src/blocks/blocks.cpp
@@ -0,0 +1,31 @@
+#include "blocks.h"
+
+#include <unordered_map>
+
+extern const unsigned char checkpoints[];
+extern const size_t checkpoints_len;
+extern const unsigned char stagenet_blocks[];
+extern const size_t stagenet_blocks_len;
+extern const unsigned char testnet_blocks[];
+extern const size_t testnet_blocks_len;
+
+namespace blocks
+{
+
+ const std::unordered_map<cryptonote::network_type, const epee::span<const unsigned char>, std::hash<size_t>> CheckpointsByNetwork = {
+ {cryptonote::network_type::MAINNET, {checkpoints, checkpoints_len}},
+ {cryptonote::network_type::STAGENET, {stagenet_blocks, stagenet_blocks_len}},
+ {cryptonote::network_type::TESTNET, {testnet_blocks, testnet_blocks_len}}
+ };
+
+ const epee::span<const unsigned char> GetCheckpointsData(cryptonote::network_type network)
+ {
+ const auto it = CheckpointsByNetwork.find(network);
+ if (it != CheckpointsByNetwork.end())
+ {
+ return it->second;
+ }
+ return nullptr;
+ }
+
+}
diff --git a/src/blocks/blocks.dat b/src/blocks/blocks.dat
deleted file mode 100644
index e69de29bb..000000000
--- a/src/blocks/blocks.dat
+++ /dev/null
diff --git a/src/blocks/blocks.h b/src/blocks/blocks.h
index ec683f47e..14e391319 100644
--- a/src/blocks/blocks.h
+++ b/src/blocks/blocks.h
@@ -1,16 +1,12 @@
#ifndef SRC_BLOCKS_BLOCKS_H_
#define SRC_BLOCKS_BLOCKS_H_
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "cryptonote_config.h"
+#include "span.h"
-const unsigned char *get_blocks_dat_start(int testnet, int stagenet);
-size_t get_blocks_dat_size(int testnet, int stagenet);
-
-#ifdef __cplusplus
+namespace blocks
+{
+ const epee::span<const unsigned char> GetCheckpointsData(cryptonote::network_type network);
}
-#endif
-
#endif /* SRC_BLOCKS_BLOCKS_H_ */
diff --git a/src/common/util.cpp b/src/common/util.cpp
index f91230528..43973c511 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -308,10 +308,19 @@ namespace tools
StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft "));
// Test for the specific product.
+ if ( osvi.dwMajorVersion == 10 )
+ {
+ if ( osvi.dwMinorVersion == 0 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 10 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2016 " ));
+ }
+ }
if ( osvi.dwMajorVersion == 6 )
{
- if( osvi.dwMinorVersion == 0 )
+ if ( osvi.dwMinorVersion == 0 )
{
if( osvi.wProductType == VER_NT_WORKSTATION )
StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista "));
@@ -325,6 +334,20 @@ namespace tools
else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " ));
}
+ if ( osvi.dwMinorVersion == 2 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 " ));
+ }
+
+ if ( osvi.dwMinorVersion == 3 )
+ {
+ if( osvi.wProductType == VER_NT_WORKSTATION )
+ StringCchCat(pszOS, BUFSIZE, TEXT("Windows 8.1 "));
+ else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2012 R2 " ));
+ }
+
pGPI = (PGPI) GetProcAddress(
GetModuleHandle(TEXT("kernel32.dll")),
"GetProductInfo");
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index e891a748d..1dc1ad71d 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -136,6 +136,7 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::set_null()
{
m_keys = account_keys();
+ m_creation_timestamp = 0;
}
//-----------------------------------------------------------------
void account_base::forget_spend_key()
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index d4558ef7b..b0eabb0aa 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -47,7 +47,6 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "misc_language.h"
-#include "tx_extra.h"
#include "ringct/rctTypes.h"
#include "device/device.hpp"
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 9e9c12605..e26aac76b 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -445,6 +445,91 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ template<typename T>
+ static bool pick(binary_archive<true> &ar, std::vector<tx_extra_field> &fields, uint8_t tag)
+ {
+ std::vector<tx_extra_field>::iterator it;
+ while ((it = std::find_if(fields.begin(), fields.end(), [](const tx_extra_field &f) { return f.type() == typeid(T); })) != fields.end())
+ {
+ bool r = ::do_serialize(ar, tag);
+ CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
+ r = ::do_serialize(ar, boost::get<T>(*it));
+ CHECK_AND_NO_ASSERT_MES_L1(r, false, "failed to serialize tx extra field");
+ fields.erase(it);
+ }
+ return true;
+ }
+ //---------------------------------------------------------------
+ bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial)
+ {
+ std::vector<tx_extra_field> tx_extra_fields;
+
+ if(tx_extra.empty())
+ {
+ sorted_tx_extra.clear();
+ return true;
+ }
+
+ std::string extra_str(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size());
+ std::istringstream iss(extra_str);
+ binary_archive<false> ar(iss);
+
+ bool eof = false;
+ size_t processed = 0;
+ while (!eof)
+ {
+ tx_extra_field field;
+ bool r = ::do_serialize(ar, field);
+ if (!r)
+ {
+ MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (!allow_partial)
+ return false;
+ break;
+ }
+ tx_extra_fields.push_back(field);
+ processed = iss.tellg();
+
+ std::ios_base::iostate state = iss.rdstate();
+ eof = (EOF == iss.peek());
+ iss.clear(state);
+ }
+ if (!::serialization::check_stream_state(ar))
+ {
+ MWARNING("failed to deserialize extra field. extra = " << string_tools::buff_to_hex_nodelimer(std::string(reinterpret_cast<const char*>(tx_extra.data()), tx_extra.size())));
+ if (!allow_partial)
+ return false;
+ }
+ MTRACE("Sorted " << processed << "/" << tx_extra.size());
+
+ std::ostringstream oss;
+ binary_archive<true> nar(oss);
+
+ // sort by:
+ if (!pick<tx_extra_pub_key>(nar, tx_extra_fields, TX_EXTRA_TAG_PUBKEY)) return false;
+ if (!pick<tx_extra_additional_pub_keys>(nar, tx_extra_fields, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS)) return false;
+ if (!pick<tx_extra_nonce>(nar, tx_extra_fields, TX_EXTRA_NONCE)) return false;
+ if (!pick<tx_extra_merge_mining_tag>(nar, tx_extra_fields, TX_EXTRA_MERGE_MINING_TAG)) return false;
+ if (!pick<tx_extra_mysterious_minergate>(nar, tx_extra_fields, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG)) return false;
+ if (!pick<tx_extra_padding>(nar, tx_extra_fields, TX_EXTRA_TAG_PADDING)) return false;
+
+ // if not empty, someone added a new type and did not add a case above
+ if (!tx_extra_fields.empty())
+ {
+ MERROR("tx_extra_fields not empty after sorting, someone forgot to add a case above");
+ return false;
+ }
+
+ std::string oss_str = oss.str();
+ if (allow_partial && processed < tx_extra.size())
+ {
+ MDEBUG("Appending unparsed data");
+ oss_str += std::string((const char*)tx_extra.data() + processed, tx_extra.size() - processed);
+ }
+ sorted_tx_extra = std::vector<uint8_t>(oss_str.begin(), oss_str.end());
+ return true;
+ }
+ //---------------------------------------------------------------
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index)
{
std::vector<tx_extra_field> tx_extra_fields;
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 725c75f4e..8d33b1ca4 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -31,6 +31,7 @@
#pragma once
#include "blobdatatype.h"
#include "cryptonote_basic_impl.h"
+#include "tx_extra.h"
#include "account.h"
#include "subaddress_index.h"
#include "include_base_utils.h"
@@ -65,6 +66,7 @@ namespace cryptonote
}
bool parse_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<tx_extra_field>& tx_extra_fields);
+ bool sort_tx_extra(const std::vector<uint8_t>& tx_extra, std::vector<uint8_t> &sorted_tx_extra, bool allow_partial = false);
crypto::public_key get_tx_pub_key_from_extra(const std::vector<uint8_t>& tx_extra, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction_prefix& tx, size_t pk_index = 0);
crypto::public_key get_tx_pub_key_from_extra(const transaction& tx, size_t pk_index = 0);
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index d0b03593e..d8ca2dd35 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -637,7 +637,7 @@ namespace cryptonote
boost::tribool battery_powered(on_battery_power());
if(!indeterminate( battery_powered ))
{
- on_ac_power = !battery_powered;
+ on_ac_power = !(bool)battery_powered;
}
}
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index a6858ce7c..c62eeb738 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -30,6 +30,7 @@
#pragma once
+#include <stdexcept>
#include <string>
#include <boost/uuid/uuid.hpp>
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 72844db66..231489fdb 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -41,12 +41,6 @@ set(cryptonote_core_private_headers
tx_pool.h
cryptonote_tx_utils.h)
-if(PER_BLOCK_CHECKPOINT)
- set(Blocks "blocks")
-else()
- set(Blocks "")
-endif()
-
monero_private_headers(cryptonote_core
${cryptonote_core_private_headers})
monero_add_library(cryptonote_core
@@ -69,5 +63,4 @@ target_link_libraries(cryptonote_core
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
PRIVATE
- ${Blocks}
${EXTRA_LIBRARIES})
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 1ec2366e4..77b6d0b69 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -53,9 +53,6 @@
#include "ringct/rctSigs.h"
#include "common/perf_timer.h"
#include "common/notify.h"
-#if defined(PER_BLOCK_CHECKPOINT)
-#include "blocks/blocks.h"
-#endif
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
@@ -341,7 +338,7 @@ uint64_t Blockchain::get_current_blockchain_height() const
//------------------------------------------------------------------
//FIXME: possibly move this into the constructor, to avoid accidentally
// dereferencing a null BlockchainDB pointer
-bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty)
+bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline, const cryptonote::test_options *test_options, difficulty_type fixed_difficulty, const GetCheckpointsCallback& get_checkpoints/* = nullptr*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -442,7 +439,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
#if defined(PER_BLOCK_CHECKPOINT)
if (m_nettype != FAKECHAIN)
- load_compiled_in_block_hashes();
+ load_compiled_in_block_hashes(get_checkpoints);
#endif
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
@@ -886,6 +883,15 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
return diff;
}
//------------------------------------------------------------------
+std::vector<time_t> Blockchain::get_last_block_timestamps(unsigned int blocks) const
+{
+ std::vector<time_t> timestamps(blocks);
+ uint64_t height = m_db->height();
+ while (blocks--)
+ timestamps[blocks] = m_db->get_block_timestamp(height - blocks - 1);
+ return timestamps;
+}
+//------------------------------------------------------------------
// This function removes blocks from the blockchain until it gets to the
// position where the blockchain switch started and then re-adds the blocks
// that had been removed.
@@ -4407,19 +4413,21 @@ void Blockchain::cancel()
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "954cb2bbfa2fe6f74b2cdd22a1a4c767aea249ad47ad4f7c9445f0f03260f511";
-void Blockchain::load_compiled_in_block_hashes()
+void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
- const bool testnet = m_nettype == TESTNET;
- const bool stagenet = m_nettype == STAGENET;
- if (m_fast_sync && get_blocks_dat_start(testnet, stagenet) != nullptr && get_blocks_dat_size(testnet, stagenet) > 0)
+ if (get_checkpoints == nullptr || !m_fast_sync)
{
- MINFO("Loading precomputed blocks (" << get_blocks_dat_size(testnet, stagenet) << " bytes)");
-
+ return;
+ }
+ const epee::span<const unsigned char> &checkpoints = get_checkpoints(m_nettype);
+ if (!checkpoints.empty())
+ {
+ MINFO("Loading precomputed blocks (" << checkpoints.size() << " bytes)");
if (m_nettype == MAINNET)
{
// first check hash
crypto::hash hash;
- if (!tools::sha256sum(get_blocks_dat_start(testnet, stagenet), get_blocks_dat_size(testnet, stagenet), hash))
+ if (!tools::sha256sum(checkpoints.data(), checkpoints.size(), hash))
{
MERROR("Failed to hash precomputed blocks data");
return;
@@ -4439,9 +4447,9 @@ void Blockchain::load_compiled_in_block_hashes()
}
}
- if (get_blocks_dat_size(testnet, stagenet) > 4)
+ if (checkpoints.size() > 4)
{
- const unsigned char *p = get_blocks_dat_start(testnet, stagenet);
+ const unsigned char *p = checkpoints.data();
const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24);
if (nblocks > (std::numeric_limits<uint32_t>::max() - 4) / sizeof(hash))
{
@@ -4449,7 +4457,7 @@ void Blockchain::load_compiled_in_block_hashes()
return;
}
const size_t size_needed = 4 + nblocks * sizeof(crypto::hash);
- if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && get_blocks_dat_size(testnet, stagenet) >= size_needed)
+ if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed)
{
p += sizeof(uint32_t);
m_blocks_hash_of_hashes.reserve(nblocks);
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index ab66fac8b..f140d7719 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -38,9 +38,11 @@
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <atomic>
+#include <functional>
#include <unordered_map>
#include <unordered_set>
+#include "span.h"
#include "syncobj.h"
#include "string_tools.h"
#include "cryptonote_basic/cryptonote_basic.h"
@@ -73,6 +75,15 @@ namespace cryptonote
db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O)
};
+ /**
+ * @brief Callback routine that returns checkpoints data for specific network type
+ *
+ * @param network network type
+ *
+ * @return checkpoints data, empty span if there ain't any checkpoints for specific network type
+ */
+ typedef std::function<const epee::span<const unsigned char>(cryptonote::network_type network)> GetCheckpointsCallback;
+
/************************************************************************/
/* */
/************************************************************************/
@@ -117,10 +128,11 @@ namespace cryptonote
* @param offline true if running offline, else false
* @param test_options test parameters
* @param fixed_difficulty fixed difficulty for testing purposes; 0 means disabled
+ * @param get_checkpoints if set, will be called to get checkpoints data
*
* @return true on success, false if any initialization steps fail
*/
- bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0);
+ bool init(BlockchainDB* db, const network_type nettype = MAINNET, bool offline = false, const cryptonote::test_options *test_options = NULL, difficulty_type fixed_difficulty = 0, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @brief Initialize the Blockchain state
@@ -952,6 +964,11 @@ namespace cryptonote
*/
void on_new_tx_from_block(const cryptonote::transaction &tx);
+ /**
+ * @brief returns the timestamps of the last N blocks
+ */
+ std::vector<time_t> get_last_block_timestamps(unsigned int blocks) const;
+
private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
@@ -1369,8 +1386,10 @@ namespace cryptonote
* A (possibly empty) set of block hashes can be compiled into the
* monero daemon binary. This function loads those hashes into
* a useful state.
+ *
+ * @param get_checkpoints if set, will be called to get checkpoints data
*/
- void load_compiled_in_block_hashes();
+ void load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints);
/**
* @brief expands v2 transaction data from blockchain
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 2fec6b613..d8c38bf9e 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -389,7 +389,7 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
- bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options)
+ bool core::init(const boost::program_options::variables_map& vm, const char *config_subdir, const cryptonote::test_options *test_options, const GetCheckpointsCallback& get_checkpoints/* = nullptr */)
{
start_time = std::time(nullptr);
@@ -567,7 +567,7 @@ namespace cryptonote
regtest_hard_forks
};
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
- r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty);
+ r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty, get_checkpoints);
r = m_mempool.init(max_txpool_weight);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -1494,6 +1494,7 @@ namespace cryptonote
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
m_check_disk_space_interval.do_call(boost::bind(&core::check_disk_space, this));
+ m_block_rate_interval.do_call(boost::bind(&core::check_block_rate, this));
m_miner.on_idle();
m_mempool.on_idle();
return true;
@@ -1682,6 +1683,52 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ double factorial(unsigned int n)
+ {
+ if (n <= 1)
+ return 1.0;
+ double f = n;
+ while (n-- > 1)
+ f *= n;
+ return f;
+ }
+ //-----------------------------------------------------------------------------------------------
+ static double probability(unsigned int blocks, unsigned int expected)
+ {
+ // https://www.umass.edu/wsp/resources/poisson/#computing
+ return pow(expected, blocks) / (factorial(blocks) * exp(expected));
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::check_block_rate()
+ {
+ if (m_offline || m_target_blockchain_height > get_current_blockchain_height())
+ {
+ MDEBUG("Not checking block rate, offline or syncing");
+ return true;
+ }
+
+ static constexpr double threshold = 1. / (864000 / DIFFICULTY_TARGET_V2); // one false positive every 10 days
+
+ const time_t now = time(NULL);
+ const std::vector<time_t> timestamps = m_blockchain_storage.get_last_block_timestamps(60);
+
+ static const unsigned int seconds[] = { 5400, 1800, 600 };
+ for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n)
+ {
+ unsigned int b = 0;
+ for (time_t ts: timestamps) b += ts >= now - seconds[n];
+ const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2);
+ MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
+ if (p < threshold)
+ {
+ MWARNING("There were " << b << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
+ break; // no need to look further
+ }
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
void core::set_target_blockchain_height(uint64_t target_blockchain_height)
{
m_target_blockchain_height = target_blockchain_height;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 58fe5b7b5..80c452f53 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -244,10 +244,11 @@ namespace cryptonote
* @param vm command line parameters
* @param config_subdir subdirectory for config storage
* @param test_options configuration options for testing
+ * @param get_checkpoints if set, will be called to get checkpoints data, must return checkpoints data pointer and size or nullptr if there ain't any checkpoints for specific network type
*
* @return false if one of the init steps fails, otherwise true
*/
- bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL);
+ bool init(const boost::program_options::variables_map& vm, const char *config_subdir = NULL, const test_options *test_options = NULL, const GetCheckpointsCallback& get_checkpoints = nullptr);
/**
* @copydoc Blockchain::reset_and_set_genesis_block
@@ -945,6 +946,13 @@ namespace cryptonote
*/
bool check_disk_space();
+ /**
+ * @brief checks block rate, and warns if it's too slow
+ *
+ * @return true on success, false otherwise
+ */
+ bool check_block_rate();
+
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
@@ -969,6 +977,7 @@ namespace cryptonote
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
epee::math_helper::once_a_time_seconds<60*10, true> m_check_disk_space_interval; //!< interval for checking for disk space
+ epee::math_helper::once_a_time_seconds<90, false> m_block_rate_interval; //!< interval for checking block rate
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 4bc33b56b..4fc2736a6 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -38,6 +38,7 @@ using namespace epee;
#include "cryptonote_tx_utils.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/miner.h"
+#include "cryptonote_basic/tx_extra.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
@@ -84,6 +85,8 @@ namespace cryptonote
if(!extra_nonce.empty())
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
return false;
+ if (!sort_tx_extra(tx.extra, tx.extra))
+ return false;
txin_gen in;
in.height = height;
@@ -434,6 +437,9 @@ namespace cryptonote
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
}
+ if (!sort_tx_extra(tx.extra, tx.extra))
+ return false;
+
//check money
if(summary_outs_money > summary_inputs_money )
{
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index f645836a4..117790455 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -26,20 +26,6 @@
# 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(blocksdat "")
-if(PER_BLOCK_CHECKPOINT)
- if(APPLE AND DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(APPLE AND NOT DEPENDS)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
- elseif(LINUX_32)
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -m elf_i386 ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- else()
- add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
- endif()
- set(blocksdat "blocksdat.o")
-endif()
-
set(daemon_sources
command_parser_executor.cpp
command_server.cpp
@@ -81,9 +67,7 @@ monero_private_headers(daemon
monero_add_executable(daemon
${daemon_sources}
${daemon_headers}
- ${daemon_private_headers}
- ${blocksdat}
-)
+ ${daemon_private_headers})
target_link_libraries(daemon
PRIVATE
rpc
@@ -106,7 +90,8 @@ target_link_libraries(daemon
${CMAKE_THREAD_LIBS_INIT}
${ZMQ_LIB}
${GNU_READLINE_LIBRARY}
- ${EXTRA_LIBRARIES})
+ ${EXTRA_LIBRARIES}
+ ${Blocks})
set_property(TARGET daemon
PROPERTY
OUTPUT_NAME "monerod")
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 475f418d6..d1defd573 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -28,6 +28,7 @@
#pragma once
+#include "blocks/blocks.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "misc_log_ex.h"
@@ -85,7 +86,12 @@ public:
//initialize core here
MGINFO("Initializing core...");
std::string config_subdir = get_config_subdir();
- if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str()))
+#if defined(PER_BLOCK_CHECKPOINT)
+ const cryptonote::GetCheckpointsCallback& get_checkpoints = blocks::GetCheckpointsData;
+#else
+ const cryptonote::GetCheckpointsCallback& get_checkpoints = nullptr;
+#endif
+ if (!m_core.init(m_vm_HACK, config_subdir.empty() ? NULL : config_subdir.c_str(), nullptr, get_checkpoints))
{
return false;
}
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 6464d372f..9a0603a10 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -449,7 +449,7 @@ bool t_rpc_command_executor::show_status() {
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet")
% bootstrap_msg
- % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
+ % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) + std::string(" to ") + mres.address ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
% get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt
index 8f446f42a..727134f75 100644
--- a/src/device/CMakeLists.txt
+++ b/src/device/CMakeLists.txt
@@ -58,12 +58,6 @@ endif()
set(device_private_headers)
-if(PER_BLOCK_CHECKPOINT)
- set(Blocks "blocks")
-else()
- set(Blocks "")
-endif()
-
monero_private_headers(device
${device_private_headers})
@@ -79,5 +73,4 @@ target_link_libraries(device
ringct_basic
${OPENSSL_CRYPTO_LIBRARIES}
PRIVATE
- ${Blocks}
${EXTRA_LIBRARIES})
diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp
index 666255cb3..1aadfb9ea 100644
--- a/src/device/device_io_hid.cpp
+++ b/src/device/device_io_hid.cpp
@@ -1,3 +1,18 @@
+// Copyright (c) 2017-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.
//
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 9390626a8..a61b6107f 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -655,10 +655,14 @@ namespace nodetool
{
kill();
m_peerlist.deinit();
- m_net_server.deinit_server();
- // remove UPnP port mapping
- if(!m_no_igd)
- delete_upnp_port_mapping(m_listening_port);
+
+ if (!m_offline)
+ {
+ m_net_server.deinit_server();
+ // remove UPnP port mapping
+ if(!m_no_igd)
+ delete_upnp_port_mapping(m_listening_port);
+ }
return store_config();
}
//-----------------------------------------------------------------------------------
@@ -2042,7 +2046,7 @@ namespace nodetool
char lanAddress[64];
result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
freeUPNPDevlist(deviceList);
- if (result != 0) {
+ if (result > 0) {
if (result == 1) {
std::ostringstream portString;
portString << port;
@@ -2088,7 +2092,7 @@ namespace nodetool
char lanAddress[64];
result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
freeUPNPDevlist(deviceList);
- if (result != 0) {
+ if (result > 0) {
if (result == 1) {
std::ostringstream portString;
portString << port;
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 55ee66a79..aa9d3d64b 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -212,23 +212,15 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_blob(cryptonote::transaction &tx)
- {
- std::stringstream ss;
- binary_archive<true> ba(ss);
- bool r = tx.serialize_base(ba);
- CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
- return ss.str();
- }
- //------------------------------------------------------------------------------------------------------------------------------
- static cryptonote::blobdata get_pruned_tx_json(cryptonote::transaction &tx)
- {
- std::stringstream ss;
- json_archive<true> ar(ss);
- bool r = tx.serialize_base(ar);
- CHECK_AND_ASSERT_MES(r, cryptonote::blobdata(), "Failed to serialize rct signatures base");
- return ss.str();
- }
+ class pruned_transaction {
+ transaction& tx;
+ public:
+ pruned_transaction(transaction& tx) : tx(tx) {}
+ BEGIN_SERIALIZE_OBJECT()
+ bool r = tx.serialize_base(ar);
+ if (!r) return false;
+ END_SERIALIZE()
+ };
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res)
{
@@ -564,10 +556,11 @@ namespace cryptonote
crypto::hash tx_hash = *vhi++;
e.tx_hash = *txhi++;
- blobdata blob = req.prune ? get_pruned_tx_blob(tx) : t_serializable_object_to_blob(tx);
+ pruned_transaction pruned_tx{tx};
+ blobdata blob = req.prune ? t_serializable_object_to_blob(pruned_tx) : t_serializable_object_to_blob(tx);
e.as_hex = string_tools::buff_to_hex_nodelimer(blob);
if (req.decode_as_json)
- e.as_json = req.prune ? get_pruned_tx_json(tx) : obj_to_json_str(tx);
+ e.as_json = req.prune ? obj_to_json_str(pruned_tx) : obj_to_json_str(tx);
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
if (e.in_pool)
{
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 3b654d4cb..b229841d6 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -782,9 +782,6 @@ namespace cryptonote
std::string tx_as_hex;
bool do_not_relay;
- request() {}
- explicit request(const transaction &);
-
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_as_hex)
KV_SERIALIZE_OPT(do_not_relay, false)
diff --git a/src/rpc/message.h b/src/rpc/message.h
index 16b8e92fc..56087b998 100644
--- a/src/rpc/message.h
+++ b/src/rpc/message.h
@@ -65,7 +65,7 @@ namespace rpc
static const char* STATUS_BAD_REQUEST;
static const char* STATUS_BAD_JSON;
- Message() : status(STATUS_OK) { }
+ Message() : status(STATUS_OK), rpc_version(0) { }
virtual ~Message() { }
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 18b596662..3d92b2823 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -4715,8 +4715,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (!try_connect_to_daemon())
return true;
- SCOPED_WALLET_UNLOCK();
-
std::vector<std::string> local_args = args_;
std::set<uint32_t> subaddr_indices;
@@ -4936,6 +4934,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
+ SCOPED_WALLET_UNLOCK();
+
try
{
// figure out what tx will be necessary
@@ -7050,12 +7050,6 @@ bool simple_wallet::run()
void simple_wallet::stop()
{
m_cmd_binder.stop_handling();
-
- m_idle_run.store(false, std::memory_order_relaxed);
- m_wallet->stop();
- // make the background refresh thread quit
- boost::unique_lock<boost::mutex> lock(m_idle_mutex);
- m_idle_cond.notify_one();
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 1b4370c36..ddf2d74ff 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -381,6 +381,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
, m_synchronized(false)
, m_rebuildWalletCache(false)
, m_is_connected(false)
+ , m_refreshShouldRescan(false)
{
m_wallet.reset(new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true));
m_history.reset(new TransactionHistoryImpl(this));
@@ -1011,6 +1012,20 @@ void WalletImpl::refreshAsync()
m_refreshCV.notify_one();
}
+bool WalletImpl::rescanBlockchain()
+{
+ clearStatus();
+ m_refreshShouldRescan = true;
+ doRefresh();
+ return status() == Status_Ok;
+}
+
+void WalletImpl::rescanBlockchainAsync()
+{
+ m_refreshShouldRescan = true;
+ refreshAsync();
+}
+
void WalletImpl::setAutoRefreshInterval(int millis)
{
if (millis > MAX_REFRESH_INTERVAL_MILLIS) {
@@ -1984,6 +1999,7 @@ void WalletImpl::refreshThreadFunc()
LOG_PRINT_L3(__FUNCTION__ << ": refresh lock acquired...");
LOG_PRINT_L3(__FUNCTION__ << ": m_refreshEnabled: " << m_refreshEnabled);
LOG_PRINT_L3(__FUNCTION__ << ": m_status: " << status());
+ LOG_PRINT_L3(__FUNCTION__ << ": m_refreshShouldRescan: " << m_refreshShouldRescan);
if (m_refreshEnabled) {
LOG_PRINT_L3(__FUNCTION__ << ": refreshing...");
doRefresh();
@@ -1994,12 +2010,16 @@ void WalletImpl::refreshThreadFunc()
void WalletImpl::doRefresh()
{
+ bool rescan = m_refreshShouldRescan.exchange(false);
// synchronizing async and sync refresh calls
boost::lock_guard<boost::mutex> guarg(m_refreshMutex2);
- try {
+ do try {
+ LOG_PRINT_L3(__FUNCTION__ << ": doRefresh, rescan = "<<rescan);
// Syncing daemon and refreshing wallet simultaneously is very resource intensive.
// Disable refresh if wallet is disconnected or daemon isn't synced.
if (m_wallet->light_wallet() || daemonSynced()) {
+ if(rescan)
+ m_wallet->rescan_blockchain(false);
m_wallet->refresh(trustedDaemon());
if (!m_synchronized) {
m_synchronized = true;
@@ -2016,7 +2036,9 @@ void WalletImpl::doRefresh()
}
} catch (const std::exception &e) {
setStatusError(e.what());
- }
+ break;
+ }while(!rescan && (rescan=m_refreshShouldRescan.exchange(false))); // repeat if not rescanned and rescan was requested
+
if (m_wallet2Callback->getListener()) {
m_wallet2Callback->getListener()->refreshed();
}
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 6d343888b..b4637b8e6 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -114,6 +114,8 @@ public:
bool synchronized() const override;
bool refresh() override;
void refreshAsync() override;
+ bool rescanBlockchain() override;
+ void rescanBlockchainAsync() override;
void setAutoRefreshInterval(int millis) override;
int autoRefreshInterval() const override;
void setRefreshFromBlockHeight(uint64_t refresh_from_block_height) override;
@@ -232,6 +234,7 @@ private:
std::atomic<bool> m_refreshEnabled;
std::atomic<bool> m_refreshThreadDone;
std::atomic<int> m_refreshIntervalMillis;
+ std::atomic<bool> m_refreshShouldRescan;
// synchronizing refresh loop;
boost::mutex m_refreshMutex;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index ec1a84877..82627de29 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -644,6 +644,17 @@ struct Wallet
virtual void refreshAsync() = 0;
/**
+ * @brief rescanBlockchain - rescans the wallet, updating transactions from daemon
+ * @return - true if refreshed successfully;
+ */
+ virtual bool rescanBlockchain() = 0;
+
+ /**
+ * @brief rescanBlockchainAsync - rescans wallet asynchronously, starting from genesys
+ */
+ virtual void rescanBlockchainAsync() = 0;
+
+ /**
* @brief setAutoRefreshInterval - setup interval for automatic refresh.
* @param seconds - interval in millis. if zero or less than zero - automatic refresh disabled;
*/
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index c5618375e..37b60c5d7 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -813,6 +813,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
+ m_upper_transaction_weight_limit(0),
m_run(true),
m_callback(0),
m_trusted_daemon(false),
@@ -845,6 +846,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_is_initialized(false),
m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
+ m_watch_only(false),
+ m_multisig(false),
+ m_multisig_threshold(0),
m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
@@ -5612,6 +5616,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
ptx.construction_data = sd;
txs.push_back(ptx);
+
+ // add tx keys only to ptx
+ txs.back().tx_key = tx_key;
+ txs.back().additional_tx_keys = additional_tx_keys;
}
// add key images
@@ -10579,7 +10587,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image));
THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature),
- error::wallet_internal_error, "Signature check failed: input " + boost::lexical_cast<std::string>(n) + "/"
+ error::signature_check_failed, boost::lexical_cast<std::string>(n) + "/"
+ boost::lexical_cast<std::string>(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)
+ ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0]));
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index bc518d04a..b3141985d 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -72,6 +72,7 @@ namespace tools
// tx_parse_error
// get_tx_pool_error
// out_of_hashchain_bounds_error
+ // signature_check_failed
// transfer_error *
// get_outs_general_error
// not_enough_unlocked_money
@@ -418,6 +419,14 @@ namespace tools
std::string to_string() const { return refresh_error::to_string(); }
};
//----------------------------------------------------------------------------------------------------
+ struct signature_check_failed : public wallet_logic_error
+ {
+ explicit signature_check_failed(std::string&& loc, const std::string& message)
+ : wallet_logic_error(std::move(loc), "Signature check failed " + message)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct transfer_error : public wallet_logic_error
{
protected:
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 1b63d65b6..6dbea2e14 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -981,6 +981,8 @@ namespace tools
for (auto &ptx: ptxs)
{
res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ if (req.get_tx_keys)
+ res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
}
if (req.export_raw)
@@ -994,6 +996,171 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er)
+ {
+ 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;
+ }
+ if (m_wallet->key_on_device())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "command not supported by HW wallet";
+ return false;
+ }
+ if(m_wallet->watch_only())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
+ er.message = "command not supported by watch-only wallet";
+ return false;
+ }
+
+ tools::wallet2::unsigned_tx_set exported_txs;
+ try
+ {
+ cryptonote::blobdata blob;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(req.unsigned_txset, blob))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_HEX;
+ er.message = "Failed to parse hex.";
+ return false;
+ }
+ if(!m_wallet->parse_unsigned_tx_from_str(blob, exported_txs))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "cannot load unsigned_txset";
+ return false;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers: " + std::string(e.what());
+ return false;
+ }
+
+ std::vector<tools::wallet2::pending_tx> ptx;
+ try
+ {
+ // gather info to ask the user
+ std::unordered_map<cryptonote::account_public_address, std::pair<std::string, uint64_t>> dests;
+ int first_known_non_zero_change_index = -1;
+ for (size_t n = 0; n < exported_txs.txes.size(); ++n)
+ {
+ const tools::wallet2::tx_construction_data &cd = exported_txs.txes[n];
+ res.desc.push_back({0, 0, std::numeric_limits<uint32_t>::max(), 0, {}, "", 0, "", 0, 0, ""});
+ wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::transfer_description &desc = res.desc.back();
+
+ std::vector<cryptonote::tx_extra_field> tx_extra_fields;
+ bool has_encrypted_payment_id = false;
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields))
+ {
+ cryptonote::tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash payment_id;
+ if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id8);
+ has_encrypted_payment_id = true;
+ }
+ else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id);
+ }
+ }
+ }
+
+ for (size_t s = 0; s < cd.sources.size(); ++s)
+ {
+ desc.amount_in += cd.sources[s].amount;
+ size_t ring_size = cd.sources[s].outputs.size();
+ if (ring_size < desc.ring_size)
+ desc.ring_size = ring_size;
+ }
+ for (size_t d = 0; d < cd.splitted_dsts.size(); ++d)
+ {
+ const cryptonote::tx_destination_entry &entry = cd.splitted_dsts[d];
+ std::string address = cryptonote::get_account_address_as_str(m_wallet->nettype(), entry.is_subaddress, entry.addr);
+ if (has_encrypted_payment_id && !entry.is_subaddress)
+ address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.addr, payment_id8);
+ auto i = dests.find(entry.addr);
+ if (i == dests.end())
+ dests.insert(std::make_pair(entry.addr, std::make_pair(address, entry.amount)));
+ else
+ i->second.second += entry.amount;
+ desc.amount_out += entry.amount;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ auto it = dests.find(cd.change_dts.addr);
+ if (it == dests.end())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change does not go to a paid address";
+ return false;
+ }
+ if (it->second.second < cd.change_dts.amount)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Claimed change is larger than payment to the change address";
+ return false;
+ }
+ if (cd.change_dts.amount > 0)
+ {
+ if (first_known_non_zero_change_index == -1)
+ first_known_non_zero_change_index = n;
+ const tools::wallet2::tx_construction_data &cdn = exported_txs.txes[first_known_non_zero_change_index];
+ if (memcmp(&cd.change_dts.addr, &cdn.change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "Change goes to more than one address";
+ return false;
+ }
+ }
+ desc.change_amount += cd.change_dts.amount;
+ it->second.second -= cd.change_dts.amount;
+ if (it->second.second == 0)
+ dests.erase(cd.change_dts.addr);
+ }
+
+ size_t n_dummy_outputs = 0;
+ for (auto i = dests.begin(); i != dests.end(); )
+ {
+ if (i->second.second > 0)
+ {
+ desc.recipients.push_back({i->second.first, i->second.second});
+ }
+ else
+ ++desc.dummy_outputs;
+ ++i;
+ }
+
+ if (desc.change_amount > 0)
+ {
+ const tools::wallet2::tx_construction_data &cd0 = exported_txs.txes[0];
+ desc.change_address = get_account_address_as_str(m_wallet->nettype(), cd0.subaddr_account > 0, cd0.change_dts.addr);
+ }
+
+ desc.fee = desc.amount_in - desc.amount_out;
+ desc.unlock_time = cd.unlock_time;
+ desc.extra = epee::to_hex::string({cd.extra.data(), cd.extra.size()});
+ }
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_BAD_UNSIGNED_TX_DATA;
+ er.message = "failed to parse unsigned transfers";
+ return false;
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -2905,6 +3072,11 @@ namespace tools
er.code = WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUT_OF_BOUNDS;
er.message = e.what();
}
+ catch (const error::signature_check_failed& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE;
+ er.message = e.what();
+ }
catch (const std::exception& e)
{
er.code = default_error_code;
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 35ea11902..887723ed5 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -87,6 +87,7 @@ namespace tools
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
+ MAP_JON_RPC_WE("describe_transfer", on_describe_transfer, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER)
MAP_JON_RPC_WE("submit_transfer", on_submit_transfer, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER)
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
MAP_JON_RPC_WE("sweep_unmixable", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
@@ -167,6 +168,7 @@ namespace tools
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er);
+ bool on_describe_transfer(const wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_DESCRIBE_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_submit_transfer(const wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_TRANSFER::response& res, epee::json_rpc::error& er);
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
bool on_sweep_all(const wallet_rpc::COMMAND_RPC_SWEEP_ALL::request& req, wallet_rpc::COMMAND_RPC_SWEEP_ALL::response& res, epee::json_rpc::error& er);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 2377b69e3..924f3a0f1 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 4
+#define WALLET_RPC_VERSION_MINOR 5
#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
@@ -531,16 +531,79 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_DESCRIBE_TRANSFER
+ {
+ struct recipient
+ {
+ std::string address;
+ uint64_t amount;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(amount)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct transfer_description
+ {
+ uint64_t amount_in;
+ uint64_t amount_out;
+ uint32_t ring_size;
+ uint64_t unlock_time;
+ std::list<recipient> recipients;
+ std::string payment_id;
+ uint64_t change_amount;
+ std::string change_address;
+ uint64_t fee;
+ uint32_t dummy_outputs;
+ std::string extra;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_in)
+ KV_SERIALIZE(amount_out)
+ KV_SERIALIZE(ring_size)
+ KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(recipients)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(change_amount)
+ KV_SERIALIZE(change_address)
+ KV_SERIALIZE(fee)
+ KV_SERIALIZE(dummy_outputs)
+ KV_SERIALIZE(extra)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct request
+ {
+ std::string unsigned_txset;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(unsigned_txset)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::list<transfer_description> desc;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(desc)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_SIGN_TRANSFER
{
struct request
{
std::string unsigned_txset;
bool export_raw;
+ bool get_tx_keys;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(unsigned_txset)
KV_SERIALIZE_OPT(export_raw, false)
+ KV_SERIALIZE_OPT(get_tx_keys, false)
END_KV_SERIALIZE_MAP()
};
@@ -549,11 +612,13 @@ namespace wallet_rpc
std::string signed_txset;
std::list<std::string> tx_hash_list;
std::list<std::string> tx_raw_list;
+ std::list<std::string> tx_key_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(signed_txset)
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_raw_list)
+ KV_SERIALIZE(tx_key_list)
END_KV_SERIALIZE_MAP()
};
};