diff --git a/src/carrot_core/exceptions.h b/src/carrot_core/exceptions.h index 7810af0..dba0e4b 100644 --- a/src/carrot_core/exceptions.h +++ b/src/carrot_core/exceptions.h @@ -53,6 +53,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(missing_randomness, carrot_logic_error) CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_inputs, carrot_logic_error) CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error) CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error) +CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error) class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; }; diff --git a/src/carrot_impl/carrot_boost_serialization.h b/src/carrot_impl/carrot_boost_serialization.h index 86e0864..d418cf9 100644 --- a/src/carrot_impl/carrot_boost_serialization.h +++ b/src/carrot_impl/carrot_boost_serialization.h @@ -74,6 +74,12 @@ inline void serialize(Archive &a, carrot::encrypted_payment_id_t &x, const boost } //--------------------------------------------------- template +inline void serialize(Archive &a, carrot::encrypted_return_pubkey_t &x, const boost::serialization::version_type ver) +{ + a & x.bytes; +} +//--------------------------------------------------- +template inline void serialize(Archive &a, carrot::CarrotDestinationV1 &x, const boost::serialization::version_type ver) { a & x.address_spend_pubkey; @@ -87,6 +93,7 @@ inline void serialize(Archive &a, carrot::CarrotPaymentProposalV1 &x, const boos { a & x.destination; a & x.amount; + a & x.asset_type; a & x.randomness; } //--------------------------------------------------- diff --git a/src/carrot_impl/carrot_chain_serialization.h b/src/carrot_impl/carrot_chain_serialization.h index 009eabd..74368d5 100644 --- a/src/carrot_impl/carrot_chain_serialization.h +++ b/src/carrot_impl/carrot_chain_serialization.h @@ -40,3 +40,4 @@ BLOB_SERIALIZER(carrot::view_tag_t); BLOB_SERIALIZER(carrot::encrypted_janus_anchor_t); +BLOB_SERIALIZER(carrot::encrypted_return_pubkey_t); diff --git a/src/carrot_impl/carrot_offchain_serialization.h b/src/carrot_impl/carrot_offchain_serialization.h index d016f58..5e98f46 100644 --- a/src/carrot_impl/carrot_offchain_serialization.h +++ b/src/carrot_impl/carrot_offchain_serialization.h @@ -54,6 +54,7 @@ END_SERIALIZE() BEGIN_SERIALIZE_OBJECT_FN(carrot::CarrotPaymentProposalV1) FIELD_F(destination) VARINT_FIELD_F(amount) + FIELD_F(asset_type) FIELD_F(randomness) END_SERIALIZE() diff --git a/src/carrot_impl/format_utils.cpp b/src/carrot_impl/format_utils.cpp index 6ad1ae2..f2276b8 100644 --- a/src/carrot_impl/format_utils.cpp +++ b/src/carrot_impl/format_utils.cpp @@ -357,16 +357,18 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx, //------------------------------------------------------------------------------------------------------------------- cryptonote::transaction store_carrot_to_coinbase_transaction_v1( const std::vector &enotes, - const cryptonote::blobdata &extra_nonce) + const cryptonote::blobdata &extra_nonce, + const cryptonote::transaction_type &tx_type) { + CARROT_CHECK_AND_THROW(tx_type == cryptonote::transaction_type::MINER || tx_type == cryptonote::transaction_type::PROTOCOL, invalid_tx_type, "invalid tx_type : is not MINER or PROTOCOL"); const size_t nouts = enotes.size(); const std::uint64_t block_index = enotes.at(0).block_index; cryptonote::transaction tx; - tx.type = cryptonote::transaction_type::MINER; + tx.type = tx_type; tx.pruned = false; tx.version = 2; - tx.unlock_time = block_index + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + tx.unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; tx.vin.reserve(1); tx.vout.reserve(nouts); tx.extra.reserve(MAX_TX_EXTRA_SIZE); @@ -423,7 +425,7 @@ cryptonote::transaction make_single_enote_carrot_coinbase_transaction_v1(const C std::vector enotes(1); get_coinbase_output_proposal_v1(payment_proposal, block_index, enotes.front()); - return store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce); + return store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER); } //------------------------------------------------------------------------------------------------------------------- bool try_load_carrot_coinbase_enote_from_transaction_v1(const cryptonote::transaction &tx, diff --git a/src/carrot_impl/format_utils.h b/src/carrot_impl/format_utils.h index ec30091..e4bbf47 100644 --- a/src/carrot_impl/format_utils.h +++ b/src/carrot_impl/format_utils.h @@ -137,7 +137,8 @@ bool try_load_carrot_from_transaction_v1(const cryptonote::transaction &tx, */ cryptonote::transaction store_carrot_to_coinbase_transaction_v1( const std::vector &enotes, - const cryptonote::blobdata &extra_nonce); + const cryptonote::blobdata &extra_nonce, + const cryptonote::transaction_type &tx_type); /** * brief: make_single_enote_carrot_coinbase_transaction_v1 - store one coinbase Carrot enote to a cryptonote::transaction * param: destination - diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index c6f8e87..9141065 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -35,6 +35,7 @@ #include "string_tools.h" using namespace epee; +#include "carrot_core/payment_proposal.h" #include "carrot_impl/format_utils.h" #include "common/apply_permutation.h" #include "cryptonote_tx_utils.h" @@ -368,7 +369,7 @@ namespace cryptonote const size_t height, transaction& tx, std::vector& protocol_data, - const uint8_t hf_version + const uint8_t hard_fork_version ) { // Clear the TX contents @@ -378,6 +379,54 @@ namespace cryptonote // Force the TX type to 2 tx.version = 2; + const bool do_carrot = hard_fork_version >= HF_VERSION_CARROT; + if (do_carrot) + { + try + { + // Create a vector of enotes + std::vector enotes; + enotes.reserve(protocol_data.size()); + + // Iterate over the protocol_data we received, creating an enote for each entry + 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 + get_coinbase_output_proposal_v1(payment_proposal, height, enotes.back()); + } + + tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL); + tx.amount_burnt = 0; + tx.invalidate_hashes(); + } + catch (const std::exception &e) + { + MERROR("Failed to construct Carrot protocol transaction: " << e.what()); + return false; + } + + return true; + } + keypair txkey = keypair::generate(hw::get_device("default")); add_tx_pub_key_to_extra(tx, txkey.pub); if (!sort_tx_extra(tx.extra, tx.extra)) diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h index 1706d7e..218914e 100644 --- a/src/serialization/binary_archive.h +++ b/src/serialization/binary_archive.h @@ -197,7 +197,7 @@ struct binary_archive : public binary_archive_base { for (size_t i = 0; i < sizeof(T); i++) { stream_.put((char)(v & 0xff)); - if (1 < sizeof(T)) v >>= 8; + if constexpr (1 < sizeof(T)) { v >>= 8; } } }