示例#1
0
    def bootstrap_network(cls) -> Tuple['TesterBlockchain', Dict[str, EthereumContractAgent]]:
        testerchain = cls.connect()

        origin = testerchain.interface.w3.eth.accounts[0]
        deployer = Deployer(blockchain=testerchain, deployer_address=origin, bare=True)

        random_deployment_secret = partial(os.urandom, DISPATCHER_SECRET_LENGTH)
        _txhashes, agents = deployer.deploy_network_contracts(miner_secret=random_deployment_secret(),
                                                              policy_secret=random_deployment_secret())
        return testerchain, agents
示例#2
0
def test_rapid_deployment():
    compiler = SolidityCompiler()
    registry = InMemoryEthereumContractRegistry()
    allocation_registry = InMemoryAllocationRegistry()
    interface = BlockchainDeployerInterface(compiler=compiler,
                                            registry=registry,
                                            provider_uri='tester://pyevm')
    blockchain = TesterBlockchain(interface=interface,
                                  airdrop=False,
                                  test_accounts=4)
    blockchain.ether_airdrop(amount=1000000000)
    origin, *everyone = blockchain.interface.w3.eth.accounts

    deployer = Deployer(blockchain=blockchain, deployer_address=origin)

    deployer_address, *all_yall = deployer.blockchain.interface.w3.eth.accounts

    # The Big Three (+ Dispatchers)
    deployer.deploy_network_contracts(miner_secret=os.urandom(32),
                                      policy_secret=os.urandom(32))

    # User Escrow Proxy
    deployer.deploy_escrow_proxy(secret=os.urandom(32))

    # Deploy User Escrow
    total_allocations = 100

    # Start with some hard-coded cases...
    allocation_data = [{
        'address': all_yall[1],
        'amount': MAX_ALLOWED_LOCKED,
        'duration': ONE_YEAR_IN_SECONDS
    }, {
        'address': all_yall[2],
        'amount': MIN_ALLOWED_LOCKED,
        'duration': ONE_YEAR_IN_SECONDS * 2
    }, {
        'address': all_yall[3],
        'amount': MIN_ALLOWED_LOCKED * 100,
        'duration': ONE_YEAR_IN_SECONDS * 3
    }]

    # Pile on the rest
    for _ in range(total_allocations - len(allocation_data)):
        random_password = ''.join(
            random.SystemRandom().choice(string.ascii_uppercase +
                                         string.digits) for _ in range(16))
        acct = w3.eth.account.create(random_password)
        beneficiary_address = acct.address
        amount = random.randint(MIN_ALLOWED_LOCKED, MAX_ALLOWED_LOCKED)
        duration = random.randint(MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS * 3)
        random_allocation = {
            'address': beneficiary_address,
            'amount': amount,
            'duration': duration
        }
        allocation_data.append(random_allocation)

    deployer.deploy_beneficiary_contracts(
        allocations=allocation_data, allocation_registry=allocation_registry)
示例#3
0
    def bootstrap_network(cls) -> Tuple['TesterBlockchain', Dict[str, EthereumContractAgent]]:
        """For use with metric testing scripts"""

        testerchain = cls(compiler=SolidityCompiler())
        power = BlockchainPower(blockchain=testerchain, account=testerchain.client.etherbase)
        power.unlock_account(password=INSECURE_DEVELOPMENT_PASSWORD)
        testerchain.transacting_power = power

        origin = testerchain.client.etherbase
        deployer = Deployer(blockchain=testerchain, deployer_address=origin, bare=True)
        _txhashes, agents = deployer.deploy_network_contracts(staker_secret=STAKING_ESCROW_DEPLOYMENT_SECRET,
                                                              policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET,
                                                              adjudicator_secret=ADJUDICATOR_DEPLOYMENT_SECRET,
                                                              user_escrow_proxy_secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET)
        return testerchain, agents
示例#4
0
    def bootstrap_network(
            cls
    ) -> Tuple['TesterBlockchain', Dict[str, EthereumContractAgent]]:
        testerchain = cls.connect()

        origin = testerchain.interface.w3.eth.accounts[0]
        deployer = Deployer(blockchain=testerchain,
                            deployer_address=origin,
                            bare=True)

        _txhashes, agents = deployer.deploy_network_contracts(
            miner_secret=MINERS_ESCROW_DEPLOYMENT_SECRET,
            policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET,
            adjudicator_secret=MINING_ADJUDICATOR_DEPLOYMENT_SECRET,
            user_escrow_proxy_secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET)
        return testerchain, agents
