Skip to content
Permalink
Browse files

wallet: reroll fake outs on local tx_sanity_check failure (RingCT)

  • Loading branch information
xiphon committed Jan 10, 2020
1 parent 3c01bff commit 8ba34f78b3ca91171d88c56b6549406242ccf0ee
@@ -28,7 +28,6 @@

#include <stdint.h>
#include <vector>
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "blockchain.h"
#include "tx_sanity_check.h"
@@ -39,7 +38,7 @@
namespace cryptonote
{

bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob)
bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available)
{
cryptonote::transaction tx;

@@ -49,6 +48,11 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
return false;
}

return tx_sanity_check(tx, rct_outs_available);
}

bool tx_sanity_check(const cryptonote::transaction &tx, uint64_t rct_outs_available)
{
if (cryptonote::is_coinbase(tx))
{
MERROR("Transaction is coinbase");
@@ -76,8 +80,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
return true;
}

uint64_t n_available = blockchain.get_num_mature_outputs(0);
if (n_available < 10000)
if (rct_outs_available < 10000)
return true;

if (rct_indices.size() < n_indices * 8 / 10)
@@ -88,9 +91,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob

std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
uint64_t median = epee::misc_utils::median(offsets);
if (median < n_available * 6 / 10)
if (median < rct_outs_available * 6 / 10)
{
MERROR("median offset index is too low (median is " << median << " out of total " << n_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
return false;
}

@@ -27,10 +27,10 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "cryptonote_basic/blobdatatype.h"
#include "cryptonote_basic/cryptonote_basic.h"

namespace cryptonote
{
class Blockchain;

bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob);
bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available);
bool tx_sanity_check(const cryptonote::transaction &tx, uint64_t rct_outs_available);
}
@@ -1087,7 +1087,7 @@ namespace cryptonote
return true;
}

if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob))
if (req.do_sanity_checks && !cryptonote::tx_sanity_check(tx_blob, m_core.get_blockchain_storage().get_num_mature_outputs(0)))
{
res.status = "Failed";
res.reason = "Sanity check failed";
@@ -44,6 +44,7 @@
using namespace epee;

#include "cryptonote_config.h"
#include "cryptonote_core/tx_sanity_check.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -7733,7 +7734,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
}
}

void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count)
void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, uint64_t *rct_outs_available/* = nullptr*/)
{
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear();
@@ -7773,6 +7774,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index,
error::get_output_distribution, "Daemon reports suspicious number of rct outputs");
}
if (rct_outs_available)
{
*rct_outs_available = has_rct_distribution ? rct_offsets.back() : 0;
}

// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
@@ -8479,6 +8484,21 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config)
{
bool sanity_check_passed = false;
for (size_t attempts = 3; !sanity_check_passed && attempts > 0; --attempts)
{
uint64_t rct_outs_available;
transfer_selected_rct(dsts, selected_transfers, fake_outputs_count, outs, unlock_time, fee, extra, tx, ptx, rct_config, &rct_outs_available);
sanity_check_passed = tx_sanity_check(tx, rct_outs_available);
}
THROW_WALLET_EXCEPTION_IF(!sanity_check_passed, error::wallet_internal_error, tr("Transaction sanity check failed"));
}

void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config,
uint64_t *rct_outs_available)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@@ -8569,7 +8589,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");

if (outs.empty())
get_outs(outs, selected_transfers, fake_outputs_count); // may throw
get_outs(outs, selected_transfers, fake_outputs_count, rct_outs_available); // may throw

//prepare inputs
LOG_PRINT_L2("preparing outputs");
@@ -1434,7 +1434,7 @@ namespace tools
void set_unspent(size_t idx);
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, uint64_t *rct_outs_available = nullptr);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
@@ -1452,6 +1452,10 @@ namespace tools
crypto::chacha_key get_ringdb_key();
void setup_keys(const epee::wipeable_string &password);
size_t get_transfer_details(const crypto::key_image &ki) const;
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config,
uint64_t *rct_outs_available);

void register_devices();
hw::device& lookup_device(const std::string & device_descriptor);

0 comments on commit 8ba34f7

Please sign in to comment.
You can’t perform that action at this time.