751 {
752 ilog(
"Recovering Block Log...");
754 "Block log not found in '${blocks_dir}'", ("blocks_dir", data_dir) );
755
757
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
768 "Cannot move existing blocks directory to already existing directory '${new_blocks_dir}'",
769 ("new_blocks_dir", backup_dir) );
770
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
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
794 old_block_stream.read( (char*)&version, sizeof(version) );
795 SYS_ASSERT( version > 0, block_log_exception,
"Block log was not setup properly" );
797 "Unsupported version of block log. Block log version is ${version} while code supports version(s) [${min},${max}]",
799
800 new_block_stream.write( (char*)&version, sizeof(version) );
801
803 if (version != 1) {
805
806
807
808
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
815 }
816
820
822 new_block_stream.write( data.data(), data.size() );
823 }
825 chain_id_type chain_id;
826 old_block_stream >> chain_id;
827
828 new_block_stream << chain_id;
829 }
830 else {
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;
853
855
856 uint64_t pos = old_block_stream.tellg();
857 while( pos < end_pos ) {
858 signed_block tmp;
859
860 try {
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();
871 elog(
"Block ${num} (${id}) skips blocks. Previous block in block log is block ${prev_num} (${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}.",
879 }
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
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);
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 SYS_THROW(exc_type, FORMAT,...)
Used to generate a useful error report when an exception is thrown.
const char * what() const noexcept override
static bool is_supported_version(uint32_t version)
void pack(Stream &s, const std::deque< T > &value)
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)
bool is_directory(const path &p)
path canonical(const path &p)
uint32_t previous(octet_iterator &it, octet_iterator pass_start)
Deprecated in versions that include "prior".