aboutsummaryrefslogtreecommitdiff
path: root/src/cryptonote_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/cryptonote_core')
-rw-r--r--src/cryptonote_core/blockchain.cpp155
-rw-r--r--src/cryptonote_core/blockchain.h9
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp18
-rw-r--r--src/cryptonote_core/cryptonote_core.h7
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp103
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h20
6 files changed, 285 insertions, 27 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 247387efe..d91589765 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -2302,6 +2302,24 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u
return true;
}
//------------------------------------------------------------------
+void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx)
+{
+#if defined(PER_BLOCK_CHECKPOINT)
+ // check if we're doing per-block checkpointing
+ if (m_db->height() < m_blocks_hash_check.size())
+ {
+ TIME_MEASURE_START(a);
+ m_blocks_txs_check.push_back(get_transaction_hash(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;
+ MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
+ }
+ }
+#endif
+}
+//------------------------------------------------------------------
//FIXME: it seems this function is meant to be merely a wrapper around
// another function of the same name, this one adding one bit of
// functionality. Should probably move anything more than that
@@ -2317,19 +2335,10 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
#if defined(PER_BLOCK_CHECKPOINT)
// check if we're doing per-block checkpointing
- // FIXME: investigate why this block returns
if (m_db->height() < m_blocks_hash_check.size() && kept_by_block)
{
- TIME_MEASURE_START(a);
- m_blocks_txs_check.push_back(get_transaction_hash(tx));
max_used_block_id = null_hash;
max_used_block_height = 0;
- 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;
- MINFO("HASH: " << "-" << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << 0 << " chcktx: " << a);
- }
return true;
}
#endif
@@ -3234,13 +3243,21 @@ leave:
if (m_db->height() < m_blocks_hash_check.size())
{
auto hash = get_block_hash(bl);
- if (memcmp(&hash, &m_blocks_hash_check[m_db->height()], sizeof(hash)) != 0)
+ const auto &expected_hash = m_blocks_hash_check[m_db->height()];
+ if (expected_hash != cryptonote::null_hash)
{
- MERROR_VER("Block with id is INVALID: " << id);
- bvc.m_verifivation_failed = true;
- goto leave;
+ if (memcmp(&hash, &expected_hash, sizeof(hash)) != 0)
+ {
+ MERROR_VER("Block with id is INVALID: " << id);
+ bvc.m_verifivation_failed = true;
+ goto leave;
+ }
+ fast_check = true;
+ }
+ else
+ {
+ MCINFO("verify", "No pre-validated hash at height " << m_db->height() << ", verifying fully");
}
- fast_check = true;
}
else
#endif
@@ -3685,6 +3702,14 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
m_blocks_txs_check.clear();
m_check_txin_table.clear();
+ // when we're well clear of the precomputed hashes, free the memory
+ if (!m_blocks_hash_check.empty() && m_db->height() > m_blocks_hash_check.size() + 4096)
+ {
+ MINFO("Dumping block hashes, we're now 4k past " << m_blocks_hash_check.size());
+ m_blocks_hash_check.clear();
+ m_blocks_hash_check.shrink_to_fit();
+ }
+
CRITICAL_REGION_END();
m_tx_pool.unlock();
@@ -3709,6 +3734,98 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin
}
}
+uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes)
+{
+ // new: . . . . . X X X X X . . . . . .
+ // pre: A A A A B B B B C C C C D D D D
+
+ // easy case: height >= hashes
+ if (height >= m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP)
+ return hashes.size();
+
+ // find hashes encompassing those block
+ size_t first_index = height / HASH_OF_HASHES_STEP;
+ size_t last_index = (height + hashes.size() - 1) / HASH_OF_HASHES_STEP;
+ MDEBUG("Blocks " << height << " - " << (height + hashes.size() - 1) << " start at " << first_index << " and end at " << last_index);
+
+ // case of not enough to calculate even a single hash
+ if (first_index == last_index && hashes.size() < HASH_OF_HASHES_STEP && (height + hashes.size()) % HASH_OF_HASHES_STEP)
+ return hashes.size();
+
+ // build hashes vector to hash hashes together
+ std::vector<crypto::hash> data;
+ data.reserve(hashes.size() + HASH_OF_HASHES_STEP - 1); // may be a bit too much
+
+ // we expect height to be either equal or a bit below db height
+ bool disconnected = (height > m_db->height());
+ size_t pop;
+ if (disconnected && height % HASH_OF_HASHES_STEP)
+ {
+ ++first_index;
+ pop = HASH_OF_HASHES_STEP - height % HASH_OF_HASHES_STEP;
+ }
+ else
+ {
+ // we might need some already in the chain for the first part of the first hash
+ for (uint64_t h = first_index * HASH_OF_HASHES_STEP; h < height; ++h)
+ {
+ data.push_back(m_db->get_block_hash_from_height(h));
+ }
+ pop = 0;
+ }
+
+ // push the data to check
+ for (const auto &h: hashes)
+ {
+ if (pop)
+ --pop;
+ else
+ data.push_back(h);
+ }
+
+ // hash and check
+ uint64_t usable = first_index * HASH_OF_HASHES_STEP - height; // may start negative, but unsigned under/overflow is not UB
+ for (size_t n = first_index; n <= last_index; ++n)
+ {
+ if (n < m_blocks_hash_of_hashes.size())
+ {
+ // if the last index isn't fully filled, we can't tell if valid
+ if (data.size() < (n - first_index) * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP)
+ break;
+
+ crypto::hash hash;
+ cn_fast_hash(data.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash);
+ bool valid = hash == m_blocks_hash_of_hashes[n];
+
+ // add to the known hashes array
+ if (!valid)
+ {
+ MWARNING("invalid hash for blocks " << n * HASH_OF_HASHES_STEP << " - " << (n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP - 1));
+ break;
+ }
+
+ size_t end = n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP;
+ for (size_t i = n * HASH_OF_HASHES_STEP; i < end; ++i)
+ {
+ CHECK_AND_ASSERT_MES(m_blocks_hash_check[i] == cryptonote::null_hash || m_blocks_hash_check[i] == data[i - first_index * HASH_OF_HASHES_STEP],
+ 0, "Consistency failure in m_blocks_hash_check construction");
+ m_blocks_hash_check[i] = data[i - first_index * HASH_OF_HASHES_STEP];
+ }
+ usable += HASH_OF_HASHES_STEP;
+ }
+ else
+ {
+ // if after the end of the precomputed blocks, accept anything
+ usable += HASH_OF_HASHES_STEP;
+ if (usable > hashes.size())
+ usable = hashes.size();
+ }
+ }
+ MDEBUG("usable: " << usable << " / " << hashes.size());
+ CHECK_AND_ASSERT_MES(usable < std::numeric_limits<uint64_t>::max() / 2, 0, "usable is negative");
+ return usable;
+}
+
//------------------------------------------------------------------
// ND: Speedups:
// 1. Thread long_hash computations if possible (m_max_prepare_blocks_threads = nthreads, default = 4)
@@ -4181,7 +4298,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "d3ca80d50661684cde0e715d46d7c19704d2e216b21ed088af9fd4ef37ed4d65";
+static const char expected_block_hashes_hash[] = "4b553162ee4e7af3c53666506591489c68560b9175e6e941dc96c89f96f0e35c";
void Blockchain::load_compiled_in_block_hashes()
{
if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0)
@@ -4217,16 +4334,18 @@ void Blockchain::load_compiled_in_block_hashes()
const unsigned char *p = get_blocks_dat_start(m_testnet);
const uint32_t nblocks = *p | ((*(p+1))<<8) | ((*(p+2))<<16) | ((*(p+3))<<24);
const size_t size_needed = 4 + nblocks * sizeof(crypto::hash);
- if(nblocks > 0 && nblocks > m_db->height() && get_blocks_dat_size(m_testnet) >= size_needed)
+ if(nblocks > 0 && nblocks * HASH_OF_HASHES_STEP > m_db->height() && get_blocks_dat_size(m_testnet) >= size_needed)
{
p += sizeof(uint32_t);
+ m_blocks_hash_of_hashes.reserve(nblocks);
for (uint32_t i = 0; i < nblocks; i++)
{
crypto::hash hash;
memcpy(hash.data, p, sizeof(hash.data));
p += sizeof(hash.data);
- m_blocks_hash_check.push_back(hash);
+ m_blocks_hash_of_hashes.push_back(hash);
}
+ m_blocks_hash_check.resize(m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP, cryptonote::null_hash);
MINFO(nblocks << " block hashes loaded");
// FIXME: clear tx_pool because the process might have been
@@ -4257,7 +4376,7 @@ void Blockchain::load_compiled_in_block_hashes()
bool Blockchain::is_within_compiled_block_hash_area(uint64_t height) const
{
#if defined(PER_BLOCK_CHECKPOINT)
- return height < m_blocks_hash_check.size();
+ return height < m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP;
#else
return false;
#endif
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 00b40d0ad..f64bd35e3 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -955,12 +955,20 @@ namespace cryptonote
bool is_within_compiled_block_hash_area(uint64_t height) const;
bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); }
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes);
void lock();
void unlock();
void cancel();
+ /**
+ * @brief called when we see a tx originating from a block
+ *
+ * Used for handling txes from historical blocks in a fast way
+ */
+ void on_new_tx_from_block(const cryptonote::transaction &tx);
+
private:
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
@@ -995,6 +1003,7 @@ namespace cryptonote
std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, bool>> m_check_txin_table;
// SHA-3 hashes for each block and for fast pow checking
+ std::vector<crypto::hash> m_blocks_hash_of_hashes;
std::vector<crypto::hash> m_blocks_hash_check;
std::vector<crypto::hash> m_blocks_txs_check;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index c1a5ae324..61f844612 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -899,6 +899,9 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
+ if (keeped_by_block)
+ get_blockchain_storage().on_new_tx_from_block(tx);
+
if(m_mempool.have_tx(tx_hash))
{
LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool");
@@ -1026,7 +1029,15 @@ namespace cryptonote
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_miner.pause();
std::list<block_complete_entry> blocks;
- blocks.push_back(get_block_complete_entry(b, m_mempool));
+ try
+ {
+ blocks.push_back(get_block_complete_entry(b, m_mempool));
+ }
+ catch (const std::exception &e)
+ {
+ m_miner.resume();
+ return false;
+ }
prepare_handle_incoming_blocks(blocks);
m_blockchain_storage.add_new_block(b, bvc);
cleanup_handle_incoming_blocks(true);
@@ -1399,6 +1410,11 @@ namespace cryptonote
return m_target_blockchain_height;
}
//-----------------------------------------------------------------------------------------------
+ uint64_t core::prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes)
+ {
+ return get_blockchain_storage().prevalidate_block_hashes(height, hashes);
+ }
+ //-----------------------------------------------------------------------------------------------
std::time_t core::get_start_time() const
{
return start_time;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 1aed86b25..7340e1024 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -750,6 +750,13 @@ namespace cryptonote
*/
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
+ /**
+ * @brief check a set of hashes against the precompiled hash set
+ *
+ * @return number of usable blocks
+ */
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::list<crypto::hash> &hashes);
+
private:
/**
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index fd647a356..586df9079 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <unordered_set>
#include "include_base_utils.h"
using namespace epee;
@@ -157,8 +158,14 @@ namespace cryptonote
return destinations[0].addr.m_view_public_key;
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
+ 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 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)
{
+ if (destinations.empty())
+ {
+ LOG_ERROR("The destinations must be non-empty");
+ return false;
+ }
+
std::vector<rct::key> amount_keys;
tx.set_null();
amount_keys.clear();
@@ -237,8 +244,12 @@ namespace cryptonote
in_contexts.push_back(input_generation_context_data());
keypair& in_ephemeral = in_contexts.back().in_ephemeral;
crypto::key_image img;
- if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img))
+ const auto& out_key = reinterpret_cast<const crypto::public_key&>(src_entr.outputs[src_entr.real_output].second.dest);
+ if(!generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral, img))
+ {
+ LOG_ERROR("Key image generation failed!");
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) )
@@ -283,6 +294,47 @@ namespace cryptonote
std::swap(sources[i0], sources[i1]);
});
+ // 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 (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");
+
+ // 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)));
+ remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
+ 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);
+
uint64_t summary_outs_money = 0;
//fill outputs
size_t output_index = 0;
@@ -291,8 +343,35 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
crypto::key_derivation derivation;
crypto::public_key out_eph_public_key;
- bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")");
+
+ // make additional tx pubkey if necessary
+ keypair additional_txkey;
+ if (need_additional_txkeys)
+ {
+ additional_txkey = keypair::generate();
+ 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)));
+ }
+
+ bool r;
+ if (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 << ")");
+ }
+ 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) << ")");
+ }
+
+ 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)
{
@@ -313,6 +392,17 @@ namespace cryptonote
summary_outs_money += dst_entr.amount;
}
+ remove_field_from_tx_extra(tx.extra, typeid(tx_extra_additional_pub_keys));
+ add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
+
+ LOG_PRINT_L2("tx pubkey: " << txkey.pub);
+ if (need_additional_txkeys)
+ {
+ LOG_PRINT_L2("additional tx pubkeys: ");
+ for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
+ LOG_PRINT_L2(additional_tx_public_keys[i]);
+ }
+
//check money
if(summary_outs_money > summary_inputs_money )
{
@@ -477,8 +567,11 @@ namespace cryptonote
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, 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;
- return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key);
+ std::vector<crypto::secret_key> additional_tx_keys;
+ return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations, destinations.back().addr, extra, tx, unlock_time, tx_key, additional_tx_keys);
}
//---------------------------------------------------------------
bool generate_genesis_block(
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index f2cc73545..b5de44b88 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -46,6 +46,7 @@ namespace cryptonote
std::vector<output_entry> outputs; //index + key + optional ringct commitment
size_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //incoming real tx public key
+ std::vector<crypto::public_key> real_out_additional_tx_keys; //incoming real tx additional public keys
size_t real_output_in_tx_index; //index in transaction outputs vector
uint64_t amount; //money
bool rct; //true if the output is rct
@@ -58,20 +59,22 @@ namespace cryptonote
{
uint64_t amount; //money
account_public_address addr; //destination address
+ bool is_subaddress;
- tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { }
- tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { }
+ tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)), is_subaddress(false) { }
+ tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress) { }
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(amount)
FIELD(addr)
+ FIELD(is_subaddress)
END_SERIALIZE()
};
//---------------------------------------------------------------
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, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
+ 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 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 generate_genesis_block(
block& bl
@@ -82,6 +85,7 @@ namespace cryptonote
}
BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0)
+BOOST_CLASS_VERSION(cryptonote::tx_destination_entry, 1)
namespace boost
{
@@ -98,5 +102,15 @@ namespace boost
a & x.rct;
a & x.mask;
}
+
+ template <class Archive>
+ inline void serialize(Archive& a, cryptonote::tx_destination_entry& x, const boost::serialization::version_type ver)
+ {
+ a & x.amount;
+ a & x.addr;
+ if (ver < 1)
+ return;
+ a & x.is_subaddress;
+ }
}
}