Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
sysio::chain::block_log Class Reference

#include <block_log.hpp>

Public Member Functions

 block_log (const fc::path &data_dir, std::optional< block_log_prune_config > prune_config)
 
 block_log (block_log &&other)
 
 ~block_log ()
 
void append (const signed_block_ptr &b)
 
void flush ()
 
void reset (const genesis_state &gs, const signed_block_ptr &genesis_block)
 
void reset (const chain_id_type &chain_id, uint32_t first_block_num)
 
signed_block_ptr read_block (uint64_t file_pos) const
 
void read_block_header (block_header &bh, uint64_t file_pos) const
 
signed_block_ptr read_block_by_num (uint32_t block_num) const
 
block_id_type read_block_id_by_num (uint32_t block_num) const
 
signed_block_ptr read_block_by_id (const block_id_type &id) const
 
uint64_t get_block_pos (uint32_t block_num) const
 
signed_block_ptr read_head () const
 
const signed_block_ptrhead () const
 
const block_id_typehead_id () const
 
uint32_t first_block_num () const
 

Static Public Member Functions

static fc::path repair_log (const fc::path &data_dir, uint32_t truncate_at_block=0, const char *reversible_block_dir_name="")
 
static std::optional< genesis_stateextract_genesis_state (const fc::path &data_dir)
 
static chain_id_type extract_chain_id (const fc::path &data_dir)
 
static void construct_index (const fc::path &block_file_name, const fc::path &index_file_name)
 
static bool contains_genesis_state (uint32_t version, uint32_t first_block_num)
 
static bool contains_chain_id (uint32_t version, uint32_t first_block_num)
 
static bool is_supported_version (uint32_t version)
 
static bool is_pruned_log (const fc::path &data_dir)
 
static bool trim_blocklog_front (const fc::path &block_dir, const fc::path &temp_dir, uint32_t truncate_at_block)
 

Static Public Attributes

static const uint64_t npos = std::numeric_limits<uint64_t>::max()
 
static const uint32_t min_supported_version = 1
 
static const uint32_t max_supported_version = 3
 

Detailed Description

Definition at line 45 of file block_log.hpp.

Constructor & Destructor Documentation

◆ block_log() [1/2]

sysio::chain::block_log::block_log ( const fc::path & data_dir,
std::optional< block_log_prune_config > prune_config )

Definition at line 222 of file block_log.cpp.

223 :my(new detail::block_log_impl(prune_config)) {
224 open(data_dir);
225 }

◆ block_log() [2/2]

sysio::chain::block_log::block_log ( block_log && other)

Definition at line 227 of file block_log.cpp.

227 {
228 my = std::move(other.my);
229 }

◆ ~block_log()

sysio::chain::block_log::~block_log ( )

Definition at line 231 of file block_log.cpp.

231 {
232 if (my) {
233 flush();
234 my->try_exit_vacuum();
235 my->close();
236 my.reset();
237 }
238 }
Here is the call graph for this function:

Member Function Documentation

◆ append()

void sysio::chain::block_log::append ( const signed_block_ptr & b)

Definition at line 358 of file block_log.cpp.

358 {
359 my->append(b);
360 }
Here is the caller graph for this function:

◆ construct_index()

void sysio::chain::block_log::construct_index ( const fc::path & block_file_name,
const fc::path & index_file_name )
static

Definition at line 726 of file block_log.cpp.

726 {
727 detail::reverse_iterator block_log_iter;
728
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()));
731
732 //for a pruned log, this will still return blocks in the count that have been removed. that's okay and desirable
733 const uint32_t num_blocks = block_log_iter.open(block_file_name);
734
735 ilog("block log version= ${version}", ("version", block_log_iter.version()));
736
737 if (num_blocks == 0) {
738 return;
739 }
740
741 ilog("first block= ${first} last block= ${last}",
742 ("first", block_log_iter.first_block_num())("last", (block_log_iter.first_block_num() + num_blocks)));
743
744 detail::index_writer index(index_file_name, num_blocks);
745 uint64_t position;
746 while ((position = block_log_iter.previous()) != npos) {
747 index.write(position);
748 }
749 }
std::string generic_string() const
static const uint64_t npos
Definition block_log.hpp:73
#define ilog(FORMAT,...)
Definition logger.hpp:118
unsigned int uint32_t
Definition stdint.h:126
unsigned __int64 uint64_t
Definition stdint.h:136
Here is the call graph for this function:

