def build_icons(check, remove): """Build FIDO app icons in the source tree.""" checks_ok = True apps = coin_info.fido_info() total_size = 0 for app in apps: if app["icon"] is None: if not app.get("no_icon"): raise click.ClickException(f"Icon not found for: {app['key']}") else: continue im = Image.open(app["icon"]) resized = im.resize(ICON_SIZE, Image.BOX) toi = toif.from_image(resized) dest_path = DESTINATION / f"icon_{app['key']}.toif" total_size += len(toi.to_bytes()) if not check: toi.save(dest_path) else: if not dest_path.exists(): print(f"Missing TOIF: {dest_path}") checks_ok = False continue data = dest_path.read_bytes() if data != toi.to_bytes(): print(f"Icon different from source: {dest_path}") checks_ok = False print(f"Total icon size: {total_size} bytes") keys = EXCLUDE | {"icon_" + app["key"] for app in apps} unrecognized_files = False for icon_file in DESTINATION.glob("*.toif"): name = icon_file.stem if name not in keys: unrecognized_files = True if remove: print(f"Removing unrecognized file: {icon_file}") icon_file.unlink() else: print(f"Unrecognized file: {icon_file}") checks_ok = False if not remove and unrecognized_files: raise click.ClickException( "Unrecognized files found in icon directory.\n" "Use 'build_icons.py -r' to remove them automatically." ) if not checks_ok: raise click.ClickException("Some checks have failed.")
def render(paths, outfile, verbose, bitcoin_only): """Generate source code from Mako templates. For every "foo.bar.mako" filename passed, runs the template and saves the result as "foo.bar". For every directory name passed, processes all ".mako" files found in that directory. If `-o` is specified, renders a single file into the specified outfile. If no arguments are given, processes the current directory. """ if not CAN_RENDER: raise click.ClickException("Please install 'mako' and 'munch'") if outfile and (len(paths) != 1 or not os.path.isfile(paths[0])): raise click.ClickException("Option -o can only be used with single input file") # prepare defs defs = coin_info.coin_info() defs["fido"] = coin_info.fido_info() support_info = coin_info.support_info(defs) if bitcoin_only: defs["bitcoin"] = [ x for x in defs["bitcoin"] if x["coin_name"] in ("Bitcoin", "Testnet", "Regtest") ] # munch dicts - make them attribute-accessible for key, value in defs.items(): defs[key] = [Munch(coin) for coin in value] for key, value in support_info.items(): support_info[key] = Munch(value) def do_render(src, dst): if verbose: click.echo("Rendering {} => {}".format(src, dst)) render_file(src, dst, defs, support_info) # single in-out case if outfile: do_render(paths[0], outfile) return # find files in directories if not paths: paths = ["."] files = [] for path in paths: if not os.path.exists(path): click.echo("Path {} does not exist".format(path)) elif os.path.isdir(path): files += glob.glob(os.path.join(path, "*.mako")) else: files.append(path) # render each file for file in files: if not file.endswith(".mako"): click.echo("File {} does not end with .mako".format(file)) else: target = file[: -len(".mako")] with open(target, "w") as dst: do_render(file, dst)
def check(backend, icons, show_duplicates): """Validate coin definitions. Checks that every btc-like coin is properly filled out, reports duplicate symbols, missing or invalid icons, backend responses, and uniform key information -- i.e., that all coins of the same type have the same fields in their JSON data. Uniformity check ignores NEM mosaics and ERC20 tokens, where non-uniformity is expected. The `--show-duplicates` option can be set to: - all: all shortcut collisions are shown, including colliding ERC20 tokens - nontoken: only collisions that affect non-ERC20 coins are shown - errors: only collisions between non-ERC20 tokens are shown. This is the default, as a collision between two or more non-ERC20 tokens is an error. In the output, duplicate ERC tokens will be shown in cyan; duplicate non-tokens in red. An asterisk (*) next to symbol name means that even though it was detected as duplicate, it is still included in results. The collision detection checks that SLIP44 numbers don't collide between different mainnets (testnet collisions are allowed), that `address_prefix` doesn't collide with Bitcoin (other collisions are reported as warnings). `address_prefix_p2sh` is also checked but we have a bunch of collisions there and can't do much about them, so it's not an error. In the collision checks, Bitcoin is shown in red, other mainnets in blue, testnets in green and unsupported networks in gray, marked with `(X)` for non-colored output. """ if backend and requests is None: raise click.ClickException("You must install requests for backend check") if icons and not CAN_BUILD_DEFS: raise click.ClickException("Missing requirements for icon check") defs, buckets = coin_info.coin_info_with_duplicates() support_info = coin_info.support_info(defs) mark_unsupported(support_info, defs.as_list()) all_checks_passed = True print("Checking BTC-like coins...") if not check_btc(defs.bitcoin): all_checks_passed = False print("Checking Ethereum networks...") if not check_eth(defs.eth): all_checks_passed = False if show_duplicates == "all": dup_level = logging.DEBUG elif show_duplicates == "nontoken": dup_level = logging.INFO else: dup_level = logging.WARNING print("Checking unexpected duplicates...") if not check_dups(buckets, dup_level): all_checks_passed = False nontoken_dups = [coin for coin in defs.as_list() if "dup_key_nontoken" in coin] if nontoken_dups: nontoken_dup_str = ", ".join( highlight_key(coin, "red") for coin in nontoken_dups ) print_log(logging.ERROR, "Non-token duplicate keys: " + nontoken_dup_str) all_checks_passed = False if icons: print("Checking icon files...") if not check_icons(defs.bitcoin): all_checks_passed = False if backend: print("Checking backend responses...") if not check_backends(defs.bitcoin): all_checks_passed = False print("Checking segwit fields...") if not check_segwit(defs.bitcoin): all_checks_passed = False print("Checking key uniformity...") for cointype, coinlist in defs.items(): if cointype in ("erc20", "nem"): continue if not check_key_uniformity(coinlist): all_checks_passed = False print("Checking FIDO app definitions...") if not check_fido(coin_info.fido_info()): all_checks_passed = False if not all_checks_passed: print("Some checks failed.") sys.exit(1) else: print("Everything is OK.")