6 #include <boost/program_options.hpp> 7 #include <libp2p/log/configurator.hpp> 42 <<
"': " << err.what() <<
"\n";
51 Command(std::string name, std::string description)
52 : name{std::move(name)}, description{std::move(description)} {}
56 virtual void execute(std::ostream &out,
const ArgumentList &args) = 0;
68 if (args.size() < min or args.size() > max) {
71 fmt::format(
"Argument count mismatch: expected {} to {}, got {}",
78 template <
typename... Ts>
81 fmt::format(fmt, std::forward<Ts>(ts)...)};
85 T
unwrapResult(std::string_view context, outcome::result<T> &&res)
const {
86 if (res.has_value())
return std::move(res).value();
87 throwError(
"{}: {}", context, res.error().message());
100 std::string name{cmd->
getName()};
101 commands_.insert({name, std::move(cmd)});
105 if (args.size() < 2) {
106 std::cerr <<
"Unspecified command!\nAvailable commands are:\n";
107 printCommands(std::cerr);
109 if (
auto command = commands_.find(args[1]); command != commands_.cend()) {
112 command->second->execute(std::cout, cmd_args);
114 std::cerr <<
"Command execution error: " << e;
115 }
catch (std::exception &e) {
116 std::cerr <<
"Exception occurred: " << e.what();
119 std::cerr <<
"Unknown command '" << args[1]
120 <<
"'!\nAvailable commands are:\n";
121 printCommands(std::cerr);
126 for (
auto &[name, cmd] : commands_) {
127 out << name <<
"\t" << cmd->getDescription() <<
"\n";
132 std::unordered_map<std::string, std::unique_ptr<Command>>
commands_;
135 std::optional<kagome::primitives::BlockId>
parseBlockId(
const char *
string) {
140 std::copy_n(id_bytes.value().begin(),
144 std::cerr <<
"Invalid block hash!\n";
147 id = std::move(id_hash);
150 id = std::stoi(
string);
151 }
catch (std::invalid_argument &) {
152 std::cerr <<
"Block number must be a hex hash or a number!\n";
162 :
Command{
"help",
"print help message"}, parser{parser} {}
165 assertArgumentCount(args, 1, 1);
166 parser.printCommands(out);
177 "# or hash - print info about the block with the given number " 179 block_storage{block_storage} {}
182 assertArgumentCount(args, 2, 2);
185 throwError(
"Failed to parse block id '{}'", args[1]);
187 if (
auto res = block_storage->getBlockHeader(opt_id.value()); res) {
188 if (!res.value().has_value()) {
189 throwError(
"Block header not found for '{}'", args[1]);
191 std::cout <<
"#: " << res.value()->number <<
"\n";
192 std::cout <<
"Parent hash: " << res.value()->parent_hash.toHex() <<
"\n";
193 std::cout <<
"State root: " << res.value()->state_root.toHex() <<
"\n";
194 std::cout <<
"Extrinsics root: " << res.value()->extrinsics_root.toHex()
197 if (
auto body_res = block_storage->getBlockBody(opt_id.value());
199 if (!body_res.value().has_value()) {
200 throwError(
"Block body not found for '{}'", args[1]);
202 std::cout <<
"# of extrinsics: " << body_res.value()->size() <<
"\n";
205 throwError(
"Internal error: {}}", body_res.error().message());
209 throwError(
"Internal error: {}}", res.error().message());
221 "# or hash - remove the block from the block tree"},
222 block_storage{block_storage} {}
225 assertArgumentCount(args, 2, 2);
228 throwError(
"Failed to parse block id '{}'", args[1]);
230 auto data = block_storage->getBlockData(opt_id.value());
232 throwError(
"Failed getting block data: {}", data.error().message());
234 if (!data.value().has_value()) {
235 throwError(
"Block data not found");
237 if (
auto res = block_storage->removeBlock(
238 {data.value().value().header->number, data.value().value().hash});
240 throwError(
"{}", res.error().message());
252 "state_hash, key - query value at a given key and state"},
253 trie_storage{trie_storage} {}
256 assertArgumentCount(args, 3, 3);
260 std::copy_n(id_bytes.value().begin(),
264 throwError(
"Invalid block hash!");
266 auto batch = trie_storage->getEphemeralBatchAt(state_root);
268 throwError(
"Failed getting trie batch: {}", batch.error().message());
274 throwError(
"Invalid key!");
276 auto value_res = batch.value()->tryGet(key);
277 if (value_res.has_error()) {
278 throwError(
"Error retrieving value from Trie: {}",
279 value_res.error().message());
281 auto &value_opt = value_res.value();
282 if (value_opt.has_value()) {
283 std::cout <<
"Value is " << value_opt->get().toHex() <<
"\n";
285 std::cout <<
"No value by given key\n";
296 std::shared_ptr<BlockStorage> block_storage,
297 std::shared_ptr<TrieStorage> trie_storage,
298 std::shared_ptr<kagome::authority::AuthorityManager> authority_manager,
299 std::shared_ptr<Hasher> hasher)
301 "target [start block/0] [end block/deepest finalized] - search " 302 "the finalized chain for the target entity. Currently, " 303 "'justification' or 'authority-update' are supported "},
304 block_storage{block_storage},
305 trie_storage{trie_storage},
307 BOOST_ASSERT(block_storage !=
nullptr);
308 BOOST_ASSERT(trie_storage !=
nullptr);
309 BOOST_ASSERT(authority_manager !=
nullptr);
310 BOOST_ASSERT(hasher !=
nullptr);
313 enum class Target { Justification, AuthorityUpdate, LastBlock };
316 assertArgumentCount(args, 2, 4);
317 Target target = parseTarget(args[1]);
318 if (target == Target::LastBlock) {
319 std::cout <<
"#" << block_storage->getLastFinalized().value().number
320 <<
" " << block_storage->getLastFinalized().value().hash.toHex()
327 if (args.size() > 2) {
330 throwError(
"Failed to parse block id '{}'", args[2]);
332 start = start_id_opt.value();
336 if (args.size() > 3) {
339 throwError(
"Failed to parse block id '{}'", args[3]);
341 end = end_id_opt.value();
343 auto last_finalized = unwrapResult(
"Getting last finalized block",
344 block_storage->getLastFinalized());
345 end = last_finalized.number;
348 auto start_header_opt = unwrapResult(
"Getting 'start' block header",
349 block_storage->getBlockHeader(start));
350 if (!start_header_opt) {
351 throwError(
"Start block header {} not found", start);
353 auto &start_header = start_header_opt.value();
355 auto end_header_opt = unwrapResult(
"Getting 'end' block header",
356 block_storage->getBlockHeader(end));
357 if (!end_header_opt) {
358 throwError(
"'End block header {} not found", end);
360 auto &end_header = end_header_opt.value();
362 for (int64_t current = start_header.number, stop = end_header.number;
365 auto current_header_opt =
366 unwrapResult(fmt::format(
"Getting header of block #{}", current),
367 block_storage->getBlockHeader(current));
368 if (!current_header_opt) {
369 throwError(
"Block header #{} not found", current);
371 searchBlock(out, current_header_opt.value(), target);
377 if (strcmp(arg,
"justification") == 0)
return Target::Justification;
378 if (strcmp(arg,
"authority-update") == 0)
return Target::AuthorityUpdate;
379 if (strcmp(arg,
"last-finalized") == 0)
return Target::LastBlock;
380 throwError(
"Invalid target '{}'", arg);
387 case Target::Justification:
388 return searchForJustification(out, header);
389 case Target::AuthorityUpdate:
390 return searchForAuthorityUpdate(out, header);
391 case Target::LastBlock:
394 BOOST_UNREACHABLE_RETURN();
399 auto just_opt = unwrapResult(
400 fmt::format(
"Getting justification for block #{}", header.
number),
401 block_storage->getJustification(header.
number));
403 out << header.
number <<
", ";
409 for (
auto &digest_item : header.
digest) {
410 auto *consensus_digest =
411 boost::get<kagome::primitives::Consensus>(&digest_item);
412 if (consensus_digest) {
413 auto decoded = unwrapResult(
"Decoding consensus digest",
414 consensus_digest->decode());
415 if (decoded.consensus_engine_id
417 reportAuthorityUpdate(out, header.
number, decoded.asGrandpaDigest());
427 if (
auto *scheduled_change = boost::get<ScheduledChange>(&digest);
429 out <<
"ScheduledChange at #" << digest_origin <<
" for ";
430 if (scheduled_change->subchain_length > 0) {
431 out <<
"#" << digest_origin + scheduled_change->subchain_length;
433 out <<
"the same block";
437 }
else if (
auto *forced_change = boost::get<ForcedChange>(&digest);
439 out <<
"ForcedChange at " << digest_origin <<
", delay starts at #" 440 << forced_change->delay_start <<
" for " 441 << forced_change->subchain_length <<
" blocks (so activates at #" 442 << forced_change->delay_start + forced_change->subchain_length <<
")";
445 }
else if (
auto *pause = boost::get<Pause>(&digest); pause) {
446 out <<
"Pause at " << digest_origin <<
" for " 447 << digest_origin + pause->subchain_length <<
"\n";
449 }
else if (
auto *resume = boost::get<Resume>(&digest); resume) {
450 out <<
"Resume at " << digest_origin <<
" for " 451 << digest_origin + resume->subchain_length <<
"\n";
453 }
else if (
auto *disabled = boost::get<OnDisabled>(&digest); disabled) {
454 out <<
"Disabled at " << digest_origin <<
" for authority " 455 << disabled->authority_index <<
"\n";
464 int main(
int argc,
const char **argv) {
468 parser.
addCommand(std::make_unique<PrintHelpCommand>(parser));
470 auto logging_system = std::make_shared<soralog::LoggingSystem>(
471 std::make_shared<kagome::log::Configurator>(
472 std::make_shared<libp2p::log::Configurator>()));
474 auto r = logging_system->configure();
475 if (not r.message.empty()) {
476 (r.has_error ? std::cerr : std::cout) << r.message << std::endl;
487 int kagome_args_start = -1;
488 for (
int i = 1; i < args.size(); i++) {
489 if (strcmp(args[i],
"--") == 0) {
490 kagome_args_start = i;
493 if (kagome_args_start == -1) {
495 <<
"You must specify arguments for kagome initialization after '--'\n";
499 if (!configuration.initializeFromArgs(
500 argc - kagome_args_start, args.subspan(kagome_args_start).data())) {
501 std::cerr <<
"Failed to initialize kagome!\n";
507 auto trie_storage = injector.injectTrieStorage();
508 auto app_state_manager = injector.injectAppStateManager();
509 auto block_tree = injector.injectBlockTree();
510 auto executor = injector.injectExecutor();
511 auto persistent_storage = injector.injectStorage();
512 auto hasher = std::make_shared<kagome::crypto::HasherImpl>();
515 std::make_shared<kagome::blockchain::BlockHeaderRepositoryImpl>(
516 persistent_storage, hasher);
518 std::make_shared<kagome::runtime::GrandpaApiImpl>(header_repo, executor);
520 auto authority_manager =
521 std::make_shared<kagome::authority::AuthorityManagerImpl>(
531 parser.
addCommand(std::make_unique<InspectBlockCommand>(block_storage));
532 parser.
addCommand(std::make_unique<RemoveBlockCommand>(block_storage));
533 parser.
addCommand(std::make_unique<QueryStateCommand>(trie_storage));
534 parser.
addCommand(std::make_unique<SearchChainCommand>(
535 block_storage, trie_storage, authority_manager, hasher));
537 parser.
invoke(args.subspan(0, kagome_args_start));
void invoke(const ArgumentList &args) const noexcept
std::shared_ptr< TrieStorage > trie_storage
Class represents arbitrary (including empty) byte buffer.
std::unordered_map< std::string, std::unique_ptr< Command > > commands_
boost::variant< Unused< 0 >, ScheduledChange, ForcedChange, OnDisabled, Pause, Resume > GrandpaDigest
https://github.com/paritytech/substrate/blob/polkadot-v0.9.8/primitives/finality-grandpa/src/lib.rs#L92
std::shared_ptr< BlockStorage > block_storage
void reportAuthorityUpdate(std::ostream &out, BlockNumber digest_origin, GrandpaDigest const &digest) const
void searchForAuthorityUpdate(std::ostream &out, BlockHeader const &header) const
QueryStateCommand(std::shared_ptr< TrieStorage > trie_storage)
InspectBlockCommand(std::shared_ptr< BlockStorage > block_storage)
std::shared_ptr< Hasher > hasher
T unwrapResult(std::string_view context, outcome::result< T > &&res) const
Target parseTarget(const char *arg) const
Command(std::string name, std::string description)
void throwError(const char *fmt, Ts &&...ts) const
std::shared_ptr< BlockStorage > block_storage
void printCommands(std::ostream &out) const
std::string_view getDescription() const
void searchForJustification(std::ostream &out, BlockHeader const &header) const
std::string_view getName() const
std::shared_ptr< blockchain::BlockStorage > injectBlockStorage()
interface for Grandpa runtime functions
CommandParser const & parser
RemoveBlockCommand(std::shared_ptr< BlockStorage > block_storage)
virtual void execute(std::ostream &out, const ArgumentList &args) override
PrintHelpCommand(CommandParser const &parser)
void addCommand(std::unique_ptr< Command > cmd)
std::shared_ptr< BlockStorage > block_storage
void setLoggingSystem(std::shared_ptr< soralog::LoggingSystem > logging_system)
virtual void execute(std::ostream &out, const ArgumentList &args) override
primitives::BlockNumber BlockNumber
virtual void execute(std::ostream &out, const ArgumentList &args) override
outcome::result< std::vector< uint8_t > > unhex(std::string_view hex)
Converts hex representation to bytes.
void assertArgumentCount(const ArgumentList &args, int min, int max)
gsl::span< const char * > ArgumentList
std::string_view command_name
SearchChainCommand(std::shared_ptr< BlockStorage > block_storage, std::shared_ptr< TrieStorage > trie_storage, std::shared_ptr< kagome::authority::AuthorityManager > authority_manager, std::shared_ptr< Hasher > hasher)
static constexpr size_t size()
CommandExecutionError(std::string_view command_name, const std::string &what)
boost::variant< BlockHash, BlockNumber > BlockId
Block id is the variant over BlockHash and BlockNumber.
const auto kGrandpaEngineId
virtual void execute(std::ostream &out, const ArgumentList &args) override
Logger createLogger(const std::string &tag)
virtual void execute(std::ostream &out, const ArgumentList &args) override
void searchBlock(std::ostream &out, BlockHeader const &header, Target target) const
std::shared_ptr< TrieStorage > trie_storage
std::optional< kagome::primitives::BlockId > parseBlockId(const char *string)
int main(int argc, const char **argv)
std::function< void(int, char **)> CommandFunctor
friend std::ostream & operator<<(std::ostream &out, CommandExecutionError const &err)
bool setLevelOfGroup(const std::string &group_name, Level level)