diff --git a/libraries/app/api.cpp b/libraries/app/api.cpp index 1a5ef740..ac16c682 100644 --- a/libraries/app/api.cpp +++ b/libraries/app/api.cpp @@ -141,6 +141,13 @@ namespace graphene { namespace app { { } + fc::variant network_node_api::get_info() const + { + fc::mutable_variant_object result = _app.p2p_node()->network_get_info(); + result["connection_count"] = _app.p2p_node()->get_connection_count(); + return result; + } + void network_node_api::add_node(const fc::ip::endpoint& ep) { _app.p2p_node()->add_node(ep); diff --git a/libraries/app/application.cpp b/libraries/app/application.cpp index e035b94b..12dd27b6 100644 --- a/libraries/app/application.cpp +++ b/libraries/app/application.cpp @@ -283,7 +283,9 @@ namespace detail { graphene::egenesis::compute_egenesis_json( egenesis_json ); FC_ASSERT( egenesis_json != "" ); FC_ASSERT( graphene::egenesis::get_egenesis_json_hash() == fc::sha256::hash( egenesis_json ) ); - return fc::json::from_string( egenesis_json ).as(); + auto genesis = fc::json::from_string( egenesis_json ).as(); + genesis.initial_chain_id = fc::sha256::hash( egenesis_json ); + return genesis; } }; diff --git a/libraries/app/include/graphene/app/api.hpp b/libraries/app/include/graphene/app/api.hpp index 0e637079..2a5827fa 100644 --- a/libraries/app/include/graphene/app/api.hpp +++ b/libraries/app/include/graphene/app/api.hpp @@ -135,6 +135,11 @@ namespace graphene { namespace app { public: network_node_api(application& a); + /** + * @brief Return general network information, such as p2p port + */ + fc::variant get_info() const; + /** * @brief add_node Connect to a new peer * @param ep The IP/Port of the peer to connect to @@ -207,6 +212,7 @@ FC_API(graphene::app::network_broadcast_api, (broadcast_block) ) FC_API(graphene::app::network_node_api, + (get_info) (add_node) (get_connected_peers) ) diff --git a/libraries/chain/db_init.cpp b/libraries/chain/db_init.cpp index a1a1e589..e27add8a 100644 --- a/libraries/chain/db_init.cpp +++ b/libraries/chain/db_init.cpp @@ -487,6 +487,7 @@ void database::init_genesis(const genesis_state_type& genesis_state) bitasset_data_id = create([&](asset_bitasset_data_object& b) { b.options.short_backing_asset = core_asset.id; + b.options.minimum_feeds = GRAPHENE_DEFAULT_MINIMUM_FEEDS; }).id; } diff --git a/libraries/chain/include/graphene/chain/config.hpp b/libraries/chain/include/graphene/chain/config.hpp index 6e2ce4d5..30d3816f 100644 --- a/libraries/chain/include/graphene/chain/config.hpp +++ b/libraries/chain/include/graphene/chain/config.hpp @@ -133,6 +133,8 @@ #define GRAPHENE_DEFAULT_WITNESS_PAY_VESTING_SECONDS (60*60*24) #define GRAPHENE_DEFAULT_WORKER_BUDGET_PER_DAY (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(500) * 1000 ) +#define GRAPHENE_DEFAULT_MINIMUM_FEEDS 7 + #define GRAPHENE_MAX_INTEREST_APR uint16_t( 10000 ) #define GRAPHENE_RECENTLY_MISSED_COUNT_INCREMENT 4 diff --git a/programs/genesis_util/apply_patch.py b/programs/genesis_util/apply_patch.py new file mode 100755 index 00000000..259ee6c9 --- /dev/null +++ b/programs/genesis_util/apply_patch.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def main(): + parser = argparse.ArgumentParser(description="Apply a patch file to a JSON object") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") + parser.add_argument("-d", "--delta", metavar="DELTA", nargs="+", help="list of delta file(s) to apply") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + opts = parser.parse_args() + + if opts.input == "-": + genesis = json.load(sys.stdin) + else: + with open(opts.input, "r") as f: + genesis = json.load(f) + + if opts.delta is None: + opts.delta = [] + for filename in opts.delta: + with open(filename, "r") as f: + patch = json.load(f) + for k, v in patch.get("append", {}).items(): + genesis[k].extend(v) + sys.stderr.write("appended {n} items to {k}\n".format(n=len(v), k=k)) + for k, v in patch.get("replace", {}).items(): + genesis[k] = v + sys.stderr.write("replaced item {k}\n".format(k=k)) + + if opts.output == "-": + dump_json( genesis, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( genesis, f, opts.pretty ) + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/canonical_format.py b/programs/genesis_util/canonical_format.py new file mode 100755 index 00000000..82850136 --- /dev/null +++ b/programs/genesis_util/canonical_format.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +if len(sys.argv) < 3: + print("syntax: "+sys.argv[0]+" INFILE OUTFILE") + sys.exit(0) + +with open(sys.argv[1], "r") as infile: + genesis = json.load(infile) +with open(sys.argv[2], "w") as outfile: + json.dump(genesis, outfile, separators=(',', ':'), sort_keys=True) diff --git a/programs/genesis_util/change_bitasset_owners.py b/programs/genesis_util/change_bitasset_owners.py new file mode 100755 index 00000000..94aa0057 --- /dev/null +++ b/programs/genesis_util/change_bitasset_owners.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def main(): + parser = argparse.ArgumentParser(description="Change initial_assets owned by the witness-account to the committee-account") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + opts = parser.parse_args() + + if opts.input == "-": + genesis = json.load(sys.stdin) + else: + with open(opts.input, "r") as f: + genesis = json.load(f) + + for asset in genesis["initial_assets"]: + if asset["issuer_name"] == "witness-account": + asset["issuer_name"] = "committee-account" + + if opts.output == "-": + dump_json( genesis, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( genesis, f, opts.pretty ) + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/generate_init_patch.py b/programs/genesis_util/generate_init_patch.py new file mode 100755 index 00000000..25b68579 --- /dev/null +++ b/programs/genesis_util/generate_init_patch.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +import argparse +import json +import subprocess +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def main(): + parser = argparse.ArgumentParser(description="Generate a patch file that adds init accounts") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-n", "--num", metavar="N", default=11, type=int, help="number of init witnesses") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + parser.add_argument("-s", "--secret", metavar="SECRET", default=None, help="private key generation secret") + opts = parser.parse_args() + + if opts.secret is None: + sys.stderr.write("missing required parameter --secret\n") + sys.stderr.flush() + sys.exit(1) + + wit_accounts = [] + wit_wits = [] + committee = [] + + for i in range(opts.num): + owner_str = subprocess.check_output(["programs/genesis_util/get_dev_key", opts.secret, "wit-owner-"+str(i)]).decode("utf-8") + active_str = subprocess.check_output(["programs/genesis_util/get_dev_key", opts.secret, "wit-active-"+str(i)]).decode("utf-8") + prod_str = subprocess.check_output(["programs/genesis_util/get_dev_key", opts.secret, "wit-block-signing-"+str(i)]).decode("utf-8") + owner = json.loads(owner_str) + active = json.loads(active_str) + prod = json.loads(prod_str) + wit_accounts.append({ + "name" : "init"+str(i), + "owner_key" : owner[0]["public_key"], + "active_key" : active[0]["public_key"], + "is_lifetime_member" : True, + }) + wit_wits.append({ + "owner_name" : "init"+str(i), + "block_signing_key" : prod[0]["public_key"], + }) + committee.append({"owner_name" : "init"+str(i)}) + result = { + "append" : { + "initial_accounts" : wit_accounts }, + "replace" : { + "initial_active_witnesses" : opts.num, + "initial_worker_candidates" : [], + "initial_witness_candidates" : wit_wits, + "initial_committee_candidates" : committee, + } + } + + if opts.output == "-": + dump_json( result, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( result, f, opts.pretty ) + + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/prefix_accounts.py b/programs/genesis_util/prefix_accounts.py new file mode 100755 index 00000000..ee5820a7 --- /dev/null +++ b/programs/genesis_util/prefix_accounts.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def main(): + parser = argparse.ArgumentParser(description="Add a prefix to selected account names") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") + parser.add_argument("-b", "--begin", metavar="PREFIX", default="", help="prefix to add") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + opts = parser.parse_args() + + if opts.input == "-": + genesis = json.load(sys.stdin) + else: + with open(opts.input, "r") as f: + genesis = json.load(f) + + taken_names = set() + unsettled_names = [] + name_map = {} + prefix = opts.begin + + for account in genesis["initial_accounts"]: + is_prefixed = account["is_prefixed"] + name = account["name"] + if is_prefixed: + unsettled_names.append(name) + name_map[name] = name + else: + taken_names.add(name) + + pass_num = 0 + while len(unsettled_names) > 0: + num_resolved = 0 + pass_num += 1 + sys.stderr.write("attempting to resolve {n} names\n".format(n=len(unsettled_names))) + if pass_num > 1: + sys.stderr.write("names: {}\n".format("\n".join(unsettled_names))) + new_unsettled_names = [] + for name in unsettled_names: + new_name = prefix+name_map[name] + name_map[name] = new_name + if new_name in taken_names: + new_unsettled_names.append(name) + else: + taken_names.add(name) + num_resolved += 1 + sys.stderr.write("resolved {n} names\n".format(n=num_resolved)) + unsettled_names = new_unsettled_names + + for account in genesis["initial_accounts"]: + name = account["name"] + account["name"] = name_map.get(name, name) + del account["is_prefixed"] + for asset in genesis["initial_assets"]: + issuer_name = asset["issuer_name"] + asset["issuer_name"] = name_map.get(issuer_name, issuer_name) + for witness in genesis["initial_witness_candidates"]: + owner_name = witness["owner_name"] + witness["owner_name"] = name_map.get(owner_name, owner_name) + for committee in genesis["initial_committee_candidates"]: + owner_name = member["owner_name"] + member["owner_name"] = name_map.get(owner_name, owner_name) + for worker in genesis["initial_worker_candidates"]: + owner_name = worker["owner_name"] + worker["owner_name"] = name_map.get(owner_name, owner_name) + + if opts.output == "-": + dump_json( genesis, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( genesis, f, opts.pretty ) + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/sort_objects.py b/programs/genesis_util/sort_objects.py new file mode 100755 index 00000000..82401277 --- /dev/null +++ b/programs/genesis_util/sort_objects.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def main(): + parser = argparse.ArgumentParser(description="Sort initial_accounts and initial_assets by \"id\" member, then remove \"id\" member") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + opts = parser.parse_args() + + if opts.input == "-": + genesis = json.load(sys.stdin) + else: + with open(opts.input, "r") as f: + genesis = json.load(f) + + genesis["initial_assets"].sort( key=lambda e : e["id"] ) + genesis["initial_accounts"].sort( key=lambda e : e["id"] ) + + for e in genesis["initial_assets"]: + del e["id"] + for e in genesis["initial_accounts"]: + del e["id"] + + if opts.output == "-": + dump_json( genesis, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( genesis, f, opts.pretty ) + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/unprefix_asset_owners.py b/programs/genesis_util/unprefix_asset_owners.py new file mode 100755 index 00000000..81935390 --- /dev/null +++ b/programs/genesis_util/unprefix_asset_owners.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def main(): + parser = argparse.ArgumentParser(description="Set is_prefixed=false for all asset owners") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + opts = parser.parse_args() + + if opts.input == "-": + genesis = json.load(sys.stdin) + else: + with open(opts.input, "r") as f: + genesis = json.load(f) + + asset_owners = set() + for asset in genesis["initial_assets"]: + asset_owners.add(asset["issuer_name"]) + for account in genesis["initial_accounts"]: + if account["name"] in asset_owners: + account["is_prefixed"] = False + + if opts.output == "-": + dump_json( genesis, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( genesis, f, opts.pretty ) + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/unprefix_names.py b/programs/genesis_util/unprefix_names.py new file mode 100755 index 00000000..0c7341fa --- /dev/null +++ b/programs/genesis_util/unprefix_names.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +import argparse +import json +import sys + +def dump_json(obj, out, pretty): + if pretty: + json.dump(obj, out, indent=2, sort_keys=True) + else: + json.dump(obj, out, separators=(",", ":"), sort_keys=True) + return + +def load_names(infile): + names = set() + for line in infile: + if '#' in line: + line = line[:line.index('#')] + line = line.strip() + if line == "": + continue + names.add(line) + return names + +def main(): + parser = argparse.ArgumentParser(description="Set is_prefixed=False for a list of names") + parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") + parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") + parser.add_argument("-n", "--names", metavar="NAMES", help="list of names to unprefix") + parser.add_argument("-p", "--pretty", action="store_true", default=False, help="pretty print output") + opts = parser.parse_args() + + if opts.input == "-": + genesis = json.load(sys.stdin) + else: + with open(opts.input, "r") as f: + genesis = json.load(f) + + if opts.names == "-": + names = load_names(sys.stdin) + else: + with open(opts.names, "r") as f: + names = load_names(f) + + for account in genesis["initial_accounts"]: + if account["name"] in names: + account["is_prefixed"] = False + + if opts.output == "-": + dump_json( genesis, sys.stdout, opts.pretty ) + sys.stdout.flush() + else: + with open(opts.output, "w") as f: + dump_json( genesis, f, opts.pretty ) + return + +if __name__ == "__main__": + main() diff --git a/programs/genesis_util/upgrade_members.py b/programs/genesis_util/upgrade_members.py index 3ca42258..83b74234 100755 --- a/programs/genesis_util/upgrade_members.py +++ b/programs/genesis_util/upgrade_members.py @@ -26,7 +26,7 @@ def load_names(infile): return names def main(): - parser = argparse.ArgumentParser(description="Remove entities from snapshot") + parser = argparse.ArgumentParser(description="Upgrade a list of members") parser.add_argument("-o", "--output", metavar="OUT", default="-", help="output filename (default: stdout)") parser.add_argument("-i", "--input", metavar="IN", default="-", help="input filename (default: stdin)") parser.add_argument("-n", "--names", metavar="NAMES", default="", help="file containing names to upgrade")