def test_nucypher_deploy_allocations(testerchain, click_runner,
                                     mock_allocation_infile, token_economics):

    deploy_command = (
        'allocations',
        '--registry-infile',
        MOCK_REGISTRY_FILEPATH,
        '--allocation-infile',
        MOCK_ALLOCATION_INFILE,
        '--allocation-outfile',
        MOCK_ALLOCATION_REGISTRY_FILEPATH,
        '--provider-uri',
        TEST_PROVIDER_URI,
        '--poa',
    )

    user_input = 'Y\n' * 2
    result = click_runner.invoke(deploy,
                                 deploy_command,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    # ensure that a pre-allocation recipient has the allocated token quantity.
    beneficiary = testerchain.interface.w3.eth.accounts[-1]
    allocation_registry = AllocationRegistry(
        registry_filepath=MOCK_ALLOCATION_REGISTRY_FILEPATH)
    user_escrow_agent = UserEscrowAgent(
        beneficiary=beneficiary, allocation_registry=allocation_registry)
    assert user_escrow_agent.unvested_tokens == token_economics.maximum_allowed_locked
示例#2
0
def test_nucypher_deploy_allocation_contracts(click_runner, testerchain,
                                              registry_filepath,
                                              mock_allocation_infile,
                                              token_economics):

    #
    # Main
    #

    deploy_command = ('allocations', '--registry-infile', registry_filepath,
                      '--allocation-infile', mock_allocation_infile.filepath,
                      '--allocation-outfile',
                      MOCK_ALLOCATION_REGISTRY_FILEPATH, '--provider',
                      TEST_PROVIDER_URI, '--poa')

    account_index = '0\n'
    yes = 'Y\n'
    user_input = account_index + yes + yes

    result = click_runner.invoke(deploy,
                                 deploy_command,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    # ensure that a pre-allocation recipient has the allocated token quantity.
    beneficiary = testerchain.client.accounts[-1]
    allocation_registry = AllocationRegistry(
        filepath=MOCK_ALLOCATION_REGISTRY_FILEPATH)
    registry = LocalContractRegistry(filepath=registry_filepath)
    user_escrow_agent = UserEscrowAgent(
        registry=registry,
        beneficiary=beneficiary,
        allocation_registry=allocation_registry)
    assert user_escrow_agent.unvested_tokens == token_economics.minimum_allowed_locked
示例#3
0
def mock_allocation_infile(testerchain, token_economics):
    accounts = testerchain.interface.w3.eth.accounts[5::]
    allocation_data = [{'address': addr, 'amount': token_economics.maximum_allowed_locked,
                        'duration': ONE_YEAR_IN_SECONDS} for addr in accounts]
    with open(MOCK_ALLOCATION_INFILE, 'w') as file:
        file.write(json.dumps(allocation_data))
    registry = AllocationRegistry(registry_filepath=MOCK_ALLOCATION_INFILE)
    yield registry
    os.remove(MOCK_ALLOCATION_INFILE)
示例#4
0
def test_nucypher_deploy_allocation_contracts(click_runner, testerchain,
                                              deploy_user_input,
                                              mock_primary_registry_filepath,
                                              mock_allocation_infile,
                                              token_economics):
    # Simulate "Reconnection"
    real_attach_provider = BlockchainDeployerInterface._attach_provider
    cached_blockchain = BlockchainDeployerInterface.reconnect()
    registry = cached_blockchain.registry
    assert registry.filepath == mock_primary_registry_filepath

    def attach_cached_provider(interface, *args, **kwargs):
        cached_provider = cached_blockchain.provider
        real_attach_provider(interface, provider=cached_provider)

    BlockchainDeployerInterface._attach_provider = attach_cached_provider

    #
    # Main
    #

    deploy_command = ('allocations', '--registry-infile',
                      MOCK_REGISTRY_FILEPATH, '--allocation-infile',
                      mock_allocation_infile.filepath, '--allocation-outfile',
                      MOCK_ALLOCATION_REGISTRY_FILEPATH, '--provider-uri',
                      TEST_PROVIDER_URI, '--poa')

    account_index = '0\n'
    yes = 'Y\n'
    node_password = f'{INSECURE_DEVELOPMENT_PASSWORD}\n'
    user_input = account_index + yes + node_password + yes

    result = click_runner.invoke(deploy,
                                 deploy_command,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    # ensure that a pre-allocation recipient has the allocated token quantity.
    beneficiary = testerchain.client.accounts[-1]
    allocation_registry = AllocationRegistry(
        registry_filepath=MOCK_ALLOCATION_REGISTRY_FILEPATH)
    user_escrow_agent = UserEscrowAgent(
        blockchain=cached_blockchain,
        beneficiary=beneficiary,
        allocation_registry=allocation_registry)
    assert user_escrow_agent.unvested_tokens == token_economics.minimum_allowed_locked

    #
    # Tear Down
    #

    # Destroy existing blockchain
    testerchain.disconnect()
示例#5
0
    def deploy_beneficiary_contracts(
        self,
        allocations: List[Dict[str, Union[str, int]]],
        allocation_outfile: str = None,
        allocation_registry: AllocationRegistry = None,
        crash_on_failure: bool = True,
    ) -> Dict[str, dict]:
        """

        Example allocation dataset (one year is 31536000 seconds):

        data = [{'beneficiary_address': '0xdeadbeef', 'amount': 100, 'duration_seconds': 31536000},
                {'beneficiary_address': '0xabced120', 'amount': 133432, 'duration_seconds': 31536000*2},
                {'beneficiary_address': '0xf7aefec2', 'amount': 999, 'duration_seconds': 31536000*3}]
        """
        if allocation_registry and allocation_outfile:
            raise self.ActorError(
                "Pass either allocation registry or allocation_outfile, not both."
            )
        if allocation_registry is None:
            allocation_registry = AllocationRegistry(
                filepath=allocation_outfile)

        allocation_txhashes, failed = dict(), list()
        for allocation in allocations:
            deployer = self.deploy_user_escrow(
                allocation_registry=allocation_registry)

            try:
                txhashes = deployer.deliver(
                    value=allocation['amount'],
                    duration=allocation['duration_seconds'],
                    beneficiary_address=allocation['beneficiary_address'])
            except TransactionFailed:
                if crash_on_failure:
                    raise
                self.log.debug(
                    f"Failed allocation transaction for {allocation['amount']} to {allocation['beneficiary_address']}"
                )
                failed.append(allocation)
                continue

            else:
                allocation_txhashes[
                    allocation['beneficiary_address']] = txhashes

        if failed:
            # TODO: More with these failures: send to isolated logfile, and reattempt
            self.log.critical(
                f"FAILED TOKEN ALLOCATION - {len(failed)} Allocations failed.")

        return allocation_txhashes
示例#6
0
def mock_allocation_infile(testerchain, token_economics):
    accounts = testerchain.unassigned_accounts
    allocation_data = [{
        'beneficiary_address': addr,
        'amount': token_economics.minimum_allowed_locked,
        'duration_seconds': ONE_YEAR_IN_SECONDS
    } for addr in accounts]

    with open(MOCK_ALLOCATION_INFILE, 'w') as file:
        file.write(json.dumps(allocation_data))

    registry = AllocationRegistry(filepath=MOCK_ALLOCATION_INFILE)
    yield registry
    os.remove(MOCK_ALLOCATION_INFILE)
示例#7
0
def mock_allocation_registry(testerchain, test_registry,
                             mock_allocation_infile):
    admin = ContractAdministrator(
        registry=test_registry,
        client_password=INSECURE_DEVELOPMENT_PASSWORD,
        deployer_address=testerchain.etherbase_account)

    admin.deploy_beneficiaries_from_file(
        allocation_data_filepath=mock_allocation_infile,
        allocation_outfile=MOCK_ALLOCATION_REGISTRY_FILEPATH)

    allocation_registry = AllocationRegistry(
        filepath=MOCK_ALLOCATION_REGISTRY_FILEPATH)
    yield allocation_registry
    if os.path.isfile(MOCK_ALLOCATION_REGISTRY_FILEPATH):
        os.remove(MOCK_ALLOCATION_REGISTRY_FILEPATH)
示例#8
0
    def deploy_beneficiary_contracts(self,
                                     allocations: List[Dict[str, Union[str, int]]],
                                     allocation_outfile: str = None,
                                     allocation_registry: AllocationRegistry = None,
                                     ) -> None:
        """

        Example allocation dataset (one year is 31540000 seconds):

        data = [{'address': '0xdeadbeef', 'amount': 100, 'duration': 31540000},
                {'address': '0xabced120', 'amount': 133432, 'duration': 31540000*2},
                {'address': '0xf7aefec2', 'amount': 999, 'duration': 31540000*3}]
        """
        if allocation_registry and allocation_outfile:
            raise self.ActorError("Pass either allocation registry or allocation_outfile, not both.")
        if allocation_registry is None:
            allocation_registry = AllocationRegistry(registry_filepath=allocation_outfile)
        for allocation in allocations:
            deployer = self.deploy_user_escrow(allocation_registry=allocation_registry)
            deployer.deliver(value=allocation['amount'],
                             duration=allocation['duration'],
                             beneficiary_address=allocation['address'])
示例#9
0
    def deploy_beneficiary_contracts(
        self,
        allocations: List[Dict[str, Union[str, int]]],
        allocation_outfile: str = None,
        allocation_registry: AllocationRegistry = None,
        crash_on_failure: bool = True,
        interactive: bool = True,
        emitter: StdoutEmitter = None,
    ) -> Dict[str, dict]:
        """
        The allocation file is a JSON file containing a list of allocations. Each allocation has a:
          * 'beneficiary_address': Checksum address of the beneficiary
          * 'name': User-friendly name of the beneficiary (Optional)
          * 'amount': Amount of tokens locked, in NuNits
          * 'duration_seconds': Lock duration expressed in seconds

        Example allocation file:

        [ {'beneficiary_address': '0xdeadbeef', 'name': 'H. E. Pennypacker', 'amount': 100, 'duration_seconds': 31536000},
          {'beneficiary_address': '0xabced120', 'amount': 133432, 'duration_seconds': 31536000},
          {'beneficiary_address': '0xf7aefec2', 'amount': 999, 'duration_seconds': 31536000}]

        """

        if interactive and not emitter:
            raise ValueError(
                "'emitter' is a required keyword argument when interactive is True."
            )

        if allocation_registry and allocation_outfile:
            raise self.ActorError(
                "Pass either allocation registry or allocation_outfile, not both."
            )
        if allocation_registry is None:
            allocation_registry = AllocationRegistry(
                filepath=allocation_outfile)

        if emitter:
            paint_input_allocation_file(emitter, allocations)

        if interactive:
            click.confirm("Continue with the allocation process?", abort=True)

        total_to_allocate = NU.from_nunits(
            sum(allocation['amount'] for allocation in allocations))
        balance = ContractAgency.get_agent(NucypherTokenAgent,
                                           self.registry).get_balance(
                                               self.deployer_address)
        if balance < total_to_allocate:
            raise ValueError(
                f"Not enough tokens to allocate. We need at least {total_to_allocate}."
            )

        allocation_receipts, failed, allocated = dict(), list(), list()
        total_deployment_transactions = len(allocations) * 4

        # Create an allocation template file, containing the allocation contract ABI and placeholder values
        # for the beneficiary and contract addresses. This file will be shared with all allocation users.
        empty_allocation_escrow_deployer = PreallocationEscrowDeployer(
            registry=self.registry)
        allocation_contract_abi = empty_allocation_escrow_deployer.get_contract_abi(
        )
        allocation_template = {
            "BENEFICIARY_ADDRESS":
            ["ALLOCATION_CONTRACT_ADDRESS", allocation_contract_abi]
        }

        parent_path = Path(allocation_registry.filepath
                           ).parent  # Use same folder as allocation registry
        template_filename = IndividualAllocationRegistry.REGISTRY_NAME
        template_filepath = os.path.join(parent_path, template_filename)
        AllocationRegistry(filepath=template_filepath).write(
            registry_data=allocation_template)
        if emitter:
            emitter.echo(
                f"Saved allocation template file to {template_filepath}",
                color='blue',
                bold=True)

        # Deploy each allocation contract
        with click.progressbar(length=total_deployment_transactions,
                               label="Allocation progress",
                               show_eta=False) as bar:
            bar.short_limit = 0
            for allocation in allocations:

                # TODO: Check if allocation already exists in allocation registry

                beneficiary = allocation['beneficiary_address']
                name = allocation.get('name', 'No name provided')

                if interactive:
                    click.pause(
                        info=f"\nPress any key to continue with allocation for "
                        f"beneficiary {beneficiary} ({name})")

                if emitter:
                    emitter.echo(
                        f"\nDeploying PreallocationEscrow contract for beneficiary {beneficiary} ({name})..."
                    )
                    bar._last_line = None
                    bar.render_progress()

                deployer = self.deploy_preallocation_escrow(
                    allocation_registry=allocation_registry, progress=bar)

                amount = allocation['amount']
                duration = allocation['duration_seconds']
                try:
                    receipts = deployer.deliver(
                        value=amount,
                        duration=duration,
                        beneficiary_address=beneficiary,
                        progress=bar)
                except TransactionFailed as e:
                    if crash_on_failure:
                        raise
                    self.log.debug(
                        f"Failed allocation transaction for {NU.from_nunits(amount)} to {beneficiary}: {e}"
                    )
                    failed.append(allocation)
                    continue

                else:
                    allocation_receipts[beneficiary] = receipts
                    allocation_contract_address = deployer.contract_address
                    self.log.info(
                        f"Created {deployer.contract_name} contract at {allocation_contract_address} "
                        f"for beneficiary {beneficiary}.")
                    allocated.append((allocation, allocation_contract_address))

                    # Create individual allocation file
                    individual_allocation_filename = f'allocation-{beneficiary}.json'
                    individual_allocation_filepath = os.path.join(
                        parent_path, individual_allocation_filename)
                    individual_allocation_file_data = {
                        'beneficiary_address': beneficiary,
                        'contract_address': allocation_contract_address
                    }
                    with open(individual_allocation_filepath, 'w') as outfile:
                        json.dump(individual_allocation_file_data, outfile)

                    if emitter:
                        blockchain = BlockchainInterfaceFactory.get_interface()
                        paint_contract_deployment(
                            contract_name=deployer.contract_name,
                            receipts=receipts,
                            contract_address=deployer.contract_address,
                            emitter=emitter,
                            chain_name=blockchain.client.chain_name,
                            open_in_browser=False)
                        emitter.echo(
                            f"Saved individual allocation file to {individual_allocation_filepath}",
                            color='blue',
                            bold=True)

            if emitter:
                paint_deployed_allocations(emitter, allocated, failed)

            csv_filename = f'allocations-{self.deployer_address[:6]}-{maya.now().epoch}.csv'
            csv_filepath = os.path.join(parent_path, csv_filename)
            write_deployed_allocations_to_csv(csv_filepath, allocated, failed)
            if emitter:
                emitter.echo(f"Saved allocation summary CSV to {csv_filepath}",
                             color='blue',
                             bold=True)

            if failed:
                # TODO: More with these failures: send to isolated logfile, and reattempt
                self.log.critical(
                    f"FAILED TOKEN ALLOCATION - {len(failed)} allocations failed."
                )

        return allocation_receipts
示例#10
0
def test_nucypher_deploy_allocation_contracts(click_runner,
                                              testerchain,
                                              deploy_user_input,
                                              mock_primary_registry_filepath,
                                              mock_allocation_infile,
                                              token_economics):

    TesterBlockchain.sever_connection()
    Agency.clear()

    if os.path.isfile(MOCK_ALLOCATION_REGISTRY_FILEPATH):
        os.remove(MOCK_ALLOCATION_REGISTRY_FILEPATH)
    assert not os.path.isfile(MOCK_ALLOCATION_REGISTRY_FILEPATH)

    # We start with a blockchain node, and nothing else...
    if os.path.isfile(mock_primary_registry_filepath):
        os.remove(mock_primary_registry_filepath)
    assert not os.path.isfile(mock_primary_registry_filepath)

    command = ['contracts',
               '--registry-outfile', mock_primary_registry_filepath,
               '--provider-uri', TEST_PROVIDER_URI,
               '--poa',
               '--no-sync']

    user_input = deploy_user_input
    result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False)
    assert result.exit_code == 0

    #
    # Main
    #

    deploy_command = ('allocations',
                      '--registry-infile', MOCK_REGISTRY_FILEPATH,
                      '--allocation-infile', mock_allocation_infile.filepath,
                      '--allocation-outfile', MOCK_ALLOCATION_REGISTRY_FILEPATH,
                      '--provider-uri', 'tester://pyevm',
                      '--poa')

    account_index = '0\n'
    yes = 'Y\n'
    node_password = f'{INSECURE_DEVELOPMENT_PASSWORD}\n'
    user_input = account_index + yes + node_password + yes

    result = click_runner.invoke(deploy,
                                 deploy_command,
                                 input=user_input,
                                 catch_exceptions=False)
    assert result.exit_code == 0

    # ensure that a pre-allocation recipient has the allocated token quantity.
    beneficiary = testerchain.interface.w3.eth.accounts[-1]
    allocation_registry = AllocationRegistry(registry_filepath=MOCK_ALLOCATION_REGISTRY_FILEPATH)
    user_escrow_agent = UserEscrowAgent(beneficiary=beneficiary, allocation_registry=allocation_registry)
    assert user_escrow_agent.unvested_tokens == token_economics.minimum_allowed_locked

    #
    # Tear Down
    #

    # Destroy existing blockchain
    BlockchainInterface.disconnect()