diff --git a/programs/cleos/httpc.hpp b/programs/cleos/httpc.hpp index 1acf8a67f9..705cdcb9ff 100644 --- a/programs/cleos/httpc.hpp +++ b/programs/cleos/httpc.hpp @@ -135,5 +135,8 @@ namespace eosio { namespace client { namespace http { const string wallet_sign_trx = wallet_func_base + "/sign_transaction"; const string keosd_stop = "/v1/" + string(client::config::key_store_executable_name) + "/stop"; + const string producer_func_base = "/v1/producer"; + const string producer_get_supported_protocol_features_func = producer_func_base + "/get_supported_protocol_features"; + FC_DECLARE_EXCEPTION( connection_exception, 1100000, "Connection Exception" ); }}} diff --git a/programs/cleos/main.cpp b/programs/cleos/main.cpp index 44cac9f4d1..6da6436c06 100644 --- a/programs/cleos/main.cpp +++ b/programs/cleos/main.cpp @@ -69,6 +69,8 @@ Usage: ./cleos create account [OPTIONS] creator name OwnerKey ActiveKey #include #include #include +#include +#include #include #include #include @@ -2247,6 +2249,120 @@ struct closerex_subcommand { } }; +struct protocol_features_t { + std::string names; + std::unordered_map digests {}; // from name to digest +}; + +protocol_features_t get_supported_protocol_features() { + protocol_features_t results; + + auto prot_features = call(producer_get_supported_protocol_features_func, fc::mutable_variant_object("exclude_disabled", true) + ("exclude_unactivatable", true) + ); + + if ( !prot_features.is_array() ) { + std::cerr << "protocol features not an array" << endl; + return {}; + } + fc::variants& feature_variants = prot_features.get_array(); + + for( auto& feature_v : feature_variants ) { + if( !feature_v.is_object() ) { + std::cerr << "feature_v not an object" << endl; + return {}; + } + fc::variant_object& feature_vo = feature_v.get_object(); + if( !feature_vo.contains( "feature_digest" ) ) { + std::cerr << "feature_digest is missing" << std::endl; + return {}; + } + if( !feature_vo["feature_digest"].is_string() ) { + std::cerr << "feature_digest not a string" << std::endl; + return {}; + } + auto digest = feature_vo["feature_digest"].as_string(); + + if( !feature_vo.contains( "specification" ) ) { + std::cerr << "specification is missing" << std::endl; + return {}; + } + if( !feature_vo["specification"].is_array() ) { + std::cerr << "specification not an array" << std::endl; + return {}; + } + const fc::variants& spec_variants = feature_vo["specification"].get_array(); + if ( spec_variants.size() != 1 ) { + std::cerr << "specification array size " << spec_variants.size() << " not 1 " << std::endl; + return {}; + } + if ( !spec_variants[0].is_object() ) { + std::cerr << "spec_variants[0] not an object" << endl; + return {}; + } + const fc::variant_object& spec_vo = spec_variants[0].get_object(); + if ( !spec_vo.contains( "value" ) ) { + std::cerr << "value is missing" << endl; + return {}; + } + if ( !spec_vo["value"].is_string() ) { + std::cerr << "value not a string" << std::endl; + return {}; + } + auto name = spec_vo["value"].as_string(); + + if ( spec_vo["value"].as_string() == "PREACTIVATE_FEATURE" ) + { + // PREACTIVATE_FEATURE must be activated by schedule_protocol_feature_activations RPC, + // not by activate action + continue; + } + + results.names += name; + results.names += "\n"; + results.digests.emplace(name, digest); + } + + return results; +}; + +struct activate_subcommand { + string feature_name_str; + std::string account_str = "eosio"; + std::string permission_str = "eosio"; + + activate_subcommand(CLI::App* actionRoot) { + auto activate = actionRoot->add_subcommand("activate", localized("Activate protocol feature by name")); + activate->add_option("feature", feature_name_str, localized("The name, can be found from \"cleos get supported_protoctol_features\" command"))->required(); + activate->add_option("-a,--account", account_str, localized("The contract account name, default is eosio")); + activate->add_option("-p,--permission", permission_str, localized("The permission level to authorize, default is eosio")); + activate->fallthrough(false); + + activate->callback([this] { + string action="activate"; + string data; + std::locale loc; + vector permissions = { permission_str }; + for(auto & c : feature_name_str) c = std::toupper(c, loc); + protocol_features_t supported_features; + supported_features = get_supported_protocol_features(); + if( supported_features.digests.find(feature_name_str) != supported_features.digests.end() ){ + std::string digest = supported_features.digests[feature_name_str]; + data = "[\"" + digest + "\"]"; + } else { + std::cerr << feature_name_str << " is unknown. Following protocol features are supported" << std::endl << std::endl; + std::cerr << supported_features.names << std::endl; + return; + } + fc::variant action_args_var; + action_args_var = json_from_file_or_string(data, fc::json::parse_type::relaxed_parser); + auto accountPermissions = get_account_permissions(permissions); + send_actions({chain::action{accountPermissions, name(account_str), name(action), + variant_to_bin( name(account_str), name(action), action_args_var ) }}, signing_keys_opt.get_keys()); + }); + } +}; + void get_account( const string& accountName, const string& coresym, bool json_format ) { fc::variant json; if (coresym.empty()) { @@ -3179,6 +3295,13 @@ int main( int argc, char** argv ) { get_schedule_subcommand{get}; auto getTransactionId = get_transaction_id_subcommand{get}; + // get supported_protocol_features + get->add_subcommand("supported_protocol_features", localized("Get supported protocol features"))->callback([] { + protocol_features_t supported_features; + supported_features = get_supported_protocol_features(); + std::cout << supported_features.names << std::endl; + }); + // set subcommand auto setSubcommand = app.add_subcommand("set", localized("Set or update blockchain state")); setSubcommand->require_subcommand(); @@ -4225,6 +4348,7 @@ int main( int argc, char** argv ) { auto unregProxy = unregproxy_subcommand(system); auto cancelDelay = canceldelay_subcommand(system); + auto activate = activate_subcommand(system); auto rex = system->add_subcommand("rex", localized("Actions related to REX (the resource exchange)")); rex->require_subcommand();