def test_rapid_deployment(token_economics):
    compiler = SolidityCompiler()
    registry = InMemoryEthereumContractRegistry()
    allocation_registry = InMemoryAllocationRegistry()
    interface = BlockchainDeployerInterface(compiler=compiler,
                                            registry=registry,
                                            provider_uri='tester://pyevm')

    blockchain = TesterBlockchain(interface=interface, airdrop=False, test_accounts=4)
    deployer_address = blockchain.etherbase_account

    deployer = Deployer(blockchain=blockchain, deployer_address=deployer_address)

    # The Big Three (+ Dispatchers)
    deployer.deploy_network_contracts(miner_secret=MINERS_ESCROW_DEPLOYMENT_SECRET,
                                      policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET,
                                      adjudicator_secret=MINING_ADJUDICATOR_DEPLOYMENT_SECRET)

    # Deploy User Escrow, too (+ Linker)
    deployer.deploy_escrow_proxy(secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET)

    total_allocations = NUMBER_OF_ALLOCATIONS_IN_TESTS

    all_yall = blockchain.unassigned_accounts
    # Start with some hard-coded cases...
    allocation_data = [{'address': all_yall[1],
                        'amount': token_economics.maximum_allowed_locked,
                        'duration': ONE_YEAR_IN_SECONDS},

                       {'address': all_yall[2],
                        'amount': token_economics.minimum_allowed_locked,
                        'duration': ONE_YEAR_IN_SECONDS*2},

                       {'address': all_yall[3],
                        'amount': token_economics.minimum_allowed_locked*100,
                        'duration': ONE_YEAR_IN_SECONDS*3}
                       ]

    # Pile on the rest
    for _ in range(total_allocations - len(allocation_data)):
        random_password = ''.join(random.SystemRandom().choice(string.ascii_uppercase+string.digits) for _ in range(16))
        acct = w3.eth.account.create(random_password)
        beneficiary_address = acct.address
        amount = random.randint(token_economics.minimum_allowed_locked, token_economics.maximum_allowed_locked)
        duration = random.randint(token_economics.minimum_locked_periods*ONE_YEAR_IN_SECONDS,
                                  (token_economics.maximum_locked_periods*ONE_YEAR_IN_SECONDS)*3)
        random_allocation = {'address': beneficiary_address, 'amount': amount, 'duration': duration}
        allocation_data.append(random_allocation)

    deployer.deploy_beneficiary_contracts(allocations=allocation_data, allocation_registry=allocation_registry)
def test_rapid_deployment(token_economics):
    compiler = SolidityCompiler()
    allocation_registry = InMemoryAllocationRegistry()

    blockchain = _TesterBlockchain(eth_airdrop=False,
                                   test_accounts=4,
                                   compiler=compiler)

    # TODO: #1092 - TransactingPower
    blockchain.transacting_power = BlockchainPower(
        blockchain=blockchain, account=blockchain.etherbase_account)
    deployer_address = blockchain.etherbase_account

    deployer = Deployer(blockchain=blockchain,
                        deployer_address=deployer_address)

    deployer.deploy_network_contracts(
        staker_secret=STAKING_ESCROW_DEPLOYMENT_SECRET,
        policy_secret=POLICY_MANAGER_DEPLOYMENT_SECRET,
        adjudicator_secret=ADJUDICATOR_DEPLOYMENT_SECRET,
        user_escrow_proxy_secret=USER_ESCROW_PROXY_DEPLOYMENT_SECRET)

    all_yall = blockchain.unassigned_accounts
    # Start with some hard-coded cases...
    allocation_data = [{
        'address': all_yall[1],
        'amount': token_economics.maximum_allowed_locked,
        'duration': ONE_YEAR_IN_SECONDS
    }, {
        'address': all_yall[2],
        'amount': token_economics.minimum_allowed_locked,
        'duration': ONE_YEAR_IN_SECONDS * 2
    }, {
        'address': all_yall[3],
        'amount': token_economics.minimum_allowed_locked * 100,
        'duration': ONE_YEAR_IN_SECONDS * 3
    }]

    # Pile on the rest
    for _ in range(NUMBER_OF_ALLOCATIONS_IN_TESTS - len(allocation_data)):
        random_password = ''.join(
            random.SystemRandom().choice(string.ascii_uppercase +
                                         string.digits) for _ in range(16))
        acct = w3.eth.account.create(random_password)
        beneficiary_address = acct.address
        amount = random.randint(token_economics.minimum_allowed_locked,
                                token_economics.maximum_allowed_locked)
        duration = random.randint(
            token_economics.minimum_locked_periods * ONE_YEAR_IN_SECONDS,
            (token_economics.maximum_locked_periods * ONE_YEAR_IN_SECONDS) * 3)
        random_allocation = {
            'address': beneficiary_address,
            'amount': amount,
            'duration': duration
        }
        allocation_data.append(random_allocation)

    deployer.deploy_beneficiary_contracts(
        allocations=allocation_data, allocation_registry=allocation_registry)
