Full stack Bitcoin software

Explorer Architecture

Architecture of libbitcoin-explorer: the command model, generation, parsing and output.

Bitcoin Explorer (bx) is a command line tool built on libbitcoin-system. Each of its commands is a class derived from a common base, generated from one XML model that declares the command's name, category, options, positional arguments, configuration settings and help text. The bx executable finds the command named by its first argument, parses the rest of the command line, the environment and a configuration file into that command's bound members, and invokes it with an output and an error stream. Paths are relative to include/bitcoin/explorer/ in libbitcoin-explorer, at master 78ccbab8 (2026-10-27).

1. Execution flow#

The console entry point forwards its arguments and the three standard streams to dispatch (console/main.cpp). BC_USE_LIBBITCOIN_MAIN is the libbitcoin-system macro that defines the platform main and supplies UTF-8 streams on Windows.

BC_USE_LIBBITCOIN_MAIN

int bc::system::main(int argc, char* argv[])
{
    return bc::explorer::dispatch(argc, const_cast<const char**>(argv),
        bc::system::cin, bc::system::cout, bc::system::cerr);
}
flowchart TB main["console main<br/>bc::system::main"] dispatch["dispatch<br/>usage if no command"] find["find<br/>symbol to command instance"] formerly["formerly<br/>superseding name"] parser["parser::parse<br/>command line, stdin fallbacks,<br/>environment, config file"] help["command::write_help"] invoke["command::invoke<br/>output, error"] result["console_result<br/>failure, okay, invalid"] main --> dispatch dispatch --> find find -- "not found" --> formerly find --> parser parser -- "help option" --> help parser --> invoke invoke --> result

dispatch (src/dispatch.cpp) displays usage and the command list when there are no arguments. Otherwise it drops the program name and calls dispatch_command, which treats the first remaining argument as the command symbol. An unknown symbol is reported as an invalid command, naming the command that replaced it if formerly returns one. A found command is parsed, then helped or invoked. An exception escaping the command is reported as an invalid parameter and returns console_result::failure.

parser metadata(*command);
std::string error_message;

if (!metadata.parse(error_message, in, argc, argv))
{
    display_invalid_parameter(error, error_message);
    return console_result::failure;
}

if (metadata.help())
{
    command->write_help(output);
    return console_result::okay;
}

return command->invoke(out, err);

In Windows builds the input and output streams are swapped for binary std::cin and std::cout when the command declares requires_raw_input() or requires_raw_output(), and set to UTF-8 otherwise. The error stream is always UTF-8.

The return value is a console_result (define.hpp): failure (-1), okay (0) or invalid (1). Positive values are domain specific, such as a validation command reporting an invalid result.

2. The command model#

2.1 Base class#

command (command.hpp) is the base of every command. It cannot be constructed directly, and each virtual has a default, so a derived class overrides only what applies to it.

Member Purpose
symbol() Static, lower case command name, such as tx-decode
name(), category(), description() Name, category (such as WALLET) and help text
obsolete() Excludes the command from the listed command names
requires_raw_input(), requires_raw_output() Binary standard input or output
load_options(), load_arguments() Named options and positional arguments
load_environment(), load_settings() Environment variables and configuration file settings
load_fallbacks() Reads unset arguments from standard input
set_defaults_from_config() Applies configuration settings to defaulted options
write_help() Writes command line help from the option and argument metadata
invoke(output, error) Executes the command

The base also holds the configuration settings shared by all commands, in [wallet] and [server] sections, each with a typed getter and setter (get_wallet_wif_version_setting() and so on). Help is produced by the libbitcoin-system config::printer, from the same metadata that the parser consumes.

2.2 Command classes#

Each command is a class in commands/, named for its symbol, that derives from command. Its options and arguments are bound to private option_ and argument_ structures, and exposed through typed accessors such as get_transaction_argument() and get_version_option(). Every command accepts --help (-h) and --config (-c). Each argument is declared both as a named option and as a positional argument with an instance limit, so it may be given either way (commands/tx-decode.hpp):

virtual arguments_metadata& load_arguments()
{
    return get_argument_metadata()
        .add("TRANSACTION", 1);
}

virtual void load_fallbacks(std::istream& input,
    po::variables_map& variables)
{
    load_input(get_transaction_argument(), "TRANSACTION", variables, input);
}

The class declares invoke, and its definition in src/commands/ reads the bound values through the accessors and writes the result (src/commands/address-embed.cpp, simplified):

console_result address_embed::invoke(std::ostream& output,
    std::ostream&)
{
    const data_chunk& data = get_data_argument();
    const auto version = get_version_option();

    const auto ops = script::to_pay_key_hash_pattern(rmd160_hash(data));
    const script script(ops);
    const auto hash = rmd160_hash(script.to_data(false));

    output << payment_address(hash, version) << std::endl;
    return console_result::okay;
}

2.3 Command table#

generated.hpp includes every command header and declares three free functions over the full set of commands, defined in src/generated.cpp:

