9#define LOG_READ (std::ios::in | std::ios::binary)
10#define LOG_WRITE (std::ios::out | std::ios::binary | std::ios::app)
11#define LOG_RW ( std::ios::in | std::ios::out | std::ios::binary )
12#define LOG_WRITE_C "ab+"
16#define FC_FOPEN(p, m) fopen(p, m)
18#define FC_CAT(s1, s2) s1 ## s2
19#define FC_PREL(s) FC_CAT(L, s)
20#define FC_FOPEN(p, m) _wfopen(p, FC_PREL(m))
23namespace sysio {
namespace chain {
56 SYS_ASSERT(
prune_config->prune_blocks, block_log_exception,
"block log prune configuration requires at least one block");
57 SYS_ASSERT(__builtin_popcount(
prune_config->prune_threshold) == 1, block_log_exception,
"block log prune threshold must be power of 2");
83 if(last_data_pos - first_data_pos < prune_config->vacuum_on_close) {
84 ilog(
"Vacuuming pruned block log");
118 template <
typename ChainContext,
typename Lambda>
124 static bool is_pruned_log_and_mask_version(
uint32_t& version) {
126 version &= ~pruned_version_flag;
156 void update_buffer();
164 std::optional<uint32_t> _prune_block_limit;
165 uint64_t _current_position_in_file = 0;
166 uint64_t _end_of_buffer_position = _unset_position;
167 uint64_t _start_of_buffer_position = 0;
168 std::unique_ptr<char[]> _buffer_ptr;
169 std::string _block_file_name;
170 constexpr static int64_t _unset_position = -1;
171 constexpr static uint64_t _position_size =
sizeof(_current_position_in_file);
182 std::optional<boost::interprocess::file_mapping> _file;
183 std::optional<boost::interprocess::mapped_region> _mapped_file_region;
195 explicit fileptr_datastream( FILE* file,
const std::string& filename ) : _file(file), _filename(filename) {}
198 auto status = fseek(_file,
s, SEEK_CUR);
200 "Could not seek past ${bytes} bytes in Block log file at '${blocks_log}'. Returned status: ${status}",
201 (
"bytes",
s)(
"blocks_log", _filename)(
"status", status) );
205 size_t result = fread(
d, 1,
s, _file );
207 "only able to read ${act} bytes of the expected ${exp} bytes in file: ${file}",
208 (
"act",result)(
"exp",
s)(
"file", _filename) );
212 bool get(
unsigned char& c ) {
return get( *(
char*)&c ); }
218 const std::string _filename;
223 :my(new
detail::block_log_impl(prune_config)) {
228 my = std::move(other.my);
234 my->try_exit_vacuum();
240 void block_log::open(
const fc::path& data_dir) {
246 my->block_file.set_file_path( data_dir /
"blocks.log" );
247 my->index_file.set_file_path( data_dir /
"blocks.index" );
269 auto log_size =
fc::file_size( my->block_file.get_file_path() );
270 auto index_size =
fc::file_size( my->index_file.get_file_path() );
273 ilog(
"Log is nonempty");
274 my->block_file.seek( 0 );
277 const bool is_currently_pruned = detail::is_pruned_log_and_mask_version(my->version);
278 SYS_ASSERT( my->version > 0, block_log_exception,
"Block log was not setup properly" );
280 "Unsupported version of block log. Block log version is ${version} while code supports version(s) [${min},${max}]",
284 my->genesis_written_to_block_log =
true;
285 if (my->version > 1){
286 my->first_block_num = 0;
288 SYS_ASSERT(my->first_block_num > 0, block_log_exception,
"Block log is malformed, first recorded block number is 0 but must be greater than or equal to 1");
290 my->first_block_num = 1;
292 my->index_first_block_num = my->first_block_num;
296 my->head_id = my->head->calculate_id();
301 my->block_file.seek_end(0);
302 if(is_currently_pruned && my->head) {
304 my->block_file.skip(-
sizeof(
uint32_t));
307 my->block_file.skip(-
sizeof(
uint32_t));
311 ilog(
"Index is nonempty");
313 my->block_file.skip(-
sizeof(
uint64_t));
314 my->block_file.read((
char*)&block_pos,
sizeof(block_pos));
317 my->index_file.seek_end(-
sizeof(
uint64_t));
318 my->index_file.read((
char*)&index_pos,
sizeof(index_pos));
320 if (block_pos < index_pos) {
321 ilog(
"block_pos < index_pos, close and reopen index_file");
323 }
else if (block_pos > index_pos) {
324 ilog(
"Index is incomplete");
328 ilog(
"Index is empty");
332 if(!is_currently_pruned && my->prune_config) {
337 my->block_file.seek(0);
341 my->block_file.seek_end(0);
347 else if(is_currently_pruned && !my->prune_config) {
350 }
else if (index_size) {
351 ilog(
"Index is nonempty, remove and recreate it");
376 block_log_append_fail,
377 "Append to index file occuring at wrong position.",
405 if(head_num - first_block_num < prune_config->prune_blocks)
408 const uint32_t prune_to_num = head_num - prune_config->prune_blocks + 1;
414 const auto max_header_size = std::max(max_header_size_v1, max_header_size_v23);
416 block_file.punch_hole(max_header_size, get_block_pos(prune_to_num));
418 first_block_num = prune_to_num;
443 SYS_ASSERT(is_pruned_log_and_mask_version(old_version), block_log_exception,
"Trying to vacuumed a non-pruned block log");
448 auto ds = block_file.create_datastream();
454 if(first_block_num == 1) {
455 SYS_ASSERT(old_first_block_num == 1, block_log_exception,
"expected an old first blocknum of 1");
474 return block_file.tellp();
480 size_t copy_to_pos = convert_existing_header_to_vacuumed();
483 prune_config.reset();
493 size_t copy_from_pos = get_block_pos(first_block_num);
494 block_file.seek_end(-
sizeof(
uint32_t));
495 size_t copy_sz = block_file.tellp() - copy_from_pos;
498 const size_t offset_bytes = copy_from_pos - copy_to_pos;
499 const size_t offset_blocks = first_block_num - index_first_block_num;
501 std::vector<char> buff;
502 buff.resize(4*1024*1024);
504 auto tick = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
506 const size_t copy_this_round = std::min(buff.size(), copy_sz);
507 block_file.seek(copy_from_pos);
508 block_file.read(buff.data(), copy_this_round);
509 block_file.punch_hole(copy_to_pos, copy_from_pos+copy_this_round);
510 block_file.seek(copy_to_pos);
511 block_file.write(buff.data(), copy_this_round);
513 copy_from_pos += copy_this_round;
514 copy_to_pos += copy_this_round;
515 copy_sz -= copy_this_round;
517 const auto tock = std::chrono::time_point_cast<std::chrono::seconds>(std::chrono::system_clock::now());
518 if(tick < tock - std::chrono::seconds(5)) {
519 ilog(
"Vacuuming pruned block log, ${b} bytes remaining", (
"b", copy_sz));
528 boost::interprocess::mapped_region index_mapped(index_file, boost::interprocess::read_write);
531 for(
uint32_t new_block_num = 0; new_block_num < num_blocks_in_log; ++new_block_num) {
532 const uint64_t new_pos = index_ptr[new_block_num + offset_blocks] - offset_bytes;
533 index_ptr[new_block_num] = new_pos;
535 if(new_block_num + 1 != num_blocks_in_log)
536 block_file.seek(index_ptr[new_block_num + offset_blocks + 1] - offset_bytes -
sizeof(
uint64_t));
538 block_file.seek_end(-
sizeof(
uint64_t));
539 block_file.write((
char*)&new_pos,
sizeof(new_pos));
544 index_first_block_num = first_block_num;
557 index_first_block_num = first_block_num = first_bnum;
559 block_file.seek_end(0);
560 block_file.write((
char*)&version,
sizeof(version));
561 block_file.write((
char*)&first_block_num,
sizeof(first_block_num));
564 genesis_written_to_block_log =
true;
568 block_file.write((
char*)&totem,
sizeof(totem));
579 auto pos = block_file.tellp();
587 block_file.seek( 0 );
588 block_file.write( (
char*)&version,
sizeof(version) );
589 block_file.seek( pos );
594 my->reset(gs, first_block, 1);
598 SYS_ASSERT( first_block_num > 1, block_log_exception,
599 "Block log version ${ver} needs to be created with a genesis state if starting from block number 1." );
605 block_file.write(data.data(), data.size());
609 block_file << chain_id;
613 my->check_open_files();
615 my->block_file.seek(pos);
617 auto ds = my->block_file.create_datastream();
623 my->check_open_files();
625 my->block_file.seek(pos);
626 auto ds = my->block_file.create_datastream();
633 uint64_t pos = get_block_pos(block_num);
636 SYS_ASSERT(b->block_num() == block_num, reversible_blocks_exception,
637 "Wrong block was read from block log.", (
"returned", b->block_num())(
"expected", block_num));
645 uint64_t pos = get_block_pos(block_num);
648 read_block_header(bh, pos);
649 SYS_ASSERT(bh.block_num() == block_num, reversible_blocks_exception,
650 "Wrong block header was read from block log.", (
"returned", bh.block_num())(
"expected", block_num));
651 return bh.calculate_id();
661 index_file.seek(
sizeof(
uint64_t) * (block_num - index_first_block_num));
663 index_file.read((
char*)&pos,
sizeof(pos));
668 return my->get_block_pos(block_num);
672 my->check_open_files();
677 my->block_file.seek_end(0);
678 if (my->block_file.tellp() <=
sizeof(pos))
683 my->block_file.seek(0);
686 const bool is_currently_pruned = detail::is_pruned_log_and_mask_version(current_version);
688 my->block_file.seek_end(0);
689 if(is_currently_pruned)
690 my->block_file.skip(-
sizeof(
uint32_t));
691 my->block_file.skip(-
sizeof(pos));
694 return read_block(pos);
707 return my->first_block_num;
710 void block_log::construct_index() {
711 ilog(
"Reconstructing Block Log Index...");
721 block_log::construct_index(my->block_file.get_file_path(), my->index_file.get_file_path());
726 void block_log::construct_index(
const fc::path& block_file_name,
const fc::path& index_file_name) {
729 ilog(
"Will read existing blocks.log file ${file}", (
"file", block_file_name.
generic_string()));
730 ilog(
"Will write new blocks.index file ${file}", (
"file", index_file_name.
generic_string()));
733 const uint32_t num_blocks = block_log_iter.
open(block_file_name);
735 ilog(
"block log version= ${version}", (
"version", block_log_iter.
version()));
737 if (num_blocks == 0) {
741 ilog(
"first block= ${first} last block= ${last}",
746 while ((position = block_log_iter.
previous()) != npos) {
747 index.write(position);
752 ilog(
"Recovering Block Log...");
754 "Block log not found in '${blocks_dir}'", (
"blocks_dir", data_dir) );
759 if( blocks_dir.filename().generic_string() ==
"." ) {
760 blocks_dir = blocks_dir.parent_path();
762 auto backup_dir = blocks_dir.parent_path();
763 auto blocks_dir_name = blocks_dir.filename();
764 SYS_ASSERT( blocks_dir_name.generic_string() !=
".", block_log_exception,
"Invalid path to blocks directory" );
765 backup_dir = backup_dir / blocks_dir_name.generic_string().append(
"-").append( now );
768 "Cannot move existing blocks directory to already existing directory '${new_blocks_dir}'",
769 (
"new_blocks_dir", backup_dir) );
772 ilog(
"Moved existing blocks directory to backup location: '${new_blocks_dir}'", (
"new_blocks_dir", backup_dir) );
774 if (strlen(reversible_block_dir_name) &&
fc::is_directory(blocks_dir/reversible_block_dir_name)) {
775 fc::rename(blocks_dir/ reversible_block_dir_name, backup_dir/ reversible_block_dir_name);
779 auto block_log_path = blocks_dir /
"blocks.log";
781 ilog(
"Reconstructing '${new_block_log}' from backed up block log", (
"new_block_log", block_log_path) );
783 std::fstream old_block_stream;
784 std::fstream new_block_stream;
786 old_block_stream.open( (backup_dir /
"blocks.log").generic_string().c_str(),
LOG_READ );
787 new_block_stream.open( block_log_path.generic_string().c_str(),
LOG_WRITE );
789 old_block_stream.seekg( 0, std::ios::end );
790 uint64_t end_pos = old_block_stream.tellg();
791 old_block_stream.seekg( 0 );
794 old_block_stream.read( (
char*)&version,
sizeof(version) );
795 SYS_ASSERT( version > 0, block_log_exception,
"Block log was not setup properly" );
796 SYS_ASSERT( is_supported_version(version), block_log_unsupported_version,
797 "Unsupported version of block log. Block log version is ${version} while code supports version(s) [${min},${max}]",
800 new_block_stream.write( (
char*)&version,
sizeof(version) );
804 old_block_stream.read ( (
char*)&first_block_num,
sizeof(first_block_num) );
809 SYS_ASSERT( first_block_num == 1, block_log_exception,
810 "Block log ${file} must contain a genesis state and start at block number 1. This block log "
811 "starts at block number ${first_block_num}.",
812 (
"file", (backup_dir /
"blocks.log").generic_string())(
"first_block_num", first_block_num));
814 new_block_stream.write( (
char*)&first_block_num,
sizeof(first_block_num) );
817 if (contains_genesis_state(version, first_block_num)) {
822 new_block_stream.write( data.data(), data.size() );
824 else if (contains_chain_id(version, first_block_num)) {
826 old_block_stream >> chain_id;
828 new_block_stream << chain_id;
832 "Block log ${file} is not supported. version: ${ver} and first_block_num: ${fbn} does not contain "
833 "a genesis_state nor a chain_id.",
834 (
"file", (backup_dir /
"blocks.log").generic_string())(
"ver", version)(
"fbn", first_block_num));
838 auto expected_totem = npos;
839 std::decay_t<
decltype(npos)> actual_totem;
840 old_block_stream.read ( (
char*)&actual_totem,
sizeof(actual_totem) );
842 SYS_ASSERT(actual_totem == expected_totem, block_log_exception,
843 "Expected separator between block log header and blocks was not found( expected: ${e}, actual: ${a} )",
844 (
"e",
fc::to_hex((
char*)&expected_totem,
sizeof(expected_totem) ))(
"a",
fc::to_hex((
char*)&actual_totem,
sizeof(actual_totem) )));
846 new_block_stream.write( (
char*)&actual_totem,
sizeof(actual_totem) );
849 std::exception_ptr except_ptr;
851 std::optional<signed_block> bad_block;
856 uint64_t pos = old_block_stream.tellg();
857 while( pos < end_pos ) {
863 except_ptr = std::current_exception();
864 incomplete_block_data.resize( end_pos - pos );
865 old_block_stream.read( incomplete_block_data.data(), incomplete_block_data.size() );
871 elog(
"Block ${num} (${id}) skips blocks. Previous block in block log is block ${prev_num} (${previous})",
876 elog(
"Block ${num} (${id}) does not link back to previous block. "
877 "Expected previous: ${expected}. Actual previous: ${actual}.",
882 uint64_t tmp_pos = std::numeric_limits<uint64_t>::max();
883 if( (
static_cast<uint64_t>(old_block_stream.tellg()) +
sizeof(pos)) <= end_pos ) {
884 old_block_stream.read(
reinterpret_cast<char*
>(&tmp_pos),
sizeof(tmp_pos) );
886 if( pos != tmp_pos ) {
887 bad_block.emplace(std::move(tmp));
892 new_block_stream.write( data.data(), data.size() );
893 new_block_stream.write(
reinterpret_cast<char*
>(&pos),
sizeof(pos) );
895 if(block_num % 1000 == 0)
896 ilog(
"Recovered block ${num}", (
"num", block_num) );
897 pos = new_block_stream.tellp();
898 if( block_num == truncate_at_block )
903 ilog(
"Recovered only up to block number ${num}. Last block in block log was not properly committed:\n${last_block}",
904 (
"num", block_num)(
"last_block", *bad_block) );
905 }
else if( except_ptr ) {
906 std::string error_msg;
909 std::rethrow_exception(except_ptr);
911 error_msg = e.
what();
912 }
catch(
const std::exception& e ) {
913 error_msg = e.
what();
915 error_msg =
"unrecognized exception";
918 ilog(
"Recovered only up to block number ${num}. "
919 "The block ${next_num} could not be deserialized from the block log due to error:\n${error_msg}",
920 (
"num", block_num)(
"next_num", block_num+1)(
"error_msg", error_msg) );
922 auto tail_path = blocks_dir / std::string(
"blocks-bad-tail-").append( now ).append(
".log");
923 if( !
fc::exists(tail_path) && incomplete_block_data.size() > 0 ) {
924 std::fstream tail_stream;
925 tail_stream.open( tail_path.generic_string().c_str(),
LOG_WRITE );
926 tail_stream.write( incomplete_block_data.data(), incomplete_block_data.size() );
928 ilog(
"Data at tail end of block log which should contain the (incomplete) serialization of block ${num} "
929 "has been written out to '${tail_path}'.",
930 (
"num", block_num+1)(
"tail_path", tail_path) );
932 }
else if( block_num == truncate_at_block && pos < end_pos ) {
933 ilog(
"Stopped recovery of block log early at specified block number: ${stop}.", (
"stop", truncate_at_block) );
935 ilog(
"Existing block log was undamaged. Recovered all irreversible blocks up to block number ${num}.", (
"num", block_num) );
941 template <
typename ChainContext,
typename Lambda>
944 "Block log not found in '${blocks_dir}'", (
"blocks_dir", data_dir) );
946 std::fstream block_stream;
947 block_stream.open( (data_dir /
"blocks.log").generic_string().c_str(),
LOG_READ );
950 block_stream.read( (
char*)&version,
sizeof(version) );
951 is_pruned_log_and_mask_version(version);
953 "Unsupported version of block log. Block log version is ${version} while code supports version(s) [${min},${max}]",
958 block_stream.read ( (
char*)&first_block_num,
sizeof(first_block_num) );
961 return lambda(block_stream, version, first_block_num);
966 if (contains_genesis_state(version, first_block_num)) {
973 return std::optional<genesis_state>();
980 if (contains_genesis_state(version, first_block_num)) {
983 return gs.compute_chain_id();
985 SYS_ASSERT( contains_chain_id(version, first_block_num), block_log_exception,
986 "Block log error! version: ${version} with first_block_num: ${num} does not contain a "
987 "chain id or genesis state, so the chain id cannot be determined.",
988 (
"version", version)(
"num", first_block_num) );
1000 log_file.
open(
"rb");
1006 return detail::is_pruned_log_and_mask_version(version);
1010 : _file(nullptr, &fclose)
1011 , _buffer_ptr(
std::make_unique<char[]>(_buf_len)) {
1018 _file.reset(
FC_FOPEN(_block_file_name.c_str(),
"r"));
1019 SYS_ASSERT( _file, block_log_exception,
"Could not open Block log file at '${blocks_log}'", (
"blocks_log", _block_file_name) );
1020 _end_of_buffer_position = _unset_position;
1024 auto size = fread((
char*)&_version,
sizeof(_version), 1, _file.get());
1025 SYS_ASSERT( size == 1, block_log_exception,
"Block log file at '${blocks_log}' could not be read.", (
"file", _block_file_name) );
1026 const bool is_prune_log = is_pruned_log_and_mask_version(_version);
1028 "block log version ${v} is not supported", (
"v", _version));
1029 if (_version == 1) {
1030 _first_block_num = 1;
1033 size = fread((
char*)&_first_block_num,
sizeof(_first_block_num), 1, _file.get());
1034 SYS_ASSERT( size == 1, block_log_exception,
"Block log file at '${blocks_log}' not formatted consistently with version ${v}.", (
"file", _block_file_name)(
"v", _version) );
1037 auto status = fseek(_file.get(), 0, SEEK_END);
1038 SYS_ASSERT( status == 0, block_log_exception,
"Could not open Block log file at '${blocks_log}'. Returned status: ${status}", (
"blocks_log", _block_file_name)(
"status", status) );
1040 auto eof_position_in_file = ftell(_file.get());
1041 SYS_ASSERT( eof_position_in_file > 0, block_log_exception,
"Block log file at '${blocks_log}' could not be read.", (
"blocks_log", _block_file_name) );
1044 fseek(_file.get(), -
sizeof(
uint32_t), SEEK_CUR);
1046 size = fread((
char*)&prune_count,
sizeof(prune_count), 1, _file.get());
1047 SYS_ASSERT( size == 1, block_log_exception,
"Block log file at '${blocks_log}' not formatted consistently with pruned version ${v}.", (
"file", _block_file_name)(
"v", _version) );
1048 _prune_block_limit = prune_count;
1049 eof_position_in_file -=
sizeof(prune_count);
1052 _current_position_in_file = eof_position_in_file - _position_size;
1057 char*
buf = _buffer_ptr.get();
1058 const uint32_t index_of_pos = _current_position_in_file - _start_of_buffer_position;
1066 if (block_pos >= _start_of_buffer_position) {
1067 const uint32_t index_of_block = block_pos - _start_of_buffer_position;
1072 auto status = fseek(_file.get(), blknum_offset_pos, SEEK_SET);
1073 SYS_ASSERT( status == 0, block_log_exception,
"Could not seek in '${blocks_log}' to position: ${pos}. Returned status: ${status}", (
"blocks_log", _block_file_name)(
"pos", blknum_offset_pos)(
"status", status) );
1074 auto size = fread((
void*)&bnum,
sizeof(bnum), 1, _file.get());
1075 SYS_ASSERT( size == 1, block_log_exception,
"Could not read in '${blocks_log}' at position: ${pos}", (
"blocks_log", _block_file_name)(
"pos", blknum_offset_pos) );
1078 _blocks_expected = _last_block_num - _first_block_num + 1;
1079 return _blocks_expected;
1084 block_log_exception,
1085 "Block log file at '${blocks_log}' first block already returned by former call to previous(), it is no longer valid to call this function.", (
"blocks_log", _block_file_name) );
1087 if ((_version == 1 && _blocks_found == _blocks_expected) || (_prune_block_limit && _blocks_found == *_prune_block_limit)) {
1089 return _current_position_in_file;
1092 if (_start_of_buffer_position > _current_position_in_file) {
1096 char*
buf = _buffer_ptr.get();
1097 auto offset = _current_position_in_file - _start_of_buffer_position;
1102 _current_position_in_file = block_location_in_file;
1103 SYS_ASSERT( _blocks_found != _blocks_expected,
1104 block_log_exception,
1105 "Block log file at '${blocks_log}' formatting indicated last block: ${last_block_num}, first block: ${first_block_num}, but found ${num} blocks",
1106 (
"blocks_log", _block_file_name)(
"last_block_num", _last_block_num)(
"first_block_num", _first_block_num)(
"num", _blocks_found) );
1109 const uint64_t previous_position_in_file = _current_position_in_file;
1110 _current_position_in_file = block_location_in_file - _position_size;
1111 SYS_ASSERT( _current_position_in_file < previous_position_in_file,
1112 block_log_exception,
1113 "Block log file at '${blocks_log}' formatting is incorrect, indicates position later location in file: ${pos}, which was retrieved at: ${orig_pos}.",
1114 (
"blocks_log", _block_file_name)(
"pos", _current_position_in_file)(
"orig_pos", previous_position_in_file) );
1117 return block_location_in_file;
1120 void detail::reverse_iterator::update_buffer() {
1124 _end_of_buffer_position = _current_position_in_file + _position_size;
1125 if (_end_of_buffer_position < _buf_len) {
1126 _start_of_buffer_position = 0;
1129 _start_of_buffer_position = _end_of_buffer_position - _buf_len;
1132 auto status = fseek(_file.get(), _start_of_buffer_position, SEEK_SET);
1133 SYS_ASSERT( status == 0, block_log_exception,
"Could not seek in '${blocks_log}' to position: ${pos}. Returned status: ${status}", (
"blocks_log", _block_file_name)(
"pos", _start_of_buffer_position)(
"status", status) );
1134 char*
buf = _buffer_ptr.get();
1135 auto size = fread((
void*)
buf, (_end_of_buffer_position - _start_of_buffer_position), 1, _file.get());
1136 SYS_ASSERT( size == 1, block_log_exception,
"blocks.log read fails" );
1140 : _blocks_remaining(blocks_expected) {
1141 const size_t file_sz = blocks_expected*
sizeof(
uint64_t);
1149 _file.emplace(block_index_name.
string().c_str(), boost::interprocess::read_write);
1150 _mapped_file_region.emplace(*_file, boost::interprocess::read_write);
1154 SYS_ASSERT( _blocks_remaining, block_log_exception,
"No more blocks were expected for the block log index" );
1156 char* base = (
char*)_mapped_file_region->get_address();
1157 base += --_blocks_remaining*
sizeof(
uint64_t);
1158 memcpy(base, &pos,
sizeof(pos));
1160 if ((_blocks_remaining & 0xfffff) == 0)
1161 ilog(
"blocks remaining to index: ${blocks_left} position in log file: ${pos}", (
"blocks_left", _blocks_remaining)(
"pos",pos));
1165 return version <= 2 || first_block_num == 1;
1169 return version >= 3 && first_block_num > 1;
1173 return std::clamp(version, min_supported_version, max_supported_version) == version;
1177 template <
typename T>
1178 T read_buffer(
const char*
buf) {
1184 template <
typename T>
1185 void write_buffer(
char* des,
const T* src) {
1191 using namespace std;
1192 SYS_ASSERT(
block_dir != temp_dir, block_log_exception,
"block_dir and temp_dir need to be different directories" );
1193 ilog(
"In directory ${dir} will trim all blocks before block ${n} from blocks.log and blocks.index.",
1194 (
"dir",
block_dir.generic_string())(
"n", truncate_at_block));
1196 if (truncate_at_block <= original_block_log.
first_block) {
1197 ilog(
"There are no blocks before block ${n} so do nothing.", (
"n", truncate_at_block));
1200 if (truncate_at_block > original_block_log.
last_block) {
1201 ilog(
"All blocks are before block ${n} so do nothing (trim front would delete entire blocks.log).", (
"n", truncate_at_block));
1207 fc::path new_block_filename = temp_dir /
"blocks.log";
1209 ilog(
"Removing old blocks.out file");
1216 new_block_file.
close();
1220 "Code was written to support version 3 format, need to update this code for latest format." );
1222 new_block_file.
seek(0);
1223 new_block_file.
write((
char*)&version,
sizeof(version));
1224 new_block_file.
write((
char*)&truncate_at_block,
sizeof(truncate_at_block));
1226 new_block_file << original_block_log.
chain_id;
1230 new_block_file.
write((
char*)&totem,
sizeof(totem));
1232 const auto new_block_file_first_block_pos = new_block_file.
tellp();
1237 char*
buf = buffer.get();
1240 const uint64_t original_file_block_pos = original_block_log.
block_pos(truncate_at_block);
1241 const uint64_t pos_delta = original_file_block_pos - new_block_file_first_block_pos;
1242 auto status = fseek(original_block_log.
blk_in, 0, SEEK_END);
1243 SYS_ASSERT( status == 0, block_log_exception,
"blocks.log seek failed" );
1246 const uint64_t to_write = ftell(original_block_log.
blk_in) - original_file_block_pos;
1247 const auto pos_size =
sizeof(
uint64_t);
1250 uint64_t original_pos = ftell(original_block_log.
blk_in) - pos_size;
1252 const auto num_blocks = original_block_log.
last_block - truncate_at_block + 1;
1254 fc::path new_index_filename = temp_dir /
"blocks.index";
1259 for(
uint64_t to_write_remaining = to_write; to_write_remaining > 0; to_write_remaining -= write_size) {
1260 read_size = to_write_remaining;
1266 const auto start_of_blk_buffer_pos = original_file_block_pos + to_write_remaining - read_size;
1267 status = fseek(original_block_log.
blk_in, start_of_blk_buffer_pos, SEEK_SET);
1268 const auto num_read = fread(
buf, read_size, 1, original_block_log.
blk_in);
1269 SYS_ASSERT( num_read == 1, block_log_exception,
"blocks.log read failed" );
1273 write_size = read_size;
1274 while(original_pos >= start_of_blk_buffer_pos) {
1275 const auto buffer_index = original_pos - start_of_blk_buffer_pos;
1276 uint64_t pos_content = read_buffer<uint64_t>(
buf + buffer_index);
1278 if ( (pos_content - start_of_blk_buffer_pos) > 0 && (pos_content - start_of_blk_buffer_pos) < pos_size ) {
1280 write_size = read_size - (pos_content - start_of_blk_buffer_pos);
1282 const auto start_of_this_block = pos_content;
1283 pos_content = start_of_this_block - pos_delta;
1284 write_buffer<uint64_t>(
buf + buffer_index, &pos_content);
1285 index.write(pos_content);
1286 original_pos = start_of_this_block - pos_size;
1288 new_block_file.
seek(new_block_file_first_block_pos + to_write_remaining - write_size);
1289 uint64_t offset = read_size - write_size;
1290 new_block_file.
write(
buf+offset, write_size);
1293 fclose(original_block_log.
blk_in);
1294 original_block_log.
blk_in =
nullptr;
1295 new_block_file.
flush();
1296 new_block_file.
close();
1298 fc::path old_log = temp_dir /
"old.log";
1301 fc::path old_ind = temp_dir /
"old.index";
1312 using namespace std;
1313 block_file_name =
block_dir /
"blocks.log";
1314 index_file_name =
block_dir /
"blocks.index";
1315 blk_in =
FC_FOPEN(block_file_name.generic_string().c_str(),
"rb");
1316 SYS_ASSERT( blk_in !=
nullptr, block_log_not_found,
"cannot read file ${file}", (
"file",block_file_name.string()) );
1317 ind_in =
FC_FOPEN(index_file_name.generic_string().c_str(),
"rb");
1318 SYS_ASSERT( ind_in !=
nullptr, block_log_not_found,
"cannot read file ${file}", (
"file",index_file_name.string()) );
1319 auto size = fread((
void*)&version,
sizeof(version), 1, blk_in);
1320 SYS_ASSERT( size == 1, block_log_unsupported_version,
"invalid format for file ${file}", (
"file",block_file_name.string()));
1321 ilog(
"block log version= ${version}",(
"version",version));
1322 bool is_pruned = detail::is_pruned_log_and_mask_version(version);
1323 SYS_ASSERT( !is_pruned, block_log_unsupported_version,
"Block log is currently in pruned format, it must be vacuumed before doing this operation");
1331 chain_id = gs.compute_chain_id();
1334 size = fread((
void *) &first_block,
sizeof(first_block), 1, blk_in);
1335 SYS_ASSERT(size == 1, block_log_exception,
"invalid format for file ${file}",
1336 (
"file", block_file_name.string()));
1340 chain_id = gs.compute_chain_id();
1347 "Block log ${file} is not supported. version: ${ver} and first_block: ${first_block} does not contain "
1348 "a genesis_state nor a chain_id.",
1349 (
"file", block_file_name.string())(
"ver", version)(
"first_block", first_block));
1354 size = fread ( (
char*)&actual_totem,
sizeof(actual_totem), 1, blk_in);
1357 "Expected to read ${size} bytes, but did not read any bytes", (
"size",
sizeof(actual_totem)));
1358 SYS_ASSERT(actual_totem == expected_totem, block_log_exception,
1359 "Expected separator between block log header and blocks was not found( expected: ${e}, actual: ${a} )",
1360 (
"e",
fc::to_hex((
char*)&expected_totem,
sizeof(expected_totem) ))(
"a",
fc::to_hex((
char*)&actual_totem,
sizeof(actual_totem) )));
1363 const uint64_t start_of_blocks = ftell(blk_in);
1365 const auto status = fseek(ind_in, 0, SEEK_END);
1366 SYS_ASSERT( status == 0, block_log_exception,
"cannot seek to ${file} end", (
"file", index_file_name.string()) );
1367 const uint64_t file_end = ftell(ind_in);
1368 last_block = first_block + file_end/
sizeof(
uint64_t) - 1;
1370 first_block_pos = block_pos(first_block);
1371 SYS_ASSERT(start_of_blocks == first_block_pos, block_log_exception,
1372 "Block log ${file} was determined to have its first block at ${determined}, but the block index "
1373 "indicates the first block is at ${index}",
1374 (
"file", block_file_name.string())(
"determined", start_of_blocks)(
"index",first_block_pos));
1375 ilog(
"first block= ${first}",(
"first",first_block));
1376 ilog(
"last block= ${last}",(
"last",last_block));
1380 if (blk_in !=
nullptr)
1382 if (ind_in !=
nullptr)
1387 using namespace std;
1388 SYS_ASSERT( first_block <= n, block_log_exception,
1389 "cannot seek in ${file} to block number ${b}, block number ${first} is the first block",
1390 (
"file", index_file_name.string())(
"b",n)(
"first",first_block) );
1391 SYS_ASSERT( n <= last_block, block_log_exception,
1392 "cannot seek in ${file} to block number ${b}, block number ${last} is the last block",
1393 (
"file", index_file_name.string())(
"b",n)(
"last",last_block) );
1394 return sizeof(
uint64_t) * (n - first_block);
1398 using namespace std;
1400 if (n == last_block + 1) {
1401 return ftell(blk_in);
1403 const uint64_t index_pos = block_index(n);
1404 auto status = fseek(ind_in, index_pos, SEEK_SET);
1405 SYS_ASSERT( status == 0, block_log_exception,
"cannot seek to ${file} ${pos} from beginning of file for block ${b}", (
"file", index_file_name.string())(
"pos", index_pos)(
"b",n) );
1406 const uint64_t pos = ftell(ind_in);
1407 SYS_ASSERT( pos == index_pos, block_log_exception,
"cannot seek to ${file} entry for block ${b}", (
"file", index_file_name.string())(
"b",n) );
1409 auto size = fread((
void*)&block_n_pos,
sizeof(block_n_pos), 1, ind_in);
1410 SYS_ASSERT( size == 1, block_log_exception,
"cannot read ${file} entry for block ${b}", (
"file", index_file_name.string())(
"b",n) );
1413 const auto calc_blknum_pos = block_n_pos + blknum_offset;
1414 status = fseek(blk_in, calc_blknum_pos, SEEK_SET);
1415 SYS_ASSERT( status == 0, block_log_exception,
"cannot seek to ${file} ${pos} from beginning of file", (
"file", block_file_name.string())(
"pos", calc_blknum_pos) );
1416 const uint64_t block_offset_pos = ftell(blk_in);
1417 SYS_ASSERT( block_offset_pos == calc_blknum_pos, block_log_exception,
"cannot seek to ${file} ${pos} from beginning of file", (
"file", block_file_name.string())(
"pos", calc_blknum_pos) );
1419 size = fread((
void*)&prior_blknum,
sizeof(prior_blknum), 1, blk_in);
1420 SYS_ASSERT( size == 1, block_log_exception,
"cannot read prior block");
1423 "At position ${pos} in ${file} expected to find ${exp_bnum} but found ${act_bnum}",
1424 (
"pos",block_offset_pos)(
"file", block_file_name.string())(
"exp_bnum",n)(
"act_bnum",bnum) );
void block_log_set_buff_len(uint64_t len)
sysio::chain
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
void open(const char *mode)
void set_file_path(fc::path file_path)
void write(const char *d, size_t n)
Used to generate a useful error report when an exception is thrown.
const char * what() const noexcept override
provides information about where and when a log message was generated.
aggregates a message along with the context and associated meta-information.
static logger get(const fc::string &name=DEFAULT_LOGGER)
bool is_enabled(log_level e) const
An order-preserving dictionary of variants.
wraps boost::filesystem::path to provide platform independent path manipulation.
std::string string() const
std::string generic_string() const
static bool trim_blocklog_front(const fc::path &block_dir, const fc::path &temp_dir, uint32_t truncate_at_block)
static const uint64_t npos
static std::optional< genesis_state > extract_genesis_state(const fc::path &data_dir)
static fc::path repair_log(const fc::path &data_dir, uint32_t truncate_at_block=0, const char *reversible_block_dir_name="")
signed_block_ptr read_head() const
static bool contains_chain_id(uint32_t version, uint32_t first_block_num)
const block_id_type & head_id() const
static bool is_supported_version(uint32_t version)
block_log(const fc::path &data_dir, std::optional< block_log_prune_config > prune_config)
static bool is_pruned_log(const fc::path &data_dir)
block_id_type read_block_id_by_num(uint32_t block_num) const
signed_block_ptr read_block(uint64_t file_pos) const
static const uint32_t min_supported_version
signed_block_ptr read_block_by_num(uint32_t block_num) const
static void construct_index(const fc::path &block_file_name, const fc::path &index_file_name)
void read_block_header(block_header &bh, uint64_t file_pos) const
uint32_t first_block_num() const
void append(const signed_block_ptr &b)
static chain_id_type extract_chain_id(const fc::path &data_dir)
static bool contains_genesis_state(uint32_t version, uint32_t first_block_num)
uint64_t get_block_pos(uint32_t block_num) const
static const uint32_t max_supported_version
const signed_block_ptr & head() const
void reset(const genesis_state &gs, const signed_block_ptr &genesis_block)
bool genesis_written_to_block_log
uint64_t get_block_pos(uint32_t block_num)
void prune(const fc::log_level &loglevel)
std::optional< block_log_prune_config > prune_config
block_log_impl(std::optional< block_log_prune_config > prune_conf)
size_t convert_existing_header_to_vacuumed()
void write(const genesis_state &gs)
static std::optional< ChainContext > extract_chain_context(const fc::path &data_dir, Lambda &&lambda)
void reset(const T &t, const signed_block_ptr &genesis_block, uint32_t first_block_num)
void append(const signed_block_ptr &b)
uint32_t index_first_block_num
fileptr_datastream(FILE *file, const std::string &filename)
bool read(char *d, size_t s)
bool get(unsigned char &c)
index_writer(const fc::path &block_index_name, uint32_t blocks_expected)
uint32_t open(const fc::path &block_file_name)
uint32_t first_block_num() const
#define FC_LOG_AND_RETHROW()
void close(T *e, websocketpp::connection_hdl hdl)
void unpack(Stream &s, std::deque< T > &value)
void pack(Stream &s, const std::deque< T > &value)
size_t pack_size(const T &v)
void rename(const path &from, const path &to)
bool remove(const path &p)
bool exists(const path &p)
void remove_all(const path &p)
bool is_regular_file(const path &p)
void create_directories(const path &p)
fc::string to_hex(const char *d, uint32_t s)
uint64_t file_size(const path &p)
bool is_directory(const path &p)
void resize_file(const path &file, size_t s)
path canonical(const path &p)
uint32_t endian_reverse_u32(uint32_t x)
constexpr uint64_t buffer_location_to_file_location(uint32_t buffer_location)
constexpr uint32_t pruned_version_flag
constexpr uint32_t file_location_to_buffer_location(uint32_t file_location)
std::unique_ptr< FILE, decltype(&fclose)> unique_file
std::shared_ptr< signed_block > signed_block_ptr
#define T(meth, val, expected)
unsigned __int64 uint64_t
uint64_t block_pos(uint32_t n)
trim_data(fc::path block_dir)
uint64_t block_index(uint32_t n) const
static constexpr int blknum_offset
memcpy((char *) pInfo->slotDescription, s, l)