Merge pull request #3256 from derekpierre/update-status

Update `nucypher status` command to now be `nucypher taco`
pull/3259/head dkg-dev-14
LunarBytes 2023-09-28 19:23:21 +02:00 committed by GitHub
commit 6ea73db2c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 307 additions and 273 deletions

View File

View File

@ -888,19 +888,20 @@ class ContractAgency:
return agent_name
@classmethod
def get_agent_by_contract_name(cls,
contract_name: str,
registry: BaseContractRegistry,
eth_provider_uri: Optional[str] = None,
contract_version: Optional[str] = None
) -> EthereumContractAgent:
def get_agent_by_contract_name(
cls,
contract_name: str,
registry: BaseContractRegistry,
provider_uri: str,
contract_version: Optional[str] = None,
) -> EthereumContractAgent:
agent_name: str = cls._contract_name_to_agent_name(name=contract_name)
agents_module = sys.modules[__name__]
agent_class: Type[EthereumContractAgent] = getattr(agents_module, agent_name)
agent: EthereumContractAgent = cls.get_agent(
agent_class=agent_class,
registry=registry,
provider_uri=eth_provider_uri,
provider_uri=provider_uri,
contract_version=contract_version
)
return agent

View File

@ -4,17 +4,22 @@
# Contract Names
#
# Legacy
NUCYPHER_TOKEN_CONTRACT_NAME = "NuCypherToken"
STAKING_ESCROW_CONTRACT_NAME = "StakingEscrow"
STAKING_ESCROW_STUB_CONTRACT_NAME = "StakingEscrowStub"
# TACo
TACO_APPLICATION_CONTRACT_NAME = "TACoApplication"
TACO_CHILD_APPLICATION_CONTRACT_NAME = "TACoChildApplication"
COORDINATOR_CONTRACT_NAME = "Coordinator"
SUBSCRIPTION_MANAGER_CONTRACT_NAME = "SubscriptionManager"
NUCYPHER_CONTRACT_NAMES = (
NUCYPHER_TOKEN_CONTRACT_NAME,
STAKING_ESCROW_CONTRACT_NAME,
TACO_CONTRACT_NAMES = (
TACO_APPLICATION_CONTRACT_NAME,
TACO_CHILD_APPLICATION_CONTRACT_NAME,
COORDINATOR_CONTRACT_NAME,
SUBSCRIPTION_MANAGER_CONTRACT_NAME
)

View File

@ -1,233 +0,0 @@
import os
from pathlib import Path
import click
from nucypher.blockchain.eth.agents import (
ContractAgency,
NucypherTokenAgent,
TACoApplicationAgent,
)
from nucypher.blockchain.eth.constants import AVERAGE_BLOCK_TIME_IN_SECONDS
from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.cli.config import group_general_config
from nucypher.cli.options import (
group_options,
option_contract_name,
option_eth_provider_uri,
option_event_name,
option_light,
option_network,
option_poa,
option_registry_filepath,
option_staking_provider,
)
from nucypher.cli.painting.status import paint_contract_status
from nucypher.cli.utils import (
connect_to_blockchain,
get_registry,
parse_event_filters_into_argument_filters,
retrieve_events,
setup_emitter,
)
from nucypher.config.constants import NUCYPHER_ENVVAR_ETH_PROVIDER_URI
from nucypher.utilities.events import generate_events_csv_filepath
STAKING_ESCROW = 'StakingEscrow'
POLICY_MANAGER = 'PolicyManager'
CONTRACT_NAMES = [
TACoApplicationAgent.contract_name,
NucypherTokenAgent.contract_name,
STAKING_ESCROW,
POLICY_MANAGER
]
class RegistryOptions:
__option_name__ = 'registry_options'
def __init__(self, eth_provider_uri, poa, registry_filepath, light, network):
self.eth_provider_uri = eth_provider_uri
self.poa = poa
self.registry_filepath = registry_filepath
self.light = light
self.network = network
def setup(self, general_config) -> tuple:
emitter = setup_emitter(general_config)
registry = get_registry(network=self.network, registry_filepath=self.registry_filepath)
blockchain = connect_to_blockchain(emitter=emitter, eth_provider_uri=self.eth_provider_uri)
return emitter, registry, blockchain
group_registry_options = group_options(
RegistryOptions,
poa=option_poa,
light=option_light,
registry_filepath=option_registry_filepath,
network=option_network(default=NetworksInventory.DEFAULT, validate=True), # TODO: See 2214
eth_provider_uri=option_eth_provider_uri(default=os.environ.get(NUCYPHER_ENVVAR_ETH_PROVIDER_URI)),
)
option_csv = click.option('--csv',
help="Write event data to a CSV file using a default filename in the current directory",
default=False,
is_flag=True)
option_csv_file = click.option('--csv-file',
help="Write event data to the CSV file at specified filepath",
type=click.Path(dir_okay=False, path_type=Path))
option_event_filters = click.option('--event-filter', '-f', 'event_filters',
help="Event filter of the form <name>=<value>",
multiple=True,
type=click.STRING,
default=[])
option_from_block = click.option(
"--from-block",
help="Collect events from this block number; defaults to the block number from ~24 hours ago",
type=click.INT,
)
option_to_block = click.option(
"--to-block",
help="Collect events until this block number; defaults to 'latest' block number",
type=click.INT,
)
@click.group()
def status():
"""Echo a snapshot of live NuCypher Network metadata."""
@status.command()
@group_registry_options
@group_general_config
def network(general_config, registry_options):
"""Overall information of the NuCypher Network."""
emitter, registry, blockchain = registry_options.setup(general_config=general_config)
paint_contract_status(registry, emitter=emitter)
@status.command("taco")
@group_registry_options
@option_staking_provider
@group_general_config
def staking_providers(general_config, registry_options, staking_provider_address):
"""Show relevant information about staking providers."""
emitter, registry, blockchain = registry_options.setup(
general_config=general_config
)
application_agent = ContractAgency.get_agent(
TACoApplicationAgent, registry=registry
)
staking_providers_list = (
[staking_provider_address]
if staking_provider_address
else application_agent.get_staking_providers()
)
emitter.echo(staking_providers_list) # TODO: staking provider painter
# paint_stakers(emitter=emitter, stakers=staking_providers_list, registry=registry)
@status.command()
@group_registry_options
@group_general_config
@option_contract_name(required=False, valid_options=CONTRACT_NAMES)
@option_event_name
@option_from_block
@option_to_block
@option_csv
@option_csv_file
@option_event_filters
def events(
general_config,
registry_options,
contract_name,
from_block,
to_block,
event_name,
csv,
csv_file,
event_filters,
):
"""Show events associated with NuCypher contracts."""
if csv or csv_file:
if csv and csv_file:
raise click.BadOptionUsage(option_name='--event-filter',
message=click.style('Pass either --csv or --csv-file, not both.', fg="red"))
# ensure that event name is specified - different events would have different columns in the csv file
if csv_file and not all((event_name, contract_name)):
# TODO consider a single csv that just gets appended to for each event
# - each appended event adds their column names first
# - single report-type functionality, see #2561
raise click.BadOptionUsage(option_name='--csv-file, --event-name, --contract_name',
message=click.style('--event-name and --contract-name must be specified when outputting to '
'specific file using --csv-file; alternatively use --csv', fg="red"))
if not contract_name:
if event_name:
raise click.BadOptionUsage(option_name='--event-name', message=click.style('--event-name requires --contract-name', fg="red"))
# FIXME should we force a contract name to be specified?
# default to TACoApplication contract
contract_names = [TACoApplicationAgent.contract_name]
else:
contract_names = [contract_name]
emitter, registry, blockchain = registry_options.setup(general_config=general_config)
if from_block is None:
# by default, this command only shows events of the last 24 hours
blocks_since_yesterday_kinda = ((60*60*24)//AVERAGE_BLOCK_TIME_IN_SECONDS)
from_block = blockchain.client.block_number - blocks_since_yesterday_kinda
if to_block is None:
to_block = 'latest'
else:
# validate block range
if from_block > to_block:
raise click.BadOptionUsage(option_name='--to-block, --from-block',
message=click.style(f'Invalid block range provided, '
f'from-block ({from_block}) > to-block ({to_block})', fg="red"))
# event argument filters
argument_filters = None
if event_filters:
try:
argument_filters = parse_event_filters_into_argument_filters(event_filters)
except ValueError as e:
raise click.BadOptionUsage(option_name='--event-filter',
message=click.style(f'Event filter must be specified as name-value pairs of '
f'the form `<name>=<value>` - {str(e)}', fg="red"))
emitter.echo(f"Retrieving events from block {from_block} to {to_block}")
for contract_name in contract_names:
agent = ContractAgency.get_agent_by_contract_name(
contract_name=contract_name, registry=registry
)
if event_name and event_name not in agent.events.names:
raise click.BadOptionUsage(option_name='--event-name, --contract_name',
message=click.style(f'{contract_name} contract does not have an event named {event_name}', fg="red"))
title = f" {agent.contract_name} Events ".center(40, "-")
emitter.echo(f"\n{title}\n", bold=True, color='green')
names = agent.events.names if not event_name else [event_name]
for name in names:
# csv output file - one per (contract_name, event_name) pair
csv_output_file = csv_file
if csv or csv_output_file:
if not csv_output_file:
csv_output_file = generate_events_csv_filepath(contract_name=agent.contract_name, event_name=name)
retrieve_events(emitter=emitter,
agent=agent,
event_name=name, # None is fine - just means all events
from_block=from_block,
to_block=to_block,
argument_filters=argument_filters,
csv_output_file=csv_output_file)

View File

@ -0,0 +1,271 @@
from pathlib import Path
import click
from web3 import Web3
from nucypher.blockchain.eth.agents import (
ContractAgency,
TACoApplicationAgent,
)
from nucypher.blockchain.eth.constants import (
AVERAGE_BLOCK_TIME_IN_SECONDS,
TACO_CONTRACT_NAMES,
)
from nucypher.blockchain.eth.networks import NetworksInventory
from nucypher.cli.config import group_general_config
from nucypher.cli.options import (
group_options,
option_contract_name,
option_event_name,
option_light,
option_poa,
option_registry_filepath,
)
from nucypher.cli.painting.status import paint_application_contract_status
from nucypher.cli.utils import (
get_registry,
parse_event_filters_into_argument_filters,
retrieve_events,
setup_emitter,
)
from nucypher.utilities.events import generate_events_csv_filepath
option_provider_uri = click.option(
"--provider-uri",
"provider_uri",
help="Blockchain provider's URI i.e. 'file:///path/to/geth.ipc'",
type=click.STRING,
required=True,
)
option_network = click.option(
"--network",
help="TACo Network",
type=click.STRING,
default=click.Choice(NetworksInventory.NETWORKS),
required=True,
)
class RegistryOptions:
__option_name__ = "registry_options"
def __init__(self, provider_uri, poa, registry_filepath, light, network):
self.provider_uri = provider_uri
self.poa = poa
self.registry_filepath = registry_filepath
self.light = light
self.network = network
def setup(self, general_config) -> tuple:
emitter = setup_emitter(general_config)
registry = get_registry(
network=self.network, registry_filepath=self.registry_filepath
)
return emitter, registry, self.provider_uri
group_registry_options = group_options(
RegistryOptions,
poa=option_poa,
light=option_light,
registry_filepath=option_registry_filepath,
network=option_network,
provider_uri=option_provider_uri,
)
option_csv = click.option(
"--csv",
help="Write event data to a CSV file using a default filename in the current directory",
default=False,
is_flag=True,
)
option_csv_file = click.option(
"--csv-file",
help="Write event data to the CSV file at specified filepath",
type=click.Path(dir_okay=False, path_type=Path),
)
option_event_filters = click.option(
"--event-filter",
"-f",
"event_filters",
help="Event filter of the form <name>=<value>",
multiple=True,
type=click.STRING,
default=[],
)
option_from_block = click.option(
"--from-block",
help="Collect events from this block number; defaults to the block number from ~24 hours ago",
type=click.INT,
)
option_to_block = click.option(
"--to-block",
help="Collect events until this block number; defaults to 'latest' block number",
type=click.INT,
)
@click.group()
def taco():
"""Provide snapshot information about the TACo Application on Threshold Network."""
@taco.command()
@group_registry_options
@group_general_config
def application_info(general_config, registry_options):
"""Overall information for the TACo Application."""
emitter, registry, provider_uri = registry_options.setup(
general_config=general_config
)
paint_application_contract_status(
emitter=emitter, registry=registry, provider_uri=provider_uri
)
@taco.command()
@group_registry_options
@group_general_config
def active_providers(general_config, registry_options):
"""List of active stakers for the TACo Application"""
emitter, registry, provider_uri = registry_options.setup(
general_config=general_config
)
application_agent = ContractAgency.get_agent(
TACoApplicationAgent, registry=registry, provider_uri=provider_uri
)
(
total_staked,
staking_providers,
) = application_agent.get_all_active_staking_providers()
emitter.echo(
f"Total Active Stakes ............... {Web3.from_wei(total_staked, 'ether'):,}"
)
emitter.echo(f"Active Staking Providers .......... {len(staking_providers)}")
for provider, staked in staking_providers.items():
emitter.echo(f"\t{provider} ..... {Web3.from_wei(staked, 'ether'):,}")
@taco.command()
@group_registry_options
@group_general_config
@option_contract_name(required=True, valid_options=TACO_CONTRACT_NAMES)
@option_event_name
@option_from_block
@option_to_block
@option_csv
@option_csv_file
@option_event_filters
def events(
general_config,
registry_options,
contract_name,
from_block,
to_block,
event_name,
csv,
csv_file,
event_filters,
):
"""Show events associated with TACo Application contracts."""
if csv or csv_file:
if csv and csv_file:
raise click.BadOptionUsage(
option_name="--event-filter",
message=click.style(
"Pass either --csv or --csv-file, not both.", fg="red"
),
)
# ensure that event name is specified - different events would have different columns in the csv file
if csv_file and not all((event_name, contract_name)):
# TODO consider a single csv that just gets appended to for each event
# - each appended event adds their column names first
# - single report-type functionality, see #2561
raise click.BadOptionUsage(
option_name="--csv-file, --event-name, --contract_name",
message=click.style(
"--event-name and --contract-name must be specified when outputting to "
"specific file using --csv-file; alternatively use --csv",
fg="red",
),
)
emitter, registry, provider_uri = registry_options.setup(
general_config=general_config
)
contract_agent = ContractAgency.get_agent_by_contract_name(
contract_name=contract_name, registry=registry, provider_uri=provider_uri
)
if from_block is None:
# by default, this command only shows events of the last 24 hours
blocks_since_yesterday_kinda = (60 * 60 * 24) // AVERAGE_BLOCK_TIME_IN_SECONDS
from_block = (
contract_agent.blockchain.client.block_number - blocks_since_yesterday_kinda
)
if to_block is None:
to_block = "latest"
else:
# validate block range
if from_block > to_block:
raise click.BadOptionUsage(
option_name="--to-block, --from-block",
message=click.style(
f"Invalid block range provided, "
f"from-block ({from_block}) > to-block ({to_block})",
fg="red",
),
)
# event argument filters
argument_filters = None
if event_filters:
try:
argument_filters = parse_event_filters_into_argument_filters(event_filters)
except ValueError as e:
raise click.BadOptionUsage(
option_name="--event-filter",
message=click.style(
f"Event filter must be specified as name-value pairs of "
f"the form `<name>=<value>` - {str(e)}",
fg="red",
),
)
emitter.echo(f"Retrieving events from block {from_block} to {to_block}")
if event_name and event_name not in contract_agent.events.names:
raise click.BadOptionUsage(
option_name="--event-name, --contract_name",
message=click.style(
f"{contract_name} contract does not have an event named {event_name}",
fg="red",
),
)
title = f" {contract_agent.contract_name} Events ".center(40, "-")
emitter.echo(f"\n{title}\n", bold=True, color="green")
names = contract_agent.events.names if not event_name else [event_name]
for name in names:
# csv output file - one per (contract_name, event_name) pair
csv_output_file = csv_file
if csv or csv_output_file:
if not csv_output_file:
csv_output_file = generate_events_csv_filepath(
contract_name=contract_name, event_name=name
)
retrieve_events(
emitter=emitter,
agent=contract_agent,
event_name=name, # None is fine - just means all events
from_block=from_block,
to_block=to_block,
argument_filters=argument_filters,
csv_output_file=csv_output_file,
)

View File

@ -1,6 +1,6 @@
import click
from nucypher.cli.commands import ursula
from nucypher.cli.commands import taco, ursula
from nucypher.cli.painting.help import (
echo_config_root_path,
echo_logging_root_path,
@ -25,6 +25,7 @@ def nucypher_cli():
ENTRY_POINTS = (
ursula.ursula,
taco.taco,
# add more entry points here
)

View File

@ -5,7 +5,7 @@ from typing import Sequence
import click
from nucypher.blockchain.eth.constants import NUCYPHER_CONTRACT_NAMES
from nucypher.blockchain.eth.constants import TACO_CONTRACT_NAMES
from nucypher.cli.types import (
EIP55_CHECKSUM_ADDRESS,
EXISTING_READABLE_FILE,
@ -144,8 +144,9 @@ def option_alice_verifying_key(required: bool = False):
required=required)
def option_contract_name(required: bool = False,
valid_options: Sequence[str] = NUCYPHER_CONTRACT_NAMES):
def option_contract_name(
required: bool = False, valid_options: Sequence[str] = TACO_CONTRACT_NAMES
):
return click.option(
'--contract-name',
help="Specify a single contract by name",

View File

@ -1,41 +1,27 @@
from web3.main import Web3
from nucypher.blockchain.eth.agents import (
ContractAgency,
TACoApplicationAgent,
)
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
def paint_contract_status(registry, emitter):
blockchain = BlockchainInterfaceFactory.get_interface()
def paint_application_contract_status(emitter, registry, provider_uri):
application_agent = ContractAgency.get_agent(
TACoApplicationAgent, registry=registry
TACoApplicationAgent, registry=registry, provider_uri=provider_uri
)
contracts = f"""
| Contract Deployments |
{application_agent.contract_name} .............. {application_agent.contract_address}
"""
blockchain = application_agent.blockchain
blockchain = f"""
| '{blockchain.client.chain_name}' Blockchain Network |
Gas Price ................ {Web3.from_wei(blockchain.client.gas_price, 'gwei')} Gwei
ETH Provider URI ......... {blockchain.eth_provider_uri}
Registry ................. {registry.filepath}
contracts = f"""
Contract Deployment
===================
Blockchain ........................ {blockchain.client.chain_name}
TACoApplication Contract........... {application_agent.contract_address}
"""
staking = f"""
| TACoApplication |
TACoApplication
================
Staking Provider Population ....... {application_agent.get_staking_providers_population()}
"""
sep = '-' * 45
emitter.echo(sep)
emitter.echo(contracts)
emitter.echo(sep)
emitter.echo(blockchain)
emitter.echo(sep)
emitter.echo(staking)
emitter.echo(sep)

View File

@ -242,7 +242,9 @@ def retrieve_events(emitter: StdoutEmitter,
emitter.echo(f'No {agent.contract_name}::{event_name} events found', color='yellow')
else:
event = agent.contract.events[event_name]
emitter.echo(f"{event_name}:", bold=True, color='yellow')
entries = event.getLogs(fromBlock=from_block, toBlock=to_block, argument_filters=argument_filters)
emitter.echo(f"{event_name}:", bold=True, color="yellow")
entries = event.get_logs(
fromBlock=from_block, toBlock=to_block, argument_filters=argument_filters
)
for event_record in entries:
emitter.echo(f" - {EventRecord(event_record)}")