Eager pre-flight checks for deployer upgrade CLI.

pull/2315/head
Kieran Prasch 2020-09-29 13:12:15 -07:00
parent ea3584656c
commit 79c360a5b9
7 changed files with 71 additions and 21 deletions

View File

@ -19,6 +19,7 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
from collections import OrderedDict
from constant_sorrow.constants import (BARE, CONTRACT_NOT_DEPLOYED, FULL, IDLE, NO_BENEFICIARY, NO_DEPLOYER_CONFIGURED)
from eth_typing.evm import ChecksumAddress
from typing import Dict, List, Tuple
from web3 import Web3
from web3.contract import Contract
@ -190,6 +191,13 @@ class OwnableContractMixin:
class ContractNotOwnable(RuntimeError):
pass
@property
def owner(self) -> ChecksumAddress:
if self._upgradeable:
proxy_deployer = self.get_proxy_deployer()
owner_address = ChecksumAddress(proxy_deployer.contract.functions.owner().call())
return owner_address
def transfer_ownership(self, new_owner: str, transaction_gas_limit: int = None) -> dict:
if not self._ownable:
raise self.ContractNotOwnable(f"{self.contract_name} is not ownable.")

View File

@ -16,14 +16,13 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import json
import os
from typing import Tuple
import click
import os
from constant_sorrow import constants
from constant_sorrow.constants import FULL
from nucypher.blockchain.eth.signers.base import Signer
from nucypher.blockchain.eth.signers.software import ClefSigner
from eth_utils.address import to_checksum_address
from typing import Tuple
from nucypher.blockchain.eth.actors import ContractAdministrator, Trustee
from nucypher.blockchain.eth.agents import ContractAgency, MultiSigAgent, NucypherTokenAgent
@ -36,6 +35,8 @@ from nucypher.blockchain.eth.registry import (
InMemoryContractRegistry,
RegistrySourceManager
)
from nucypher.blockchain.eth.signers.base import Signer
from nucypher.blockchain.eth.signers.software import ClefSigner
from nucypher.blockchain.eth.token import NU
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.cli.actions.auth import get_client_password
@ -74,7 +75,8 @@ from nucypher.cli.literature import (
SUCCESSFUL_SAVE_DEPLOY_RECEIPTS,
SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL,
SUCCESSFUL_UPGRADE,
UNKNOWN_CONTRACT_NAME
UNKNOWN_CONTRACT_NAME,
IDENTICAL_REGISTRY_WARNING
)
from nucypher.cli.options import (
group_options,
@ -227,7 +229,7 @@ group_actor_options = group_options(
provider_uri=option_provider_uri(),
gas_strategy=option_gas_strategy,
signer_uri=option_signer_uri,
contract_name=option_contract_name,
contract_name=option_contract_name(required=True),
poa=option_poa,
force=option_force,
hw_wallet=option_hw_wallet,
@ -301,11 +303,11 @@ def inspect(general_config, provider_uri, config_root, registry_infile, deployer
provider_uri=provider_uri,
emitter=emitter,
ignore_solidity_check=ignore_solidity_check)
local_registry = establish_deployer_registry(emitter=emitter,
registry_infile=registry_infile,
download_registry=not bool(registry_infile))
registry = establish_deployer_registry(emitter=emitter,
registry_infile=registry_infile,
download_registry=not bool(registry_infile))
paint_deployer_contract_inspection(emitter=emitter,
registry=local_registry,
registry=registry,
deployer_address=deployer_address)
@ -314,21 +316,46 @@ def inspect(general_config, provider_uri, config_root, registry_infile, deployer
@group_actor_options
@option_target_address
@option_ignore_deployed
@option_confirmations
@click.option('--retarget', '-d', help="Retarget a contract's proxy.", is_flag=True)
@click.option('--multisig', help="Build raw transaction for upgrade via MultiSig ", is_flag=True)
def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig):
"""
Upgrade NuCypher existing proxy contract deployments.
"""
# Init
emitter = general_config.emitter
def upgrade(general_config, actor_options, retarget, target_address, ignore_deployed, multisig, confirmations):
"""Upgrade NuCypher existing proxy contract deployments."""
ADMINISTRATOR, _, _, registry = actor_options.create_actor(emitter, is_multisig=bool(multisig)) # FIXME: Workaround for building MultiSig TXs
# Setup
emitter = general_config.emitter
ADMINISTRATOR, deployer_address, blockchain, local_registry = actor_options.create_actor(emitter, is_multisig=bool(multisig)) # FIXME: Workaround for building MultiSig TXs | NRN
#
# Pre-flight
#
contract_name = actor_options.contract_name
# Check deployer address is owner
agent = ContractAgency.get_agent_by_contract_name(contract_name=contract_name, registry=local_registry)
dispatcher = agent.get_proxy_contract() # Implicit verification of updatability
dispatcher_owner = dispatcher.owner # contract read
if deployer_address != dispatcher_owner:
DEPLOYER_IS_NOT_OWNER = f"Address {deployer_address} is not the owner of {contract_name}'s" \
f" Dispatcher ({dispatcher.address}). Aborting." # TODO: Move to literature.py
emitter.echo(DEPLOYER_IS_NOT_OWNER)
raise click.Abort()
# Check registry ID has changed locally compared to remote source
github_registry = establish_deployer_registry(emitter=emitter, download_registry=True)
if github_registry.id == local_registry.id:
emitter.echo(IDENTICAL_REGISTRY_WARNING.format(github_registry=github_registry,
local_registry=local_registry), color='red')
raise click.Abort()
if not contract_name:
raise click.BadArgumentUsage(message="--contract-name is required when using --upgrade")
#
# Business
#
if multisig:
if not target_address:
raise click.BadArgumentUsage(message="--multisig requires using --target-address.")
@ -349,7 +376,7 @@ def upgrade(general_config, actor_options, retarget, target_address, ignore_depl
if not actor_options.force:
click.confirm(CONFIRM_SELECTED_ACCOUNT.format(address=trustee_address), abort=True)
trustee = Trustee(registry=registry, checksum_address=trustee_address)
trustee = Trustee(registry=local_registry, checksum_address=trustee_address)
transaction_proposal = trustee.create_transaction_proposal(transaction)
message = SUCCESSFUL_RETARGET_TX_BUILT.format(contract_name=contract_name, target_address=target_address)

View File

@ -115,7 +115,7 @@ def locked_tokens(general_config, registry_options, periods):
@status.command()
@group_registry_options
@group_general_config
@option_contract_name
@option_contract_name(required=False)
@option_event_name
@click.option('--from-block', help="Collect events from this block number", type=click.INT)
@click.option('--to-block', help="Collect events until this block number", type=click.INT)

View File

@ -469,6 +469,12 @@ ETHERSCAN_FLAG_DISABLED_WARNING = """
WARNING: --etherscan is disabled. If you want to see deployed contracts and TXs in your browser, activate --etherscan.
"""
#
# Upgrade
#
IDENTICAL_REGISTRY_WARNING = "Local registry ({local_registry.id}) is identical to the one on GitHub ({github_registry.id})."
#
# Multisig

View File

@ -35,7 +35,6 @@ from nucypher.utilities.logging import Logger
option_checksum_address = click.option('--checksum-address', help="Run with a specified account", type=EIP55_CHECKSUM_ADDRESS)
option_config_file = click.option('--config-file', help="Path to configuration file", type=EXISTING_READABLE_FILE)
option_config_root = click.option('--config-root', help="Custom configuration directory", type=click.Path())
option_contract_name = click.option('--contract-name', help="Specify a single contract by name", type=click.Choice(NUCYPHER_CONTRACT_NAMES))
option_dev = click.option('--dev', '-d', help="Enable development mode", is_flag=True)
option_db_filepath = click.option('--db-filepath', help="The database filepath to connect to", type=click.STRING)
option_dry_run = click.option('--dry-run', '-x', help="Execute normally without actually starting the node", is_flag=True)
@ -67,6 +66,15 @@ option_rate = click.option('--rate', help="Policy rate per period (in wei)", typ
# Alphabetical
#
def option_contract_name(required: bool = False):
return click.option(
'--contract-name',
help="Specify a single contract by name",
type=click.Choice(NUCYPHER_CONTRACT_NAMES),
required=required
)
def option_controller_port(default=None):
return click.option(
'--controller-port',

View File

@ -121,6 +121,7 @@ def establish_deployer_registry(emitter,
filepath = registry_infile
default_registry_filepath = os.path.join(DEFAULT_CONFIG_ROOT, BaseContractRegistry.REGISTRY_NAME)
if registry_outfile:
# mutative usage of existing registry
registry_infile = registry_infile or default_registry_filepath
if use_existing_registry:
try:

View File

@ -92,7 +92,7 @@ def test_nucypher_deploy_inspect_fully_deployed(click_runner, agency_local_regis
assert policy_agent.owner in result.output
assert adjudicator_agent.owner in result.output
minimum, default, maximum = 10, 20, 30
minimum, default, maximum = 10, 10, 10 # TODO: Fix with skipped test see Issue #2314
assert 'Range' in result.output
assert f"{minimum} wei" in result.output
assert f"{default} wei" in result.output