aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h10
-rw-r--r--src/cryptonote_basic/miner.cpp3
-rw-r--r--src/cryptonote_basic/miner.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp197
-rw-r--r--src/cryptonote_core/blockchain.h20
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp11
-rw-r--r--src/cryptonote_core/cryptonote_core.h4
-rw-r--r--src/rpc/core_rpc_server.cpp23
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h10
-rw-r--r--src/simplewallet/simplewallet.cpp39
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/wallet/ringdb.cpp32
-rw-r--r--src/wallet/ringdb.h1
-rw-r--r--src/wallet/wallet2.cpp37
-rw-r--r--src/wallet/wallet2.h2
15 files changed, 308 insertions, 85 deletions
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 3f8eef076..c9de2a56e 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -142,6 +142,16 @@ namespace cryptonote
std::string print_money(uint64_t amount, unsigned int decimal_point = -1);
//---------------------------------------------------------------
template<class t_object>
+ bool t_serializable_object_from_blob(t_object& to, const blobdata& b_blob)
+ {
+ std::stringstream ss;
+ ss << b_blob;
+ binary_archive<false> ba(ss);
+ bool r = ::serialization::serialize(ba, to);
+ return r;
+ }
+ //---------------------------------------------------------------
+ template<class t_object>
bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob)
{
std::stringstream ss;
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index ded6e346f..e6c6bddb6 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -576,7 +576,8 @@ namespace cryptonote
//we lucky!
++m_config.current_extra_message_index;
MGINFO_GREEN("Found block " << get_block_hash(b) << " at height " << height << " for difficulty: " << local_diff);
- if(!m_phandler->handle_block_found(b))
+ cryptonote::block_verification_context bvc;
+ if(!m_phandler->handle_block_found(b, bvc) || !bvc.m_added_to_main_chain)
{
--m_config.current_extra_message_index;
}else
diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h
index bbb576fff..285075f51 100644
--- a/src/cryptonote_basic/miner.h
+++ b/src/cryptonote_basic/miner.h
@@ -34,6 +34,7 @@
#include <boost/logic/tribool_fwd.hpp>
#include <atomic>
#include "cryptonote_basic.h"
+#include "verification_context.h"
#include "difficulty.h"
#include "math_helper.h"
#ifdef _WIN32
@@ -45,7 +46,7 @@ namespace cryptonote
struct i_miner_handler
{
- virtual bool handle_block_found(block& b) = 0;
+ virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0;
protected:
~i_miner_handler(){};
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 8ab249ac1..7ef8f8c45 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1008,7 +1008,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
//------------------------------------------------------------------
// This function attempts to switch to an alternate chain, returning
// boolean based on success therein.
-bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain)
+bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1109,7 +1109,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
//------------------------------------------------------------------
// This function calculates the difficulty target for the block being added to
// an alternate chain.
-difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const
+difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const
{
if (m_fixed_difficulty)
{
@@ -1351,7 +1351,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
-bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_weight;
@@ -1361,8 +1361,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
m_tx_pool.lock();
const auto unlock_guard = epee::misc_utils::create_scope_leave_handler([&]() { m_tx_pool.unlock(); });
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- height = m_db->height();
- if (m_btc_valid) {
+ if (m_btc_valid && !from_block) {
// The pool cookie is atomic. The lack of locking is OK, as if it changes
// just as we compare it, we'll just use a slightly old template, but
// this would be the case anyway if we'd lock, and the change happened
@@ -1376,13 +1375,75 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
expected_reward = m_btc_expected_reward;
return true;
}
- MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()));
+ MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()) << ", from_block " << (!!from_block));
invalidate_block_template_cache();
}
- b.major_version = m_hardfork->get_current_version();
- b.minor_version = m_hardfork->get_ideal_version();
- b.prev_id = get_tail_id();
+ if (from_block)
+ {
+ //build alternative subchain, front -> mainchain, back -> alternative head
+ //block is not related with head of main chain
+ //first of all - look in alternative chains container
+ auto it_prev = m_alternative_chains.find(*from_block);
+ bool parent_in_main = m_db->block_exists(*from_block);
+ if(it_prev == m_alternative_chains.end() && !parent_in_main)
+ {
+ MERROR("Unknown from block");
+ return false;
+ }
+
+ //we have new block in alternative chain
+ std::list<blocks_ext_by_hash::const_iterator> alt_chain;
+ block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ std::vector<uint64_t> timestamps;
+ if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
+ return false;
+
+ if (parent_in_main)
+ {
+ cryptonote::block prev_block;
+ CHECK_AND_ASSERT_MES(get_block_by_hash(*from_block, prev_block), false, "From block not found"); // TODO
+ uint64_t from_block_height = cryptonote::get_block_height(prev_block);
+ height = from_block_height + 1;
+ }
+ else
+ {
+ height = alt_chain.back()->second.height + 1;
+ }
+ b.major_version = m_hardfork->get_ideal_version(height);
+ b.minor_version = m_hardfork->get_ideal_version();
+ b.prev_id = *from_block;
+
+ // cheat and use the weight of the block we start from, virtually certain to be acceptable
+ // and use 1.9 times rather than 2 times so we're even more sure
+ if (parent_in_main)
+ {
+ median_weight = m_db->get_block_weight(height - 1);
+ already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+ }
+ else
+ {
+ median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20;
+ already_generated_coins = alt_chain.back()->second.already_generated_coins;
+ }
+
+ // FIXME: consider moving away from block_extended_info at some point
+ block_extended_info bei = boost::value_initialized<block_extended_info>();
+ bei.bl = b;
+ bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(*from_block) + 1;
+
+ diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei);
+ }
+ else
+ {
+ height = m_db->height();
+ b.major_version = m_hardfork->get_current_version();
+ b.minor_version = m_hardfork->get_ideal_version();
+ b.prev_id = get_tail_id();
+ median_weight = m_current_block_cumul_weight_limit / 2;
+ diffic = get_difficulty_for_next_block();
+ already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
+ }
b.timestamp = time(NULL);
uint64_t median_ts;
@@ -1391,15 +1452,11 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
b.timestamp = median_ts;
}
- diffic = get_difficulty_for_next_block();
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
- median_weight = m_current_block_cumul_weight_limit / 2;
- already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
-
size_t txs_weight;
uint64_t fee;
- if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version()))
+ if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
{
return false;
}
@@ -1462,7 +1519,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
- uint8_t hf_version = m_hardfork->get_current_version();
+ uint8_t hf_version = b.major_version;
size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
@@ -1517,16 +1574,22 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
", cumulative weight " << cumulative_weight << " is now good");
#endif
- cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, pool_cookie);
+ if (!from_block)
+ cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, pool_cookie);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
return false;
}
//------------------------------------------------------------------
+bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+{
+ return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce);
+}
+//------------------------------------------------------------------
// for an alternate chain, get the timestamps from the main chain to complete
// the needed number of timestamps for the BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW.
-bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
+bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1546,6 +1609,52 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
return true;
}
//------------------------------------------------------------------
+bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const
+{
+ //build alternative subchain, front -> mainchain, back -> alternative head
+ blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id);
+ timestamps.clear();
+ while(alt_it != m_alternative_chains.end())
+ {
+ alt_chain.push_front(alt_it);
+ timestamps.push_back(alt_it->second.bl.timestamp);
+ alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
+ }
+
+ // if block to be added connects to known blocks that aren't part of the
+ // main chain -- that is, if we're adding on to an alternate chain
+ if(!alt_chain.empty())
+ {
+ // make sure alt chain doesn't somehow start past the end of the main chain
+ CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
+
+ // make sure that the blockchain contains the block that should connect
+ // this alternate chain with it.
+ if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
+ {
+ MERROR("alternate chain does not appear to connect to main chain...");
+ return false;
+ }
+
+ // make sure block connects correctly to the main chain
+ auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
+ CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
+ complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
+ }
+ // if block not associated with known alternate chain
+ else
+ {
+ // if block parent is not part of main chain or an alternate chain,
+ // we ignore it
+ bool parent_in_main = m_db->block_exists(prev_id);
+ CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition: parent_in_main");
+
+ complete_timestamps_vector(m_db->get_block_height(prev_id), timestamps);
+ }
+
+ return true;
+}
+//------------------------------------------------------------------
// If a block is to be added and its parent block is not the current
// main chain top block, then we need to see if we know about its parent block.
// If its parent block is part of a known forked chain, then we need to see
@@ -1590,47 +1699,18 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
if(it_prev != m_alternative_chains.end() || parent_in_main)
{
//we have new block in alternative chain
-
- //build alternative subchain, front -> mainchain, back -> alternative head
- blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find()
- std::list<blocks_ext_by_hash::iterator> alt_chain;
+ std::list<blocks_ext_by_hash::const_iterator> alt_chain;
std::vector<uint64_t> timestamps;
- while(alt_it != m_alternative_chains.end())
- {
- alt_chain.push_front(alt_it);
- timestamps.push_back(alt_it->second.bl.timestamp);
- alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
- }
-
- // if block to be added connects to known blocks that aren't part of the
- // main chain -- that is, if we're adding on to an alternate chain
- if(!alt_chain.empty())
- {
- // make sure alt chain doesn't somehow start past the end of the main chain
- CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
-
- // make sure that the blockchain contains the block that should connect
- // this alternate chain with it.
- if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
- {
- MERROR("alternate chain does not appear to connect to main chain...");
- return false;
- }
-
- // make sure block connects correctly to the main chain
- auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
- CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
- complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
- }
- // if block not associated with known alternate chain
- else
- {
- // if block parent is not part of main chain or an alternate chain,
- // we ignore it
- CHECK_AND_ASSERT_MES(parent_in_main, false, "internal error: broken imperative condition: parent_in_main");
+ if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
+ return false;
- complete_timestamps_vector(m_db->get_block_height(b.prev_id), timestamps);
- }
+ // FIXME: consider moving away from block_extended_info at some point
+ block_extended_info bei = boost::value_initialized<block_extended_info>();
+ bei.bl = b;
+ const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id);
+ bei.height = prev_height + 1;
+ uint64_t block_reward = get_outs_money_amount(b.miner_tx);
+ bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
// verify that the block's timestamp is within the acceptable range
// (not earlier than the median of the last X blocks)
@@ -1641,11 +1721,6 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return false;
}
- // FIXME: consider moving away from block_extended_info at some point
- block_extended_info bei = boost::value_initialized<block_extended_info>();
- bei.bl = b;
- bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(b.prev_id) + 1;
-
bool is_a_checkpoint;
if(!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
{
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index c1677ed37..3588bbd1b 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -336,6 +336,7 @@ namespace cryptonote
* @brief creates a new block to mine against
*
* @param b return-by-reference block to be filled in
+ * @param from_block optional block hash to start mining from (main chain tip if NULL)
* @param miner_address address new coins for the block will go to
* @param di return-by-reference tells the miner what the difficulty target is
* @param height return-by-reference tells the miner what height it's mining against
@@ -345,6 +346,7 @@ namespace cryptonote
* @return true if block template filled in successfully, else false
*/
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
+ bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
/**
* @brief checks if a block is known about with a given hash
@@ -1180,7 +1182,7 @@ namespace cryptonote
*
* @return false if the reorganization fails, otherwise true
*/
- bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
+ bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain);
/**
* @brief removes the most recent block from the blockchain
@@ -1234,6 +1236,18 @@ namespace cryptonote
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
/**
+ * @brief builds a list of blocks connecting a block to the main chain
+ *
+ * @param prev_id the block hash of the tip of the alt chain
+ * @param alt_chain the chain to be added to
+ * @param timestamps returns the timestamps of previous blocks
+ * @param bvc the block verification context for error return
+ *
+ * @return true on success, false otherwise
+ */
+ bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const;
+
+ /**
* @brief gets the difficulty requirement for a new block on an alternate chain
*
* @param alt_chain the chain to be added to
@@ -1241,7 +1255,7 @@ namespace cryptonote
*
* @return the difficulty requirement
*/
- difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
+ difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const;
/**
* @brief sanity checks a miner transaction before validating an entire block
@@ -1401,7 +1415,7 @@ namespace cryptonote
*
* @return true unless start_height is greater than the current blockchain height
*/
- bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
+ bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps) const;
/**
* @brief calculate the block weight limit for the next block to be added
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 6b0052dc0..91dea4982 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1268,6 +1268,11 @@ namespace cryptonote
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
+ {
+ return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
@@ -1321,9 +1326,9 @@ namespace cryptonote
return bce;
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_block_found(block& b)
+ bool core::handle_block_found(block& b, block_verification_context &bvc)
{
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ bvc = boost::value_initialized<block_verification_context>();
m_miner.pause();
std::vector<block_complete_entry> blocks;
try
@@ -1373,7 +1378,7 @@ namespace cryptonote
m_pprotocol->relay_block(arg, exclude_context);
}
- return bvc.m_added_to_main_chain;
+ return true;
}
//-----------------------------------------------------------------------------------------------
void core::on_synchronized()
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 356265dd6..2fcf26a17 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -195,10 +195,11 @@ namespace cryptonote
* the network.
*
* @param b the block found
+ * @param bvc returns the block verification flags
*
* @return true if the block was added to the main chain, otherwise false
*/
- virtual bool handle_block_found( block& b);
+ virtual bool handle_block_found(block& b, block_verification_context &bvc);
/**
* @copydoc Blockchain::create_block_template
@@ -206,6 +207,7 @@ namespace cryptonote
* @note see Blockchain::create_block_template
*/
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
+ virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
/**
* @brief called when a transaction is relayed
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index b540520c7..71bfcc950 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -496,6 +496,7 @@ namespace cryptonote
cryptonote::COMMAND_RPC_GET_OUTPUTS_BIN::request req_bin;
req_bin.outputs = req.outputs;
+ req_bin.get_txid = req.get_txid;
cryptonote::COMMAND_RPC_GET_OUTPUTS_BIN::response res_bin;
if(!m_core.get_outs(req_bin, res_bin))
{
@@ -1259,7 +1260,17 @@ namespace cryptonote
cryptonote::blobdata blob_reserve;
blob_reserve.resize(req.reserve_size, 0);
cryptonote::difficulty_type wdiff;
- if(!m_core.get_block_template(b, info.address, wdiff, res.height, res.expected_reward, blob_reserve))
+ crypto::hash prev_block;
+ if (!req.prev_block.empty())
+ {
+ if (!epee::string_tools::hex_to_pod(req.prev_block, prev_block))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Invalid prev_block";
+ return false;
+ }
+ }
+ if(!m_core.get_block_template(b, req.prev_block.empty() ? NULL : &prev_block, info.address, wdiff, res.height, res.expected_reward, blob_reserve))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
@@ -1345,7 +1356,8 @@ namespace cryptonote
return false;
}
- if(!m_core.handle_block_found(b))
+ block_verification_context bvc;
+ if(!m_core.handle_block_found(b, bvc))
{
error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED;
error_resp.message = "Block not accepted";
@@ -1377,15 +1389,17 @@ namespace cryptonote
template_req.reserve_size = 1;
template_req.wallet_address = req.wallet_address;
+ template_req.prev_block = req.prev_block;
submit_req.push_back(boost::value_initialized<std::string>());
res.height = m_core.get_blockchain_storage().get_current_blockchain_height();
- bool r;
+ bool r = CORE_RPC_STATUS_OK;
for(size_t i = 0; i < req.amount_of_blocks; i++)
{
r = on_getblocktemplate(template_req, template_res, error_resp, ctx);
res.status = template_res.status;
+ template_req.prev_block.clear();
if (!r) return false;
@@ -1403,6 +1417,7 @@ namespace cryptonote
error_resp.message = "Wrong block blob";
return false;
}
+ b.nonce = req.starting_nonce;
miner::find_nonce_for_given_block(b, template_res.difficulty, template_res.height);
submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b));
@@ -1411,6 +1426,8 @@ namespace cryptonote
if (!r) return false;
+ res.blocks.push_back(epee::string_tools::pod_to_hex(get_block_hash(b)));
+ template_req.prev_block = res.blocks.back();
res.height = template_res.height;
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 7811db979..d2aba8d67 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -530,9 +530,11 @@ namespace cryptonote
struct request_t
{
std::vector<get_outputs_out> outputs;
+ bool get_txid;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(outputs)
+ KV_SERIALIZE(get_txid)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -904,10 +906,12 @@ namespace cryptonote
{
uint64_t reserve_size; //max 255 bytes
std::string wallet_address;
+ std::string prev_block;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(reserve_size)
KV_SERIALIZE(wallet_address)
+ KV_SERIALIZE(prev_block)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -964,10 +968,14 @@ namespace cryptonote
{
uint64_t amount_of_blocks;
std::string wallet_address;
+ std::string prev_block;
+ uint32_t starting_nonce;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount_of_blocks)
KV_SERIALIZE(wallet_address)
+ KV_SERIALIZE(prev_block)
+ KV_SERIALIZE_OPT(starting_nonce, (uint32_t)0)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -975,10 +983,12 @@ namespace cryptonote
struct response_t
{
uint64_t height;
+ std::vector<std::string> blocks;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(height)
+ KV_SERIALIZE(blocks)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 838729e6c..4692f7d59 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -235,6 +235,7 @@ namespace
const char* USAGE_MMS_AUTO_CONFIG("mms auto_config <auto_config_token>");
const char* USAGE_PRINT_RING("print_ring <key_image> | <txid>");
const char* USAGE_SET_RING("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )");
+ const char* USAGE_UNSET_RING("unset_ring <txid> | ( <key_image> [<key_image>...] )");
const char* USAGE_SAVE_KNOWN_RINGS("save_known_rings");
const char* USAGE_MARK_OUTPUT_SPENT("mark_output_spent <amount>/<offset> | <filename> [add]");
const char* USAGE_MARK_OUTPUT_UNSPENT("mark_output_unspent <amount>/<offset>");
@@ -1871,6 +1872,38 @@ bool simple_wallet::set_ring(const std::vector<std::string> &args)
return true;
}
+bool simple_wallet::unset_ring(const std::vector<std::string> &args)
+{
+ crypto::hash txid;
+ std::vector<crypto::key_image> key_images;
+
+ if (args.size() < 1)
+ {
+ PRINT_USAGE(USAGE_UNSET_RING);
+ return true;
+ }
+
+ key_images.resize(args.size());
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ if (!epee::string_tools::hex_to_pod(args[i], key_images[i]))
+ {
+ fail_msg_writer() << tr("Invalid key image or txid");
+ return true;
+ }
+ }
+ static_assert(sizeof(crypto::hash) == sizeof(crypto::key_image), "hash and key_image must have the same size");
+ memcpy(&txid, &key_images[0], sizeof(txid));
+
+ if (!m_wallet->unset_ring(key_images) && !m_wallet->unset_ring(txid))
+ {
+ fail_msg_writer() << tr("failed to unset ring");
+ return true;
+ }
+
+ return true;
+}
+
bool simple_wallet::blackball(const std::vector<std::string> &args)
{
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
@@ -3102,6 +3135,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::set_ring, this, _1),
tr(USAGE_SET_RING),
tr("Set the ring used for a given key image, so it can be reused in a fork"));
+ m_cmd_binder.set_handler("unset_ring",
+ boost::bind(&simple_wallet::unset_ring, this, _1),
+ tr(USAGE_UNSET_RING),
+ tr("Unsets the ring used for a given key image or transaction"));
m_cmd_binder.set_handler("save_known_rings",
boost::bind(&simple_wallet::save_known_rings, this, _1),
tr(USAGE_SAVE_KNOWN_RINGS),
@@ -5462,7 +5499,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
}
const cryptonote::tx_source_entry& source = *sptr;
- ostr << boost::format(tr("\nInput %llu/%llu: amount=%s")) % (i + 1) % tx.vin.size() % print_money(source.amount);
+ ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount);
// convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount
std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets);
// get block heights from which those ring member keys originated
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index c6328fbc7..0cbb33cf9 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -235,6 +235,7 @@ namespace cryptonote
bool mms(const std::vector<std::string>& args);
bool print_ring(const std::vector<std::string>& args);
bool set_ring(const std::vector<std::string>& args);
+ bool unset_ring(const std::vector<std::string>& args);
bool save_known_rings(const std::vector<std::string>& args);
bool blackball(const std::vector<std::string>& args);
bool unblackball(const std::vector<std::string>& args);
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index b69022af4..8da95de7b 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -281,7 +281,7 @@ bool ringdb::add_rings(const crypto::chacha_key &chacha_key, const cryptonote::t
return true;
}
-bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
+bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images)
{
MDB_txn *txn;
int dbr;
@@ -294,17 +294,10 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
- for (const auto &in: tx.vin)
+ for (const crypto::key_image &key_image: key_images)
{
- if (in.type() != typeid(cryptonote::txin_to_key))
- continue;
- const auto &txin = boost::get<cryptonote::txin_to_key>(in);
- const uint32_t ring_size = txin.key_offsets.size();
- if (ring_size == 1)
- continue;
-
MDB_val key, data;
- std::string key_ciphertext = encrypt(txin.k_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
@@ -314,7 +307,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
continue;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
- MDEBUG("Removing ring data for key image " << txin.k_image);
+ MDEBUG("Removing ring data for key image " << key_image);
dbr = mdb_del(txn, dbi_rings, &key, NULL);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to remove ring to database: " + std::string(mdb_strerror(dbr)));
}
@@ -325,6 +318,23 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
return true;
}
+bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
+{
+ std::vector<crypto::key_image> key_images;
+ key_images.reserve(tx.vin.size());
+ for (const auto &in: tx.vin)
+ {
+ if (in.type() != typeid(cryptonote::txin_to_key))
+ continue;
+ const auto &txin = boost::get<cryptonote::txin_to_key>(in);
+ const uint32_t ring_size = txin.key_offsets.size();
+ if (ring_size == 1)
+ continue;
+ key_images.push_back(txin.k_image);
+ }
+ return remove_rings(chacha_key, key_images);
+}
+
bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{
MDB_txn *txn;
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index 7b448b0d7..9c7e624bc 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -45,6 +45,7 @@ namespace tools
~ringdb();
bool add_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
+ bool remove_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images);
bool remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 39fe4608c..dfcc61426 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -7094,6 +7094,43 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
catch (const std::exception &e) { return false; }
}
+bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images)
+{
+ if (!m_ringdb)
+ return false;
+
+ try { return m_ringdb->remove_rings(get_ringdb_key(), key_images); }
+ catch (const std::exception &e) { return false; }
+}
+
+bool wallet2::unset_ring(const crypto::hash &txid)
+{
+ if (!m_ringdb)
+ return false;
+
+ COMMAND_RPC_GET_TRANSACTIONS::request req;
+ COMMAND_RPC_GET_TRANSACTIONS::response res;
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ req.decode_as_json = false;
+ req.prune = true;
+ m_daemon_rpc_mutex.lock();
+ bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon");
+ if (res.txs.empty())
+ return false;
+ THROW_WALLET_EXCEPTION_IF(res.txs.size(), error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ cryptonote::transaction tx;
+ crypto::hash tx_hash;
+ if (!get_pruned_tx(res.txs.front(), tx, tx_hash))
+ return false;
+ THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, "Failed to get the right transaction from daemon");
+
+ try { return m_ringdb->remove_rings(get_ringdb_key(), tx); }
+ catch (const std::exception &e) { return false; }
+}
+
bool wallet2::find_and_save_rings(bool force)
{
if (!force && m_ring_history_saved)
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index f4b1233e0..c8ac7d429 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1251,6 +1251,8 @@ namespace tools
bool get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs);
bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
+ bool unset_ring(const std::vector<crypto::key_image> &key_images);
+ bool unset_ring(const crypto::hash &txid);
bool find_and_save_rings(bool force = true);
bool blackball_output(const std::pair<uint64_t, uint64_t> &output);