mirror of
https://github.com/monero-project/monero.git
synced 2025-07-22 00:08:49 -04: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
|
@ -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,14 +1100,24 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
|
|||
{
|
||||
LOG_ERROR("Creating block template: error: invalid transaction size");
|
||||
}
|
||||
uint64_t inputs_amount;
|
||||
if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
|
||||
if (cur_tx.tx.version == 1)
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: cannot get inputs amount");
|
||||
uint64_t inputs_amount;
|
||||
if (!get_inputs_money_amount(cur_tx.tx, inputs_amount))
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: cannot get inputs amount");
|
||||
}
|
||||
else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx))
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: invalid fee");
|
||||
}
|
||||
}
|
||||
else if (cur_tx.fee != inputs_amount - get_outs_money_amount(cur_tx.tx))
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: invalid fee");
|
||||
if (cur_tx.fee != cur_tx.tx.txnFee)
|
||||
{
|
||||
LOG_ERROR("Creating block template: error: invalid fee");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (txs_size != real_txs_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;
|
||||
oen.commitment = m_db->get_rct_commitment(i);
|
||||
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,9 +2220,24 @@ 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 (!is_valid_decomposed_amount(o.amount)) {
|
||||
tvc.m_invalid_output = true;
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,28 +2369,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
|
||||
#if defined(CACHE_VIN_RESULTS)
|
||||
auto itk = it->second.find(in_to_key.k_image);
|
||||
if(itk != it->second.end())
|
||||
{
|
||||
if(!itk->second)
|
||||
auto itk = it->second.find(in_to_key.k_image);
|
||||
if(itk != it->second.end())
|
||||
{
|
||||
LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
|
||||
return false;
|
||||
}
|
||||
if(!itk->second)
|
||||
{
|
||||
LOG_PRINT_L1("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
// txin has been verified already, skip
|
||||
sig_index++;
|
||||
continue;
|
||||
}
|
||||
// txin has been verified already, skip
|
||||
sig_index++;
|
||||
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,28 +2405,31 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
return false;
|
||||
}
|
||||
|
||||
if (threads > 1)
|
||||
if (tx.version == 1)
|
||||
{
|
||||
// ND: Speedup
|
||||
// 1. Thread ring signature verification if possible.
|
||||
ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
|
||||
}
|
||||
else
|
||||
{
|
||||
check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
|
||||
if (!results[sig_index])
|
||||
if (threads > 1)
|
||||
{
|
||||
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);
|
||||
|
||||
if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
|
||||
{
|
||||
LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height);
|
||||
}
|
||||
|
||||
return false;
|
||||
// ND: Speedup
|
||||
// 1. Thread ring signature verification if possible.
|
||||
ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
|
||||
}
|
||||
else
|
||||
{
|
||||
check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
|
||||
if (!results[sig_index])
|
||||
{
|
||||
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);
|
||||
|
||||
if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
|
||||
{
|
||||
LOG_PRINT_L1("*pmax_used_block_height: " << *pmax_used_block_height);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
it->second[in_to_key.k_image] = true;
|
||||
}
|
||||
it->second[in_to_key.k_image] = true;
|
||||
}
|
||||
|
||||
sig_index++;
|
||||
|
@ -2349,30 +2437,80 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
|
|||
|
||||
KILL_IOSERVICE();
|
||||
|
||||
if (threads > 1)
|
||||
if (tx.version == 1)
|
||||
{
|
||||
// save results to table, passed or otherwise
|
||||
bool failed = false;
|
||||
for (size_t i = 0; i < tx.vin.size(); i++)
|
||||
if (threads > 1)
|
||||
{
|
||||
const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
|
||||
it->second[in_to_key.k_image] = results[i];
|
||||
if(!failed && !results[i])
|
||||
failed = true;
|
||||
// save results to table, passed or otherwise
|
||||
bool failed = false;
|
||||
for (size_t i = 0; i < tx.vin.size(); i++)
|
||||
{
|
||||
const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
|
||||
it->second[in_to_key.k_image] = results[i];
|
||||
if(!failed && !results[i])
|
||||
failed = true;
|
||||
}
|
||||
|
||||
if (failed)
|
||||
{
|
||||
LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
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 ring signatures!, t_loop: " << t_t1);
|
||||
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;
|
||||
}
|
||||
}
|
||||
LOG_PRINT_L1("t_loop: " << t_t1);
|
||||
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;
|
||||
}
|
||||
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());
|
||||
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,34 +197,46 @@ namespace cryptonote
|
|||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELDS(*static_cast<transaction_prefix *>(this))
|
||||
|
||||
ar.tag("signatures");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
|
||||
bool signatures_not_expected = signatures.empty();
|
||||
if (!signatures_not_expected && vin.size() != signatures.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < vin.size(); ++i)
|
||||
if (version == 1)
|
||||
{
|
||||
size_t signature_size = get_signature_size(vin[i]);
|
||||
if (signatures_not_expected)
|
||||
{
|
||||
if (0 == signature_size)
|
||||
continue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
|
||||
if (signature_size != signatures[i].size())
|
||||
ar.tag("signatures");
|
||||
ar.begin_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures);
|
||||
bool signatures_not_expected = signatures.empty();
|
||||
if (!signatures_not_expected && vin.size() != signatures.size())
|
||||
return false;
|
||||
|
||||
FIELDS(signatures[i]);
|
||||
for (size_t i = 0; i < vin.size(); ++i)
|
||||
{
|
||||
size_t signature_size = get_signature_size(vin[i]);
|
||||
if (signatures_not_expected)
|
||||
{
|
||||
if (0 == signature_size)
|
||||
continue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vin.size() - i > 1)
|
||||
ar.delimit_array();
|
||||
PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]);
|
||||
if (signature_size != signatures[i].size())
|
||||
return false;
|
||||
|
||||
FIELDS(signatures[i]);
|
||||
|
||||
if (vin.size() - i > 1)
|
||||
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;
|
||||
}
|
||||
}
|
||||
ar.end_array();
|
||||
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;
|
||||
a & x.signatures;
|
||||
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,15 +576,19 @@ namespace cryptonote
|
|||
return false;
|
||||
}
|
||||
|
||||
uint64_t amount_in = 0;
|
||||
get_inputs_money_amount(tx, amount_in);
|
||||
uint64_t amount_out = get_outs_money_amount(tx);
|
||||
|
||||
if(amount_in <= amount_out)
|
||||
if (tx.version == 1)
|
||||
{
|
||||
LOG_PRINT_RED_L1("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
|
||||
return false;
|
||||
uint64_t amount_in = 0;
|
||||
get_inputs_money_amount(tx, amount_in);
|
||||
uint64_t amount_out = get_outs_money_amount(tx);
|
||||
|
||||
if(amount_in <= amount_out)
|
||||
{
|
||||
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,33 +605,100 @@ namespace cryptonote
|
|||
}
|
||||
|
||||
|
||||
//generate ring signatures
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
||||
std::stringstream ss_ring_s;
|
||||
size_t i = 0;
|
||||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
if (tx.version == 1)
|
||||
{
|
||||
ss_ring_s << "pub_keys:" << ENDL;
|
||||
std::vector<const crypto::public_key*> keys_ptrs;
|
||||
BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
|
||||
//generate ring signatures
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||
|
||||
std::stringstream ss_ring_s;
|
||||
size_t i = 0;
|
||||
BOOST_FOREACH(const tx_source_entry& src_entr, sources)
|
||||
{
|
||||
keys_ptrs.push_back(&o.second);
|
||||
ss_ring_s << o.second << ENDL;
|
||||
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[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>());
|
||||
std::vector<crypto::signature>& sigs = tx.signatures.back();
|
||||
sigs.resize(src_entr.outputs.size());
|
||||
crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
|
||||
ss_ring_s << "signatures:" << ENDL;
|
||||
std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
|
||||
ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output;
|
||||
i++;
|
||||
}
|
||||
|
||||
tx.signatures.push_back(std::vector<crypto::signature>());
|
||||
std::vector<crypto::signature>& sigs = tx.signatures.back();
|
||||
sigs.resize(src_entr.outputs.size());
|
||||
crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
|
||||
ss_ring_s << "signatures:" << ENDL;
|
||||
std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
|
||||
ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output;
|
||||
i++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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,25 +108,34 @@ namespace cryptonote
|
|||
return false;
|
||||
}
|
||||
|
||||
uint64_t inputs_amount = 0;
|
||||
if(!get_inputs_money_amount(tx, inputs_amount))
|
||||
{
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
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));
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_overspend = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// fee per kilobyte, size rounded up.
|
||||
uint64_t fee = inputs_amount - outputs_amount;
|
||||
uint64_t fee;
|
||||
|
||||
if (tx.version == 1)
|
||||
{
|
||||
uint64_t inputs_amount = 0;
|
||||
if(!get_inputs_money_amount(tx, inputs_amount))
|
||||
{
|
||||
tvc.m_verifivation_failed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
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));
|
||||
tvc.m_verifivation_failed = true;
|
||||
tvc.m_overspend = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue