Deprecation of automated deployment series by ContractAdministrator

pull/2470/head
Kieran Prasch 2020-10-05 13:43:28 -07:00
parent b75d02ec9e
commit a9d3b6cf1e
11 changed files with 90 additions and 181 deletions

View File

@ -351,71 +351,6 @@ class ContractAdministrator(NucypherTokenActor):
receipts = deployer.rollback()
return receipts
def deploy_network_contracts(self,
interactive: bool = True,
emitter: StdoutEmitter = None,
etherscan: bool = False,
ignore_deployed: bool = False) -> dict:
"""
:param interactive: If True, wait for keypress after each contract deployment
:param emitter: A console output emitter instance. If emitter is None, no output will be echoed to the console.
:param etherscan: Open deployed contracts in Etherscan
:param ignore_deployed: Ignore already deployed contracts if exist
:return: Returns a dictionary of deployment receipts keyed by contract name
"""
if interactive and not emitter:
raise ValueError("'emitter' is a required keyword argument when interactive is True.")
deployment_receipts = dict()
gas_limit = None # TODO: Gas management - #842
# deploy contracts
total_deployment_transactions = 0
for deployer_class in self.primary_deployer_classes:
total_deployment_transactions += len(deployer_class.deployment_steps)
first_iteration = True
with click.progressbar(length=total_deployment_transactions,
label="Deployment progress",
show_eta=False) as bar:
bar.short_limit = 0
for deployer_class in self.primary_deployer_classes:
if interactive and not first_iteration:
click.pause(info=f"\nPress any key to continue with deployment of {deployer_class.contract_name}")
if emitter:
emitter.echo(f"\nDeploying {deployer_class.contract_name} ...")
bar._last_line = None
bar.render_progress()
if deployer_class in self.standard_deployer_classes:
receipts, deployer = self.deploy_contract(contract_name=deployer_class.contract_name,
gas_limit=gas_limit,
progress=bar,
emitter=emitter)
else:
receipts, deployer = self.deploy_contract(contract_name=deployer_class.contract_name,
gas_limit=gas_limit,
progress=bar,
ignore_deployed=ignore_deployed,
emitter=emitter)
if emitter:
blockchain = BlockchainInterfaceFactory.get_interface()
paint_contract_deployment(contract_name=deployer_class.contract_name,
receipts=receipts,
contract_address=deployer.contract_address,
emitter=emitter,
chain_name=blockchain.client.chain_name,
open_in_browser=etherscan)
deployment_receipts[deployer_class.contract_name] = receipts
first_iteration = False
return deployment_receipts
def batch_deposits(self,
allocation_data_filepath: str,
interactive: bool = True,
@ -492,7 +427,7 @@ class ContractAdministrator(NucypherTokenActor):
emitter.echo()
paint_receipt_summary(emitter=emitter,
receipt=receipt,
chain_name=chain_name,
chain_name=chain_name, # TODO: this variable might be unused
transaction_type=f'batch_deposit_{number_of_deposits}_stakers')
batch_deposit_receipts.update({staker: {'batch_deposit': receipt} for staker in deposited_stakers})

View File

@ -90,7 +90,6 @@ from nucypher.cli.options import (
from nucypher.cli.painting.deployment import (
paint_contract_deployment,
paint_deployer_contract_inspection,
paint_deployment_delay,
paint_staged_deployment
)
from nucypher.cli.painting.help import echo_solidity_version
@ -478,72 +477,37 @@ def contracts(general_config, actor_options, mode, activate, gas, ignore_deploye
with open(parameters) as json_file:
deployment_parameters = json.load(json_file)
#
# Deploy Single Contract (Amend Registry)
#
contract_name = actor_options.contract_name
deployment_mode = constants.__getattr__(mode.upper()) # TODO: constant sorrow
if contract_name: # TODO: Remove this conditional, make it the default
try:
contract_deployer_class = ADMINISTRATOR.deployers[contract_name]
except KeyError:
message = UNKNOWN_CONTRACT_NAME.format(contract_name=contract_name,
constants=ADMINISTRATOR.deployers.keys())
emitter.echo(message, color='red', bold=True)
raise click.Abort()
try:
contract_deployer_class = ADMINISTRATOR.deployers[contract_name]
except KeyError:
message = UNKNOWN_CONTRACT_NAME.format(contract_name=contract_name,
constants=ADMINISTRATOR.deployers.keys())
emitter.echo(message, color='red', bold=True)
raise click.Abort()
if activate:
# For the moment, only StakingEscrow can be activated
staking_escrow_deployer = contract_deployer_class(registry=ADMINISTRATOR.registry,
deployer_address=ADMINISTRATOR.deployer_address)
if contract_name != STAKING_ESCROW_CONTRACT_NAME or not staking_escrow_deployer.ready_to_activate:
raise click.BadOptionUsage(option_name="--activate",
message=f"You can only activate an idle instance of {STAKING_ESCROW_CONTRACT_NAME}")
if activate:
# For the moment, only StakingEscrow can be activated
staking_escrow_deployer = contract_deployer_class(registry=ADMINISTRATOR.registry,
deployer_address=ADMINISTRATOR.deployer_address)
if contract_name != STAKING_ESCROW_CONTRACT_NAME or not staking_escrow_deployer.ready_to_activate:
raise click.BadOptionUsage(option_name="--activate",
message=f"You can only activate an idle instance of {STAKING_ESCROW_CONTRACT_NAME}")
escrow_address = staking_escrow_deployer._get_deployed_contract().address
prompt = CONFIRM_NETWORK_ACTIVATION.format(staking_escrow_name=STAKING_ESCROW_CONTRACT_NAME,
staking_escrow_address=escrow_address)
click.confirm(prompt, abort=True)
escrow_address = staking_escrow_deployer._get_deployed_contract().address
prompt = CONFIRM_NETWORK_ACTIVATION.format(staking_escrow_name=STAKING_ESCROW_CONTRACT_NAME,
staking_escrow_address=escrow_address)
click.confirm(prompt, abort=True)
receipts = staking_escrow_deployer.activate(gas_limit=gas, confirmations=confirmations)
for tx_name, receipt in receipts.items():
paint_receipt_summary(emitter=emitter,
receipt=receipt,
chain_name=chain_name,
transaction_type=tx_name)
return # Exit
# Deploy
emitter.echo(CONTRACT_DEPLOYMENT_SERIES_BEGIN_ADVISORY.format(contract_name=contract_name))
receipts, agent = ADMINISTRATOR.deploy_contract(contract_name=contract_name,
gas_limit=gas,
deployment_mode=deployment_mode,
ignore_deployed=ignore_deployed,
confirmations=confirmations,
deployment_parameters=deployment_parameters)
# Report
paint_contract_deployment(contract_name=contract_name,
contract_address=agent.contract_address,
receipts=receipts,
emitter=emitter,
receipts = staking_escrow_deployer.activate(gas_limit=gas, confirmations=confirmations)
for tx_name, receipt in receipts.items():
paint_receipt_summary(emitter=emitter,
receipt=receipt,
chain_name=chain_name,
open_in_browser=actor_options.etherscan)
transaction_type=tx_name)
return # Exit
#
# Deploy Automated Series (Create Registry)
#
if deployment_mode is not FULL:
raise click.BadOptionUsage(option_name='--mode',
message="Only 'full' mode is supported when deploying all network contracts")
# Confirm filesystem registry writes.
if os.path.isfile(local_registry.filepath):
emitter.echo(EXISTING_REGISTRY_FOR_DOMAIN.format(registry_filepath=local_registry.filepath), color='yellow')
click.confirm(CONFIRM_LOCAL_REGISTRY_DESTRUCTION, abort=True)
os.remove(local_registry.filepath)
# Stage Deployment
paint_staged_deployment(deployer_interface=deployer_interface, administrator=ADMINISTRATOR, emitter=emitter)
@ -551,21 +515,29 @@ def contracts(general_config, actor_options, mode, activate, gas, ignore_deploye
if not confirm_deployment(emitter=emitter, deployer_interface=deployer_interface):
raise click.Abort()
# Delay - Last chance to abort via KeyboardInterrupt
paint_deployment_delay(emitter=emitter)
# Deploy
emitter.echo(CONTRACT_DEPLOYMENT_SERIES_BEGIN_ADVISORY.format(contract_name=contract_name))
receipts, agent = ADMINISTRATOR.deploy_contract(contract_name=contract_name,
gas_limit=gas,
deployment_mode=deployment_mode,
ignore_deployed=ignore_deployed,
confirmations=confirmations,
deployment_parameters=deployment_parameters)
# Execute Deployment
deployment_receipts = ADMINISTRATOR.deploy_network_contracts(emitter=emitter,
interactive=not actor_options.force,
etherscan=actor_options.etherscan,
ignore_deployed=ignore_deployed)
# Report
paint_contract_deployment(contract_name=contract_name,
contract_address=agent.contract_address,
receipts=receipts,
emitter=emitter,
chain_name=chain_name,
open_in_browser=actor_options.etherscan)
# Paint outfile paths
# Success
registry_outfile = local_registry.filepath
emitter.echo(SUCCESSFUL_REGISTRY_CREATION.format(registry_outfile=registry_outfile), bold=True, color='blue')
# Save transaction metadata
receipts_filepath = ADMINISTRATOR.save_deployment_receipts(receipts=deployment_receipts)
receipts_filepath = ADMINISTRATOR.save_deployment_receipts(receipts=receipts)
emitter.echo(SUCCESSFUL_SAVE_DEPLOY_RECEIPTS.format(receipts_filepath=receipts_filepath), color='blue', bold=True)

View File

@ -173,13 +173,11 @@ class UrsulaConfigOptions:
provider_uri=self.provider_uri,
signer_uri=self.signer_uri)
# Resolve rest host
rest_host = self.rest_host
if not rest_host:
rest_host = os.environ.get(NUCYPHER_ENVVAR_WORKER_IP_ADDRESS)
if not rest_host:
# TODO: Something less centralized... :-(
# TODO: Ask Ursulas instead
rest_host = determine_external_ip_address(emitter, force=force)
rest_host = os.environ.get(NUCYPHER_ENVVAR_WORKER_IP_ADDRESS,
determine_external_ip_address(emitter, force=force))
return UrsulaConfiguration.generate(password=get_nucypher_password(confirm=True),
config_root=config_root,

View File

@ -431,7 +431,7 @@ SUCCESSFUL_SAVE_BATCH_DEPOSIT_RECEIPTS = "Saved batch deposits receipts to {rece
SUCCESSFUL_SAVE_DEPLOY_RECEIPTS = "Saved deployment receipts to {receipts_filepath}"
SUCCESSFUL_REGISTRY_CREATION = 'Generated registry {registry_outfile}'
SUCCESSFUL_REGISTRY_CREATION = 'Wrote to registry {registry_outfile}'
CONFIRM_LOCAL_REGISTRY_DESTRUCTION = "*DESTROY* existing local registry and continue?"

View File

@ -48,14 +48,7 @@ def paint_staged_deployment(emitter, deployer_interface, administrator) -> None:
emitter.echo(f"Chain Name .......... {deployer_interface.client.chain_name}")
# Ask - Last chance to gracefully abort. This step cannot be forced.
emitter.echo("\nDeployment successfully staged. Take a deep breath. \n", color='green')
def paint_deployment_delay(emitter, delay: int = 3) -> None:
emitter.echo(f"Starting deployment in {delay} seconds...", color='red')
for i in range(delay)[::-1]:
emitter.echo(f"{i}...", color='yellow')
time.sleep(1)
emitter.echo("\nDeployment successfully staged.", color='green')
def paint_contract_deployment(emitter,

View File

@ -15,15 +15,16 @@ You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import json
import pytest
import random
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.characters.control.emitters import StdoutEmitter
from nucypher.crypto.powers import TransactingPower
from tests.constants import INSECURE_DEVELOPMENT_PASSWORD, NUMBER_OF_ALLOCATIONS_IN_TESTS
# Prevents TesterBlockchain to be picked up by py.test as a test class
from tests.utils.blockchain import TesterBlockchain as _TesterBlockchain
@ -40,10 +41,8 @@ def test_rapid_deployment(token_economics, test_registry, tmpdir, get_random_che
blockchain.transacting_power.activate()
deployer_address = blockchain.etherbase_account
administrator = ContractAdministrator(deployer_address=deployer_address,
registry=test_registry)
administrator.deploy_network_contracts(emitter=StdoutEmitter(), interactive=False)
administrator = ContractAdministrator(deployer_address=deployer_address, registry=test_registry)
blockchain.bootstrap_network(registry=test_registry)
all_yall = blockchain.unassigned_accounts

View File

@ -51,7 +51,7 @@ def test_deploy_single_contract(click_runner, tempfile_path):
'--network', TEMPORARY_DOMAIN,
'--debug']
user_input = '0\n' + YES_ENTER
user_input = '0\n' + YES_ENTER + 'DEPLOY'
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0, result.output
@ -68,7 +68,7 @@ def test_deploy_signer_uri_testnet_check(click_runner, mocker, tempfile_path):
'--network', TEMPORARY_DOMAIN,
'--debug']
user_input = '0\n' + YES_ENTER
user_input = '0\n' + YES_ENTER + 'DEPLOY'
# fail trying to deploy contract to testnet since ETH blanace is 0, signer will already have been initialized
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)

View File

@ -293,7 +293,9 @@ def test_manual_deployment_of_idle_network(click_runner):
os.remove(ALTERNATE_REGISTRY_FILEPATH_2)
assert not os.path.exists(ALTERNATE_REGISTRY_FILEPATH_2)
registry = LocalContractRegistry(filepath=ALTERNATE_REGISTRY_FILEPATH_2)
registry.write(InMemoryContractRegistry().read()) # FIXME: Manual deployments from scratch require an existing but empty registry (i.e., a json file just with "[]")
registry.write(InMemoryContractRegistry().read()) # TODO: Manual deployments from scratch require an existing but empty registry (i.e., a json file just with "[]")
user_input = '0\n' + YES_ENTER + 'DEPLOY'
# 1. Deploy NuCypherToken
command = ('contracts',
@ -302,9 +304,8 @@ def test_manual_deployment_of_idle_network(click_runner):
'--network', TEMPORARY_DOMAIN,
'--registry-infile', ALTERNATE_REGISTRY_FILEPATH_2)
user_input = '0\n' + YES_ENTER + INSECURE_DEVELOPMENT_PASSWORD
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
assert result.exit_code == 0, result.output
assert os.path.exists(ALTERNATE_REGISTRY_FILEPATH_2)
new_registry = LocalContractRegistry(filepath=ALTERNATE_REGISTRY_FILEPATH_2)
@ -320,7 +321,6 @@ def test_manual_deployment_of_idle_network(click_runner):
'--network', TEMPORARY_DOMAIN,
'--registry-infile', ALTERNATE_REGISTRY_FILEPATH_2)
user_input = '0\n' + YES_ENTER + INSECURE_DEVELOPMENT_PASSWORD
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
@ -334,7 +334,6 @@ def test_manual_deployment_of_idle_network(click_runner):
'--network', TEMPORARY_DOMAIN,
'--registry-infile', ALTERNATE_REGISTRY_FILEPATH_2)
user_input = '0\n' + YES_ENTER + INSECURE_DEVELOPMENT_PASSWORD
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
@ -348,7 +347,6 @@ def test_manual_deployment_of_idle_network(click_runner):
'--network', TEMPORARY_DOMAIN,
'--registry-infile', ALTERNATE_REGISTRY_FILEPATH_2)
user_input = '0\n' + YES_ENTER + INSECURE_DEVELOPMENT_PASSWORD
result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
assert result.exit_code == 0

