Skip to content

Commit

Permalink
Merge pull request #333 from fireice-uk/checkpoint-hash
Browse files Browse the repository at this point in the history
  • Loading branch information
AxVultis committed Aug 17, 2024
2 parents 246754d + 4b96cd6 commit 027b370
Show file tree
Hide file tree
Showing 21 changed files with 631 additions and 461 deletions.
187 changes: 23 additions & 164 deletions src/CryptoNoteConfig.h

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions src/CryptoNoteCore/BlockIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ namespace cn {
return result;
}

crypto::Hash BlockIndex::getHashOfIds(uint32_t startBlockIndex, uint32_t maxCount) const {
/* This can be made more efficient by hashing in-place instead of copying all hashes, but it needs three function hash code */
std::vector<crypto::Hash> block_ids = getBlockIds(startBlockIndex, maxCount);
return crypto::cn_fast_hash(block_ids.data(), block_ids.size() * sizeof(crypto::Hash));
}

bool BlockIndex::findSupplement(const std::vector<crypto::Hash>& ids, uint32_t& offset) const {
for (const auto& id : ids) {
if (getBlockHeight(id, offset)) {
Expand Down
1 change: 1 addition & 0 deletions src/CryptoNoteCore/BlockIndex.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ namespace cn

crypto::Hash getBlockId(uint32_t height) const;
std::vector<crypto::Hash> getBlockIds(uint32_t startBlockIndex, uint32_t maxCount) const;
crypto::Hash getHashOfIds(uint32_t startBlockIndex, uint32_t maxCount) const;
bool findSupplement(const std::vector<crypto::Hash>& ids, uint32_t& offset) const;
std::vector<crypto::Hash> buildSparseChain(const crypto::Hash& startBlockId) const;
crypto::Hash getTailId() const;
Expand Down
96 changes: 70 additions & 26 deletions src/CryptoNoteCore/Blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,6 @@ namespace cn
m_blockchainIndexesEnabled(blockchainIndexesEnabled),
m_blockchainAutosaveEnabled(blockchainAutosaveEnabled),
logger(logger, "Blockchain")

{
}

Expand Down Expand Up @@ -461,7 +460,6 @@ namespace cn
bool Blockchain::init(const std::string &config_folder, bool load_existing, bool testnet)
{
m_testnet = testnet;
m_checkpoints.set_testnet(testnet);
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
if (!config_folder.empty() && !tools::create_directories_if_necessary(config_folder))
{
Expand All @@ -471,6 +469,9 @@ namespace cn

m_config_folder = config_folder;

m_checkpoints.init_targets(testnet, appendPath(config_folder, m_currency.checkpointFileName()));
m_checkpoints.load_checkpoints_from_file();

if (!m_blocks.open(appendPath(config_folder, m_currency.blocksFileName()), appendPath(config_folder, m_currency.blockIndexesFileName()), 1024))
{
return false;
Expand Down Expand Up @@ -600,25 +601,44 @@ namespace cn

bool Blockchain::checkCheckpoints(uint32_t &lastValidCheckpointHeight)
{
std::vector<uint32_t> checkpointHeights = m_checkpoints.getCheckpointHeights();
for (const auto &checkpointHeight : checkpointHeights)
bool rv = true;
std::vector<std::pair<uint32_t, crypto::Hash>> checkpointHeights = m_checkpoints.get_checkpoint_targets();
lastValidCheckpointHeight = 0;

/* Hashes are in reverse order so if everything is ok we will only do one iteration */
for (const auto &check : checkpointHeights)
{
if (m_blocks.size() <= checkpointHeight)
{
return true;
}
uint32_t height = check.first;
if (m_blocks.size() <= height)
continue;

if (m_checkpoints.check_block(checkpointHeight, getBlockIdByHeight(checkpointHeight)))
crypto::Hash hv = m_blockIndex.getHashOfIds(0, height+1);
if (hv == check.second)
{
lastValidCheckpointHeight = checkpointHeight;
lastValidCheckpointHeight = height;
break;
}
else
{
return false;
logger(ERROR, BRIGHT_RED) << "Checkpoint failed for " << height << " got " <<
hv << " expected " << check.second;
rv = false;
}
}
logger(INFO, BRIGHT_WHITE) << "Checkpoints passed";
return true;

if(rv)
{
logger(INFO, BRIGHT_WHITE) << "Checkpoints passed " << m_checkpoints.get_points_size() << " " << lastValidCheckpointHeight;
uint32_t n_valid_blocks = lastValidCheckpointHeight+1;
if(m_checkpoints.get_points_size() < n_valid_blocks)
m_checkpoints.set_checkpoint_list(m_blockIndex.getBlockIds(0, n_valid_blocks));
}
else
{
logger(ERROR, BRIGHT_RED) << "Checkpoints failed, last valid height " << lastValidCheckpointHeight;
}

return rv;
}

void Blockchain::rebuildCache()
Expand Down Expand Up @@ -1416,6 +1436,29 @@ namespace cn
return true;
}

bool Blockchain::is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const {
if (0 == block_height)
return false;

uint32_t lowest_height = blockchain_height - cn::parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;

if (blockchain_height < cn::parameters::CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)
{
lowest_height = 0;
}

if (block_height < lowest_height && !m_checkpoints.is_in_checkpoint_zone(block_height))
{
logger(logging::DEBUGGING, logging::WHITE)
<< "<< Checkpoints.cpp << "
<< "Reorganization depth too deep : " << (blockchain_height - block_height) << ". Block Rejected";
return false;
}

uint32_t checkpoint_height = m_checkpoints.get_greatest_target_height();
return checkpoint_height < block_height;
}

bool Blockchain::handle_alternative_block(const Block &b, const crypto::Hash &id, block_verification_context &bvc, bool sendNewAlternativeBlockMessage)
{
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
Expand All @@ -1429,10 +1472,8 @@ namespace cn
return false;
}

/* in the absence of a better solution, we fetch checkpoints from dns records */
m_checkpoints.load_checkpoints_from_dns();

if (!m_checkpoints.is_alternative_block_allowed(getCurrentBlockchainHeight(), block_height))
if (!is_alternative_block_allowed(getCurrentBlockchainHeight(), block_height))
{
logger(DEBUGGING) << "Block with id: " << id << std::endl
<< " can't be accepted for alternative chain, block height: " << block_height << std::endl
Expand Down Expand Up @@ -1527,8 +1568,8 @@ namespace cn
bei.bl = b;
bei.height = alt_chain.size() ? it_prev->second.height + 1 : mainPrevHeight + 1;

bool is_a_checkpoint;
if (!m_checkpoints.check_block(bei.height, id, is_a_checkpoint))
auto checkpoint_status = m_checkpoints.check_checkpoint(bei.height, id);
if ( checkpoint_status == CheckpointList::is_in_zone_failed )
{
logger(ERROR, BRIGHT_RED) << "Checkpoint validaton failure";

Expand Down Expand Up @@ -1594,7 +1635,7 @@ namespace cn

alt_chain.push_back(i_res.first->first);

if (is_a_checkpoint)
if ( checkpoint_status == CheckpointList::is_checkpointed )
{
//do reorganize!
logger(INFO, BRIGHT_GREEN) << "###### REORGANIZE on height: " << m_alternative_chains[alt_chain.front()].height << " of " << m_blocks.size() - 1 << ", checkpoint is found in alternative chain on height " << bei.height;
Expand Down Expand Up @@ -2494,15 +2535,13 @@ namespace cn

auto longhashTimeStart = std::chrono::steady_clock::now();
crypto::Hash proof_of_work = NULL_HASH;
if (m_checkpoints.is_in_checkpoint_zone(getCurrentBlockchainHeight()))
auto checkpoint_status = m_checkpoints.check_checkpoint(getCurrentBlockchainHeight(), blockHash);
if (checkpoint_status == CheckpointList::is_in_zone_failed )
{
if (!m_checkpoints.check_block(getCurrentBlockchainHeight(), blockHash))
{
bvc.m_verification_failed = true;
return false;
}
bvc.m_verification_failed = true;
return false;
}
else
else if(checkpoint_status == CheckpointList::is_out_of_zone )
{
if (!m_currency.checkProofOfWork(m_cn_context, blockData, currentDifficulty, proof_of_work))
{
Expand Down Expand Up @@ -3300,4 +3339,9 @@ namespace cn
return m_checkpoints.is_in_checkpoint_zone(height);
}

crypto::Hash Blockchain::getCheckpointHash(uint32_t height) const
{
std::lock_guard<decltype(m_blockchain_lock)> lk(m_blockchain_lock);
return m_blockIndex.getHashOfIds(0, height+1);
}
} // namespace cn
13 changes: 9 additions & 4 deletions src/CryptoNoteCore/Blockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#include "Common/ObserverManager.h"
#include "Common/Util.h"
#include "CryptoNoteCore/BlockIndex.h"
#include "CryptoNoteCore/Checkpoints.h"
#include "CryptoNoteCore/CheckpointList.h"
#include "CryptoNoteCore/Currency.h"
#include "CryptoNoteCore/DepositIndex.h"
#include "CryptoNoteCore/IBlockchainStorageObserver.h"
Expand Down Expand Up @@ -68,7 +68,6 @@ namespace cn
bool getLowerBound(uint64_t timestamp, uint64_t startOffset, uint32_t &height);
std::vector<crypto::Hash> getBlockIds(uint32_t startHeight, uint32_t maxCount);

void setCheckpoints(Checkpoints &&chk_pts) { m_checkpoints = std::move(chk_pts); }
bool getBlocks(uint32_t start_offset, uint32_t count, std::list<Block> &blocks, std::list<Transaction> &txs);
bool getBlocks(uint32_t start_offset, uint32_t count, std::list<Block> &blocks);
bool getAlternativeBlocks(std::list<Block> &blocks);
Expand All @@ -84,6 +83,10 @@ namespace cn
bool haveTransaction(const crypto::Hash &id);
bool haveTransactionKeyImagesAsSpent(const Transaction &tx);

CheckpointList& getCheckpointList() {
return m_checkpoints;
}

uint32_t getCurrentBlockchainHeight(); // TODO rename to getCurrentBlockchainSize
crypto::Hash getTailId();
crypto::Hash getTailId(uint32_t &height);
Expand Down Expand Up @@ -125,6 +128,7 @@ namespace cn
uint64_t coinsEmittedAtHeight(uint64_t height);
uint64_t difficultyAtHeight(uint64_t height);
bool isInCheckpointZone(const uint32_t height) const;
crypto::Hash getCheckpointHash(uint32_t height) const;

template <class visitor_t>
bool scanOutputKeysForIndexes(const KeyInput &tx_in_to_key, visitor_t &vis, uint32_t *pmax_related_block_height = nullptr);
Expand Down Expand Up @@ -284,7 +288,7 @@ namespace cn
outputs_container m_outputs;

std::string m_config_folder;
Checkpoints m_checkpoints;
CheckpointList m_checkpoints;
std::atomic<bool> m_is_in_checkpoint_zone;

using Blocks = SwappedVector<BlockEntry>;
Expand Down Expand Up @@ -319,6 +323,7 @@ namespace cn


bool switch_to_alternative_blockchain(const std::list<crypto::Hash> &alt_chain, bool discard_disconnected_chain);
bool is_alternative_block_allowed(uint32_t blockchain_height, uint32_t block_height) const;
bool handle_alternative_block(const Block &b, const crypto::Hash &id, block_verification_context &bvc, bool sendNewAlternativeBlockMessage = true);
difficulty_type get_next_difficulty_for_alternative_chain(const std::list<crypto::Hash> &alt_chain, const BlockEntry &bei);
void pushToDepositIndex(const BlockEntry &block, uint64_t interest);
Expand Down Expand Up @@ -482,4 +487,4 @@ namespace cn
return true;
}
};
} // namespace cn
} // namespace cn
130 changes: 130 additions & 0 deletions src/CryptoNoteCore/CheckpointList.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright (c) 2011-2017 The Cryptonote developers
// Copyright (c) 2017-2018 The Circle Foundation & Conceal Devs
// Copyright (c) 2018-2023 Conceal Network & Conceal Devs
//
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#pragma once
#include <vector>
#include <unordered_set>

#include "CryptoNoteBasicImpl.h"
#include <Logging/LoggerRef.h>

namespace cn
{
class CheckpointList
{
public:
explicit CheckpointList(logging::ILogger& log) : logger(log, "checkpoint_list") {}

void init_targets(bool is_testnet, const std::string& save_file);

bool add_checkpoint_list(uint32_t start_height, std::vector<crypto::Hash>& points);
bool set_checkpoint_list(std::vector<crypto::Hash>&& points);
bool load_checkpoints_from_file();

uint32_t get_points_size() const
{
const std::lock_guard<std::mutex> lock(m_points_lock);
return m_points.size();
}

uint32_t get_greatest_target_height() const
{
return m_targets.rbegin()->first - 1;
}

bool is_ready() const
{
const std::lock_guard<std::mutex> lock(m_points_lock);
return m_points.size()-1 >= get_greatest_target_height();
}

bool is_in_checkpoint_zone(uint32_t height) const
{
const std::lock_guard<std::mutex> lock(m_points_lock);
return m_points.size() < height;
}

enum check_rt
{
is_out_of_zone,
is_in_zone_failed,
is_checkpointed
};

check_rt check_checkpoint(uint32_t height, const crypto::Hash& hv) const
{
const std::lock_guard<std::mutex> lock(m_points_lock);

if(m_points.size() <= height)
return is_out_of_zone;
if(m_points[height] == hv)
return is_checkpointed;
else
return is_in_zone_failed;
}

std::vector<std::pair<uint32_t, crypto::Hash>> get_checkpoint_targets() const
{
std::vector<std::pair<uint32_t, crypto::Hash>> rv;
rv.reserve(m_targets.size());
for(auto it = m_targets.rbegin(); it != m_targets.rend(); ++it)
rv.emplace_back(it->first-1, it->second);
return rv;
}

struct t_get_incomplete_checkpoint_target_rv {
crypto::Hash target_hash = NULL_HASH;
uint32_t start_height;
uint32_t end_height;
};

t_get_incomplete_checkpoint_target_rv get_incomplete_checkpoint_target() const
{
t_get_incomplete_checkpoint_target_rv rv;
size_t point_size = m_points.size();
if(point_size >= m_targets.rbegin()->first)
return rv;
rv.start_height = 0;
for(const auto& p : m_targets)
{
if(point_size < p.first)
{
rv.end_height = p.first;
rv.target_hash = p.second;
return rv;
}
else
{
rv.start_height = p.first;
}
}
return rv;
}


private:
bool m_testnet;
logging::LoggerRef logger;
std::string m_save_file;

mutable std::mutex m_points_lock;
std::map<uint32_t, crypto::Hash> m_targets; /*NB uint32_t is size not height */
std::unordered_set<uint32_t> m_valid_point_sizes;
std::vector<crypto::Hash> m_points;

bool save_checkpoints();
bool add_checkpoint_target(uint32_t height, const std::string &hash_str);
bool is_fsize_valid(uint32_t fsize)
{
if(fsize % sizeof(crypto::Hash) != 0)
return false;
fsize /= sizeof(crypto::Hash);

return m_valid_point_sizes.find(fsize) != m_valid_point_sizes.end();
}
};
}
Loading

0 comments on commit 027b370

Please sign in to comment.