aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blocks/checkpoints.datbin272772 -> 332676 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp1
-rw-r--r--src/cryptonote_config.h2
-rw-r--r--src/cryptonote_core/blockchain.cpp2
-rw-r--r--src/hardforks/hardforks.cpp6
-rw-r--r--src/multisig/multisig_tx_builder_ringct.cpp142
-rw-r--r--src/multisig/multisig_tx_builder_ringct.h1
-rw-r--r--src/version.cpp.in4
-rw-r--r--src/wallet/wallet2.cpp27
-rw-r--r--src/wallet/wallet2.h15
10 files changed, 171 insertions, 29 deletions
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index e75e379f2..2ed1d630f 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 27e77cae8..330e3653c 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -239,6 +239,7 @@ namespace cryptonote
ADD_CHECKPOINT2(2046000, "5e867f0b8baefed9244a681df97fc885d8ab36c3dfcd24c7a3abf3b8ac8b8314", "0x9cb8b6ff2978c6");
ADD_CHECKPOINT2(2092500, "c4e00820c9c7989b49153d5e90ae095a18a11d990e82fcc3be54e6ed785472b5", "0xb4e585a31369cb");
ADD_CHECKPOINT2(2182500, "0d22b5f81982eff21d094af9e821dc2007e6342069e3b1a37b15d97646353124", "0xead4a874083492");
+ ADD_CHECKPOINT2(2661600, "41c9060e8426012238e8a26da26fcb90797436896cc70886a894c2c560bcccf2", "0x2e0d87526ff161f");
return true;
}
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 962346017..2ec194ef8 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -246,6 +246,8 @@ namespace config
const unsigned char HASH_KEY_CLSAG_AGG_1[] = "CLSAG_agg_1";
const char HASH_KEY_MESSAGE_SIGNING[] = "MoneroMessageSignature";
const unsigned char HASH_KEY_MM_SLOT = 'm';
+ const constexpr char HASH_KEY_MULTISIG_TX_PRIVKEYS_SEED[] = "multisig_tx_privkeys_seed";
+ const constexpr char HASH_KEY_MULTISIG_TX_PRIVKEYS[] = "multisig_tx_privkeys";
// Multisig
const uint32_t MULTISIG_MAX_SIGNERS{16};
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 5b7b4353d..c37dfe9e7 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -5604,7 +5604,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "8da80ca560793f252d1d4ed449c85d75c74867f3f86b8832c8e3f88b1cbb6ae3";
+static const char expected_block_hashes_hash[] = "e9371004b9f6be59921b27bc81e28b4715845ade1c6d16891d5c455f72e21365";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp
index 0dd0cf5f0..336ef6519 100644
--- a/src/hardforks/hardforks.cpp
+++ b/src/hardforks/hardforks.cpp
@@ -71,8 +71,8 @@ const hardfork_t mainnet_hard_forks[] = {
{ 13, 2210000, 0, 1598180817 },
{ 14, 2210720, 0, 1598180818 },
- { 15, 8000000, 0, 1608223241 }, // temp so tests test with these consensus rules
- { 16, 8000001, 0, 1608223242 }, // temp so tests test with these consensus rules
+ { 15, 2688888, 0, 1656629117 },
+ { 16, 2689608, 0, 1656629118 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = 1009826;
@@ -122,5 +122,7 @@ const hardfork_t stagenet_hard_forks[] = {
{ 12, 454721, 0, 1571419280 },
{ 13, 675405, 0, 1598180817 },
{ 14, 676125, 0, 1598180818 },
+ { 15, 1151000, 0, 1656629117 },
+ { 16, 1151720, 0, 1656629118 },
};
const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]);
diff --git a/src/multisig/multisig_tx_builder_ringct.cpp b/src/multisig/multisig_tx_builder_ringct.cpp
index cbc556b71..e5c9ac483 100644
--- a/src/multisig/multisig_tx_builder_ringct.cpp
+++ b/src/multisig/multisig_tx_builder_ringct.cpp
@@ -34,6 +34,7 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_config.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "device/device.hpp"
#include "multisig_clsag_context.h"
@@ -47,6 +48,7 @@
#include <cstring>
#include <limits>
#include <set>
+#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@@ -242,6 +244,80 @@ static bool set_tx_extra(
}
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
+static void make_tx_secret_key_seed(const crypto::secret_key& tx_secret_key_entropy,
+ const std::vector<cryptonote::tx_source_entry>& sources,
+ crypto::secret_key& tx_secret_key_seed)
+{
+ // seed = H(H("domain separator"), entropy, {KI})
+ static const std::string domain_separator{config::HASH_KEY_MULTISIG_TX_PRIVKEYS_SEED};
+
+ rct::keyV hash_context;
+ hash_context.reserve(2 + sources.size());
+ auto hash_context_wiper = epee::misc_utils::create_scope_leave_handler([&]{
+ memwipe(hash_context.data(), hash_context.size());
+ });
+ hash_context.emplace_back();
+ rct::cn_fast_hash(hash_context.back(), domain_separator.data(), domain_separator.size()); //domain sep
+ hash_context.emplace_back(rct::sk2rct(tx_secret_key_entropy)); //entropy
+
+ for (const cryptonote::tx_source_entry& source : sources)
+ hash_context.emplace_back(source.multisig_kLRki.ki); //{KI}
+
+ // set the seed
+ tx_secret_key_seed = rct::rct2sk(rct::cn_fast_hash(hash_context));
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+static void make_tx_secret_keys(const crypto::secret_key& tx_secret_key_seed,
+ const std::size_t num_tx_keys,
+ std::vector<crypto::secret_key>& tx_secret_keys)
+{
+ // make tx secret keys as a hash chain of the seed
+ // h1 = H_n(seed || H("domain separator"))
+ // h2 = H_n(seed || h1)
+ // h3 = H_n(seed || h2)
+ // ...
+ static const std::string domain_separator{config::HASH_KEY_MULTISIG_TX_PRIVKEYS};
+
+ rct::keyV hash_context;
+ hash_context.resize(2);
+ auto hash_context_wiper = epee::misc_utils::create_scope_leave_handler([&]{
+ memwipe(hash_context.data(), hash_context.size());
+ });
+ hash_context[0] = rct::sk2rct(tx_secret_key_seed);
+ rct::cn_fast_hash(hash_context[1], domain_separator.data(), domain_separator.size());
+
+ tx_secret_keys.clear();
+ tx_secret_keys.resize(num_tx_keys);
+
+ for (crypto::secret_key& tx_secret_key : tx_secret_keys)
+ {
+ // advance the hash chain
+ hash_context[1] = rct::hash_to_scalar(hash_context);
+
+ // set this key
+ tx_secret_key = rct::rct2sk(hash_context[1]);
+ }
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
+static bool collect_tx_secret_keys(const std::vector<crypto::secret_key>& tx_secret_keys,
+ crypto::secret_key& tx_secret_key,
+ std::vector<crypto::secret_key>& tx_aux_secret_keys)
+{
+ if (tx_secret_keys.size() == 0)
+ return false;
+
+ tx_secret_key = tx_secret_keys[0];
+ tx_aux_secret_keys.clear();
+ tx_aux_secret_keys.reserve(tx_secret_keys.size() - 1);
+ for (std::size_t tx_key_index{1}; tx_key_index < tx_secret_keys.size(); ++tx_key_index)
+ tx_aux_secret_keys.emplace_back(tx_secret_keys[tx_key_index]);
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------------
static bool compute_keys_for_destinations(
const cryptonote::account_keys& account_keys,
const std::uint32_t subaddr_account,
@@ -250,6 +326,7 @@ static bool compute_keys_for_destinations(
const std::vector<std::uint8_t>& extra,
const bool use_view_tags,
const bool reconstruction,
+ const crypto::secret_key& tx_secret_key_seed,
crypto::secret_key& tx_secret_key,
std::vector<crypto::secret_key>& tx_aux_secret_keys,
rct::keyV& output_public_keys,
@@ -288,8 +365,35 @@ static bool compute_keys_for_destinations(
unique_std_recipients.insert(dst_entr.addr);
}
- if (not reconstruction) {
- tx_secret_key = rct::rct2sk(rct::skGen());
+ // figure out how many tx secret keys are needed
+ // - tx aux keys: add if there are > 1 non-change recipients, with at least one to a subaddress
+ const std::size_t num_destinations = destinations.size();
+ const bool need_tx_aux_keys = unique_subbaddr_recipients.size() + bool(unique_std_recipients.size()) > 1;
+
+ const std::size_t num_tx_keys = 1 + (need_tx_aux_keys ? num_destinations : 0);
+
+ // make tx secret keys
+ std::vector<crypto::secret_key> all_tx_secret_keys;
+ make_tx_secret_keys(tx_secret_key_seed, num_tx_keys, all_tx_secret_keys);
+
+ // split up tx secret keys
+ crypto::secret_key tx_secret_key_temp;
+ std::vector<crypto::secret_key> tx_aux_secret_keys_temp;
+ if (not collect_tx_secret_keys(all_tx_secret_keys, tx_secret_key_temp, tx_aux_secret_keys_temp))
+ return false;
+
+ if (reconstruction)
+ {
+ // when reconstructing, the tx secret keys should be reproducible from input seed
+ if (!(tx_secret_key == tx_secret_key_temp))
+ return false;
+ if (!(tx_aux_secret_keys == tx_aux_secret_keys_temp))
+ return false;
+ }
+ else
+ {
+ tx_secret_key = tx_secret_key_temp;
+ tx_aux_secret_keys = std::move(tx_aux_secret_keys_temp);
}
// tx pub key: R
@@ -312,17 +416,6 @@ static bool compute_keys_for_destinations(
}
// additional tx pubkeys: R_t
- // - add if there are > 1 non-change recipients, with at least one to a subaddress
- const std::size_t num_destinations = destinations.size();
-
- const bool need_tx_aux_keys = unique_subbaddr_recipients.size() + bool(unique_std_recipients.size()) > 1;
- if (not reconstruction and need_tx_aux_keys) {
- tx_aux_secret_keys.clear();
- tx_aux_secret_keys.reserve(num_destinations);
- for(std::size_t i = 0; i < num_destinations; ++i)
- tx_aux_secret_keys.push_back(rct::rct2sk(rct::skGen()));
- }
-
output_public_keys.resize(num_destinations);
view_tags.resize(num_destinations);
std::vector<crypto::public_key> tx_aux_public_keys;
@@ -738,6 +831,7 @@ bool tx_builder_ringct_t::init(
const bool reconstruction,
crypto::secret_key& tx_secret_key,
std::vector<crypto::secret_key>& tx_aux_secret_keys,
+ crypto::secret_key& tx_secret_key_entropy,
cryptonote::transaction& unsigned_tx
)
{
@@ -765,6 +859,23 @@ bool tx_builder_ringct_t::init(
// sort inputs
sort_sources(sources);
+ // prepare tx secret key seed (must be AFTER sorting sources)
+ // - deriving the seed from sources plus entropy ensures uniqueness for every new tx attempt
+ // - the goal is that two multisig txs added to the chain will never have outputs with the same onetime addresses,
+ // which would burn funds (embedding the inputs' key images guarantees this)
+ // - it is acceptable if two tx attempts use the same input set and entropy (only a malicious tx proposer will do
+ // that, but all it can accomplish is leaking information about the recipients - which a malicious proposer can
+ // easily do outside the signing ritual anyway)
+ if (not reconstruction)
+ tx_secret_key_entropy = rct::rct2sk(rct::skGen());
+
+ // expect not null (note: wallet serialization code may set this to null if handling an old partial tx)
+ if (tx_secret_key_entropy == crypto::null_skey)
+ return false;
+
+ crypto::secret_key tx_secret_key_seed;
+ make_tx_secret_key_seed(tx_secret_key_entropy, sources, tx_secret_key_seed);
+
// get secret keys for signing input CLSAGs (multisig: or for the initial partial signature)
rct::keyV input_secret_keys;
auto input_secret_keys_wiper = epee::misc_utils::create_scope_leave_handler([&]{
@@ -791,6 +902,7 @@ bool tx_builder_ringct_t::init(
extra,
use_view_tags,
reconstruction,
+ tx_secret_key_seed,
tx_secret_key,
tx_aux_secret_keys,
output_public_keys,
@@ -921,6 +1033,7 @@ bool tx_builder_ringct_t::finalize_tx(
cryptonote::transaction& unsigned_tx
)
{
+ // checks
const std::size_t num_sources = sources.size();
if (num_sources != unsigned_tx.rct_signatures.p.CLSAGs.size())
return false;
@@ -928,6 +1041,8 @@ bool tx_builder_ringct_t::finalize_tx(
return false;
if (num_sources != s.size())
return false;
+
+ // finalize tx signatures
for (std::size_t i = 0; i < num_sources; ++i) {
const std::size_t ring_size = unsigned_tx.rct_signatures.p.CLSAGs[i].s.size();
if (sources[i].real_output >= ring_size)
@@ -935,6 +1050,7 @@ bool tx_builder_ringct_t::finalize_tx(
unsigned_tx.rct_signatures.p.CLSAGs[i].s[sources[i].real_output] = s[i];
unsigned_tx.rct_signatures.p.CLSAGs[i].c1 = c_0[i];
}
+
return true;
}
//----------------------------------------------------------------------------------------------------------------------
diff --git a/src/multisig/multisig_tx_builder_ringct.h b/src/multisig/multisig_tx_builder_ringct.h
index 67ef9e065..853934659 100644
--- a/src/multisig/multisig_tx_builder_ringct.h
+++ b/src/multisig/multisig_tx_builder_ringct.h
@@ -82,6 +82,7 @@ public:
const bool reconstruction,
crypto::secret_key& tx_secret_key,
std::vector<crypto::secret_key>& tx_aux_secret_keys,
+ crypto::secret_key& tx_secret_key_entropy,
cryptonote::transaction& unsigned_tx
);
diff --git a/src/version.cpp.in b/src/version.cpp.in
index 9f6ffd97b..c6d473bf9 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,6 +1,6 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.17.0.0"
-#define DEF_MONERO_RELEASE_NAME "Oxygen Orion"
+#define DEF_MONERO_VERSION "0.18.0.0"
+#define DEF_MONERO_RELEASE_NAME "Fluorine Fermi"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index ed153d681..195763949 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -7157,6 +7157,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
true, //true = we are reconstructing the tx (it was first constructed by the tx proposer)
ptx.tx_key,
ptx.additional_tx_keys,
+ ptx.multisig_tx_key_entropy,
ptx.tx
),
error::wallet_internal_error,
@@ -8063,8 +8064,13 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
has_rct = true;
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
}
- const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets));
- if (has_rct_distribution)
+
+ if (has_rct && rct_offsets.empty()) {
+ THROW_WALLET_EXCEPTION_IF(!get_rct_distribution(rct_start_height, rct_offsets),
+ error::get_output_distribution, "Could not obtain output distribution.");
+ }
+
+ if (has_rct)
{
// check we're clear enough of rct start, to avoid corner cases below
THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE,
@@ -8076,11 +8082,11 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
- // request histogram for all outputs, except 0 if we have the rct distribution
+ // request histogram for all pre-rct outputs
req_t.amounts.reserve(selected_transfers.size());
for(size_t idx: selected_transfers)
- if (!m_transfers[idx].is_rct() || !has_rct_distribution)
- req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
+ if (!m_transfers[idx].is_rct())
+ req_t.amounts.push_back(m_transfers[idx].amount());
if (!req_t.amounts.empty())
{
std::sort(req_t.amounts.begin(), req_t.amounts.end());
@@ -8180,7 +8186,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
std::unique_ptr<gamma_picker> gamma;
- if (has_rct_distribution)
+ if (has_rct)
gamma.reset(new gamma_picker(rct_offsets));
size_t num_selected_transfers = 0;
@@ -8195,7 +8201,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// request more for rct in base recent (locked) coinbases are picked, since they're locked for longer
size_t requested_outputs_count = base_requested_outputs_count + (td.is_rct() ? CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE : 0);
size_t start = req.outputs.size();
- bool use_histogram = amount != 0 || !has_rct_distribution;
+ bool use_histogram = amount != 0;
const bool output_is_pre_fork = td.m_block_height < segregation_fork_height;
uint64_t num_outs = 0, num_recent_outs = 0;
@@ -8382,7 +8388,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t i;
const char *type = "";
- if (amount == 0 && has_rct_distribution)
+ if (amount == 0)
{
THROW_WALLET_EXCEPTION_IF(!gamma, error::wallet_internal_error, "No gamma picker");
// gamma distribution
@@ -8544,7 +8550,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
break;
}
}
- bool use_histogram = amount != 0 || !has_rct_distribution;
+ bool use_histogram = amount != 0;
if (!use_histogram)
num_outs = rct_offsets[rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE];
@@ -9006,6 +9012,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
+ crypto::secret_key multisig_tx_key_entropy;
LOG_PRINT_L2("constructing tx");
auto sources_copy = sources;
multisig::signing::tx_builder_ringct_t multisig_tx_builder;
@@ -9029,6 +9036,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
false,
tx_key,
additional_tx_keys,
+ multisig_tx_key_entropy,
tx
),
error::wallet_internal_error,
@@ -9155,6 +9163,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
ptx.additional_tx_keys = additional_tx_keys;
ptx.dests = dsts;
ptx.multisig_sigs = multisig_sigs;
+ ptx.multisig_tx_key_entropy = multisig_tx_key_entropy;
ptx.construction_data.sources = sources_copy;
ptx.construction_data.change_dts = change_dts;
ptx.construction_data.splitted_dsts = splitted_dsts;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 836373939..16e898ad8 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -494,6 +494,7 @@ private:
struct confirmed_transfer_details
{
+ cryptonote::transaction_prefix m_tx;
uint64_t m_amount_in;
uint64_t m_amount_out;
uint64_t m_change;
@@ -508,10 +509,12 @@ private:
confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(crypto::null_hash), m_timestamp(0), m_unlock_time(0), m_subaddr_account((uint32_t)-1) {}
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
- m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time), m_subaddr_account(utd.m_subaddr_account), m_subaddr_indices(utd.m_subaddr_indices), m_rings(utd.m_rings) {}
+ m_tx(utd.m_tx), m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time), m_subaddr_account(utd.m_subaddr_account), m_subaddr_indices(utd.m_subaddr_indices), m_rings(utd.m_rings) {}
BEGIN_SERIALIZE_OBJECT()
- VERSION_FIELD(0)
+ VERSION_FIELD(1)
+ if (version >= 1)
+ FIELD(m_tx)
VARINT_FIELD(m_amount_in)
VARINT_FIELD(m_amount_out)
VARINT_FIELD(m_change)
@@ -632,10 +635,12 @@ private:
std::vector<crypto::secret_key> additional_tx_keys;
std::vector<cryptonote::tx_destination_entry> dests;
std::vector<multisig_sig> multisig_sigs;
+ crypto::secret_key multisig_tx_key_entropy;
tx_construction_data construction_data;
BEGIN_SERIALIZE_OBJECT()
+ VERSION_FIELD(1)
FIELD(tx)
FIELD(dust)
FIELD(fee)
@@ -648,6 +653,12 @@ private:
FIELD(dests)
FIELD(construction_data)
FIELD(multisig_sigs)
+ if (version < 1)
+ {
+ multisig_tx_key_entropy = crypto::null_skey;
+ return true;
+ }
+ FIELD(multisig_tx_key_entropy)
END_SERIALIZE()
};