carrot_impl: load enotes directly from transactions
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
#include "carrot_tx_format_utils.h"
|
#include "carrot_tx_format_utils.h"
|
||||||
|
|
||||||
//local headers
|
//local headers
|
||||||
|
#include "carrot_core/enote_utils.h"
|
||||||
#include "common/container_helpers.h"
|
#include "common/container_helpers.h"
|
||||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||||
#include "cryptonote_config.h"
|
#include "cryptonote_config.h"
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
//standard headers
|
//standard headers
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
#define MONERO_DEFAULT_LOG_CATEGORY "carrot_impl"
|
#define MONERO_DEFAULT_LOG_CATEGORY "carrot_impl.format_utils"
|
||||||
|
|
||||||
static_assert(sizeof(mx25519_pubkey) == sizeof(crypto::public_key),
|
static_assert(sizeof(mx25519_pubkey) == sizeof(crypto::public_key),
|
||||||
"cannot use crypto::public_key as storage for X25519 keys since size is different");
|
"cannot use crypto::public_key as storage for X25519 keys since size is different");
|
||||||
@@ -114,6 +115,39 @@ bool is_carrot_transaction_v1(const cryptonote::transaction_prefix &tx_prefix)
|
|||||||
return tx_prefix.vout.at(0).target.type() == typeid(cryptonote::txout_to_carrot_v1);
|
return tx_prefix.vout.at(0).target.type() == typeid(cryptonote::txout_to_carrot_v1);
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
input_context_t parse_carrot_input_context(const cryptonote::txin_gen &txin)
|
||||||
|
{
|
||||||
|
return make_carrot_input_context_coinbase(txin.height);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
input_context_t parse_carrot_input_context(const cryptonote::txin_to_key &txin)
|
||||||
|
{
|
||||||
|
return make_carrot_input_context(txin.k_image);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool parse_carrot_input_context(const cryptonote::txin_v &txin, input_context_t &input_context_out)
|
||||||
|
{
|
||||||
|
struct parse_carrot_input_context_v_visitor
|
||||||
|
{
|
||||||
|
bool operator()(const cryptonote::txin_gen &txin) const
|
||||||
|
{ input_context_out = parse_carrot_input_context(txin); return true; }
|
||||||
|
bool operator()(const cryptonote::txin_to_key &txin) const
|
||||||
|
{ input_context_out = parse_carrot_input_context(txin); return true; }
|
||||||
|
bool operator()(const cryptonote::txin_to_script&) const { return false; }
|
||||||
|
bool operator()(const cryptonote::txin_to_scripthash&) const { return false; }
|
||||||
|
|
||||||
|
input_context_t &input_context_out;
|
||||||
|
};
|
||||||
|
|
||||||
|
return boost::apply_visitor(parse_carrot_input_context_v_visitor{input_context_out}, txin);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool parse_carrot_input_context(const cryptonote::transaction_prefix &tx_prefix, input_context_t &input_context_out)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(!tx_prefix.vin.empty(), false, "parse_carrot_input_context: no input available");
|
||||||
|
return parse_carrot_input_context(tx_prefix.vin.at(0), input_context_out);
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
bool try_load_carrot_extra_v1(
|
bool try_load_carrot_extra_v1(
|
||||||
const std::vector<std::uint8_t> &tx_extra,
|
const std::vector<std::uint8_t> &tx_extra,
|
||||||
std::vector<mx25519_pubkey> &enote_ephemeral_pubkeys_out,
|
std::vector<mx25519_pubkey> &enote_ephemeral_pubkeys_out,
|
||||||
@@ -216,6 +250,63 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
|||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool try_load_carrot_enote_from_transaction_v1(const cryptonote::transaction &tx,
|
||||||
|
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys,
|
||||||
|
const std::size_t local_output_index,
|
||||||
|
CarrotEnoteV1 &enote_out)
|
||||||
|
{
|
||||||
|
const rct::rctSigBase &rv = tx.rct_signatures;
|
||||||
|
|
||||||
|
const size_t nins = tx.vin.size();
|
||||||
|
const size_t nouts = tx.vout.size();
|
||||||
|
const bool shared_ephemeral_pubkey = enote_ephemeral_pubkeys.size() == 1;
|
||||||
|
const size_t ephemeral_pubkey_index = shared_ephemeral_pubkey ? 0 : local_output_index;
|
||||||
|
|
||||||
|
CHECK_AND_ASSERT_MES(nins, false, "try_load_carrot_enote_from_transaction_v1: no inputs");
|
||||||
|
CHECK_AND_ASSERT_MES(ephemeral_pubkey_index < enote_ephemeral_pubkeys.size(),
|
||||||
|
false,
|
||||||
|
"try_load_carrot_enote_from_transaction_v1: not enough ephemeral pubkeys");
|
||||||
|
CHECK_AND_ASSERT_MES(local_output_index < nouts,
|
||||||
|
false,
|
||||||
|
"try_load_carrot_enote_from_transaction_v1: not enough outputs");
|
||||||
|
CHECK_AND_ASSERT_MES(nouts == rv.ecdhInfo.size(),
|
||||||
|
false,
|
||||||
|
"try_load_carrot_enote_from_transaction_v1: ecdhInfo wrong size");
|
||||||
|
CHECK_AND_ASSERT_MES(nouts == rv.outPk.size(),
|
||||||
|
false,
|
||||||
|
"try_load_carrot_enote_from_transaction_v1: outPk wrong size");
|
||||||
|
|
||||||
|
const cryptonote::txout_target_v &t = tx.vout.at(local_output_index).target;
|
||||||
|
const cryptonote::txout_to_carrot_v1 * const c = boost::strict_get<cryptonote::txout_to_carrot_v1>(&t);
|
||||||
|
CHECK_AND_ASSERT_MES(c, false, "try_load_carrot_enote_from_transaction_v1: wrong output type");
|
||||||
|
|
||||||
|
const cryptonote::txin_to_key * const inp = boost::strict_get<cryptonote::txin_to_key>(&tx.vin.at(0));
|
||||||
|
CHECK_AND_ASSERT_MES(inp, false, "try_load_carrot_enote_from_transaction_v1: wrong input type");
|
||||||
|
|
||||||
|
//K_o
|
||||||
|
enote_out.onetime_address = c->key;
|
||||||
|
|
||||||
|
//vt
|
||||||
|
enote_out.view_tag = c->view_tag;
|
||||||
|
|
||||||
|
//anchor_enc
|
||||||
|
enote_out.anchor_enc = c->encrypted_janus_anchor;
|
||||||
|
|
||||||
|
//L_1
|
||||||
|
enote_out.tx_first_key_image = inp->k_image;
|
||||||
|
|
||||||
|
//a_enc
|
||||||
|
memcpy(enote_out.amount_enc.bytes, rv.ecdhInfo.at(local_output_index).amount.bytes, sizeof(encrypted_amount_t));
|
||||||
|
|
||||||
|
//C_a
|
||||||
|
enote_out.amount_commitment = rv.outPk.at(local_output_index).mask;
|
||||||
|
|
||||||
|
//D_e
|
||||||
|
enote_out.enote_ephemeral_pubkey = enote_ephemeral_pubkeys[ephemeral_pubkey_index];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx,
|
bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx,
|
||||||
std::vector<CarrotEnoteV1> &enotes_out,
|
std::vector<CarrotEnoteV1> &enotes_out,
|
||||||
std::vector<crypto::key_image> &key_images_out,
|
std::vector<crypto::key_image> &key_images_out,
|
||||||
@@ -228,15 +319,6 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx,
|
|||||||
const size_t nins = tx.vin.size();
|
const size_t nins = tx.vin.size();
|
||||||
const size_t nouts = tx.vout.size();
|
const size_t nouts = tx.vout.size();
|
||||||
|
|
||||||
if (0 == nins)
|
|
||||||
return false; // no input_context
|
|
||||||
else if (2 > nouts)
|
|
||||||
return false; // <2 outs is invalid
|
|
||||||
else if (nouts != rv.ecdhInfo.size())
|
|
||||||
return false; // incorrect # of encrypted amounts
|
|
||||||
else if (nouts != rv.outPk.size())
|
|
||||||
return false; // incorrect # of amount commitments
|
|
||||||
|
|
||||||
//inputs
|
//inputs
|
||||||
key_images_out.resize(nins);
|
key_images_out.resize(nins);
|
||||||
for (size_t i = 0; i < nins; ++i)
|
for (size_t i = 0; i < nins; ++i)
|
||||||
@@ -249,34 +331,6 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx,
|
|||||||
key_images_out[i] = k->k_image;
|
key_images_out[i] = k->k_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
//outputs
|
|
||||||
enotes_out.resize(nouts);
|
|
||||||
for (size_t i = 0; i < nouts; ++i)
|
|
||||||
{
|
|
||||||
const cryptonote::txout_target_v &t = tx.vout.at(i).target;
|
|
||||||
const cryptonote::txout_to_carrot_v1 * const c = boost::strict_get<cryptonote::txout_to_carrot_v1>(&t);
|
|
||||||
if (nullptr == c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//K_o
|
|
||||||
enotes_out[i].onetime_address = c->key;
|
|
||||||
|
|
||||||
//vt
|
|
||||||
enotes_out[i].view_tag = c->view_tag;
|
|
||||||
|
|
||||||
//anchor_enc
|
|
||||||
enotes_out[i].anchor_enc = c->encrypted_janus_anchor;
|
|
||||||
|
|
||||||
//L_1
|
|
||||||
enotes_out[i].tx_first_key_image = key_images_out.at(0);
|
|
||||||
|
|
||||||
//a_enc
|
|
||||||
memcpy(enotes_out[i].amount_enc.bytes, rv.ecdhInfo.at(i).amount.bytes, sizeof(encrypted_amount_t));
|
|
||||||
|
|
||||||
//C_a
|
|
||||||
enotes_out[i].amount_commitment = rv.outPk.at(i).mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
//D_e, pid_enc
|
//D_e, pid_enc
|
||||||
std::vector<mx25519_pubkey> enote_ephemeral_pubkeys;
|
std::vector<mx25519_pubkey> enote_ephemeral_pubkeys;
|
||||||
if (!try_load_carrot_extra_v1(tx.extra, enote_ephemeral_pubkeys, encrypted_payment_id_out))
|
if (!try_load_carrot_extra_v1(tx.extra, enote_ephemeral_pubkeys, encrypted_payment_id_out))
|
||||||
@@ -286,9 +340,11 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx,
|
|||||||
if (n_ephemeral == 0 || n_ephemeral > nouts)
|
if (n_ephemeral == 0 || n_ephemeral > nouts)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// collect D_e
|
//outputs
|
||||||
for (size_t i = 0; i < enotes_out.size(); ++i)
|
enotes_out.resize(nouts);
|
||||||
enotes_out[i].enote_ephemeral_pubkey = enote_ephemeral_pubkeys.at(std::min(i, n_ephemeral - 1));
|
for (size_t i = 0; i < nouts; ++i)
|
||||||
|
if (!try_load_carrot_enote_from_transaction_v1(tx, epee::to_span(enote_ephemeral_pubkeys), i, enotes_out[i]))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -333,47 +389,53 @@ cryptonote::transaction store_carrot_to_coinbase_transaction_v1(
|
|||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
//-------------------------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool try_load_carrot_coinbase_enote_from_transaction_v1(const cryptonote::transaction &tx,
|
||||||
|
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys,
|
||||||
|
const std::size_t local_output_index,
|
||||||
|
CarrotCoinbaseEnoteV1 &enote_out)
|
||||||
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(!tx.vin.empty(), false, "try_load_carrot_coinbase_enote_from_transaction_v1: no inputs");
|
||||||
|
const cryptonote::txin_gen * const inp = boost::strict_get<cryptonote::txin_gen>(&tx.vin.at(0));
|
||||||
|
CHECK_AND_ASSERT_MES(inp, false, "try_load_carrot_coinbase_enote_from_transaction_v1: wrong input type");
|
||||||
|
|
||||||
|
//block_index
|
||||||
|
enote_out.block_index = inp->height;
|
||||||
|
|
||||||
|
CHECK_AND_ASSERT_MES(local_output_index < tx.vout.size(),
|
||||||
|
false,
|
||||||
|
"try_load_carrot_coinbase_enote_from_transaction_v1: not enough outputs");
|
||||||
|
const cryptonote::tx_out &o = tx.vout.at(local_output_index);
|
||||||
|
|
||||||
|
//a
|
||||||
|
enote_out.amount = o.amount;
|
||||||
|
|
||||||
|
const cryptonote::txout_to_carrot_v1 * const c = boost::strict_get<cryptonote::txout_to_carrot_v1>(&o.target);
|
||||||
|
CHECK_AND_ASSERT_MES(c, false, "try_load_carrot_coinbase_enote_from_transaction_v1: wrong output type");
|
||||||
|
|
||||||
|
//K_o
|
||||||
|
enote_out.onetime_address = c->key;
|
||||||
|
|
||||||
|
//vt
|
||||||
|
enote_out.view_tag = c->view_tag;
|
||||||
|
|
||||||
|
//anchor_enc
|
||||||
|
enote_out.anchor_enc = c->encrypted_janus_anchor;
|
||||||
|
|
||||||
|
CHECK_AND_ASSERT_MES(local_output_index < enote_ephemeral_pubkeys.size(),
|
||||||
|
false,
|
||||||
|
"try_load_carrot_coinbase_enote_from_transaction_v1: no enough ephemeral pubkeys");
|
||||||
|
|
||||||
|
//D_e
|
||||||
|
enote_out.enote_ephemeral_pubkey = enote_ephemeral_pubkeys[local_output_index];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//-------------------------------------------------------------------------------------------------------------------
|
||||||
bool try_load_carrot_from_coinbase_transaction_v1(const cryptonote::transaction &tx,
|
bool try_load_carrot_from_coinbase_transaction_v1(const cryptonote::transaction &tx,
|
||||||
std::vector<CarrotCoinbaseEnoteV1> &enotes_out)
|
std::vector<CarrotCoinbaseEnoteV1> &enotes_out)
|
||||||
{
|
{
|
||||||
const size_t nins = tx.vin.size();
|
|
||||||
const size_t nouts = tx.vout.size();
|
const size_t nouts = tx.vout.size();
|
||||||
|
|
||||||
if (1 != nins)
|
|
||||||
return false; //not coinbase
|
|
||||||
else if (0 == nouts)
|
|
||||||
return false; //0-out coinbase not allowed
|
|
||||||
|
|
||||||
//input
|
|
||||||
const cryptonote::txin_gen * const h = boost::strict_get<cryptonote::txin_gen>(&tx.vin.front());
|
|
||||||
if (nullptr == h)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//outputs
|
|
||||||
enotes_out.resize(nouts);
|
|
||||||
for (size_t i = 0; i < nouts; ++i)
|
|
||||||
{
|
|
||||||
//a
|
|
||||||
enotes_out[i].amount = tx.vout.at(i).amount;
|
|
||||||
|
|
||||||
const cryptonote::txout_target_v &t = tx.vout.at(i).target;
|
|
||||||
const cryptonote::txout_to_carrot_v1 * const c = boost::strict_get<cryptonote::txout_to_carrot_v1>(&t);
|
|
||||||
if (nullptr == c)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//K_o
|
|
||||||
enotes_out[i].onetime_address = c->key;
|
|
||||||
|
|
||||||
//vt
|
|
||||||
enotes_out[i].view_tag = c->view_tag;
|
|
||||||
|
|
||||||
//anchor_enc
|
|
||||||
enotes_out[i].anchor_enc = c->encrypted_janus_anchor;
|
|
||||||
|
|
||||||
//block_index
|
|
||||||
enotes_out[i].block_index = h->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
//D_e, pid_enc
|
//D_e, pid_enc
|
||||||
std::vector<mx25519_pubkey> enote_ephemeral_pubkeys;
|
std::vector<mx25519_pubkey> enote_ephemeral_pubkeys;
|
||||||
std::optional<encrypted_payment_id_t> dummy_encrypted_payment_id;
|
std::optional<encrypted_payment_id_t> dummy_encrypted_payment_id;
|
||||||
@@ -382,9 +444,14 @@ bool try_load_carrot_from_coinbase_transaction_v1(const cryptonote::transaction
|
|||||||
else if (enote_ephemeral_pubkeys.size() != nouts)
|
else if (enote_ephemeral_pubkeys.size() != nouts)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//collect D_e
|
//outputs
|
||||||
for (size_t i = 0; i < enotes_out.size(); ++i)
|
enotes_out.resize(nouts);
|
||||||
enotes_out[i].enote_ephemeral_pubkey = enote_ephemeral_pubkeys.at(i);
|
for (size_t i = 0; i < nouts; ++i)
|
||||||
|
if (!try_load_carrot_coinbase_enote_from_transaction_v1(tx,
|
||||||
|
epee::to_span(enote_ephemeral_pubkeys),
|
||||||
|
i,
|
||||||
|
enotes_out[i]))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,13 @@ static inline T raw_byte_convert(const U &u)
|
|||||||
* is_carrot_transaction_v1 - determine whether a transaction uses the Carrot addressing protocol
|
* is_carrot_transaction_v1 - determine whether a transaction uses the Carrot addressing protocol
|
||||||
*/
|
*/
|
||||||
bool is_carrot_transaction_v1(const cryptonote::transaction_prefix &tx_prefix);
|
bool is_carrot_transaction_v1(const cryptonote::transaction_prefix &tx_prefix);
|
||||||
|
/**
|
||||||
|
* brief: parse_carrot_input_context - try parsing carrot input context from cryptonote transaction components
|
||||||
|
*/
|
||||||
|
input_context_t parse_carrot_input_context(const cryptonote::txin_gen &txin);
|
||||||
|
input_context_t parse_carrot_input_context(const cryptonote::txin_to_key &txin);
|
||||||
|
bool parse_carrot_input_context(const cryptonote::txin_v &txin, input_context_t &input_context_out);
|
||||||
|
bool parse_carrot_input_context(const cryptonote::transaction_prefix &tx_prefix, input_context_t &input_context_out);
|
||||||
/**
|
/**
|
||||||
* try_load_carrot_extra_v1 - load Carrot info which is stored in tx_extra
|
* try_load_carrot_extra_v1 - load Carrot info which is stored in tx_extra
|
||||||
* param: tx_extra_fields -
|
* param: tx_extra_fields -
|
||||||
@@ -91,6 +98,18 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
|
|||||||
const std::vector<crypto::key_image> &key_images,
|
const std::vector<crypto::key_image> &key_images,
|
||||||
const rct::xmr_amount fee,
|
const rct::xmr_amount fee,
|
||||||
const encrypted_payment_id_t encrypted_payment_id);
|
const encrypted_payment_id_t encrypted_payment_id);
|
||||||
|
/**
|
||||||
|
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
|
||||||
|
* param: tx -
|
||||||
|
* param: enote_ephemeral_pubkeys - D_e
|
||||||
|
* param: local_output_index -
|
||||||
|
* outparam: enote_out -
|
||||||
|
* return: true iff enote was successfully parsed
|
||||||
|
*/
|
||||||
|
bool try_load_carrot_enote_from_transaction_v1(const cryptonote::transaction &tx,
|
||||||
|
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys,
|
||||||
|
const std::size_t local_output_index,
|
||||||
|
CarrotEnoteV1 &enote_out);
|
||||||
/**
|
/**
|
||||||
* brief: load_carrot_from_transaction_v1 - load non-coinbase Carrot info from a cryptonote::transaction
|
* brief: load_carrot_from_transaction_v1 - load non-coinbase Carrot info from a cryptonote::transaction
|
||||||
* param: tx -
|
* param: tx -
|
||||||
@@ -113,11 +132,22 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx,
|
|||||||
*/
|
*/
|
||||||
cryptonote::transaction store_carrot_to_coinbase_transaction_v1(
|
cryptonote::transaction store_carrot_to_coinbase_transaction_v1(
|
||||||
const std::vector<CarrotCoinbaseEnoteV1> &enotes);
|
const std::vector<CarrotCoinbaseEnoteV1> &enotes);
|
||||||
|
/**
|
||||||
|
* brief: try_load_carrot_coinbase_enote_from_transaction_v1 - load one coinbase Carrot enote from a cryptonote::transaction
|
||||||
|
* param: tx -
|
||||||
|
* param: enote_ephemeral_pubkeys -
|
||||||
|
* param: local_output_index -
|
||||||
|
* outparam: enote_out -
|
||||||
|
* return: true iff enote was successfully parsed
|
||||||
|
*/
|
||||||
|
bool try_load_carrot_coinbase_enote_from_transaction_v1(const cryptonote::transaction &tx,
|
||||||
|
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys,
|
||||||
|
const std::size_t local_output_index,
|
||||||
|
CarrotCoinbaseEnoteV1 &enote_out);
|
||||||
/**
|
/**
|
||||||
* brief: try_load_carrot_from_coinbase_transaction_v1 - load coinbase Carrot info from a cryptonote::transaction
|
* brief: try_load_carrot_from_coinbase_transaction_v1 - load coinbase Carrot info from a cryptonote::transaction
|
||||||
* param: tx -
|
* param: tx -
|
||||||
* outparam: enotes_out -
|
* outparam: enotes_out -
|
||||||
* outparam: block_index_out -
|
|
||||||
* return: Carrot coinbase enotes and block index contained within a coinbase transaction
|
* return: Carrot coinbase enotes and block index contained within a coinbase transaction
|
||||||
*/
|
*/
|
||||||
bool try_load_carrot_from_coinbase_transaction_v1(const cryptonote::transaction &tx,
|
bool try_load_carrot_from_coinbase_transaction_v1(const cryptonote::transaction &tx,
|
||||||
|
|||||||
Reference in New Issue
Block a user