예제 #1
0
def test_rollback(click_runner, mock_primary_registry_filepath):
    """Roll 'em all back!"""

    # 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

    # Input Components
    yes = 'Y\n'

    # Stage Rollbacks
    old_secret = INSECURE_SECRETS[PLANNED_UPGRADES]
    rollback_secret = generate_insecure_secret()
    user_input = '0\n' + yes + old_secret + rollback_secret + rollback_secret

    contracts_to_rollback = ('StakingEscrow',  # v4 -> v3
                             'PolicyManager',  # v4 -> v3
                             'Adjudicator',    # v4 -> v3
                             )
    # Execute Rollbacks
    for contract_name in contracts_to_rollback:

        command = ('rollback',
                   '--contract-name', contract_name,
                   '--registry-infile', MOCK_REGISTRY_FILEPATH,
                   '--provider-uri', TEST_PROVIDER_URI,
                   '--poa')

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

        blockchain = BlockchainDeployerInterface.reconnect()
        records = blockchain.registry.search(contract_name=contract_name)
        assert len(records) == 4

        *old_records, v3, v4 = records
        current_target, rollback_target = v4, v3

        _name, current_target_address, *abi = current_target
        _name, rollback_target_address, *abi = rollback_target
        assert current_target_address != rollback_target_address

        # Ensure the proxy targets the rollback target (previous version)
        with pytest.raises(BlockchainInterface.UnknownContract):
            blockchain.get_proxy(target_address=current_target_address, proxy_name='Dispatcher')

        proxy = blockchain.get_proxy(target_address=rollback_target_address, proxy_name='Dispatcher')

        # Deeper - Ensure the proxy targets the old deployment on-chain
        targeted_address = proxy.functions.target().call()
        assert targeted_address != current_target
        assert targeted_address == rollback_target_address
예제 #2
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()
예제 #3
0
def test_transfer_tokens(click_runner, mock_primary_registry_filepath):
    #
    # Setup
    #

    # 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

    # Let's transfer some NU to a random stranger
    recipient_address = to_checksum_address(os.urandom(20))

    token_agent = NucypherTokenAgent()
    assert token_agent.get_balance(address=recipient_address) == 0

    command = [
        'transfer', '--recipient-address', recipient_address, '--amount', 42,
        '--registry-infile', mock_primary_registry_filepath, '--provider',
        TEST_PROVIDER_URI, '--poa'
    ]

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

    # Check that the NU has arrived to the recipient
    assert token_agent.get_balance(address=recipient_address) == 42
예제 #4
0
def test_run_felix(click_runner, testerchain, agency, deploy_user_input,
                   mock_primary_registry_filepath):

    clock = Clock()
    Felix._CLOCK = clock
    Felix.DISTRIBUTION_INTERVAL = 5  # seconds
    Felix.DISBURSEMENT_INTERVAL = 0.01  # hours
    Felix.STAGING_DELAY = 2  # seconds

    # Main thread (Flask)
    os.environ['NUCYPHER_FELIX_DB_SECRET'] = INSECURE_DEVELOPMENT_PASSWORD

    # Simulate "Reconnection"
    real_attach_provider = BlockchainDeployerInterface._attach_provider
    cached_blockchain = BlockchainDeployerInterface.reconnect()
    cached_blockchain.registry.commit(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

    # Mock live contract registry reads
    EthereumContractRegistry.read = lambda *a, **kw: cached_blockchain.registry.read(
    )

    # Test subproc (Click)
    envvars = {
        'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD,
        'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD,
        'FLASK_DEBUG': '1'
    }

    # Felix creates a system configuration
    init_args = ('--debug', 'felix', 'init', '--registry-filepath',
                 mock_primary_registry_filepath, '--checksum-address',
                 testerchain.client.accounts[0], '--config-root',
                 MOCK_CUSTOM_INSTALLATION_PATH_2, '--network',
                 TEMPORARY_DOMAIN, '--no-registry', '--provider-uri',
                 TEST_PROVIDER_URI)

    result = click_runner.invoke(nucypher_cli,
                                 init_args,
                                 catch_exceptions=False,
                                 env=envvars)
    assert result.exit_code == 0

    configuration_file_location = os.path.join(
        MOCK_CUSTOM_INSTALLATION_PATH_2,
        FelixConfiguration.generate_filename())

    # Felix Creates a Database
    db_args = ('--debug', 'felix', 'createdb', '--registry-filepath',
               mock_primary_registry_filepath, '--config-file',
               configuration_file_location, '--provider-uri',
               TEST_PROVIDER_URI)

    result = click_runner.invoke(nucypher_cli,
                                 db_args,
                                 catch_exceptions=False,
                                 env=envvars)
    assert result.exit_code == 0

    # Felix Runs Web Services
    def run_felix():
        args = ('--debug', 'felix', 'run', '--registry-filepath',
                mock_primary_registry_filepath, '--config-file',
                configuration_file_location, '--provider-uri',
                TEST_PROVIDER_URI, '--dry-run', '--no-registry')

        run_result = click_runner.invoke(nucypher_cli,
                                         args,
                                         catch_exceptions=False,
                                         env=envvars)
        assert run_result.exit_code == 0
        return run_result

    # A (mocked) client requests Felix's services
    def request_felix_landing_page(_result):

        # Init an equal Felix to the already running one.
        felix_config = FelixConfiguration.from_configuration_file(
            filepath=configuration_file_location)

        felix_config.attach_keyring()
        felix_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD)
        felix = felix_config.produce()

        # Make a flask app
        web_app = felix.make_web_app()
        test_client = web_app.test_client()

        # Load the landing page
        response = test_client.get('/')
        assert response.status_code == 200

        # Register a new recipient
        response = test_client.post(
            '/register',
            data={'address': felix.blockchain.client.accounts[-1]})
        assert response.status_code == 200

        return

    def time_travel(_result):
        clock.advance(amount=60)

    # Record starting ether balance
    recipient = testerchain.client.accounts[-1]
    staker = Staker(checksum_address=recipient,
                    blockchain=testerchain,
                    is_me=True)
    original_eth_balance = staker.eth_balance

    # Run the callbacks
    d = threads.deferToThread(run_felix)
    d.addCallback(request_felix_landing_page)
    d.addCallback(time_travel)

    yield d

    def confirm_airdrop(_results):
        recipient = testerchain.client.accounts[-1]
        staker = Staker(checksum_address=recipient,
                        blockchain=testerchain,
                        is_me=True)

        assert staker.token_balance == NU(15000, 'NU')

        # TODO: Airdrop Testnet Ethers?
        # new_eth_balance = original_eth_balance + testerchain.w3.fromWei(Felix.ETHER_AIRDROP_AMOUNT, 'ether')
        assert staker.eth_balance == original_eth_balance

    staged_airdrops = Felix._AIRDROP_QUEUE
    next_airdrop = staged_airdrops[0]
    next_airdrop.addCallback(confirm_airdrop)
    yield next_airdrop
