mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-29 18:53:14 +00:00
add rct to the protocol
It is not yet constrained to a fork, so don't use on the real network or you'll be orphaned or rejected.
This commit is contained in:
parent
211d1db762
commit
dc4aad7eb5
26 changed files with 1219 additions and 285 deletions
|
@ -91,6 +91,8 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
|
|||
for (uint64_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
amount_output_indices.push_back(add_output(tx_hash, tx.vout[i], i, tx.unlock_time));
|
||||
if (tx.version > 1 && tx.vout[i].amount == 0)
|
||||
add_rct_commitment(tx.rct_signatures.outPk[i].mask);
|
||||
}
|
||||
add_tx_amount_output_indices(tx_id, amount_output_indices);
|
||||
}
|
||||
|
|
|
@ -861,6 +861,8 @@ void BlockchainLMDB::remove_tx_outputs(const uint64_t tx_id, const transaction&
|
|||
{
|
||||
const tx_out tx_output = tx.vout[i-1];
|
||||
remove_output(tx_output.amount, amount_output_indices[i-1]);
|
||||
if (tx_output.amount == 0)
|
||||
remove_rct_commitment(amount_output_indices[i-1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
#define CRYPTONOTE_MAX_TX_SIZE 1000000000
|
||||
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
|
||||
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
|
||||
#define CURRENT_TRANSACTION_VERSION 1
|
||||
#define CURRENT_TRANSACTION_VERSION 2
|
||||
#define CURRENT_BLOCK_MAJOR_VERSION 1
|
||||
#define CURRENT_BLOCK_MINOR_VERSION 0
|
||||
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "crypto/hash.h"
|
||||
#include "cryptonote_core/checkpoints.h"
|
||||
#include "cryptonote_core/cryptonote_core.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
#if defined(PER_BLOCK_CHECKPOINT)
|
||||
#include "blocks/blocks.h"
|
||||
#endif
|
||||
|
@ -127,7 +128,7 @@ bool Blockchain::have_tx_keyimg_as_spent(const crypto::key_image &key_im) const
|
|||
// and collects the public key for each from the transaction it was included in
|
||||
// via the visitor passed to it.
|
||||
template <class visitor_t>
|
||||
bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const
|
||||
bool Blockchain::scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
|
@ -206,8 +207,21 @@ bool Blockchain::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, vi
|
|||
else
|
||||
output_index = m_db->get_output_key(tx_in_to_key.amount, i);
|
||||
|
||||
rct::key commitment;
|
||||
if (tx_version > 1)
|
||||
{
|
||||
if (tx_in_to_key.amount == 0)
|
||||
commitment = m_db->get_rct_commitment(i);
|
||||
else
|
||||
commitment = rct::zeroCommit(tx_in_to_key.amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
rct::identity(commitment);
|
||||
}
|
||||
|
||||
// call to the passed boost visitor to grab the public key for the output
|
||||
if (!vis.handle_output(output_index.unlock_time, output_index.pubkey))
|
||||
if (!vis.handle_output(output_index.unlock_time, output_index.pubkey, commitment))
|
||||
{
|
||||
LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i);
|
||||
return false;
|
||||
|
@ -1086,6 +1100,8 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
{
|
||||
LOG_ERROR("Creating block template: error: invalid transaction size");
|
||||
}
|
||||
if (cur_tx.tx.version == 1)
|
||||
{
|
||||
uint64_t inputs_amount;
|
||||
if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
|
||||
{
|
||||
|
@ -1096,6 +1112,14 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
LOG_ERROR("Creating block template: error: invalid fee");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cur_tx.fee != cur_tx.tx.txnFee)
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: invalid fee");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txs_size != real_txs_size)
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: wrongly calculated transaction size");
|
||||
|
@ -1599,16 +1623,25 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
|
|||
//------------------------------------------------------------------
|
||||
// This function adds the ringct output at index i to the list
|
||||
// unlocked and other such checks should be done by here.
|
||||
void Blockchain::add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, size_t i) const
|
||||
void Blockchain::add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||
|
||||
COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& oen = *outs.insert(outs.end(), COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry());
|
||||
oen.amount = amount;
|
||||
oen.global_amount_index = i;
|
||||
output_data_t data = m_db->get_output_key(0, i);
|
||||
output_data_t data = m_db->get_output_key(amount, i);
|
||||
oen.out_key = data.pubkey;
|
||||
if (amount == 0)
|
||||
{
|
||||
oen.commitment = m_db->get_rct_commitment(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a rct output, make a fake commitment with zero key
|
||||
oen.commitment = rct::zeroCommit(amount);
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
// This function takes an RPC request for mixins and creates an RPC response
|
||||
|
@ -1648,7 +1681,7 @@ bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::r
|
|||
// if tx is unlocked, add output to result_outs
|
||||
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
|
||||
{
|
||||
add_out_to_get_rct_random_outs(res.outs, i);
|
||||
add_out_to_get_rct_random_outs(res.outs, 0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1688,10 +1721,44 @@ bool Blockchain::get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::r
|
|||
// our list.
|
||||
if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
|
||||
{
|
||||
add_out_to_get_rct_random_outs(res.outs, i);
|
||||
add_out_to_get_rct_random_outs(res.outs, 0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res.outs.size() < req.outs_count)
|
||||
return false;
|
||||
#if 0
|
||||
// if we do not have enough RCT inputs, we can pick from the non RCT ones
|
||||
// which will have a zero mask
|
||||
if (res.outs.size() < req.outs_count)
|
||||
{
|
||||
LOG_PRINT_L0("Out of RCT inputs (" << res.outs.size() << "/" << req.outs_count << "), using regular ones");
|
||||
|
||||
// TODO: arbitrary selection, needs better
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req2 = AUTO_VAL_INIT(req2);
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res2 = AUTO_VAL_INIT(res2);
|
||||
req2.outs_count = req.outs_count - res.outs.size();
|
||||
static const uint64_t amounts[] = {1, 10, 20, 50, 100, 200, 500, 1000, 10000};
|
||||
for (uint64_t a: amounts)
|
||||
req2.amounts.push_back(a);
|
||||
if (!get_random_outs_for_amounts(req2, res2))
|
||||
return false;
|
||||
|
||||
// pick random ones from there
|
||||
while (res.outs.size() < req.outs_count)
|
||||
{
|
||||
int list_idx = rand() % (sizeof(amounts)/sizeof(amounts[0]));
|
||||
if (!res2.outs[list_idx].outs.empty())
|
||||
{
|
||||
const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry oe = res2.outs[list_idx].outs.back();
|
||||
res2.outs[list_idx].outs.pop_back();
|
||||
add_out_to_get_rct_random_outs(res.outs, res2.outs[list_idx].amount, oe.global_amount_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
@ -2153,12 +2220,27 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
|
|||
// from hard fork 2, we forbid dust and compound outputs
|
||||
if (m_hardfork->get_current_version() >= 2) {
|
||||
for (auto &o: tx.vout) {
|
||||
if (tx.version == 1)
|
||||
{
|
||||
if (!is_valid_decomposed_amount(o.amount)) {
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in a v2 tx, all outputs must have 0 amount
|
||||
if (m_hardfork->get_current_version() >= 3) {
|
||||
if (tx.version >= 2) {
|
||||
for (auto &o: tx.vout) {
|
||||
if (o.amount != 0) {
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2238,7 +2320,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
}
|
||||
|
||||
uint64_t t_t1 = 0;
|
||||
std::vector<std::vector<crypto::public_key>> pubkeys(tx.vin.size());
|
||||
std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
|
||||
std::vector < uint64_t > results;
|
||||
results.resize(tx.vin.size(), 0);
|
||||
|
||||
|
@ -2287,6 +2369,8 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
return false;
|
||||
}
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
// basically, make sure number of inputs == number of signatures
|
||||
CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
|
||||
|
||||
|
@ -2305,10 +2389,11 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
continue;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// make sure that output being spent matches up correctly with the
|
||||
// signature spending it.
|
||||
if (!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pubkeys[sig_index], pmax_used_block_height))
|
||||
if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height))
|
||||
{
|
||||
it->second[in_to_key.k_image] = false;
|
||||
LOG_PRINT_L1("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
|
||||
|
@ -2320,6 +2405,8 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
return false;
|
||||
}
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
if (threads > 1)
|
||||
{
|
||||
// ND: Speedup
|
||||
|
@ -2343,12 +2430,15 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
}
|
||||
it->second[in_to_key.k_image] = true;
|
||||
}
|
||||
}
|
||||
|
||||
sig_index++;
|
||||
}
|
||||
|
||||
KILL_IOSERVICE();
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
if (threads > 1)
|
||||
{
|
||||
// save results to table, passed or otherwise
|
||||
|
@ -2367,12 +2457,60 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
return false;
|
||||
}
|
||||
}
|
||||
LOG_PRINT_L1("t_loop: " << t_t1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// from version 2, check ringct signatures
|
||||
|
||||
// RCT needs the same mixin for all inputs
|
||||
for (size_t n = 1; n < pubkeys.size(); ++n)
|
||||
{
|
||||
if (pubkeys[n].size() != pubkeys[0].size())
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool size_matches = true;
|
||||
for (size_t i = 0; i < pubkeys.size(); ++i)
|
||||
size_matches &= pubkeys[i].size() == tx.rct_signatures.mixRing.size();
|
||||
for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i)
|
||||
size_matches &= pubkeys.size() == tx.rct_signatures.mixRing[i].size();
|
||||
if (!size_matches)
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < pubkeys.size(); ++n)
|
||||
{
|
||||
for (size_t m = 0; m < pubkeys[n].size(); ++m)
|
||||
{
|
||||
if (pubkeys[n][m].dest != rct::rct2pk(tx.rct_signatures.mixRing[m][n].dest))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m);
|
||||
return false;
|
||||
}
|
||||
if (pubkeys[n][m].mask != rct::rct2pk(tx.rct_signatures.mixRing[m][n].mask))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!rct::verRct(tx.rct_signatures))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ringct signatures!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------
|
||||
void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
|
||||
void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
|
||||
{
|
||||
if (m_is_in_checkpoint_zone)
|
||||
{
|
||||
|
@ -2383,7 +2521,8 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const
|
|||
std::vector<const crypto::public_key *> p_output_keys;
|
||||
for (auto &key : pubkeys)
|
||||
{
|
||||
p_output_keys.push_back(&key);
|
||||
// rct::key and crypto::public_key have the same structure, avoid object ctor/memcpy
|
||||
p_output_keys.push_back(&(const crypto::public_key&)key.dest);
|
||||
}
|
||||
|
||||
result = crypto::check_ring_signature(tx_prefix_hash, key_image, p_output_keys, sig.data()) ? 1 : 0;
|
||||
|
@ -2419,7 +2558,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
|
|||
// This function locates all outputs associated with a given input (mixins)
|
||||
// and validates that they exist and are usable. It also checks the ring
|
||||
// signature for each input.
|
||||
bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height)
|
||||
bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
|
@ -2429,13 +2568,13 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
|
|||
|
||||
struct outputs_visitor
|
||||
{
|
||||
std::vector<crypto::public_key >& m_output_keys;
|
||||
std::vector<rct::ctkey >& m_output_keys;
|
||||
const Blockchain& m_bch;
|
||||
outputs_visitor(std::vector<crypto::public_key>& output_keys, const Blockchain& bch) :
|
||||
outputs_visitor(std::vector<rct::ctkey>& output_keys, const Blockchain& bch) :
|
||||
m_output_keys(output_keys), m_bch(bch)
|
||||
{
|
||||
}
|
||||
bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey)
|
||||
bool handle_output(uint64_t unlock_time, const crypto::public_key &pubkey, const rct::key &commitment)
|
||||
{
|
||||
//check tx unlock time
|
||||
if (!m_bch.is_tx_spendtime_unlocked(unlock_time))
|
||||
|
@ -2449,7 +2588,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
|
|||
// but only txout_to_key outputs are stored in the DB in the first place, done in
|
||||
// Blockchain*::add_output
|
||||
|
||||
m_output_keys.push_back(pubkey);
|
||||
m_output_keys.push_back(rct::ctkey({rct::pk2rct(pubkey), commitment}));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -2458,7 +2597,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
|
|||
|
||||
// collect output keys
|
||||
outputs_visitor vi(output_keys, *this);
|
||||
if (!scan_outputkeys_for_indexes(txin, vi, tx_prefix_hash, pmax_related_block_height))
|
||||
if (!scan_outputkeys_for_indexes(tx_version, txin, vi, tx_prefix_hash, pmax_related_block_height))
|
||||
{
|
||||
LOG_PRINT_L1("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size());
|
||||
return false;
|
||||
|
@ -2469,7 +2608,13 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
|
|||
LOG_PRINT_L1("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size());
|
||||
return false;
|
||||
}
|
||||
if (tx_version == 1) {
|
||||
CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -884,11 +884,12 @@ namespace cryptonote
|
|||
* @param vis an instance of the visitor to use
|
||||
* @param tx_prefix_hash the hash of the associated transaction_prefix
|
||||
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
|
||||
* @param tx_version version of the tx, if > 1 we also get commitments
|
||||
*
|
||||
* @return false if any keys are not found or any inputs are not unlocked, otherwise true
|
||||
*/
|
||||
template<class visitor_t>
|
||||
inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
|
||||
inline bool scan_outputkeys_for_indexes(size_t tx_version, const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
|
||||
|
||||
/**
|
||||
* @brief collect output public keys of a transaction input set
|
||||
|
@ -900,15 +901,17 @@ namespace cryptonote
|
|||
* If pmax_related_block_height is not NULL, its value is set to the height
|
||||
* of the most recent block which contains an output used in the input set
|
||||
*
|
||||
* @param tx_version the transaction version
|
||||
* @param txin the transaction input
|
||||
* @param tx_prefix_hash the transaction prefix hash, for caching organization
|
||||
* @param sig the input signature
|
||||
* @param output_keys return-by-reference the public keys of the outputs in the input set
|
||||
* @param rct_signatures the ringCT signatures, which are only valid if tx version > 1
|
||||
* @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
|
||||
*
|
||||
* @return false if any output is not yet unlocked, or is missing, otherwise true
|
||||
*/
|
||||
bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height);
|
||||
bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height);
|
||||
|
||||
/**
|
||||
* @brief validate a transaction's inputs and their keys
|
||||
|
@ -1074,9 +1077,10 @@ namespace cryptonote
|
|||
* @brief adds the given output to the requested set of random ringct outputs
|
||||
*
|
||||
* @param outs return-by-reference the set the output is to be added to
|
||||
* @param amount the output amount (0 for rct inputs)
|
||||
* @param i the rct output index
|
||||
*/
|
||||
void add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, size_t i) const;
|
||||
void add_out_to_get_rct_random_outs(std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>& outs, uint64_t amount, size_t i) const;
|
||||
|
||||
/**
|
||||
* @brief checks if a transaction is unlocked (its outputs spendable)
|
||||
|
@ -1197,7 +1201,7 @@ namespace cryptonote
|
|||
* @param result false if the ring signature is invalid, otherwise true
|
||||
*/
|
||||
void check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image,
|
||||
const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
|
||||
const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
|
||||
|
||||
/**
|
||||
* @brief loads block hashes from compiled-in data set
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "crypto/hash.h"
|
||||
#include "misc_language.h"
|
||||
#include "tx_extra.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
|
||||
namespace cryptonote
|
||||
{
|
||||
|
@ -172,7 +173,7 @@ namespace cryptonote
|
|||
|
||||
BEGIN_SERIALIZE()
|
||||
VARINT_FIELD(version)
|
||||
if(CURRENT_TRANSACTION_VERSION < version) return false;
|
||||
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
|
||||
VARINT_FIELD(unlock_time)
|
||||
FIELD(vin)
|
||||
FIELD(vout)
|
||||
|
@ -187,6 +188,7 @@ namespace cryptonote
|
|||
{
|
||||
public:
|
||||
std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count
|
||||
rct::rctSig rct_signatures;
|
||||
|
||||
transaction();
|
||||
virtual ~transaction();
|
||||
|
@ -195,6 +197,8 @@ namespace cryptonote
|
|||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELDS(*static_cast<transaction_prefix *>(this))
|
||||
|
||||
if (version == 1)
|
||||
{
|
||||
ar.tag("signatures");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
|
||||
|
@ -223,6 +227,16 @@ namespace cryptonote
|
|||
ar.delimit_array();
|
||||
}
|
||||
ar.end_array();
|
||||
}
|
||||
else
|
||||
{
|
||||
FIELD(rct_signatures)
|
||||
for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i)
|
||||
{
|
||||
if (rct_signatures.mixRing[i].size() != vin.size())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
END_SERIALIZE()
|
||||
|
||||
private:
|
||||
|
@ -245,7 +259,7 @@ namespace cryptonote
|
|||
inline
|
||||
void transaction::set_null()
|
||||
{
|
||||
version = 0;
|
||||
version = 1;
|
||||
unlock_time = 0;
|
||||
vin.clear();
|
||||
vout.clear();
|
||||
|
|
|
@ -148,7 +148,10 @@ namespace boost
|
|||
a & x.vin;
|
||||
a & x.vout;
|
||||
a & x.extra;
|
||||
if (x.version == 1)
|
||||
a & x.signatures;
|
||||
else
|
||||
a & x.rct_signatures;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ using namespace epee;
|
|||
#include "misc_language.h"
|
||||
#include <csignal>
|
||||
#include "cryptonote_core/checkpoints.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "blockchain_db/blockchain_db.h"
|
||||
#include "blockchain_db/lmdb/db_lmdb.h"
|
||||
#if defined(BERKELEY_DB)
|
||||
|
@ -552,6 +553,22 @@ namespace cryptonote
|
|||
LOG_PRINT_RED_L1("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
if (tx.version > 1)
|
||||
{
|
||||
if (tx.rct_signatures.outPk.size() != tx.vout.size())
|
||||
{
|
||||
LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
for (size_t n = 0; n < tx.vout.size(); ++n)
|
||||
{
|
||||
if (tx.rct_signatures.outPk[n].dest != boost::get<txout_to_key>(tx.vout[n].target).key)
|
||||
{
|
||||
LOG_PRINT_RED_L1("tx ringct public key does not match output public key for tx id= " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!check_money_overflow(tx))
|
||||
{
|
||||
|
@ -559,6 +576,8 @@ namespace cryptonote
|
|||
return false;
|
||||
}
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
uint64_t amount_in = 0;
|
||||
get_inputs_money_amount(tx, amount_in);
|
||||
uint64_t amount_out = get_outs_money_amount(tx);
|
||||
|
@ -568,6 +587,8 @@ namespace cryptonote
|
|||
LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// for version > 1, ringct signatures check verifies amounts match
|
||||
|
||||
if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
|
||||
{
|
||||
|
|
|
@ -37,6 +37,7 @@ using namespace epee;
|
|||
#include "miner.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
|
||||
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
|
||||
|
||||
|
@ -181,7 +182,7 @@ namespace cryptonote
|
|||
|
||||
CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
|
||||
|
||||
tx.version = CURRENT_TRANSACTION_VERSION;
|
||||
tx.version = 1;
|
||||
//lock
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
tx.vin.push_back(in);
|
||||
|
@ -252,6 +253,11 @@ namespace cryptonote
|
|||
//---------------------------------------------------------------
|
||||
bool get_tx_fee(const transaction& tx, uint64_t & fee)
|
||||
{
|
||||
if (tx.version > 1)
|
||||
{
|
||||
fee = tx.rct_signatures.txnFee;
|
||||
return true;
|
||||
}
|
||||
uint64_t amount_in = 0;
|
||||
uint64_t amount_out = 0;
|
||||
BOOST_FOREACH(auto& in, tx.vin)
|
||||
|
@ -447,13 +453,14 @@ namespace cryptonote
|
|||
return encrypt_payment_id(payment_id, public_key, secret_key);
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key)
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
|
||||
{
|
||||
tx.vin.clear();
|
||||
tx.vout.clear();
|
||||
tx.signatures.clear();
|
||||
tx.rct_signatures = rct::rctSig();
|
||||
|
||||
tx.version = CURRENT_TRANSACTION_VERSION;
|
||||
tx.version = rct ? 2 : 1;
|
||||
tx.unlock_time = unlock_time;
|
||||
|
||||
tx.extra = extra;
|
||||
|
@ -509,6 +516,18 @@ namespace cryptonote
|
|||
};
|
||||
std::vector<input_generation_context_data> in_contexts;
|
||||
|
||||
if (tx.version > 1)
|
||||
{
|
||||
// ringct requires all real inputs to be at the same index for all inputs // TODO
|
||||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
{
|
||||
if(src_entr.real_output != sources.begin()->real_output)
|
||||
{
|
||||
LOG_ERROR("All inputs must have the same index for ringct");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t summary_inputs_money = 0;
|
||||
//fill inputs
|
||||
|
@ -529,7 +548,7 @@ namespace cryptonote
|
|||
return false;
|
||||
|
||||
//check that derivated key is equal with real output key
|
||||
if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) )
|
||||
if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
|
||||
{
|
||||
LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:"
|
||||
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
|
||||
|
@ -559,7 +578,7 @@ namespace cryptonote
|
|||
size_t output_index = 0;
|
||||
BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount);
|
||||
CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
|
||||
crypto::key_derivation derivation;
|
||||
crypto::public_key out_eph_public_key;
|
||||
bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
|
||||
|
@ -586,6 +605,8 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
//generate ring signatures
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
@ -596,10 +617,14 @@ namespace cryptonote
|
|||
{
|
||||
ss_ring_s << "pub_keys:" << ENDL;
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
std::vector<crypto::public_key> keys(src_entr.outputs.size());
|
||||
size_t ii = 0;
|
||||
BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
|
||||
{
|
||||
keys_ptrs.push_back(&o.second);
|
||||
ss_ring_s << o.second << ENDL;
|
||||
keys[ii] = rct2pk(o.second.dest);
|
||||
keys_ptrs.push_back(&keys[ii]);
|
||||
ss_ring_s << o.second.dest << ENDL;
|
||||
++ii;
|
||||
}
|
||||
|
||||
tx.signatures.push_back(std::vector<crypto::signature>());
|
||||
|
@ -613,6 +638,67 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3);
|
||||
}
|
||||
else
|
||||
{
|
||||
// enforce same mixin for all outputs
|
||||
size_t n_total_outs = sources[0].outputs.size();
|
||||
for (size_t i = 1; i < sources.size(); ++i) {
|
||||
if (n_total_outs != sources[i].outputs.size()) {
|
||||
LOG_ERROR("Ringct transaction has varying mixin");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t amount_in = 0, amount_out = 0;
|
||||
rct::ctkeyV inSk;
|
||||
rct::ctkeyM mixRing(n_total_outs);
|
||||
rct::keyV destinations;
|
||||
std::vector<uint64_t> amounts;
|
||||
for (size_t i = 0; i < sources.size(); ++i)
|
||||
{
|
||||
rct::ctkey ctkey;
|
||||
amount_in += sources[i].amount;
|
||||
// inSk: (secret key, mask)
|
||||
ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
|
||||
ctkey.mask = sources[i].mask;
|
||||
inSk.push_back(ctkey);
|
||||
// inPk: (public key, commitment)
|
||||
// will be done when filling in mixRing
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
|
||||
amounts.push_back(tx.vout[i].amount);
|
||||
amount_out += tx.vout[i].amount;
|
||||
}
|
||||
for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
|
||||
{
|
||||
mixRing[i].resize(sources.size());
|
||||
for (size_t n = 0; n < sources.size(); ++n)
|
||||
{
|
||||
mixRing[i][n] = sources[n].outputs[i].second;
|
||||
}
|
||||
}
|
||||
|
||||
// fee
|
||||
if (amount_in > amount_out)
|
||||
amounts.push_back(amount_in - amount_out);
|
||||
|
||||
LOG_PRINT_L1("Signing tx: " << obj_to_json_str(tx));
|
||||
tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, sources[0].real_output); // same index assumption
|
||||
|
||||
// zero out all amounts to mask rct outputs, real amounts are now encrypted
|
||||
for (size_t i = 0; i < tx.vin.size(); ++i)
|
||||
{
|
||||
if (!(sources[i].mask == rct::identity()))
|
||||
boost::get<txin_to_key>(tx.vin[i]).amount = 0;
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
tx.vout[i].amount = 0;
|
||||
|
||||
LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -661,7 +747,10 @@ namespace cryptonote
|
|||
<< out.target.type().name() << ", expected " << typeid(txout_to_key).name()
|
||||
<< ", in transaction id=" << get_transaction_hash(tx));
|
||||
|
||||
CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx));
|
||||
if (tx.version == 1)
|
||||
{
|
||||
CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount output in transaction id=" << get_transaction_hash(tx));
|
||||
}
|
||||
|
||||
if(!check_key(boost::get<txout_to_key>(out.target).key))
|
||||
return false;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "include_base_utils.h"
|
||||
#include "crypto/crypto.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
|
||||
namespace cryptonote
|
||||
|
@ -50,13 +51,16 @@ namespace cryptonote
|
|||
|
||||
struct tx_source_entry
|
||||
{
|
||||
typedef std::pair<uint64_t, crypto::public_key> output_entry;
|
||||
typedef std::pair<uint64_t, rct::ctkey> output_entry;
|
||||
|
||||
std::vector<output_entry> outputs; //index + key
|
||||
std::vector<output_entry> outputs; //index + key + optional ringct commitment
|
||||
size_t real_output; //index in outputs vector of real output_entry
|
||||
crypto::public_key real_out_tx_key; //incoming real tx public key
|
||||
size_t real_output_in_tx_index; //index in transaction outputs vector
|
||||
uint64_t amount; //money
|
||||
rct::key mask; //ringct amount mask
|
||||
|
||||
void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
|
||||
};
|
||||
|
||||
struct tx_destination_entry
|
||||
|
@ -70,7 +74,7 @@ namespace cryptonote
|
|||
|
||||
//---------------------------------------------------------------
|
||||
bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey);
|
||||
bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &txkey, bool rct = false);
|
||||
|
||||
template<typename T>
|
||||
bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field)
|
||||
|
|
|
@ -78,6 +78,19 @@ namespace cryptonote
|
|||
//---------------------------------------------------------------------------------
|
||||
bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version)
|
||||
{
|
||||
if (tx.version == 0)
|
||||
{
|
||||
// v0 never accepted
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
if (tx.version > 2) // TODO: max 1/2 needs to be conditioned by a hard fork
|
||||
{
|
||||
// v2 is the latest one we know
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// we do not accept transactions that timed out before, unless they're
|
||||
// kept_by_block
|
||||
if (!kept_by_block && m_timed_out_transactions.find(id) != m_timed_out_transactions.end())
|
||||
|
@ -95,6 +108,11 @@ namespace cryptonote
|
|||
return false;
|
||||
}
|
||||
|
||||
// fee per kilobyte, size rounded up.
|
||||
uint64_t fee;
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
uint64_t inputs_amount = 0;
|
||||
if(!get_inputs_money_amount(tx, inputs_amount))
|
||||
{
|
||||
|
@ -103,7 +121,6 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
uint64_t outputs_amount = get_outs_money_amount(tx);
|
||||
|
||||
if(outputs_amount >= inputs_amount)
|
||||
{
|
||||
LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
|
||||
|
@ -112,8 +129,13 @@ namespace cryptonote
|
|||
return false;
|
||||
}
|
||||
|
||||
// fee per kilobyte, size rounded up.
|
||||
uint64_t fee = inputs_amount - outputs_amount;
|
||||
fee = inputs_amount - outputs_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
fee = tx.rct_signatures.txnFee;
|
||||
}
|
||||
|
||||
uint64_t needed_fee = blob_size / 1024;
|
||||
needed_fee += (blob_size % 1024) ? 1 : 0;
|
||||
needed_fee *= FEE_PER_KB;
|
||||
|
@ -150,7 +172,7 @@ namespace cryptonote
|
|||
|
||||
if (!m_blockchain.check_tx_outputs(tx, tvc))
|
||||
{
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid outout");
|
||||
LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid output");
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
|
@ -170,7 +192,7 @@ namespace cryptonote
|
|||
CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool");
|
||||
txd_p.first->second.blob_size = blob_size;
|
||||
txd_p.first->second.tx = tx;
|
||||
txd_p.first->second.fee = inputs_amount - outputs_amount;
|
||||
txd_p.first->second.fee = fee;
|
||||
txd_p.first->second.max_used_block_id = null_hash;
|
||||
txd_p.first->second.max_used_block_height = 0;
|
||||
txd_p.first->second.kept_by_block = kept_by_block;
|
||||
|
@ -193,7 +215,7 @@ namespace cryptonote
|
|||
txd_p.first->second.blob_size = blob_size;
|
||||
txd_p.first->second.tx = tx;
|
||||
txd_p.first->second.kept_by_block = kept_by_block;
|
||||
txd_p.first->second.fee = inputs_amount - outputs_amount;
|
||||
txd_p.first->second.fee = fee;
|
||||
txd_p.first->second.max_used_block_id = max_used_block_id;
|
||||
txd_p.first->second.max_used_block_height = max_used_block_height;
|
||||
txd_p.first->second.last_failed_height = 0;
|
||||
|
|
|
@ -537,6 +537,9 @@ namespace rct {
|
|||
bool verRct(const rctSig & rv) {
|
||||
CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs");
|
||||
|
||||
// some rct ops can throw
|
||||
try
|
||||
{
|
||||
size_t i = 0;
|
||||
bool rvb = true;
|
||||
bool tmp;
|
||||
|
@ -554,6 +557,11 @@ namespace rct {
|
|||
|
||||
return (rvb && mgVerd);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//RingCT protocol
|
||||
//genRct:
|
||||
|
|
|
@ -329,6 +329,7 @@ namespace cryptonote
|
|||
#pragma pack (push, 1)
|
||||
struct out_entry
|
||||
{
|
||||
uint64_t amount;
|
||||
uint64_t global_amount_index;
|
||||
crypto::public_key out_key;
|
||||
rct::key commitment;
|
||||
|
|
|
@ -99,6 +99,12 @@ typedef cryptonote::simple_wallet sw;
|
|||
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
|
||||
})
|
||||
|
||||
enum TransferType {
|
||||
TransferOriginal,
|
||||
TransferNew,
|
||||
TransferRingCT,
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
const command_line::arg_descriptor<std::string> arg_wallet_file = {"wallet-file", sw::tr("Use wallet <arg>"), ""};
|
||||
|
@ -644,6 +650,7 @@ simple_wallet::simple_wallet()
|
|||
m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height"));
|
||||
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 0 to maximum available)"));
|
||||
m_cmd_binder.set_handler("transfer_new", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer, but using a new transaction building algorithm"));
|
||||
m_cmd_binder.set_handler("transfer_rct", boost::bind(&simple_wallet::transfer_rct, this, _1), tr("Same as transfer, but using RingCT transactions"));
|
||||
m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0"));
|
||||
m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("Send all unlocked balance an address"));
|
||||
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log <level> - Change current log detail level, <0-4>"));
|
||||
|
@ -1888,24 +1895,24 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block
|
|||
m_refresh_progress_reporter.update(height, false);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index)
|
||||
void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
|
||||
{
|
||||
message_writer(epee::log_space::console_color_green, false) << "\r" <<
|
||||
tr("Height ") << height << ", " <<
|
||||
tr("transaction ") << get_transaction_hash(tx) << ", " <<
|
||||
tr("received ") << print_money(tx.vout[out_index].amount);
|
||||
tr("received ") << print_money(amount);
|
||||
if (m_auto_refresh_refreshing)
|
||||
m_cmd_binder.print_prompt();
|
||||
else
|
||||
m_refresh_progress_reporter.update(height, true);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx)
|
||||
void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx)
|
||||
{
|
||||
message_writer(epee::log_space::console_color_magenta, false) << "\r" <<
|
||||
tr("Height ") << height << ", " <<
|
||||
tr("transaction ") << get_transaction_hash(spend_tx) << ", " <<
|
||||
tr("spent ") << print_money(in_tx.vout[out_index].amount);
|
||||
tr("spent ") << print_money(amount);
|
||||
if (m_auto_refresh_refreshing)
|
||||
m_cmd_binder.print_prompt();
|
||||
else
|
||||
|
@ -2052,13 +2059,15 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
|
|||
{
|
||||
if (!transfers_found)
|
||||
{
|
||||
message_writer() << boost::format("%21s%8s%16s%68s") % tr("amount") % tr("spent") % tr("global index") % tr("tx id");
|
||||
message_writer() << boost::format("%21s%8s%12s%8s%16s%68s") % tr("amount") % tr("spent") % tr("unlocked") % tr("ringct") % tr("global index") % tr("tx id");
|
||||
transfers_found = true;
|
||||
}
|
||||
message_writer(td.m_spent ? epee::log_space::console_color_magenta : epee::log_space::console_color_green, false) <<
|
||||
boost::format("%21s%8s%16u%68s") %
|
||||
boost::format("%21s%8s%12s%8s%16u%68s") %
|
||||
print_money(td.amount()) %
|
||||
(td.m_spent ? tr("T") : tr("F")) %
|
||||
(m_wallet->is_transfer_unlocked(td) ? tr("unlocked") : tr("locked")) %
|
||||
(td.is_rct() ? tr("RingCT") : tr("-")) %
|
||||
td.m_global_output_index %
|
||||
get_transaction_hash (td.m_tx);
|
||||
}
|
||||
|
@ -2274,7 +2283,7 @@ bool simple_wallet::get_address_from_str(const std::string &str, cryptonote::acc
|
|||
return true;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::string> &args_)
|
||||
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_)
|
||||
{
|
||||
if (!try_connect_to_daemon())
|
||||
return true;
|
||||
|
@ -2386,10 +2395,20 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
|
|||
{
|
||||
// figure out what tx will be necessary
|
||||
std::vector<tools::wallet2::pending_tx> ptx_vector;
|
||||
if (new_algorithm)
|
||||
switch (transfer_type)
|
||||
{
|
||||
case TransferNew:
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
|
||||
else
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Unknown transfer method, using original");
|
||||
case TransferOriginal:
|
||||
ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
|
||||
break;
|
||||
case TransferRingCT:
|
||||
ptx_vector = m_wallet->create_transactions_rct(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
|
||||
break;
|
||||
}
|
||||
|
||||
// if more than one tx necessary, prompt user to confirm
|
||||
if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
|
||||
|
@ -2531,14 +2550,18 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
|
|||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
||||
{
|
||||
return transfer_main(false, args_);
|
||||
return transfer_main(TransferOriginal, args_);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::transfer_new(const std::vector<std::string> &args_)
|
||||
{
|
||||
return transfer_main(true, args_);
|
||||
return transfer_main(TransferNew, args_);
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::transfer_rct(const std::vector<std::string> &args_)
|
||||
{
|
||||
return transfer_main(TransferRingCT, args_);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
|
||||
{
|
||||
|
@ -3228,9 +3251,8 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
|
|||
m_wallet->get_unconfirmed_payments_out(upayments);
|
||||
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
|
||||
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
|
||||
uint64_t amount = 0;
|
||||
cryptonote::get_inputs_money_amount(pd.m_tx, amount);
|
||||
uint64_t fee = amount - get_outs_money_amount(pd.m_tx);
|
||||
uint64_t amount = pd.m_amount_in;
|
||||
uint64_t fee = amount - pd.m_amount_out;
|
||||
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
|
||||
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
|
||||
payment_id = payment_id.substr(0,16);
|
||||
|
|
|
@ -120,9 +120,10 @@ namespace cryptonote
|
|||
bool show_incoming_transfers(const std::vector<std::string> &args);
|
||||
bool show_payments(const std::vector<std::string> &args);
|
||||
bool show_blockchain_height(const std::vector<std::string> &args);
|
||||
bool transfer_main(bool new_algorithm, const std::vector<std::string> &args);
|
||||
bool transfer_main(int transfer_type, const std::vector<std::string> &args);
|
||||
bool transfer(const std::vector<std::string> &args);
|
||||
bool transfer_new(const std::vector<std::string> &args);
|
||||
bool transfer_rct(const std::vector<std::string> &args);
|
||||
bool sweep_all(const std::vector<std::string> &args);
|
||||
bool sweep_unmixable(const std::vector<std::string> &args);
|
||||
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
|
||||
|
@ -171,8 +172,8 @@ namespace cryptonote
|
|||
|
||||
//----------------- i_wallet2_callback ---------------------
|
||||
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
|
||||
virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index);
|
||||
virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx);
|
||||
virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount);
|
||||
virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx);
|
||||
virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx);
|
||||
//----------------------------------------------------------
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ using namespace epee;
|
|||
#include "rapidjson/stringbuffer.h"
|
||||
#include "common/json_util.h"
|
||||
#include "common/base58.h"
|
||||
#include "ringct/rctSigs.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -168,7 +169,7 @@ bool wallet2::is_deprecated() const
|
|||
return is_old_file_format;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const crypto::public_key &tx_pub_key, size_t i, uint64_t &money_transfered, bool &error) const
|
||||
void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const crypto::public_key &tx_pub_key, size_t i, bool &received, uint64_t &money_transfered, bool &error) const
|
||||
{
|
||||
if (o.target.type() != typeid(txout_to_key))
|
||||
{
|
||||
|
@ -176,9 +177,10 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp
|
|||
LOG_ERROR("wrong type id in transaction out");
|
||||
return;
|
||||
}
|
||||
if(is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i))
|
||||
received = is_out_to_acc(acc, boost::get<txout_to_key>(o.target), tx_pub_key, i);
|
||||
if(received)
|
||||
{
|
||||
money_transfered = o.amount;
|
||||
money_transfered = o.amount; // may be 0 for ringct outputs
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -216,6 +218,10 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
|
||||
tx_pub_key = pub_key_field.pub_key;
|
||||
bool r = true;
|
||||
std::deque<cryptonote::keypair> in_ephemeral(tx.vout.size());
|
||||
std::deque<crypto::key_image> ki(tx.vout.size());
|
||||
std::deque<uint64_t> amount(tx.vout.size());
|
||||
std::deque<rct::key> mask(tx.vout.size());
|
||||
int threads = tools::get_max_concurrency();
|
||||
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
|
||||
{
|
||||
|
@ -224,8 +230,8 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
|
||||
{
|
||||
uint64_t money_transfered = 0;
|
||||
bool error = false;
|
||||
check_acc_out(m_account.get_keys(), tx.vout[0], tx_pub_key, 0, money_transfered, error);
|
||||
bool error = false, received = false;
|
||||
check_acc_out(m_account.get_keys(), tx.vout[0], tx_pub_key, 0, received, money_transfered, error);
|
||||
if (error)
|
||||
{
|
||||
r = false;
|
||||
|
@ -233,9 +239,16 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
else
|
||||
{
|
||||
// this assumes that the miner tx pays a single address
|
||||
if (money_transfered > 0)
|
||||
if (received)
|
||||
{
|
||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, 0, in_ephemeral[0], ki[0]);
|
||||
THROW_WALLET_EXCEPTION_IF(in_ephemeral[0].pub != boost::get<cryptonote::txout_to_key>(tx.vout[0].target).key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
|
||||
outs.push_back(0);
|
||||
if (money_transfered == 0)
|
||||
money_transfered = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[0].sec), 0, mask[0]);
|
||||
amount[0] = money_transfered;
|
||||
tx_money_got_in_outs = money_transfered;
|
||||
|
||||
// process the other outs from that tx
|
||||
|
@ -250,11 +263,12 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
const account_keys &keys = m_account.get_keys();
|
||||
std::vector<uint64_t> money_transfered(tx.vout.size());
|
||||
std::deque<bool> error(tx.vout.size());
|
||||
std::deque<bool> received(tx.vout.size());
|
||||
// the first one was already checked
|
||||
for (size_t i = 1; i < tx.vout.size(); ++i)
|
||||
{
|
||||
ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i,
|
||||
std::ref(money_transfered[i]), std::ref(error[i])));
|
||||
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
|
||||
}
|
||||
KILL_IOSERVICE();
|
||||
for (size_t i = 1; i < tx.vout.size(); ++i)
|
||||
|
@ -264,10 +278,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
r = false;
|
||||
break;
|
||||
}
|
||||
if (money_transfered[i])
|
||||
if (received[i])
|
||||
{
|
||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
|
||||
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
|
||||
outs.push_back(i);
|
||||
if (money_transfered[i] == 0)
|
||||
money_transfered[i] = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]);
|
||||
tx_money_got_in_outs += money_transfered[i];
|
||||
amount[i] = money_transfered[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,10 +307,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
const account_keys &keys = m_account.get_keys();
|
||||
std::vector<uint64_t> money_transfered(tx.vout.size());
|
||||
std::deque<bool> error(tx.vout.size());
|
||||
std::deque<bool> received(tx.vout.size());
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i,
|
||||
std::ref(money_transfered[i]), std::ref(error[i])));
|
||||
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
|
||||
}
|
||||
KILL_IOSERVICE();
|
||||
tx_money_got_in_outs = 0;
|
||||
|
@ -300,16 +322,48 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
r = false;
|
||||
break;
|
||||
}
|
||||
if (money_transfered[i])
|
||||
if (received[i])
|
||||
{
|
||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
|
||||
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
|
||||
outs.push_back(i);
|
||||
if (money_transfered[i] == 0)
|
||||
money_transfered[i] = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]);
|
||||
tx_money_got_in_outs += money_transfered[i];
|
||||
amount[i] = money_transfered[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs);
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
{
|
||||
uint64_t money_transfered = 0;
|
||||
bool error = false, received = false;
|
||||
check_acc_out(m_account.get_keys(), tx.vout[i], tx_pub_key, i, received, money_transfered, error);
|
||||
if (error)
|
||||
{
|
||||
r = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (received)
|
||||
{
|
||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, i, in_ephemeral[i], ki[i]);
|
||||
THROW_WALLET_EXCEPTION_IF(in_ephemeral[i].pub != boost::get<cryptonote::txout_to_key>(tx.vout[i].target).key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
|
||||
outs.push_back(i);
|
||||
if (money_transfered == 0)
|
||||
money_transfered = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]);
|
||||
amount[i] = money_transfered;
|
||||
tx_money_got_in_outs += money_transfered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
|
||||
|
||||
|
@ -338,13 +392,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
|
||||
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
|
||||
|
||||
crypto::key_image ki;
|
||||
cryptonote::keypair in_ephemeral;
|
||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, ki);
|
||||
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(tx.vout[o].target).key,
|
||||
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key");
|
||||
|
||||
auto kit = m_key_images.find(ki);
|
||||
auto kit = m_key_images.find(ki[o]);
|
||||
THROW_WALLET_EXCEPTION_IF(kit != m_key_images.end() && kit->second >= m_transfers.size(),
|
||||
error::wallet_internal_error, std::string("Unexpected transfer index from key image: ")
|
||||
+ "got " + (kit == m_key_images.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second))
|
||||
|
@ -359,12 +407,22 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
td.m_internal_output_index = o;
|
||||
td.m_global_output_index = res.o_indexes[o];
|
||||
td.m_tx = tx;
|
||||
td.m_key_image = ki;
|
||||
td.m_key_image = ki[o];
|
||||
td.m_amount = tx.vout[o].amount;
|
||||
if (td.m_amount == 0)
|
||||
{
|
||||
td.m_mask = mask[o];
|
||||
td.m_amount = amount[o];
|
||||
}
|
||||
else
|
||||
{
|
||||
td.m_mask = rct::identity();
|
||||
}
|
||||
td.m_spent = false;
|
||||
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
||||
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
|
||||
if (0 != m_callback)
|
||||
m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
|
||||
m_callback->on_money_received(height, td.m_tx, td.m_amount);
|
||||
}
|
||||
}
|
||||
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
|
||||
|
@ -389,12 +447,22 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
td.m_internal_output_index = o;
|
||||
td.m_global_output_index = res.o_indexes[o];
|
||||
td.m_tx = tx;
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki, error::wallet_internal_error, "Inconsistent key images");
|
||||
td.m_amount = tx.vout[o].amount;
|
||||
if (td.m_amount == 0)
|
||||
{
|
||||
td.m_mask = mask[o];
|
||||
td.m_amount = amount[o];
|
||||
}
|
||||
else
|
||||
{
|
||||
td.m_mask = rct::identity();
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki[o], error::wallet_internal_error, "Inconsistent key images");
|
||||
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
|
||||
|
||||
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
|
||||
if (0 != m_callback)
|
||||
m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
|
||||
m_callback->on_money_received(height, td.m_tx, td.m_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,12 +478,20 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
|
|||
auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
|
||||
if(it != m_key_images.end())
|
||||
{
|
||||
LOG_PRINT_L0("Spent money: " << print_money(boost::get<cryptonote::txin_to_key>(in).amount) << ", with tx: " << get_transaction_hash(tx));
|
||||
tx_money_spent_in_ins += boost::get<cryptonote::txin_to_key>(in).amount;
|
||||
transfer_details& td = m_transfers[it->second];
|
||||
uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
|
||||
if (amount > 0)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error,
|
||||
std::string("Inconsistent amount in tx input: got ") + print_money(amount) +
|
||||
std::string(", expected ") + print_money(td.amount()));
|
||||
}
|
||||
amount = td.amount();
|
||||
LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << get_transaction_hash(tx));
|
||||
tx_money_spent_in_ins += amount;
|
||||
td.m_spent = true;
|
||||
if (0 != m_callback)
|
||||
m_callback->on_money_spent(height, td.m_tx, td.m_internal_output_index, tx);
|
||||
m_callback->on_money_spent(height, td.m_tx, amount, tx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -501,14 +577,22 @@ void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t he
|
|||
void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received)
|
||||
{
|
||||
crypto::hash txid = get_transaction_hash(tx);
|
||||
confirmed_transfer_details &ctd = m_confirmed_txs[txid];
|
||||
// operator[] creates if not found
|
||||
std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
|
||||
// fill with the info we know, some info might already be there
|
||||
ctd.m_amount_in = spent;
|
||||
ctd.m_amount_out = get_outs_money_amount(tx);
|
||||
ctd.m_change = received;
|
||||
ctd.m_block_height = height;
|
||||
ctd.m_timestamp = ts;
|
||||
if (entry.second)
|
||||
{
|
||||
// this case will happen if the tx is from our outputs, but was sent by another
|
||||
// wallet (eg, we're a cold wallet and the hot wallet sent it). For RCT transactions,
|
||||
// we only see 0 input amounts, so have to deduce amount out from other parameters.
|
||||
entry.first->second.m_amount_in = spent;
|
||||
if (tx.version == 1)
|
||||
entry.first->second.m_amount_out = get_outs_money_amount(tx);
|
||||
else
|
||||
entry.first->second.m_amount_out = spent - tx.rct_signatures.txnFee;
|
||||
entry.first->second.m_change = received;
|
||||
}
|
||||
entry.first->second.m_block_height = height;
|
||||
entry.first->second.m_timestamp = ts;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height)
|
||||
|
@ -2002,9 +2086,13 @@ uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> un
|
|||
return found_money;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount)
|
||||
void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount)
|
||||
{
|
||||
unconfirmed_transfer_details& utd = m_unconfirmed_txs[cryptonote::get_transaction_hash(tx)];
|
||||
utd.m_amount_in = amount_in;
|
||||
utd.m_amount_out = 0;
|
||||
for (const auto &d: dests)
|
||||
utd.m_amount_out += d.amount;
|
||||
utd.m_change = change_amount;
|
||||
utd.m_sent_time = time(NULL);
|
||||
utd.m_tx = tx;
|
||||
|
@ -2190,12 +2278,15 @@ void wallet2::commit_tx(pending_tx& ptx)
|
|||
txid = get_transaction_hash(ptx.tx);
|
||||
crypto::hash payment_id = cryptonote::null_hash;
|
||||
std::vector<cryptonote::tx_destination_entry> dests;
|
||||
uint64_t amount_in = 0;
|
||||
if (store_tx_info())
|
||||
{
|
||||
payment_id = get_payment_id(ptx);
|
||||
dests = ptx.dests;
|
||||
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
|
||||
amount_in += it->amount();
|
||||
}
|
||||
add_unconfirmed_tx(ptx.tx, dests, payment_id, ptx.change_dts.amount);
|
||||
add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount);
|
||||
if (store_tx_info())
|
||||
m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
|
||||
|
||||
|
@ -2546,7 +2637,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
|||
{
|
||||
tx_output_entry oe;
|
||||
oe.first = outs[out_index][n].first;
|
||||
oe.second = outs[out_index][n].second;
|
||||
oe.second.dest = rct::pk2rct(outs[out_index][n].second);
|
||||
oe.second.mask = rct::zeroCommit(td.amount());
|
||||
|
||||
src.outputs.push_back(oe);
|
||||
++i;
|
||||
}
|
||||
|
@ -2561,7 +2654,8 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
|||
|
||||
tx_output_entry real_oe;
|
||||
real_oe.first = td.m_global_output_index;
|
||||
real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
|
||||
real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
|
||||
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
|
||||
*it_to_replace = real_oe;
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
|
||||
src.real_output = it_to_replace - src.outputs.begin();
|
||||
|
@ -2621,6 +2715,219 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
|
|||
ptx.dests = dsts;
|
||||
}
|
||||
|
||||
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
// throw if attempting a transaction with no destinations
|
||||
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
|
||||
|
||||
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
|
||||
uint64_t needed_money = fee;
|
||||
LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
|
||||
|
||||
// calculate total amount being sent to all destinations
|
||||
// throw if total amount overflows uint64_t
|
||||
BOOST_FOREACH(auto& dt, dsts)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
|
||||
needed_money += dt.amount;
|
||||
LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
|
||||
THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet);
|
||||
}
|
||||
|
||||
uint64_t found_money = 0;
|
||||
BOOST_FOREACH(auto it, selected_transfers)
|
||||
{
|
||||
found_money += it->amount();
|
||||
}
|
||||
|
||||
LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee));
|
||||
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
|
||||
|
||||
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
|
||||
|
||||
std::vector<size_t> selected_transfer_to_daemon_resp;
|
||||
COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request rct_req = AUTO_VAL_INIT(rct_req);
|
||||
COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response rct_daemon_resp = AUTO_VAL_INIT(rct_daemon_resp);
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request daemon_req = AUTO_VAL_INIT(daemon_req);
|
||||
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
|
||||
if(fake_outputs_count)
|
||||
{
|
||||
rct_req.outs_count = 0;
|
||||
size_t n_rct_inputs = 0;
|
||||
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
|
||||
"m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
|
||||
" is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
|
||||
if (it->is_rct())
|
||||
++n_rct_inputs;
|
||||
else
|
||||
daemon_req.amounts.push_back(it->amount());
|
||||
selected_transfer_to_daemon_resp.push_back(daemon_req.amounts.size() - 1);
|
||||
}
|
||||
size_t n_amounts_requested = daemon_req.amounts.size();
|
||||
|
||||
rct_req.outs_count = n_rct_inputs * (fake_outputs_count + 1);
|
||||
if (rct_req.outs_count > 0)
|
||||
{
|
||||
LOG_PRINT_L2("We need RCT fake outs for " << n_rct_inputs << " inputs");
|
||||
rct_req.outs_count = n_rct_inputs * (fake_outputs_count + 1); // add one to make possible (if need) to skip real output key
|
||||
m_daemon_rpc_mutex.lock();
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_rctouts.bin", rct_req, rct_daemon_resp, m_http_client, 200000);
|
||||
m_daemon_rpc_mutex.unlock();
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_rctouts.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(rct_daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_rctouts.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(rct_daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, rct_daemon_resp.status);
|
||||
THROW_WALLET_EXCEPTION_IF(rct_daemon_resp.outs.size() != n_rct_inputs * (fake_outputs_count + 1), error::wallet_internal_error,
|
||||
"daemon returned wrong response for getrandom_rctouts.bin, wrong amounts count = " +
|
||||
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(n_rct_inputs * (fake_outputs_count + 1)));
|
||||
}
|
||||
if (!daemon_req.amounts.empty())
|
||||
{
|
||||
LOG_PRINT_L2("We need non-RCT fake outs for " << daemon_req.amounts.size() << " inputs");
|
||||
daemon_req.outs_count = fake_outputs_count + 1; // add one to make possible (if need) to skip real output key
|
||||
m_daemon_rpc_mutex.lock();
|
||||
bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", daemon_req, daemon_resp, m_http_client, 200000);
|
||||
m_daemon_rpc_mutex.unlock();
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin");
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status);
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != n_amounts_requested, error::wallet_internal_error,
|
||||
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " +
|
||||
std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(n_amounts_requested));
|
||||
|
||||
std::unordered_map<uint64_t, uint64_t> scanty_outs;
|
||||
BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs)
|
||||
{
|
||||
if (amount_outs.outs.size() < fake_outputs_count)
|
||||
{
|
||||
scanty_outs[amount_outs.amount] = amount_outs.outs.size();
|
||||
}
|
||||
}
|
||||
THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
|
||||
}
|
||||
}
|
||||
|
||||
//prepare inputs
|
||||
size_t i = 0;
|
||||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
rct_daemon_resp.outs.sort([](const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& a, const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry& b){return a.global_amount_index < b.global_amount_index;});
|
||||
std::list<COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::out_entry>::const_iterator rctit = rct_daemon_resp.outs.begin();
|
||||
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
|
||||
{
|
||||
sources.resize(sources.size()+1);
|
||||
cryptonote::tx_source_entry& src = sources.back();
|
||||
transfer_details& td = *it;
|
||||
src.amount = td.amount();
|
||||
//paste mixin transaction
|
||||
if(it->is_rct())
|
||||
{
|
||||
while (src.outputs.size() < fake_outputs_count)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(rctit == rct_daemon_resp.outs.end(), error::wallet_internal_error, "Out of rct inputs");
|
||||
// check if we have the daemon supplied output in our real ones
|
||||
bool found = false;
|
||||
BOOST_FOREACH(transfer_container::iterator checkit, selected_transfers)
|
||||
{
|
||||
if (checkit->m_global_output_index == rctit->global_amount_index && checkit->m_tx.vout[checkit->m_internal_output_index].amount == rctit->amount)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
{
|
||||
++rctit;
|
||||
continue;
|
||||
}
|
||||
tx_output_entry oe;
|
||||
oe.first = rctit->global_amount_index;
|
||||
oe.second.dest = rct::pk2rct(rctit->out_key);
|
||||
oe.second.mask = rctit->commitment;
|
||||
++rctit;
|
||||
src.outputs.push_back(oe);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
|
||||
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
|
||||
|
||||
size_t idx = selected_transfer_to_daemon_resp[i];
|
||||
THROW_WALLET_EXCEPTION_IF(daemon_req.amounts.size() != daemon_resp.outs.size(), error::wallet_internal_error, "Bad amounts/out size");
|
||||
THROW_WALLET_EXCEPTION_IF(idx >= daemon_resp.outs.size(), error::wallet_internal_error, "Bad mapping to daemon_resp.outs");
|
||||
THROW_WALLET_EXCEPTION_IF(td.amount() != daemon_req.amounts[idx], error::wallet_internal_error, "Bad mapping to daemon_resp.outs/amounts");
|
||||
daemon_resp.outs[idx].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;});
|
||||
BOOST_FOREACH(out_entry& daemon_oe, daemon_resp.outs[idx].outs)
|
||||
{
|
||||
if(td.m_global_output_index == daemon_oe.global_amount_index)
|
||||
continue;
|
||||
tx_output_entry oe;
|
||||
oe.first = daemon_oe.global_amount_index;
|
||||
oe.second.dest = rct::pk2rct(daemon_oe.out_key);
|
||||
oe.second.mask = rct::zeroCommit(td.amount());
|
||||
src.outputs.push_back(oe);
|
||||
if(src.outputs.size() >= fake_outputs_count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//paste real transaction to the random index
|
||||
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
|
||||
{
|
||||
return a.first >= td.m_global_output_index;
|
||||
});
|
||||
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
|
||||
// TODO: same index (for now)
|
||||
tx_output_entry real_oe;
|
||||
real_oe.first = td.m_global_output_index;
|
||||
real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
|
||||
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
|
||||
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
|
||||
src.real_output = interted_it - src.outputs.begin();
|
||||
src.real_output_in_tx_index = td.m_internal_output_index;
|
||||
src.mask = td.m_mask;
|
||||
detail::print_source_entry(src);
|
||||
++i;
|
||||
}
|
||||
|
||||
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||
if (needed_money < found_money)
|
||||
{
|
||||
change_dts.addr = m_account.get_keys().m_account_address;
|
||||
change_dts.amount = found_money - needed_money;
|
||||
}
|
||||
|
||||
dsts.push_back(change_dts);
|
||||
|
||||
crypto::secret_key tx_key;
|
||||
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, dsts, extra, tx, unlock_time, tx_key, true);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet);
|
||||
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
|
||||
|
||||
std::string key_images;
|
||||
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
|
||||
{
|
||||
CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
|
||||
key_images += boost::to_string(in.k_image) + " ";
|
||||
return true;
|
||||
});
|
||||
THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
|
||||
|
||||
ptx.key_images = key_images;
|
||||
ptx.fee = fee;
|
||||
ptx.dust = 0;
|
||||
ptx.dust_added_to_fee = false;
|
||||
ptx.tx = tx;
|
||||
ptx.change_dts = change_dts;
|
||||
ptx.selected_transfers = selected_transfers;
|
||||
ptx.tx_key = tx_key;
|
||||
ptx.dests = dsts;
|
||||
}
|
||||
|
||||
// Another implementation of transaction creation that is hopefully better
|
||||
// While there is anything left to pay, it goes through random outputs and tries
|
||||
// to fill the next destination/amount. If it fully fills it, it will use the
|
||||
|
@ -2686,7 +2993,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||
{
|
||||
const transfer_details& td = m_transfers[i];
|
||||
if (!td.m_spent && is_transfer_unlocked(td))
|
||||
if (!td.m_spent && !td.is_rct() && is_transfer_unlocked(td))
|
||||
{
|
||||
if (is_valid_decomposed_amount(td.amount()))
|
||||
unused_transfers_indices.push_back(i);
|
||||
|
@ -2862,7 +3169,269 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
|||
return ptx_vector;
|
||||
}
|
||||
|
||||
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
|
||||
static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
// tx prefix
|
||||
|
||||
// first few bytes
|
||||
size += 1 + 6;
|
||||
|
||||
// vin
|
||||
size += n_inputs * (1+6+(mixin+1)*2+32);
|
||||
|
||||
// vout
|
||||
size += n_outputs * (6+32);
|
||||
|
||||
// extra
|
||||
size += 40;
|
||||
|
||||
// rct signatures
|
||||
|
||||
// rangeSigs
|
||||
size += (2*64*32+32+64*32) * n_outputs;
|
||||
// MG
|
||||
size += 32 * (mixin+1) * n_inputs + 32 + 32 * n_inputs;
|
||||
// mixRing
|
||||
size += 2 * 32 * (mixin+1) * n_inputs;
|
||||
// ecdhInfo
|
||||
size += 3 * 32 * n_outputs;
|
||||
// outPk
|
||||
size += 2 * 32 * n_outputs;
|
||||
|
||||
LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " at mixin " << mixin << " and " << n_outputs << ": " << size);
|
||||
return size;
|
||||
}
|
||||
|
||||
std::vector<wallet2::pending_tx> wallet2::create_transactions_rct(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
|
||||
{
|
||||
std::vector<size_t> unused_transfers_indices;
|
||||
std::vector<size_t> unused_dust_indices;
|
||||
uint64_t needed_money;
|
||||
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
|
||||
struct TX {
|
||||
std::list<transfer_container::iterator> selected_transfers;
|
||||
std::vector<cryptonote::tx_destination_entry> dsts;
|
||||
cryptonote::transaction tx;
|
||||
pending_tx ptx;
|
||||
size_t bytes;
|
||||
|
||||
void add(const account_public_address &addr, uint64_t amount) {
|
||||
std::vector<cryptonote::tx_destination_entry>::iterator i;
|
||||
i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); });
|
||||
if (i == dsts.end())
|
||||
dsts.push_back(tx_destination_entry(amount,addr));
|
||||
else
|
||||
i->amount += amount;
|
||||
}
|
||||
};
|
||||
std::vector<TX> txes;
|
||||
bool adding_fee; // true if new outputs go towards fee, rather than destinations
|
||||
uint64_t needed_fee, available_for_fee = 0;
|
||||
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
|
||||
|
||||
fee_multiplier = sanitize_fee_multiplier(fee_multiplier);
|
||||
|
||||
// throw if attempting a transaction with no destinations
|
||||
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
|
||||
|
||||
// calculate total amount being sent to all destinations
|
||||
// throw if total amount overflows uint64_t
|
||||
needed_money = 0;
|
||||
BOOST_FOREACH(auto& dt, dsts)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
|
||||
needed_money += dt.amount;
|
||||
LOG_PRINT_L2("transfer: adding " << print_money(dt.amount) << ", for a total of " << print_money (needed_money));
|
||||
THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, 0, m_testnet);
|
||||
}
|
||||
|
||||
// throw if attempting a transaction with no money
|
||||
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
|
||||
|
||||
// gather all our dust and non dust outputs
|
||||
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||
{
|
||||
const transfer_details& td = m_transfers[i];
|
||||
if (!td.m_spent && is_transfer_unlocked(td))
|
||||
{
|
||||
if (is_valid_decomposed_amount(td.amount()) || td.is_rct())
|
||||
unused_transfers_indices.push_back(i);
|
||||
else
|
||||
unused_dust_indices.push_back(i);
|
||||
}
|
||||
}
|
||||
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
|
||||
|
||||
// start with an empty tx
|
||||
txes.push_back(TX());
|
||||
accumulated_fee = 0;
|
||||
accumulated_outputs = 0;
|
||||
accumulated_change = 0;
|
||||
adding_fee = false;
|
||||
needed_fee = 0;
|
||||
|
||||
// while we have something to send
|
||||
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee) {
|
||||
TX &tx = txes.back();
|
||||
|
||||
// if we need to spend money and don't have any left, we fail
|
||||
if (unused_dust_indices.empty() && unused_transfers_indices.empty()) {
|
||||
LOG_PRINT_L2("No more outputs to choose from");
|
||||
THROW_WALLET_EXCEPTION_IF(1, error::not_enough_money, unlocked_balance(), needed_money, accumulated_fee + needed_fee);
|
||||
}
|
||||
|
||||
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
|
||||
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
|
||||
size_t idx = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_indices);
|
||||
|
||||
const transfer_details &td = m_transfers[idx];
|
||||
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
|
||||
|
||||
// add this output to the list to spend
|
||||
tx.selected_transfers.push_back(m_transfers.begin() + idx);
|
||||
uint64_t available_amount = td.amount();
|
||||
accumulated_outputs += available_amount;
|
||||
|
||||
if (adding_fee)
|
||||
{
|
||||
LOG_PRINT_L2("We need more fee, adding it to fee");
|
||||
available_for_fee += available_amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!dsts.empty() && dsts[0].amount <= available_amount)
|
||||
{
|
||||
// we can fully pay that destination
|
||||
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].addr) <<
|
||||
" for " << print_money(dsts[0].amount));
|
||||
tx.add(dsts[0].addr, dsts[0].amount);
|
||||
available_amount -= dsts[0].amount;
|
||||
dsts[0].amount = 0;
|
||||
pop_index(dsts, 0);
|
||||
}
|
||||
|
||||
if (available_amount > 0 && !dsts.empty()) {
|
||||
// we can partially fill that destination
|
||||
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].addr) <<
|
||||
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
|
||||
tx.add(dsts[0].addr, available_amount);
|
||||
dsts[0].amount -= available_amount;
|
||||
available_amount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// here, check if we need to sent tx and start a new one
|
||||
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
|
||||
<< upper_transaction_size_limit);
|
||||
bool try_tx;
|
||||
if (adding_fee)
|
||||
{
|
||||
/* might not actually be enough if adding this output bumps size to next kB, but we need to try */
|
||||
try_tx = available_for_fee >= needed_fee;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
|
||||
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
|
||||
}
|
||||
|
||||
if (try_tx) {
|
||||
cryptonote::transaction test_tx;
|
||||
pending_tx test_ptx;
|
||||
|
||||
needed_fee = 0;
|
||||
|
||||
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
|
||||
tx.selected_transfers.size() << " outputs");
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
needed_fee = calculate_fee(txBlob, fee_multiplier);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
|
||||
LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
|
||||
print_money(needed_fee) << " needed)");
|
||||
|
||||
if (needed_fee > available_for_fee && dsts[0].amount > 0)
|
||||
{
|
||||
// we don't have enough for the fee, but we've only partially paid the current address,
|
||||
// so we can take the fee from the paid amount, since we'll have to make another tx anyway
|
||||
std::vector<cryptonote::tx_destination_entry>::iterator i;
|
||||
i = std::find_if(tx.dsts.begin(), tx.dsts.end(),
|
||||
[&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &dsts[0].addr, sizeof(dsts[0].addr)); });
|
||||
THROW_WALLET_EXCEPTION_IF(i == tx.dsts.end(), error::wallet_internal_error, "paid address not found in outputs");
|
||||
if (i->amount > needed_fee)
|
||||
{
|
||||
uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee;
|
||||
LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_testnet, i->addr) << " from " <<
|
||||
print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accomodate " <<
|
||||
print_money(needed_fee) << " fee");
|
||||
dsts[0].amount += i->amount - new_paid_amount;
|
||||
i->amount = new_paid_amount;
|
||||
test_ptx.fee = needed_fee;
|
||||
available_for_fee = needed_fee;
|
||||
}
|
||||
}
|
||||
|
||||
if (needed_fee > available_for_fee)
|
||||
{
|
||||
LOG_PRINT_L2("We could not make a tx, switching to fee accumulation");
|
||||
|
||||
adding_fee = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_PRINT_L2("We made a tx, adjusting fee and saving it");
|
||||
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
|
||||
test_tx, test_ptx);
|
||||
txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) <<
|
||||
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
|
||||
|
||||
tx.tx = test_tx;
|
||||
tx.ptx = test_ptx;
|
||||
tx.bytes = txBlob.size();
|
||||
accumulated_fee += test_ptx.fee;
|
||||
accumulated_change += test_ptx.change_dts.amount;
|
||||
adding_fee = false;
|
||||
if (!dsts.empty())
|
||||
{
|
||||
LOG_PRINT_L2("We have more to pay, starting another tx");
|
||||
txes.push_back(TX());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (adding_fee)
|
||||
{
|
||||
LOG_PRINT_L1("We ran out of outputs while trying to gather final fee");
|
||||
THROW_WALLET_EXCEPTION_IF(1, error::not_enough_money, unlocked_balance(), needed_money, accumulated_fee + needed_fee);
|
||||
}
|
||||
|
||||
LOG_PRINT_L1("Done creating " << txes.size() << " transactions, " << print_money(accumulated_fee) <<
|
||||
" total fee, " << print_money(accumulated_change) << " total change");
|
||||
|
||||
std::vector<wallet2::pending_tx> ptx_vector;
|
||||
for (std::vector<TX>::iterator i = txes.begin(); i != txes.end(); ++i)
|
||||
{
|
||||
TX &tx = *i;
|
||||
uint64_t tx_money = 0;
|
||||
for (std::list<transfer_container::iterator>::const_iterator mi = tx.selected_transfers.begin(); mi != tx.selected_transfers.end(); ++mi)
|
||||
tx_money += (*mi)->amount();
|
||||
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
|
||||
": " << (tx.bytes+1023)/1024 << " kB, sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
|
||||
" outputs to " << tx.dsts.size() << " destination(s), including " <<
|
||||
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
|
||||
ptx_vector.push_back(tx.ptx);
|
||||
}
|
||||
|
||||
// if we made it this far, we're OK to actually send the transactions
|
||||
return ptx_vector;
|
||||
}
|
||||
|
||||
std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon)
|
||||
{
|
||||
std::vector<size_t> unused_transfers_indices;
|
||||
std::vector<size_t> unused_dust_indices;
|
||||
|
@ -2878,13 +3447,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
|
|||
uint64_t needed_fee, available_for_fee = 0;
|
||||
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
|
||||
|
||||
fee_multiplier = sanitize_fee_multiplier(fee_multiplier);
|
||||
|
||||
// gather all our dust and non dust outputs
|
||||
for (size_t i = 0; i < m_transfers.size(); ++i)
|
||||
{
|
||||
const transfer_details& td = m_transfers[i];
|
||||
if (!td.m_spent && is_transfer_unlocked(td))
|
||||
if (!td.m_spent && !td.is_rct() && is_transfer_unlocked(td))
|
||||
{
|
||||
if (is_valid_decomposed_amount(td.amount()))
|
||||
unused_transfers_indices.push_back(i);
|
||||
|
@ -3065,7 +3632,8 @@ void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs,
|
|||
});
|
||||
tx_output_entry real_oe;
|
||||
real_oe.first = td.m_global_output_index;
|
||||
real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
|
||||
real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
|
||||
real_oe.second.mask = rct::commit(td.amount(), td.m_mask);
|
||||
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
|
||||
src.real_output = interted_it - src.outputs.begin();
|
||||
|
@ -3213,6 +3781,8 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
|
|||
}
|
||||
|
||||
return select_available_outputs([mixable, atleast](const transfer_details &td) {
|
||||
if (td.is_rct())
|
||||
return false;
|
||||
const uint64_t amount = td.amount();
|
||||
if (atleast) {
|
||||
if (mixable.find(amount) != mixable.end())
|
||||
|
@ -3521,7 +4091,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
|
|||
for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
|
||||
{
|
||||
transfer_details &td = m_transfers[n];
|
||||
uint64_t amount = td.m_tx.vout[td.m_internal_output_index].amount;
|
||||
uint64_t amount = td.amount();
|
||||
td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT;
|
||||
if (td.m_spent)
|
||||
spent += amount;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <boost/archive/binary_iarchive.hpp>
|
||||
#include <boost/serialization/list.hpp>
|
||||
#include <boost/serialization/vector.hpp>
|
||||
#include <atomic>
|
||||
|
@ -46,6 +47,8 @@
|
|||
#include "common/unordered_containers_boost_serialization.h"
|
||||
#include "crypto/chacha8.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "ringct/rctTypes.h"
|
||||
#include "ringct/rctOps.h"
|
||||
|
||||
#include "wallet_errors.h"
|
||||
|
||||
|
@ -58,8 +61,8 @@ namespace tools
|
|||
{
|
||||
public:
|
||||
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
|
||||
virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) {}
|
||||
virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) {}
|
||||
virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {}
|
||||
virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {}
|
||||
virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {}
|
||||
virtual ~i_wallet2_callback() {}
|
||||
};
|
||||
|
@ -101,8 +104,11 @@ namespace tools
|
|||
uint64_t m_global_output_index;
|
||||
bool m_spent;
|
||||
crypto::key_image m_key_image; //TODO: key_image stored twice :(
|
||||
rct::key m_mask;
|
||||
uint64_t m_amount;
|
||||
|
||||
uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; }
|
||||
bool is_rct() const { return m_tx.vout[m_internal_output_index].amount == 0; }
|
||||
uint64_t amount() const { return m_amount; }
|
||||
};
|
||||
|
||||
struct payment_details
|
||||
|
@ -117,6 +123,8 @@ namespace tools
|
|||
struct unconfirmed_transfer_details
|
||||
{
|
||||
cryptonote::transaction m_tx;
|
||||
uint64_t m_amount_in;
|
||||
uint64_t m_amount_out;
|
||||
uint64_t m_change;
|
||||
time_t m_sent_time;
|
||||
std::vector<cryptonote::tx_destination_entry> m_dests;
|
||||
|
@ -137,7 +145,7 @@ namespace tools
|
|||
|
||||
confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash) {}
|
||||
confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height):
|
||||
m_amount_out(get_outs_money_amount(utd.m_tx)), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp) { get_inputs_money_amount(utd.m_tx, m_amount_in); }
|
||||
m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp) {}
|
||||
};
|
||||
|
||||
typedef std::vector<transfer_details> transfer_container;
|
||||
|
@ -289,11 +297,14 @@ namespace tools
|
|||
template<typename T>
|
||||
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
|
||||
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);
|
||||
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
|
||||
|
||||
void commit_tx(pending_tx& ptx_vector);
|
||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon);
|
||||
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon);
|
||||
std::vector<wallet2::pending_tx> create_transactions_rct(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon);
|
||||
std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint64_t fee_multiplier, const std::vector<uint8_t> extra, bool trusted_daemon);
|
||||
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
|
||||
bool check_connection(bool *same_version = NULL);
|
||||
|
@ -308,6 +319,7 @@ namespace tools
|
|||
uint64_t get_blockchain_current_height() const { return m_local_bc_height; }
|
||||
void rescan_spent();
|
||||
void rescan_blockchain(bool refresh = true);
|
||||
bool is_transfer_unlocked(const transfer_details& td) const;
|
||||
template <class t_archive>
|
||||
inline void serialize(t_archive &a, const unsigned int ver)
|
||||
{
|
||||
|
@ -418,7 +430,6 @@ namespace tools
|
|||
void detach_blockchain(uint64_t height);
|
||||
void get_short_chain_history(std::list<crypto::hash>& ids) const;
|
||||
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_height) const;
|
||||
bool is_transfer_unlocked(const transfer_details& td) const;
|
||||
bool clear();
|
||||
void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks);
|
||||
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<crypto::hash> &hashes);
|
||||
|
@ -429,12 +440,12 @@ namespace tools
|
|||
bool prepare_file_names(const std::string& file_path);
|
||||
void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height);
|
||||
void process_outgoing(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received);
|
||||
void add_unconfirmed_tx(const cryptonote::transaction& tx, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount);
|
||||
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount);
|
||||
void generate_genesis(cryptonote::block& b);
|
||||
void check_genesis(const crypto::hash& genesis_hash) const; //throws
|
||||
bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const;
|
||||
crypto::hash get_payment_id(const pending_tx &ptx) const;
|
||||
void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, uint64_t &money_transfered, bool &error) const;
|
||||
void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, bool &received, uint64_t &money_transfered, bool &error) const;
|
||||
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
|
||||
uint64_t get_upper_tranaction_size_limit();
|
||||
std::vector<uint64_t> get_unspent_amounts_vector();
|
||||
|
@ -479,14 +490,26 @@ namespace tools
|
|||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 13)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 3)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 4)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 2)
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace serialization
|
||||
{
|
||||
template <class Archive>
|
||||
inline void initialize_transfer_details(Archive &a, tools::wallet2::transfer_details &x)
|
||||
{
|
||||
}
|
||||
template<>
|
||||
inline void initialize_transfer_details(boost::archive::binary_iarchive &a, tools::wallet2::transfer_details &x)
|
||||
{
|
||||
x.m_mask = rct::identity();
|
||||
x.m_amount = x.m_tx.vout[x.m_internal_output_index].amount;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, tools::wallet2::transfer_details &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
|
@ -496,6 +519,14 @@ namespace boost
|
|||
a & x.m_tx;
|
||||
a & x.m_spent;
|
||||
a & x.m_key_image;
|
||||
if (ver < 1)
|
||||
{
|
||||
// ensure mask and amount are set
|
||||
initialize_transfer_details(a, x);
|
||||
return;
|
||||
}
|
||||
a & x.m_mask;
|
||||
a & x.m_amount;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
@ -514,6 +545,10 @@ namespace boost
|
|||
if (ver < 3)
|
||||
return;
|
||||
a & x.m_timestamp;
|
||||
if (ver < 4)
|
||||
return;
|
||||
a & x.m_amount_in;
|
||||
a & x.m_amount_out;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
|
@ -698,7 +733,8 @@ namespace tools
|
|||
continue;
|
||||
tx_output_entry oe;
|
||||
oe.first = daemon_oe.global_amount_index;
|
||||
oe.second = daemon_oe.out_key;
|
||||
oe.second.dest = rct::pk2rct(daemon_oe.out_key);
|
||||
oe.second.mask = rct::identity();
|
||||
src.outputs.push_back(oe);
|
||||
if(src.outputs.size() >= fake_outputs_count)
|
||||
break;
|
||||
|
@ -713,7 +749,8 @@ namespace tools
|
|||
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
|
||||
tx_output_entry real_oe;
|
||||
real_oe.first = td.m_global_output_index;
|
||||
real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
|
||||
real_oe.second.dest = rct::pk2rct(boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key);
|
||||
real_oe.second.mask = rct::identity();
|
||||
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
|
||||
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
|
||||
src.real_output = interted_it - src.outputs.begin();
|
||||
|
|
|
@ -961,10 +961,8 @@ namespace tools
|
|||
entry.payment_id = entry.payment_id.substr(0,16);
|
||||
entry.height = 0;
|
||||
entry.timestamp = pd.m_timestamp;
|
||||
uint64_t amount = 0;
|
||||
cryptonote::get_inputs_money_amount(pd.m_tx, amount);
|
||||
entry.fee = amount - get_outs_money_amount(pd.m_tx);
|
||||
entry.amount = amount - pd.m_change - entry.fee;
|
||||
entry.fee = pd.m_amount_in - pd.m_amount_out;
|
||||
entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
|
||||
entry.note = m_wallet.get_tx_note(i->first);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -335,7 +335,7 @@ bool gen_block_miner_tx_has_2_in::generate(std::vector<test_event_entry>& events
|
|||
|
||||
tx_source_entry se;
|
||||
se.amount = blk_0.miner_tx.vout[0].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key));
|
||||
se.push_output(0, boost::get<txout_to_key>(blk_0.miner_tx.vout[0].target).key, se.amount);
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_0.miner_tx);
|
||||
se.real_output_in_tx_index = 0;
|
||||
|
@ -377,7 +377,7 @@ bool gen_block_miner_tx_with_txin_to_key::generate(std::vector<test_event_entry>
|
|||
|
||||
tx_source_entry se;
|
||||
se.amount = blk_1.miner_tx.vout[0].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key));
|
||||
se.push_output(0, boost::get<txout_to_key>(blk_1.miner_tx.vout[0].target).key, se.amount);
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(blk_1.miner_tx);
|
||||
se.real_output_in_tx_index = 0;
|
||||
|
|
|
@ -412,7 +412,7 @@ bool fill_output_entries(std::vector<output_index>& out_indices, size_t sender_o
|
|||
if (append)
|
||||
{
|
||||
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
|
||||
output_entries.push_back(tx_source_entry::output_entry(oi.idx, otk.key));
|
||||
output_entries.push_back(tx_source_entry::output_entry(oi.idx, rct::ctkey({rct::pk2rct(otk.key), rct::identity()})));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -544,7 +544,7 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins
|
|||
out.target = txout_to_key(out_eph_public_key);
|
||||
tx.vout.push_back(out);
|
||||
|
||||
tx.version = CURRENT_TRANSACTION_VERSION;
|
||||
tx.version = 1;
|
||||
tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -128,7 +128,7 @@ bool gen_double_spend_in_tx<txs_keeped_by_block>::generate(std::vector<test_even
|
|||
std::vector<cryptonote::tx_source_entry> sources;
|
||||
cryptonote::tx_source_entry se;
|
||||
se.amount = tx_0.vout[0].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key));
|
||||
se.push_output(0, boost::get<cryptonote::txout_to_key>(tx_0.vout[0].target).key, se.amount);
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(tx_0);
|
||||
se.real_output_in_tx_index = 0;
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace
|
|||
{
|
||||
cryptonote::tx_source_entry se;
|
||||
se.amount = tx.vout[out_idx].amount;
|
||||
se.outputs.push_back(std::make_pair(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key));
|
||||
se.push_output(0, boost::get<cryptonote::txout_to_key>(tx.vout[out_idx].target).key, se.amount);
|
||||
se.real_output = 0;
|
||||
se.real_out_tx_key = get_tx_pub_key_from_extra(tx);
|
||||
se.real_output_in_tx_index = out_idx;
|
||||
|
|
|
@ -82,29 +82,18 @@ bool test_transaction_generation_and_ring_signature()
|
|||
src.amount = 70368744177663;
|
||||
{
|
||||
tx_output_entry oe;
|
||||
oe.first = 0;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_1.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
|
||||
oe.first = 1;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_2.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
src.push_output(0, boost::get<txout_to_key>(tx_mine_1.vout[0].target).key, src.amount);
|
||||
|
||||
oe.first = 2;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_3.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
src.push_output(1, boost::get<txout_to_key>(tx_mine_2.vout[0].target).key, src.amount);
|
||||
|
||||
oe.first = 3;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_4.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
src.push_output(2, boost::get<txout_to_key>(tx_mine_3.vout[0].target).key, src.amount);
|
||||
|
||||
oe.first = 4;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_5.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
src.push_output(3, boost::get<txout_to_key>(tx_mine_4.vout[0].target).key, src.amount);
|
||||
|
||||
oe.first = 5;
|
||||
oe.second = boost::get<txout_to_key>(tx_mine_6.vout[0].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
src.push_output(4, boost::get<txout_to_key>(tx_mine_5.vout[0].target).key, src.amount);
|
||||
|
||||
src.push_output(5, boost::get<txout_to_key>(tx_mine_6.vout[0].target).key, src.amount);
|
||||
|
||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(tx_mine_2);
|
||||
src.real_output = 1;
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace
|
|||
{
|
||||
struct tx_builder
|
||||
{
|
||||
void step1_init(size_t version = CURRENT_TRANSACTION_VERSION, uint64_t unlock_time = 0)
|
||||
void step1_init(size_t version = 1, uint64_t unlock_time = 0)
|
||||
{
|
||||
m_tx.vin.clear();
|
||||
m_tx.vout.clear();
|
||||
|
@ -108,9 +108,13 @@ namespace
|
|||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
{
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
std::vector<crypto::public_key> keys(src_entr.outputs.size());
|
||||
size_t j = 0;
|
||||
BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
|
||||
{
|
||||
keys_ptrs.push_back(&o.second);
|
||||
keys[j] = rct::rct2pk(o.second.dest);
|
||||
keys_ptrs.push_back(&keys[j]);
|
||||
++j;
|
||||
}
|
||||
|
||||
m_tx.signatures.push_back(std::vector<crypto::signature>());
|
||||
|
@ -136,7 +140,7 @@ namespace
|
|||
fill_tx_sources_and_destinations(events, blk_head, from, to, amount, TESTS_DEFAULT_FEE, 0, sources, destinations);
|
||||
|
||||
tx_builder builder;
|
||||
builder.step1_init(CURRENT_TRANSACTION_VERSION, unlock_time);
|
||||
builder.step1_init(1, unlock_time);
|
||||
builder.step2_fill_inputs(from.get_keys(), sources);
|
||||
builder.step3_fill_outputs(destinations);
|
||||
builder.step4_calc_hash();
|
||||
|
@ -177,7 +181,7 @@ bool gen_tx_big_version::generate(std::vector<test_event_entry>& events) const
|
|||
fill_tx_sources_and_destinations(events, blk_0, miner_account, miner_account, MK_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations);
|
||||
|
||||
tx_builder builder;
|
||||
builder.step1_init(CURRENT_TRANSACTION_VERSION + 1, 0);
|
||||
builder.step1_init(1 + 1, 0);
|
||||
builder.step2_fill_inputs(miner_account.get_keys(), sources);
|
||||
builder.step3_fill_outputs(destinations);
|
||||
builder.step4_calc_hash();
|
||||
|
|
|
@ -79,7 +79,6 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
|
|||
}
|
||||
|
||||
// create a tx with the Nth outputs of miner's block reward
|
||||
typedef tx_source_entry::output_entry tx_output_entry;
|
||||
std::vector<tx_source_entry> sources;
|
||||
for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) {
|
||||
sources.resize(sources.size()+1);
|
||||
|
@ -88,13 +87,12 @@ bool gen_v2_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
|
|||
src.amount = blocks[0].miner_tx.vout[out_idx[out_idx_idx]].amount;
|
||||
std::cout << "using " << print_money(src.amount) << " output at index " << out_idx[out_idx_idx] << std::endl;
|
||||
for (int m = 0; m <= mixin; ++m) {
|
||||
tx_output_entry oe;
|
||||
int idx;
|
||||
if (is_valid_decomposed_amount(src.amount))
|
||||
oe.first = m+1; // one out of that size per miner tx, including genesis
|
||||
idx = m+1; // one out of that size per miner tx, including genesis
|
||||
else
|
||||
oe.first = 0; // dusty, no other output of that size
|
||||
oe.second = boost::get<txout_to_key>(blocks[m].miner_tx.vout[out_idx[out_idx_idx]].target).key;
|
||||
src.outputs.push_back(oe);
|
||||
idx = 0; // dusty, no other output of that size
|
||||
src.push_output(idx, boost::get<txout_to_key>(blocks[m].miner_tx.vout[out_idx[out_idx_idx]].target).key, src.amount);
|
||||
}
|
||||
src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[0].miner_tx);
|
||||
src.real_output = 0;
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
return false;
|
||||
|
||||
txout_to_key tx_out = boost::get<txout_to_key>(m_miner_txs[i].vout[0].target);
|
||||
output_entries.push_back(std::make_pair(i, tx_out.key));
|
||||
output_entries.push_back(std::make_pair(i, rct::ctkey({rct::pk2rct(tx_out.key), rct::identity()})));
|
||||
m_public_keys[i] = tx_out.key;
|
||||
m_public_key_ptrs[i] = &m_public_keys[i];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue