mirror of
https://github.com/vtnerd/monero-lws.git
synced 2025-01-03 17:29:25 +00:00
Refuse chain rollback past a checkpoint (#92)
This commit is contained in:
parent
d2ca5b4180
commit
55f6bbb386
4 changed files with 76 additions and 50 deletions
|
@ -409,52 +409,6 @@ namespace db
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \return a single instance of compiled-in checkpoints for lws
|
|
||||||
cryptonote::checkpoints const& get_checkpoints()
|
|
||||||
{
|
|
||||||
struct initializer
|
|
||||||
{
|
|
||||||
cryptonote::checkpoints data;
|
|
||||||
|
|
||||||
initializer()
|
|
||||||
: data()
|
|
||||||
{
|
|
||||||
data.init_default_checkpoints(lws::config::network);
|
|
||||||
|
|
||||||
std::string const* genesis_tx = nullptr;
|
|
||||||
std::uint32_t genesis_nonce = 0;
|
|
||||||
|
|
||||||
switch (lws::config::network)
|
|
||||||
{
|
|
||||||
case cryptonote::TESTNET:
|
|
||||||
genesis_tx = std::addressof(::config::testnet::GENESIS_TX);
|
|
||||||
genesis_nonce = ::config::testnet::GENESIS_NONCE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case cryptonote::STAGENET:
|
|
||||||
genesis_tx = std::addressof(::config::stagenet::GENESIS_TX);
|
|
||||||
genesis_nonce = ::config::stagenet::GENESIS_NONCE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case cryptonote::MAINNET:
|
|
||||||
genesis_tx = std::addressof(::config::GENESIS_TX);
|
|
||||||
genesis_nonce = ::config::GENESIS_NONCE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
MONERO_THROW(lws::error::bad_blockchain, "Unsupported net type");
|
|
||||||
}
|
|
||||||
cryptonote::block b;
|
|
||||||
cryptonote::generate_genesis_block(b, *genesis_tx, genesis_nonce);
|
|
||||||
crypto::hash block_hash = cryptonote::get_block_hash(b);
|
|
||||||
if (!data.add_checkpoint(0, epee::to_hex::string(epee::as_byte_span(block_hash))))
|
|
||||||
MONERO_THROW(lws::error::bad_blockchain, "Genesis tx and checkpoints file mismatch");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static const initializer instance;
|
|
||||||
return instance.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! \return Current block hash at `id` using `cur`.
|
//! \return Current block hash at `id` using `cur`.
|
||||||
expect<crypto::hash> do_get_block_hash(MDB_cursor& cur, block_id id) noexcept
|
expect<crypto::hash> do_get_block_hash(MDB_cursor& cur, block_id id) noexcept
|
||||||
{
|
{
|
||||||
|
@ -469,7 +423,7 @@ namespace db
|
||||||
cursor::blocks cur = MONERO_UNWRAP(lmdb::open_cursor<cursor::close_blocks>(txn, tbl));
|
cursor::blocks cur = MONERO_UNWRAP(lmdb::open_cursor<cursor::close_blocks>(txn, tbl));
|
||||||
|
|
||||||
std::map<std::uint64_t, crypto::hash> const& points =
|
std::map<std::uint64_t, crypto::hash> const& points =
|
||||||
get_checkpoints().get_points();
|
storage::get_checkpoints().get_points();
|
||||||
|
|
||||||
if (points.empty() || points.begin()->first != 0)
|
if (points.empty() || points.begin()->first != 0)
|
||||||
MONERO_THROW(lws::error::bad_blockchain, "Checkpoints are empty/expected genesis hash");
|
MONERO_THROW(lws::error::bad_blockchain, "Checkpoints are empty/expected genesis hash");
|
||||||
|
@ -558,7 +512,7 @@ namespace db
|
||||||
return success();
|
return success();
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::uint64_t checkpoint = get_checkpoints().get_max_height();
|
const std::uint64_t checkpoint = lws::db::storage::get_checkpoints().get_max_height();
|
||||||
const std::uint64_t anchor = lmdb::to_native(out.back().id);
|
const std::uint64_t anchor = lmdb::to_native(out.back().id);
|
||||||
|
|
||||||
for (unsigned i = 1; i <= max_internal; ++i)
|
for (unsigned i = 1; i <= max_internal; ++i)
|
||||||
|
@ -1155,6 +1109,51 @@ namespace db
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cryptonote::checkpoints const& storage::get_checkpoints()
|
||||||
|
{
|
||||||
|
struct initializer
|
||||||
|
{
|
||||||
|
cryptonote::checkpoints data;
|
||||||
|
|
||||||
|
initializer()
|
||||||
|
: data()
|
||||||
|
{
|
||||||
|
data.init_default_checkpoints(lws::config::network);
|
||||||
|
|
||||||
|
std::string const* genesis_tx = nullptr;
|
||||||
|
std::uint32_t genesis_nonce = 0;
|
||||||
|
|
||||||
|
switch (lws::config::network)
|
||||||
|
{
|
||||||
|
case cryptonote::TESTNET:
|
||||||
|
genesis_tx = std::addressof(::config::testnet::GENESIS_TX);
|
||||||
|
genesis_nonce = ::config::testnet::GENESIS_NONCE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cryptonote::STAGENET:
|
||||||
|
genesis_tx = std::addressof(::config::stagenet::GENESIS_TX);
|
||||||
|
genesis_nonce = ::config::stagenet::GENESIS_NONCE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case cryptonote::MAINNET:
|
||||||
|
genesis_tx = std::addressof(::config::GENESIS_TX);
|
||||||
|
genesis_nonce = ::config::GENESIS_NONCE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MONERO_THROW(lws::error::bad_blockchain, "Unsupported net type");
|
||||||
|
}
|
||||||
|
cryptonote::block b;
|
||||||
|
cryptonote::generate_genesis_block(b, *genesis_tx, genesis_nonce);
|
||||||
|
crypto::hash block_hash = cryptonote::get_block_hash(b);
|
||||||
|
if (!data.add_checkpoint(0, epee::to_hex::string(epee::as_byte_span(block_hash))))
|
||||||
|
MONERO_THROW(lws::error::bad_blockchain, "Genesis tx and checkpoints file mismatch");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static const initializer instance;
|
||||||
|
return instance.data;
|
||||||
|
}
|
||||||
|
|
||||||
storage storage::open(const char* path, unsigned create_queue_max)
|
storage storage::open(const char* path, unsigned create_queue_max)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
@ -1454,6 +1453,12 @@ namespace db
|
||||||
|
|
||||||
if (*hash != chain.front())
|
if (*hash != chain.front())
|
||||||
{
|
{
|
||||||
|
if (current <= get_checkpoints().get_max_height())
|
||||||
|
{
|
||||||
|
MERROR("Attempting rollback past last checkpoint; invalid daemon chain response");
|
||||||
|
return {lws::error::bad_blockchain};
|
||||||
|
}
|
||||||
|
|
||||||
MONERO_CHECK(rollback_chain(this->db->tables, txn, *blocks_cur, db::block_id(current)));
|
MONERO_CHECK(rollback_chain(this->db->tables, txn, *blocks_cur, db::block_id(current)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "lmdb/key_stream.h"
|
#include "lmdb/key_stream.h"
|
||||||
#include "lmdb/value_stream.h"
|
#include "lmdb/value_stream.h"
|
||||||
|
|
||||||
|
namespace cryptonote { class checkpoints; }
|
||||||
namespace lws
|
namespace lws
|
||||||
{
|
{
|
||||||
namespace db
|
namespace db
|
||||||
|
@ -167,6 +168,10 @@ namespace db
|
||||||
{}
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
//! \return A single instance of compiled-in checkpoints for lws
|
||||||
|
static cryptonote::checkpoints const& get_checkpoints();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Open a light_wallet_server LDMB database.
|
Open a light_wallet_server LDMB database.
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "chain.test.h"
|
#include "chain.test.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include "checkpoints/checkpoints.h" // monero/src
|
||||||
#include "db/storage.test.h"
|
#include "db/storage.test.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
|
@ -112,6 +113,23 @@ LWS_CASE("db::storage::sync_chain")
|
||||||
lws_test::test_chain(lest_env, MONERO_UNWRAP(db.start_read()), last_block.id, fchain);
|
lws_test::test_chain(lest_env, MONERO_UNWRAP(db.start_read()), last_block.id, fchain);
|
||||||
EXPECT(get_account().scan_height == lws::db::block_id(std::uint64_t(last_block.id) + 1));
|
EXPECT(get_account().scan_height == lws::db::block_id(std::uint64_t(last_block.id) + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("Fork past checkpoint")
|
||||||
|
{
|
||||||
|
const auto& checkpoints = lws::db::storage::get_checkpoints();
|
||||||
|
const auto& points = checkpoints.get_points();
|
||||||
|
EXPECT(!points.empty());
|
||||||
|
|
||||||
|
auto point = points.begin();
|
||||||
|
const crypto::hash fchain[3] = {
|
||||||
|
point->second,
|
||||||
|
crypto::rand<crypto::hash>(),
|
||||||
|
crypto::rand<crypto::hash>()
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto sync_result = db.sync_chain(lws::db::block_id(point->first), fchain);
|
||||||
|
EXPECT(sync_result == lws::error::bad_blockchain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,8 +282,6 @@ namespace
|
||||||
|
|
||||||
LWS_CASE("lws::scanner::sync and lws::scanner::run")
|
LWS_CASE("lws::scanner::sync and lws::scanner::run")
|
||||||
{
|
{
|
||||||
mlog_set_log_level(4);
|
|
||||||
|
|
||||||
cryptonote::account_keys keys{};
|
cryptonote::account_keys keys{};
|
||||||
crypto::generate_keys(keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key);
|
crypto::generate_keys(keys.m_account_address.m_spend_public_key, keys.m_spend_secret_key);
|
||||||
crypto::generate_keys(keys.m_account_address.m_view_public_key, keys.m_view_secret_key);
|
crypto::generate_keys(keys.m_account_address.m_view_public_key, keys.m_view_secret_key);
|
||||||
|
|
Loading…
Reference in a new issue