post carrot stake tx support (#32)

* add support for stake txs and payouts post-carrot

* fixes on the stake return payments

* complete post-carrot stake txs

* rename protocol_tx_data to protocol_tx_data_t
This commit is contained in:
akildemir
2025-07-23 16:58:11 +03:00
committed by GitHub
parent 3e0649a8d2
commit 9ba621b3ae
40 changed files with 827 additions and 140 deletions
+13
View File
@@ -207,6 +207,17 @@ typedef struct yield_tx_info {
crypto::public_key return_pubkey; crypto::public_key return_pubkey;
} yield_tx_info; } yield_tx_info;
typedef struct yield_tx_info_carrot {
uint8_t version;
uint64_t block_height;
crypto::hash tx_hash;
uint64_t locked_coins;
crypto::public_key return_address;
crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
} yield_tx_info_carrot;
#define DBF_SAFE 1 #define DBF_SAFE 1
#define DBF_FAST 2 #define DBF_FAST 2
#define DBF_FASTEST 4 #define DBF_FASTEST 4
@@ -1924,6 +1935,8 @@ public:
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const = 0; virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const = 0;
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const = 0; virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const = 0;
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const = 0;
/** /**
* @brief set whether or not to automatically remove logs * @brief set whether or not to automatically remove logs
+108 -3
View File
@@ -298,6 +298,7 @@ const char* const LMDB_YIELD_TXS = "yield_txs";
const char* const LMDB_YIELD_BLOCKS = "yield_blocks"; const char* const LMDB_YIELD_BLOCKS = "yield_blocks";
const char* const LMDB_AUDIT_TXS = "audit_txs"; const char* const LMDB_AUDIT_TXS = "audit_txs";
const char* const LMDB_AUDIT_BLOCKS = "audit_blocks"; const char* const LMDB_AUDIT_BLOCKS = "audit_blocks";
const char* const LMDB_CARROT_YIELD_TXS = "carrot_yield_txs";
const char zerokey[8] = {0}; const char zerokey[8] = {0};
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey }; const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
@@ -947,6 +948,43 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_t
return 0; return 0;
} }
int BlockchainLMDB::get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const {
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
// Clear the container
yti_container.clear();
// Query for the (presumably matured) YIELD_TX_INFO information
TXN_PREFIX_RDONLY();
RCURSOR(carrot_yield_txs);
MDB_val v;
MDB_val_set(k, height);
MDB_cursor_op op = MDB_SET;
while (1)
{
int ret = mdb_cursor_get(m_cur_carrot_yield_txs, &k, &v, op);
op = MDB_NEXT_DUP;
if (ret == MDB_NOTFOUND)
break;
if (ret)
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield TX info: ", ret).c_str()));
// Get the data
yield_tx_info_carrot *p = (yield_tx_info_carrot*)v.mv_data;
// Push result back into the container
yti_container.emplace_back(*p);
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
yti_container.back().block_height = height;
}
TXN_POSTFIX_RDONLY();
// Return success to caller
return 0;
}
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, uint64_t audit_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi) void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, uint64_t audit_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi)
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1215,6 +1253,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
CURSOR(circ_supply_tally) CURSOR(circ_supply_tally)
CURSOR(yield_txs) CURSOR(yield_txs)
CURSOR(audit_txs) CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
MDB_val_set(val_tx_id, tx_id); MDB_val_set(val_tx_id, tx_id);
MDB_val_set(val_h, tx_hash); MDB_val_set(val_h, tx_hash);
@@ -1321,7 +1360,12 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally); write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
} }
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) { if (tx.type == cryptonote::transaction_type::BURN ||
tx.type == cryptonote::transaction_type::CONVERT ||
tx.type == cryptonote::transaction_type::STAKE ||
tx.type == cryptonote::transaction_type::AUDIT ||
tx.type == cryptonote::transaction_type::TRANSFER)
{
// Get the current tally value for the source currency type // Get the current tally value for the source currency type
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(tx.source_asset_type)); MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(tx.source_asset_type));
@@ -1392,8 +1436,42 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
} }
// Is there yield_tx data to add? // Is there yield_tx data to add?
if (tx.type == cryptonote::transaction_type::STAKE) { if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE)
{
// Create the object we are going to write to the database
yield_tx_info_carrot yield_data;
yield_data.block_height = m_height;
yield_data.tx_hash = tx_hash;
yield_data.return_pubkey = tx.protocol_tx_data.return_pubkey;
yield_data.return_address = tx.protocol_tx_data.return_address;
yield_data.return_view_tag = tx.protocol_tx_data.return_view_tag;
yield_data.return_anchor_enc = tx.protocol_tx_data.return_anchor_enc;
yield_data.locked_coins = tx.amount_burnt;
// Because LMDB is shockingly bad at handling duplicates, we have resorted to using a counter of elements
// in the first element of the struct.
MDB_val data;
MDB_val_set(val_height, m_height);
result = mdb_cursor_get(m_cur_carrot_yield_txs, &val_height, &data, MDB_SET);
if (!result)
{
mdb_size_t num_elems = 0;
result = mdb_cursor_count(m_cur_carrot_yield_txs, &num_elems);
if (result)
throw0(DB_ERROR(std::string("Failed to get number of yield TXs for height: ").append(mdb_strerror(result)).c_str()));
yield_data.block_height = num_elems;
}
else if (result != MDB_NOTFOUND)
throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str()));
else
yield_data.block_height = 0;
// Now we know how many there are, write out the data to the DB
MDB_val_set(val_yield_tx_data, yield_data);
result = mdb_cursor_put(m_cur_carrot_yield_txs, &val_height, &val_yield_tx_data, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx carrot yield data to db transaction: ", result).c_str()));
} else if (tx.type == cryptonote::transaction_type::STAKE) {
// Create the object we are going to write to the database // Create the object we are going to write to the database
yield_tx_info yield_data; yield_tx_info yield_data;
yield_data.block_height = m_height; yield_data.block_height = m_height;
@@ -1522,6 +1600,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
CURSOR(circ_supply_tally) CURSOR(circ_supply_tally)
CURSOR(yield_txs) CURSOR(yield_txs)
CURSOR(audit_txs) CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
MDB_val_set(val_h, tx_hash); MDB_val_set(val_h, tx_hash);
@@ -1720,7 +1799,28 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
} }
// Is there yield_tx data to remove? // Is there yield_tx data to remove?
if (tx.type == cryptonote::transaction_type::STAKE) { if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE) {
// Remove any yield_tx data for this transaction
MDB_val_set(val_height, m_height);
MDB_val v;
MDB_cursor_op op = MDB_SET;
while (1) {
result = mdb_cursor_get(m_cur_carrot_yield_txs, &val_height, &v, op);
if (result == MDB_NOTFOUND) {
throw1(DB_ERROR("Failed to locate carrot yield tx for removal from db transaction"));
} else if (result) {
throw1(DB_ERROR(lmdb_error("Failed to locate carrot yield_tx data for removal: ", result).c_str()));
}
op = MDB_NEXT_DUP;
const yield_tx_info_carrot yti = *(const yield_tx_info_carrot*)v.mv_data;
if (yti.tx_hash == tx_hash) {
result = mdb_cursor_del(m_cur_carrot_yield_txs, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Failed to add removal of carrot yield_tx data to db transaction: ", result).c_str()));
break;
}
}
} else if (tx.type == cryptonote::transaction_type::STAKE) {
// Remove any yield_tx data for this transaction // Remove any yield_tx data for this transaction
MDB_val_set(val_height, m_height); MDB_val_set(val_height, m_height);
MDB_val v; MDB_val v;
@@ -2288,6 +2388,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
lmdb_db_open(txn, LMDB_AUDIT_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_audit_txs, "Failed to open db handle for m_audit_txs"); lmdb_db_open(txn, LMDB_AUDIT_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_audit_txs, "Failed to open db handle for m_audit_txs");
lmdb_db_open(txn, LMDB_AUDIT_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_audit_blocks, "Failed to open db handle for m_audit_blocks"); lmdb_db_open(txn, LMDB_AUDIT_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_audit_blocks, "Failed to open db handle for m_audit_blocks");
lmdb_db_open(txn, LMDB_CARROT_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_carrot_yield_txs, "Failed to open db handle for m_carrot_yield_txs");
mdb_set_dupsort(txn, m_spent_keys, compare_hash32); mdb_set_dupsort(txn, m_spent_keys, compare_hash32);
mdb_set_dupsort(txn, m_block_heights, compare_hash32); mdb_set_dupsort(txn, m_block_heights, compare_hash32);
mdb_set_dupsort(txn, m_tx_indices, compare_hash32); mdb_set_dupsort(txn, m_tx_indices, compare_hash32);
@@ -2312,6 +2414,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
mdb_set_dupsort(txn, m_yield_txs, compare_uint64); mdb_set_dupsort(txn, m_yield_txs, compare_uint64);
mdb_set_dupsort(txn, m_audit_txs, compare_uint64); mdb_set_dupsort(txn, m_audit_txs, compare_uint64);
mdb_set_dupsort(txn, m_carrot_yield_txs, compare_uint64);
if (!(mdb_flags & MDB_RDONLY)) if (!(mdb_flags & MDB_RDONLY))
{ {
@@ -2497,6 +2600,8 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_txs: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_txs: ", result).c_str()));
if (auto result = mdb_drop(txn, m_audit_blocks, 0)) if (auto result = mdb_drop(txn, m_audit_blocks, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str()));
if (auto result = mdb_drop(txn, m_carrot_yield_txs, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_carrot_yield_txs: ", result).c_str()));
// init with current version // init with current version
MDB_val_str(k, "version"); MDB_val_str(k, "version");
+6
View File
@@ -80,6 +80,7 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_yield_blocks; MDB_cursor *m_txc_yield_blocks;
MDB_cursor *m_txc_audit_txs; MDB_cursor *m_txc_audit_txs;
MDB_cursor *m_txc_audit_blocks; MDB_cursor *m_txc_audit_blocks;
MDB_cursor *m_txc_carrot_yield_txs;
} mdb_txn_cursors; } mdb_txn_cursors;
@@ -108,6 +109,7 @@ typedef struct mdb_txn_cursors
#define m_cur_yield_blocks m_cursors->m_txc_yield_blocks #define m_cur_yield_blocks m_cursors->m_txc_yield_blocks
#define m_cur_audit_txs m_cursors->m_txc_audit_txs #define m_cur_audit_txs m_cursors->m_txc_audit_txs
#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks #define m_cur_audit_blocks m_cursors->m_txc_audit_blocks
#define m_cur_carrot_yield_txs m_cursors->m_txc_carrot_yield_txs
typedef struct mdb_rflags typedef struct mdb_rflags
{ {
@@ -137,6 +139,7 @@ typedef struct mdb_rflags
bool m_rf_yield_blocks; bool m_rf_yield_blocks;
bool m_rf_audit_txs; bool m_rf_audit_txs;
bool m_rf_audit_blocks; bool m_rf_audit_blocks;
bool m_rf_carrot_yield_txs;
} mdb_rflags; } mdb_rflags;
typedef struct mdb_threadinfo typedef struct mdb_threadinfo
@@ -474,6 +477,7 @@ private:
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const; virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const;
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const; virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const;
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const;
private: private:
MDB_env* m_env; MDB_env* m_env;
@@ -515,6 +519,8 @@ private:
MDB_dbi m_audit_txs; MDB_dbi m_audit_txs;
MDB_dbi m_audit_blocks; MDB_dbi m_audit_blocks;
MDB_dbi m_carrot_yield_txs;
mutable uint64_t m_cum_size; // used in batch size estimation mutable uint64_t m_cum_size; // used in batch size estimation
mutable unsigned int m_cum_count; mutable unsigned int m_cum_count;
std::string m_folder; std::string m_folder;
+1
View File
@@ -145,6 +145,7 @@ public:
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const override { return 0; } virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const override { return 0; }
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const override { return 0; } virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const override { return 0; }
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const override { return 0; }
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; } virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; }
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata_ref*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; } virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata_ref*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; }
+4
View File
@@ -134,6 +134,10 @@ struct view_incoming_key_device
const crypto::public_key &onetime_address, const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const = 0; janus_anchor_t &anchor_special_out) const = 0;
virtual void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const = 0;
virtual ~view_incoming_key_device() = default; virtual ~view_incoming_key_device() = default;
}; };
+7
View File
@@ -55,6 +55,13 @@ bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_x25519(const mx
return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kvD); return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kvD);
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void view_incoming_key_ram_borrowed_device::make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const
{
make_sparc_return_privkey(to_bytes(m_k_view_incoming), input_context, onetime_address, return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_incoming_key_ram_borrowed_device::make_janus_anchor_special( void view_incoming_key_ram_borrowed_device::make_janus_anchor_special(
const mx25519_pubkey &enote_ephemeral_pubkey, const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context, const input_context_t &input_context,
+4
View File
@@ -60,6 +60,10 @@ public:
const crypto::public_key &onetime_address, const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const override; janus_anchor_t &anchor_special_out) const override;
void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const override;
protected: protected:
const crypto::secret_key &m_k_view_incoming; const crypto::secret_key &m_k_view_incoming;
}; };
@@ -159,6 +159,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
const view_incoming_key_device *k_view_dev, const view_incoming_key_device *k_view_dev,
const crypto::key_image &tx_first_key_image, const crypto::key_image &tx_first_key_image,
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out, std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
RCTOutputEnoteProposal &return_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out, encrypted_payment_id_t &encrypted_payment_id_out,
cryptonote::transaction_type tx_type, cryptonote::transaction_type tx_type,
size_t &change_index_out, size_t &change_index_out,
@@ -173,7 +174,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
// assert payment proposals numbers // assert payment proposals numbers
const size_t num_selfsend_proposals = selfsend_payment_proposals.size(); const size_t num_selfsend_proposals = selfsend_payment_proposals.size();
const size_t num_proposals = normal_payment_proposals.size() + num_selfsend_proposals; const size_t num_proposals = normal_payment_proposals.size() + num_selfsend_proposals;
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::BURN) {
CARROT_CHECK_AND_THROW(num_proposals == 1, too_few_outputs, "tx doesn't have correct number of proposals");
} else {
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals"); CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
}
CARROT_CHECK_AND_THROW(num_proposals <= CARROT_MAX_TX_OUTPUTS, too_many_outputs, "too many payment proposals"); CARROT_CHECK_AND_THROW(num_proposals <= CARROT_MAX_TX_OUTPUTS, too_many_outputs, "too many payment proposals");
CARROT_CHECK_AND_THROW(num_selfsend_proposals, too_few_outputs, "no selfsend payment proposal"); CARROT_CHECK_AND_THROW(num_selfsend_proposals, too_few_outputs, "no selfsend payment proposal");
@@ -281,7 +286,9 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
get_output_proposal_special_v1(selfsend_payment_proposal, get_output_proposal_special_v1(selfsend_payment_proposal,
*k_view_dev, *k_view_dev,
tx_first_key_image, tx_first_key_image,
tx_type,
other_enote_ephemeral_pubkey, other_enote_ephemeral_pubkey,
return_enote_out,
output_entry.first); output_entry.first);
} }
else // neither k_v nor s_vb device passed else // neither k_v nor s_vb device passed
@@ -109,6 +109,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
const view_incoming_key_device *k_view_dev, const view_incoming_key_device *k_view_dev,
const crypto::key_image &tx_first_key_image, const crypto::key_image &tx_first_key_image,
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out, std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
RCTOutputEnoteProposal &return_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out, encrypted_payment_id_t &encrypted_payment_id_out,
cryptonote::transaction_type tx_type, cryptonote::transaction_type tx_type,
size_t &change_index_out, size_t &change_index_out,
+33
View File
@@ -357,7 +357,9 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal, void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
const view_incoming_key_device &k_view_dev, const view_incoming_key_device &k_view_dev,
const crypto::key_image &tx_first_key_image, const crypto::key_image &tx_first_key_image,
const cryptonote::transaction_type tx_type,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey, const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
RCTOutputEnoteProposal &return_enote_out,
RCTOutputEnoteProposal &output_enote_out) RCTOutputEnoteProposal &output_enote_out)
{ {
// 1. sanity checks // 1. sanity checks
@@ -424,6 +426,37 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
output_enote_out.enote.asset_type = "SAL1"; output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>(); output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount; output_enote_out.amount = proposal.amount;
// 10. construct the stake return enote
if (tx_type == cryptonote::transaction_type::STAKE) {
// make k_return
crypto::secret_key k_return;
k_view_dev.make_internal_return_privkey(input_context, output_enote_out.enote.onetime_address, k_return);
// compute K_return = k_return * G
crypto::public_key return_pub;
crypto::secret_key_to_public_key(k_return, return_pub);
// Make a destination address for the return
CarrotDestinationV1 return_destination;
make_carrot_main_address_v1(output_enote_out.enote.onetime_address, return_pub, return_destination);
// Create the return proposal, using the return address and the amount
const CarrotPaymentProposalV1 proposal_return = CarrotPaymentProposalV1{
.destination = return_destination,
.amount = 0,
.randomness = gen_janus_anchor()
};
encrypted_payment_id_t encrypted_payment_id_return;
get_output_proposal_return_v1(
proposal_return,
tx_first_key_image,
nullptr, // s_view_balance_dev
return_enote_out,
encrypted_payment_id_return
);
}
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal, void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
+2
View File
@@ -160,7 +160,9 @@ void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal, void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &proposal,
const view_incoming_key_device &k_view_dev, const view_incoming_key_device &k_view_dev,
const crypto::key_image &tx_first_key_image, const crypto::key_image &tx_first_key_image,
const cryptonote::transaction_type tx_type,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey, const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
RCTOutputEnoteProposal &return_enote_out,
RCTOutputEnoteProposal &output_enote_out); RCTOutputEnoteProposal &output_enote_out);
/** /**
* brief: get_output_proposal_internal_v1 - convert the carrot proposal to an output proposal (internal) * brief: get_output_proposal_internal_v1 - convert the carrot proposal to an output proposal (internal)
+13 -3
View File
@@ -110,7 +110,7 @@ CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index
return addr; return addr;
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const
{ {
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> res; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> res;
for (const auto &p : subaddress_map) for (const auto &p : subaddress_map)
@@ -120,10 +120,15 @@ std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_
return res; return res;
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() { const std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() const {
return subaddress_map; return subaddress_map;
} }
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
const std::unordered_map<crypto::public_key, return_output_info_t>&
carrot_and_legacy_account::get_return_output_map_ref() const {
return return_output_map;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_extended &subaddress_index, void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_extended &subaddress_index,
crypto::secret_key &address_privkey_g_out, crypto::secret_key &address_privkey_g_out,
crypto::secret_key &address_privkey_t_out, crypto::secret_key &address_privkey_t_out,
@@ -345,7 +350,12 @@ void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<cry
for (const auto &p : subaddress_map_cn) for (const auto &p : subaddress_map_cn)
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}}); subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
} }
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
{
for (const auto &p : roi_map)
return_output_map.insert({p.first, p.second});
}
//---------------------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------------------
AddressDeriveType carrot_and_legacy_account::resolve_derive_type(const AddressDeriveType derive_type) const AddressDeriveType carrot_and_legacy_account::resolve_derive_type(const AddressDeriveType derive_type) const
{ {
+30 -2
View File
@@ -43,6 +43,28 @@ static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
namespace carrot namespace carrot
{ {
struct return_output_info_t {
input_context_t input_context;
crypto::public_key address_spend_pubkey;
crypto::key_image key_image;
crypto::secret_key x;
crypto::secret_key y;
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &address_spend_pubkey,
const crypto::key_image &key_image,
const crypto::secret_key &x,
const crypto::secret_key &y):
input_context(input_context),
address_spend_pubkey(address_spend_pubkey),
key_image(key_image),
x(x),
y(y) {}
};
class carrot_and_legacy_account : public cryptonote::account_base class carrot_and_legacy_account : public cryptonote::account_base
{ {
public: public:
@@ -68,8 +90,9 @@ namespace carrot
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const; CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const; const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const;
std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref(); const std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref() const;
const std::unordered_map<crypto::public_key, return_output_info_t>& get_return_output_map_ref() const;
// brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T) // brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T)
void opening_for_subaddress(const subaddress_index_extended &subaddress_index, void opening_for_subaddress(const subaddress_index_extended &subaddress_index,
@@ -108,10 +131,15 @@ namespace carrot
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot); void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map); void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map);
void insert_return_output_info(
const std::unordered_map<crypto::public_key, return_output_info_t>& input_context_map
);
AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const; AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const;
private: private:
std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map; std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map;
// Kr -> return_output_info
std::unordered_map<crypto::public_key, return_output_info_t> return_output_map;
}; };
} }
+21 -3
View File
@@ -198,7 +198,9 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
const std::vector<cryptonote::tx_source_entry> &sources, const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee, const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type, const cryptonote::transaction_type tx_type,
const std::vector<uint8_t> change_masks, const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const carrot::RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id) const encrypted_payment_id_t encrypted_payment_id)
{ {
const size_t nins = key_images.size(); const size_t nins = key_images.size();
@@ -208,11 +210,15 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
cryptonote::transaction tx; cryptonote::transaction tx;
tx.pruned = true; tx.pruned = true;
tx.version = TRANSACTION_VERSION_N_OUTS;
tx.unlock_time = 0; tx.unlock_time = 0;
tx.source_asset_type = "SAL1"; tx.source_asset_type = "SAL1";
tx.destination_asset_type = "SAL1"; tx.destination_asset_type = "SAL1";
tx.type = tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type; tx.version = TRANSACTION_VERSION_CARROT;
tx.type =
tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type;
tx.amount_burnt = (
tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN
) ? tx_amount_burnt : 0;
tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end()); tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end());
tx.vin.reserve(nins); tx.vin.reserve(nins);
tx.vout.reserve(nouts); tx.vout.reserve(nouts);
@@ -259,10 +265,22 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment}); tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
//K_return //K_return
if (tx_type != cryptonote::transaction_type::STAKE) {
crypto::public_key K_return; crypto::public_key K_return;
memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key)); memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key));
tx.return_address_list.push_back(K_return); tx.return_address_list.push_back(K_return);
} }
}
// store the return pubkey for stake txs
if (tx_type == cryptonote::transaction_type::STAKE)
{
tx.protocol_tx_data.version = 1;
memcpy(tx.protocol_tx_data.return_address.data, return_enote.enote.onetime_address.data, sizeof(crypto::public_key));
memcpy(tx.protocol_tx_data.return_pubkey.data, return_enote.enote.enote_ephemeral_pubkey.data, sizeof(crypto::public_key));
tx.protocol_tx_data.return_view_tag = return_enote.enote.view_tag;
tx.protocol_tx_data.return_anchor_enc = return_enote.enote.anchor_enc;
}
//ephemeral pubkeys: D_e //ephemeral pubkeys: D_e
store_carrot_ephemeral_pubkeys_to_extra(enotes, tx.extra); store_carrot_ephemeral_pubkeys_to_extra(enotes, tx.extra);
+4 -1
View File
@@ -35,6 +35,7 @@
#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/tx_extra.h" #include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/cryptonote_tx_utils.h" #include "cryptonote_core/cryptonote_tx_utils.h"
#include "carrot_core/payment_proposal.h"
//third party headers //third party headers
@@ -105,7 +106,9 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
const std::vector<cryptonote::tx_source_entry> &sources, const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee, const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type, const cryptonote::transaction_type tx_type,
const std::vector<uint8_t> change_masks, const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const RCTOutputEnoteProposal &return_enote,
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 * brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
+4
View File
@@ -60,6 +60,7 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal
// derive enote proposals // derive enote proposals
size_t change_index; size_t change_index;
RCTOutputEnoteProposal return_enote_out;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices; std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals(tx_proposal.normal_payment_proposals, get_output_enote_proposals(tx_proposal.normal_payment_proposals,
selfsend_payment_proposal_cores, selfsend_payment_proposal_cores,
@@ -68,6 +69,7 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal
k_view_dev, k_view_dev,
tx_proposal.key_images_sorted.at(0), tx_proposal.key_images_sorted.at(0),
output_enote_proposals_out, output_enote_proposals_out,
return_enote_out,
encrypted_payment_id_out, encrypted_payment_id_out,
tx_proposal.tx_type, tx_proposal.tx_type,
change_index, change_index,
@@ -127,7 +129,9 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
tx_proposal.sources, tx_proposal.sources,
tx_proposal.fee, tx_proposal.fee,
cryptonote::transaction_type::TRANSFER, cryptonote::transaction_type::TRANSFER,
0, // tx_amount_burnt
{}, // change_masks {}, // change_masks
{}, // return_enote
encrypted_payment_id); encrypted_payment_id);
// add extra payload and sort // add extra payload and sort
+2
View File
@@ -86,6 +86,8 @@ struct CarrotTransactionProposalV1
rct::xmr_amount fee; rct::xmr_amount fee;
/// transaction type, e.g. /// transaction type, e.g.
cryptonote::transaction_type tx_type; cryptonote::transaction_type tx_type;
/// how much money tx burns
rct::xmr_amount amount_burnt;
/// This field is truly "extra". It should contain only tx.extra fields that aren't present in a /// This field is truly "extra". It should contain only tx.extra fields that aren't present in a
/// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs /// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs
+22 -1
View File
@@ -217,8 +217,19 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
input_amount_sum -= normal_payment_proposal.amount; input_amount_sum -= normal_payment_proposal.amount;
for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals) for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
input_amount_sum -= selfsend_payment_proposal.proposal.amount; input_amount_sum -= selfsend_payment_proposal.proposal.amount;
if (tx_type != cryptonote::transaction_type::STAKE &&
tx_type != cryptonote::transaction_type::BURN)
{
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0, CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance"); "make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
} else {
tx_proposal_out.amount_burnt = input_amount_sum.convert_to<uint64_t>();
CHECK_AND_ASSERT_THROW_MES(tx_proposal_out.amount_burnt >= 0,
"make_carrot_transaction_proposal_v1: post-carved transaction burnt amount is negative: "
<< tx_proposal_out.amount_burnt);
input_amount_sum -= tx_proposal_out.amount_burnt;
}
// collect and sort key images // collect and sort key images
tx_proposal_out.key_images_sorted.reserve(selected_inputs.size()); tx_proposal_out.key_images_sorted.reserve(selected_inputs.size());
@@ -273,7 +284,8 @@ void make_carrot_transaction_proposal_v1_transfer(
carve_fees_and_balance_func_t carve_fees_and_balance = carve_fees_and_balance_func_t carve_fees_and_balance =
[ [
&subtractable_normal_payment_proposals, &subtractable_normal_payment_proposals,
&subtractable_selfsend_payment_proposals &subtractable_selfsend_payment_proposals,
&tx_type
] ]
( (
const boost::multiprecision::uint128_t &input_sum_amount, const boost::multiprecision::uint128_t &input_sum_amount,
@@ -319,6 +331,15 @@ void make_carrot_transaction_proposal_v1_transfer(
selfsend_payment_proposals.back().proposal.amount = selfsend_payment_proposals.back().proposal.amount =
boost::numeric_cast<rct::xmr_amount>(implicit_change_amount); boost::numeric_cast<rct::xmr_amount>(implicit_change_amount);
// remove the self send payment we have made to ourself now that we have our change payment.
if (tx_type == cryptonote::transaction_type::STAKE ||
tx_type == cryptonote::transaction_type::BURN)
{
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
}
// deduct an even fee amount from all subtractable outputs // deduct an even fee amount from all subtractable outputs
const size_t num_subtractble_normal = subtractable_normal_payment_proposals.size(); const size_t num_subtractble_normal = subtractable_normal_payment_proposals.size();
const size_t num_subtractable_selfsend = subtractable_selfsend_payment_proposals.size(); const size_t num_subtractable_selfsend = subtractable_selfsend_payment_proposals.size();
+32
View File
@@ -197,6 +197,23 @@ namespace cryptonote
}; };
class protocol_tx_data_t {
public:
uint8_t version;
crypto::public_key return_address;
crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(return_address)
FIELD(return_pubkey)
FIELD(return_view_tag)
FIELD(return_anchor_enc)
END_SERIALIZE()
};
class transaction_prefix class transaction_prefix
{ {
@@ -227,6 +244,8 @@ namespace cryptonote
// Slippage limit // Slippage limit
uint64_t amount_slippage_limit; uint64_t amount_slippage_limit;
protocol_tx_data_t protocol_tx_data;
BEGIN_SERIALIZE() BEGIN_SERIALIZE()
VARINT_FIELD(version) VARINT_FIELD(version)
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false; if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
@@ -243,10 +262,16 @@ namespace cryptonote
if (type == cryptonote::transaction_type::TRANSFER && version >= TRANSACTION_VERSION_N_OUTS) { if (type == cryptonote::transaction_type::TRANSFER && version >= TRANSACTION_VERSION_N_OUTS) {
FIELD(return_address_list) FIELD(return_address_list)
FIELD(return_address_change_mask) FIELD(return_address_change_mask)
} else {
if (type == cryptonote::transaction_type::STAKE &&
version >= TRANSACTION_VERSION_CARROT)
{
FIELD(protocol_tx_data)
} else { } else {
FIELD(return_address) FIELD(return_address)
FIELD(return_pubkey) FIELD(return_pubkey)
} }
}
FIELD(source_asset_type) FIELD(source_asset_type)
FIELD(destination_asset_type) FIELD(destination_asset_type)
VARINT_FIELD(amount_slippage_limit) VARINT_FIELD(amount_slippage_limit)
@@ -268,6 +293,10 @@ namespace cryptonote
return_address_list.clear(); return_address_list.clear();
return_address_change_mask.clear(); return_address_change_mask.clear();
return_pubkey = crypto::null_pkey; return_pubkey = crypto::null_pkey;
protocol_tx_data.return_address = crypto::null_pkey;
protocol_tx_data.return_pubkey = crypto::null_pkey;
protocol_tx_data.return_view_tag = {};
protocol_tx_data.return_anchor_enc = {};
source_asset_type.clear(); source_asset_type.clear();
destination_asset_type.clear(); destination_asset_type.clear();
amount_burnt = 0; amount_burnt = 0;
@@ -754,6 +783,7 @@ VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1);
VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2); VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3); VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4); VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4);
VARIANT_TAG(binary_archive, cryptonote::protocol_tx_data_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
@@ -766,6 +796,7 @@ VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash");
VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key"); VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key"); VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key");
VARIANT_TAG(json_archive, cryptonote::txout_to_carrot_v1, "carrot_v1"); VARIANT_TAG(json_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
VARIANT_TAG(json_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
VARIANT_TAG(json_archive, cryptonote::transaction, "tx"); VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
VARIANT_TAG(json_archive, cryptonote::block, "block"); VARIANT_TAG(json_archive, cryptonote::block, "block");
@@ -778,5 +809,6 @@ VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash");
VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key"); VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key"); VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key");
VARIANT_TAG(debug_archive, cryptonote::txout_to_carrot_v1, "carrot_v1"); VARIANT_TAG(debug_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
VARIANT_TAG(debug_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx"); VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
VARIANT_TAG(debug_archive, cryptonote::block, "block"); VARIANT_TAG(debug_archive, cryptonote::block, "block");
@@ -166,6 +166,15 @@ namespace boost
a & x.target; a & x.target;
} }
template <class Archive>
inline void serialize(Archive &a, cryptonote::protocol_tx_data_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.return_address;
a & x.return_pubkey;
a & x.return_view_tag;
a & x.return_anchor_enc;
}
template <class Archive> template <class Archive>
inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver) inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
@@ -182,10 +191,16 @@ namespace boost
if (x.type == cryptonote::transaction_type::TRANSFER && x.version >= TRANSACTION_VERSION_N_OUTS) { if (x.type == cryptonote::transaction_type::TRANSFER && x.version >= TRANSACTION_VERSION_N_OUTS) {
a & x.return_address_list; a & x.return_address_list;
a & x.return_address_change_mask; a & x.return_address_change_mask;
} else {
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
a & x.protocol_tx_data;
} else { } else {
a & x.return_address; a & x.return_address;
a & x.return_pubkey; a & x.return_pubkey;
} }
}
a & x.source_asset_type; a & x.source_asset_type;
a & x.destination_asset_type; a & x.destination_asset_type;
a & x.amount_slippage_limit; a & x.amount_slippage_limit;
@@ -208,10 +223,16 @@ namespace boost
if (x.type == cryptonote::transaction_type::TRANSFER && x.version >= TRANSACTION_VERSION_N_OUTS) { if (x.type == cryptonote::transaction_type::TRANSFER && x.version >= TRANSACTION_VERSION_N_OUTS) {
a & x.return_address_list; a & x.return_address_list;
a & x.return_address_change_mask; a & x.return_address_change_mask;
} else {
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
a & x.protocol_tx_data;
} else { } else {
a & x.return_address; a & x.return_address;
a & x.return_pubkey; a & x.return_pubkey;
} }
}
a & x.source_asset_type; a & x.source_asset_type;
a & x.destination_asset_type; a & x.destination_asset_type;
a & x.amount_slippage_limit; a & x.amount_slippage_limit;
+2 -1
View File
@@ -44,9 +44,10 @@
#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000 #define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60 #define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
#define CURRENT_TRANSACTION_VERSION 3 #define CURRENT_TRANSACTION_VERSION 4
#define TRANSACTION_VERSION_2_OUTS 2 #define TRANSACTION_VERSION_2_OUTS 2
#define TRANSACTION_VERSION_N_OUTS 3 #define TRANSACTION_VERSION_N_OUTS 3
#define TRANSACTION_VERSION_CARROT 4
#define CURRENT_BLOCK_MAJOR_VERSION 1 #define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 1 #define CURRENT_BLOCK_MINOR_VERSION 1
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 #define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
+141 -11
View File
@@ -1563,6 +1563,7 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
// Get the staking data for the block that matured this time // Get the staking data for the block that matured this time
cryptonote::yield_block_info ybi_matured; cryptonote::yield_block_info ybi_matured;
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts; std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
uint64_t matured_height = height - stake_lock_period - 1; uint64_t matured_height = height - stake_lock_period - 1;
bool ok = get_ybi_entry(matured_height, ybi_matured); bool ok = get_ybi_entry(matured_height, ybi_matured);
if (!ok) { if (!ok) {
@@ -1572,11 +1573,18 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
LOG_PRINT_L1("Block at height: " << height << " - no yield payouts due - skipping"); LOG_PRINT_L1("Block at height: " << height << " - no yield payouts due - skipping");
} else { } else {
// Iterate over the cached data for block yield, calculating the yield payouts due // Iterate over the cached data for block yield, calculating the yield payouts due
if (hf_version >= HF_VERSION_CARROT) {
if (!calculate_yield_payouts(matured_height, carrot_yield_payouts)) {
LOG_ERROR("Block at height: " << height << " - Failed to obtain carrot yield payout information - aborting");
return false;
}
} else {
if (!calculate_yield_payouts(matured_height, yield_payouts)) { if (!calculate_yield_payouts(matured_height, yield_payouts)) {
LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting"); LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting");
return false; return false;
} }
} }
}
// Get the audit data for the block that matures at this height // Get the audit data for the block that matures at this height
std::vector<std::pair<yield_tx_info, uint64_t>> audit_payouts; std::vector<std::pair<yield_tx_info, uint64_t>> audit_payouts;
@@ -1607,7 +1615,36 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
} }
// Check we have the correct number of entries // Check we have the correct number of entries
CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting"); CHECK_AND_ASSERT_MES(
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size(),
false, "Invalid number of outputs in protocol_tx - aborting"
);
if (hf_version >= HF_VERSION_CARROT) {
// TODO: add other verifications for carrot yield payouts
size_t output_idx = 0;
for (auto it = carrot_yield_payouts.begin(); it != carrot_yield_payouts.end(); it++, output_idx++) {
// Verify the output key
crypto::public_key out_key;
cryptonote::get_output_public_key(b.protocol_tx.vout[output_idx], out_key);
CHECK_AND_ASSERT_MES(out_key == it->first.return_address, false, "Incorrect output key detected in protocol_tx");
// Verify the output amount
uint64_t expected_amount = it->second;
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == expected_amount, false, "Incorrect output amount detected in protocol_tx. expected_amount: " << expected_amount);
// Verify the output asset type
std::string out_asset_type;
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
uint8_t hf_yield = m_hardfork->get_ideal_version(it->first.block_height);
if (hf_yield >= HF_VERSION_SALVIUM_ONE_PROOFS)
CHECK_AND_ASSERT_MES(out_asset_type == "SAL1", false, "Incorrect output asset_type (!= SAL1) detected in protocol_tx");
else
CHECK_AND_ASSERT_MES(out_asset_type == "SAL", false, "Incorrect output asset_type (!= SAL) detected in protocol_tx");
}
return true;
}
// Merge the yield and audit payouts into an iterable vector // Merge the yield and audit payouts into an iterable vector
std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts}; std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts};
@@ -1917,15 +1954,45 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
// Iterate over the cached data for block yield, calculating the yield payouts due // Iterate over the cached data for block yield, calculating the yield payouts due
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts; std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
if (b.major_version >= HF_VERSION_CARROT) {
if (!calculate_yield_payouts(start_height, carrot_yield_payouts)) {
LOG_ERROR("Failed to obtain yield payout information - aborting");
return false;
}
} else {
if (!calculate_yield_payouts(start_height, yield_payouts)) { if (!calculate_yield_payouts(start_height, yield_payouts)) {
LOG_ERROR("Failed to obtain yield payout information - aborting"); LOG_ERROR("Failed to obtain yield payout information - aborting");
return false; return false;
} }
}
// Work out what the asset_type should be based on height of submission // Work out what the asset_type should be based on height of submission
uint8_t hf_submitted = m_hardfork->get_ideal_version(start_height); uint8_t hf_submitted = m_hardfork->get_ideal_version(start_height);
// Create the protocol_metadata entries here // Create the protocol_metadata entries here
if (b.major_version >= HF_VERSION_CARROT) {
for (const auto& yield_entry: carrot_yield_payouts) {
cryptonote::protocol_data_entry entry;
entry.amount_burnt = yield_entry.second;
entry.amount_minted = 0;
entry.amount_slippage_limit = 0;
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
entry.source_asset = "SAL1";
entry.destination_asset = "SAL1";
} else {
entry.source_asset = "SAL";
entry.destination_asset = "SAL";
}
entry.return_address = yield_entry.first.return_address;
entry.type = cryptonote::transaction_type::STAKE;
entry.return_pubkey = yield_entry.first.return_pubkey;
entry.origin_height = start_height;
entry.return_view_tag = yield_entry.first.return_view_tag;
entry.return_anchor_enc = yield_entry.first.return_anchor_enc;
protocol_entries.push_back(entry);
}
} else {
for (const auto& yield_entry: yield_payouts) { for (const auto& yield_entry: yield_payouts) {
cryptonote::protocol_data_entry entry; cryptonote::protocol_data_entry entry;
entry.amount_burnt = yield_entry.second; entry.amount_burnt = yield_entry.second;
@@ -1946,6 +2013,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
protocol_entries.push_back(entry); protocol_entries.push_back(entry);
} }
} }
}
// Get the audit data for the block that matures at this height // Get the audit data for the block that matures at this height
std::vector<std::pair<yield_tx_info, uint64_t>> audit_payouts; std::vector<std::pair<yield_tx_info, uint64_t>> audit_payouts;
@@ -3700,7 +3768,7 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
} }
// After v2 allow N-out TXs for TRANSFER ONLY // After v2 allow N-out TXs for TRANSFER ONLY
if (hf_version >= HF_VERSION_ENABLE_N_OUTS) { if (hf_version >= HF_VERSION_ENABLE_N_OUTS && hf_version < HF_VERSION_CARROT) {
if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) { if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) {
MERROR("N-out TXs are only permitted for TRANSFER TX type"); MERROR("N-out TXs are only permitted for TRANSFER TX type");
tvc.m_version_mismatch = true; tvc.m_version_mismatch = true;
@@ -3708,6 +3776,14 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
} }
} }
if (hf_version >= HF_VERSION_CARROT) {
if (tx.version != TRANSACTION_VERSION_CARROT) {
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_CARROT));
tvc.m_version_mismatch = true;
return false;
}
}
// Make sure CONVERT TXs are disabled until we are ready - belt and braces! // Make sure CONVERT TXs are disabled until we are ready - belt and braces!
if (hf_version < HF_VERSION_ENABLE_CONVERT) { if (hf_version < HF_VERSION_ENABLE_CONVERT) {
if (tx.type == cryptonote::transaction_type::CONVERT) { if (tx.type == cryptonote::transaction_type::CONVERT) {
@@ -3751,7 +3827,7 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys, const uint8_t &hf_version) bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys, const uint8_t &hf_version)
{ {
PERF_TIMER(expand_transaction_2); PERF_TIMER(expand_transaction_2);
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3, false, "Transaction version is not 2/3"); CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4, false, "Transaction version is not 2/3/4");
rct::rctSig &rv = tx.rct_signatures; rct::rctSig &rv = tx.rct_signatures;
@@ -3950,14 +4026,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false; return false;
} }
// min/max tx version based on HF, and we accept v1 txes if having a non mixable
const size_t max_tx_version = (hf_version >= HF_VERSION_ENABLE_N_OUTS) ? TRANSACTION_VERSION_N_OUTS : TRANSACTION_VERSION_2_OUTS;
if (tx.version > max_tx_version)
{
MERROR_VER("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version);
tvc.m_verifivation_failed = true;
return false;
}
const size_t min_tx_version = (n_unmixable > 0 ? 1 : 2); const size_t min_tx_version = (n_unmixable > 0 ? 1 : 2);
if (tx.version < min_tx_version) if (tx.version < min_tx_version)
{ {
@@ -4591,6 +4659,68 @@ bool Blockchain::get_abi_entry(const uint64_t height, cryptonote::audit_block_in
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info_carrot, uint64_t>>& yield_container)
{
LOG_PRINT_L3("Blockchain::" << __func__);
// Clear the yield payout amounts
yield_container.clear();
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info_carrot> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_carrot_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
LOG_ERROR("calculate_yield_payouts() called, but no yield TXs found at height " << start_height << " - aborting");
return false;
}
// Build a blacklist of staking TXs _not_ to pay out for
const std::set<std::string> txs_blacklist = {};
// Get the YBI information for the 21,600 blocks that the matured TX(s), we can calculate yield
for (const auto& entry: yield_entries) {
// Check to see if this entry is in the blacklist
if (txs_blacklist.find(epee::string_tools::pod_to_hex(entry.tx_hash)) != txs_blacklist.end()) {
LOG_ERROR("calculate_yield_payouts() found blacklisted TX at height " << start_height << " - skipping payout");
continue;
}
yield_container.emplace_back(std::make_pair(entry, entry.locked_coins));
}
// Iterate over the cached yield_block_info data
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
for (uint64_t idx = start_height+1; idx <= start_height + yield_lock_period; ++idx) {
// Get the next block
if (m_yield_block_info_cache.count(idx) == 0) {
LOG_ERROR("failed to locate yield information for block height " << idx <<" - aborting");
return false;
}
yield_block_info ybi = m_yield_block_info_cache[idx];
if (ybi.slippage_total_this_block == 0) continue;
if (ybi.locked_coins_tally == 0) continue;
boost::multiprecision::int128_t slippage_128 = ybi.slippage_total_this_block;
// Get the total number of coins locked at this height
boost::multiprecision::int128_t locked_total_128 = ybi.locked_coins_tally;
// Iterate over the yield_container, adding each proportion of the yield
for (auto& entry: yield_container) {
boost::multiprecision::int128_t locked_coins_128 = entry.first.locked_coins;
boost::multiprecision::int128_t yield_128 = (slippage_128 * locked_coins_128) / locked_total_128;
entry.second += yield_128.convert_to<uint64_t>();
}
}
// Return success to caller
return true;
}
//------------------------------------------------------------------
//------------------------------------------------------------------
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_container) bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_container)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
+7
View File
@@ -1175,6 +1175,13 @@ namespace cryptonote
*/ */
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_payouts); bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_payouts);
/**
* calculate the yield payouts
*
* @return TRUE if the payouts were calculated successfully, FALSE otherwise
*/
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info_carrot, uint64_t>>& yield_payouts);
/** /**
* @brief get the ABI entry for a particular height from the cache * @brief get the ABI entry for a particular height from the cache
* *
+3 -1
View File
@@ -843,7 +843,9 @@ namespace cryptonote
bad_semantics_txes_lock.unlock(); bad_semantics_txes_lock.unlock();
uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version(); uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
const size_t max_tx_version = (hf_version >= HF_VERSION_ENABLE_N_OUTS) ? TRANSACTION_VERSION_N_OUTS : TRANSACTION_VERSION_2_OUTS; const size_t max_tx_version = hf_version >= HF_VERSION_CARROT ? TRANSACTION_VERSION_CARROT :
hf_version >= HF_VERSION_ENABLE_N_OUTS ? TRANSACTION_VERSION_N_OUTS :
TRANSACTION_VERSION_2_OUTS;
if (tx.version == 0 || tx.version > max_tx_version) if (tx.version == 0 || tx.version > max_tx_version)
{ {
// v2 is the latest one we know // v2 is the latest one we know
+7 -21
View File
@@ -362,29 +362,15 @@ namespace cryptonote
// Iterate over the protocol_data we received, creating an enote for each entry // Iterate over the protocol_data we received, creating an enote for each entry
for (auto const& entry: protocol_data) { for (auto const& entry: protocol_data) {
carrot::CarrotDestinationV1 destination;
carrot::make_carrot_main_address_v1(entry.P_change,
entry.return_address,
destination);
CHECK_AND_ASSERT_THROW_MES(!destination.is_subaddress,
"construct_protocol_tx: subaddress are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(destination.payment_id == carrot::null_payment_id,
"construct_protocol_tx: integrated addresses are not allowed in miner transactions");
LOG_PRINT_L2(((entry.type == cryptonote::transaction_type::STAKE) ? "Yield TX payout submitted " : "Audit TX payout submitted ") << entry.amount_burnt << entry.source_asset);
const carrot::CarrotPaymentProposalV1 payment_proposal{
.destination = destination,
.amount = entry.amount_burnt,
.asset_type = "SAL1",
.randomness = carrot::gen_janus_anchor()
};
// Build the proposal // Build the proposal
carrot::CarrotCoinbaseEnoteV1 e; carrot::CarrotCoinbaseEnoteV1 e;
get_coinbase_output_proposal_v1(payment_proposal, height, e); e.onetime_address = entry.return_address;
e.amount = entry.amount_burnt;
e.asset_type = entry.destination_asset;
e.view_tag = entry.return_view_tag;
e.anchor_enc = entry.return_anchor_enc;
e.block_index = height;
memcpy(e.enote_ephemeral_pubkey.data, entry.return_pubkey.data, sizeof(crypto::public_key));
enotes.push_back(e); enotes.push_back(e);
} }
@@ -68,6 +68,8 @@ namespace cryptonote
uint8_t type; uint8_t type;
crypto::public_key P_change; crypto::public_key P_change;
crypto::public_key return_pubkey; crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
uint64_t origin_height; uint64_t origin_height;
}; };
@@ -187,7 +187,7 @@ bool ver_rct_non_semantics_simple_cached
// mixring. Future versions of the protocol may differ in this regard, but if this assumptions // mixring. Future versions of the protocol may differ in this regard, but if this assumptions
// holds true in the future, enable the verification hash by modifying the `untested_tx` // holds true in the future, enable the verification hash by modifying the `untested_tx`
// condition below. // condition below.
const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeSalviumOne; const bool untested_tx = tx.version > 4 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here."); VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here.");
// Don't cache older (or newer) rctSig types // Don't cache older (or newer) rctSig types
+39
View File
@@ -276,10 +276,16 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
if (tx.type == cryptonote::transaction_type::TRANSFER && tx.version >= TRANSACTION_VERSION_N_OUTS) { if (tx.type == cryptonote::transaction_type::TRANSFER && tx.version >= TRANSACTION_VERSION_N_OUTS) {
INSERT_INTO_JSON_OBJECT(dest, return_address_list, tx.return_address_list); INSERT_INTO_JSON_OBJECT(dest, return_address_list, tx.return_address_list);
INSERT_INTO_JSON_OBJECT(dest, return_address_change_mask, tx.return_address_change_mask); INSERT_INTO_JSON_OBJECT(dest, return_address_change_mask, tx.return_address_change_mask);
} else {
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
INSERT_INTO_JSON_OBJECT(dest, protocol_tx_data, tx.protocol_tx_data);
} else { } else {
INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address); INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, tx.return_pubkey); INSERT_INTO_JSON_OBJECT(dest, return_pubkey, tx.return_pubkey);
} }
}
INSERT_INTO_JSON_OBJECT(dest, source_asset_type, tx.source_asset_type); INSERT_INTO_JSON_OBJECT(dest, source_asset_type, tx.source_asset_type);
INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type); INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type);
INSERT_INTO_JSON_OBJECT(dest, amount_slippage_limit, tx.amount_slippage_limit); INSERT_INTO_JSON_OBJECT(dest, amount_slippage_limit, tx.amount_slippage_limit);
@@ -319,10 +325,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
if (tx.type == cryptonote::transaction_type::TRANSFER && tx.version >= TRANSACTION_VERSION_N_OUTS) { if (tx.type == cryptonote::transaction_type::TRANSFER && tx.version >= TRANSACTION_VERSION_N_OUTS) {
GET_FROM_JSON_OBJECT(val, tx.return_address_list, return_address_list); GET_FROM_JSON_OBJECT(val, tx.return_address_list, return_address_list);
GET_FROM_JSON_OBJECT(val, tx.return_address_change_mask, return_address_change_mask); GET_FROM_JSON_OBJECT(val, tx.return_address_change_mask, return_address_change_mask);
} else {
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
GET_FROM_JSON_OBJECT(val, tx.protocol_tx_data, protocol_tx_data);
} else { } else {
GET_FROM_JSON_OBJECT(val, tx.return_address, return_address); GET_FROM_JSON_OBJECT(val, tx.return_address, return_address);
GET_FROM_JSON_OBJECT(val, tx.return_pubkey, return_pubkey); GET_FROM_JSON_OBJECT(val, tx.return_pubkey, return_pubkey);
} }
}
GET_FROM_JSON_OBJECT(val, tx.source_asset_type, source_asset_type); GET_FROM_JSON_OBJECT(val, tx.source_asset_type, source_asset_type);
GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type); GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type);
GET_FROM_JSON_OBJECT(val, tx.amount_slippage_limit, amount_slippage_limit); GET_FROM_JSON_OBJECT(val, tx.amount_slippage_limit, amount_slippage_limit);
@@ -506,6 +518,33 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
} }
} }
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, ptd.version);
INSERT_INTO_JSON_OBJECT(dest, return_address, ptd.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, ptd.return_pubkey);
INSERT_INTO_JSON_OBJECT(dest, return_view_tag, ptd.return_view_tag);
INSERT_INTO_JSON_OBJECT(dest, return_anchor_enc, ptd.return_anchor_enc);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, ptd.version, version);
GET_FROM_JSON_OBJECT(val, ptd.return_address, return_address);
GET_FROM_JSON_OBJECT(val, ptd.return_pubkey, return_pubkey);
GET_FROM_JSON_OBJECT(val, ptd.return_view_tag, return_view_tag);
GET_FROM_JSON_OBJECT(val, ptd.return_anchor_enc, return_anchor_enc);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin) void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin)
{ {
dest.StartObject(); dest.StartObject();
+3
View File
@@ -218,6 +218,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin); void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin); void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd);
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_target_v& txout); void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_target_v& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout); void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
+149 -4
View File
@@ -255,26 +255,123 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
.amount_blinding_factor = amount_blinding_factor, .amount_blinding_factor = amount_blinding_factor,
.asset_type = enote.asset_type, .asset_type = enote.asset_type,
.main_tx_pubkey_index = main_deriv_idx, .main_tx_pubkey_index = main_deriv_idx,
.is_carrot = false .is_carrot = false,
.is_return = false
}; };
} }
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
static bool scan_return_output(
const carrot::CarrotCoinbaseEnoteV1 &enote,
carrot::carrot_and_legacy_account &account,
crypto::public_key &address_spend_pubkey_out
) {
const auto &return_output_map = account.get_return_output_map_ref();
CHECK_AND_ASSERT_MES(return_output_map.count(enote.onetime_address), false, "return output not found");
const auto &return_output = return_output_map.at(enote.onetime_address);
// 1. make k_return
crypto::secret_key k_return;
account.k_view_incoming_dev.make_internal_return_privkey(return_output.input_context, return_output.address_spend_pubkey, k_return);
// 2. compute K_return' = k_return * G
crypto::public_key K_return;
crypto::secret_key_to_public_key(k_return, K_return);
// 3. ssr
mx25519_pubkey shared_secret_return_unctx;
crypto::hash shared_secret_return;
carrot::make_carrot_uncontextualized_shared_key_receiver(k_return, enote.enote_ephemeral_pubkey, shared_secret_return_unctx);
carrot::make_carrot_sender_receiver_secret(
shared_secret_return_unctx.data,
enote.enote_ephemeral_pubkey,
return_output.input_context,
shared_secret_return
);
// 4. verify the view_tag
CHECK_AND_ASSERT_MES(
carrot::test_carrot_view_tag(
shared_secret_return_unctx.data,
return_output.input_context,
enote.onetime_address,
enote.view_tag
),
false,
"view tag verification failed for carrot coinbase enote"
);
// 5. compute anchor_return
carrot::janus_anchor_t recovered_anchor_return =
carrot::decrypt_carrot_anchor(enote.anchor_enc, shared_secret_return, enote.onetime_address);
// 6. compute d_e'
crypto::secret_key recovered_ephemeral_privkey_return;
carrot::make_carrot_enote_ephemeral_privkey(
recovered_anchor_return,
return_output.input_context,
return_output.address_spend_pubkey,
carrot::null_payment_id,
recovered_ephemeral_privkey_return
);
// 7. compute D_e'
mx25519_pubkey recovered_ephemeral_pubkey_return;
carrot::make_carrot_enote_ephemeral_pubkey(
recovered_ephemeral_privkey_return,
return_output.address_spend_pubkey,
false,
recovered_ephemeral_pubkey_return
);
// 8. verify the enote ephemeral pubkey
CHECK_AND_ASSERT_MES(
memcmp(recovered_ephemeral_pubkey_return.data, enote.enote_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0,
false,
"carrot coinbase enote protection verification failed"
);
address_spend_pubkey_out = return_output.address_spend_pubkey;
return true;
}
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_coinbase_enote( static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_coinbase_enote(
const carrot::CarrotCoinbaseEnoteV1 &enote, const carrot::CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx, const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &main_address_spend_pubkey) const crypto::public_key &main_address_spend_pubkey,
carrot::carrot_and_legacy_account &account)
{ {
enote_view_incoming_scan_info_t res; enote_view_incoming_scan_info_t res;
bool found_in_return = false;
if (!carrot::try_scan_carrot_coinbase_enote_receiver(enote, if (!carrot::try_scan_carrot_coinbase_enote_receiver(enote,
s_sender_receiver_unctx, s_sender_receiver_unctx,
main_address_spend_pubkey, main_address_spend_pubkey,
res.sender_extension_g, res.sender_extension_g,
res.sender_extension_t)) res.sender_extension_t))
{
// check for known return addresses
const auto &subaddress_map = account.get_subaddress_map_ref();
if (subaddress_map.find(enote.onetime_address) == subaddress_map.end())
return std::nullopt; return std::nullopt;
found_in_return = true;
}
if (found_in_return) {
// scan the return output
crypto::public_key address_spend_pubkey;
if (!scan_return_output(enote, account, address_spend_pubkey))
return std::nullopt;
res.address_spend_pubkey = address_spend_pubkey;
res.return_address = enote.onetime_address;
res.is_return = true;
} else {
// we received a coinbase enote
res.address_spend_pubkey = main_address_spend_pubkey; res.address_spend_pubkey = main_address_spend_pubkey;
res.is_return = false;
}
res.payment_id = crypto::null_hash; res.payment_id = crypto::null_hash;
res.subaddr_index = carrot::subaddress_index_extended{{0, 0}}; res.subaddr_index = carrot::subaddress_index_extended{{0, 0}};
res.amount = enote.amount; res.amount = enote.amount;
@@ -326,6 +423,7 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
res.main_tx_pubkey_index = 0; res.main_tx_pubkey_index = 0;
res.asset_type = enote.asset_type; res.asset_type = enote.asset_type;
res.is_carrot = true; res.is_carrot = true;
res.is_return = false;
return res; return res;
} }
@@ -372,11 +470,50 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
memset(&res.payment_id, 0, sizeof(res.payment_id)); memset(&res.payment_id, 0, sizeof(res.payment_id));
memcpy(&res.payment_id, &payment_id, sizeof(carrot::payment_id_t)); memcpy(&res.payment_id, &payment_id, sizeof(carrot::payment_id_t));
// we received and output
// save the Kr = K_change + K_return to out subaddress map
// make k_return
crypto::secret_key k_return;
const carrot::input_context_t input_context = carrot::make_carrot_input_context(enote.tx_first_key_image);
k_view_dev.make_internal_return_privkey(input_context, enote.onetime_address, k_return);
// compute K_return = k_return * G
crypto::public_key K_return;
crypto::secret_key_to_public_key(k_return, K_return);
// compute K_r = K_return + K_o
crypto::public_key K_r = rct::rct2pk(rct::addKeys(rct::pk2rct(K_return), rct::pk2rct(enote.onetime_address)));
account.insert_subaddresses({{K_r, {{subaddr_index.index.major, subaddr_index.index.minor},
carrot::AddressDeriveType::Carrot, true}}});
// calculate the key image for the return output
crypto::secret_key sum_g;
sc_add(to_bytes(sum_g), to_bytes(res.sender_extension_g), to_bytes(k_return));
crypto::key_image key_image = account.derive_key_image(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
res.sender_extension_t,
K_r
);
crypto::secret_key x, y;
account.try_searching_for_opening_for_onetime_address(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
res.sender_extension_t,
x,
y
);
// save the input context & change output key
account.insert_return_output_info({{K_r, {input_context, enote.onetime_address, key_image, x, y}}});
res.subaddr_index = subaddr_index; res.subaddr_index = subaddr_index;
res.amount_blinding_factor = rct::sk2rct(amount_blinding_factor_sk); res.amount_blinding_factor = rct::sk2rct(amount_blinding_factor_sk);
res.main_tx_pubkey_index = 0; res.main_tx_pubkey_index = 0;
res.asset_type = enote.asset_type; res.asset_type = enote.asset_type;
res.is_carrot = true; res.is_carrot = true;
res.is_return = false;
return res; return res;
} }
@@ -516,7 +653,8 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
return view_incoming_scan_carrot_coinbase_enote(enote, return view_incoming_scan_carrot_coinbase_enote(enote,
s_sender_receiver_unctx, s_sender_receiver_unctx,
address.m_spend_public_key); address.m_spend_public_key,
account);
} }
std::optional<enote_view_incoming_scan_info_t> operator()(const carrot::CarrotEnoteV1 &enote) const std::optional<enote_view_incoming_scan_info_t> operator()(const carrot::CarrotEnoteV1 &enote) const
@@ -827,7 +965,7 @@ std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_t
// 3. do view-incoming scan for each output enotes // 3. do view-incoming scan for each output enotes
hw::device &hwdev = hw::get_device("default"); hw::device &hwdev = hw::get_device("default");
carrot::carrot_and_legacy_account dummy_account; carrot::carrot_and_legacy_account dummy_account;
dummy_account.get_subaddress_map_ref()[address.m_spend_public_key] = {{}}; dummy_account.insert_subaddresses({{address.m_spend_public_key, {{0, 0}, carrot::AddressDeriveType::Carrot, false}}});
for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index) for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index)
{ {
auto &enote_scan_info = res[local_output_index]; auto &enote_scan_info = res[local_output_index];
@@ -861,9 +999,16 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
const enote_view_incoming_scan_info_t &enote_scan_info, const enote_view_incoming_scan_info_t &enote_scan_info,
const carrot::carrot_and_legacy_account &acc) const carrot::carrot_and_legacy_account &acc)
{ {
// we skip the return output key image generation here to do it in process_new_scanned_transaction.
if (!enote_scan_info.subaddr_index) if (!enote_scan_info.subaddr_index)
return std::nullopt; return std::nullopt;
// if we have a carrot return enote, we return already derived key image
if (enote_scan_info.is_carrot && enote_scan_info.is_return)
{
return acc.get_return_output_map_ref().at(enote_scan_info.return_address).key_image;
}
// k^j_subext // k^j_subext
rct::key subaddress_extension; rct::key subaddress_extension;
if (enote_scan_info.subaddr_index->index.is_subaddress()) if (enote_scan_info.subaddr_index->index.is_subaddress())
+6
View File
@@ -78,6 +78,12 @@ struct enote_view_incoming_scan_info_t
// whether this output is to a carrot address or not // whether this output is to a carrot address or not
bool is_carrot; bool is_carrot;
// whether this output is a return output
bool is_return;
// Kr = K_return + K_o
crypto::public_key return_address;
}; };
struct PreCarrotEnote struct PreCarrotEnote
+29 -10
View File
@@ -812,6 +812,18 @@ bool get_address_openings_x_y(
crypto::secret_key &x_out, crypto::secret_key &x_out,
crypto::secret_key &y_out) crypto::secret_key &y_out)
{ {
// If the output is a return output, we can use the return output secret key
// to derive x and y directly.
const auto& return_output_map = w.get_account().get_return_output_map_ref();
if (return_output_map.find(rct::rct2pk(src.outputs[src.real_output].second.dest)) != return_output_map.end())
{
const auto &return_output = return_output_map.at(rct::rct2pk(src.outputs[src.real_output].second.dest));
x_out = return_output.x;
y_out = return_output.y;
return true;
}
const std::vector<crypto::public_key> v_pubkeys{src.real_out_tx_key}; const std::vector<crypto::public_key> v_pubkeys{src.real_out_tx_key};
const std::vector<crypto::public_key> v_pubkeys_empty{}; const std::vector<crypto::public_key> v_pubkeys_empty{};
const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys = (src.real_out_tx_key == crypto::null_pkey) ? epee::to_span(v_pubkeys_empty) : epee::to_span(v_pubkeys); const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys = (src.real_out_tx_key == crypto::null_pkey) ? epee::to_span(v_pubkeys_empty) : epee::to_span(v_pubkeys);
@@ -1003,6 +1015,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals; std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals;
carrot::encrypted_payment_id_t encrypted_payment_id; carrot::encrypted_payment_id_t encrypted_payment_id;
size_t change_index; size_t change_index;
carrot::RCTOutputEnoteProposal return_enote_out;
std::unordered_map<crypto::public_key, size_t> payments_indices; std::unordered_map<crypto::public_key, size_t> payments_indices;
carrot::get_output_enote_proposals(tx_proposal.normal_payment_proposals, carrot::get_output_enote_proposals(tx_proposal.normal_payment_proposals,
selfsend_payment_proposal_cores, selfsend_payment_proposal_cores,
@@ -1011,6 +1024,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
&addr_dev, &addr_dev,
tx_proposal.key_images_sorted.at(0), tx_proposal.key_images_sorted.at(0),
output_enote_proposals, output_enote_proposals,
return_enote_out,
encrypted_payment_id, encrypted_payment_id,
tx_proposal.tx_type, tx_proposal.tx_type,
change_index, change_index,
@@ -1054,7 +1068,9 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
tx_proposal.sources, tx_proposal.sources,
tx_proposal.fee, tx_proposal.fee,
tx_proposal.tx_type, tx_proposal.tx_type,
tx_proposal.amount_burnt,
change_masks, change_masks,
return_enote_out,
encrypted_payment_id); encrypted_payment_id);
// aliases // aliases
@@ -1216,16 +1232,24 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
//------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal, wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers, const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view, const carrot::carrot_and_legacy_account &account)
hw::device &hwdev)
{ {
const std::size_t n_inputs = tx_proposal.key_images_sorted.size(); const std::size_t n_inputs = tx_proposal.key_images_sorted.size();
const std::size_t n_outputs = tx_proposal.normal_payment_proposals.size() + const std::size_t n_outputs = tx_proposal.normal_payment_proposals.size() +
tx_proposal.selfsend_payment_proposals.size(); tx_proposal.selfsend_payment_proposals.size();
const bool shared_ephemeral_pubkey = n_outputs == 2; const bool shared_ephemeral_pubkey = n_outputs == 2;
CARROT_CHECK_AND_THROW(
tx_proposal.tx_type != cryptonote::transaction_type::UNSET,
carrot::missing_components,
"make_pending_carrot_tx: tx proposal has unset tx type"
);
CARROT_CHECK_AND_THROW(n_inputs >= 1, carrot::too_few_inputs, "carrot tx proposal missing inputs"); CARROT_CHECK_AND_THROW(n_inputs >= 1, carrot::too_few_inputs, "carrot tx proposal missing inputs");
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE || tx_proposal.tx_type == cryptonote::transaction_type::BURN) {
CARROT_CHECK_AND_THROW(n_outputs == 1, carrot::too_few_outputs, "carrot tx proposal doesn't have correct number of outputs");
} else {
CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs"); CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs");
}
const crypto::key_image &tx_first_key_image = tx_proposal.key_images_sorted.at(0); const crypto::key_image &tx_first_key_image = tx_proposal.key_images_sorted.at(0);
@@ -1249,16 +1273,13 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
key_images_string << ki; key_images_string << ki;
} }
//! @TODO: HW device
carrot::view_incoming_key_ram_borrowed_device k_view_dev(k_view);
// get order of payment proposals // get order of payment proposals
std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals; std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals;
carrot::encrypted_payment_id_t encrypted_payment_id; carrot::encrypted_payment_id_t encrypted_payment_id;
std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices; std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices;
carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal, carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal,
/*s_view_balance_dev=*/nullptr, /*s_view_balance_dev=*/nullptr,
&k_view_dev, &account.k_view_incoming_dev,
output_enote_proposals, output_enote_proposals,
encrypted_payment_id, encrypted_payment_id,
&sorted_payment_proposal_indices); &sorted_payment_proposal_indices);
@@ -1288,7 +1309,7 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
if (is_selfsend) if (is_selfsend)
{ {
dest = make_tx_destination_entry(tx_proposal.selfsend_payment_proposals.at(payment_idx.second), dest = make_tx_destination_entry(tx_proposal.selfsend_payment_proposals.at(payment_idx.second),
k_view_dev); account.k_view_incoming_dev);
ephemeral_privkeys.push_back(crypto::null_skey); ephemeral_privkeys.push_back(crypto::null_skey);
} }
else // !is_selfsend else // !is_selfsend
@@ -1348,11 +1369,9 @@ wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx(
const wallet2::transfer_container &transfers, const wallet2::transfer_container &transfers,
const wallet2 &w) const wallet2 &w)
{ {
const auto acc_keys = w.get_account().get_keys();
wallet2::pending_tx ptx = make_pending_carrot_tx(tx_proposal, wallet2::pending_tx ptx = make_pending_carrot_tx(tx_proposal,
transfers, transfers,
acc_keys.m_view_secret_key, w.get_account());
acc_keys.get_device());
ptx.tx = finalize_all_proofs_from_transfer_details( ptx.tx = finalize_all_proofs_from_transfer_details(
tx_proposal, tx_proposal,
+1 -7
View File
@@ -125,19 +125,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::set<uint32_t> &subaddr_indices); const std::set<uint32_t> &subaddr_indices);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal, wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers, const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view, const carrot::carrot_and_legacy_account &account);
hw::device &hwdev);
cryptonote::transaction finalize_all_proofs_from_transfer_details( cryptonote::transaction finalize_all_proofs_from_transfer_details(
const carrot::CarrotTransactionProposalV1 &tx_proposal, const carrot::CarrotTransactionProposalV1 &tx_proposal,
const cryptonote::transaction_type tx_type, const cryptonote::transaction_type tx_type,
const wallet2 &w); const wallet2 &w);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev);
wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx( wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx(
const carrot::CarrotTransactionProposalV1 &tx_proposal, const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers, const wallet2::transfer_container &transfers,
+7 -4
View File
@@ -98,6 +98,7 @@ using namespace epee;
#include "carrot_impl/format_utils.h" #include "carrot_impl/format_utils.h"
#include "tx_builder.h" #include "tx_builder.h"
#include "scanning_tools.h" #include "scanning_tools.h"
#include "carrot_core/scan.h"
extern "C" extern "C"
{ {
@@ -2652,8 +2653,8 @@ void wallet2::process_new_scanned_transaction(
} }
} }
// override the key image for PROTOCOL/RETURN tx outputs. // override the key image for pre-carrot PROTOCOL/RETURN tx outputs.
if (td.m_td_origin_idx != std::numeric_limits<uint64_t>::max()) if (!enote_scan_info->is_carrot && td.m_td_origin_idx != std::numeric_limits<uint64_t>::max())
{ {
THROW_WALLET_EXCEPTION_IF(td.m_td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers"); THROW_WALLET_EXCEPTION_IF(td.m_td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td.m_td_origin_idx]; const transfer_details &td_origin = m_transfers[td.m_td_origin_idx];
@@ -2669,7 +2670,8 @@ void wallet2::process_new_scanned_transaction(
origin_tx_data.output_index = td_origin.m_internal_output_index; origin_tx_data.output_index = td_origin.m_internal_output_index;
origin_tx_data.tx_type = td_origin.m_tx.type; origin_tx_data.tx_type = td_origin.m_tx.type;
rct::salvium_input_data_t sid; rct::salvium_input_data_t sid;
THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(m_account.get_keys(), THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(
m_account.get_keys(),
m_account.get_subaddress_map_cn(), m_account.get_subaddress_map_cn(),
onetime_address, onetime_address,
tx_public_key, tx_public_key,
@@ -2682,7 +2684,8 @@ void wallet2::process_new_scanned_transaction(
origin_tx_data, origin_tx_data,
sid), sid),
error::wallet_internal_error, error::wallet_internal_error,
"failed to obtain key image for protocol_tx output"); "failed to obtain key image for protocol_tx output"
);
td.m_key_image_known = true; td.m_key_image_known = true;
td.m_key_image = ki; td.m_key_image = ki;
} }
+13
View File
@@ -391,6 +391,19 @@ private:
return output_public_key; return output_public_key;
}; };
const crypto::public_key get_eph_public_key() const {
crypto::public_key eph_public_key;
eph_public_key = cryptonote::get_tx_pub_key_from_extra(m_tx);
if (eph_public_key != crypto::null_pkey)
return eph_public_key;
const auto additional_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(m_tx);
if (!additional_pub_keys.empty())
return additional_pub_keys[m_internal_output_index];
return crypto::null_pkey;
};
bool is_carrot() const bool is_carrot() const
{ {
THROW_WALLET_EXCEPTION_IF(m_tx.vout.size() <= m_internal_output_index, THROW_WALLET_EXCEPTION_IF(m_tx.vout.size() <= m_internal_output_index,
+8
View File
@@ -323,10 +323,13 @@ TEST(carrot_core, main_address_special_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen()); const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal; RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_enote;
get_output_proposal_special_v1(proposal, get_output_proposal_special_v1(proposal,
keys.k_view_incoming_dev, keys.k_view_incoming_dev,
tx_first_key_image, tx_first_key_image,
cryptonote::transaction_type::TRANSFER, // tx_type
std::nullopt, std::nullopt,
return_enote,
enote_proposal); enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount); ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -400,10 +403,13 @@ TEST(carrot_core, subaddress_special_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen()); const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal; RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_enote;
get_output_proposal_special_v1(proposal, get_output_proposal_special_v1(proposal,
keys.k_view_incoming_dev, keys.k_view_incoming_dev,
tx_first_key_image, tx_first_key_image,
cryptonote::transaction_type::TRANSFER, // tx_type
std::nullopt, std::nullopt,
return_enote,
enote_proposal); enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount); ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -714,6 +720,7 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const
std::vector<RCTOutputEnoteProposal> enote_proposals; std::vector<RCTOutputEnoteProposal> enote_proposals;
encrypted_payment_id_t encrypted_payment_id; encrypted_payment_id_t encrypted_payment_id;
size_t change_index; size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices; std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals({bob_payment_proposal}, get_output_enote_proposals({bob_payment_proposal},
{alice_payment_proposal}, {alice_payment_proposal},
@@ -722,6 +729,7 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const
&alice.k_view_incoming_dev, &alice.k_view_incoming_dev,
tx_first_key_image, tx_first_key_image,
enote_proposals, enote_proposals,
return_enote,
encrypted_payment_id, encrypted_payment_id,
cryptonote::transaction_type::TRANSFER, cryptonote::transaction_type::TRANSFER,
change_index, change_index,
+3
View File
@@ -417,7 +417,10 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx)
tx_proposal.key_images_sorted, tx_proposal.key_images_sorted,
tx_proposal.sources, tx_proposal.sources,
tx_proposal.fee, tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
{}, // change_masks {}, // change_masks
{}, // return_enote
encrypted_payment_id); encrypted_payment_id);
ASSERT_EQ(2, tx.version); ASSERT_EQ(2, tx.version);
+2
View File
@@ -206,6 +206,7 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
std::vector<RCTOutputEnoteProposal> rederived_output_enote_proposals; std::vector<RCTOutputEnoteProposal> rederived_output_enote_proposals;
encrypted_payment_id_t rederived_encrypted_payment_id; encrypted_payment_id_t rederived_encrypted_payment_id;
size_t change_index; size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices; std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals(tx_proposal.normal_payment_proposals, get_output_enote_proposals(tx_proposal.normal_payment_proposals,
modified_selfsend_payment_proposals, modified_selfsend_payment_proposals,
@@ -214,6 +215,7 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
&ss_keys.k_view_incoming_dev, &ss_keys.k_view_incoming_dev,
parsed_key_images.at(0), parsed_key_images.at(0),
rederived_output_enote_proposals, rederived_output_enote_proposals,
return_enote,
rederived_encrypted_payment_id, rederived_encrypted_payment_id,
cryptonote::transaction_type::TRANSFER, cryptonote::transaction_type::TRANSFER,
change_index, change_index,
+2
View File
@@ -188,6 +188,7 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness
std::vector<RCTOutputEnoteProposal> enote_proposals; std::vector<RCTOutputEnoteProposal> enote_proposals;
encrypted_payment_id_t encrypted_payment_id; encrypted_payment_id_t encrypted_payment_id;
size_t change_index; size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices; std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals({bob_payment_proposal}, get_output_enote_proposals({bob_payment_proposal},
{alice_payment_proposal}, {alice_payment_proposal},
@@ -196,6 +197,7 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness
&alive_k_v_dev, &alive_k_v_dev,
tx_first_key_image, tx_first_key_image,
enote_proposals, enote_proposals,
return_enote,
encrypted_payment_id, encrypted_payment_id,
cryptonote::transaction_type::TRANSFER, cryptonote::transaction_type::TRANSFER,
change_index, change_index,
+1 -1
View File
@@ -226,7 +226,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
get_output_proposal_return_v1( get_output_proposal_return_v1(
proposal_return, proposal_return,
tx_return_first_key_image, tx_return_first_key_image,
&bob.s_view_balance_dev, nullptr, // s_view_balance_dev
enote_proposal_return, enote_proposal_return,
encrypted_payment_id_return encrypted_payment_id_return
); );