View File

@ -32,7 +32,7 @@ from tests.constants import (FAKE_PASSWORD_CONFIRMED, INSECURE_DEVELOPMENT_PASSW
CONFIG_CLASSES = (AliceConfiguration, BobConfiguration, UrsulaConfiguration)
ENV = {NUCYPHER_ENVVAR_WORKER_IP_ADDRESS: MOCK_IP_ADDRESS,
ENV = {NUCYPHER_ENVVAR_WORKER_IP_ADDRESS: MOCK_IP_ADDRESS, # TODO: Remove this #2512
NUCYPHER_ENVVAR_KEYRING_PASSWORD: INSECURE_DEVELOPMENT_PASSWORD}
@ -46,6 +46,9 @@ def test_initialize_via_cli(config_class, custom_filepath, click_runner, monkeyp
'--federated-only',
'--config-root', custom_filepath)
if config_class == UrsulaConfiguration:
init_args += ('--rest-host', MOCK_IP_ADDRESS)
result = click_runner.invoke(nucypher_cli,
init_args,
input=FAKE_PASSWORD_CONFIRMED,

View File

@ -18,21 +18,21 @@ You should have received a copy of the GNU Affero General Public License
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
"""
import io
import json
from os.path import abspath, dirname
import io
import os
import re
import time
from os.path import abspath, dirname
from unittest.mock import Mock
import tabulate
import time
from twisted.logger import ILogObserver, globalLogPublisher, jsonFileLogObserver
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from unittest.mock import Mock
from zope.interface import provider
from nucypher.exceptions import DevelopmentInstallationRequired
from nucypher.blockchain.economics import StandardTokenEconomics
from nucypher.blockchain.eth.agents import (
AdjudicatorAgent,
@ -42,6 +42,7 @@ from nucypher.blockchain.eth.agents import (
)
from nucypher.blockchain.eth.constants import NUCYPHER_CONTRACT_NAMES
from nucypher.crypto.signing import SignatureStamp
from nucypher.exceptions import DevelopmentInstallationRequired
from nucypher.policy.policies import Policy
from nucypher.utilities.logging import Logger
from tests.utils.blockchain import TesterBlockchain

View File

@ -21,13 +21,13 @@ import os
from eth_tester.exceptions import TransactionFailed
from eth_utils import to_canonical_address
from hexbytes import HexBytes
from typing import List, Tuple, Union
from typing import List, Tuple, Union, Optional
from web3 import Web3
from nucypher.blockchain.economics import BaseEconomics, StandardTokenEconomics
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, BaseContractRegistry
from nucypher.blockchain.eth.sol.compile.constants import TEST_SOLIDITY_SOURCE_ROOT, SOLIDITY_SOURCE_ROOT
from nucypher.blockchain.eth.sol.compile.types import SourceBundle
from nucypher.blockchain.eth.token import NU
@ -40,7 +40,8 @@ from tests.constants import (
INSECURE_DEVELOPMENT_PASSWORD,
NUMBER_OF_ETH_TEST_ACCOUNTS,
NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS,
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS, PYEVM_DEV_URI
NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS,
PYEVM_DEV_URI
)
@ -207,24 +208,33 @@ class TesterBlockchain(BlockchainDeployerInterface):
f"| epoch {end_timestamp}")
@classmethod
def bootstrap_network(cls, economics: BaseEconomics = None) -> Tuple['TesterBlockchain', 'InMemoryContractRegistry']:
def bootstrap_network(cls,
registry: Optional[BaseContractRegistry] = None,
economics: BaseEconomics = None
) -> Tuple['TesterBlockchain', 'InMemoryContractRegistry']:
"""For use with metric testing scripts"""
registry = InMemoryContractRegistry()
if registry is None:
registry = InMemoryContractRegistry()
testerchain = cls()
BlockchainInterfaceFactory.register_interface(testerchain)
power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD,
account=testerchain.etherbase_account)
if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=testerchain.provider_uri):
BlockchainInterfaceFactory.register_interface(interface=testerchain)
power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD, account=testerchain.etherbase_account)
power.activate()
testerchain.transacting_power = power
origin = testerchain.client.etherbase
deployer = ContractAdministrator(deployer_address=origin,
registry=registry,
economics=economics or cls.DEFAULT_ECONOMICS,
staking_escrow_test_mode=True)
admin = ContractAdministrator(deployer_address=origin,
registry=registry,
economics=economics or cls.DEFAULT_ECONOMICS,
staking_escrow_test_mode=True)
_receipts = deployer.deploy_network_contracts(interactive=False)
gas_limit = None # TODO: Gas management - #842
for deployer_class in admin.primary_deployer_classes:
if deployer_class in ContractAdministrator.standard_deployer_classes:
admin.deploy_contract(contract_name=deployer_class.contract_name, gas_limit=gas_limit)
else:
admin.deploy_contract(contract_name=deployer_class.contract_name, gas_limit=gas_limit)
return testerchain, registry
@property