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
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()
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
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
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