Skip to content

Commit

Permalink
wip - add rct to the protocol
Browse files Browse the repository at this point in the history
It is not yet constrained to a fork, so don't use on the real network
or you'll be orphaned or rejected.

Note 1: wip, unfinished
Note 2: if someone links you to this with "omg monero 2damoon", see note 1
Note 3: comments and bug reports welcome
  • Loading branch information
moneromooo-monero committed Jun 16, 2016
1 parent 2019468 commit d966d9a
Show file tree
Hide file tree
Showing 19 changed files with 1,047 additions and 214 deletions.
8 changes: 8 additions & 0 deletions Makefile
Expand Up @@ -84,6 +84,14 @@ release-static-win32:
mkdir -p build/release
cd build/release && cmake -G "MSYS Makefiles" -D STATIC=ON -D ARCH="i686" -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=Release -D CMAKE_TOOLCHAIN_FILE=../../cmake/32-bit-toolchain.cmake -D MSYS2_FOLDER=c:/msys32 ../.. && $(MAKE)

custom:
mkdir -p build/custom
cd build/custom && cmake -D BUILD_TESTS=ON -D USE_LTO=OFF -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=custom ../.. && $(MAKE)

custom-test:
mkdir -p build/custom
cd build/custom && cmake -D BUILD_TESTS=ON -D USE_LTO=OFF -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=custom ../.. && $(MAKE) all test

clean:
@echo "WARNING: Back-up your wallet if it exists within ./build!" ; \
read -r -p "This will destroy the build directory, continue (y/N)?: " CONTINUE; \
Expand Down
4 changes: 4 additions & 0 deletions src/blockchain_db/blockchain_db.cpp
Expand Up @@ -91,6 +91,10 @@ 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_ecdh_info(tx.rct_signatures.ecdhInfo[i]);
}
}
add_tx_amount_output_indices(tx_id, amount_output_indices);
}
Expand Down
2 changes: 2 additions & 0 deletions src/blockchain_db/lmdb/db_lmdb.cpp
Expand Up @@ -868,6 +868,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_ecdh_info(amount_output_indices[i-1]);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/cryptonote_config.h
Expand Up @@ -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
Expand Down
201 changes: 143 additions & 58 deletions src/cryptonote_core/blockchain.cpp
Expand Up @@ -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
Expand All @@ -63,6 +64,13 @@
*
*/

#undef DIFFICULTY_WINDOW
#undef DIFFICULTY_BLOCKS_COUNT
#undef DIFFICULTY_LAG
#define DIFFICULTY_WINDOW 8
#define DIFFICULTY_LAG 2
#define DIFFICULTY_BLOCKS_COUNT (DIFFICULTY_WINDOW + DIFFICULTY_LAG)

using namespace cryptonote;
using epee::string_tools::pod_to_hex;
extern "C" void slow_hash_allocate_state();
Expand Down Expand Up @@ -1086,14 +1094,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)
Expand Down Expand Up @@ -1599,16 +1617,26 @@ 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);

LOG_PRINT_L0("Adding out to RCTR random out: amount " << amount << ", index " << i);
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.ecdh_info = m_db->get_rct_ecdh_info(i);
if (amount == 0)
{
oen.mask = m_db->get_rct_ecdh_info(i).mask;
}
else
{
// not a rct output, make a fake commitment with zero key
oen.mask = rct::zeroCommit(amount);
}
}
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
Expand Down Expand Up @@ -1648,7 +1676,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);
}
}
}
Expand Down Expand Up @@ -1688,10 +1716,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;
}
//------------------------------------------------------------------
Expand Down Expand Up @@ -2134,9 +2196,12 @@ 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;
}
}
}
}
Expand Down Expand Up @@ -2268,28 +2333,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.signatures[sig_index], 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);
Expand All @@ -2301,54 +2369,68 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
return false;
}

if (threads > 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
if (tx.version == 1)
{
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()
// 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])
{
LOG_PRINT_L1("*pmax_used_block_height: " << *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);

return false;
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++;
}

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)
if (failed)
{
LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
return false;
}
}
}
else
{
// from version 2, check ringct signatures
if (!rct::verRct(tx.rct_signatures))
{
LOG_PRINT_L1("Failed to check ring signatures!, t_loop: " << t_t1);
LOG_PRINT_L1("Failed to check ringct signatures!");
return false;
}
}
LOG_PRINT_L1("t_loop: " << t_t1);
return true;
}

Expand Down Expand Up @@ -2400,7 +2482,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, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height)
{
LOG_PRINT_L3("Blockchain::" << __func__);

Expand Down Expand Up @@ -2450,7 +2532,10 @@ 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) {
// ringct uses another signature scheme
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());
}
return true;
}
//------------------------------------------------------------------
Expand Down
6 changes: 4 additions & 2 deletions src/cryptonote_core/blockchain.h
Expand Up @@ -877,6 +877,7 @@ 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
Expand All @@ -885,7 +886,7 @@ namespace cryptonote
*
* @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, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height);

/**
* @brief validate a transaction's inputs and their keys
Expand Down Expand Up @@ -1051,9 +1052,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)
Expand Down

0 comments on commit d966d9a

Please sign in to comment.