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:
@@ -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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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__);
|
||||||
|
|||||||
@@ -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
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user