Function Result
find(symbol) A new instance of the command with that symbol, or null
formerly(former) The symbol of the command that replaced a former name, or empty
broadcast(func) Calls func with an instance of every command

find compares the symbol against each class's static symbol(), so commands are constructed only on demand. broadcast lists the commands for usage (display_command_names), skipping obsolete ones. The help command uses find to load and write the help of another command.

3. Generation#

The command headers are generated from model/generate.xml by the iMatix GSL templates in model/templates/ (model/generate.gsl). Implementations in src/commands/ are written by hand.

Template Output
gsl.command.hpp command.hpp, with the configuration settings
gsl.commands.hpp commands/<symbol>.hpp, one per command
gsl.generated.hpp, gsl.generated.cpp generated.hpp, src/generated.cpp
data/gsl.bx data/bx, a bash command completion script
test/gsl.generated__*.cpp Tests of broadcast, find, formerly and symbol

The model declares each command with its arguments and options, the configuration sections, and the localizable resource strings that become the BX_* macros in generated.hpp:

<command symbol="address-embed" formerly="embed-addr" output="payment_address" category="WALLET" description="...">
  <option name="version" type="byte" configuration="wallet.pay_to_public_key_hash_version" description="..." />
  <argument name="DATA" stdin="true" type="bytes" description="..." />
</command>

Model types map to C++ types in model/primitives.gsl, either a libbitcoin-system type (system::config::hash256, system::wallet::hd_private) or one of the explorer config types (section 4.3). An option without a type is a bool switch. The stdin attribute generates the load_input fallback, an argument of type bytes read from standard input makes the command raw input, and an output of bytes makes it raw output. The configuration attribute generates set_defaults_from_config, and obsolete and formerly generate obsolete() and the formerly entry.

4. Parsing#

4.1 Sources#

parser (parser.hpp) derives from the libbitcoin-system config::parser and supplies its metadata from the command instance. The sources are loaded into one variables_map, with the first stored value of a variable taking precedence (src/parser.cpp):

Order Source Detail
First Command line Named options and positional arguments
Second Standard input load_fallbacks, for arguments not given on the command line
Third Environment Variables prefixed BX_, declaring BX_CONFIG
Fourth Configuration file The file named by --config or BX_CONFIG

If --help is present only the command line is loaded, and help() returns true. Otherwise the parser notifies the bound variables and calls set_defaults_from_config, which replaces a defaulted option with its configured setting when that setting was not defaulted (commands/address-embed.hpp):

const auto& option_version = variables["version"];
const auto& option_version_config = variables["wallet.pay_to_public_key_hash_version"];

if (option_version.defaulted() && !option_version_config.defaulted())
{
    option_.version = option_version_config.as<explorer::config::byte>();
}

A parse error is returned as the error text and reported by display_invalid_parameter.

4.2 Standard input and files#

utility.hpp provides the fallback readers. load_input deserializes a parameter from the input stream when its variable was not set, and throws on failure. load_path reads a parameter from the file named by its variable, where - (BX_STDIO_PATH_SENTINEL) leaves the variable unset for standard input. write_file writes to the named file, or to the output stream when the path is empty or -.

4.3 Config types#

Arguments and options are typed by config objects that round-trip through stream operators, as described in Config. Most are libbitcoin-system types. The types in config/ cover values specific to bx, each with a native value type and a pair of stream operators:

Type Value Text form
address std::string A valid payment address
algorithm points_value::selection greedy or individual
btc uint64_t Bitcoin decimal, read as satoshis
byte uint8_t Decimal, not a character
bytes data_chunk The entire stream, unencoded
ec_private ec_secret Base16
electrum electrum::seed_prefix Seed prefix name
endorsement data_chunk Base16, up to max_endorsement_size
hd_key wallet::hd_key Base58 of hd_key_size bytes
language system::language Word list language name
sighash chain::coverage Signature hash type name
signature message_signature Base64 of message_signature_size bytes
witness witness_address::program_type Witness program name

5. Output#

A command writes its result to the output stream passed to invoke, and diagnostics to the error stream. Most commands write a single value in its text form through the value's operator<<, followed by a newline, such as base16(data) or a payment_address. A raw output command writes bytes to binary standard output.

Structured results are JSON. write_stream (utility.hpp) serializes a system::json::value followed by a newline. Chain objects convert through the libbitcoin-system JSON conversions, and json.hpp adds conversions for the settings list and for a bitcoin_uri (src/commands/tx-decode.cpp):

console_result tx_decode::invoke(std::ostream& output, std::ostream&)
{
    write_stream(output, value_from(chain::transaction{ get_transaction_argument() }));
    return console_result::okay;
}

to_json(settings_list) nests the dot-delimited setting names into section objects under a settings key, as written by the settings command. display.hpp holds the fixed messages of the dispatcher: usage with version and command list, invalid command (naming the superseding command where one exists), invalid parameter, and connection failure.

Copyright © 2011-2026 libbitcoin developers