updated to support propagation of the transaction type - this was necessary to distinguish between YIELD and TRANSFER type
This commit is contained in:
@@ -277,6 +277,8 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
oracle::asset_type_counts num_rct_outs_by_asset_type;
|
||||
blobdata miner_bd = tx_to_blob(blk.miner_tx);
|
||||
add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd)));
|
||||
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
|
||||
add_transaction(blk_hash, std::make_pair(blk.protocol_tx, blobdata_ref(protocol_bd)));
|
||||
|
||||
if (blk.miner_tx.version == 2)
|
||||
{
|
||||
@@ -291,6 +293,19 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
}
|
||||
}
|
||||
|
||||
if (blk.protocol_tx.version == 2)
|
||||
{
|
||||
num_rct_outs += blk.protocol_tx.vout.size();
|
||||
|
||||
// count the current block's rct outs by asset type
|
||||
for (auto& vout: blk.protocol_tx.vout) {
|
||||
std::string asset_type;
|
||||
if (!get_output_asset_type(vout, asset_type))
|
||||
throw std::runtime_error("Failed to get output asset type");
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
}
|
||||
}
|
||||
|
||||
int tx_i = 0;
|
||||
crypto::hash tx_hash = crypto::null_hash;
|
||||
for (const std::pair<transaction, blobdata>& tx : txs)
|
||||
@@ -344,6 +359,7 @@ void BlockchainDB::pop_block(block& blk, std::vector<transaction>& txs)
|
||||
txs.push_back(std::move(tx));
|
||||
remove_transaction(h);
|
||||
}
|
||||
remove_transaction(get_transaction_hash(blk.protocol_tx));
|
||||
remove_transaction(get_transaction_hash(blk.miner_tx));
|
||||
}
|
||||
|
||||
@@ -364,6 +380,10 @@ void BlockchainDB::remove_transaction(const crypto::hash& tx_hash)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for yield_tx entries
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
}
|
||||
|
||||
const bool miner_tx = tx.vin.size() == 1 && tx.vin[0].type() == typeid(txin_gen);
|
||||
|
||||
// need tx as tx.vout has the tx outputs, and the output amounts are needed
|
||||
|
||||
@@ -211,6 +211,8 @@ namespace
|
||||
*
|
||||
* alt_blocks block hash {block data, block blob}
|
||||
*
|
||||
* yield_txs block height {txn hash, dest address, amount}
|
||||
*
|
||||
* Note: where the data items are of uniform size, DUPFIXED tables have
|
||||
* been used to save space. In most of these cases, a dummy "zerokval"
|
||||
* key is used when accessing the table; the Key listed above will be
|
||||
@@ -249,6 +251,35 @@ const char* const LMDB_PROPERTIES = "properties";
|
||||
const char* const LMDB_CIRC_SUPPLY = "circ_supply";
|
||||
const char* const LMDB_CIRC_SUPPLY_TALLY = "circ_supply_tally";
|
||||
|
||||
/**
|
||||
* We have the following information that will go into a table in the blockchain:
|
||||
* block_height (this is the key field)
|
||||
* -----------------------------------------
|
||||
* txn_hash (so we can verify)
|
||||
* dest_address (where to send the yield)
|
||||
* amount (how much was locked)
|
||||
*
|
||||
*
|
||||
* If we only allow a 30-day (actually 21,600 block) lock, and no variation on that period, then the code
|
||||
* to identify locks that are earning yield in a given block is probably pretty simple. It gets a LOT more
|
||||
* complicated if you can lock for a variable period, of course.
|
||||
*
|
||||
* So, let's say that we have a block height h for which we want to assess the yield payments. First off,
|
||||
* we are ONLY interested in making ANY payment if we have YIELD.block_height == h + 21600 (i.e. the yield
|
||||
* TX has matured).
|
||||
*
|
||||
* Now, to calculate the payable yield, we need to know:
|
||||
* # how much slippage accrued in each of the last 21600 blocks
|
||||
* (call this slippage_amounts, which is a vector of slippage_height and slippage_amount tuples)
|
||||
* # the list of all yield TXs that fulfil the criteria
|
||||
* (YIELD.block_height > (h - 21600)) and (YIELD.block_height <= h) (call this yield_txs)
|
||||
*
|
||||
* Given this information, we would sort yield_txs by block_height. Then we iterate over slippage_amounts,
|
||||
* and for each entry, we look to see which of the yield_txs overlaps the height for the given slippage_height.
|
||||
* This allows us to work out the % of the slippage_amount that should be paid to the maturing yield TX.
|
||||
*/
|
||||
const char* const LMDB_YIELD_TXS = "yield_txs";
|
||||
|
||||
const char zerokey[8] = {0};
|
||||
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
|
||||
|
||||
@@ -350,7 +381,13 @@ typedef struct circ_supply_tally {
|
||||
uint64_t amount_hi;
|
||||
uint64_t amount_lo;
|
||||
} circ_supply_tally;
|
||||
|
||||
|
||||
typedef struct yield_tx_data {
|
||||
crypto::hash tx_hash;
|
||||
crypto::public_key destination_address;
|
||||
uint64_t amount;
|
||||
} yield_tx_data;
|
||||
|
||||
std::atomic<uint64_t> mdb_txn_safe::num_active_txns{0};
|
||||
std::atomic_flag mdb_txn_safe::creation_gate = ATOMIC_FLAG_INIT;
|
||||
|
||||
@@ -973,6 +1010,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
CURSOR(tx_indices)
|
||||
CURSOR(circ_supply)
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_txs)
|
||||
|
||||
MDB_val_set(val_tx_id, tx_id);
|
||||
MDB_val_set(val_h, tx_hash);
|
||||
@@ -1085,6 +1123,20 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
"\nDest tally before mint =" << dest_tally.str() << "\nDest tally after mint =" << final_dest_tally.str());
|
||||
}
|
||||
|
||||
// Is there yield_tx data to add?
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
// Create the object we are going to write to the database
|
||||
yield_tx_data yield_data;
|
||||
yield_data.tx_hash = tx_hash;
|
||||
yield_data.destination_address = tx.destination_address;
|
||||
yield_data.amount = tx.amount_burnt; // SRCG - this feels as though we are bastardising the variable for an invalid purpose
|
||||
MDB_val_set(val_height, m_height);
|
||||
MDB_val_set(val_yield_tx_data, yield_data);
|
||||
result = mdb_cursor_put(m_cur_yield_txs, &val_height, &val_yield_tx_data, MDB_APPEND);
|
||||
if (result)
|
||||
throw0(DB_ERROR( lmdb_error("Failed to add tx yield data to db transaction: ", result).c_str() ));
|
||||
}
|
||||
|
||||
return tx_id;
|
||||
}
|
||||
|
||||
@@ -1107,6 +1159,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
CURSOR(tx_outputs)
|
||||
CURSOR(circ_supply)
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_txs)
|
||||
|
||||
MDB_val_set(val_h, tx_hash);
|
||||
|
||||
@@ -1213,6 +1266,22 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of tx outputs to db transaction: ", result).c_str()));
|
||||
}
|
||||
|
||||
// Is there yield_tx data to remove?
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
// Remove any yield_tx data for this transaction
|
||||
result = mdb_cursor_get(m_cur_yield_txs, &val_tx_id, NULL, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
LOG_PRINT_L1("tx has no yield_tx data to remove: " << tx_hash);
|
||||
else if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate yield_tx data for removal: ", result).c_str()));
|
||||
if (!result)
|
||||
{
|
||||
result = mdb_cursor_del(m_cur_yield_txs, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of yield_tx data to db transaction: ", result).c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Don't delete the tx_indices entry until the end, after we're done with val_tx_id
|
||||
if (mdb_cursor_del(m_cur_tx_indices, 0))
|
||||
throw1(DB_ERROR("Failed to add removal of tx index to db transaction"));
|
||||
|
||||
@@ -76,6 +76,7 @@ typedef struct mdb_txn_cursors
|
||||
|
||||
MDB_cursor *m_txc_circ_supply;
|
||||
MDB_cursor *m_txc_circ_supply_tally;
|
||||
MDB_cursor *m_txc_yield_txs;
|
||||
|
||||
} mdb_txn_cursors;
|
||||
|
||||
@@ -98,9 +99,9 @@ typedef struct mdb_txn_cursors
|
||||
#define m_cur_alt_blocks m_cursors->m_txc_alt_blocks
|
||||
#define m_cur_hf_versions m_cursors->m_txc_hf_versions
|
||||
#define m_cur_properties m_cursors->m_txc_properties
|
||||
|
||||
#define m_cur_circ_supply m_cursors->m_txc_circ_supply
|
||||
#define m_cur_circ_supply_tally m_cursors->m_txc_circ_supply_tally
|
||||
#define m_cur_yield_txs m_cursors->m_txc_yield_txs
|
||||
|
||||
typedef struct mdb_rflags
|
||||
{
|
||||
@@ -126,6 +127,7 @@ typedef struct mdb_rflags
|
||||
bool m_rf_properties;
|
||||
bool m_rf_circ_supply;
|
||||
bool m_rf_circ_supply_tally;
|
||||
bool m_rf_yield_txs;
|
||||
} mdb_rflags;
|
||||
|
||||
typedef struct mdb_threadinfo
|
||||
@@ -484,6 +486,7 @@ private:
|
||||
|
||||
MDB_dbi m_circ_supply;
|
||||
MDB_dbi m_circ_supply_tally;
|
||||
MDB_dbi m_yield_txs;
|
||||
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
|
||||
@@ -198,7 +198,7 @@ namespace cryptonote
|
||||
//extra
|
||||
std::vector<uint8_t> extra;
|
||||
// TX type
|
||||
uint8_t type;
|
||||
cryptonote::transaction_type type;
|
||||
// Destination address (encrypted)
|
||||
crypto::public_key destination_address;
|
||||
// Source asset type
|
||||
@@ -234,7 +234,7 @@ namespace cryptonote
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
extra.clear();
|
||||
type = static_cast<uint8_t>(cryptonote::transaction_type::TRANSFER);
|
||||
type = cryptonote::transaction_type::TRANSFER;
|
||||
destination_address = crypto::null_pkey;
|
||||
source_asset_type.clear();
|
||||
destination_asset_type.clear();
|
||||
|
||||
@@ -55,6 +55,10 @@
|
||||
#define EMISSION_SPEED_FACTOR_PER_MINUTE (19)
|
||||
#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)30000000) // 3 * pow(10, 7)
|
||||
|
||||
#define BURN_LOCK_PERIOD 0
|
||||
#define CONVERT_LOCK_PERIOD 0
|
||||
#define YIELD_LOCK_PERIOD 30*24*30
|
||||
|
||||
#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100
|
||||
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size
|
||||
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
||||
#include "tx_pool.h"
|
||||
#include "blockchain.h"
|
||||
#include "blockchain_db/locked_txn.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "cryptonote_basic/cryptonote_boost_serialization.h"
|
||||
#include "cryptonote_config.h"
|
||||
@@ -1399,6 +1400,45 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function does a sanity check on basic things that all protocol
|
||||
// transactions have in common, such as:
|
||||
// one input, of type txin_gen, with height set to the block's height
|
||||
// valid output types
|
||||
bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vin.size() == 1, false, "coinbase protocol transaction in the block has no inputs");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type");
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version");
|
||||
|
||||
// for v2 txes (ringct), we only accept empty rct signatures for protocol transactions,
|
||||
if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.protocol_tx.version >= 2)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(b.protocol_tx.rct_signatures.type == rct::RCTTypeNull, false, "RingCT signatures not allowed in coinbase protocol transactions");
|
||||
}
|
||||
|
||||
if(boost::get<txin_gen>(b.protocol_tx.vin[0]).height != height)
|
||||
{
|
||||
MWARNING("The protocol transaction in block has invalid height: " << boost::get<txin_gen>(b.protocol_tx.vin[0]).height << ", expected: " << height);
|
||||
return false;
|
||||
}
|
||||
MDEBUG("Protocol tx hash: " << get_transaction_hash(b.protocol_tx));
|
||||
//CHECK_AND_ASSERT_MES(b.protocol_tx.unlock_time == height, false, "coinbase protocol transaction transaction has the wrong unlock time=" << b.protocol_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW);
|
||||
|
||||
// SRCG - we still need to make output checks
|
||||
/*
|
||||
//check outs overflow
|
||||
if(!check_outs_overflow(b.protocol_tx))
|
||||
{
|
||||
MERROR("protocol transaction has money overflow in block " << get_block_hash(b));
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(check_output_types(b.miner_tx, hf_version), false, "miner transaction has invalid output type(s) in block " << get_block_hash(b));
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function validates the miner transaction reward
|
||||
bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version)
|
||||
{
|
||||
@@ -1427,7 +1467,98 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// SRCG
|
||||
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, std::vector<std::pair<transaction, blobdata>>& txs, const std::map<std::string, uint64_t>& circ_supply, uint8_t hf_version)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CHECK_AND_ASSERT_MES(b.tx_hashes.size() == txs.size(), false, "Invalid number of TXs / hashes supplied");
|
||||
|
||||
key_images_container keys;
|
||||
|
||||
uint64_t fee_summary = 0;
|
||||
uint64_t t_checktx = 0;
|
||||
uint64_t t_exists = 0;
|
||||
uint64_t t_pool = 0;
|
||||
uint64_t t_dblspnd = 0;
|
||||
uint64_t n_pruned = 0;
|
||||
|
||||
// Build a map of outputs from the protocol_tx
|
||||
std::map<crypto::public_key, std::tuple<std::string, uint64_t, uint64_t>> outputs;
|
||||
for (auto& o : b.protocol_tx.vout) {
|
||||
if (o.target.type() == typeid(txout_to_key)) {
|
||||
txout_to_key out = boost::get<txout_to_key>(o.target);
|
||||
outputs[out.key] = {out.asset_type, o.amount, out.unlock_time};
|
||||
} else if (o.target.type() == typeid(txout_to_tagged_key)) {
|
||||
txout_to_tagged_key out = boost::get<txout_to_tagged_key>(o.target);
|
||||
outputs[out.key] = {out.asset_type, o.amount, out.unlock_time};
|
||||
} else {
|
||||
MERROR("Block at height: " << height << " attempting to add protocol transaction with invalid type " << o.target.type().name());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t tx_index = 0;
|
||||
// Iterate over the block's transaction hashes, grabbing each
|
||||
// from the tx_pool and validating them.
|
||||
for (const auto& tx_tmp : txs)
|
||||
{
|
||||
// Get a ptr to the TX to simplify coding
|
||||
const transaction* tx = &tx_tmp.first;
|
||||
|
||||
// Check to see if the TX exists in the DB - this probably duplicates the effort in our caller, but better to be sure
|
||||
if (m_db->tx_exists(tx->hash))
|
||||
{
|
||||
MERROR("Block at height: " << height << " attempting to add transaction already in blockchain with id: " << tx->hash);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to see if the TX is a conversion or not
|
||||
if (tx->type != cryptonote::transaction_type::CONVERT) {
|
||||
// Only conversion (and failed conversion, aka refund) TXs need to be verified - skip this TX
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify that the TX has an output in the protocol_tx to verify
|
||||
if (outputs.count(tx->destination_address) != 1) {
|
||||
LOG_ERROR("Failed to locate output for conversion TX id " << tx->hash << " - rejecting block");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the output information
|
||||
std::string output_asset_type;
|
||||
uint64_t output_amount;
|
||||
uint64_t output_unlock_time;
|
||||
std::tie(output_asset_type, output_amount, output_unlock_time) = outputs[tx->destination_address];
|
||||
|
||||
// Verify the asset_type
|
||||
if (tx->source_asset_type == output_asset_type) {
|
||||
// Check the amount for REFUND
|
||||
if (tx->amount_burnt != output_amount) {
|
||||
LOG_ERROR("Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block");
|
||||
return false;
|
||||
}
|
||||
} else if (tx->destination_asset_type == output_asset_type) {
|
||||
// Check the amount for CONVERT
|
||||
|
||||
// Verify the amount of the conversion
|
||||
uint64_t amount_minted_check = 0, amount_slippage_check = 0;
|
||||
bool ok = cryptonote::calculate_conversion(tx->source_asset_type, tx->destination_asset_type, tx->amount_burnt, tx->amount_slippage_limit, amount_minted_check, amount_slippage_check, circ_supply, b.pricing_record, hf_version);
|
||||
if (!ok) {
|
||||
LOG_ERROR("Failed to calculate conversion for TX id " << tx->hash << " - rejecting block");
|
||||
return false;
|
||||
}
|
||||
if (amount_minted_check != output_amount) {
|
||||
LOG_ERROR("Output amount does not match amount_burnt for refunded TX id " << tx->hash << " - rejecting block");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("Output asset type incorrect: source " << tx->source_asset_type << ", dest " << tx->destination_asset_type << ", got " << output_asset_type << " - rejecting block");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// get the block weights of the last <count> blocks, and return by reference <sz>.
|
||||
void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const
|
||||
@@ -2059,6 +2190,13 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!prevalidate_protocol_transaction(b, bei.height, hf_version))
|
||||
{
|
||||
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect miner transaction.");
|
||||
bvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME:
|
||||
// this brings up an interesting point: consider allowing to get block
|
||||
// difficulty both by height OR by hash, not just height.
|
||||
@@ -4283,6 +4421,14 @@ leave:
|
||||
goto leave;
|
||||
}
|
||||
|
||||
// sanity check basic protocol tx properties;
|
||||
if(!prevalidate_protocol_transaction(bl, blockchain_height, hf_version))
|
||||
{
|
||||
MERROR_VER("Block with id: " << id << " failed to pass protocol prevalidation");
|
||||
bvc.m_verifivation_failed = true;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
size_t coinbase_weight = get_transaction_weight(bl.miner_tx);
|
||||
size_t cumulative_block_weight = coinbase_weight;
|
||||
|
||||
@@ -4431,6 +4577,22 @@ leave:
|
||||
}
|
||||
|
||||
TIME_MEASURE_FINISH(vmt);
|
||||
|
||||
TIME_MEASURE_START(gcs);
|
||||
std::map<std::string, uint64_t> circ_supply = get_db().get_circulating_supply();
|
||||
TIME_MEASURE_FINISH(gcs);
|
||||
|
||||
TIME_MEASURE_START(vpt);
|
||||
if(!validate_protocol_transaction(bl, blockchain_height, txs, circ_supply, m_hardfork->get_current_version()))
|
||||
{
|
||||
MERROR_VER("Block with id: " << id << " has incorrect protocol transaction");
|
||||
bvc.m_verifivation_failed = true;
|
||||
return_tx_to_pool(txs);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
TIME_MEASURE_FINISH(vpt);
|
||||
|
||||
size_t block_weight;
|
||||
difficulty_type cumulative_difficulty;
|
||||
|
||||
|
||||
@@ -1420,6 +1420,20 @@ namespace cryptonote
|
||||
*/
|
||||
bool prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version);
|
||||
|
||||
/**
|
||||
* @brief sanity checks a protocol transaction before validating an entire block
|
||||
*
|
||||
* This function merely checks basic things like the structure of the protocol
|
||||
* transaction, the unlock time, and that the amount doesn't overflow.
|
||||
*
|
||||
* @param b the block containing the protocol transaction
|
||||
* @param height the height at which the block will be added
|
||||
* @param hf_version the consensus rules to apply
|
||||
*
|
||||
* @return false if anything is found wrong with the protocol transaction, otherwise true
|
||||
*/
|
||||
bool prevalidate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version);
|
||||
|
||||
/**
|
||||
* @brief validates a miner (coinbase) transaction
|
||||
*
|
||||
@@ -1438,6 +1452,21 @@ namespace cryptonote
|
||||
*/
|
||||
bool validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version);
|
||||
|
||||
/**
|
||||
* @brief validates a protocol (coinbase) transaction
|
||||
*
|
||||
* This function makes sure that the protocol rules are being implemented correctly
|
||||
* and that the conversions and yield payouts match what is expected.
|
||||
*
|
||||
* @param b the block containing the miner transaction to be validated
|
||||
* @param height the blockchain's weight
|
||||
* @param txs a vector containing all the TXs and their blobs, needed to obtain tx_types, asset_types and burnt amounts
|
||||
* @param version hard fork version for that transaction
|
||||
*
|
||||
* @return false if anything is found wrong with the protocol transaction, otherwise true
|
||||
*/
|
||||
bool validate_protocol_transaction(const block& b, uint64_t height, std::vector<std::pair<transaction, blobdata>>& txs, const std::map<std::string, uint64_t>& circ_supply, uint8_t hf_version);
|
||||
|
||||
/**
|
||||
* @brief reverts the blockchain to its previous state following a failed switch
|
||||
*
|
||||
|
||||
@@ -141,10 +141,13 @@ namespace cryptonote
|
||||
bool ok = get_conversion_rate(pr, source_asset, dest_asset, conversion_rate);
|
||||
CHECK_AND_ASSERT_MES(ok, false, "Unable to get conversion rate for " << source_asset << " to " << dest_asset);
|
||||
|
||||
// Apply slippage to the burnt amount
|
||||
amount_slippage = amount_burnt >> 5; // (1/32)
|
||||
|
||||
if (hf_version >= HF_VERSION_SLIPPAGE_YIELD) {
|
||||
|
||||
// Apply slippage to the burnt amount
|
||||
amount_slippage = 0;
|
||||
amount_slippage = amount_burnt >> 5; // (1/32)
|
||||
|
||||
// Check that the slippage is acceptable
|
||||
if (amount_slippage > amount_slippage_limit) {
|
||||
@@ -173,14 +176,20 @@ namespace cryptonote
|
||||
// Clear the TX contents
|
||||
tx.set_null();
|
||||
|
||||
// Force the TX type to 2
|
||||
tx.version = 2;
|
||||
|
||||
// Clear the unlock_time
|
||||
tx.unlock_time = 0;
|
||||
|
||||
// Force the TX type to "special" (0)
|
||||
tx.type = cryptonote::transaction_type::UNSET;
|
||||
|
||||
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))
|
||||
return false;
|
||||
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
|
||||
// Update the circulating_supply information, while keeping a count of amount to be created using txin_gen
|
||||
std::map<std::string, uint64_t> txin_gen_totals;
|
||||
uint64_t txin_gen_final = 0;
|
||||
@@ -194,6 +203,7 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
// Calculate the slippage for the output amounts
|
||||
LOG_PRINT_L2("Creating protocol_tx...");
|
||||
for (auto const& entry: protocol_data) {
|
||||
if (entry.destination_asset == "") {
|
||||
// BURN TX - no slippage, no money minted - skip
|
||||
@@ -209,6 +219,7 @@ namespace cryptonote
|
||||
if (amount_minted == 0) {
|
||||
|
||||
// REFUND
|
||||
LOG_PRINT_L2("Conversion TX refunded - slippage too high");
|
||||
txin_gen_totals[entry.source_asset] += entry.amount_burnt;
|
||||
|
||||
// Create the TX output for this refund
|
||||
@@ -218,6 +229,7 @@ namespace cryptonote
|
||||
} else {
|
||||
|
||||
// CONVERTED
|
||||
LOG_PRINT_L2("Conversion TX submitted - converted " << entry.amount_burnt << entry.source_asset << " to " << amount_minted << entry.destination_asset << "(slippage " << amount_slippage << ")");
|
||||
txin_gen_totals[entry.destination_asset] += amount_minted;
|
||||
|
||||
// Create the TX output for this conversion
|
||||
@@ -229,10 +241,13 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
// TODO: create the YIELD outputs
|
||||
|
||||
// TODO: sum the txin_gen_totals values so that we know how much we are going to include in the protocol TX
|
||||
|
||||
return false;
|
||||
|
||||
// Create the txin_gen now
|
||||
txin_gen in;
|
||||
in.height = height;
|
||||
tx.vin.push_back(in);
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
|
||||
@@ -396,6 +411,7 @@ namespace cryptonote
|
||||
const uint8_t hf_version,
|
||||
const std::string& source_asset,
|
||||
const std::string& dest_asset,
|
||||
const transaction_type& tx_type,
|
||||
const boost::optional<cryptonote::account_public_address>& change_addr,
|
||||
const std::vector<uint8_t> &extra,
|
||||
transaction& tx,
|
||||
@@ -430,17 +446,14 @@ namespace cryptonote
|
||||
tx.extra = extra;
|
||||
crypto::public_key txkey_pub;
|
||||
|
||||
transaction_type tx_type;
|
||||
if (!get_tx_type(source_asset, dest_asset, tx_type)) {
|
||||
LOG_ERROR("invalid tx type");
|
||||
return false;
|
||||
}
|
||||
tx.type = static_cast<uint8_t>(tx_type);
|
||||
tx.type = tx_type;
|
||||
|
||||
// Is this a CONVERT tx?
|
||||
if (tx_type == cryptonote::transaction_type::CONVERT) {
|
||||
// Set the destination address to be something only our wallet can identify
|
||||
// This is where Fulmo gets interesting...
|
||||
// This is where Fulmo gets interesting... we need to include the input key images
|
||||
// so that we get uniqueness and prevent either Monero burning bug or key leakage.
|
||||
// tx.d_a = Hs("convert" || input_key_images || 8rAG) + B
|
||||
tx.destination_address = get_destination_view_key_pub(destinations, change_addr);
|
||||
}
|
||||
|
||||
@@ -875,7 +888,7 @@ namespace cryptonote
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool use_view_tags)
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const transaction_type& tx_type, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, const rct::RCTConfig &rct_config, bool use_view_tags)
|
||||
{
|
||||
hw::device &hwdev = sender_account_keys.get_device();
|
||||
hwdev.open_tx(tx_key);
|
||||
@@ -896,7 +909,7 @@ namespace cryptonote
|
||||
}
|
||||
|
||||
bool shuffle_outs = true;
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, hf_version, source_asset, dest_asset, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, shuffle_outs, use_view_tags);
|
||||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, hf_version, source_asset, dest_asset, tx_type, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, shuffle_outs, use_view_tags);
|
||||
hwdev.close_tx();
|
||||
return r;
|
||||
} catch(...) {
|
||||
@@ -905,14 +918,14 @@ namespace cryptonote
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time)
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time)
|
||||
{
|
||||
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
|
||||
subaddresses[sender_account_keys.m_account_address.m_spend_public_key] = {0,0};
|
||||
crypto::secret_key tx_key;
|
||||
std::vector<crypto::secret_key> additional_tx_keys;
|
||||
std::vector<tx_destination_entry> destinations_copy = destinations;
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, hf_version, source_asset, dest_asset, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0});
|
||||
return construct_tx_and_get_tx_key(sender_account_keys, subaddresses, sources, destinations_copy, hf_version, source_asset, dest_asset, tx_type, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, { rct::RangeProofBorromean, 0});
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool generate_genesis_block(
|
||||
|
||||
@@ -150,9 +150,9 @@ namespace cryptonote
|
||||
|
||||
//---------------------------------------------------------------
|
||||
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const boost::optional<cryptonote::account_public_address>& change_addr);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false);
|
||||
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry> &sources, const std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_with_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool shuffle_outs = true, bool use_view_tags = false);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, std::vector<tx_destination_entry>& destinations, const uint8_t hf_version, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, const rct::RCTConfig &rct_config = { rct::RangeProofBorromean, 0 }, bool use_view_tags = false);
|
||||
bool generate_output_ephemeral_keys(const size_t tx_version, const cryptonote::account_keys &sender_account_keys, const crypto::public_key &txkey_pub, const crypto::secret_key &tx_key,
|
||||
const cryptonote::tx_destination_entry &dst_entr, const boost::optional<cryptonote::account_public_address> &change_addr, const size_t output_index,
|
||||
const bool &need_additional_txkeys, const std::vector<crypto::secret_key> &additional_tx_keys,
|
||||
|
||||
@@ -43,11 +43,12 @@ namespace cryptonote
|
||||
block //!< Received in block, takes precedence over others
|
||||
};
|
||||
|
||||
enum class transaction_type : std::uint8_t
|
||||
enum transaction_type
|
||||
{
|
||||
UNSET = 0,
|
||||
TRANSFER,
|
||||
CONVERT,
|
||||
BURN
|
||||
TRANSFER = 1,
|
||||
CONVERT = 2,
|
||||
BURN = 3,
|
||||
YIELD = 4
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
//
|
||||
// Portions of this code based upon code Copyright (c) 2019, The Monero Project
|
||||
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#include "pricing_record.h"
|
||||
|
||||
#include "serialization/keyvalue_serialization.h"
|
||||
@@ -116,6 +117,18 @@ namespace oracle
|
||||
::memcpy(signature, orig.signature, sizeof(signature));
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint64_t pricing_record::operator[](const std::string& asset_type) const
|
||||
{
|
||||
if (asset_type == "FULM") return spot;
|
||||
if (asset_type == "FUSD") {
|
||||
boost::multiprecision::uint128_t exchange_128 = COIN;
|
||||
exchange_128 *= COIN;
|
||||
exchange_128 /= spot;
|
||||
return exchange_128.convert_to<uint64_t>();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool pricing_record::equal(const pricing_record& other) const noexcept
|
||||
{
|
||||
|
||||
@@ -161,6 +161,7 @@ enum TransferType {
|
||||
TransferLocked,
|
||||
Convert,
|
||||
Burn,
|
||||
LockForYield
|
||||
};
|
||||
|
||||
static std::string get_human_readable_timespan(std::chrono::seconds seconds);
|
||||
@@ -208,6 +209,7 @@ namespace
|
||||
const char* USAGE_SWEEP_SINGLE("sweep_single [<priority>] [<ring_size>] [outputs=<N>] <key_image> <address> [<payment_id (obsolete)>]");
|
||||
const char* USAGE_BURN("burn <amount> <asset_type>");
|
||||
const char* USAGE_CONVERT("convert [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] (<URI> | <address> <source_amount>) <source_asset> <dest_asset> [<payment_id>]");
|
||||
const char* USAGE_LOCK_FOR_YIELD("lock_for_yield <amount>");
|
||||
const char* USAGE_PRICE_INFO("price_info");
|
||||
const char* USAGE_SUPPLY_INFO("supply_info");
|
||||
const char* USAGE_YIELD_INFO("yield_info");
|
||||
@@ -3152,6 +3154,7 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st
|
||||
message_writer() << tr("\"transfer <address> <amount> [<asset_type>]\" - Send FULM or F$ to an address.");
|
||||
message_writer() << tr("\"burn <amount> <asset_type>\" - destroy coins forever.");
|
||||
message_writer() << tr("\"convert <address> <amount> <source_asset> <dest_asset>\" - convert between coin types and send to address.");
|
||||
message_writer() << tr("\"lock_for_tield <amount>\" - lock FULM in order to earn yield.");
|
||||
message_writer() << tr("\"price_info\" - Display current pricing information for supported assets.");
|
||||
message_writer() << tr("\"supply_info\" - Display circulating supply information.");
|
||||
message_writer() << tr("\"yield_info\" - Display current stats on Fulmo yield.");
|
||||
@@ -3336,6 +3339,10 @@ simple_wallet::simple_wallet()
|
||||
boost::bind(&simple_wallet::convert, this, _1),
|
||||
tr(USAGE_CONVERT),
|
||||
tr("Converts <amount> <source_asset> into <dest_asset>, with optional <priority> [0-5]"));
|
||||
m_cmd_binder.set_handler("lock_for_yield",
|
||||
boost::bind(&simple_wallet::lock_for_yield, this, _1),
|
||||
tr(USAGE_LOCK_FOR_YIELD),
|
||||
tr("Locks <amount> of FULM in order to earn yield"));
|
||||
m_cmd_binder.set_handler("price_info",
|
||||
boost::bind(&simple_wallet::price_info, this, _1),
|
||||
tr(USAGE_PRICE_INFO),
|
||||
@@ -6789,6 +6796,18 @@ bool simple_wallet::transfer_main(
|
||||
std::string err;
|
||||
switch (transfer_type)
|
||||
{
|
||||
case Burn:
|
||||
unlock_block = 0;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::BURN, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
case Convert:
|
||||
unlock_block = CONVERT_LOCK_PERIOD;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
case LockForYield:
|
||||
unlock_block = YIELD_LOCK_PERIOD;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::YIELD, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
case TransferLocked:
|
||||
bc_height = get_daemon_blockchain_height(err);
|
||||
if (!err.empty())
|
||||
@@ -6797,13 +6816,13 @@ bool simple_wallet::transfer_main(
|
||||
return false;
|
||||
}
|
||||
unlock_block = bc_height + locked_blocks;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown transfer method, using default");
|
||||
/* FALLTHRU */
|
||||
case Transfer:
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, 0 /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7904,6 +7923,23 @@ bool simple_wallet::convert(const std::vector<std::string> &args_)
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::lock_for_yield(const std::vector<std::string> &args_)
|
||||
{
|
||||
// TODO: add locked versions
|
||||
if (args_.size() != 1)
|
||||
{
|
||||
PRINT_USAGE(USAGE_LOCK_FOR_YIELD);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> local_args;
|
||||
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
|
||||
local_args.insert(local_args.end(), args_.begin(), args_.end());
|
||||
|
||||
transfer_main(LockForYield, "FULM", "FULM", local_args, false);
|
||||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::price_info(const std::vector<std::string> &args) {
|
||||
// get circulating supply
|
||||
std::vector<std::pair<std::string, std::string>> supply_amounts;
|
||||
|
||||
@@ -182,6 +182,7 @@ namespace cryptonote
|
||||
bool sweep_unmixable(const std::vector<std::string> &args);
|
||||
bool burn(const std::vector<std::string> &args);
|
||||
bool convert(const std::vector<std::string> &args);
|
||||
bool lock_for_yield(const std::vector<std::string> &args);
|
||||
bool price_info(const std::vector<std::string> &args);
|
||||
bool supply_info(const std::vector<std::string> &args);
|
||||
bool yield_info(const std::vector<std::string> &args);
|
||||
|
||||
+16
-15
@@ -6907,7 +6907,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
|
||||
// To-do - work out the source_asset and dest_asset.
|
||||
std::string source_asset = "FULM";
|
||||
std::string dest_asset = "FULM";
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, hf_version, source_asset, dest_asset, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, sd.use_view_tags);
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, hf_version, source_asset, dest_asset, sd.tx_type, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, rct_config, sd.use_view_tags);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
|
||||
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
|
||||
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
|
||||
@@ -9064,7 +9064,8 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
||||
// If it _does_ get called, it's necessary to work out the source_asset and dest_asset as well.
|
||||
std::string source_asset = "FULM";
|
||||
std::string dest_asset = "FULM";
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, use_view_tags);
|
||||
cryptonote::transaction_type tx_type = TRANSFER;
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, {}, use_view_tags);
|
||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
|
||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
|
||||
@@ -9120,11 +9121,13 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
uint64_t unlock_time,
|
||||
uint64_t fee,
|
||||
const std::vector<uint8_t>& extra,
|
||||
cryptonote::transaction& tx, pending_tx &ptx,
|
||||
cryptonote::transaction& tx,
|
||||
pending_tx &ptx,
|
||||
const rct::RCTConfig &rct_config,
|
||||
bool use_view_tags,
|
||||
const std::string& source_asset,
|
||||
const std::string& dest_asset,
|
||||
const transaction_type& tx_type,
|
||||
const oracle::pricing_record& pr)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
@@ -9350,7 +9353,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
std::vector<std::pair<std::string, std::string>> circ_amounts;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_circulating_supply(circ_amounts), error::wallet_internal_error, "Failed to get circulating supply");
|
||||
// make a normal tx
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, hf_version, source_asset, dest_asset, tx_type, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, rct_config, use_view_tags);
|
||||
LOG_PRINT_L2("constructed tx, r="<<r);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
|
||||
}
|
||||
@@ -10108,6 +10111,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(
|
||||
std::vector<cryptonote::tx_destination_entry> dsts,
|
||||
const std::string& source_asset,
|
||||
const std::string& dest_asset,
|
||||
const transaction_type& tx_type,
|
||||
const size_t fake_outs_count,
|
||||
const uint64_t unlock_time,
|
||||
uint32_t priority,
|
||||
@@ -10119,7 +10123,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(
|
||||
hw::device &hwdev = m_account.get_device();
|
||||
boost::unique_lock<hw::device> hwdev_lock (hwdev);
|
||||
hw::reset_mode rst(hwdev);
|
||||
using tt = cryptonote::transaction_type;
|
||||
|
||||
auto original_dsts = dsts;
|
||||
|
||||
@@ -10200,12 +10203,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(
|
||||
uint32_t hf_version = get_current_hard_fork();
|
||||
//const auto specific_transfers = m_transfers.at(source_asset);
|
||||
|
||||
// get tx type
|
||||
tt tx_type;
|
||||
THROW_WALLET_EXCEPTION_IF(!get_tx_type(source_asset, dest_asset, tx_type), error::wallet_internal_error, "invalid tx type");
|
||||
oracle::pricing_record pricing_record;
|
||||
std::vector<std::pair<std::string, std::string>> circ_amounts;
|
||||
bool b = false;
|
||||
std::vector<std::pair<std::string, std::string>> circ_amounts;
|
||||
bool b = false;
|
||||
|
||||
// Now perform all sanity checks specific to each tx type
|
||||
switch (tx_type) {
|
||||
@@ -10594,7 +10594,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(
|
||||
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
|
||||
tx.selected_transfers.size() << " inputs");
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config, use_view_tags, source_asset, dest_asset, pricing_record);
|
||||
test_tx, test_ptx, rct_config, use_view_tags, source_asset, dest_asset, tx_type, pricing_record);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
|
||||
@@ -10615,7 +10615,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(
|
||||
LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
|
||||
while (needed_fee > test_ptx.fee) {
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config, use_view_tags, source_asset, dest_asset, pricing_record);
|
||||
test_tx, test_ptx, rct_config, use_view_tags, source_asset, dest_asset, tx_type, pricing_record);
|
||||
txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
|
||||
LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
|
||||
@@ -10689,6 +10689,7 @@ skip_tx:
|
||||
use_view_tags, /* const bool use_view_tags */
|
||||
source_asset,
|
||||
dest_asset,
|
||||
tx_type,
|
||||
pricing_record);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
tx.tx = test_tx;
|
||||
@@ -10987,7 +10988,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
tx.selected_transfers.size() << " outputs");
|
||||
if (use_rct)
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config, use_view_tags, asset_type, asset_type, oracle::pricing_record());
|
||||
test_tx, test_ptx, rct_config, use_view_tags, asset_type, asset_type, cryptonote::transaction_type::TRANSFER, oracle::pricing_record());
|
||||
else
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
@@ -11024,7 +11025,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
}
|
||||
if (use_rct)
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config, use_view_tags, asset_type, asset_type, oracle::pricing_record());
|
||||
test_tx, test_ptx, rct_config, use_view_tags, asset_type, asset_type, cryptonote::transaction_type::TRANSFER, oracle::pricing_record());
|
||||
else
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
@@ -11063,7 +11064,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
|
||||
pending_tx test_ptx;
|
||||
if (use_rct) {
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra,
|
||||
test_tx, test_ptx, rct_config, use_view_tags, asset_type, asset_type, oracle::pricing_record());
|
||||
test_tx, test_ptx, rct_config, use_view_tags, asset_type, asset_type, cryptonote::transaction_type::TRANSFER, oracle::pricing_record());
|
||||
} else {
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
|
||||
|
||||
+18
-7
@@ -553,6 +553,7 @@ private:
|
||||
std::vector<size_t> selected_transfers;
|
||||
std::vector<uint8_t> extra;
|
||||
uint64_t unlock_time;
|
||||
cryptonote::transaction_type tx_type;
|
||||
bool use_rct;
|
||||
rct::RCTConfig rct_config;
|
||||
bool use_view_tags;
|
||||
@@ -1062,12 +1063,22 @@ private:
|
||||
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags);
|
||||
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags,
|
||||
const std::string& source_asset,
|
||||
const std::string& dest_asset,
|
||||
const oracle::pricing_record& pr);
|
||||
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts,
|
||||
const std::vector<size_t>& selected_transfers,
|
||||
size_t fake_outputs_count,
|
||||
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
|
||||
std::unordered_set<crypto::public_key> &valid_public_keys_cache,
|
||||
uint64_t unlock_time,
|
||||
uint64_t fee,
|
||||
const std::vector<uint8_t>& extra,
|
||||
cryptonote::transaction& tx,
|
||||
pending_tx &ptx,
|
||||
const rct::RCTConfig &rct_config,
|
||||
const bool use_view_tags,
|
||||
const std::string& source_asset,
|
||||
const std::string& dest_asset,
|
||||
const cryptonote::transaction_type& tx_type,
|
||||
const oracle::pricing_record& pr);
|
||||
|
||||
void commit_tx(pending_tx& ptx_vector);
|
||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||
@@ -1089,7 +1100,7 @@ private:
|
||||
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
|
||||
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
|
||||
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type& tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices); // pass subaddr_indices by value on purpose
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const std::string &asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, const std::string &asset_type, bool is_subaddress, const size_t outputs, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
|
||||
|
||||
@@ -1080,7 +1080,7 @@ namespace tools
|
||||
{
|
||||
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
|
||||
uint32_t priority = m_wallet->adjust_priority(req.priority);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, static_cast<cryptonote::transaction_type>(req.tx_type), mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
|
||||
|
||||
if (ptx_vector.empty())
|
||||
{
|
||||
@@ -1135,7 +1135,7 @@ namespace tools
|
||||
uint64_t mixin = m_wallet->adjust_mixin(req.ring_size ? req.ring_size - 1 : 0);
|
||||
uint32_t priority = m_wallet->adjust_priority(req.priority);
|
||||
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, req.source_asset, req.dest_asset, static_cast<cryptonote::transaction_type>(req.tx_type), mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
|
||||
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
|
||||
|
||||
if (ptx_vector.empty())
|
||||
|
||||
@@ -537,6 +537,7 @@ namespace wallet_rpc
|
||||
std::list<transfer_destination> destinations;
|
||||
std::string source_asset;
|
||||
std::string dest_asset;
|
||||
uint8_t tx_type;
|
||||
uint32_t account_index;
|
||||
std::set<uint32_t> subaddr_indices;
|
||||
uint32_t priority;
|
||||
@@ -552,6 +553,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE(destinations)
|
||||
KV_SERIALIZE(source_asset)
|
||||
KV_SERIALIZE(dest_asset)
|
||||
KV_SERIALIZE(tx_type)
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(subaddr_indices)
|
||||
KV_SERIALIZE(priority)
|
||||
@@ -602,6 +604,7 @@ namespace wallet_rpc
|
||||
std::list<transfer_destination> destinations;
|
||||
std::string source_asset;
|
||||
std::string dest_asset;
|
||||
uint8_t tx_type;
|
||||
uint32_t account_index;
|
||||
std::set<uint32_t> subaddr_indices;
|
||||
uint32_t priority;
|
||||
@@ -617,6 +620,7 @@ namespace wallet_rpc
|
||||
KV_SERIALIZE(destinations)
|
||||
KV_SERIALIZE(source_asset)
|
||||
KV_SERIALIZE(dest_asset)
|
||||
KV_SERIALIZE(tx_type)
|
||||
KV_SERIALIZE(account_index)
|
||||
KV_SERIALIZE(subaddr_indices)
|
||||
KV_SERIALIZE(priority)
|
||||
|
||||
Reference in New Issue
Block a user