示例#7
0
def deployed_blockchain():

    #
    # Interface
    #
    compiler = SolidityCompiler()
    registry = InMemoryEthereumContractRegistry()
    allocation_registry = InMemoryAllocationRegistry()
    interface = BlockchainDeployerInterface(compiler=compiler,
                                            registry=registry,
                                            provider_uri=TEST_PROVIDER_URI)

    #
    # Blockchain
    #
    blockchain = TesterBlockchain(interface=interface,
                                  airdrop=False,
                                  test_accounts=5,
                                  poa=True)
    blockchain.ether_airdrop(amount=TESTING_ETH_AIRDROP_AMOUNT)
    origin, *everyone = blockchain.interface.w3.eth.accounts

    #
    # Delpoyer
    #
    deployer = Deployer(blockchain=blockchain, deployer_address=origin)

    deployer_address, *all_yall = deployer.blockchain.interface.w3.eth.accounts

    # The Big Three (+ Dispatchers)
    deployer.deploy_network_contracts(miner_secret=os.urandom(32),
                                      policy_secret=os.urandom(32))

    # User Escrow Proxy
    deployer.deploy_escrow_proxy(secret=os.urandom(32))

    # Start with some hard-coded cases...
    allocation_data = [{
        'address': all_yall[1],
        'amount': MAX_ALLOWED_LOCKED,
        'duration': ONE_YEAR_IN_SECONDS
    }]

    deployer.deploy_beneficiary_contracts(
        allocations=allocation_data, allocation_registry=allocation_registry)

    yield blockchain, deployer_address, registry
示例#8
0
def deployed_blockchain(token_economics):

    # Interface
    compiler = SolidityCompiler()
    registry = InMemoryEthereumContractRegistry()
    allocation_registry = InMemoryAllocationRegistry()
    interface = BlockchainDeployerInterface(compiler=compiler,
                                            registry=registry,
                                            provider_uri=TEST_PROVIDER_URI)

    # Blockchain
    blockchain = TesterBlockchain(interface=interface,
                                  airdrop=True,
                                  test_accounts=5,
                                  poa=True)
    deployer_address = blockchain.etherbase_account

    # Deployer
    deployer = Deployer(blockchain=blockchain,
                        deployer_address=deployer_address)

    # The Big Three (+ Dispatchers)
    deployer.deploy_network_contracts(miner_secret=os.urandom(32),
                                      policy_secret=os.urandom(32),
                                      adjudicator_secret=os.urandom(32))

    # User Escrow Proxy
    deployer.deploy_escrow_proxy(secret=os.urandom(32))

    # Start with some hard-coded cases...
    all_yall = blockchain.unassigned_accounts
    allocation_data = [{
        'address': all_yall[1],
        'amount': token_economics.maximum_allowed_locked,
        'duration': ONE_YEAR_IN_SECONDS
    }]

    deployer.deploy_beneficiary_contracts(
        allocations=allocation_data, allocation_registry=allocation_registry)

    yield blockchain, deployer_address, registry