예제 #5
0
def test_upgrade_contracts(click_runner, mock_primary_registry_filepath):

    #
    # Setup
    #

    # 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

    # Check the existing state of the registry before the meat and potatoes
    expected_enrollments = 9
    with open(mock_primary_registry_filepath, 'r') as file:
        raw_registry_data = file.read()
        registry_data = json.loads(raw_registry_data)
        assert len(registry_data) == expected_enrollments

    #
    # Input Components
    #

    cli_action = 'upgrade'
    base_command = ('--registry-infile', mock_primary_registry_filepath,
                    '--provider-uri', TEST_PROVIDER_URI, '--poa')

    # Generate user inputs
    yes = 'Y\n'  # :-)
    upgrade_inputs = dict()
    for version, insecure_secret in INSECURE_SECRETS.items():

        next_version = version + 1
        old_secret = INSECURE_SECRETS[version]
        try:
            new_secret = INSECURE_SECRETS[next_version]
        except KeyError:
            continue
        #             addr-----secret----new deploy secret (2x for confirmation)
        user_input = '0\n' + yes + old_secret + (new_secret * 2)
        upgrade_inputs[next_version] = user_input

    #
    # Stage Upgrades
    #

    contracts_to_upgrade = (
        'StakingEscrow',  # v1 -> v2
        'PolicyManager',  # v1 -> v2
        'Adjudicator',  # v1 -> v2
        'UserEscrowProxy',  # v1 -> v2
        'StakingEscrow',  # v2 -> v3
        'StakingEscrow',  # v3 -> v4
        'Adjudicator',  # v2 -> v3
        'PolicyManager',  # v2 -> v3
        'UserEscrowProxy',  # v2 -> v3
        'UserEscrowProxy',  # v3 -> v4
        'PolicyManager',  # v3 -> v4
        'Adjudicator',  # v3 -> v4
    )  # NOTE: Keep all versions the same in this test (all version 4, for example)

    # Each contract starts at version 1
    version_tracker = {name: 1 for name in contracts_to_upgrade}

    #
    # Upgrade Contracts
    #

    for contract_name in contracts_to_upgrade:

        # Assemble CLI command
        command = (cli_action, '--contract-name', contract_name, *base_command)

        # Select upgrade interactive input scenario
        current_version = version_tracker[contract_name]
        new_version = current_version + 1
        user_input = upgrade_inputs[new_version]

        # Execute upgrade (Meat)
        result = click_runner.invoke(deploy,
                                     command,
                                     input=user_input,
                                     catch_exceptions=False)
        assert result.exit_code == 0  # TODO: Console painting

        # Mutate the version tracking
        version_tracker[contract_name] += 1
        expected_enrollments += 1

        # Verify the registry is updated (Potatoes)
        with open(mock_primary_registry_filepath, 'r') as file:

            # Read the registry file directly, bypassing its interfaces
            raw_registry_data = file.read()
            registry_data = json.loads(raw_registry_data)
            assert len(
                registry_data
            ) == expected_enrollments, f'Unexpected number of enrollments for {contract_name}'

            # Check that there is more than one entry, since we've deployed a "version 2"
            expected_contract_enrollments = current_version + 1

            registered_names = [r[0] for r in registry_data]
            contract_enrollments = registered_names.count(contract_name)

            assert contract_enrollments > 1, f"New contract is not enrolled in {MOCK_REGISTRY_FILEPATH}"
            error = f"Incorrect number of records enrolled for {contract_name}. " \
                    f"Expected {expected_contract_enrollments} got {contract_enrollments}."
            assert contract_enrollments == expected_contract_enrollments, error

        # Ensure deployments are different addresses
        cached_blockchain = BlockchainDeployerInterface.reconnect()
        records = cached_blockchain.registry.search(
            contract_name=contract_name)
        assert len(records) == contract_enrollments, error

        old, new = records[-2:]  # Get the last two entries
        old_name, old_address, *abi = old  # Previous version
        new_name, new_address, *abi = new  # New version
        assert old_name == new_name  # TODO: Inspect ABI / Move to different test.
        assert old_address != new_address

        # Select proxy (Dispatcher vs Linker)
        if contract_name == "UserEscrowProxy":
            proxy_name = "UserEscrowLibraryLinker"
        else:
            proxy_name = 'Dispatcher'

        # Ensure the proxy targets the new deployment
        proxy = cached_blockchain.get_proxy(target_address=new_address,
                                            proxy_name=proxy_name)
        targeted_address = proxy.functions.target().call()
        assert targeted_address != old_address
        assert targeted_address == new_address