Merge pull request #425

79c95c1 simplewallet: bump default mixin from 3 to 4 (moneromooo-monero)
ac90d48 from hard fork 2, all outputs must be decomposed (moneromooo-monero)
90ccad1 from hard fork 2, claim a quantized reward in coinbase (moneromooo-monero)
22b1570 cryptonote_format_utils: add a function to tell if an amount is canonical (moneromooo-monero)
This commit is contained in:
Riccardo Spagni 2015-10-11 21:28:06 +02:00
commit 8169d429bf
No known key found for this signature in database
GPG key ID: 55432DF31CCD4FCD
9 changed files with 121 additions and 53 deletions

View file

@ -118,6 +118,7 @@ namespace config
uint64_t const DEFAULT_FEE_ATOMIC_XMR_PER_KB = 500; // Just a placeholder! Change me! uint64_t const DEFAULT_FEE_ATOMIC_XMR_PER_KB = 500; // Just a placeholder! Change me!
uint8_t const FEE_CALCULATION_MAX_RETRIES = 10; uint8_t const FEE_CALCULATION_MAX_RETRIES = 10;
uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)10000000000); // pow(10, 10) uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)10000000000); // pow(10, 10)
uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8)
std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000";
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18;

View file

@ -975,6 +975,14 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
return false; return false;
} }
} }
else
{
// from hard fork 2, since a miner can claim less than the full block reward, we update the base_reward
// to show the amount of coins that were actually generated, the remainder will be pushed back for later
// emission. This modifies the emission curve very slightly.
CHECK_AND_ASSERT_MES(money_in_use - fee <= base_reward, false, "base reward calculation bug");
base_reward = money_in_use - fee;
}
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -1093,7 +1101,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
*/ */
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11); bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
@ -1102,7 +1110,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
#endif #endif
for (size_t try_count = 0; try_count != 10; ++try_count) for (size_t try_count = 0; try_count != 10; ++try_count)
{ {
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11); r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, 11, m_hardfork->get_current_version());
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
@ -1964,6 +1972,23 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool Blockchain::check_tx_outputs(const transaction& tx)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// from hard fork 2, we forbid dust and compound outputs
if (m_hardfork->get_current_version() >= 2) {
BOOST_FOREACH(auto &o, tx.vout) {
if (!is_valid_decomposed_amount(o.amount)) {
return false;
}
}
}
return true;
}
//------------------------------------------------------------------
bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);

View file

@ -134,6 +134,7 @@ namespace cryptonote
bool store_blockchain(); bool store_blockchain();
bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, bool kept_by_block = false); bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, bool kept_by_block = false);
bool check_tx_outputs(const transaction& tx);
uint64_t get_current_cumulative_blocksize_limit() const; uint64_t get_current_cumulative_blocksize_limit() const;
bool is_storing_blockchain()const{return m_is_blockchain_storing;} bool is_storing_blockchain()const{return m_is_blockchain_storing;}
uint64_t block_difficulty(uint64_t i) const; uint64_t block_difficulty(uint64_t i) const;

View file