◆ contains_chain_id()

bool sysio::chain::block_log::contains_chain_id ( uint32_t version,
uint32_t first_block_num )
static

Definition at line 1168 of file block_log.cpp.

1168 {
1169 return version >= 3 && first_block_num > 1;
1170 }
uint32_t first_block_num() const
Here is the caller graph for this function:

◆ contains_genesis_state()

bool sysio::chain::block_log::contains_genesis_state ( uint32_t version,
uint32_t first_block_num )
static

Definition at line 1164 of file block_log.cpp.

1164 {
1165 return version <= 2 || first_block_num == 1;
1166 }
Here is the caller graph for this function:

◆ extract_chain_id()

chain_id_type sysio::chain::block_log::extract_chain_id ( const fc::path & data_dir)
static

Definition at line 977 of file block_log.cpp.

977 {
978 return *(detail::block_log_impl::extract_chain_context<chain_id_type>(data_dir, [](std::fstream& block_stream, uint32_t version, uint32_t first_block_num ) -> std::optional<chain_id_type> {
979 // supported versions either contain a genesis state, or else the chain id only
981 genesis_state gs;
982 fc::raw::unpack(block_stream, gs);
983 return gs.compute_chain_id();
984 }
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) );
989 chain_id_type chain_id;
990 fc::raw::unpack(block_stream, chain_id);
991 return chain_id;
992 }));
993 }
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
static bool contains_chain_id(uint32_t version, uint32_t first_block_num)
static bool contains_genesis_state(uint32_t version, uint32_t first_block_num)
static std::optional< ChainContext > extract_chain_context(const fc::path &data_dir, Lambda &&lambda)
static const Segment gs(Segment::gs)
void unpack(Stream &s, std::deque< T > &value)
Definition raw.hpp:540
Here is the call graph for this function:
Here is the caller graph for this function:

◆ extract_genesis_state()

std::optional< genesis_state > sysio::chain::block_log::extract_genesis_state ( const fc::path & data_dir)
static

Definition at line 964 of file block_log.cpp.

964 {
965 return detail::block_log_impl::extract_chain_context<genesis_state>(data_dir, [](std::fstream& block_stream, uint32_t version, uint32_t first_block_num ) -> std::optional<genesis_state> {
967 genesis_state gs;
968 fc::raw::unpack(block_stream, gs);
969 return gs;
970 }
971
972 // current versions only have a genesis state if they start with block number 1
973 return std::optional<genesis_state>();
974 });
975 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ first_block_num()

uint32_t sysio::chain::block_log::first_block_num ( ) const

Definition at line 706 of file block_log.cpp.

706 {
707 return my->first_block_num;
708 }
Here is the caller graph for this function:

◆ flush()

void sysio::chain::block_log::flush ( )

Definition at line 426 of file block_log.cpp.

426 {
427 my->flush();
428 }
Here is the caller graph for this function:

◆ get_block_pos()

uint64_t sysio::chain::block_log::get_block_pos ( uint32_t block_num) const

Return offset of block in file, or block_log::npos if it does not exist.

Definition at line 667 of file block_log.cpp.

667 {
668 return my->get_block_pos(block_num);
669 }

◆ head()

const signed_block_ptr & sysio::chain::block_log::head ( ) const

Definition at line 698 of file block_log.cpp.

698 {
699 return my->head;
700 }
Here is the caller graph for this function:

◆ head_id()

const block_id_type & sysio::chain::block_log::head_id ( ) const

Definition at line 702 of file block_log.cpp.

702 {
703 return my->head_id;
704 }
Here is the caller graph for this function:

◆ is_pruned_log()

bool sysio::chain::block_log::is_pruned_log ( const fc::path & data_dir)
static