示例#9
0
    def __connect(deployer_address=None):

        # Ensure config root exists
        if not os.path.exists(DEFAULT_CONFIG_ROOT):
            os.makedirs(DEFAULT_CONFIG_ROOT)

        # Connect to Blockchain
        blockchain = Blockchain.connect(provider_uri=provider_uri,
                                        deployer=True,
                                        compile=not no_compile,
                                        poa=poa)

        if not deployer_address:
            etherbase = blockchain.interface.w3.eth.accounts[0]
            deployer_address = etherbase
        click.confirm(
            "Deployer Address is {} - Continue?".format(deployer_address),
            abort=True)

        deployer = Deployer(blockchain=blockchain,
                            deployer_address=deployer_address)

        return deployer
示例#10
0
def deploy(click_config, action, poa, provider_uri, geth, enode,
           deployer_address, contract_name, allocation_infile,
           allocation_outfile, registry_infile, registry_outfile, no_compile,
           amount, recipient_address, config_root, sync, force):
    """Manage contract and registry deployment"""

    ETH_NODE = None

    #
    # Validate
    #

    # Ensure config root exists, because we need a default place to put output files.
    config_root = config_root or DEFAULT_CONFIG_ROOT
    if not os.path.exists(config_root):
        os.makedirs(config_root)

    #
    # Connect to Blockchain
    #

    # Establish a contract registry from disk if specified
    registry, registry_filepath = None, (registry_outfile or registry_infile)
    if registry_filepath is not None:
        registry = EthereumContractRegistry(
            registry_filepath=registry_filepath)

    if geth:
        # Spawn geth child process
        ETH_NODE = NuCypherGethDevnetProcess(config_root=config_root)
        ETH_NODE.ensure_account_exists(password=click_config.get_password(
            confirm=True))
        if not ETH_NODE.initialized:
            ETH_NODE.initialize_blockchain()
        ETH_NODE.start()  # TODO: Graceful shutdown
        provider_uri = ETH_NODE.provider_uri

    # Deployment-tuned blockchain connection
    blockchain = Blockchain.connect(provider_uri=provider_uri,
                                    poa=poa,
                                    registry=registry,
                                    compile=not no_compile,
                                    deployer=True,
                                    fetch_registry=False,
                                    sync=sync)

    #
    # Deployment Actor
    #

    if not deployer_address:
        for index, address in enumerate(blockchain.interface.w3.eth.accounts):
            click.secho(f"{index} --- {address}")
        choices = click.IntRange(0, len(blockchain.interface.w3.eth.accounts))
        deployer_address_index = click.prompt("Select deployer address",
                                              default=0,
                                              type=choices)
        deployer_address = blockchain.interface.w3.eth.accounts[
            deployer_address_index]

    # Verify Address
    if not force:
        click.confirm("Selected {} - Continue?".format(deployer_address),
                      abort=True)
    deployer = Deployer(blockchain=blockchain,
                        deployer_address=deployer_address)

    # Verify ETH Balance
    click.secho(f"\n\nDeployer ETH balance: {deployer.eth_balance}")
    if deployer.eth_balance == 0:
        click.secho("Deployer address has no ETH.", fg='red', bold=True)
        raise click.Abort()

    if not blockchain.interface.is_local:
        # (~ dev mode; Assume accounts are already unlocked)
        password = click.prompt("Enter ETH node password", hide_input=True)
        blockchain.interface.w3.geth.personal.unlockAccount(
            deployer_address, password)

    # Add ETH Bootnode or Peer
    if enode:
        if geth:
            blockchain.interface.w3.geth.admin.addPeer(enode)
            click.secho(f"Added ethereum peer {enode}")
        else:
            raise NotImplemented  # TODO: other backends

    #
    # Action switch
    #

    if action == 'upgrade':
        if not contract_name:
            raise click.BadArgumentUsage(
                message="--contract-name is required when using --upgrade")
        existing_secret = click.prompt(
            'Enter existing contract upgrade secret', hide_input=True)
        new_secret = click.prompt('Enter new contract upgrade secret',
                                  hide_input=True,
                                  confirmation_prompt=True)
        deployer.upgrade_contract(contract_name=contract_name,
                                  existing_plaintext_secret=existing_secret,
                                  new_plaintext_secret=new_secret)

    elif action == 'rollback':
        existing_secret = click.prompt(
            'Enter existing contract upgrade secret', hide_input=True)
        new_secret = click.prompt('Enter new contract upgrade secret',
                                  hide_input=True,
                                  confirmation_prompt=True)
        deployer.rollback_contract(contract_name=contract_name,
                                   existing_plaintext_secret=existing_secret,
                                   new_plaintext_secret=new_secret)

    elif action == "contracts":

        registry_filepath = deployer.blockchain.interface.registry.filepath
        if os.path.isfile(registry_filepath):
            click.secho(
                f"\nThere is an existing contract registry at {registry_filepath}.\n"
                f"Did you mean 'nucypher-deploy upgrade'?\n",
                fg='yellow')
            click.confirm(
                "Optionally, destroy existing local registry and continue?",
                abort=True)
            click.confirm(
                f"Confirm deletion of contract registry '{registry_filepath}'?",
                abort=True)
            os.remove(registry_filepath)

        #
        # Deploy Single Contract
        #

        if contract_name:
            # TODO: Handle secret collection for single contract deployment

            try:
                deployer_func = deployer.deployers[contract_name]
            except KeyError:
                message = f"No such contract {contract_name}. Available contracts are {deployer.deployers.keys()}"
                click.secho(message, fg='red', bold=True)
                raise click.Abort()
            else:
                # Deploy single contract
                _txs, _agent = deployer_func()

            # TODO: Painting for single contract deployment
            if ETH_NODE:
                ETH_NODE.stop()
            return

        #
        # Stage Deployment
        #

        # Track tx hashes, and new agents
        __deployment_transactions = dict()
        __deployment_agents = dict()

        secrets = click_config.collect_deployment_secrets()
        click.clear()
        click.secho(NU_BANNER)

        w3 = deployer.blockchain.interface.w3
        click.secho(f"Current Time ........ {maya.now().iso8601()}")
        click.secho(
            f"Web3 Provider ....... {deployer.blockchain.interface.provider_uri}"
        )
        click.secho(f"Block ............... {w3.eth.blockNumber}")
        click.secho(f"Gas Price ........... {w3.eth.gasPrice}")

        click.secho(f"Deployer Address .... {deployer.checksum_address}")
        click.secho(f"ETH ................. {deployer.eth_balance}")
        click.secho(
            f"CHAIN ID............. {deployer.blockchain.interface.chain_id}")
        click.secho(
            f"CHAIN................ {deployer.blockchain.interface.chain_name}"
        )

        # Ask - Last chance to gracefully abort
        if not force:
            click.secho(
                "\nDeployment successfully staged. Take a deep breath. \n",
                fg='green')
            if click.prompt("Type 'DEPLOY' to continue") != 'DEPLOY':
                raise click.Abort()

        # Delay - Last chance to crash and abort
        click.secho(f"Starting deployment in 3 seconds...", fg='red')
        time.sleep(1)
        click.secho(f"2...", fg='yellow')
        time.sleep(1)
        click.secho(f"1...", fg='green')
        time.sleep(1)
        click.secho(f"Deploying...", bold=True)

        #
        # DEPLOY < -------
        #

        txhashes, deployers = deployer.deploy_network_contracts(
            miner_secret=secrets.miner_secret,
            policy_secret=secrets.policy_secret,
            adjudicator_secret=secrets.mining_adjudicator_secret,
            user_escrow_proxy_secret=secrets.escrow_proxy_secret)

        # Success
        __deployment_transactions.update(txhashes)

        #
        # Paint
        #

        total_gas_used = 0  # TODO: may be faulty
        for contract_name, transactions in __deployment_transactions.items():

            # Paint heading
            heading = '\n{} ({})'.format(
                contract_name, deployers[contract_name].contract_address)
            click.secho(heading, bold=True)
            click.echo('*' * (42 + 3 + len(contract_name)))

            for tx_name, txhash in transactions.items():

                # Wait for inclusion in the blockchain
                try:
                    receipt = deployer.blockchain.wait_for_receipt(
                        txhash=txhash)
                except TimeExhausted:
                    raise  # TODO: Option to wait longer or retry

                # Examine Receipt # TODO: This currently cannot receive failed transactions
                if receipt['status'] == 1:
                    click.secho("OK", fg='green', nl=False, bold=True)
                else:
                    click.secho("Failed", fg='red', nl=False, bold=True)

                # Accumulate gas
                total_gas_used += int(receipt['gasUsed'])

                # Paint
                click.secho(" | {}".format(tx_name), fg='yellow', nl=False)
                click.secho(" | {}".format(txhash.hex()),
                            fg='yellow',
                            nl=False)
                click.secho(" ({} gas)".format(receipt['cumulativeGasUsed']))
                click.secho("Block #{} | {}\n".format(
                    receipt['blockNumber'], receipt['blockHash'].hex()))

        # Paint outfile paths
        click.secho(
            "Cumulative Gas Consumption: {} gas".format(total_gas_used),
            bold=True,
            fg='blue')
        registry_outfile = deployer.blockchain.interface.registry.filepath
        click.secho('Generated registry {}'.format(registry_outfile),
                    bold=True,
                    fg='blue')

        # Save transaction metadata
        receipts_filepath = deployer.save_deployment_receipts(
            transactions=__deployment_transactions)
        click.secho(f"Saved deployment receipts to {receipts_filepath}",
                    fg='blue',
                    bold=True)

        #
        # Publish Contract Registry
        #

        if not deployer.blockchain.interface.is_local:
            if click.confirm("Publish new contract registry?"):
                try:
                    response = registry.publish(
                    )  # TODO: Handle non-200 response and dehydrate
                except EthereumContractRegistry.RegistryError as e:
                    click.secho("Registry publication failed.",
                                fg='red',
                                bold=True)
                    click.secho(str(e))
                    raise click.Abort()
                click.secho(f"Published new contract registry.", fg='green')

    elif action == "allocations":
        if not allocation_infile:
            allocation_infile = click.prompt("Enter allocation data filepath")
        click.confirm("Continue deploying and allocating?", abort=True)
        deployer.deploy_beneficiaries_from_file(
            allocation_data_filepath=allocation_infile,
            allocation_outfile=allocation_outfile)

    elif action == "transfer":
        token_agent = NucypherTokenAgent(blockchain=blockchain)
        click.confirm(
            f"Transfer {amount} from {token_agent.contract_address} to {recipient_address}?",
            abort=True)
        txhash = token_agent.transfer(
            amount=amount,
            sender_address=token_agent.contract_address,
            target_address=recipient_address)
        click.secho(f"OK | {txhash}")

    elif action == "publish-registry":
        registry = deployer.blockchain.interface.registry
        click.confirm(
            f"Publish {registry.filepath} to GitHub (Authentication Required)?",
            abort=True)
        try:
            response = registry.publish(
            )  # TODO: Handle non-200 response and dehydrate
        except EthereumContractRegistry.RegistryError as e:
            click.secho(str(e))
            raise click.Abort()
        click.secho(f"Published new contract registry.", fg='green')

    elif action == "destroy-registry":
        registry_filepath = deployer.blockchain.interface.registry.filepath
        click.confirm(
            f"Are you absolutely sure you want to destroy the contract registry at {registry_filepath}?",
            abort=True)
        os.remove(registry_filepath)
        click.secho(f"Successfully destroyed {registry_filepath}", fg='red')

    else:
        raise click.BadArgumentUsage(message=f"Unknown action '{action}'")

    if ETH_NODE:
        ETH_NODE.stop()
