aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/CMakeLists.txt1
-rw-r--r--src/cryptonote_core/blockchain.cpp69
-rw-r--r--src/cryptonote_core/blockchain.h33
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp98
-rw-r--r--src/cryptonote_core/cryptonote_core.h34
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp128
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h13
7 files changed, 190 insertions, 186 deletions
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 169a38f0a..eeed881da 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -59,6 +59,7 @@ target_link_libraries(cryptonote_core
common
cncrypto
blockchain_db
+ multisig
ringct
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 836856bae..709c5e852 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -305,7 +305,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 bool testnet, const cryptonote::test_options *test_options)
+bool Blockchain::init(BlockchainDB* db, const bool testnet, bool offline, const cryptonote::test_options *test_options)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_tx_pool);
@@ -321,12 +321,14 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te
if (!db->is_open())
{
LOG_ERROR("Attempted to init Blockchain with unopened DB");
+ delete db;
return false;
}
m_db = db;
m_testnet = testnet;
+ m_offline = offline;
if (m_hardfork == nullptr)
{
if (fakechain)
@@ -414,11 +416,11 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::te
return true;
}
//------------------------------------------------------------------
-bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet)
+bool Blockchain::init(BlockchainDB* db, HardFork*& hf, const bool testnet, bool offline)
{
if (hf != nullptr)
m_hardfork = hf;
- bool res = init(db, testnet, NULL);
+ bool res = init(db, testnet, offline, NULL);
if (hf == nullptr)
hf = m_hardfork;
return res;
@@ -470,7 +472,7 @@ bool Blockchain::deinit()
// memory operation), otherwise we may cause a loop.
if (m_db == NULL)
{
- throw new DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
+ throw DB_ERROR("The db pointer is null in Blockchain, the blockchain may be corrupt!");
}
try
@@ -488,7 +490,9 @@ bool Blockchain::deinit()
}
delete m_hardfork;
+ m_hardfork = NULL;
delete m_db;
+ m_db = NULL;
return true;
}
//------------------------------------------------------------------
@@ -2049,49 +2053,6 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
return true;
}
//------------------------------------------------------------------
-void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto h = m_db->height();
- if(start_index > h)
- {
- MERROR("Wrong starter index set: " << start_index << ", expected max index " << h);
- return;
- }
-
- for(size_t i = start_index; i <= h && i != end_index; i++)
- {
- ss << "height " << i << ", timestamp " << m_db->get_block_timestamp(i) << ", cumul_dif " << m_db->get_block_cumulative_difficulty(i) << ", size " << m_db->get_block_size(i) << "\nid\t\t" << m_db->get_block_hash_from_height(i) << "\ndifficulty\t\t" << m_db->get_block_difficulty(i) << ", nonce " << m_db->get_block_from_height(i).nonce << ", tx_count " << m_db->get_block_from_height(i).tx_hashes.size() << std::endl;
- }
- MCINFO("globlal", "Current blockchain:" << std::endl << ss.str());
-}
-//------------------------------------------------------------------
-void Blockchain::print_blockchain_index() const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- std::stringstream ss;
- CRITICAL_REGION_LOCAL(m_blockchain_lock);
- auto height = m_db->height();
- if (height != 0)
- {
- for(uint64_t i = 0; i <= height; i++)
- {
- ss << "height: " << i << ", hash: " << m_db->get_block_hash_from_height(i);
- }
- }
-
- MINFO("Current blockchain index:" << std::endl << ss.str());
-}
-//------------------------------------------------------------------
-//TODO: remove this function and references to it
-void Blockchain::print_blockchain_outs(const std::string& file) const
-{
- LOG_PRINT_L3("Blockchain::" << __func__);
- return;
-}
-//------------------------------------------------------------------
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
@@ -2337,7 +2298,7 @@ void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx)
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
}
}
@@ -2372,7 +2333,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
TIME_MEASURE_FINISH(a);
if(m_show_time_stats)
{
- size_t ring_size = tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
+ size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx));
}
if (!res)
@@ -2465,6 +2426,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
// mixRing - full and simple store it in opposite ways
if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
+ CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys[0].size());
for (size_t m = 0; m < pubkeys[0].size(); ++m)
rv.mixRing[m].clear();
@@ -2479,6 +2441,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
+ CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys");
rv.mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
{
@@ -2810,7 +2773,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
for (size_t n = 0; n < tx.vin.size(); ++n)
{
- if (memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
+ if (rv.p.MGs[n].II.empty() || memcmp(&boost::get<txin_to_key>(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32))
{
MERROR_VER("Failed to check ringct signatures: mismatched key image");
return false;
@@ -2863,7 +2826,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
MERROR_VER("Failed to check ringct signatures: Bad MGs size");
return false;
}
- if (rv.p.MGs[0].II.size() != tx.vin.size())
+ if (rv.p.MGs.empty() || rv.p.MGs[0].II.size() != tx.vin.size())
{
MERROR_VER("Failed to check ringct signatures: mismatched II/vin sizes");
return false;
@@ -3641,14 +3604,14 @@ bool Blockchain::update_checkpoints(const std::string& file_path, bool check_dns
// if we're checking both dns and json, load checkpoints from dns.
// if we're not hard-enforcing dns checkpoints, handle accordingly
- if (m_enforce_dns_checkpoints && check_dns)
+ if (m_enforce_dns_checkpoints && check_dns && !m_offline)
{
if (!m_checkpoints.load_checkpoints_from_dns())
{
return false;
}
}
- else if (check_dns)
+ else if (check_dns && !m_offline)
{
checkpoints dns_points;
dns_points.load_checkpoints_from_dns();
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index e0936da8f..2d5307ac0 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -112,11 +112,12 @@ namespace cryptonote
*
* @param db a pointer to the backing store to use for the blockchain
* @param testnet true if on testnet, else false
+ * @param offline true if running offline, else false
* @param test_options test parameters
*
* @return true on success, false if any initialization steps fail
*/
- bool init(BlockchainDB* db, const bool testnet = false, const cryptonote::test_options *test_options = NULL);
+ bool init(BlockchainDB* db, const bool testnet = false, bool offline = false, const cryptonote::test_options *test_options = NULL);
/**
* @brief Initialize the Blockchain state
@@ -124,10 +125,11 @@ namespace cryptonote
* @param db a pointer to the backing store to use for the blockchain
* @param hf a structure containing hardfork information
* @param testnet true if on testnet, else false
+ * @param offline true if running offline, else false
*
* @return true on success, false if any initialization steps fail
*/
- bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false);
+ bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false, bool offline = false);
/**
* @brief Uninitializes the blockchain state
@@ -678,32 +680,6 @@ namespace cryptonote
//debug functions
/**
- * @brief prints data about a snippet of the blockchain
- *
- * if start_index is greater than the blockchain height, do nothing
- *
- * @param start_index height on chain to start at
- * @param end_index height on chain to end at
- */
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
-
- /**
- * @brief prints every block's hash
- *
- * WARNING: This function will absolutely crush a terminal in prints, so
- * it is recommended to redirect this output to a log file (or null sink
- * if a log file is already set up, as should be the default)
- */
- void print_blockchain_index() const;
-
- /**
- * @brief currently does nothing, candidate for removal
- *
- * @param file
- */
- void print_blockchain_outs(const std::string& file) const;
-
- /**
* @brief check the blockchain against a set of checkpoints
*
* If a block fails a checkpoint and enforce is enabled, the blockchain
@@ -1027,6 +1003,7 @@ namespace cryptonote
HardFork *m_hardfork;
bool m_testnet;
+ bool m_offline;
std::atomic<bool> m_cancel;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 5cfa4b3e9..adbc727b0 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -28,7 +28,10 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <boost/algorithm/string.hpp>
+
#include "include_base_utils.h"
+#include "string_tools.h"
using namespace epee;
#include <unordered_set>
@@ -44,12 +47,13 @@ using namespace epee;
#include "cryptonote_config.h"
#include "cryptonote_tx_utils.h"
#include "misc_language.h"
+#include "file_io_utils.h"
#include <csignal>
-#include <p2p/net_node.h>
#include "checkpoints/checkpoints.h"
#include "ringct/rctTypes.h"
#include "blockchain_db/blockchain_db.h"
#include "ringct/rctSigs.h"
+#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "cn"
@@ -75,6 +79,10 @@ namespace cryptonote
, "Run on testnet. The wallet must be launched with --testnet flag."
, false
};
+ const command_line::arg_descriptor<bool> arg_offline = {
+ "offline"
+ , "Do not listen for peers, nor connect to any"
+ };
static const command_line::arg_descriptor<bool> arg_test_drop_download = {
"test-drop-download"
@@ -227,10 +235,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_check_updates);
command_line::add_arg(desc, arg_fluffy_blocks);
command_line::add_arg(desc, arg_test_dbg_lock_sleep);
-
- // we now also need some of net_node's options (p2p bind arg, for separate data dir)
- command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false);
- command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false);
+ command_line::add_arg(desc, arg_offline);
miner::init_options(desc);
BlockchainDB::init_options(desc);
@@ -264,6 +269,7 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
m_fluffy_blocks_enabled = m_testnet || get_arg(vm, arg_fluffy_blocks);
+ m_offline = get_arg(vm, arg_offline);
if (command_line::get_arg(vm, arg_test_drop_download) == true)
test_drop_download();
@@ -329,21 +335,17 @@ namespace cryptonote
return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
- bool core::init(const boost::program_options::variables_map& vm, 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)
{
start_time = std::time(nullptr);
m_fakechain = test_options != NULL;
bool r = handle_command_line(vm);
bool testnet = command_line::get_arg(vm, arg_testnet_on);
- auto p2p_bind_arg = testnet ? nodetool::arg_testnet_p2p_bind_port : nodetool::arg_p2p_bind_port;
- std::string m_port = command_line::get_arg(vm, p2p_bind_arg);
std::string m_config_folder_mempool = m_config_folder;
- if ((!testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT))
- || (testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) {
- m_config_folder_mempool = m_config_folder_mempool + "/" + m_port;
- }
+ if (config_subdir)
+ m_config_folder_mempool = m_config_folder_mempool + "/" + config_subdir;
std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
@@ -376,7 +378,7 @@ namespace cryptonote
// folder might not be a directory, etc, etc
catch (...) { }
- BlockchainDB* db = new_db(db_type);
+ std::unique_ptr<BlockchainDB> db(new_db(db_type));
if (db == NULL)
{
LOG_ERROR("Attempted to use non-existent database type");
@@ -467,7 +469,7 @@ namespace cryptonote
m_blockchain_storage.set_user_options(blocks_threads,
blocks_per_sync, sync_mode, fast_sync);
- r = m_blockchain_storage.init(db, m_testnet, test_options);
+ r = m_blockchain_storage.init(db.release(), m_testnet, m_offline, test_options);
r = m_mempool.init();
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -1051,21 +1053,6 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count);
}
//-----------------------------------------------------------------------------------------------
- void core::print_blockchain(uint64_t start_index, uint64_t end_index) const
- {
- m_blockchain_storage.print_blockchain(start_index, end_index);
- }
- //-----------------------------------------------------------------------------------------------
- void core::print_blockchain_index() const
- {
- m_blockchain_storage.print_blockchain_index();
- }
- //-----------------------------------------------------------------------------------------------
- void core::print_blockchain_outs(const std::string& file)
- {
- m_blockchain_storage.print_blockchain_outs(file);
- }
- //-----------------------------------------------------------------------------------------------
bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const
{
return m_blockchain_storage.get_random_outs_for_amounts(req, res);
@@ -1340,11 +1327,16 @@ namespace cryptonote
{
if(!m_starter_message_showed)
{
+ std::string main_message;
+ if (m_offline)
+ main_message = "The daemon is running offline and will not attempt to sync to the Monero network.";
+ else
+ main_message = "The daemon will start synchronizing with the network. This may take a long time to complete.";
MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL
- << "The daemon will start synchronizing with the network. This may take a long time to complete." << ENDL
+ << main_message << ENDL
<< ENDL
- << "You can set the level of process detailization* through \"set_log <level|categories>\" command*," << ENDL
- << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)" << ENDL
+ << "You can set the level of process detailization through \"set_log <level|categories>\" command," << ENDL
+ << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL
<< ENDL
<< "Use the \"help\" command to see the list of available commands." << ENDL
<< "Use \"help <command>\" to see a command's documentation." << ENDL
@@ -1404,6 +1396,9 @@ namespace cryptonote
static const char subdir[] = "source"; // because it can never be simple
#endif
+ if (m_offline)
+ return true;
+
if (check_updates_level == UPDATES_DISABLED)
return true;
@@ -1443,27 +1438,56 @@ namespace cryptonote
if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash)))
{
MCDEBUG("updates", "We don't have that file already, downloading");
+ const std::string tmppath = path.string() + ".tmp";
+ if (epee::file_io_utils::is_file_exist(tmppath))
+ {
+ MCDEBUG("updates", "We have part of the file already, resuming download");
+ }
m_last_update_length = 0;
- m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) {
+ m_update_download = tools::download_async(tmppath, url, [this, hash, path](const std::string &tmppath, const std::string &uri, bool success) {
+ bool remove = false, good = true;
if (success)
{
crypto::hash file_hash;
- if (!tools::sha256sum(path, file_hash))
+ if (!tools::sha256sum(tmppath, file_hash))
{
- MCERROR("updates", "Failed to hash " << path);
+ MCERROR("updates", "Failed to hash " << tmppath);
+ remove = true;
+ good = false;
}
- if (hash != epee::string_tools::pod_to_hex(file_hash))
+ else if (hash != epee::string_tools::pod_to_hex(file_hash))
{
MCERROR("updates", "Download from " << uri << " does not match the expected hash");
+ remove = true;
+ good = false;
}
- MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path);
}
else
{
MCERROR("updates", "Failed to download " << uri);
+ good = false;
}
boost::unique_lock<boost::mutex> lock(m_update_mutex);
m_update_download = 0;
+ if (success && !remove)
+ {
+ std::error_code e = tools::replace_file(tmppath, path.string());
+ if (e)
+ {
+ MCERROR("updates", "Failed to rename downloaded file");
+ good = false;
+ }
+ }
+ else if (remove)
+ {
+ if (!boost::filesystem::remove(tmppath))
+ {
+ MCERROR("updates", "Failed to remove invalid downloaded file");
+ good = false;
+ }
+ }
+ if (good)
+ MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path.string());
}, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) {
if (length >= m_last_update_length + 1024 * 1024 * 10)
{
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 905e67f6d..adc201fb5 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -36,7 +36,6 @@
#include <boost/program_options/variables_map.hpp>
#include <boost/interprocess/sync/file_lock.hpp>
-#include "p2p/net_node_common.h"
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "common/download.h"
@@ -62,6 +61,7 @@ namespace cryptonote
extern const command_line::arg_descriptor<std::string> arg_data_dir;
extern const command_line::arg_descriptor<std::string> arg_testnet_data_dir;
extern const command_line::arg_descriptor<bool, false> arg_testnet_on;
+ extern const command_line::arg_descriptor<bool> arg_offline;
/************************************************************************/
/* */
@@ -241,11 +241,12 @@ namespace cryptonote
* a miner instance with parameters given on the command line (or defaults)
*
* @param vm command line parameters
+ * @param config_subdir subdirectory for config storage
* @param test_options configuration options for testing
*
* @return false if one of the init steps fails, otherwise true
*/
- bool init(const boost::program_options::variables_map& vm, 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);
/**
* @copydoc Blockchain::reset_and_set_genesis_block
@@ -600,20 +601,6 @@ namespace cryptonote
const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;}
/**
- * @copydoc Blockchain::print_blockchain
- *
- * @note see Blockchain::print_blockchain
- */
- void print_blockchain(uint64_t start_index, uint64_t end_index) const;
-
- /**
- * @copydoc Blockchain::print_blockchain_index
- *
- * @note see Blockchain::print_blockchain_index
- */
- void print_blockchain_index() const;
-
- /**
* @copydoc tx_memory_pool::print_pool
*
* @note see tx_memory_pool::print_pool
@@ -621,13 +608,6 @@ namespace cryptonote
std::string print_pool(bool short_format) const;
/**
- * @copydoc Blockchain::print_blockchain_outs
- *
- * @note see Blockchain::print_blockchain_outs
- */
- void print_blockchain_outs(const std::string& file);
-
- /**
* @copydoc miner::on_synchronized
*
* @note see miner::on_synchronized
@@ -773,6 +753,13 @@ namespace cryptonote
*/
uint64_t get_free_space() const;
+ /**
+ * @brief get whether the core is running offline
+ *
+ * @return whether the core is running offline
+ */
+ bool offline() const { return m_offline; }
+
private:
/**
@@ -1000,6 +987,7 @@ namespace cryptonote
boost::mutex m_update_mutex;
bool m_fluffy_blocks_enabled;
+ bool m_offline;
};
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 4afa669fd..916b1e05a 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -30,6 +30,7 @@
#include <unordered_set>
#include "include_base_utils.h"
+#include "string_tools.h"
using namespace epee;
#include "common/apply_permutation.h"
@@ -39,12 +40,39 @@ using namespace epee;
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
+#include "multisig/multisig.h"
using namespace crypto;
namespace cryptonote
{
//---------------------------------------------------------------
+ void classify_addresses(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr, size_t &num_stdaddresses, size_t &num_subaddresses, account_public_address &single_dest_subaddress)
+ {
+ num_stdaddresses = 0;
+ num_subaddresses = 0;
+ std::unordered_set<cryptonote::account_public_address> unique_dst_addresses;
+ for(const tx_destination_entry& dst_entr: destinations)
+ {
+ if (change_addr && dst_entr.addr == change_addr)
+ continue;
+ if (unique_dst_addresses.count(dst_entr.addr) == 0)
+ {
+ unique_dst_addresses.insert(dst_entr.addr);
+ if (dst_entr.is_subaddress)
+ {
+ ++num_subaddresses;
+ single_dest_subaddress = dst_entr.addr;
+ }
+ else
+ {
+ ++num_stdaddresses;
+ }
+ }
+ }
+ LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses");
+ }
+ //---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
tx.vin.clear();
tx.vout.clear();
@@ -160,19 +188,27 @@ namespace cryptonote
return destinations[0].addr.m_view_public_key;
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof)
+ bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
{
+ if (sources.empty())
+ {
+ LOG_ERROR("Empty sources");
+ return false;
+ }
+
std::vector<rct::key> amount_keys;
tx.set_null();
amount_keys.clear();
+ if (msout)
+ {
+ msout->c.clear();
+ }
tx.version = rct ? 2 : 1;
tx.unlock_time = unlock_time;
tx.extra = extra;
- keypair txkey;
- txkey.sec = rct::rct2sk(rct::skGen());
- tx_key = txkey.sec;
+ crypto::public_key txkey_pub;
// if we have a stealth payment id, find it and encrypt it with the tx key now
std::vector<tx_extra_field> tx_extra_fields;
@@ -192,7 +228,7 @@ namespace cryptonote
return false;
}
- if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec))
+ if (!encrypt_payment_id(payment_id, view_key_pub, tx_key))
{
LOG_ERROR("Failed to encrypt payment id");
return false;
@@ -246,8 +282,8 @@ namespace cryptonote
return false;
}
- //check that derivated key is equal with real output key
- if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
+ //check that derivated key is equal with real output key (if non multisig)
+ if(!msout && !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
{
LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
@@ -260,7 +296,7 @@ namespace cryptonote
//put key image into tx input
txin_to_key input_to_key;
input_to_key.amount = src_entr.amount;
- input_to_key.k_image = img;
+ input_to_key.k_image = msout ? rct::rct2ki(src_entr.multisig_kLRki.ki) : img;
//fill outputs array and use relative offsets
for(const tx_source_entry::output_entry& out_entry: src_entr.outputs)
@@ -292,47 +328,29 @@ namespace cryptonote
// figure out if we need to make additional tx pubkeys
size_t num_stdaddresses = 0;
size_t num_subaddresses = 0;
- std::unordered_set<cryptonote::account_public_address> unique_dst_addresses;
account_public_address single_dest_subaddress;
- for(const tx_destination_entry& dst_entr: destinations)
- {
- if (change_addr && dst_entr.addr == *change_addr)
- continue;
- if (unique_dst_addresses.count(dst_entr.addr) == 0)
- {
- unique_dst_addresses.insert(dst_entr.addr);
- if (dst_entr.is_subaddress)
- {
- ++num_subaddresses;
- single_dest_subaddress = dst_entr.addr;
- }
- else
- {
- ++num_stdaddresses;
- }
- }
- }
- LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << "subaddresses");
+ classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
// if this is a single-destination transfer to a subaddress, we set the tx pubkey to R=s*D
if (num_stdaddresses == 0 && num_subaddresses == 1)
{
- txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(txkey.sec)));
+ txkey_pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(single_dest_subaddress.m_spend_public_key), rct::sk2rct(tx_key)));
}
else
{
- txkey.pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(txkey.sec)));
+ txkey_pub = rct::rct2pk(rct::scalarmultBase(rct::sk2rct(tx_key)));
}
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
- add_tx_pub_key_to_extra(tx, txkey.pub);
+ add_tx_pub_key_to_extra(tx, txkey_pub);
std::vector<crypto::public_key> additional_tx_public_keys;
- additional_tx_keys.clear();
// we don't need to include additional tx keys if:
// - all the destinations are standard addresses
// - there's only one destination which is a subaddress
bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
+ if (need_additional_txkeys)
+ CHECK_AND_ASSERT_MES(destinations.size() == additional_tx_keys.size(), false, "Wrong amount of additional tx keys");
uint64_t summary_outs_money = 0;
//fill outputs
@@ -347,7 +365,7 @@ namespace cryptonote
keypair additional_txkey;
if (need_additional_txkeys)
{
- additional_txkey.sec = rct::rct2sk(rct::skGen());
+ additional_txkey.sec = additional_tx_keys[output_index];
if (dst_entr.is_subaddress)
additional_txkey.pub = rct::rct2pk(rct::scalarmultKey(rct::pk2rct(dst_entr.addr.m_spend_public_key), rct::sk2rct(additional_txkey.sec)));
else
@@ -358,20 +376,19 @@ namespace cryptonote
if (change_addr && dst_entr.addr == *change_addr)
{
// sending change to yourself; derivation = a*R
- r = crypto::generate_key_derivation(txkey.pub, sender_account_keys.m_view_secret_key, derivation);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey.pub << ", " << sender_account_keys.m_view_secret_key << ")");
+ r = crypto::generate_key_derivation(txkey_pub, sender_account_keys.m_view_secret_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << txkey_pub << ", " << sender_account_keys.m_view_secret_key << ")");
}
else
{
// sending to the recipient; derivation = r*A (or s*C in the subaddress scheme)
- r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec, derivation);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : txkey.sec) << ")");
+ r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << (dst_entr.is_subaddress && need_additional_txkeys ? additional_txkey.sec : tx_key) << ")");
}
if (need_additional_txkeys)
{
additional_tx_public_keys.push_back(additional_txkey.pub);
- additional_tx_keys.push_back(additional_txkey.sec);
}
if (tx.version > 1)
@@ -392,10 +409,11 @@ namespace cryptonote
output_index++;
summary_outs_money += dst_entr.amount;
}
+ CHECK_AND_ASSERT_MES(additional_tx_public_keys.size() == additional_tx_keys.size(), false, "Internal error creating additional public keys");
remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys));
- LOG_PRINT_L2("tx pubkey: " << txkey.pub);
+ LOG_PRINT_L2("tx pubkey: " << txkey_pub);
if (need_additional_txkeys)
{
LOG_PRINT_L2("additional tx pubkeys: ");
@@ -491,6 +509,7 @@ namespace cryptonote
rct::keyV destinations;
std::vector<uint64_t> inamounts, outamounts;
std::vector<unsigned int> index;
+ std::vector<rct::multisig_kLRki> kLRki;
for (size_t i = 0; i < sources.size(); ++i)
{
rct::ctkey ctkey;
@@ -503,6 +522,10 @@ namespace cryptonote
inSk.push_back(ctkey);
// inPk: (public key, commitment)
// will be done when filling in mixRing
+ if (msout)
+ {
+ kLRki.push_back(sources[i].multisig_kLRki);
+ }
}
for (size_t i = 0; i < tx.vout.size(); ++i)
{
@@ -552,9 +575,9 @@ namespace cryptonote
get_transaction_prefix_hash(tx, tx_prefix_hash);
rct::ctkeyV outSk;
if (use_simple_rct)
- tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, bulletproof);
+ tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, bulletproof);
else
- tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, bulletproof); // same index assumption
+ tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, bulletproof); // same index assumption
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
@@ -566,13 +589,34 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof, rct::multisig_out *msout)
+ {
+ keypair txkey = keypair::generate();
+ tx_key = txkey.sec;
+
+ // figure out if we need to make additional tx pubkeys
+ size_t num_stdaddresses = 0;
+ size_t num_subaddresses = 0;
+ account_public_address single_dest_subaddress;
+ classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
+ bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
+ if (need_additional_txkeys)
+ {
+ additional_tx_keys.clear();
+ for (const auto &d: destinations)
+ additional_tx_keys.push_back(keypair::generate().sec);
+ }
+
+ return construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, bulletproof, msout);
+ }
+ //---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time)
{
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys);
+ return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, NULL);
}
//---------------------------------------------------------------
bool generate_genesis_block(
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index d72f5d13b..5947522e2 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -51,6 +51,7 @@ namespace cryptonote
uint64_t amount; //money
bool rct; //true if the output is rct
rct::key mask; //ringct amount mask
+ rct::multisig_kLRki multisig_kLRki; //multisig info
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
@@ -63,6 +64,7 @@ namespace cryptonote
FIELD(amount)
FIELD(rct)
FIELD(mask)
+ FIELD(multisig_kLRki)
if (real_output >= outputs.size())
return false;
@@ -87,8 +89,9 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys);
- bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false);
+ bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
+ bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false, rct::multisig_out *msout = NULL);
bool generate_genesis_block(
block& bl
@@ -98,7 +101,7 @@ namespace cryptonote
}
-BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0)
+BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1)
BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1)
namespace boost
@@ -115,6 +118,10 @@ namespace boost
a & x.amount;
a & x.rct;
a & x.mask;
+ if (ver < 1)
+ return;
+ a & x.multisig_kLRki;
+ a & x.real_out_additional_tx_keys;
}
template <class Archive>