Definition at line 995 of file block_log.cpp.

995 {
996 uint32_t version = 0;
997 try {
998 fc::cfile log_file;
999 log_file.set_file_path(data_dir / "blocks.log");
1000 log_file.open("rb");
1001 fc::raw::unpack(log_file, version);
1002 }
1003 catch(...) {
1004 return false;
1005 }
1006 return detail::is_pruned_log_and_mask_version(version);
1007 }
void open(const char *mode)
Definition cfile.hpp:65
void set_file_path(fc::path file_path)
Definition cfile.hpp:37
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_supported_version()

bool sysio::chain::block_log::is_supported_version ( uint32_t version)
static

Definition at line 1172 of file block_log.cpp.

1172 {
1173 return std::clamp(version, min_supported_version, max_supported_version) == version;
1174 }
static const uint32_t min_supported_version
Definition block_log.hpp:75
static const uint32_t max_supported_version
Definition block_log.hpp:76
Here is the caller graph for this function:

◆ read_block()

signed_block_ptr sysio::chain::block_log::read_block ( uint64_t file_pos) const

Definition at line 612 of file block_log.cpp.

612 {
613 my->check_open_files();
614
615 my->block_file.seek(pos);
616 signed_block_ptr result = std::make_shared<signed_block>();
617 auto ds = my->block_file.create_datastream();
618 fc::raw::unpack(ds, *result);
619 return result;
620 }
static const Segment ds(Segment::ds)
std::shared_ptr< signed_block > signed_block_ptr
Definition block.hpp:105
Here is the call graph for this function:

◆ read_block_by_id()

signed_block_ptr sysio::chain::block_log::read_block_by_id ( const block_id_type & id) const
inline

Definition at line 60 of file block_log.hpp.

60 {
62 }
signed_block_ptr read_block_by_num(uint32_t block_num) const
static uint32_t num_from_id(const block_id_type &id)
Here is the call graph for this function:

◆ read_block_by_num()

signed_block_ptr sysio::chain::block_log::read_block_by_num ( uint32_t block_num) const

Definition at line 630 of file block_log.cpp.

630 {
631 try {
633 uint64_t pos = get_block_pos(block_num);
634 if (pos != npos) {
635 b = read_block(pos);
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));
638 }
639 return b;
641 }
signed_block_ptr read_block(uint64_t file_pos) const
uint64_t get_block_pos(uint32_t block_num) const
#define FC_LOG_AND_RETHROW()
Here is the caller graph for this function:

◆ read_block_header()

void sysio::chain::block_log::read_block_header ( block_header & bh,
uint64_t file_pos ) const

Definition at line 622 of file block_log.cpp.

622 {
623 my->check_open_files();
624
625 my->block_file.seek(pos);
626 auto ds = my->block_file.create_datastream();
627 fc::raw::unpack(ds, bh);
628 }
Here is the call graph for this function:

◆ read_block_id_by_num()

block_id_type sysio::chain::block_log::read_block_id_by_num ( uint32_t block_num) const

Definition at line 643 of file block_log.cpp.

