Skip to content

Commit

Permalink
Merge pull request #863 from AntelopeIO/merge_replay_with_ref_blockch…
Browse files Browse the repository at this point in the history
…ain_to_main

[1.0.2 -> main] Test regression of syncing and replay with actual committed reference blockchain data
  • Loading branch information
linh2931 authored Oct 2, 2024
2 parents d355d43 + 79c7455 commit be25239
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 5 deletions.
8 changes: 8 additions & 0 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,13 @@ namespace eosio::testing {
return {cfg, gen};
}

static bool arguments_contains(const std::string& arg) {
auto argc = boost::unit_test::framework::master_test_suite().argc;
auto argv = boost::unit_test::framework::master_test_suite().argv;

return std::find(argv, argv + argc, arg) != (argv + argc);
}

// ideally, users of `tester` should not access the controller directly,
// so we provide APIs to access the chain head and fork_db head, and some
// other commonly used APIs.
Expand All @@ -529,6 +536,7 @@ namespace eosio::testing {
block_handle fork_db_head() const { return control->fork_db_head(); }

chain_id_type get_chain_id() const { return control->get_chain_id(); }
block_id_type last_irreversible_block_id() const { return control->last_irreversible_block_id(); }
uint32_t last_irreversible_block_num() const { return control->last_irreversible_block_num(); }
bool block_exists(const block_id_type& id) const { return control->block_exists(id); }

Expand Down
150 changes: 145 additions & 5 deletions unittests/savanna_misc_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include "savanna_cluster.hpp"
#include <savanna_cluster.hpp>
#include <test-data.hpp>

#include <fc/io/fstream.hpp> // for read_file_contents

using namespace eosio::chain;
using namespace eosio::testing;
Expand Down Expand Up @@ -591,18 +594,133 @@ BOOST_FIXTURE_TEST_CASE(validate_qc_requiring_finalizer_policies, savanna_cluste

} FC_LOG_AND_RETHROW()

static void save_blockchain_data(const std::filesystem::path& ref_blockchain_path,
const std::filesystem::path& blocks_path,
const block_id_type& id,
const std::string& snapshot) {
auto source_log_file = blocks_path / "blocks.log";
auto source_index_file = blocks_path / "blocks.index";

auto ref_log_file = ref_blockchain_path / "blocks.log";
auto ref_index_file = ref_blockchain_path / "blocks.index";
auto ref_id_file_name = ref_blockchain_path / "id";
auto ref_snapshot_file_name = ref_blockchain_path / "snapshot";

// save reference blocks log
std::filesystem::copy_file(source_log_file, ref_log_file, std::filesystem::copy_options::overwrite_existing);
std::filesystem::copy_file(source_index_file, ref_index_file, std::filesystem::copy_options::overwrite_existing);

// save reference block_id
fc::cfile ref_id_file;
ref_id_file.set_file_path(ref_id_file_name);
ref_id_file.open("wb");
ref_id_file.write(id.data(), id.data_size());
ref_id_file.close();

// save reference snapshot
fc::cfile snapshot_file;
snapshot_file.set_file_path(ref_snapshot_file_name);
snapshot_file.open("w");
snapshot_file.write(snapshot.data(), snapshot.size());
snapshot_file.close();
}

static block_id_type read_reference_id(const std::filesystem::path& ref_blockchain_path) {
auto ref_id_file_path = ref_blockchain_path / "id";
std::string content;
fc::read_file_contents(ref_id_file_path, content);

return block_id_type(content.data(), content.size());
}

static std::string read_reference_snapshot(const std::filesystem::path& ref_blockchain_path) {
auto ref_snapshot_file_path = ref_blockchain_path / "snapshot";
std::string content;
fc::read_file_contents(ref_snapshot_file_path, content);

return content;
}

// need to pass in temp_dir. otherwise it will be destroyed after replay_reference_blockchain returns
static std::unique_ptr<tester> replay_reference_blockchain(const std::filesystem::path& ref_blockchain_path,
const fc::temp_directory& temp_dir,
const block_log& blog) {
// replay the reference blockchain and make sure LIB id in the replayed
// chain matches reference LIB id
// --------------------------------------------------------------------
auto config = tester::default_config(temp_dir).first;

auto genesis = eosio::chain::block_log::extract_genesis_state(ref_blockchain_path);
BOOST_REQUIRE(genesis);

std::filesystem::create_directories(config.blocks_dir);

std::filesystem::copy(ref_blockchain_path / "blocks.log", config.blocks_dir / "blocks.log");
std::filesystem::copy(ref_blockchain_path / "blocks.index", config.blocks_dir / "blocks.index");

// do a full block invariants check
config.force_all_checks = true;

// replay the reference blockchain
std::unique_ptr<tester> replay_chain = std::make_unique<tester>(config, *genesis);

auto ref_lib_id = blog.head_id();
BOOST_REQUIRE_EQUAL(*ref_lib_id, replay_chain->last_irreversible_block_id());

return replay_chain;
}

static void sync_replayed_blockchain(const std::filesystem::path& ref_blockchain_path,
std::unique_ptr<tester>&& replay_chain,
const block_log& blog) {
tester sync_chain;
sync_chain.close(); // stop the chain

// remove state and blocks log so we can restart from snapshot
std::filesystem::remove_all(sync_chain.get_config().state_dir);
std::filesystem::remove_all(sync_chain.get_config().blocks_dir);

// restart from reference snapshot
sync_chain.open(buffered_snapshot_suite::get_reader(read_reference_snapshot(ref_blockchain_path)));

// sync with the replayed blockchain
while( sync_chain.fork_db_head().block_num() < replay_chain->fork_db_head().block_num() ) {
auto fb = replay_chain->fetch_block_by_number( sync_chain.fork_db_head().block_num()+1 );
sync_chain.push_block( fb );
}

// In syncing, use the head for checking as it advances further than LIB
auto head_block_num = sync_chain.head().block_num();
signed_block_ptr ref_block = blog.read_block_by_num(head_block_num);

BOOST_REQUIRE_EQUAL(ref_block->calculate_id(), sync_chain.head().id());
}

// ----------------------------------------------------------------------------------------------------
// For issue #694, we need to change the finality core of the `block_header_state`, but we want to
// ensure that this doesn't create a consensus incompatibility with Spring 1.0.0, so the blocks created
// with newer versions remain compatible (and linkable) by Spring 1.0.0.
// with newer versions remain compatible (and linkable) with blocks by Spring 1.0.0.
//
// This test adds a utility that saves reference blockchain data and checks for
// regression in compatibility of syncing and replaying the reference blockchain data.
//
// To save reference blockchain data in `unittests/test-data/consensus_blockchain`,
// run
// `unittests/unit_test -t savanna_misc_tests/verify_block_compatibitity -- --save-blockchain`
// ----------------------------------------------------------------------------------------------------
BOOST_FIXTURE_TEST_CASE(verify_spring_1_0_block_compatibitity, savanna_cluster::cluster_t) try {
BOOST_FIXTURE_TEST_CASE(verify_block_compatibitity, savanna_cluster::cluster_t) try {
using namespace savanna_cluster;
auto& A=_nodes[0];
const auto& tester_account = "tester"_n;
//_debug_mode = true;

bool save_blockchain = tester::arguments_contains("--save-blockchain");

std::string snapshot;
if (save_blockchain) { // take a snapshot at the beginning
snapshot = A.snapshot();
}

// update finalizer_policy with a new key for B
// --------------------------------------------
base_tester::finalizer_policy_input input;
Expand Down Expand Up @@ -681,10 +799,32 @@ BOOST_FIXTURE_TEST_CASE(verify_spring_1_0_block_compatibitity, savanna_cluster::
BOOST_REQUIRE_EQUAL(qc_s(qc(b9)), strong_qc(b8)); // b9 claims a strong QC on b8
BOOST_REQUIRE_EQUAL(A.lib_number, b6->block_num()); // b9 makes B6 final

// check that the block id of b9 match what we got with Spring 1.0.0
std::filesystem::path test_data_path { UNITTEST_TEST_DATA_DIR };
auto ref_blockchain_path = test_data_path / "consensus_blockchain";

// check that the block id of b9 match what we got before.
auto b9_id = b9->calculate_id();
BOOST_REQUIRE_EQUAL(b9_id, block_id_type{"00000013725f3d79bd4dd4091d0853d010a320f95240981711a942673ad87918"});

if (save_blockchain) {
save_blockchain_data(ref_blockchain_path, A.get_config().blocks_dir, b9_id, snapshot);
return;
}

// Do block id validation after we save blockchain data in case the id needs to be changed in future
BOOST_REQUIRE_EQUAL(b9_id, read_reference_id(ref_blockchain_path));

block_log blog(ref_blockchain_path);

// replay the reference blockchain and make sure LIB id in the replayed
// chain matches reference LIB id
// --------------------------------------------------------------------
fc::temp_directory temp_dir; // need to pass in temp_dir. otherwise it would be destroyed after replay_reference_blockchain returns
std::unique_ptr<tester> replay_chain = replay_reference_blockchain(ref_blockchain_path, temp_dir, blog);

// start another blockchain using reference snapshot, and sync with the blocks
// from the replayed blockchain
// ---------------------------------------------------------------------------
sync_replayed_blockchain(ref_blockchain_path, std::move(replay_chain), blog);
} FC_LOG_AND_RETHROW()

/* -----------------------------------------------------------------------------------------------------
Expand Down
Binary file not shown.
Binary file not shown.
Binary file added unittests/test-data/consensus_blockchain/id
Binary file not shown.
Binary file added unittests/test-data/consensus_blockchain/snapshot
Binary file not shown.

0 comments on commit be25239

Please sign in to comment.