wallet: reroll fake outs selection on local tx_sanity_check failure
This commit is contained in:
parent
3c01bffd0c
commit
ffe7165ebf
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "cryptonote_basic/cryptonote_basic_impl.h"
|
#include "cryptonote_basic/cryptonote_basic.h"
|
||||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||||
#include "blockchain.h"
|
#include "blockchain.h"
|
||||||
#include "tx_sanity_check.h"
|
#include "tx_sanity_check.h"
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
namespace cryptonote
|
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;
|
cryptonote::transaction tx;
|
||||||
|
|
||||||
|
@ -70,14 +70,18 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
|
||||||
n_indices += in_to_key.key_offsets.size();
|
n_indices += in_to_key.key_offsets.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return tx_sanity_check(rct_indices, n_indices, rct_outs_available);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available)
|
||||||
|
{
|
||||||
if (n_indices <= 10)
|
if (n_indices <= 10)
|
||||||
{
|
{
|
||||||
MDEBUG("n_indices is only " << n_indices << ", not checking");
|
MDEBUG("n_indices is only " << n_indices << ", not checking");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t n_available = blockchain.get_num_mature_outputs(0);
|
if (rct_outs_available < 10000)
|
||||||
if (n_available < 10000)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (rct_indices.size() < n_indices * 8 / 10)
|
if (rct_indices.size() < n_indices * 8 / 10)
|
||||||
|
@ -88,9 +92,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
|
||||||
|
|
||||||
std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
|
std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
|
||||||
uint64_t median = epee::misc_utils::median(offsets);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,11 @@
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include "cryptonote_basic/blobdatatype.h"
|
#include "cryptonote_basic/blobdatatype.h"
|
||||||
|
|
||||||
namespace cryptonote
|
namespace cryptonote
|
||||||
{
|
{
|
||||||
class Blockchain;
|
bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available);
|
||||||
|
bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available);
|
||||||
bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1087,7 +1087,7 @@ namespace cryptonote
|
||||||
return true;
|
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.status = "Failed";
|
||||||
res.reason = "Sanity check failed";
|
res.reason = "Sanity check failed";
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
using namespace epee;
|
using namespace epee;
|
||||||
|
|
||||||
#include "cryptonote_config.h"
|
#include "cryptonote_config.h"
|
||||||
|
#include "cryptonote_core/tx_sanity_check.h"
|
||||||
#include "wallet_rpc_helpers.h"
|
#include "wallet_rpc_helpers.h"
|
||||||
#include "wallet2.h"
|
#include "wallet2.h"
|
||||||
#include "cryptonote_basic/cryptonote_format_utils.h"
|
#include "cryptonote_basic/cryptonote_format_utils.h"
|
||||||
|
@ -7733,7 +7734,49 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs)
|
||||||
|
{
|
||||||
|
std::set<uint64_t> unique;
|
||||||
|
size_t total = 0;
|
||||||
|
|
||||||
|
for (const auto &it : outs)
|
||||||
|
{
|
||||||
|
for (const auto &out : it)
|
||||||
|
{
|
||||||
|
const uint64_t global_index = std::get<0>(out);
|
||||||
|
unique.insert(global_index);
|
||||||
|
}
|
||||||
|
total += it.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(std::move(unique), total);
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> rct_offsets;
|
||||||
|
for (size_t attempts = 3; attempts > 0; --attempts)
|
||||||
|
{
|
||||||
|
get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets);
|
||||||
|
|
||||||
|
const auto unique = outs_unique(outs);
|
||||||
|
if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<crypto::key_image> key_images;
|
||||||
|
key_images.reserve(selected_transfers.size());
|
||||||
|
std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) {
|
||||||
|
key_images.push_back(m_transfers[index].m_key_image);
|
||||||
|
});
|
||||||
|
unset_ring(key_images);
|
||||||
|
}
|
||||||
|
|
||||||
|
THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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, std::vector<uint64_t> &rct_offsets)
|
||||||
{
|
{
|
||||||
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
|
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
|
||||||
outs.clear();
|
outs.clear();
|
||||||
|
@ -7755,7 +7798,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
|
|
||||||
// if we have at least one rct out, get the distribution, or fall back to the previous system
|
// if we have at least one rct out, get the distribution, or fall back to the previous system
|
||||||
uint64_t rct_start_height;
|
uint64_t rct_start_height;
|
||||||
std::vector<uint64_t> rct_offsets;
|
|
||||||
bool has_rct = false;
|
bool has_rct = false;
|
||||||
uint64_t max_rct_index = 0;
|
uint64_t max_rct_index = 0;
|
||||||
for (size_t idx: selected_transfers)
|
for (size_t idx: selected_transfers)
|
||||||
|
@ -7764,7 +7806,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
|
||||||
has_rct = true;
|
has_rct = true;
|
||||||
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
|
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
|
||||||
}
|
}
|
||||||
const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
|
const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets));
|
||||||
if (has_rct_distribution)
|
if (has_rct_distribution)
|
||||||
{
|
{
|
||||||
// check we're clear enough of rct start, to avoid corner cases below
|
// check we're clear enough of rct start, to avoid corner cases below
|
||||||
|
|
|
@ -1435,6 +1435,7 @@ private:
|
||||||
bool is_spent(const transfer_details &td, bool strict = true) const;
|
bool is_spent(const transfer_details &td, bool strict = true) const;
|
||||||
bool is_spent(size_t idx, 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);
|
||||||
|
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets);
|
||||||
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 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;
|
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;
|
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
|
||||||
|
|
Loading…
Reference in New Issue