643 {
644 try {
645 uint64_t pos = get_block_pos(block_num);
646 if (pos != npos) {
647 block_header bh;
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();
652 }
653 return {};
655 }
void read_block_header(block_header &bh, uint64_t file_pos) const
static const Reg8 bh(Operand::BH)

◆ read_head()

signed_block_ptr sysio::chain::block_log::read_head ( ) const

Definition at line 671 of file block_log.cpp.

671 {
672 my->check_open_files();
673
674 uint64_t pos;
675
676 // Check that the file is not empty
677 my->block_file.seek_end(0);
678 if (my->block_file.tellp() <= sizeof(pos))
679 return {};
680
681 //figure out if this is a pruned log or not. we can't just look at the configuration since
682 // read_head() is called early on, and this isn't hot enough to warrant a member bool to track it
683 my->block_file.seek(0);
684 uint32_t current_version;
685 fc::raw::unpack(my->block_file, current_version);
686 const bool is_currently_pruned = detail::is_pruned_log_and_mask_version(current_version);
687
688 my->block_file.seek_end(0);
689 if(is_currently_pruned)
690 my->block_file.skip(-sizeof(uint32_t)); //skip the trailer containing block count
691 my->block_file.skip(-sizeof(pos));
692 fc::raw::unpack(my->block_file, pos);
693 if (pos != npos)
694 return read_block(pos);
695 return {};
696 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ repair_log()

fc::path sysio::chain::block_log::repair_log ( const fc::path & data_dir,
uint32_t truncate_at_block = 0,
const char * reversible_block_dir_name = "" )
static

Definition at line 751 of file block_log.cpp.

751 {
752 ilog("Recovering Block Log...");
753 SYS_ASSERT( fc::is_directory(data_dir) && fc::is_regular_file(data_dir / "blocks.log"), block_log_not_found,
754 "Block log not found in '${blocks_dir}'", ("blocks_dir", data_dir) );
755
756 auto now = fc::time_point::now();
757
758 auto blocks_dir = fc::canonical( data_dir );
759 if( blocks_dir.filename().generic_string() == "." ) {
760 blocks_dir = blocks_dir.parent_path();
761 }
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 );
766
767 SYS_ASSERT( !fc::exists(backup_dir), block_log_backup_dir_exist,
768 "Cannot move existing blocks directory to already existing directory '${new_blocks_dir}'",
769 ("new_blocks_dir", backup_dir) );
770
771 fc::rename( blocks_dir, backup_dir );
772 ilog( "Moved existing blocks directory to backup location: '${new_blocks_dir}'", ("new_blocks_dir", backup_dir) );
773
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);
776 }
777
778 fc::create_directories(blocks_dir);
779 auto block_log_path = blocks_dir / "blocks.log";
780
781 ilog( "Reconstructing '${new_block_log}' from backed up block log", ("new_block_log", block_log_path) );
782
783 std::fstream old_block_stream;
784 std::fstream new_block_stream;
785
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 );
788
789 old_block_stream.seekg( 0, std::ios::end );
790 uint64_t end_pos = old_block_stream.tellg();
791 old_block_stream.seekg( 0 );
792
793 uint32_t version = 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}]",
798 ("version", version)("min", block_log::min_supported_version)("max", block_log::max_supported_version) );
799
800 new_block_stream.write( (char*)&version, sizeof(version) );
801
803 if (version != 1) {
804 old_block_stream.read ( (char*)&first_block_num, sizeof(first_block_num) );
805
806 // this assert is only here since repair_log is only used for --hard-replay-blockchain, which removes any
807 // existing state, if another API needs to use it, this can be removed and the check for the first block's
808 // previous block id will need to accommodate this.
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));
813
814 new_block_stream.write( (char*)&first_block_num, sizeof(first_block_num) );
815 }
816
818 genesis_state gs;
819 fc::raw::unpack(old_block_stream, gs);
820
821 auto data = fc::raw::pack( gs );
822 new_block_stream.write( data.data(), data.size() );
823 }
824 else if (contains_chain_id(version, first_block_num)) {
825 chain_id_type chain_id;
826 old_block_stream >> chain_id;
827
828 new_block_stream << chain_id;
829 }
830 else {
831 SYS_THROW( block_log_exception,
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));
835 }
836
837 if (version != 1) {
838 auto expected_totem = npos;
839 std::decay_t<decltype(npos)> actual_totem;
840 old_block_stream.read ( (char*)&actual_totem, sizeof(actual_totem) );
841
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) )));
845
846 new_block_stream.write( (char*)&actual_totem, sizeof(actual_totem) );
847 }
848
849 std::exception_ptr except_ptr;
850 vector<char> incomplete_block_data;
851 std::optional<signed_block> bad_block;
852 uint32_t block_num = 0;
853
855
856 uint64_t pos = old_block_stream.tellg();
857 while( pos < end_pos ) {
858 signed_block tmp;
859
860 try {
861 fc::raw::unpack(old_block_stream, tmp);
862 } catch( ... ) {
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() );
866 break;
867 }
868
869 auto id = tmp.calculate_id();
870 if( block_header::num_from_id(previous) + 1 != block_header::num_from_id(id) ) {
871 elog( "Block ${num} (${id}) skips blocks. Previous block in block log is block ${prev_num} (${previous})",
872 ("num", block_header::num_from_id(id))("id", id)
873 ("prev_num", block_header::num_from_id(previous))("previous", previous) );
874 }
875 if( previous != tmp.previous ) {
876 elog( "Block ${num} (${id}) does not link back to previous block. "
877 "Expected previous: ${expected}. Actual previous: ${actual}.",
878 ("num", block_header::num_from_id(id))("id", id)("expected", previous)("actual", tmp.previous) );
879 }
880 previous = id;
881
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) );
885 }
886 if( pos != tmp_pos ) {
887 bad_block.emplace(std::move(tmp));
888 break;
889 }
890
891 auto data = fc::raw::pack(tmp);
892 new_block_stream.write( data.data(), data.size() );
893 new_block_stream.write( reinterpret_cast<char*>(&pos), sizeof(pos) );
894 block_num = tmp.block_num();
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 )
899 break;
900 }
901
902 if( bad_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;
907
908 try {
909 std::rethrow_exception(except_ptr);
910 } catch( const fc::exception& e ) {
911 error_msg = e.what();
912 } catch( const std::exception& e ) {
913 error_msg = e.what();
914 } catch( ... ) {
915 error_msg = "unrecognized exception";
916 }
917
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) );
921
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() );
927
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) );
931 }
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) );
934 } else {
935 ilog( "Existing block log was undamaged. Recovered all irreversible blocks up to block number ${num}.", ("num", block_num) );
936 }
937
938 return backup_dir;
939 }
#define LOG_READ
Definition block_log.cpp:9
#define LOG_WRITE
Definition block_log.cpp:10
#define SYS_THROW(exc_type, FORMAT,...)
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
const char * what() const noexcept override
static time_point now()
Definition time.cpp:14
static bool is_supported_version(uint32_t version)
uint64_t id
Definition code_cache.cpp:0
#define elog(FORMAT,...)
Definition logger.hpp:130
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
void rename(const path &from, const path &to)
bool exists(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)
Definition hex.cpp:17
bool is_directory(const path &p)
path canonical(const path &p)
fc::sha256 block_id_type
Definition types.hpp:231
uint32_t previous(octet_iterator &it, octet_iterator pass_start)
Deprecated in versions that include "prior".
Definition checked.h:179
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset() [1/2]

