Compare commits

...

3 Commits

Author SHA1 Message Date
766C6164 aada079735 Cleanup 2022-10-14 10:57:17 -04:00
766C6164 882ae7fba4 Added print-log, unified subcommand handlers 2022-10-13 16:43:13 -04:00
766C6164 68c1f80a74 Refactoring of block-log subcommand 2022-10-13 12:31:18 -04:00
2 changed files with 153 additions and 137 deletions
+142 -121
View File
@@ -47,142 +47,76 @@ struct report_time {
void blocklog_actions::setup(CLI::App& app) {
// callback helper with error code handling
auto err_guard = [this](int (blocklog_actions::*fun)()) {
try {
int rc = (this->*fun)();
if(rc) throw(CLI::RuntimeError(rc));
} catch(...) {
print_exception();
throw(CLI::RuntimeError(-1));
}
};
// main command
auto* sub = app.add_subcommand("block-log", "Blocklog utility");
sub->require_subcommand(1);
// callback with error code handling
auto cb = [this]() {
int rc = run_subcommand();
// properly return err code in main
if(rc) throw(CLI::RuntimeError(rc));
};
// subcommand - print log
auto* print_log = sub->add_subcommand("print-log", "Print blocks.log as JSON")->callback([err_guard]() { err_guard(&blocklog_actions::read_log); });
print_log->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
print_log->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
print_log->add_option("--first,-f", opt->first_block, "The first block number to log or the first to keep if trim-blocklog.");
print_log->add_option("--last,-l", opt->last_block, "The last block number to log or the last to keep if trim-blocklog.");
print_log->add_flag("--no-pretty-print", opt->no_pretty_print, "Do not pretty print the output. Useful if piping to jq to improve performance.");
print_log->add_flag("--as-json-array", opt->as_json_array, "Print out json blocks wrapped in json array (otherwise the output is free-standing json objects).");
// options
sub->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
sub->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
sub->add_option("--first,-f", opt->first_block, "The first block number to log or the first to keep if trim-blocklog.");
sub->add_option("--last,-l", opt->last_block, "The last block number to log or the last to keep if trim-blocklog.");
sub->add_option("--output-dir", opt->output_dir, "The output directory for the block log extracted from blocks-dir.");
// subcommand - make index
auto* make_index = sub->add_subcommand("make-index", "Create blocks.index from blocks.log. Must give 'blocks-dir'. Give 'output-file' relative to current directory or absolute path (default is <blocks-dir>/blocks.index).")->callback([err_guard]() { err_guard(&blocklog_actions::make_index); });
make_index->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
make_index->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
// flags
sub->add_flag("--no-pretty-print", opt->no_pretty_print, "Do not pretty print the output. Useful if piping to jq to improve performance.");
sub->add_flag("--as-json-array", opt->as_json_array, "Print out json blocks wrapped in json array (otherwise the output is free-standing json objects).");
// subcommand - trim blocklog
auto* trim_blocklog = sub->add_subcommand("trim-blocklog", "Trim blocks.log and blocks.index. Must give 'blocks-dir' and 'first' and/or 'last'.")->callback([err_guard]() { err_guard(&blocklog_actions::trim_blocklog); });
trim_blocklog->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
trim_blocklog->add_option("--first,-f", opt->first_block, "The first block number to keep.")->required();
trim_blocklog->add_option("--last,-l", opt->last_block, "The last block number to keep.")->required();
// subcommands
sub->add_subcommand("make-index", "Create blocks.index from blocks.log. Must give 'blocks-dir'. Give 'output-file' relative to current directory or absolute path (default is <blocks-dir>/blocks.index).")->callback([this, cb]() {opt->make_index=true; cb(); });
sub->add_subcommand("trim-blocklog", "Trim blocks.log and blocks.index. Must give 'blocks-dir' and 'first' and/or 'last'.")->callback([this, cb]() {opt->trim_blocklog=true; cb(); });
sub->add_subcommand("extract-blocks", "Extract range of blocks from blocks.log and write to output-dir. Must give 'first' and/or 'last'.")->callback([this, cb]() {opt->extract_blocks=true; cb(); });
sub->add_subcommand("smoke-test", "Quick test that blocks.log and blocks.index are well formed and agree with each other.")->callback([this, cb]() {opt->smoke_test=true; cb(); });
sub->add_subcommand("vacuum", "Vacuum a pruned blocks.log in to an un-pruned blocks.log")->callback([this, cb]() {opt->vacuum=true; cb(); });
sub->add_subcommand("genesis", "Extract genesis_state from blocks.log as JSON")->callback([this, cb]() {opt->genesis=true; cb(); });
// subcommand - extract blocks
auto* extract_blocks = sub->add_subcommand("extract-blocks", "Extract range of blocks from blocks.log and write to output-dir. Must give 'first' and/or 'last'.")->callback([err_guard]() { err_guard(&blocklog_actions::extract_blocks); });
extract_blocks->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
extract_blocks->add_option("--first,-f", opt->first_block, "The first block number to keep.")->required();
extract_blocks->add_option("--last,-l", opt->last_block, "The last block number to keep.")->required();
extract_blocks->add_option("--output-dir", opt->output_dir, "The output directory for the block log extracted from blocks-dir.");
sub->callback([cb]() { cb(); });
// subcommand - smoke test
auto* smoke_test = sub->add_subcommand("smoke-test", "Quick test that blocks.log and blocks.index are well formed and agree with each other.")->callback([err_guard]() { err_guard(&blocklog_actions::smoke_test); });
smoke_test->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
// subcommand - vacuum
auto* vacuum = sub->add_subcommand("vacuum", "Vacuum a pruned blocks.log in to an un-pruned blocks.log")->callback([err_guard]() { err_guard(&blocklog_actions::do_vacuum); });
vacuum->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
// subcommand - genesis
auto* genesis = sub->add_subcommand("genesis", "Extract genesis_state from blocks.log as JSON")->callback([err_guard]() { err_guard(&blocklog_actions::do_genesis); });
genesis->add_option("--blocks-dir", opt->blocks_dir, "The location of the blocks directory (absolute path or relative to the current directory).");
genesis->add_option("--output-file,-o", opt->output_file, "The file to write the output to (absolute or relative path). If not specified then output is to stdout.");
}
int blocklog_actions::run_subcommand() {
std::ios::sync_with_stdio(false);// for potential performance boost for large block log files
void blocklog_actions::print_exception() noexcept {
try {
if(opt->trim_blocklog) {
if(opt->first_block == 0 && opt->last_block == std::numeric_limits<uint32_t>::max()) {
std::cerr << "trim-blocklog does nothing unless first and/or last block are specified.";
return -1;
}
if(opt->last_block != std::numeric_limits<uint32_t>::max()) {
if(trim_blocklog_end(opt->blocks_dir, opt->last_block) != 0)
return -1;
}
if(opt->first_block != 0) {
if(!trim_blocklog_front(opt->blocks_dir, opt->first_block))
return -1;
}
return 0;
}
if(opt->extract_blocks) {
if(opt->first_block == 0 && opt->last_block == std::numeric_limits<uint32_t>::max()) {
std::cerr << "extract-blocklog does nothing unless first and/or last block are specified.";
return -1;
}
if(!extract_block_range(opt->blocks_dir, opt->output_dir, opt->first_block, opt->last_block))
return -1;
return 0;
}
if(opt->vacuum) {
initialize();
return do_vacuum();
}
if(opt->genesis) {
initialize();
return do_genesis();
}
if(opt->make_index) {
const bfs::path blocks_dir = opt->blocks_dir;
bfs::path out_file = blocks_dir / "blocks.index";
const bfs::path block_file = blocks_dir / "blocks.log";
if(!opt->output_file.empty())
out_file = opt->output_file;
report_time rt("making index");
const auto log_level = fc::logger::get(DEFAULT_LOGGER).get_log_level();
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
block_log::construct_index(block_file.generic_string(), out_file.generic_string());
fc::logger::get(DEFAULT_LOGGER).set_log_level(log_level);
rt.report();
return 0;
}
//else print blocks.log as JSON
initialize();
read_log();
throw;
} catch(const fc::exception& e) {
elog("${e}", ("e", e.to_detail_string()));
return -1;
} catch(const boost::exception& e) {
elog("${e}", ("e", boost::diagnostic_information(e)));
return -1;
} catch(const CLI::RuntimeError& e) {
// avoid reporting it twice, RuntimeError is only for cli11
} catch(const std::exception& e) {
elog("${e}", ("e", e.what()));
return -1;
} catch(...) {
elog("unknown exception");
return -1;
}
return 0;
}
int blocklog_actions::do_genesis() {
std::optional<genesis_state> gs;
bfs::path bld = opt->blocks_dir;
auto full_path = (bld / "blocks.log").generic_string();
if(fc::exists(bld / "blocks.log")) {
gs = block_log::extract_genesis_state(opt->blocks_dir);
if (!gs) {
std::cerr << "Block log at '" << full_path
<< "' does not contain a genesis state, it only has the chain-id." << std::endl;
return -1;
}
} else {
std::cerr << "No blocks.log found at '" << full_path << "'." << std::endl;
return -1;
}
// just print if output not set
if(opt->output_file.empty()) {
std::cout << json::to_pretty_string(*gs) << std::endl;
} else {
bfs::path p = opt->output_file;
if(p.is_relative()) {
p = bfs::current_path() / p;
}
if (!fc::json::save_to_file(*gs, p, true)) {
std::cerr << "Error occurred while writing genesis JSON to '" << p.generic_string() << "'" << std::endl;
return -1;
}
std::cout << "Saved genesis JSON to '" << p.generic_string() << "'" << std::endl;
}
return 0;
}
void blocklog_actions::initialize() {
@@ -211,6 +145,78 @@ void blocklog_actions::initialize() {
FC_LOG_AND_RETHROW()
}
int blocklog_actions::make_index() {
initialize();
const bfs::path blocks_dir = opt->blocks_dir;
bfs::path out_file = blocks_dir / "blocks.index";
const bfs::path block_file = blocks_dir / "blocks.log";
if(!opt->output_file.empty()) out_file = opt->output_file;
report_time rt("making index");
const auto log_level = fc::logger::get(DEFAULT_LOGGER).get_log_level();
fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::debug);
block_log::construct_index(block_file.generic_string(), out_file.generic_string());
fc::logger::get(DEFAULT_LOGGER).set_log_level(log_level);
rt.report();
return 0;
}
int blocklog_actions::trim_blocklog() {
initialize();
if(opt->last_block != std::numeric_limits<uint32_t>::max()) {
if(trim_blocklog_end(opt->blocks_dir, opt->last_block) != 0) return -1;
}
if(opt->first_block != 0) {
if(!trim_blocklog_front(opt->blocks_dir, opt->first_block)) return -1;
}
return 0;
}
int blocklog_actions::extract_blocks() {
initialize();
if(!extract_block_range(opt->blocks_dir, opt->output_dir, opt->first_block, opt->last_block))
return -1;
return 0;
}
int blocklog_actions::do_genesis() {
initialize();
std::optional<genesis_state> gs;
bfs::path bld = opt->blocks_dir;
auto full_path = (bld / "blocks.log").generic_string();
if(fc::exists(bld / "blocks.log")) {
gs = block_log::extract_genesis_state(opt->blocks_dir);
if(!gs) {
std::cerr << "Block log at '" << full_path
<< "' does not contain a genesis state, it only has the chain-id." << std::endl;
return -1;
}
} else {
std::cerr << "No blocks.log found at '" << full_path << "'." << std::endl;
return -1;
}
// just print if output not set
if(opt->output_file.empty()) {
std::cout << json::to_pretty_string(*gs) << std::endl;
} else {
bfs::path p = opt->output_file;
if(p.is_relative()) {
p = bfs::current_path() / p;
}
if(!fc::json::save_to_file(*gs, p, true)) {
std::cerr << "Error occurred while writing genesis JSON to '" << p.generic_string() << "'" << std::endl;
return -1;
}
std::cout << "Saved genesis JSON to '" << p.generic_string() << "'" << std::endl;
}
return 0;
}
int blocklog_actions::trim_blocklog_end(bfs::path block_dir, uint32_t n) {//n is last block to keep (remove later blocks)
report_time rt("trimming blocklog end");
using namespace std;
@@ -250,9 +256,10 @@ bool blocklog_actions::extract_block_range(bfs::path block_dir, bfs::path output
return status;
}
void blocklog_actions::smoke_test(bfs::path block_dir) {
int blocklog_actions::smoke_test() {
initialize();
using namespace std;
bfs::path block_dir = opt->blocks_dir;
cout << "\nSmoke test of blocks.log and blocks.index in directory " << block_dir << '\n';
trim_data td(block_dir);
auto status = fseek(td.blk_in, -sizeof(uint64_t), SEEK_END);//get last_block from blocks.log, compare to from blocks.index
@@ -279,10 +286,21 @@ void blocklog_actions::smoke_test(bfs::path block_dir) {
break;
}
cout << "\nno problems found\n";//if get here there were no exceptions
return 0;
}
int blocklog_actions::do_vacuum() {
if (!opt->blog_keep_prune_conf) {
initialize();
bfs::path bld = opt->blocks_dir;
auto full_path = (bld / "blocks.log").generic_string();
if(!fc::exists(bld / "blocks.log")) {
std::cerr << "No blocks.log found at '" << full_path << "'." << std::endl;
return -1;
}
if(!opt->blog_keep_prune_conf) {
std::cerr << "blocks.log is not a pruned log; nothing to vacuum" << std::endl;
return -1;
}
@@ -291,7 +309,8 @@ int blocklog_actions::do_vacuum() {
return 0;
}
void blocklog_actions::read_log() {
int blocklog_actions::read_log() {
initialize();
report_time rt("reading log");
block_log block_logger(opt->blocks_dir, opt->blog_keep_prune_conf);
const auto end = block_logger.read_head();
@@ -385,4 +404,6 @@ void blocklog_actions::read_log() {
if(opt->as_json_array)
*out << "]";
rt.report();
return 0;
}
+11 -16
View File
@@ -12,20 +12,12 @@ struct blocklog_options {
std::string output_file = "";
uint32_t first_block = 0;
uint32_t last_block = std::numeric_limits<uint32_t>::max();
std::string output_dir;
std::string output_dir = "";
// flags
bool no_pretty_print = false;
bool as_json_array = false;
// subcommands
bool make_index = false;
bool trim_blocklog = false;
bool extract_blocks = false;
bool smoke_test = false;
bool vacuum = false;
bool genesis = false;
std::optional<block_log_prune_config> blog_keep_prune_conf;
};
@@ -34,16 +26,19 @@ public:
blocklog_actions() : sub_command() {}
void setup(CLI::App& app);
// callbacks
int run_subcommand();
protected:
void print_exception() noexcept;
void initialize();
int trim_blocklog_end(bfs::path block_dir, uint32_t n);
bool trim_blocklog_front(bfs::path block_dir, uint32_t n);
bool extract_block_range(bfs::path block_dir, bfs::path output_dir, uint32_t start, uint32_t end);
void smoke_test(bfs::path block_dir);
int do_vacuum();
int do_genesis();
void read_log();
int make_index();
int trim_blocklog();
int extract_blocks();
int smoke_test();
int do_vacuum();
int do_genesis();
int read_log();
};