New "Halfway RingCT" outputs for coinbase transactions
When RingCT is enabled, outputs from coinbase transactions are created as a single output, and stored as RingCT output, with a fake mask. Their amount is not hidden on the blockchain itself, but they are then able to be used as fake inputs in a RingCT ring. Since the output amounts are hidden, their "dustiness" is not an obstacle anymore to mixing, and this makes the coinbase transactions a lot smaller, as well as helping the TXO set to grow more slowly. Also add a new "Null" type of rct signature, which decreases the size required when no signatures are to be stored, as in a coinbase tx.
This commit is contained in:
parent
6f526cdff8
commit
c3b3260ae5
|
@ -46,6 +46,7 @@ void BlockchainDB::pop_block()
|
||||||
|
|
||||||
void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr)
|
void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr)
|
||||||
{
|
{
|
||||||
|
bool miner_tx = false;
|
||||||
crypto::hash tx_hash;
|
crypto::hash tx_hash;
|
||||||
if (!tx_hash_ptr)
|
if (!tx_hash_ptr)
|
||||||
{
|
{
|
||||||
|
@ -67,6 +68,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
|
||||||
else if (tx_input.type() == typeid(txin_gen))
|
else if (tx_input.type() == typeid(txin_gen))
|
||||||
{
|
{
|
||||||
/* nothing to do here */
|
/* nothing to do here */
|
||||||
|
miner_tx = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -90,8 +92,21 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
|
||||||
// we need the index
|
// we need the index
|
||||||
for (uint64_t i = 0; i < tx.vout.size(); ++i)
|
for (uint64_t i = 0; i < tx.vout.size(); ++i)
|
||||||
{
|
{
|
||||||
amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
|
// miner v2 txes have their coinbase output in one single out to save space,
|
||||||
tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL));
|
// and we store them as rct outputs with an identity mask
|
||||||
|
if (miner_tx && tx.version == 2)
|
||||||
|
{
|
||||||
|
cryptonote::tx_out vout = tx.vout[i];
|
||||||
|
rct::key commitment = rct::zeroCommit(vout.amount);
|
||||||
|
vout.amount = 0;
|
||||||
|
amount_output_indices.push_back(add_output(tx_hash, vout, i, tx.unlock_time,
|
||||||
|
&commitment));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
|
||||||
|
tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
add_tx_amount_output_indices(tx_id, amount_output_indices);
|
add_tx_amount_output_indices(tx_id, amount_output_indices);
|
||||||
}
|
}
|
||||||
|
|
|
@ -959,7 +959,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||||
money_in_use += o.amount;
|
money_in_use += o.amount;
|
||||||
partial_block_reward = false;
|
partial_block_reward = false;
|
||||||
|
|
||||||
if (version >= 3) {
|
if (version == 3) {
|
||||||
for (auto &o: b.miner_tx.vout) {
|
for (auto &o: b.miner_tx.vout) {
|
||||||
if (!is_valid_decomposed_amount(o.amount)) {
|
if (!is_valid_decomposed_amount(o.amount)) {
|
||||||
LOG_PRINT_L1("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount");
|
LOG_PRINT_L1("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount");
|
||||||
|
@ -1128,7 +1128,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
||||||
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
|
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
|
||||||
*/
|
*/
|
||||||
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
|
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
|
||||||
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
|
uint8_t hf_version = m_hardfork->get_current_version();
|
||||||
|
size_t max_outs = hf_version >= 4 ? 1 : 11;
|
||||||
|
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
|
||||||
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
|
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
|
||||||
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
|
||||||
|
@ -1137,7 +1139,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
||||||
#endif
|
#endif
|
||||||
for (size_t try_count = 0; try_count != 10; ++try_count)
|
for (size_t try_count = 0; try_count != 10; ++try_count)
|
||||||
{
|
{
|
||||||
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
|
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
|
||||||
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
|
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
|
||||||
|
@ -2354,17 +2356,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for v3, we force txes with all mixable inputs to be rct
|
|
||||||
if (m_hardfork->get_current_version() >= 4)
|
|
||||||
{
|
|
||||||
if (n_unmixable == 0 && tx.version == 1)
|
|
||||||
{
|
|
||||||
LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " is not rct and does not have unmixable inputs");
|
|
||||||
tvc.m_not_rct = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mixin < 2)
|
if (mixin < 2)
|
||||||
{
|
{
|
||||||
if (n_unmixable == 0)
|
if (n_unmixable == 0)
|
||||||
|
@ -2543,6 +2534,11 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
|
||||||
const rct::rctSig &rv = tx.rct_signatures;
|
const rct::rctSig &rv = tx.rct_signatures;
|
||||||
switch (rv.type)
|
switch (rv.type)
|
||||||
{
|
{
|
||||||
|
case rct::RCTTypeNull: {
|
||||||
|
// we only accept no signatures for coinbase txes
|
||||||
|
LOG_PRINT_L1("Null rct signature on non-coinbase tx");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case rct::RCTTypeSimple: {
|
case rct::RCTTypeSimple: {
|
||||||
// check all this, either recontructed (so should really pass), or not
|
// check all this, either recontructed (so should really pass), or not
|
||||||
{
|
{
|
||||||
|
|
|
@ -233,6 +233,8 @@ namespace cryptonote
|
||||||
FIELD(rct_signatures)
|
FIELD(rct_signatures)
|
||||||
switch (rct_signatures.type)
|
switch (rct_signatures.type)
|
||||||
{
|
{
|
||||||
|
case rct::RCTTypeNull:
|
||||||
|
break;
|
||||||
case rct::RCTTypeSimple:
|
case rct::RCTTypeSimple:
|
||||||
if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size())
|
if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size())
|
||||||
return false;
|
return false;
|
||||||
|
@ -276,6 +278,7 @@ namespace cryptonote
|
||||||
vout.clear();
|
vout.clear();
|
||||||
extra.clear();
|
extra.clear();
|
||||||
signatures.clear();
|
signatures.clear();
|
||||||
|
rct_signatures.type = rct::RCTTypeNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline
|
inline
|
||||||
|
|
|
@ -249,6 +249,8 @@ namespace boost
|
||||||
inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver)
|
inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver)
|
||||||
{
|
{
|
||||||
a & x.type;
|
a & x.type;
|
||||||
|
if (x.type == rct::RCTTypeNull)
|
||||||
|
return;
|
||||||
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
|
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
|
||||||
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
|
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
|
||||||
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
|
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
|
||||||
|
@ -264,6 +266,8 @@ namespace boost
|
||||||
inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver)
|
inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver)
|
||||||
{
|
{
|
||||||
a & x.type;
|
a & x.type;
|
||||||
|
if (x.type == rct::RCTTypeNull)
|
||||||
|
return;
|
||||||
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
|
if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
|
||||||
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
|
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
|
||||||
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
|
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
|
||||||
|
|
|
@ -136,7 +136,10 @@ namespace cryptonote
|
||||||
// from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
|
// from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
|
||||||
// keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
|
// keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
|
||||||
// emission schedule
|
// emission schedule
|
||||||
if (hard_fork_version >= 2) {
|
// from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
|
||||||
|
// and avoids the quantization. These outputs will be added as rct outputs with identity
|
||||||
|
// masks, to they can be used as rct inputs.
|
||||||
|
if (hard_fork_version >= 2 && hard_fork_version < 4) {
|
||||||
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
|
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +149,16 @@ namespace cryptonote
|
||||||
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
|
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
|
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
|
||||||
if (height == 0)
|
if (height == 0 || hard_fork_version >= 4)
|
||||||
{
|
{
|
||||||
// the genesis block was not decomposed, for unknown reasons
|
// the genesis block was not decomposed, for unknown reasons
|
||||||
while (max_outs < out_amounts.size())
|
while (max_outs < out_amounts.size())
|
||||||
{
|
{
|
||||||
out_amounts[out_amounts.size() - 2] += out_amounts.back();
|
//out_amounts[out_amounts.size() - 2] += out_amounts.back();
|
||||||
|
//out_amounts.resize(out_amounts.size() - 1);
|
||||||
|
out_amounts[1] += out_amounts[0];
|
||||||
|
for (size_t n = 1; n < out_amounts.size(); ++n)
|
||||||
|
out_amounts[n - 1] = out_amounts[n];
|
||||||
out_amounts.resize(out_amounts.size() - 1);
|
out_amounts.resize(out_amounts.size() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +189,11 @@ namespace cryptonote
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
|
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
|
||||||
|
|
||||||
tx.version = 1;
|
if (hard_fork_version >= 4)
|
||||||
|
tx.version = 2;
|
||||||
|
else
|
||||||
|
tx.version = 1;
|
||||||
|
|
||||||
//lock
|
//lock
|
||||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||||
tx.vin.push_back(in);
|
tx.vin.push_back(in);
|
||||||
|
@ -726,7 +737,7 @@ namespace cryptonote
|
||||||
// zero out all amounts to mask rct outputs, real amounts are now encrypted
|
// zero out all amounts to mask rct outputs, real amounts are now encrypted
|
||||||
for (size_t i = 0; i < tx.vin.size(); ++i)
|
for (size_t i = 0; i < tx.vin.size(); ++i)
|
||||||
{
|
{
|
||||||
if (!(sources[i].mask == rct::identity()))
|
if (sources[i].rct)
|
||||||
boost::get<txin_to_key>(tx.vin[i]).amount = 0;
|
boost::get<txin_to_key>(tx.vin[i]).amount = 0;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||||
|
|
|
@ -58,6 +58,7 @@ namespace cryptonote
|
||||||
crypto::public_key real_out_tx_key; //incoming real tx public key
|
crypto::public_key real_out_tx_key; //incoming real tx public key
|
||||||
size_t real_output_in_tx_index; //index in transaction outputs vector
|
size_t real_output_in_tx_index; //index in transaction outputs vector
|
||||||
uint64_t amount; //money
|
uint64_t amount; //money
|
||||||
|
bool rct; //true if the output is rct
|
||||||
rct::key mask; //ringct amount mask
|
rct::key mask; //ringct amount mask
|
||||||
|
|
||||||
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)}))); }
|
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)}))); }
|
||||||
|
|
|
@ -174,8 +174,9 @@ namespace rct {
|
||||||
// outPk contains public keypairs which are destinations (P, C),
|
// outPk contains public keypairs which are destinations (P, C),
|
||||||
// P = address, C = commitment to amount
|
// P = address, C = commitment to amount
|
||||||
enum {
|
enum {
|
||||||
RCTTypeFull = 0,
|
RCTTypeNull = 0,
|
||||||
RCTTypeSimple = 1,
|
RCTTypeFull = 1,
|
||||||
|
RCTTypeSimple = 2,
|
||||||
};
|
};
|
||||||
struct rctSigBase {
|
struct rctSigBase {
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
|
@ -189,6 +190,8 @@ namespace rct {
|
||||||
|
|
||||||
BEGIN_SERIALIZE()
|
BEGIN_SERIALIZE()
|
||||||
FIELD(type)
|
FIELD(type)
|
||||||
|
if (type == RCTTypeNull)
|
||||||
|
return true;
|
||||||
// FIELD(message) - not serialized, it can be reconstructed
|
// FIELD(message) - not serialized, it can be reconstructed
|
||||||
// FIELD(mixRing) - not serialized, it can be reconstructed
|
// FIELD(mixRing) - not serialized, it can be reconstructed
|
||||||
if (type == RCTTypeSimple)
|
if (type == RCTTypeSimple)
|
||||||
|
@ -224,6 +227,8 @@ namespace rct {
|
||||||
|
|
||||||
BEGIN_SERIALIZE_OBJECT()
|
BEGIN_SERIALIZE_OBJECT()
|
||||||
FIELDS(*static_cast<rctSigBase *>(this))
|
FIELDS(*static_cast<rctSigBase *>(this))
|
||||||
|
if (type == RCTTypeNull)
|
||||||
|
return true;
|
||||||
FIELDS(p);
|
FIELDS(p);
|
||||||
END_SERIALIZE()
|
END_SERIALIZE()
|
||||||
};
|
};
|
||||||
|
|
|
@ -489,10 +489,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
|
||||||
{
|
{
|
||||||
td.m_mask = mask[o];
|
td.m_mask = mask[o];
|
||||||
td.m_amount = amount[o];
|
td.m_amount = amount[o];
|
||||||
|
td.m_rct = true;
|
||||||
|
}
|
||||||
|
else if (miner_tx && tx.version == 2)
|
||||||
|
{
|
||||||
|
td.m_mask = rct::identity();
|
||||||
|
td.m_rct = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
td.m_mask = rct::identity();
|
td.m_mask = rct::identity();
|
||||||
|
td.m_rct = false;
|
||||||
}
|
}
|
||||||
set_unspent(td);
|
set_unspent(td);
|
||||||
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
||||||
|
@ -529,10 +536,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
|
||||||
{
|
{
|
||||||
td.m_mask = mask[o];
|
td.m_mask = mask[o];
|
||||||
td.m_amount = amount[o];
|
td.m_amount = amount[o];
|
||||||
|
td.m_rct = true;
|
||||||
|
}
|
||||||
|
else if (miner_tx && tx.version == 2)
|
||||||
|
{
|
||||||
|
td.m_mask = rct::identity();
|
||||||
|
td.m_rct = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
td.m_mask = rct::identity();
|
td.m_mask = rct::identity();
|
||||||
|
td.m_rct = false;
|
||||||
}
|
}
|
||||||
THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki[o], error::wallet_internal_error, "Inconsistent key images");
|
THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki[o], error::wallet_internal_error, "Inconsistent key images");
|
||||||
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
|
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
|
||||||
|
@ -2847,6 +2861,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||||
cryptonote::tx_source_entry& src = sources.back();
|
cryptonote::tx_source_entry& src = sources.back();
|
||||||
transfer_details& td = *it;
|
transfer_details& td = *it;
|
||||||
src.amount = td.amount();
|
src.amount = td.amount();
|
||||||
|
src.rct = td.is_rct();
|
||||||
//paste keys (fake and real)
|
//paste keys (fake and real)
|
||||||
|
|
||||||
for (size_t n = 0; n < fake_outputs_count + 1; ++n)
|
for (size_t n = 0; n < fake_outputs_count + 1; ++n)
|
||||||
|
@ -3037,6 +3052,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||||
cryptonote::tx_source_entry& src = sources.back();
|
cryptonote::tx_source_entry& src = sources.back();
|
||||||
transfer_details& td = *it;
|
transfer_details& td = *it;
|
||||||
src.amount = td.amount();
|
src.amount = td.amount();
|
||||||
|
src.rct = td.is_rct();
|
||||||
//paste mixin transaction
|
//paste mixin transaction
|
||||||
if(it->is_rct())
|
if(it->is_rct())
|
||||||
{
|
{
|
||||||
|
@ -3732,6 +3748,7 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
|
||||||
cryptonote::tx_source_entry& src = sources.back();
|
cryptonote::tx_source_entry& src = sources.back();
|
||||||
transfer_details& td = *it;
|
transfer_details& td = *it;
|
||||||
src.amount = td.amount();
|
src.amount = td.amount();
|
||||||
|
src.rct = td.is_rct();
|
||||||
|
|
||||||
//paste real transaction to the random index
|
//paste real transaction to the random index
|
||||||
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
|
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
|
||||||
|
|
|
@ -108,8 +108,9 @@ namespace tools
|
||||||
crypto::key_image m_key_image; //TODO: key_image stored twice :(
|
crypto::key_image m_key_image; //TODO: key_image stored twice :(
|
||||||
rct::key m_mask;
|
rct::key m_mask;
|
||||||
uint64_t m_amount;
|
uint64_t m_amount;
|
||||||
|
bool m_rct;
|
||||||
|
|
||||||
bool is_rct() const { return m_tx.vout[m_internal_output_index].amount == 0; }
|
bool is_rct() const { return m_rct; }
|
||||||
uint64_t amount() const { return m_amount; }
|
uint64_t amount() const { return m_amount; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -502,7 +503,7 @@ namespace tools
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BOOST_CLASS_VERSION(tools::wallet2, 14)
|
BOOST_CLASS_VERSION(tools::wallet2, 14)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 3)
|
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 4)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
|
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 5)
|
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 5)
|
||||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
|
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
|
||||||
|
@ -527,6 +528,10 @@ namespace boost
|
||||||
{
|
{
|
||||||
x.m_spent_height = 0;
|
x.m_spent_height = 0;
|
||||||
}
|
}
|
||||||
|
if (ver < 4)
|
||||||
|
{
|
||||||
|
x.m_rct = x.m_tx.vout[x.m_internal_output_index].amount == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
|
@ -563,8 +568,17 @@ namespace boost
|
||||||
}
|
}
|
||||||
a & x.m_spent_height;
|
a & x.m_spent_height;
|
||||||
if (ver < 3)
|
if (ver < 3)
|
||||||
|
{
|
||||||
|
initialize_transfer_details(a, x, ver);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
a & x.m_txid;
|
a & x.m_txid;
|
||||||
|
if (ver < 4)
|
||||||
|
{
|
||||||
|
initialize_transfer_details(a, x, ver);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
a & x.m_rct;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
|
@ -770,6 +784,7 @@ namespace tools
|
||||||
cryptonote::tx_source_entry& src = sources.back();
|
cryptonote::tx_source_entry& src = sources.back();
|
||||||
transfer_details& td = *it;
|
transfer_details& td = *it;
|
||||||
src.amount = td.amount();
|
src.amount = td.amount();
|
||||||
|
src.rct = false;
|
||||||
//paste mixin transaction
|
//paste mixin transaction
|
||||||
if(daemon_resp.outs.size())
|
if(daemon_resp.outs.size())
|
||||||
{
|
{
|
||||||
|
|
|
@ -337,6 +337,7 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events
|
||||||
se.amount = blk_0.miner_tx.vout[0].amount;
|
se.amount = blk_0.miner_tx.vout[0].amount;
|
||||||
se.push_output(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key, se.amount);
|
se.push_output(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key, se.amount);
|
||||||
se.real_output = 0;
|
se.real_output = 0;
|
||||||
|
se.rct = false;
|
||||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
|
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
|
||||||
se.real_output_in_tx_index = 0;
|
se.real_output_in_tx_index = 0;
|
||||||
std::vector<tx_source_entry> sources;
|
std::vector<tx_source_entry> sources;
|
||||||
|
@ -379,6 +380,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
|
||||||
se.amount = blk_1.miner_tx.vout[0].amount;
|
se.amount = blk_1.miner_tx.vout[0].amount;
|
||||||
se.push_output(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key, se.amount);
|
se.push_output(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key, se.amount);
|
||||||
se.real_output = 0;
|
se.real_output = 0;
|
||||||
|
se.rct = false;
|
||||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
|
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
|
||||||
se.real_output_in_tx_index = 0;
|
se.real_output_in_tx_index = 0;
|
||||||
std::vector<tx_source_entry> sources;
|
std::vector<tx_source_entry> sources;
|
||||||
|
|
|
@ -458,6 +458,7 @@ bool fill_tx_sources(std::vector<tx_source_entry>& sources, const std::vector<te
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ts.real_output = realOutput;
|
ts.real_output = realOutput;
|
||||||
|
ts.rct = false;
|
||||||
|
|
||||||
sources.push_back(ts);
|
sources.push_back(ts);
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,7 @@ bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_even
|
||||||
se.amount = tx_0.vout[0].amount;
|
se.amount = tx_0.vout[0].amount;
|
||||||
se.push_output(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key, se.amount);
|
se.push_output(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key, se.amount);
|
||||||
se.real_output = 0;
|
se.real_output = 0;
|
||||||
|
se.rct = false;
|
||||||
se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0);
|
se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0);
|
||||||
se.real_output_in_tx_index = 0;
|
se.real_output_in_tx_index = 0;
|
||||||
sources.push_back(se);
|
sources.push_back(se);
|
||||||
|
|
|
@ -63,6 +63,7 @@ namespace
|
||||||
se.amount = tx.vout[out_idx].amount;
|
se.amount = tx.vout[out_idx].amount;
|
||||||
se.push_output(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key, se.amount);
|
se.push_output(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key, se.amount);
|
||||||
se.real_output = 0;
|
se.real_output = 0;
|
||||||
|
se.rct = false;
|
||||||
se.real_out_tx_key = get_tx_pub_key_from_extra(tx);
|
se.real_out_tx_key = get_tx_pub_key_from_extra(tx);
|
||||||
se.real_output_in_tx_index = out_idx;
|
se.real_output_in_tx_index = out_idx;
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
miner_accounts[n].generate();
|
miner_accounts[n].generate();
|
||||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n],
|
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n],
|
||||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
|
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
|
||||||
4, 4, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||||
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 4),
|
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
|
||||||
false, "Failed to generate block");
|
false, "Failed to generate block");
|
||||||
events.push_back(blocks[n]);
|
events.push_back(blocks[n]);
|
||||||
prev_block = blocks + n;
|
prev_block = blocks + n;
|
||||||
|
@ -74,8 +74,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
cryptonote::block blk;
|
cryptonote::block blk;
|
||||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
|
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
|
||||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
|
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
|
||||||
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||||
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 4),
|
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
|
||||||
false, "Failed to generate block");
|
false, "Failed to generate block");
|
||||||
events.push_back(blk);
|
events.push_back(blk);
|
||||||
blk_last = blk;
|
blk_last = blk;
|
||||||
|
@ -104,6 +104,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
src.real_output = n;
|
src.real_output = n;
|
||||||
src.real_output_in_tx_index = index_in_tx;
|
src.real_output_in_tx_index = index_in_tx;
|
||||||
src.mask = rct::identity();
|
src.mask = rct::identity();
|
||||||
|
src.rct = false;
|
||||||
|
|
||||||
//fill outputs entry
|
//fill outputs entry
|
||||||
tx_destination_entry td;
|
tx_destination_entry td;
|
||||||
|
@ -135,9 +136,9 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account,
|
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account,
|
||||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version,
|
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs,
|
||||||
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||||
crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 0, 4),
|
crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 4),
|
||||||
false, "Failed to generate block");
|
false, "Failed to generate block");
|
||||||
events.push_back(blk_txes[n]);
|
events.push_back(blk_txes[n]);
|
||||||
blk_last = blk_txes[n];
|
blk_last = blk_txes[n];
|
||||||
|
@ -149,9 +150,9 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
{
|
{
|
||||||
cryptonote::block blk;
|
cryptonote::block blk;
|
||||||
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
|
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account,
|
||||||
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
|
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs,
|
||||||
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
|
||||||
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 4),
|
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 6, 4),
|
||||||
false, "Failed to generate block");
|
false, "Failed to generate block");
|
||||||
events.push_back(blk);
|
events.push_back(blk);
|
||||||
blk_last = blk;
|
blk_last = blk;
|
||||||
|
@ -161,7 +162,9 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
|
|
||||||
// create a tx from the requested ouputs
|
// create a tx from the requested ouputs
|
||||||
std::vector<tx_source_entry> sources;
|
std::vector<tx_source_entry> sources;
|
||||||
size_t rct_idx = 0, pre_rct_idx = 0;
|
size_t global_rct_idx = 6; // skip first coinbase (6 outputs)
|
||||||
|
size_t rct_idx = 0;
|
||||||
|
size_t pre_rct_idx = 0;
|
||||||
for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) {
|
for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) {
|
||||||
sources.resize(sources.size()+1);
|
sources.resize(sources.size()+1);
|
||||||
tx_source_entry& src = sources.back();
|
tx_source_entry& src = sources.back();
|
||||||
|
@ -173,12 +176,16 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
src.real_out_tx_key = get_tx_pub_key_from_extra(rct_txes[rct_idx/4]);
|
src.real_out_tx_key = get_tx_pub_key_from_extra(rct_txes[rct_idx/4]);
|
||||||
src.real_output_in_tx_index = rct_idx&3;
|
src.real_output_in_tx_index = rct_idx&3;
|
||||||
src.mask = rct_tx_masks[rct_idx];
|
src.mask = rct_tx_masks[rct_idx];
|
||||||
|
src.rct = true;
|
||||||
for (int m = 0; m <= mixin; ++m) {
|
for (int m = 0; m <= mixin; ++m) {
|
||||||
rct::ctkey ctkey;
|
rct::ctkey ctkey;
|
||||||
ctkey.dest = rct::pk2rct(boost::get<txout_to_key>(rct_txes[rct_idx/4].vout[rct_idx&3].target).key);
|
ctkey.dest = rct::pk2rct(boost::get<txout_to_key>(rct_txes[rct_idx/4].vout[rct_idx&3].target).key);
|
||||||
ctkey.mask = rct_txes[rct_idx/4].rct_signatures.outPk[rct_idx&3].mask;
|
ctkey.mask = rct_txes[rct_idx/4].rct_signatures.outPk[rct_idx&3].mask;
|
||||||
src.outputs.push_back(std::make_pair(rct_idx, ctkey));
|
src.outputs.push_back(std::make_pair(global_rct_idx, ctkey));
|
||||||
++rct_idx;
|
++rct_idx;
|
||||||
|
++global_rct_idx;
|
||||||
|
if (global_rct_idx % 10 == 0)
|
||||||
|
global_rct_idx += 6; // skip the coinbase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -188,6 +195,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
|
||||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[pre_rct_idx].miner_tx);
|
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[pre_rct_idx].miner_tx);
|
||||||
src.real_output_in_tx_index = 4;
|
src.real_output_in_tx_index = 4;
|
||||||
src.mask = rct::identity();
|
src.mask = rct::identity();
|
||||||
|
src.rct = false;
|
||||||
for (int m = 0; m <= mixin; ++m) {
|
for (int m = 0; m <= mixin; ++m) {
|
||||||
src.push_output(m, boost::get<txout_to_key>(blocks[pre_rct_idx].miner_tx.vout[4].target).key, src.amount);
|
src.push_output(m, boost::get<txout_to_key>(blocks[pre_rct_idx].miner_tx.vout[4].target).key, src.amount);
|
||||||
++pre_rct_idx;
|
++pre_rct_idx;
|
||||||
|
|
|
@ -81,7 +81,7 @@ private:
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct get_test_options<gen_rct_tx_validation_base> {
|
struct get_test_options<gen_rct_tx_validation_base> {
|
||||||
const std::pair<uint8_t, uint64_t> hard_forks[2] = {std::make_pair(1, 0), std::make_pair(4, 1)};
|
const std::pair<uint8_t, uint64_t> hard_forks[3] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65)};
|
||||||
const cryptonote::test_options test_options = {
|
const cryptonote::test_options test_options = {
|
||||||
hard_forks
|
hard_forks
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,6 +97,7 @@ bool test_transaction_generation_and_ring_signature()
|
||||||
|
|
||||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2);
|
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2);
|
||||||
src.real_output = 1;
|
src.real_output = 1;
|
||||||
|
src.rct = false;
|
||||||
src.real_output_in_tx_index = 0;
|
src.real_output_in_tx_index = 0;
|
||||||
}
|
}
|
||||||
//fill outputs entry
|
//fill outputs entry
|
||||||
|
|
|
@ -96,6 +96,7 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
|
||||||
}
|
}
|
||||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[0].miner_tx);
|
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[0].miner_tx);
|
||||||
src.real_output = 0;
|
src.real_output = 0;
|
||||||
|
src.rct = false;
|
||||||
src.real_output_in_tx_index = out_idx[out_idx_idx];
|
src.real_output_in_tx_index = out_idx[out_idx_idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ bool make_tx(blockchain_storage& bch)
|
||||||
src.real_out_tx_key = td.m_tx.tx_pub_key;
|
src.real_out_tx_key = td.m_tx.tx_pub_key;
|
||||||
src.real_output = interted_it - src.outputs.begin();
|
src.real_output = interted_it - src.outputs.begin();
|
||||||
src.real_output_in_tx_index = td.m_internal_output_index;
|
src.real_output_in_tx_index = td.m_internal_output_index;
|
||||||
|
src.rct = false;
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ public:
|
||||||
source_entry.real_output_in_tx_index = 0;
|
source_entry.real_output_in_tx_index = 0;
|
||||||
source_entry.outputs.swap(output_entries);
|
source_entry.outputs.swap(output_entries);
|
||||||
source_entry.real_output = real_source_idx;
|
source_entry.real_output = real_source_idx;
|
||||||
|
source_entry.rct = false;
|
||||||
|
|
||||||
m_sources.push_back(source_entry);
|
m_sources.push_back(source_entry);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue