Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corrupted block generator for fisherman testing #1506

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ endif()
if (TON_ARCH AND NOT MSVC)
CHECK_CXX_COMPILER_FLAG( "-march=${TON_ARCH}" COMPILER_OPT_ARCH_SUPPORTED )
if (TON_ARCH STREQUAL "apple-m1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${TON_ARCH}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=${TON_ARCH}")
elseif(COMPILER_OPT_ARCH_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}")
elseif(NOT TON_ARCH STREQUAL "native")
Expand Down Expand Up @@ -555,6 +555,18 @@ target_link_libraries(test-http PRIVATE tonhttp)
add_executable(test-emulator test/test-td-main.cpp emulator/test/emulator-tests.cpp)
target_link_libraries(test-emulator PRIVATE emulator)

add_executable(test-fisherman
test/fisherman/tests.cpp
test/fisherman/block_manipulator/header_corrupter.cpp
test/fisherman/block_manipulator/factory.cpp
test/fisherman/block_reader.cpp
test/fisherman/utils.cpp
)
target_link_libraries(test-fisherman PRIVATE validator ton_validator validator ton_db)

add_executable(print-all-shard-states test/print-all-shard-states.cpp)
target_link_libraries(print-all-shard-states PRIVATE validator ton_db)

get_directory_property(HAS_PARENT PARENT_DIRECTORY)
if (HAS_PARENT)
set(ALL_TEST_SOURCE
Expand Down
1 change: 1 addition & 0 deletions tddb/td/db/RocksDb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ RocksDb RocksDb::clone() const {
return RocksDb{db_, options_};
}

// TODO: Add support for opening the database in read-only mode.
Result<RocksDb> RocksDb::open(std::string path, RocksDbOptions options) {
rocksdb::OptimisticTransactionDB *db;
{
Expand Down
13 changes: 13 additions & 0 deletions test/fisherman/block_manipulator/base.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include "crypto/block/block-auto.h"

namespace test::fisherman {

class BaseManipulator {
public:
virtual void modify(block::gen::Block::Record &block) = 0;
virtual ~BaseManipulator() = default;
};

} // namespace test::fisherman
28 changes: 28 additions & 0 deletions test/fisherman/block_manipulator/factory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "factory.hpp"

#include "header_corrupter.hpp"

namespace test::fisherman {

auto ManipulatorFactory::create(td::JsonValue jv) -> std::shared_ptr<BaseManipulator> {
auto res = createImpl(std::move(jv));
if (res.is_error()) {
throw std::runtime_error("Error while creating manipulator: " + res.error().message().str());
}
return res.move_as_ok();
}

auto ManipulatorFactory::createImpl(td::JsonValue jv) -> td::Result<std::shared_ptr<BaseManipulator>> {
CHECK(jv.type() == td::JsonValue::Type::Object);

auto &obj = jv.get_object();
TRY_RESULT(type, td::get_json_object_string_field(obj, "type", false));
TRY_RESULT(json_config, td::get_json_object_field(obj, "config", td::JsonValue::Type::Object, false));

if (type == "HeaderCorrupter") {
return std::make_shared<HeaderCorrupter>(HeaderCorrupter::Config::fromJson(std::move(json_config)));
}
return td::Status::Error(400, PSLICE() << "Unknown manipulator type: " << type);
}

} // namespace test::fisherman
16 changes: 16 additions & 0 deletions test/fisherman/block_manipulator/factory.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "base.hpp"
#include "td/utils/JsonBuilder.h"

namespace test::fisherman {

class ManipulatorFactory {
public:
auto create(td::JsonValue jv) -> std::shared_ptr<BaseManipulator>;

private:
auto createImpl(td::JsonValue jv) -> td::Result<std::shared_ptr<BaseManipulator>>;
};

} // namespace test::fisherman
21 changes: 21 additions & 0 deletions test/fisherman/block_manipulator/header_corrupter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "header_corrupter.hpp"

namespace test::fisherman {

auto HeaderCorrupter::Config::fromJson(td::JsonValue jv) -> Config {
return Config{};
}

HeaderCorrupter::HeaderCorrupter(Config config) : config_(std::move(config)) {
}

void HeaderCorrupter::modify(block::gen::Block::Record &block) {
block::gen::BlockInfo::Record info_rec;
bool ok = block::gen::BlockInfo().cell_unpack(block.info, info_rec);
CHECK(ok);
info_rec.after_merge = true;
info_rec.after_split = true;
block::gen::BlockInfo().cell_pack(block.info, info_rec);
}

} // namespace test::fisherman
23 changes: 23 additions & 0 deletions test/fisherman/block_manipulator/header_corrupter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include "base.hpp"
#include "td/utils/JsonBuilder.h"

namespace test::fisherman {

class HeaderCorrupter : public BaseManipulator {
public:
struct Config {
// TODO: add corruption field and method

static auto fromJson(td::JsonValue jv) -> Config;
};

explicit HeaderCorrupter(Config config);
void modify(block::gen::Block::Record &block) final;

private:
Config config_;
};

} // namespace test::fisherman
95 changes: 95 additions & 0 deletions test/fisherman/block_manipulator/ideas.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
- Некорректная работа твм, т.е. результат исполнения записанный в блоке не совпадает с "локальным" исполнением, в частности
-- некорректная проверка подписи (принята подпись, которая не была бы принята в реальной сети)
-- некорректная комиссия транзакции (одна из)
-- некорректные исходящие сообщения
-- некорректный апдейт стейта
- Непоследовательное исполнение сообщений (в блок включено сообщение с большим lt/hash чем сообщения оставшиеся в очереди)
- Несоблюдение лимитов блока (по газу, размеру, lt)
- Некорректные value-flow (уничтожаются/минтятся деньги из воздуха)

Подпись
1. Неверная подпись валидатора в заголовке.
2. Задвоенная подпись (или отсутствующая подпись при наличии).
В заголовке блока записано, что "N валидаторов подписались",
но по факту часть подписей повторяется, либо не совпадает с публичным ключом.
3. Подделка подписи внешнего сообщения. Внешнее сообщение якобы подписано кошельком пользователя, но фактически подпись неверна.

Газ / комиссии
1. Некорректная сумма комиссии в транзакции. В том числе и обнуление комиссии.
2. Неверное распределение комиссии между валидаторами, коллатором и пр.
Сумма собранной комиссии не совпадает с тем, сколько реально поступило на счета валидаторов/коллатора.

Исходящие сообщения
1. Неверная форма исходящего сообщения ???
Внутреннее сообщение, сформированное контрактом, имеет некорректную структуру.
2. Сообщение отправляется на несуществующий адрес (или адрес, не соответствующий данному шарду).
3. Неверная сумма, переданная в исходящем сообщении.
Контракт "списывает" у себя X TON, отправляет их в сообщении, но на балансе контракта фактически было меньше X.
Контракт "списывает" у себя X TON, а в сообщении указано Y TON.

Некорректный апдейт стейта
???

Некорректное исполнение сообщений
1. Включено сообщение с большим lt/hash, в то время как сообщения с меньшим lt не были исполнены.
2. Использование "просроченных" входящих сообщений (по timeout).

Несоблюдение лимитов блока
1. Превышение лимита по газу.
2. Превышение лимита по размеру блока.
Фактический размер сериализованного блока больше, чем что-то ???
3. Превышение лимита по количеству транзакций.
4. Превышение лимита по lt / изменению lt.

Некорректные value-flow
1. Баланс контракта увеличился на 1000 TON, хотя никаких транзакций перевода и минтинга нет.
2. Баланс контракта уменьшился на 1000 TON, хотя никаких транзакций перевода и сжигания нет.
3. Некоректоное изменение баланса при отправке сообщения.

Соответствие цепочки блоков шарда мастерчейну
1. Мастерчейн содержит более новый блок по seqno.
2. Шард содержит незарегистированную длинную цепочку.
3. В мастерчейне содердится другой последний зарегистрированный блок, нежели в шарде.

Ошибки в заголовке и структуре самого блока
1. Неверная ссылка на предыдущий блок.
2. Несогласованность с мастерчейном
Шард-блок утверждает, что он ссылается на определённые данные в мастерчейне, которые не совпадают с реальной историей.
3. Искажение timestamp.
Ставится время из далекого "будущего" или "прошлого".
4. Невалидный шард (например битово некорректный).
5. Блок является подшардом мастерчейна. workchain == -1, shard != ShardIdFull.
6. start_lt_ >= end_lt_.

Несоответствие глобальным настройкам сети ???
1. Сеть поменяла лимиты по gas, а блок генерируется со старыми лимитами.

Ошибки в медшардовых сообщениях
1. Неверный маршрут сообщения.
Указывается, что сообщение идёт в шард A, но на самом деле адрес принадлежит шару B.

Нарушение правил "консенсуса"
1. Блок подписан валидаторами, которые не являются валидаторами этого шарда.
2. Недостаточное число подписей.

Дополнительные идеи порчи блока на основе reject_query
- Хэш корневой ячейки блока не совпадает с тем, что заявлено в block_id.
- Внутренние структуры коллатора, например TopBlockDescrSet, Merkle proof и т.д. некорректны.
- Отметили "key block" в шардчейне, что запрещено.
- Поле info.after_merge/info.before_split/info.after_split true, но мы в мастерчейне.
- Поля info.after_merge и info.after_split оба установлены в true.
- Пустой префикс шардирования, но блок объявлен как "после сплита". info.after_split == true, но shard_pfx_len == 0.
- В BlockInfo есть поле vert_seqno_incr (!= 0).
- Указан другой публичный ключ создателя, нежели тот, что мы ожидаем.
- Для мастерчейн-блока ожидаем extra.custom->size_refs() != 0, но оказалось 0. И наоборот.
- Противоречие end_lt в header и gen_lt в ShardState. В заголовке стоит end_lt_, а в самом стейте gen_lt, и они не совпадают.
- Аналогично противоречение timestamp. info.gen_utime (во финальном стейте) != now_ (из заголовка).
- Внутри состояния (ShardState) поле seq_no и shard_id указывает на другой блок, нежели этот.
- Некорректный мерж ???
- Несуществующий workchain_id, который отсутствует в глобальном списке.
- Workchain есть, но active == false. Или wc_info_->basic == false.
Или wc_info_->enabled_since && wc_info_->enabled_since > config_->utime.
- При store_out_msg_queue_size_ == true ожидаем, что в стейте прописан out_msg_queue_size, а его нет.
Или размер не совпадает с найденным ns_.out_msg_queue_size_.value().
- is_key_block_ == true, а "важные" параметры конфигурации не менялись.
- При повторном вычислении транзакции выяснилось, что финальный хэш аккаунта не совпадает с тем, что заявлен.
50 changes: 50 additions & 0 deletions test/fisherman/block_reader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "block_reader.hpp"

namespace test::fisherman {

BlockDataLoader::BlockDataLoader(const std::string &db_path) : scheduler_({1}) {
auto opts = ton::validator::ValidatorManagerOptions::create(ton::BlockIdExt{}, ton::BlockIdExt{});
scheduler_.run_in_context([&] {
root_db_actor_ = td::actor::create_actor<ton::validator::RootDb>(
"RootDbActor", td::actor::ActorId<ton::validator::ValidatorManager>(), db_path, opts);
});
}

BlockDataLoader::~BlockDataLoader() {
scheduler_.stop();
}

td::Result<td::Ref<ton::validator::BlockData>> BlockDataLoader::load_block_data(const ton::BlockIdExt &block_id) {
std::atomic<bool> done{false};
td::Result<td::Ref<ton::validator::BlockData>> block_data_result;

scheduler_.run_in_context([&] {
auto handle_promise = td::PromiseCreator::lambda([&](td::Result<ton::validator::ConstBlockHandle> handle_res) {
if (handle_res.is_error()) {
block_data_result = td::Result<td::Ref<ton::validator::BlockData>>(handle_res.move_as_error());
done = true;
return;
}
auto handle = handle_res.move_as_ok();

auto data_promise = td::PromiseCreator::lambda([&](td::Result<td::Ref<ton::validator::BlockData>> data_res) {
block_data_result = std::move(data_res);
done = true;
});

td::actor::send_closure(root_db_actor_, &ton::validator::RootDb::get_block_data, handle, std::move(data_promise));
});

td::actor::send_closure(root_db_actor_, &ton::validator::RootDb::get_block_by_seqno,
ton::AccountIdPrefixFull{block_id.id.workchain, block_id.id.shard}, block_id.id.seqno,
std::move(handle_promise));
});

while (!done) {
scheduler_.run(1);
}

return block_data_result;
}

} // namespace test::fisherman
25 changes: 25 additions & 0 deletions test/fisherman/block_reader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <atomic>
#include <memory>
#include <string>

#include "validator/db/rootdb.hpp"
#include "td/actor/actor.h"

namespace test::fisherman {

// TODO: Verify that the database does not get corrupted when reading while the validator is running
class BlockDataLoader {
public:
explicit BlockDataLoader(const std::string &db_path);
~BlockDataLoader();

td::Result<td::Ref<ton::validator::BlockData>> load_block_data(const ton::BlockIdExt &block_id);

private:
td::actor::Scheduler scheduler_;
td::actor::ActorOwn<ton::validator::RootDb> root_db_actor_;
};

} // namespace test::fisherman
11 changes: 11 additions & 0 deletions test/fisherman/configs/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"block_id": {
"workchain_id": -1,
"shard_id": "8000000000000000",
"seqno": 27492934
},
"manipulation": {
"type": "HeaderCorrupter",
"config": {}
}
}
Loading