mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 15:38:11 +00:00
tools: add filtering capabilities to cointool.py dump
This commit is contained in:
parent
6c47e483d4
commit
265935811e
@ -19,7 +19,8 @@ the following commands:
|
|||||||
template. By default, `cointool.py render foo.bar.mako` will put its result into
|
template. By default, `cointool.py render foo.bar.mako` will put its result into
|
||||||
file `foo.bar` in the same directory. See [usage in `trezor-core`](https://github.com/trezor/trezor-core/commit/348b99b8dc5bcfc4ab85e1e7faad3fb4ef3e8763).
|
file `foo.bar` in the same directory. See [usage in `trezor-core`](https://github.com/trezor/trezor-core/commit/348b99b8dc5bcfc4ab85e1e7faad3fb4ef3e8763).
|
||||||
* **`check`**: check validity of json definitions and associated data. Used in CI.
|
* **`check`**: check validity of json definitions and associated data. Used in CI.
|
||||||
* **`dump`**: dump all coin information, including support status, in JSON format.
|
* **`dump`**: dump coin information, including support status, in JSON format. Various
|
||||||
|
filtering options are available, check help for details.
|
||||||
* **`coindefs`**: generate signed protobuf descriptions of coins. This is for future use
|
* **`coindefs`**: generate signed protobuf descriptions of coins. This is for future use
|
||||||
and could allow us to not need to store coin data in Trezor itself.
|
and could allow us to not need to store coin data in Trezor itself.
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import fnmatch
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
@ -556,18 +557,41 @@ def check(backend, icons, show_duplicates):
|
|||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
# fmt: off
|
# fmt: off
|
||||||
@click.option("-o", "--outfile", type=click.File(mode="w"), default="./coins.json")
|
@click.option("-o", "--outfile", type=click.File(mode="w"), default="-")
|
||||||
@click.option("-s/-S", "--support/--no-support", default=True, help="Include support data for each coin")
|
@click.option("-s/-S", "--support/--no-support", default=True, help="Include support data for each coin")
|
||||||
@click.option("-p", "--pretty", is_flag=True, help="Generate nicely formatted JSON")
|
@click.option("-p", "--pretty", is_flag=True, help="Generate nicely formatted JSON")
|
||||||
@click.option("-t", "--tokens", type=click.Choice(["full", "stripped", "none"]), default="full", help="Filter token data")
|
@click.option("-l", "--list", "--flat-list", is_flag=True, help="Output a flat list of coins")
|
||||||
|
@click.option("-i", "--include", metavar="FIELD", multiple=True, help="Include only these fields")
|
||||||
|
@click.option("-e", "--exclude", metavar="FIELD", multiple=True, help="Exclude these fields")
|
||||||
|
@click.option("-I", "--include-type", metavar="TYPE", multiple=True, help="Include only these categories")
|
||||||
|
@click.option("-E", "--exclude-type", metavar="TYPE", multiple=True, help="Exclude these categories")
|
||||||
|
@click.option("-f", "--filter", metavar="FIELD=FILTER", multiple=True, help="Include only coins that match a filter")
|
||||||
|
@click.option("-F", "--filter-exclude", metavar="FIELD=FILTER", multiple=True, help="Exclude coins that match a filter")
|
||||||
|
@click.option("-t", "--exclude-tokens", is_flag=True, help="Exclude ERC20 tokens. Equivalent to '-E erc20'")
|
||||||
|
@click.option("-d", "--device", metavar="NAME", help="Only include coins supported on a given device")
|
||||||
# fmt: on
|
# fmt: on
|
||||||
def dump(outfile, support, pretty, tokens):
|
def dump(
|
||||||
"""Dump all coin data in a single JSON file.
|
outfile,
|
||||||
|
support,
|
||||||
|
pretty,
|
||||||
|
flat_list,
|
||||||
|
include,
|
||||||
|
exclude,
|
||||||
|
include_type,
|
||||||
|
exclude_type,
|
||||||
|
filter,
|
||||||
|
filter_exclude,
|
||||||
|
exclude_tokens,
|
||||||
|
device,
|
||||||
|
):
|
||||||
|
"""Dump coin data in JSON format
|
||||||
|
|
||||||
This file is structured the same as the internal data. That is, top-level object
|
This file is structured the same as the internal data. That is, top-level object
|
||||||
is a dict with keys: 'bitcoin', 'eth', 'erc20', 'nem' and 'misc'. Value for each
|
is a dict with keys: 'bitcoin', 'eth', 'erc20', 'nem' and 'misc'. Value for each
|
||||||
key is a list of dicts, each describing a known coin.
|
key is a list of dicts, each describing a known coin.
|
||||||
|
|
||||||
|
If '--list' is specified, the top-level object is instead a flat list of coins.
|
||||||
|
|
||||||
\b
|
\b
|
||||||
Fields are category-specific, except for four common ones:
|
Fields are category-specific, except for four common ones:
|
||||||
- 'name' - human-readable name
|
- 'name' - human-readable name
|
||||||
@ -576,41 +600,87 @@ def dump(outfile, support, pretty, tokens):
|
|||||||
- 'support' - a dict with entries per known device
|
- 'support' - a dict with entries per known device
|
||||||
|
|
||||||
To control the size and properties of the resulting file, you can specify whether
|
To control the size and properties of the resulting file, you can specify whether
|
||||||
or not you want pretty-printing, whether or not to include support data with
|
or not you want pretty-printing and whether or not to include support data with
|
||||||
each coin, and whether to include information about ERC20 tokens, which takes up
|
each coin.
|
||||||
several hundred kB of space.
|
|
||||||
|
|
||||||
\b
|
You can specify which categories and which fields will be included or excluded.
|
||||||
The option '--tokens' can have one of three values:
|
You cannot specify both include and exclude at the same time. Include is "stronger"
|
||||||
'full': include all token data
|
than exclude, in that _only_ the specified fields are included.
|
||||||
'stripped': exclude 'social' links and 'logo' data from tokens
|
|
||||||
'none': exclude the 'erc20' category altogether.
|
You can also specify filters, in the form '-f field=value' (or '-F' for inverse
|
||||||
|
filter). Filter values are case-insensitive and support shell-style wildcards,
|
||||||
|
so '-f name=bit*' finds all coins whose names start with "bit" or "Bit".
|
||||||
"""
|
"""
|
||||||
|
if exclude_tokens:
|
||||||
|
exclude_type = ("erc20",)
|
||||||
|
|
||||||
|
if include and exclude:
|
||||||
|
raise click.ClickException(
|
||||||
|
"You cannot specify --include and --exclude at the same time."
|
||||||
|
)
|
||||||
|
if include_type and exclude_type:
|
||||||
|
raise click.ClickException(
|
||||||
|
"You cannot specify --include-type and --exclude-type at the same time."
|
||||||
|
)
|
||||||
|
|
||||||
coins = coin_info.coin_info()
|
coins = coin_info.coin_info()
|
||||||
|
support_info = coin_info.support_info(coins.as_list())
|
||||||
|
|
||||||
if support:
|
if support:
|
||||||
support_info = coin_info.support_info(coins.as_list())
|
|
||||||
|
|
||||||
for category in coins.values():
|
for category in coins.values():
|
||||||
for coin in category:
|
for coin in category:
|
||||||
coin["support"] = support_info[coin["key"]]
|
coin["support"] = support_info[coin["key"]]
|
||||||
|
|
||||||
# get rid of address_bytes which are bytes which can't be JSON encoded
|
# filter types
|
||||||
for coin in coins.erc20:
|
if include_type:
|
||||||
coin.pop("address_bytes", None)
|
coins_dict = {k: v for k, v in coins.items() if k in include_type}
|
||||||
if tokens == "stripped":
|
else:
|
||||||
coin.pop("social", None)
|
coins_dict = {k: v for k, v in coins.items() if k not in exclude_type}
|
||||||
coin.pop("logo", None)
|
|
||||||
|
|
||||||
if tokens == "none":
|
# filter individual coins
|
||||||
del coins["erc20"]
|
include_filters = [f.split("=", maxsplit=1) for f in filter]
|
||||||
|
exclude_filters = [f.split("=", maxsplit=1) for f in filter_exclude]
|
||||||
|
|
||||||
|
# always exclude 'address_bytes', not encodable in JSON
|
||||||
|
exclude += ("address_bytes",)
|
||||||
|
|
||||||
|
def should_include_coin(coin):
|
||||||
|
for field, filter in include_filters:
|
||||||
|
filter = filter.lower()
|
||||||
|
if field not in coin:
|
||||||
|
return False
|
||||||
|
if not fnmatch.fnmatch(coin[field].lower(), filter):
|
||||||
|
return False
|
||||||
|
for field, filter in exclude_filters:
|
||||||
|
filter = filter.lower()
|
||||||
|
if field not in coin:
|
||||||
|
continue
|
||||||
|
if fnmatch.fnmatch(coin[field].lower(), filter):
|
||||||
|
return False
|
||||||
|
if device:
|
||||||
|
is_supported = support_info[coin["key"]].get(device, None)
|
||||||
|
if not is_supported:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def modify_coin(coin):
|
||||||
|
if include:
|
||||||
|
return {k: v for k, v in coin.items() if k in include}
|
||||||
|
else:
|
||||||
|
return {k: v for k, v in coin.items() if k not in exclude}
|
||||||
|
|
||||||
|
for key, coinlist in coins_dict.items():
|
||||||
|
coins_dict[key] = [modify_coin(c) for c in coinlist if should_include_coin(c)]
|
||||||
|
|
||||||
|
if flat_list:
|
||||||
|
output = sum(coins_dict.values(), [])
|
||||||
|
else:
|
||||||
|
output = coins_dict
|
||||||
|
|
||||||
with outfile:
|
with outfile:
|
||||||
if pretty:
|
indent = 4 if pretty else None
|
||||||
json.dump(coins, outfile, indent=4, sort_keys=True)
|
json.dump(output, outfile, indent=indent, sort_keys=True)
|
||||||
outfile.write("\n")
|
outfile.write("\n")
|
||||||
else:
|
|
||||||
json.dump(coins, outfile)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
|
Loading…
Reference in New Issue
Block a user