def test_get_remaining_work(testerchain, agency, token_economics, test_registry): agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry) bidder = testerchain.unassigned_accounts[0] remaining = agent.get_remaining_work(checksum_address=bidder) assert remaining == 35905203136136849607983
def test_deploy_idle_network(testerchain, deployment_progress, test_registry): origin, *everybody_else = testerchain.client.accounts # # Nucypher Token # token_deployer = NucypherTokenDeployer(registry=test_registry, deployer_address=origin) assert token_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert token_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not token_deployer.is_deployed() token_deployer.deploy(progress=deployment_progress) assert token_deployer.is_deployed() token_agent = NucypherTokenAgent(registry=test_registry) assert token_agent.contract_address == token_deployer.contract_address another_token_agent = token_deployer.make_agent() assert another_token_agent.contract_address == token_deployer.contract_address == token_agent.contract_address # # StakingEscrow - in IDLE mode, i.e. without activation steps (approve_funding and initialize) # stakers_escrow_secret = os.urandom(DispatcherDeployer._secret_length) staking_escrow_deployer = StakingEscrowDeployer(registry=test_registry, deployer_address=origin) assert staking_escrow_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert staking_escrow_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not staking_escrow_deployer.is_deployed() staking_escrow_deployer.deploy(secret_hash=keccak(stakers_escrow_secret), progress=deployment_progress, deployment_mode=constants.IDLE) assert staking_escrow_deployer.is_deployed() staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) assert staking_agent.contract_address == staking_escrow_deployer.contract_address # The contract has no tokens yet assert token_agent.get_balance(staking_agent.contract_address) == 0 # # Policy Manager # policy_manager_secret = os.urandom(DispatcherDeployer._secret_length) policy_manager_deployer = PolicyManagerDeployer(registry=test_registry, deployer_address=origin) assert policy_manager_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert policy_manager_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not policy_manager_deployer.is_deployed() policy_manager_deployer.deploy(secret_hash=keccak(policy_manager_secret), progress=deployment_progress) assert policy_manager_deployer.is_deployed() policy_agent = policy_manager_deployer.make_agent() assert policy_agent.contract_address == policy_manager_deployer.contract_address # # Adjudicator # adjudicator_secret = os.urandom(DispatcherDeployer._secret_length) adjudicator_deployer = AdjudicatorDeployer(registry=test_registry, deployer_address=origin) assert adjudicator_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert adjudicator_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not adjudicator_deployer.is_deployed() adjudicator_deployer.deploy(secret_hash=keccak(adjudicator_secret), progress=deployment_progress) assert adjudicator_deployer.is_deployed() adjudicator_agent = adjudicator_deployer.make_agent() assert adjudicator_agent.contract_address == adjudicator_deployer.contract_address
def test_bond_worker(software_stakeholder, manual_worker, test_registry): software_stakeholder.bond_worker(worker_address=manual_worker) staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) assert staking_agent.get_worker_from_staker( staker_address=software_stakeholder.checksum_address) == manual_worker
def __init__(self, influx_host: str, influx_port: int, crawler_http_port: int = DEFAULT_CRAWLER_HTTP_PORT, registry: BaseContractRegistry = None, node_storage_filepath: str = CrawlerNodeStorage. DEFAULT_DB_FILEPATH, refresh_rate=DEFAULT_REFRESH_RATE, restart_on_error=True, *args, **kwargs): # Settings self.federated_only = False # Nope - for compatibility with Learner TODO # nucypher/466 Teacher.set_federated_mode(False) self.registry = registry or InMemoryContractRegistry.from_latest_publication( ) self._refresh_rate = refresh_rate self._restart_on_error = restart_on_error # TODO: Needs cleanup # Tracking node_storage = CrawlerNodeStorage( storage_filepath=node_storage_filepath) class MonitoringTracker(FleetStateTracker): def record_fleet_state(self, *args, **kwargs): new_state_or_none = super().record_fleet_state(*args, **kwargs) if new_state_or_none: _, new_state = new_state_or_none state = self.abridged_state_details(new_state) node_storage.store_state_metadata(state) self.tracker_class = MonitoringTracker super().__init__(save_metadata=True, node_storage=node_storage, *args, **kwargs) self.log = Logger(self.__class__.__name__) self.log.info( f"Storing node metadata in DB: {node_storage.db_filepath}") self.log.info( f"Storing blockchain metadata in DB: {influx_host}:{influx_port}") # In-memory Metrics self._stats = {'status': 'initializing'} self._crawler_client = None # Initialize InfluxDB self._db_host = influx_host self._db_port = influx_port self._influx_client = None # Agency self.staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=self.registry) # Crawler Tasks self.__collection_round = 0 self.__collecting_nodes = False # thread tracking self.__collecting_stats = False self.__events_from_block = 0 # from the beginning self.__collecting_events = False self._node_details_task = task.LoopingCall(self._learn_about_nodes) self._stats_collection_task = task.LoopingCall(self._collect_stats, threaded=True) self._events_collection_task = task.LoopingCall(self._collect_events) # JSON Endpoint self._crawler_http_port = crawler_http_port self._flask = None
def paint_worklock_status(emitter, registry: BaseContractRegistry): from maya import MayaDT worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=registry) # type: WorkLockAgent blockchain = worklock_agent.blockchain # Time bidding_start = MayaDT(worklock_agent.contract.functions.startBidDate().call()) bidding_end = MayaDT(worklock_agent.contract.functions.endBidDate().call()) cancellation_end = MayaDT(worklock_agent.contract.functions.endCancellationDate().call()) bidding_duration = bidding_end - bidding_start cancellation_duration = cancellation_end - bidding_start now = maya.now() bidding_remaining = bidding_end - now if bidding_end > now else 'Closed' cancellation_remaining = cancellation_end - now if cancellation_end > now else "Closed" cancellation_open = bidding_start <= now <= cancellation_end bidding_open = bidding_start <= now <= bidding_end # Refund refund_multiple = worklock_agent.contract.functions.boostingRefund().call() \ / worklock_agent.contract.functions.SLOWING_REFUND().call() payload = f""" Time ══════════════════════════════════════════════════════ Contribution Period ({'Open' if bidding_open else 'Closed'}) ------------------------------------------------------ Claims Available ...... {'Yes' if worklock_agent.is_claiming_available() else 'No'} Start Date ............ {bidding_start} End Date .............. {bidding_end} Duration .............. {bidding_duration} Time Remaining ........ {bidding_remaining} Cancellation Period ({'Open' if cancellation_open else 'Closed'}) ------------------------------------------------------ End Date .............. {cancellation_end} Duration .............. {cancellation_duration} Time Remaining ........ {cancellation_remaining} Economics ══════════════════════════════════════════════════════ Participation ------------------------------------------------------ Lot Size .............. {NU.from_nunits(worklock_agent.lot_value)} Min. Allowed Bid ...... {prettify_eth_amount(worklock_agent.minimum_allowed_bid)} Participants .......... {worklock_agent.get_bidders_population()} ETH Supply ............ {prettify_eth_amount(worklock_agent.get_eth_supply())} ETH Pool .............. {prettify_eth_amount(blockchain.client.get_balance(worklock_agent.contract_address))} Base (minimum bid) ------------------------------------------------------ Base Deposit Rate ..... {worklock_agent.get_base_deposit_rate()} NU per base ETH Bonus (surplus over minimum bid) ------------------------------------------------------ Bonus ETH Supply ...... {prettify_eth_amount(worklock_agent.get_bonus_eth_supply())} Bonus Lot Size ........ {NU.from_nunits(worklock_agent.get_bonus_lot_value())} Bonus Deposit Rate .... {worklock_agent.get_bonus_deposit_rate()} NU per bonus ETH Refunds ------------------------------------------------------ Refund Rate Multiple .. {refund_multiple:.2f} Bonus Refund Rate ..... {worklock_agent.get_bonus_refund_rate()} units of work to unlock 1 bonus ETH Base Refund Rate ...... {worklock_agent.get_base_refund_rate()} units of work to unlock 1 base ETH * NOTE: bonus ETH is refunded before base ETH """ emitter.echo(payload) return
def test_get_remaining_work(testerchain, agency, token_economics, test_registry): agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry) bidder = testerchain.client.accounts[0] remaining = agent.get_remaining_work(checksum_address=bidder) assert remaining > 0
def test_unknown_contract(testerchain, test_registry): with pytest.raises(BaseContractRegistry.UnknownContract) as exception: _staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) assert exception.value.args[0] == StakingEscrowAgent.registry_contract_name
def test_get_min_authorization(agency, test_registry, application_economics): application_agent = ContractAgency.get_agent(PREApplicationAgent, registry=test_registry) result = application_agent.get_min_authorization() assert result == application_economics.min_authorization
def test_get_min_seconds(agency, test_registry, application_economics): application_agent = ContractAgency.get_agent(PREApplicationAgent, registry=test_registry) result = application_agent.get_min_operator_seconds() assert result == application_economics.min_operator_seconds
def test_stake_via_contract(click_runner, custom_filepath, agency_local_registry, mock_allocation_registry, testerchain, stakeholder_configuration_file_location, stake_value, token_economics, agency, beneficiary, preallocation_escrow_agent): # # Inital setup and checks: beneficiary and pre-allocation contract # # First, let's be sure the beneficiary is in the allocation registry... assert mock_allocation_registry.is_beneficiary_enrolled(beneficiary) # ... and that the pre-allocation contract has enough tokens preallocation_contract_address = preallocation_escrow_agent.principal_contract.address token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=agency_local_registry) assert token_agent.get_balance(preallocation_contract_address ) >= token_economics.minimum_allowed_locked # Let's not forget to create a stakeholder init_args = ('stake', 'init-stakeholder', '--poa', '--config-root', custom_filepath, '--provider', TEST_PROVIDER_URI, '--network', TEMPORARY_DOMAIN, '--registry-filepath', agency_local_registry.filepath) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False) assert result.exit_code == 0 with open(stakeholder_configuration_file_location) as f: print(f.read()) # # The good stuff: Using `nucypher stake create --escrow` # # Staking contract has no stakes yet staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry) stakes = list( staking_agent.get_all_stakes( staker_address=preallocation_contract_address)) assert not stakes stake_args = ('stake', 'create', '--config-file', stakeholder_configuration_file_location, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--value', str(stake_value.to_tokens()), '--lock-periods', token_economics.minimum_locked_periods, '--force') # TODO: This test is writing to the default system directory and ignoring updates to the passed filepath user_input = '0\n' + 'Y\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' + 'Y\n' result = click_runner.invoke(nucypher_cli, stake_args, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Test integration with BaseConfiguration with open(stakeholder_configuration_file_location, 'r') as config_file: _config_data = json.loads(config_file.read()) # Verify the stake is on-chain # Test integration with Agency stakes = list( staking_agent.get_all_stakes( staker_address=preallocation_contract_address)) assert len(stakes) == 1 # Test integration with NU start_period, end_period, value = stakes[0] assert NU(int(value), 'NuNit') == stake_value assert (end_period - start_period) == token_economics.minimum_locked_periods - 1 # Test integration with Stake stake = Stake.from_stake_info( index=0, checksum_address=preallocation_contract_address, stake_info=stakes[0], staking_agent=staking_agent, economics=token_economics) assert stake.value == stake_value assert stake.duration == token_economics.minimum_locked_periods
def paint_deployer_contract_inspection(emitter, administrator) -> None: blockchain = BlockchainInterfaceFactory.get_interface() token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=administrator.registry) sep = '-' * 45 emitter.echo(sep) contract_payload = f""" * Web3 Provider ==================================================================== Provider URI ............. {blockchain.provider_uri} Registry ................ {administrator.registry.filepath} * Standard Deployments ===================================================================== NucypherToken ........... {token_agent.contract_address} ~ Ethers ............ {Web3.fromWei(blockchain.client.get_balance(token_agent.contract_address), 'ether')} ETH ~ Tokens ............ {NU.from_nunits(token_agent.get_balance(token_agent.contract_address))}""" emitter.echo(contract_payload) banner = """ * Proxy-Contract Deployments =====================================================================""" emitter.echo(banner) for contract_deployer_class in administrator.dispatched_upgradeable_deployer_classes: try: bare_contract = blockchain.get_contract_by_name( name=contract_deployer_class.contract_name, proxy_name=DispatcherDeployer.contract_name, registry=administrator.registry, use_proxy_address=False) dispatcher_deployer = DispatcherDeployer( registry=administrator.registry, target_contract=bare_contract, deployer_address=administrator.deployer_address, bare=True) # acquire agency for the dispatcher itself. agent = contract_deployer_class.agency( registry=administrator.registry, contract=bare_contract) proxy_payload = f""" {agent.contract_name} .... {bare_contract.address} ~ Owner .............. {bare_contract.functions.owner().call()} ~ Ethers ............. {Web3.fromWei(blockchain.client.get_balance(dispatcher_deployer.contract_address), 'ether')} ETH ~ Tokens ............. {NU.from_nunits(token_agent.get_balance(dispatcher_deployer.contract_address))} ~ Dispatcher ......... {dispatcher_deployer.contract_address} ~ Owner .......... {dispatcher_deployer.contract.functions.owner().call()} ~ Target ......... {dispatcher_deployer.contract.functions.target().call()} ~ Ethers ......... {Web3.fromWei(blockchain.client.get_balance(dispatcher_deployer.contract_address), 'ether')} ETH ~ Tokens ......... {NU.from_nunits(token_agent.get_balance(dispatcher_deployer.contract_address))}""" emitter.echo(proxy_payload) emitter.echo(sep, nl=False) except BaseContractRegistry.UnknownContract: message = f"\n{contract_deployer_class.contract_name} is not enrolled in {administrator.registry.filepath}" emitter.echo(message, color='yellow') emitter.echo(sep, nl=False) try: # # UserEscrowProxy # user_escrow_proxy_agent = UserEscrowAgent.UserEscrowProxyAgent( registry=administrator.registry) bare_contract = blockchain.get_contract_by_name( name=user_escrow_proxy_agent.contract_name, proxy_name=LibraryLinkerDeployer.contract_name, use_proxy_address=False, registry=administrator.registry) linker_deployer = LibraryLinkerDeployer( registry=administrator.registry, target_contract=bare_contract, deployer_address=administrator.deployer_address, bare=True) # acquire agency for the dispatcher itself. user_escrow_payload = f""" UserEscrowProxy .......... {bare_contract.address} ~ LibraryLinker ...... {linker_deployer.contract.address} ~ Owner .......... {linker_deployer.contract.functions.owner().call()} ~ Target ......... {linker_deployer.contract.functions.target().call()}""" emitter.echo(user_escrow_payload) emitter.echo(sep) except BaseContractRegistry.UnknownContract: message = f"\nUserEscrowProxy is not enrolled in {administrator.registry.filepath}" emitter.echo(message, color='yellow') return
def test_collect_rewards_integration( click_runner, testerchain, agency_local_registry, stakeholder_configuration_file_location, blockchain_alice, blockchain_bob, random_policy_label, beneficiary, preallocation_escrow_agent, mock_allocation_registry, manual_worker, token_economics, mock_transacting_power_activation, stake_value, policy_value, policy_rate): # Disable re-staking restake_args = ('stake', 'restake', '--disable', '--config-file', stakeholder_configuration_file_location, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, restake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 half_stake_time = token_economics.minimum_locked_periods // 2 # Test setup logger = Logger("Test-CLI") # Enter the Teacher's Logger, and current_period = 0 # State the initial period for incrementing staker_address = preallocation_escrow_agent.principal_contract.address worker_address = manual_worker # The staker is staking. stakes = StakeList(registry=agency_local_registry, checksum_address=staker_address) stakes.refresh() assert stakes staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry) assert worker_address == staking_agent.get_worker_from_staker( staker_address=staker_address) ursula_port = select_test_port() ursula = Ursula(is_me=True, checksum_address=staker_address, worker_address=worker_address, registry=agency_local_registry, rest_host='127.0.0.1', rest_port=ursula_port, start_working_now=False, network_middleware=MockRestMiddleware()) MOCK_KNOWN_URSULAS_CACHE[ursula_port] = ursula assert ursula.worker_address == worker_address assert ursula.checksum_address == staker_address mock_transacting_power_activation(account=worker_address, password=INSECURE_DEVELOPMENT_PASSWORD) # Confirm for half the first stake duration for _ in range(half_stake_time): logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.confirm_activity() testerchain.time_travel(periods=1) current_period += 1 # Alice creates a policy and grants Bob access blockchain_alice.selection_buffer = 1 M, N = 1, 1 days = 3 now = testerchain.w3.eth.getBlock(block_identifier='latest').timestamp expiration = maya.MayaDT(now).add(days=days - 1) blockchain_policy = blockchain_alice.grant(bob=blockchain_bob, label=random_policy_label, m=M, n=N, value=policy_value, expiration=expiration, handpicked_ursulas={ursula}) # Ensure that the handpicked Ursula was selected for the policy arrangement = list(blockchain_policy._accepted_arrangements)[0] assert arrangement.ursula == ursula # Bob learns about the new staker and joins the policy blockchain_bob.start_learning_loop() blockchain_bob.remember_node(node=ursula) blockchain_bob.join_policy(random_policy_label, bytes(blockchain_alice.stamp)) # Enrico Encrypts (of course) enrico = Enrico(policy_encrypting_key=blockchain_policy.public_key, network_middleware=MockRestMiddleware()) verifying_key = blockchain_alice.stamp.as_umbral_pubkey() for index in range(half_stake_time - 5): logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.confirm_activity() # Encrypt random_data = os.urandom(random.randrange(20, 100)) message_kit, signature = enrico.encrypt_message(message=random_data) # Decrypt cleartexts = blockchain_bob.retrieve(message_kit, enrico=enrico, alice_verifying_key=verifying_key, label=random_policy_label) assert random_data == cleartexts[0] # Ursula Staying online and the clock advancing testerchain.time_travel(periods=1) current_period += 1 # Finish the passage of time for _ in range( 5 - 1 ): # minus 1 because the first period was already confirmed in test_ursula_run logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") ursula.confirm_activity() current_period += 1 testerchain.time_travel(periods=1) # # WHERES THE MONEY URSULA?? - Collecting Rewards # # The address the client wants Ursula to send policy rewards to burner_wallet = testerchain.w3.eth.account.create( INSECURE_DEVELOPMENT_PASSWORD) # The policy rewards wallet is initially empty, because it is freshly created assert testerchain.client.get_balance(burner_wallet.address) == 0 # Rewards will be unlocked after the # final confirmed period has passed (+1). logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") testerchain.time_travel(periods=1) current_period += 1 logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") # Since we are mocking the blockchain connection, manually consume the transacting power of the Beneficiary. mock_transacting_power_activation(account=beneficiary, password=INSECURE_DEVELOPMENT_PASSWORD) # Collect Policy Reward collection_args = ('stake', 'collect-reward', '--config-file', stakeholder_configuration_file_location, '--policy-reward', '--no-staking-reward', '--withdraw-address', burner_wallet.address, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # Policy Reward collected_policy_reward = testerchain.client.get_balance( burner_wallet.address) expected_collection = policy_rate * 30 assert collected_policy_reward == expected_collection # # Collect Staking Reward # token_agent = ContractAgency.get_agent(agent_class=NucypherTokenAgent, registry=agency_local_registry) balance_before_collecting = token_agent.get_balance(address=staker_address) collection_args = ('stake', 'collect-reward', '--config-file', stakeholder_configuration_file_location, '--no-policy-reward', '--staking-reward', '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # The beneficiary has withdrawn her staking rewards, which are now in the staking contract assert token_agent.get_balance( address=staker_address) >= balance_before_collecting
def test_stake_restake(click_runner, beneficiary, preallocation_escrow_agent, mock_allocation_registry, agency_local_registry, manual_worker, testerchain, individual_allocation, stakeholder_configuration_file_location): staker = Staker(is_me=True, checksum_address=beneficiary, registry=agency_local_registry, individual_allocation=individual_allocation) assert staker.is_restaking restake_args = ('stake', 'restake', '--disable', '--config-file', stakeholder_configuration_file_location, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, restake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 assert not staker.is_restaking assert "Successfully disabled" in result.output staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry) current_period = staking_agent.get_current_period() release_period = current_period + 1 lock_args = ('stake', 'restake', '--lock-until', release_period, '--config-file', stakeholder_configuration_file_location, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, lock_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # Still not staking and the lock is enabled assert not staker.is_restaking assert staker.restaking_lock_enabled # CLI Output includes success message assert "Successfully enabled" in result.output assert str(release_period) in result.output # Wait until release period testerchain.time_travel(periods=1) assert not staker.restaking_lock_enabled assert not staker.is_restaking disable_args = ('stake', 'restake', '--enable', '--config-file', stakeholder_configuration_file_location, '--allocation-filepath', MOCK_INDIVIDUAL_ALLOCATION_FILEPATH, '--force') result = click_runner.invoke(nucypher_cli, disable_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 allocation_contract_address = preallocation_escrow_agent.principal_contract.address assert staking_agent.is_restaking(allocation_contract_address) staker = Staker(is_me=True, checksum_address=beneficiary, registry=agency_local_registry, individual_allocation=individual_allocation) assert staker.is_restaking assert "Successfully enabled" in result.output
def create_actor( self, emitter: StdoutEmitter, is_multisig: bool = False ) -> Tuple[ContractAdministrator, str, BlockchainInterface, BaseContractRegistry]: ensure_config_root(self.config_root) deployer_interface = initialize_deployer_interface( poa=self.poa, provider_uri=self.provider_uri, emitter=emitter, ignore_solidity_check=self.ignore_solidity_check, gas_strategy=self.gas_strategy, max_gas_price=self.max_gas_price) # Warnings deployer_pre_launch_warnings(emitter, self.etherscan, self.hw_wallet) # # Establish Registry # local_registry = establish_deployer_registry( emitter=emitter, use_existing_registry=bool( self.contract_name), # TODO: Issue #2314 registry_infile=self.registry_infile, registry_outfile=self.registry_outfile, dev=self.dev, network=self.network) # # Make Authenticated Deployment Actor # # Verify Address & collect password if is_multisig: multisig_agent = ContractAgency.get_agent(MultiSigAgent, registry=local_registry) deployer_address = multisig_agent.contract.address transacting_power = None else: testnet = deployer_interface.client.chain_name != PUBLIC_CHAINS[ 1] # Mainnet signer = Signer.from_signer_uri(self.signer_uri, testnet=testnet) deployer_address = self.deployer_address if not deployer_address: deployer_address = select_client_account( emitter=emitter, prompt=SELECT_DEPLOYER_ACCOUNT, registry=local_registry, provider_uri=self.provider_uri, signer=signer, show_eth_balance=True) if not self.force: click.confirm( CONFIRM_SELECTED_ACCOUNT.format(address=deployer_address), abort=True) # Authenticate is_clef = ClefSigner.is_valid_clef_uri(self.signer_uri) password_required = all( (not is_clef, not signer.is_device(account=deployer_address), not deployer_interface.client.is_local, not self.hw_wallet)) if password_required: password = get_client_password( checksum_address=deployer_address) signer.unlock_account(password=password, account=deployer_address) transacting_power = TransactingPower(signer=signer, account=deployer_address) # Produce Actor ADMINISTRATOR = ContractAdministrator( registry=local_registry, domain=self.network, transacting_power=transacting_power) # Verify ETH Balance emitter.echo( DEPLOYER_BALANCE.format(eth_balance=ADMINISTRATOR.eth_balance)) if transacting_power and ADMINISTRATOR.eth_balance == 0: emitter.echo(DEPLOYER_ADDRESS_ZERO_ETH, color='red', bold=True) raise click.Abort() return ADMINISTRATOR, deployer_address, deployer_interface, local_registry
def test_get_base_deposit_rate(agency, token_economics, test_registry): agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry) base_deposit_rate = agent.get_base_deposit_rate() assert base_deposit_rate == token_economics.minimum_allowed_locked / token_economics.worklock_min_allowed_bid
def test_deploy_ethereum_contracts(testerchain, deployment_progress, test_registry): testerchain = testerchain origin, *everybody_else = testerchain.client.accounts # # Nucypher Token # token_deployer = NucypherTokenDeployer(registry=test_registry, deployer_address=origin) assert token_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert token_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not token_deployer.is_deployed token_deployer.deploy(progress=deployment_progress) assert token_deployer.is_deployed assert len(token_deployer.contract_address) == 42 token_agent = NucypherTokenAgent(registry=test_registry) assert len(token_agent.contract_address) == 42 assert token_agent.contract_address == token_deployer.contract_address another_token_agent = token_deployer.make_agent() assert len(another_token_agent.contract_address) == 42 assert another_token_agent.contract_address == token_deployer.contract_address == token_agent.contract_address # # StakingEscrow # stakers_escrow_secret = os.urandom(DispatcherDeployer._secret_length) staking_escrow_deployer = StakingEscrowDeployer(registry=test_registry, deployer_address=origin) assert staking_escrow_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert staking_escrow_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not staking_escrow_deployer.is_deployed staking_escrow_deployer.deploy(secret_hash=keccak(stakers_escrow_secret), progress=deployment_progress) assert staking_escrow_deployer.is_deployed assert len(staking_escrow_deployer.contract_address) == 42 staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) assert len(staking_agent.contract_address) == 42 assert staking_agent.contract_address == staking_escrow_deployer.contract_address another_staking_agent = staking_escrow_deployer.make_agent() assert len(another_staking_agent.contract_address) == 42 assert another_staking_agent.contract_address == staking_escrow_deployer.contract_address == staking_agent.contract_address # # Policy Manager # policy_manager_secret = os.urandom(DispatcherDeployer._secret_length) policy_manager_deployer = PolicyManagerDeployer(registry=test_registry, deployer_address=origin) assert policy_manager_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert policy_manager_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not policy_manager_deployer.is_deployed policy_manager_deployer.deploy(secret_hash=keccak(policy_manager_secret), progress=deployment_progress) assert policy_manager_deployer.is_deployed assert len(policy_manager_deployer.contract_address) == 42 policy_agent = policy_manager_deployer.make_agent() assert len(policy_agent.contract_address) == 42 assert policy_agent.contract_address == policy_manager_deployer.contract_address another_policy_agent = policy_manager_deployer.make_agent() assert len(another_policy_agent.contract_address) == 42 assert another_policy_agent.contract_address == policy_manager_deployer.contract_address == policy_agent.contract_address # # Adjudicator # adjudicator_secret = os.urandom(DispatcherDeployer._secret_length) adjudicator_deployer = AdjudicatorDeployer(registry=test_registry, deployer_address=origin) assert adjudicator_deployer.deployer_address == origin with pytest.raises(BaseContractDeployer.ContractDeploymentError): assert adjudicator_deployer.contract_address is constants.CONTRACT_NOT_DEPLOYED assert not adjudicator_deployer.is_deployed adjudicator_deployer.deploy(secret_hash=keccak(adjudicator_secret), progress=deployment_progress) assert adjudicator_deployer.is_deployed assert len(adjudicator_deployer.contract_address) == 42 adjudicator_agent = adjudicator_deployer.make_agent() assert len(adjudicator_agent.contract_address) == 42 assert adjudicator_agent.contract_address == adjudicator_deployer.contract_address another_adjudicator_agent = AdjudicatorAgent(registry=test_registry) assert len(another_adjudicator_agent.contract_address) == 42 assert another_adjudicator_agent.contract_address == adjudicator_deployer.contract_address == adjudicator_agent.contract_address # overall deployment steps must match aggregated individual expected number of steps all_deployment_transactions = token_deployer.deployment_steps + staking_escrow_deployer.deployment_steps + \ policy_manager_deployer.deployment_steps + adjudicator_deployer.deployment_steps assert deployment_progress.num_steps == len(all_deployment_transactions)
def test_create_worklock_agent(testerchain, test_registry, agency, token_economics): agent = WorkLockAgent(registry=test_registry) assert agent.contract_address same_agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry) assert agent == same_agent assert not agent.is_claiming_available()
def bid(general_config, worklock_options, force, hw_wallet, value): """Place a bid, or increase an existing bid""" emitter, registry, blockchain = worklock_options.setup( general_config=general_config) worklock_agent = ContractAgency.get_agent( WorkLockAgent, registry=registry) # type: WorkLockAgent now = maya.now().epoch if not worklock_agent.start_bidding_date <= now <= worklock_agent.end_bidding_date: raise click.Abort(f"You can't bid, the bidding window is closed.") if not worklock_options.bidder_address: worklock_options.bidder_address = select_client_account( emitter=emitter, provider_uri=worklock_options.provider_uri, poa=worklock_options.poa, network=worklock_options.network, registry=registry, show_eth_balance=True) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) if not value: if force: raise click.MissingParameter("Missing --value.") existing_bid_amount = bidder.get_deposited_eth if not existing_bid_amount: # It's the first bid minimum_bid = bidder.worklock_agent.minimum_allowed_bid minimum_bid_in_eth = Web3.fromWei(minimum_bid, 'ether') prompt = f"Enter bid amount in ETH (at least {minimum_bid_in_eth} ETH)" else: # There's an existing bid and the bidder is increasing the amount emitter.message( f"You have an existing bid of {Web3.fromWei(existing_bid_amount, 'ether')} ETH" ) minimum_bid_in_eth = Web3.fromWei(1, 'ether') prompt = f"Enter the amount in ETH that you want to increase your bid" value = click.prompt(prompt, type=DecimalRange(min=minimum_bid_in_eth)) value = int(Web3.toWei(Decimal(value), 'ether')) if not force: paint_bidding_notice(emitter=emitter, bidder=bidder) click.confirm(f"Place WorkLock bid of {prettify_eth_amount(value)}?", abort=True) receipt = bidder.place_bid(value=value) emitter.message("Publishing WorkLock Bid...") maximum = NU.from_nunits(bidder.economics.maximum_allowed_locked) available_claim = NU.from_nunits(bidder.available_claim) message = f'Current bid: {prettify_eth_amount(bidder.get_deposited_eth)} | Claim: {available_claim}\n' if available_claim > maximum: message += f"This claim is currently above the allowed max ({maximum}), so the bid may be partially refunded.\n" message += f'Note that available claim value may fluctuate until bidding closes and claims are finalized.\n' emitter.echo(message, color='yellow') paint_receipt_summary( receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) return # Exit
def test_early_claim(testerchain, agency, token_economics, test_registry): agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry) bidder = testerchain.client.accounts[0] with pytest.raises(TransactionFailed): _receipt = agent.claim(checksum_address=bidder)
def claim(general_config, worklock_options, force, hw_wallet): """Claim tokens for your bid, and start staking them""" emitter, registry, blockchain = worklock_options.setup( general_config=general_config) worklock_agent = ContractAgency.get_agent( WorkLockAgent, registry=registry) # type: WorkLockAgent if not worklock_agent.is_claiming_available(): raise click.Abort( f"You can't claim tokens. Claiming is not currently available.") if not worklock_options.bidder_address: worklock_options.bidder_address = select_client_account( emitter=emitter, provider_uri=worklock_options.provider_uri, poa=worklock_options.poa, network=worklock_options.network, registry=registry, show_eth_balance=True) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) unspent_bid = bidder.available_compensation if unspent_bid: emitter.echo( f"Note that WorkLock did not use your entire bid due to a maximum claim limit.\n" f"Therefore, an unspent amount of {prettify_eth_amount(unspent_bid)} is available for refund." ) if not force: click.confirm( f"Before claiming your NU tokens for {worklock_options.bidder_address}, you will need to be refunded your unspent bid amount. Would you like to proceed?", abort=True) emitter.echo("Requesting refund of unspent bid amount...") receipt = bidder.withdraw_compensation() paint_receipt_summary( receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) has_claimed = bidder._has_claimed if has_claimed: emitter.echo(f"Claim was already done for {bidder.checksum_address}", color='red') return tokens = NU.from_nunits(bidder.available_claim) emitter.echo(f"\nYou have an available claim of {tokens} 🎉 \n", color='green', bold=True) if not force: lock_duration = bidder.worklock_agent.worklock_parameters()[-2] emitter.echo( f"Note: Claiming WorkLock NU tokens will initialize a new stake to be locked for {lock_duration} periods.", color='blue') click.confirm( f"Continue WorkLock claim for bidder {worklock_options.bidder_address}?", abort=True) emitter.echo("Submitting Claim...") receipt = bidder.claim() paint_receipt_summary( receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) paint_worklock_claim(emitter=emitter, bidder_address=worklock_options.bidder_address, network=worklock_options.network, provider_uri=worklock_options.provider_uri) return # Exit
def create_actor( self, emitter: StdoutEmitter, is_multisig: bool = False ) -> Tuple[ContractAdministrator, str, BlockchainInterface, BaseContractRegistry]: ensure_config_root(self.config_root) deployer_interface = initialize_deployer_interface( poa=self.poa, provider_uri=self.provider_uri, emitter=emitter, ignore_solidity_check=self.ignore_solidity_check, gas_strategy=self.gas_strategy) # Warnings deployer_pre_launch_warnings(emitter, self.etherscan, self.hw_wallet) # # Establish Registry # local_registry = establish_deployer_registry( emitter=emitter, use_existing_registry=bool(self.contract_name), registry_infile=self.registry_infile, registry_outfile=self.registry_outfile, dev=self.dev) # # Make Authenticated Deployment Actor # # Verify Address & collect password password = None if is_multisig: multisig_agent = ContractAgency.get_agent(MultiSigAgent, registry=local_registry) deployer_address = multisig_agent.contract.address is_transacting = False else: is_transacting = True deployer_address = self.deployer_address if not deployer_address: deployer_address = select_client_account( emitter=emitter, prompt=SELECT_DEPLOYER_ACCOUNT, provider_uri=self.provider_uri, signer_uri=self.signer_uri, show_eth_balance=True) if not self.force: click.confirm( CONFIRM_SELECTED_ACCOUNT.format(address=deployer_address), abort=True) is_clef = ClefSigner.is_valid_clef_uri(self.signer_uri) eth_password_is_needed = not self.hw_wallet and not deployer_interface.client.is_local and not is_clef if eth_password_is_needed: password = get_client_password( checksum_address=deployer_address) # Produce Actor signer = Signer.from_signer_uri( self.signer_uri) if self.signer_uri else None ADMINISTRATOR = ContractAdministrator( registry=local_registry, client_password=password, deployer_address=deployer_address, is_transacting=is_transacting, signer=signer, staking_escrow_test_mode=self.se_test_mode) # Verify ETH Balance emitter.echo( DEPLOYER_BALANCE.format(eth_balance=ADMINISTRATOR.eth_balance)) if is_transacting and ADMINISTRATOR.eth_balance == 0: emitter.echo(DEPLOYER_ADDRESS_ZERO_ETH, color='red', bold=True) raise click.Abort() return ADMINISTRATOR, deployer_address, deployer_interface, local_registry
def paint_stakers(emitter, stakers: List[str], registry: BaseContractRegistry) -> None: staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=registry) current_period = staking_agent.get_current_period() emitter.echo(f"\nCurrent period: {current_period}") emitter.echo("\n| Stakers |\n") emitter.echo(f"{'Checksum address':42} Staker information") emitter.echo('=' * (42 + 2 + 53)) for staker_address in stakers: staker = Staker(is_me=False, checksum_address=staker_address, registry=registry) nickname = Nickname.from_seed(staker_address) emitter.echo( f"{staker_address} {'Nickname:':10} {nickname} {nickname.icon}") tab = " " * len(staker_address) owned_tokens = staker.owned_tokens() last_committed_period = staker.last_committed_period worker = staker.worker_address is_restaking = staker.is_restaking is_winding_down = staker.is_winding_down is_taking_snapshots = staker.is_taking_snapshots missing_commitments = current_period - last_committed_period owned_in_nu = round(owned_tokens, 2) current_locked_tokens = round(staker.locked_tokens(periods=0), 2) next_locked_tokens = round(staker.locked_tokens(periods=1), 2) emitter.echo(f"{tab} {'Owned:':10} {owned_in_nu}") emitter.echo( f"{tab} Staked in current period: {current_locked_tokens}") emitter.echo(f"{tab} Staked in next period: {next_locked_tokens}") if is_restaking: if staker.restaking_lock_enabled: unlock_period = staker.restake_unlock_period emitter.echo( f"{tab} {'Re-staking:':10} Yes (Locked until period: {unlock_period})" ) else: emitter.echo(f"{tab} {'Re-staking:':10} Yes (Unlocked)") else: emitter.echo(f"{tab} {'Re-staking:':10} No") emitter.echo( f"{tab} {'Winding down:':10} {'Yes' if is_winding_down else 'No'}" ) emitter.echo( f"{tab} {'Snapshots:':10} {'Yes' if is_taking_snapshots else 'No'}" ) emitter.echo(f"{tab} {'Activity:':10} ", nl=False) if missing_commitments == -1: emitter.echo(f"Next period committed (#{last_committed_period})", color='green') elif missing_commitments == 0: emitter.echo( f"Current period committed (#{last_committed_period}). " f"Pending commitment to next period.", color='yellow') elif missing_commitments == current_period: emitter.echo(f"Never made a commitment", color='red') else: emitter.echo( f"Missing {missing_commitments} commitments " f"(last time for period #{last_committed_period})", color='red') emitter.echo(f"{tab} {'Worker:':10} ", nl=False) if worker == NULL_ADDRESS: emitter.echo(f"Worker not bonded", color='red') else: emitter.echo(f"{worker}") fees = prettify_eth_amount(staker.calculate_policy_fee()) emitter.echo(f"{tab} Unclaimed fees: {fees}") min_rate = prettify_eth_amount(staker.min_fee_rate) emitter.echo(f"{tab} Min fee rate: {min_rate}")
def test_stake_restake(click_runner, manual_staker, custom_filepath, testerchain, agency_local_registry, stakeholder_configuration_file_location): staker = Staker(is_me=True, checksum_address=manual_staker, registry=agency_local_registry) assert staker.is_restaking restake_args = ('stake', 'restake', '--disable', '--config-file', stakeholder_configuration_file_location, '--staking-address', manual_staker, '--force') result = click_runner.invoke(nucypher_cli, restake_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 assert not staker.is_restaking assert "Successfully disabled" in result.output staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry) current_period = staking_agent.get_current_period() release_period = current_period + 1 lock_args = ('stake', 'restake', '--lock-until', release_period, '--config-file', stakeholder_configuration_file_location, '--staking-address', manual_staker, '--force') result = click_runner.invoke(nucypher_cli, lock_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 # Still not staking and the lock is enabled assert not staker.is_restaking assert staker.restaking_lock_enabled # CLI Output includes success message assert "Successfully enabled" in result.output assert str(release_period) in result.output # Wait until release period testerchain.time_travel(periods=1) assert not staker.restaking_lock_enabled assert not staker.is_restaking disable_args = ('stake', 'restake', '--enable', '--config-file', stakeholder_configuration_file_location, '--staking-address', manual_staker, '--force') result = click_runner.invoke(nucypher_cli, disable_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 assert staker.is_restaking assert "Successfully enabled" in result.output # Disable again disable_args = ('stake', 'restake', '--disable', '--config-file', stakeholder_configuration_file_location, '--staking-address', manual_staker, '--force') result = click_runner.invoke(nucypher_cli, disable_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0
def test_adjudicator_slashes(agency, testerchain, mock_ursula_reencrypts, token_economics, test_registry, mocker): staker_account = testerchain.staker_account(0) worker_account = testerchain.ursula_account(0) ##### STAKING ESCROW STUFF ##### token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=test_registry) staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry) locked_tokens = token_economics.minimum_allowed_locked * 5 # The staker receives an initial amount of tokens tpower = TransactingPower(account=testerchain.etherbase_account, signer=Web3Signer(testerchain.client)) _txhash = token_agent.transfer(amount=locked_tokens, target_address=staker_account, transacting_power=tpower) # Deposit: The staker deposits tokens in the StakingEscrow contract. tpower = TransactingPower(account=staker_account, signer=Web3Signer(testerchain.client)) staker = Staker(domain=TEMPORARY_DOMAIN, registry=test_registry, transacting_power=tpower) staker.initialize_stake(amount=NU(locked_tokens, 'NuNit'), lock_periods=token_economics.minimum_locked_periods) assert staker.locked_tokens(periods=1) == locked_tokens # The staker hasn't bond a worker yet assert NULL_ADDRESS == staking_agent.get_worker_from_staker(staker_address=staker_account) _txhash = staking_agent.bond_worker(transacting_power=tpower, worker_address=worker_account) assert worker_account == staking_agent.get_worker_from_staker(staker_address=staker_account) assert staker_account == staking_agent.get_staker_from_worker(worker_address=worker_account) ###### END OF STAKING ESCROW STUFF #### adjudicator_agent = AdjudicatorAgent(registry=test_registry) bob_account = testerchain.bob_account bobby = NucypherTokenActor(checksum_address=bob_account, domain=TEMPORARY_DOMAIN, registry=test_registry) ursula = mock_ursula(testerchain, worker_account, mocker=mocker) # Let's create a bad cfrag evidence = mock_ursula_reencrypts(ursula, corrupt_cfrag=True) assert not adjudicator_agent.was_this_evidence_evaluated(evidence) bobby_old_balance = bobby.token_balance bob_tpower = TransactingPower(account=bob_account, signer=Web3Signer(testerchain.client)) adjudicator_agent.evaluate_cfrag(evidence=evidence, transacting_power=bob_tpower) assert adjudicator_agent.was_this_evidence_evaluated(evidence) investigator_reward = bobby.token_balance - bobby_old_balance assert investigator_reward > 0 assert investigator_reward == token_economics.base_penalty / token_economics.reward_coefficient assert staker.locked_tokens(periods=1) < locked_tokens
def create_actor(self, emitter, is_multisig: bool = False): _ensure_config_root(self.config_root) deployer_interface = _initialize_blockchain( poa=self.poa, provider_uri=self.provider_uri, emitter=emitter, ignore_solidity_check=self.ignore_solidity_check, gas_strategy=self.gas_strategy) # Warnings _pre_launch_warnings(emitter, self.etherscan, self.hw_wallet) # # Establish Registry # local_registry = establish_deployer_registry( emitter=emitter, use_existing_registry=bool(self.contract_name), registry_infile=self.registry_infile, registry_outfile=self.registry_outfile, dev=self.dev) # # Make Authenticated Deployment Actor # # Verify Address & collect password password = None if is_multisig: multisig_agent = ContractAgency.get_agent(MultiSigAgent, registry=local_registry) deployer_address = multisig_agent.contract.address is_transacting = False else: is_transacting = True deployer_address = self.deployer_address if not deployer_address: prompt = "Select deployer account" deployer_address = select_client_account( emitter=emitter, prompt=prompt, provider_uri=self.provider_uri, signer_uri=self.signer_uri, show_eth_balance=True) if not self.force: click.confirm( "Selected {} - Continue?".format(deployer_address), abort=True) if not self.hw_wallet and not deployer_interface.client.is_local: password = get_client_password( checksum_address=deployer_address) # Produce Actor signer = Signer.from_signer_uri( self.signer_uri) if self.signer_uri else None ADMINISTRATOR = ContractAdministrator( registry=local_registry, client_password=password, deployer_address=deployer_address, is_transacting=is_transacting, signer=signer, staking_escrow_test_mode=self.se_test_mode) # Verify ETH Balance emitter.echo(f"\n\nDeployer ETH balance: {ADMINISTRATOR.eth_balance}") if is_transacting and ADMINISTRATOR.eth_balance == 0: emitter.echo("Deployer address has no ETH.", color='red', bold=True) raise click.Abort() return ADMINISTRATOR, deployer_address, deployer_interface, local_registry
def events(general_config, registry_options, contract_name, from_block, to_block, event_name, csv, csv_file, event_filters): """Show events associated with NuCypher contracts.""" if csv or csv_file: if csv and csv_file: raise click.BadOptionUsage( option_name='--event-filter', message=f'Pass either --csv or --csv-file, not both.') # ensure that event name is specified - different events would have different columns in the csv file if csv_file and not all((event_name, contract_name)): # TODO consider a single csv that just gets appended to for each event # - each appended event adds their column names first # - single report-type functionality, see #2561 raise click.BadOptionUsage( option_name='--csv-file, --event-name, --contract_name', message= '--event-name and --contract-name must be specified when outputting to ' 'specific file using --csv-file; alternatively use --csv') if not contract_name: if event_name: raise click.BadOptionUsage( option_name='--event-name', message='--event-name requires --contract-name') # FIXME should we force a contract name to be specified? contract_names = [ STAKING_ESCROW_CONTRACT_NAME, POLICY_MANAGER_CONTRACT_NAME ] else: contract_names = [contract_name] emitter, registry, blockchain = registry_options.setup( general_config=general_config) if from_block is None: # by default, this command only shows events of the current period last_block = blockchain.client.block_number staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=registry) current_period = staking_agent.get_current_period() from_block = estimate_block_number_for_period( period=current_period, seconds_per_period=staking_agent.staking_parameters()[1], latest_block=last_block) if to_block is None: to_block = 'latest' else: # validate block range if from_block > to_block: raise click.BadOptionUsage( option_name='--to-block, --from-block', message=f'Invalid block range provided, ' f'from-block ({from_block}) > to-block ({to_block})') # event argument filters argument_filters = None if event_filters: try: argument_filters = parse_event_filters_into_argument_filters( event_filters) except ValueError as e: raise click.BadOptionUsage( option_name='--event-filter', message=f'Event filter must be specified as name-value pairs of ' f'the form `<name>=<value>` - {str(e)}') emitter.echo(f"Retrieving events from block {from_block} to {to_block}") for contract_name in contract_names: agent = ContractAgency.get_agent_by_contract_name( contract_name, registry) if event_name and event_name not in agent.events.names: raise click.BadOptionUsage( option_name='--event-name, --contract_name', message= f'{contract_name} contract does not have an event named {event_name}' ) title = f" {agent.contract_name} Events ".center(40, "-") emitter.echo(f"\n{title}\n", bold=True, color='green') names = agent.events.names if not event_name else [event_name] for name in names: # csv output file - one per (contract_name, event_name) pair csv_output_file = csv_file if csv or csv_output_file: if not csv_output_file: csv_output_file = generate_events_csv_file( contract_name=agent.contract_name, event_name=name) retrieve_events( emitter=emitter, agent=agent, event_name=name, # None is fine - just means all events from_block=from_block, to_block=to_block, argument_filters=argument_filters, csv_output_file=csv_output_file)
def paint_deployer_contract_inspection(emitter, registry, deployer_address) -> None: blockchain = BlockchainInterfaceFactory.get_interface() sep = '-' * 45 emitter.echo(sep) provider_info = f""" * Web3 Provider ==================================================================== Provider URI ............. {blockchain.provider_uri} Registry ................ {registry.filepath} * Standard Deployments ===================================================================== """ emitter.echo(provider_info) try: token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=registry) token_contract_info = f""" {token_agent.contract_name} ........... {token_agent.contract_address} ~ Ethers ............ {Web3.fromWei(blockchain.client.get_balance(token_agent.contract_address), 'ether')} ETH ~ Tokens ............ {NU.from_nunits(token_agent.get_balance(token_agent.contract_address))}""" except BaseContractRegistry.UnknownContract: message = f"\n{NucypherTokenAgent.contract_name} is not enrolled in {registry.filepath}" emitter.echo(message, color='yellow') emitter.echo(sep, nl=False) else: emitter.echo(token_contract_info) banner = """ * Proxy-Contract Deployments =====================================================================""" emitter.echo(banner) from nucypher.blockchain.eth.actors import ContractAdministrator for contract_deployer_class in ContractAdministrator.dispatched_upgradeable_deployer_classes: try: bare_contract = blockchain.get_contract_by_name(contract_name=contract_deployer_class.contract_name, proxy_name=DispatcherDeployer.contract_name, registry=registry, use_proxy_address=False) dispatcher_deployer = DispatcherDeployer(registry=registry, target_contract=bare_contract, deployer_address=deployer_address, bare=True) # acquire agency for the dispatcher itself. agent = contract_deployer_class.agency(registry=registry, contract=bare_contract) proxy_payload = f""" {agent.contract_name} .... {bare_contract.address} ~ Owner .............. {bare_contract.functions.owner().call()} ~ Ethers ............. {Web3.fromWei(blockchain.client.get_balance(bare_contract.address), 'ether')} ETH ~ Tokens ............. {NU.from_nunits(token_agent.get_balance(bare_contract.address))} ~ Dispatcher ......... {dispatcher_deployer.contract_address} ~ Owner .......... {dispatcher_deployer.contract.functions.owner().call()} ~ Target ......... {dispatcher_deployer.contract.functions.target().call()} ~ Ethers ......... {Web3.fromWei(blockchain.client.get_balance(dispatcher_deployer.contract_address), 'ether')} ETH ~ Tokens ......... {NU.from_nunits(token_agent.get_balance(dispatcher_deployer.contract_address))}""" emitter.echo(proxy_payload) emitter.echo(sep, nl=False) except BaseContractRegistry.UnknownContract: message = f"\n{contract_deployer_class.contract_name} is not enrolled in {registry.filepath}" emitter.echo(message, color='yellow') emitter.echo(sep, nl=False) try: # # StakingInterface # staking_interface_agent = PreallocationEscrowAgent.StakingInterfaceAgent(registry=registry) bare_contract = blockchain.get_contract_by_name(contract_name=staking_interface_agent.contract_name, proxy_name=StakingInterfaceRouterDeployer.contract_name, use_proxy_address=False, registry=registry) router_deployer = StakingInterfaceRouterDeployer(registry=registry, target_contract=bare_contract, deployer_address=deployer_address, bare=True) # acquire agency for the dispatcher itself. preallocation_escrow_payload = f""" {staking_interface_agent.contract_name} ......... {bare_contract.address} ~ Ethers ............... {Web3.fromWei(blockchain.client.get_balance(bare_contract.address), 'ether')} ETH ~ Tokens ............... {NU.from_nunits(token_agent.get_balance(bare_contract.address))} ~ StakingInterfaceRouter {router_deployer.contract.address} ~ Owner .......... {router_deployer.contract.functions.owner().call()} ~ Target ......... {router_deployer.contract.functions.target().call()} ~ Ethers ......... {Web3.fromWei(blockchain.client.get_balance(router_deployer.contract_address), 'ether')} ETH ~ Tokens ......... {NU.from_nunits(token_agent.get_balance(router_deployer.contract_address))}""" emitter.echo(preallocation_escrow_payload) emitter.echo(sep) except BaseContractRegistry.UnknownContract: message = f"\nStakingInterface is not enrolled in {registry.filepath}" emitter.echo(message, color='yellow')
def create_staking_events_metric_collectors(ursula: 'Ursula', metrics_prefix: str) -> List[MetricsCollector]: """Create collectors for staking-related events.""" collectors: List[MetricsCollector] = [] staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=ursula.registry) staker_address = ursula.checksum_address # CommitmentMade collectors.append(CommitmentMadeEventMetricsCollector( event_args_config={ "value": (Gauge, f'{metrics_prefix}_activity_confirmed_value', 'CommitmentMade to next period with value of locked tokens'), "period": (Gauge, f'{metrics_prefix}_activity_confirmed_period', 'Commitment made for period') }, staker_address=staker_address, contract_agent=staking_agent)) # Minted collectors.append(EventMetricsCollector( event_name='Minted', event_args_config={ "value": (Gauge, f'{metrics_prefix}_mined_value', 'Minted value'), "period": (Gauge, f'{metrics_prefix}_mined_period', 'Minted period'), "block_number": (Gauge, f'{metrics_prefix}_mined_block_number', 'Minted block number') }, argument_filters={'staker': staker_address}, contract_agent=staking_agent)) # Slashed collectors.append(EventMetricsCollector( event_name='Slashed', event_args_config={ "penalty": (Gauge, f'{metrics_prefix}_last_slashed_penalty', 'Penalty for slashing'), "block_number": (Gauge, f'{metrics_prefix}_last_slashed_penalty_block_number', 'Slashed penalty block number') }, argument_filters={'staker': staker_address}, contract_agent=staking_agent)) # RestakeSet collectors.append(ReStakeEventMetricsCollector( event_args_config={ "reStake": (Gauge, f'{metrics_prefix}_restaking', 'Restake set') }, staker_address=staker_address, contract_agent=staking_agent)) # WindDownSet collectors.append(WindDownEventMetricsCollector( event_args_config={ "windDown": (Gauge, f'{metrics_prefix}_wind_down', 'is windDown') }, staker_address=staker_address, contract_agent=staking_agent)) # WorkerBonded collectors.append(WorkerBondedEventMetricsCollector( event_args_config={ "startPeriod": (Gauge, f'{metrics_prefix}_worker_set_start_period', 'New worker was bonded'), "block_number": (Gauge, f'{metrics_prefix}_worker_set_block_number', 'WorkerBonded block number') }, staker_address=staker_address, worker_address=ursula.worker_address, contract_agent=staking_agent)) return collectors
def deploy(action, poa, etherscan, provider_uri, gas, deployer_address, contract_name, allocation_infile, allocation_outfile, registry_infile, registry_outfile, value, target_address, retarget, bare, config_root, hw_wallet, force, dev): """ Manage contract and registry deployment. \b Actions ----------------------------------------------------------------------------- contracts Compile and deploy contracts. allocations Deploy pre-allocation contracts. upgrade Upgrade NuCypher existing proxy contract deployments. rollback Rollback a proxy contract's target. inspect Echo owner information and bare contract metadata. transfer-tokens Transfer tokens from a contract to another address using the owner's address. transfer-ownership Transfer ownership of contracts to another address. """ emitter = StdoutEmitter() # # 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) # # Pre-Launch Warnings # if not hw_wallet: emitter.echo("WARNING: --no-hw-wallet is enabled.", color='yellow') if etherscan: emitter.echo( "WARNING: --etherscan is enabled. " "A browser tab will be opened with deployed contracts and TXs as provided by Etherscan.", color='yellow') else: emitter.echo( "WARNING: --etherscan is disabled. " "If you want to see deployed contracts and TXs in your browser, activate --etherscan.", color='yellow') if action == "download-registry": if not force: prompt = f"Fetch and download latest registry from {BaseContractRegistry.PUBLICATION_ENDPOINT}?" click.confirm(prompt, abort=True) registry = InMemoryContractRegistry.from_latest_publication() output_filepath = registry.commit(filepath=registry_outfile, overwrite=force) emitter.message( f"Successfully downloaded latest registry to {output_filepath}") return # Exit # # Connect to Blockchain # if not provider_uri: raise click.BadOptionUsage( message=f"--provider is required to deploy.", option_name="--provider") if not BlockchainInterfaceFactory.is_interface_initialized( provider_uri=provider_uri): # Note: For test compatibility. deployer_interface = BlockchainDeployerInterface( provider_uri=provider_uri, poa=poa) BlockchainInterfaceFactory.register_interface( interface=deployer_interface, sync=False, show_sync_progress=False) else: deployer_interface = BlockchainInterfaceFactory.get_interface( provider_uri=provider_uri) if action == "inspect": if registry_infile: registry = LocalContractRegistry(filepath=registry_infile) else: registry = InMemoryContractRegistry.from_latest_publication() administrator = ContractAdministrator( registry=registry, deployer_address=deployer_address) paint_deployer_contract_inspection(emitter=emitter, administrator=administrator) return # Exit # # Establish Registry # local_registry = establish_deployer_registry( emitter=emitter, use_existing_registry=bool(contract_name), registry_infile=registry_infile, registry_outfile=registry_outfile, dev=dev) # # Make Authenticated Deployment Actor # # Verify Address & collect password if not deployer_address: prompt = "Select deployer account" deployer_address = select_client_account(emitter=emitter, prompt=prompt, provider_uri=provider_uri, show_balances=False) if not force: click.confirm("Selected {} - Continue?".format(deployer_address), abort=True) password = None if not hw_wallet and not deployer_interface.client.is_local: password = get_client_password(checksum_address=deployer_address) # Produce Actor ADMINISTRATOR = ContractAdministrator(registry=local_registry, client_password=password, deployer_address=deployer_address) # Verify ETH Balance emitter.echo(f"\n\nDeployer ETH balance: {ADMINISTRATOR.eth_balance}") if ADMINISTRATOR.eth_balance == 0: emitter.echo("Deployer address has no ETH.", color='red', bold=True) raise click.Abort() # # 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) if retarget: if not target_address: raise click.BadArgumentUsage( message="--target-address is required when using --retarget" ) if not force: click.confirm( f"Confirm re-target {contract_name}'s proxy to {target_address}?", abort=True) receipt = ADMINISTRATOR.retarget_proxy( contract_name=contract_name, target_address=target_address, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) emitter.message( f"Successfully re-targeted {contract_name} proxy to {target_address}", color='green') paint_receipt_summary(emitter=emitter, receipt=receipt) return # Exit else: if not force: click.confirm( f"Confirm deploy new version of {contract_name} and retarget proxy?", abort=True) receipts = ADMINISTRATOR.upgrade_contract( contract_name=contract_name, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) emitter.message( f"Successfully deployed and upgraded {contract_name}", color='green') for name, receipt in receipts.items(): paint_receipt_summary(emitter=emitter, receipt=receipt) return # Exit elif action == 'rollback': if not contract_name: raise click.BadArgumentUsage( message="--contract-name is required when using --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) ADMINISTRATOR.rollback_contract( contract_name=contract_name, existing_plaintext_secret=existing_secret, new_plaintext_secret=new_secret) return # Exit elif action == "contracts": # # Deploy Single Contract (Amend Registry) # if contract_name: try: contract_deployer = ADMINISTRATOR.deployers[contract_name] except KeyError: message = f"No such contract {contract_name}. Available contracts are {ADMINISTRATOR.deployers.keys()}" emitter.echo(message, color='red', bold=True) raise click.Abort() # Deploy emitter.echo(f"Deploying {contract_name}") if contract_deployer._upgradeable and not bare: # NOTE: Bare deployments do not engage the proxy contract secret = ADMINISTRATOR.collect_deployment_secret( deployer=contract_deployer) receipts, agent = ADMINISTRATOR.deploy_contract( contract_name=contract_name, plaintext_secret=secret, gas_limit=gas, bare=bare) else: # Non-Upgradeable or Bare receipts, agent = ADMINISTRATOR.deploy_contract( contract_name=contract_name, gas_limit=gas, bare=bare) # Report paint_contract_deployment( contract_name=contract_name, contract_address=agent.contract_address, receipts=receipts, emitter=emitter, chain_name=deployer_interface.client.chain_name, open_in_browser=etherscan) return # Exit # # Deploy Automated Series (Create Registry) # # Confirm filesystem registry writes. if os.path.isfile(local_registry.filepath): emitter.echo( f"\nThere is an existing contract registry at {local_registry.filepath}.\n" f"Did you mean 'nucypher-deploy upgrade'?\n", color='yellow') click.confirm("*DESTROY* existing local registry and continue?", abort=True) os.remove(local_registry.filepath) # Stage Deployment secrets = ADMINISTRATOR.collect_deployment_secrets() paint_staged_deployment(deployer_interface=deployer_interface, administrator=ADMINISTRATOR, emitter=emitter) # Confirm Trigger Deployment 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) # Execute Deployment deployment_receipts = ADMINISTRATOR.deploy_network_contracts( secrets=secrets, emitter=emitter, interactive=not force, etherscan=etherscan) # Paint outfile paths registry_outfile = local_registry.filepath emitter.echo('Generated registry {}'.format(registry_outfile), bold=True, color='blue') # Save transaction metadata receipts_filepath = ADMINISTRATOR.save_deployment_receipts( receipts=deployment_receipts) emitter.echo(f"Saved deployment receipts to {receipts_filepath}", color='blue', bold=True) return # Exit elif action == "allocations": if not allocation_infile: allocation_infile = click.prompt("Enter allocation data filepath") click.confirm("Continue deploying and allocating?", abort=True) ADMINISTRATOR.deploy_beneficiaries_from_file( allocation_data_filepath=allocation_infile, allocation_outfile=allocation_outfile) return # Exit elif action == "transfer-tokens": token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=local_registry) if not target_address: target_address = click.prompt("Enter recipient's checksum address", type=EIP55_CHECKSUM_ADDRESS) if not value: stake_value_range = click.FloatRange(min=0, clamp=False) value = NU.from_tokens( click.prompt(f"Enter value in NU", type=stake_value_range)) click.confirm( f"Transfer {value} from {deployer_address} to {target_address}?", abort=True) receipt = token_agent.transfer(amount=value, sender_address=deployer_address, target_address=target_address) emitter.echo(f"OK | Receipt: {receipt['transactionHash'].hex()}") return # Exit elif action == "transfer-ownership": if not target_address: target_address = click.prompt("Enter new owner's checksum address", type=EIP55_CHECKSUM_ADDRESS) if contract_name: try: contract_deployer_class = ADMINISTRATOR.deployers[ contract_name] except KeyError: message = f"No such contract {contract_name}. Available contracts are {ADMINISTRATOR.deployers.keys()}" emitter.echo(message, color='red', bold=True) raise click.Abort() else: contract_deployer = contract_deployer_class( registry=ADMINISTRATOR.registry, deployer_address=ADMINISTRATOR.deployer_address) receipt = contract_deployer.transfer_ownership( new_owner=target_address, transaction_gas_limit=gas) emitter.ipc(receipt, request_id=0, duration=0) # TODO: #1216 return # Exit else: receipts = ADMINISTRATOR.relinquish_ownership( new_owner=target_address, transaction_gas_limit=gas) emitter.ipc(receipts, request_id=0, duration=0) # TODO: #1216 return # Exit else: raise click.BadArgumentUsage(message=f"Unknown action '{action}'")
def claim(general_config: GroupGeneralConfig, worklock_options: WorkLockOptions, force: bool, hw_wallet: bool): """Claim tokens for your escrow, and start staking them""" emitter, registry, blockchain = worklock_options.setup( general_config=general_config) worklock_agent = ContractAgency.get_agent( WorkLockAgent, registry=registry) # type: WorkLockAgent if not worklock_agent.is_claiming_available(): emitter.echo(CLAIMING_NOT_AVAILABLE, color='red') raise click.Abort() bidder_address = worklock_options.get_bidder_address(emitter, registry) bidder = worklock_options.create_bidder(registry=registry, hw_wallet=hw_wallet) unspent_bid = bidder.available_compensation if unspent_bid: emitter.echo( WORKLOCK_ADDITIONAL_COMPENSATION_AVAILABLE.format( amount=prettify_eth_amount(unspent_bid))) if not force: message = CONFIRM_REQUEST_WORKLOCK_COMPENSATION.format( bidder_address=bidder_address) click.confirm(message, abort=True) emitter.echo(REQUESTING_WORKLOCK_COMPENSATION) receipt = bidder.withdraw_compensation() paint_receipt_summary( receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) has_claimed = bidder.has_claimed if bool(has_claimed): emitter.echo(CLAIM_ALREADY_PLACED.format( bidder_address=bidder.checksum_address), color='red') raise click.Abort() tokens = NU.from_nunits(bidder.available_claim) emitter.echo(AVAILABLE_CLAIM_NOTICE.format(tokens=tokens), color='green', bold=True) if not force: lock_duration = bidder.worklock_agent.worklock_parameters()[-2] emitter.echo( WORKLOCK_CLAIM_ADVISORY.format(lock_duration=lock_duration), color='blue') click.confirm( CONFIRM_WORKLOCK_CLAIM.format(bidder_address=bidder_address), abort=True) emitter.echo(SUBMITTING_WORKLOCK_CLAIM) receipt = bidder.claim() paint_receipt_summary( receipt=receipt, emitter=emitter, chain_name=bidder.staking_agent.blockchain.client.chain_name) paint_worklock_claim(emitter=emitter, bidder_address=bidder_address, network=worklock_options.network, provider_uri=worklock_options.provider_uri)