def deploy(click_config, action, poa, provider_uri, deployer_address,
           contract_name, allocation_infile, allocation_outfile,
           registry_infile, registry_outfile, no_compile, amount,
           recipient_address, config_root, force):
    """Manage contract and registry deployment"""

    # Ensure config root exists, because we need a default place to put outfiles.
    config_root = config_root or DEFAULT_CONFIG_ROOT
    if not os.path.exists(config_root):
        os.makedirs(config_root)

    # Establish a contract Registry
    registry, registry_filepath = None, (registry_outfile or registry_infile)
    if registry_filepath is not None:
        registry = EthereumContractRegistry(
            registry_filepath=registry_filepath)

    # Connect to Blockchain
    blockchain = Blockchain.connect(provider_uri=provider_uri,
                                    registry=registry,
                                    deployer=True,
                                    compile=not no_compile,
                                    poa=poa)

    # OK - Let's init a Deployment actor
    if not deployer_address:
        etherbase = blockchain.interface.w3.eth.accounts[0]
        deployer_address = etherbase  # TODO: Make this required instead, perhaps interactive

    click.confirm(
        "Deployer Address is {} - Continue?".format(deployer_address),
        abort=True)
    deployer = Deployer(blockchain=blockchain,
                        deployer_address=deployer_address)

    # The Big Three
    if action == "contracts":
        secrets = click_config.collect_deployment_secrets()

        # Track tx hashes, and new agents
        __deployment_transactions = dict()
        __deployment_agents = dict()

        if force:
            deployer.blockchain.interface.registry._destroy()

        try:
            txhashes, agents = deployer.deploy_network_contracts(
                miner_secret=bytes(secrets.miner_secret, encoding='utf-8'),
                policy_secret=bytes(secrets.policy_secret, encoding='utf-8'),
                adjudicator_secret=bytes(secrets.mining_adjudicator_secret,
                                         encoding='utf-8'))
        except BlockchainInterface.InterfaceError:
            raise  # TODO: Handle registry management here (contract may already exist)
        else:
            __deployment_transactions.update(txhashes)

        # User Escrow Proxy
        deployer.deploy_escrow_proxy(
            secret=bytes(secrets.escrow_proxy_secret, encoding='utf-8'))
        click.secho("Deployed!", fg='green', bold=True)

        #
        # Deploy Single Contract
        #
        if contract_name:

            try:
                deployer_func = deployer.deployers[contract_name]
            except KeyError:
                message = "No such contract {}. Available contracts are {}".format(
                    contract_name, deployer.deployers.keys())
                click.secho(message, fg='red', bold=True)
                raise click.Abort()
            else:
                _txs, _agent = deployer_func()

        registry_outfile = deployer.blockchain.interface.registry.filepath
        click.secho(
            '\nDeployment Transaction Hashes for {}'.format(registry_outfile),
            bold=True,
            fg='blue')
        for contract_name, transactions in __deployment_transactions.items():

            heading = '\n{} ({})'.format(
                contract_name, agents[contract_name].contract_address)
            click.secho(heading, bold=True)
            click.echo('*' * (42 + 3 + len(contract_name)))

            total_gas_used = 0
            for tx_name, txhash in transactions.items():
                receipt = deployer.blockchain.wait_for_receipt(txhash=txhash)
                total_gas_used += int(receipt['gasUsed'])

                if receipt['status'] == 1:
                    click.secho("OK", fg='green', nl=False, bold=True)
                else:
                    click.secho("Failed", fg='red', nl=False, bold=True)
                click.secho(" | {}".format(tx_name), fg='yellow', nl=False)
                click.secho(" | {}".format(txhash.hex()),
                            fg='yellow',
                            nl=False)
                click.secho(" ({} gas)".format(receipt['cumulativeGasUsed']))

                click.secho("Block #{} | {}\n".format(
                    receipt['blockNumber'], receipt['blockHash'].hex()))

        click.secho(
            "Cumulative Gas Consumption: {} gas\n".format(total_gas_used),
            bold=True,
            fg='blue')

    elif action == "allocations":
        if not allocation_infile:
            allocation_infile = click.prompt("Enter allocation data filepath")
        click.confirm("Continue deploying and allocating?", abort=True)
        deployer.deploy_beneficiaries_from_file(
            allocation_data_filepath=allocation_infile,
            allocation_outfile=allocation_outfile)

    elif action == "transfer":
        token_agent = NucypherTokenAgent(blockchain=blockchain)
        click.confirm(
            f"Transfer {amount} from {token_agent.contract_address} to {recipient_address}?",
            abort=True)
        txhash = token_agent.transfer(
            amount=amount,
            sender_address=token_agent.contract_address,
            target_address=recipient_address)
        click.secho(f"OK | {txhash}")
        return

    elif action == "destroy-registry":
        registry_filepath = deployer.blockchain.interface.registry.filepath
        click.confirm(
            f"Are you absolutely sure you want to destroy the contract registry at {registry_filepath}?",
            abort=True)
        os.remove(registry_filepath)
        click.secho(f"Successfully destroyed {registry_filepath}", fg='red')

    else:
        raise click.BadArgumentUsage(message=f"Unknown action '{action}'")