@ -40,6 +40,29 @@ using namespace epee;
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d #define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
static const uint64_t valid_decomposed_outputs[] = {
(uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero
(uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90,
(uint64_t)100, (uint64_t)200, (uint64_t)300, (uint64_t)400, (uint64_t)500, (uint64_t)600, (uint64_t)700, (uint64_t)800, (uint64_t)900,
(uint64_t)1000, (uint64_t)2000, (uint64_t)3000, (uint64_t)4000, (uint64_t)5000, (uint64_t)6000, (uint64_t)7000, (uint64_t)8000, (uint64_t)9000,
(uint64_t)10000, (uint64_t)20000, (uint64_t)30000, (uint64_t)40000, (uint64_t)50000, (uint64_t)60000, (uint64_t)70000, (uint64_t)80000, (uint64_t)90000,
(uint64_t)100000, (uint64_t)200000, (uint64_t)300000, (uint64_t)400000, (uint64_t)500000, (uint64_t)600000, (uint64_t)700000, (uint64_t)800000, (uint64_t)900000,
(uint64_t)1000000, (uint64_t)2000000, (uint64_t)3000000, (uint64_t)4000000, (uint64_t)5000000, (uint64_t)6000000, (uint64_t)7000000, (uint64_t)8000000, (uint64_t)9000000, // 1 micronero
(uint64_t)10000000, (uint64_t)20000000, (uint64_t)30000000, (uint64_t)40000000, (uint64_t)50000000, (uint64_t)60000000, (uint64_t)70000000, (uint64_t)80000000, (uint64_t)90000000,
(uint64_t)100000000, (uint64_t)200000000, (uint64_t)300000000, (uint64_t)400000000, (uint64_t)500000000, (uint64_t)600000000, (uint64_t)700000000, (uint64_t)800000000, (uint64_t)900000000,
(uint64_t)1000000000, (uint64_t)2000000000, (uint64_t)3000000000, (uint64_t)4000000000, (uint64_t)5000000000, (uint64_t)6000000000, (uint64_t)7000000000, (uint64_t)8000000000, (uint64_t)9000000000,
(uint64_t)10000000000, (uint64_t)20000000000, (uint64_t)30000000000, (uint64_t)40000000000, (uint64_t)50000000000, (uint64_t)60000000000, (uint64_t)70000000000, (uint64_t)80000000000, (uint64_t)90000000000,
(uint64_t)100000000000, (uint64_t)200000000000, (uint64_t)300000000000, (uint64_t)400000000000, (uint64_t)500000000000, (uint64_t)600000000000, (uint64_t)700000000000, (uint64_t)800000000000, (uint64_t)900000000000,
(uint64_t)1000000000000, (uint64_t)2000000000000, (uint64_t)3000000000000, (uint64_t)4000000000000, (uint64_t)5000000000000, (uint64_t)6000000000000, (uint64_t)7000000000000, (uint64_t)8000000000000, (uint64_t)9000000000000, // 1 monero
(uint64_t)10000000000000, (uint64_t)20000000000000, (uint64_t)30000000000000, (uint64_t)40000000000000, (uint64_t)50000000000000, (uint64_t)60000000000000, (uint64_t)70000000000000, (uint64_t)80000000000000, (uint64_t)90000000000000,
(uint64_t)100000000000000, (uint64_t)200000000000000, (uint64_t)300000000000000, (uint64_t)400000000000000, (uint64_t)500000000000000, (uint64_t)600000000000000, (uint64_t)700000000000000, (uint64_t)800000000000000, (uint64_t)900000000000000,
(uint64_t)1000000000000000, (uint64_t)2000000000000000, (uint64_t)3000000000000000, (uint64_t)4000000000000000, (uint64_t)5000000000000000, (uint64_t)6000000000000000, (uint64_t)7000000000000000, (uint64_t)8000000000000000, (uint64_t)9000000000000000,
(uint64_t)10000000000000000, (uint64_t)20000000000000000, (uint64_t)30000000000000000, (uint64_t)40000000000000000, (uint64_t)50000000000000000, (uint64_t)60000000000000000, (uint64_t)70000000000000000, (uint64_t)80000000000000000, (uint64_t)90000000000000000,
(uint64_t)100000000000000000, (uint64_t)200000000000000000, (uint64_t)300000000000000000, (uint64_t)400000000000000000, (uint64_t)500000000000000000, (uint64_t)600000000000000000, (uint64_t)700000000000000000, (uint64_t)800000000000000000, (uint64_t)900000000000000000,
(uint64_t)1000000000000000000, (uint64_t)2000000000000000000, (uint64_t)3000000000000000000, (uint64_t)4000000000000000000, (uint64_t)5000000000000000000, (uint64_t)6000000000000000000, (uint64_t)7000000000000000000, (uint64_t)8000000000000000000, (uint64_t)9000000000000000000, // 1 meganero
(uint64_t)10000000000000000000ull
};
namespace cryptonote namespace cryptonote
{ {
//--------------------------------------------------------------- //---------------------------------------------------------------
@ -82,7 +105,7 @@ namespace cryptonote
return true; return true;
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs) { bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
tx.vin.clear(); tx.vin.clear();
tx.vout.clear(); tx.vout.clear();
tx.extra.clear(); tx.extra.clear();
@ -102,6 +125,15 @@ namespace cryptonote
LOG_PRINT_L0("Block is too big"); LOG_PRINT_L0("Block is too big");
return false; return false;
} }
// from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
// keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
// emission schedule
if (hard_fork_version >= 2)
{
block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
}
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
LOG_PRINT_L1("Creating block template: reward " << block_reward << LOG_PRINT_L1("Creating block template: reward " << block_reward <<
", fee " << fee) ", fee " << fee)
@ -109,7 +141,7 @@ namespace cryptonote
block_reward += fee; block_reward += fee;
std::vector<uint64_t> out_amounts; std::vector<uint64_t> out_amounts;
decompose_amount_into_digits(block_reward, ::config::DEFAULT_DUST_THRESHOLD, decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
@ -922,4 +954,11 @@ namespace cryptonote
return get_tx_tree_hash(txs_ids); return get_tx_tree_hash(txs_ids);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool is_valid_decomposed_amount(uint64_t amount)
{
const uint64_t *begin = valid_decomposed_outputs;
const uint64_t *end = valid_decomposed_outputs + sizeof(valid_decomposed_outputs) / sizeof(valid_decomposed_outputs[0]);
return std::binary_search(begin, end, amount);
}
//---------------------------------------------------------------
} }

View file

@ -44,7 +44,7 @@ namespace cryptonote
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 1); bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 1, uint8_t hard_fork_version = 1);
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key);
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key); bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key);
@ -231,6 +231,7 @@ namespace cryptonote
void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h); void get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes, crypto::hash& h);
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes); crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes);
crypto::hash get_tx_tree_hash(const block& b); crypto::hash get_tx_tree_hash(const block& b);
bool is_valid_decomposed_amount(uint64_t amount);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ #define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \

View file

@ -125,6 +125,12 @@ namespace cryptonote
} }
} }
if (!m_blockchain.check_tx_outputs(tx))
{
LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid outout");
tvc.m_verifivation_failed = true;
return false;
}
crypto::hash max_used_block_id = null_hash; crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0; uint64_t max_used_block_height = 0;

View file

@ -74,7 +74,7 @@ typedef cryptonote::simple_wallet sw;
unsigned int epee::g_test_dbg_lock_sleep = 0; unsigned int epee::g_test_dbg_lock_sleep = 0;
#define DEFAULT_MIX 3 #define DEFAULT_MIX 4
namespace namespace
{ {

View file

@ -1143,7 +1143,7 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_
const transfer_details& td = m_transfers[i]; const transfer_details& td = m_transfers[i];
if (!td.m_spent && is_transfer_unlocked(td)) if (!td.m_spent && is_transfer_unlocked(td))
{ {
if (dust < td.amount()) if (dust < td.amount() && is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i); unused_transfers_indices.push_back(i);
else else
unused_dust_indices.push_back(i); unused_dust_indices.push_back(i);
@ -1572,14 +1572,17 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
change_dts.amount = found_money - needed_money; change_dts.amount = found_money - needed_money;
} }
std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
uint64_t dust = 0; uint64_t dust = 0;
std::vector<cryptonote::tx_destination_entry> splitted_dsts; destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust); BOOST_FOREACH(auto& d, dust_dsts) {
THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < dust, error::wallet_internal_error, "invalid dust value: dust = " + THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold)); std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
if (0 != dust && !dust_policy.add_to_fee) }
{ BOOST_FOREACH(auto& d, dust_dsts) {
splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust)); if (!dust_policy.add_to_fee)
splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust));
dust += d.amount;
} }
crypto::secret_key tx_key; crypto::secret_key tx_key;
@ -1669,7 +1672,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const transfer_details& td = m_transfers[i]; const transfer_details& td = m_transfers[i];
if (!td.m_spent && is_transfer_unlocked(td)) if (!td.m_spent && is_transfer_unlocked(td))
{ {
if (::config::DEFAULT_DUST_THRESHOLD <= td.amount()) if (is_valid_decomposed_amount(td.amount()))
unused_transfers_indices.push_back(i); unused_transfers_indices.push_back(i);
else else
unused_dust_indices.push_back(i); unused_dust_indices.push_back(i);
@ -1923,11 +1926,12 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
if (dust_policy.dust_threshold > 0) if (dust_policy.dust_threshold > 0)
money_back = money_back - money_back % dust_policy.dust_threshold; money_back = money_back - money_back % dust_policy.dust_threshold;
dsts.push_back(cryptonote::tx_destination_entry(money_back, m_account_public_address)); dsts.push_back(cryptonote::tx_destination_entry(money_back, m_account_public_address));
uint64_t dust = 0; std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust;
std::vector<cryptonote::tx_destination_entry> splitted_dsts;
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust); destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust);
THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < dust, error::wallet_internal_error, "invalid dust value: dust = " + BOOST_FOREACH(auto& d, dust) {
std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold)); THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
}
crypto::secret_key tx_key; crypto::secret_key tx_key;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key);
@ -1945,7 +1949,7 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
ptx.key_images = key_images; ptx.key_images = key_images;
ptx.fee = money - money_back; ptx.fee = money - money_back;
ptx.dust = dust; ptx.dust = 0;
ptx.tx = tx; ptx.tx = tx;
ptx.change_dts = change_dts; ptx.change_dts = change_dts;
ptx.selected_transfers = selected_transfers; ptx.selected_transfers = selected_transfers;
@ -1961,7 +1965,7 @@ std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i) for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
{ {
const transfer_details& td = *i; const transfer_details& td = *i;
if (!td.m_spent && td.amount() < dust_policy.dust_threshold && is_transfer_unlocked(td)) if (!td.m_spent && (td.amount() < dust_policy.dust_threshold || !is_valid_decomposed_amount(td.amount())) && is_transfer_unlocked(td))
{ {
num_dust_outputs++; num_dust_outputs++;
} }

View file

@ -400,48 +400,36 @@ namespace tools
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
inline void digit_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts, inline void digit_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts,
const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold,
std::vector<cryptonote::tx_destination_entry>& splitted_dsts, uint64_t& dust) std::vector<cryptonote::tx_destination_entry>& splitted_dsts, std::vector<cryptonote::tx_destination_entry> &dust_dsts)
{ {
splitted_dsts.clear(); splitted_dsts.clear();
dust = 0; dust_dsts.clear();
BOOST_FOREACH(auto& de, dsts) BOOST_FOREACH(auto& de, dsts)
{ {
cryptonote::decompose_amount_into_digits(de.amount, dust_threshold, cryptonote::decompose_amount_into_digits(de.amount, 0,
[&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, de.addr)); }, [&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, de.addr)); },
[&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, de.addr)); } ); [&](uint64_t a_dust) { splitted_dsts.push_back(cryptonote::tx_destination_entry(a_dust, de.addr)); } );
} }
cryptonote::decompose_amount_into_digits(change_dst.amount, dust_threshold, cryptonote::decompose_amount_into_digits(change_dst.amount, 0,
[&](uint64_t chunk) { splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr)); }, [&](uint64_t chunk) {
[&](uint64_t a_dust) { dust = a_dust; } ); if (chunk <= dust_threshold)
dust_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr));
else
splitted_dsts.push_back(cryptonote::tx_destination_entry(chunk, change_dst.addr));
},
[&](uint64_t a_dust) { dust_dsts.push_back(cryptonote::tx_destination_entry(a_dust, change_dst.addr)); } );
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
inline void null_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts, inline void null_split_strategy(const std::vector<cryptonote::tx_destination_entry>& dsts,
const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold, const cryptonote::tx_destination_entry& change_dst, uint64_t dust_threshold,
std::vector<cryptonote::tx_destination_entry>& splitted_dsts, uint64_t& dust) std::vector<cryptonote::tx_destination_entry>& splitted_dsts, std::vector<cryptonote::tx_destination_entry> &dust_dsts)
{ {
splitted_dsts = dsts; splitted_dsts = dsts;
dust = 0; dust_dsts.clear();
uint64_t change = change_dst.amount; uint64_t change = change_dst.amount;
if (0 < dust_threshold)
{
for (uint64_t order = 10; order <= 10 * dust_threshold; order *= 10)
{
uint64_t dust_candidate = change_dst.amount % order;
uint64_t change_candidate = (change_dst.amount / order) * order;
if (dust_candidate <= dust_threshold)
{
dust = dust_candidate;
change = change_candidate;
}
else
{
break;
}
}
}
if (0 != change) if (0 != change)
{ {
@ -577,14 +565,17 @@ namespace tools
change_dts.amount = found_money - needed_money; change_dts.amount = found_money - needed_money;
} }
std::vector<cryptonote::tx_destination_entry> splitted_dsts, dust_dsts;
uint64_t dust = 0; uint64_t dust = 0;
std::vector<cryptonote::tx_destination_entry> splitted_dsts; destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust_dsts);
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust); BOOST_FOREACH(auto& d, dust_dsts) {
THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < dust, error::wallet_internal_error, "invalid dust value: dust = " + THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < d.amount, error::wallet_internal_error, "invalid dust value: dust = " +
std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold)); std::to_string(d.amount) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
if (0 != dust && !dust_policy.add_to_fee) }
{ BOOST_FOREACH(auto& d, dust_dsts) {
splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust)); if (!dust_policy.add_to_fee)
splitted_dsts.push_back(cryptonote::tx_destination_entry(d.amount, dust_policy.addr_for_dust));
dust += d.amount;
} }
crypto::secret_key tx_key; crypto::secret_key tx_key;