void sysio::chain::block_log::reset ( const chain_id_type & chain_id,
uint32_t first_block_num )

Definition at line 597 of file block_log.cpp.

597 {
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." );
600 my->reset(chain_id, signed_block_ptr(), first_block_num);
601 }

◆ reset() [2/2]

void sysio::chain::block_log::reset ( const genesis_state & gs,
const signed_block_ptr & genesis_block )

Definition at line 593 of file block_log.cpp.

593 {
594 my->reset(gs, first_block, 1);
595 }
Here is the caller graph for this function:

◆ trim_blocklog_front()

bool sysio::chain::block_log::trim_blocklog_front ( const fc::path & block_dir,
const fc::path & temp_dir,
uint32_t truncate_at_block )
static

Definition at line 1190 of file block_log.cpp.

1190 {
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));
1195 trim_data original_block_log(block_dir);
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));
1198 return false;
1199 }
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));
1202 return false;
1203 }
1204
1205 // ****** create the new block log file and write out the header for the file
1206 fc::create_directories(temp_dir);
1207 fc::path new_block_filename = temp_dir / "blocks.log";
1208 if (fc::remove(new_block_filename)) {
1209 ilog("Removing old blocks.out file");
1210 }
1211 fc::cfile new_block_file;
1212 new_block_file.set_file_path(new_block_filename);
1213 // need to open as append since the file doesn't already exist, then reopen without append to allow writing the
1214 // file in any order
1215 new_block_file.open( LOG_WRITE_C );
1216 new_block_file.close();
1217 new_block_file.open( LOG_RW_C );
1218
1219 static_assert( block_log::max_supported_version == 3,
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));
1225
1226 new_block_file << original_block_log.chain_id;
1227
1228 // append a totem to indicate the division between blocks and header
1229 auto totem = block_log::npos;
1230 new_block_file.write((char*)&totem, sizeof(totem));
1231
1232 const auto new_block_file_first_block_pos = new_block_file.tellp();
1233 // ****** end of new block log header
1234
1235 // copy over remainder of block log to new block log
1236 auto buffer = make_unique<char[]>(detail::reverse_iterator::_buf_len);
1237 char* buf = buffer.get();
1238
1239 // offset bytes to shift from old blocklog position to new blocklog position
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" );
1244
1245 // all blocks to copy to the new blocklog
1246 const uint64_t to_write = ftell(original_block_log.blk_in) - original_file_block_pos;
1247 const auto pos_size = sizeof(uint64_t);
1248
1249 // start with the last block's position stored at the end of the block
1250 uint64_t original_pos = ftell(original_block_log.blk_in) - pos_size;
1251
1252 const auto num_blocks = original_block_log.last_block - truncate_at_block + 1;
1253
1254 fc::path new_index_filename = temp_dir / "blocks.index";
1255 detail::index_writer index(new_index_filename, num_blocks);
1256
1257 uint64_t read_size = 0;
1258 uint64_t write_size = 0;
1259 for(uint64_t to_write_remaining = to_write; to_write_remaining > 0; to_write_remaining -= write_size) {
1260 read_size = to_write_remaining;
1261 if (read_size > detail::reverse_iterator::_buf_len) {
1263 }
1264
1265 // read in the previous contiguous memory into the read buffer
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" );
1270
1271 // walk this memory section to adjust block position to match the adjusted location
1272 // of the block start and store in the new index file
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);
1277
1278 if ( (pos_content - start_of_blk_buffer_pos) > 0 && (pos_content - start_of_blk_buffer_pos) < pos_size ) {
1279 // avoid the whole 8 bytes that contains a blk pos being split by the buffer
1280 write_size = read_size - (pos_content - start_of_blk_buffer_pos);
1281 }
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;
1287 }
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);
1291 }
1292
1293 fclose(original_block_log.blk_in);
1294 original_block_log.blk_in = nullptr;
1295 new_block_file.flush();
1296 new_block_file.close();
1297
1298 fc::path old_log = temp_dir / "old.log";
1299 rename(original_block_log.block_file_name, old_log);
1300 rename(new_block_filename, original_block_log.block_file_name);
1301 fc::path old_ind = temp_dir / "old.index";
1302 rename(original_block_log.index_file_name, old_ind);
1303 rename(new_index_filename, original_block_log.index_file_name);
1304
1305 return true;
1306 }
#define LOG_RW_C
Definition block_log.cpp:13
#define LOG_WRITE_C
Definition block_log.cpp:12
void close()
Definition cfile.hpp:202
void flush()
Definition cfile.hpp:135
void seek(long loc)
Definition cfile.hpp:87
size_t tellp() const
Definition cfile.hpp:79
void write(const char *d, size_t n)
Definition cfile.hpp:127
wraps boost::filesystem::path to provide platform independent path manipulation.
bool remove(const path &p)
Definition name.hpp:106
const string block_dir
Definition main.cpp:48
uint8_t buf[2048]
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ max_supported_version

const uint32_t sysio::chain::block_log::max_supported_version = 3
static

History: Version 1: complete block log from genesis Version 2: adds optional partial block log, cannot be used for replay without snapshot this is in the form of an first_block_num that is written immediately after the version Version 3: improvement on version 2 to not require the genesis state be provided when not starting from block 1

Definition at line 76 of file block_log.hpp.

◆ min_supported_version

const uint32_t sysio::chain::block_log::min_supported_version = 1
static

Definition at line 75 of file block_log.hpp.

◆ npos

const uint64_t sysio::chain::block_log::npos = std::numeric_limits<uint64_t>::max()
static

Definition at line 73 of file block_log.hpp.


The documentation for this class was generated from the following files: