def test_transfer_tokens(click_runner, registry_filepath): # # Setup # # Let's transfer some NU to a random stranger recipient_address = to_checksum_address(os.urandom(20)) registry = LocalContractRegistry(filepath=registry_filepath) token_agent = NucypherTokenAgent(registry=registry) assert token_agent.get_balance(address=recipient_address) == 0 command = [ 'transfer-tokens', '--target-address', recipient_address, '--value', 42, '--registry-infile', 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_transfer_tokens(click_runner, registry_filepath, get_random_checksum_address, testerchain): # Let's transfer some NU to a random stranger recipient_address = get_random_checksum_address() registry = LocalContractRegistry(filepath=registry_filepath) token_agent = NucypherTokenAgent(registry=registry) assert token_agent.get_balance(address=recipient_address) == 0 command = [ 'transfer-tokens', '--target-address', recipient_address, '--value', 42, '--registry-infile', registry_filepath, '--provider', TEST_PROVIDER_URI ] user_input = '0\n' + YES_ENTER + YES_ENTER 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 # Let's approve an allowance to a random spender spender_address = get_random_checksum_address() owner_address = testerchain.client.accounts[0] assert token_agent.get_allowance(spender=spender_address, owner=owner_address) == 0 command = [ 'transfer-tokens', '--target-address', spender_address, '--value', 42, '--allowance', '--registry-infile', registry_filepath, '--provider', TEST_PROVIDER_URI ] user_input = '0\n' + YES_ENTER + YES_ENTER result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Check that the NU was approved for the spender assert token_agent.get_allowance(spender=spender_address, owner=owner_address) == 42
def test_nucypher_deploy_contracts(testerchain, click_runner, mock_primary_registry_filepath): # We start with a blockchain node, and nothing else... assert not os.path.isfile(mock_primary_registry_filepath) command = ( 'contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--poa', ) user_input = 'Y\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' * 6 result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Check that the primary contract registry was written assert os.path.isfile(mock_primary_registry_filepath) # Now show that we can use contract Agency and read from the blockchain token_agent = NucypherTokenAgent() assert token_agent.get_balance() == 0 miner_agent = MinerAgent() assert miner_agent.get_current_period() testerchain.sever_connection()
def test_nucypher_deploy_contracts(testerchain, click_runner, mock_primary_registry_filepath): # We start with a blockchain node, and nothing else... assert not os.path.isfile(mock_primary_registry_filepath) command = ('contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--poa') user_input = 'Y\n' + f'{INSECURE_DEVELOPMENT_PASSWORD}\n' * 8 result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Ensure there is a report on each contract for registry_name in Deployer.contract_names: assert registry_name in result.output # Check that the primary contract registry was written # and peek at some of the registered entries assert os.path.isfile(mock_primary_registry_filepath) with open(mock_primary_registry_filepath, 'r') as file: # Ensure every contract's name was written to the file, somehow raw_registry_data = file.read() for registry_name in Deployer.contract_names: assert registry_name in raw_registry_data # Ensure the Registry is JSON deserializable registry_data = json.loads(raw_registry_data) # and that is has the correct number of entries assert len(registry_data) == 9 # Read several records token_record, escrow_record, dispatcher_record, *other_records = registry_data registered_name, registered_address, registered_abi = token_record token_agent = NucypherTokenAgent() assert token_agent.contract_name == registered_name assert token_agent.registry_contract_name == registered_name assert token_agent.contract_address == registered_address # Now show that we can use contract Agency and read from the blockchain assert token_agent.get_balance() == 0 miner_agent = MinerAgent() assert miner_agent.get_current_period() # and at least the others can be instantiated assert PolicyAgent() assert MiningAdjudicatorAgent() testerchain.sever_connection()
class NucypherTokenActor: """ Concrete base class for any actor that will interface with NuCypher's ethereum smart contracts. """ class ActorError(Exception): pass def __init__(self, checksum_address: str = None, blockchain: Blockchain = None) -> None: """ :param checksum_address: If not passed, we assume this is an unknown actor :param token_agent: The token agent with the blockchain attached; If not passed, A default token agent and blockchain connection will be created from default values. """ try: parent_address = self.checksum_public_address # type: str if checksum_address is not None: if parent_address != checksum_address: raise ValueError("Can't have two different addresses.") except AttributeError: self.checksum_public_address = checksum_address # type: str if blockchain is None: blockchain = Blockchain.connect() self.blockchain = blockchain self.token_agent = NucypherTokenAgent() self._transaction_cache = list( ) # type: list # track transactions transmitted def __repr__(self): class_name = self.__class__.__name__ r = "{}(address='{}')" r = r.format(class_name, self.checksum_public_address) return r @property def eth_balance(self): """Return this actors's current ETH balance""" balance = self.token_agent.blockchain.interface.w3.eth.getBalance( self.checksum_public_address) return balance @property def token_balance(self): """Return this actors's current token balance""" balance = self.token_agent.get_balance( address=self.checksum_public_address) return balance
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
class NucypherTokenActor: """ Concrete base class for any actor that will interface with NuCypher's ethereum smart contracts. """ class ActorError(Exception): pass def __init__(self, blockchain: BlockchainInterface, checksum_address: str = None): """ :param checksum_address: If not passed, we assume this is an unknown actor """ try: parent_address = self.checksum_address # type: str if checksum_address is not None: if parent_address != checksum_address: raise ValueError("Can't have two different addresses.") except AttributeError: self.checksum_address = checksum_address # type: str self.blockchain = blockchain self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self._saved_receipts = list( ) # type: list # track receipts of transmitted transactions def __repr__(self): class_name = self.__class__.__name__ r = "{}(address='{}')" r = r.format(class_name, self.checksum_address) return r def __eq__(self, other) -> bool: """Actors are equal if they have the same address.""" return bool(self.checksum_address == other.checksum_address) @property def eth_balance(self) -> Decimal: """Return this actors's current ETH balance""" balance = self.blockchain.client.get_balance(self.checksum_address) return self.blockchain.client.w3.fromWei(balance, 'ether') @property def token_balance(self) -> NU: """Return this actors's current token balance""" balance = int( self.token_agent.get_balance(address=self.checksum_address)) nu_balance = NU(balance, 'NuNit') return nu_balance
class NucypherTokenActor: """ Concrete base class for any actor that will interface with NuCypher's ethereum smart contracts. """ class ActorError(Exception): pass def __init__(self, checksum_address: str = None, blockchain: Blockchain = None): """ :param checksum_address: If not passed, we assume this is an unknown actor """ try: parent_address = self.checksum_address # type: str if checksum_address is not None: if parent_address != checksum_address: raise ValueError("Can't have two different addresses.") except AttributeError: self.checksum_address = checksum_address # type: str if blockchain is None: blockchain = Blockchain.connect() # Attempt to connect self.blockchain = blockchain self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) self._transaction_cache = list( ) # type: list # track transactions transmitted def __repr__(self): class_name = self.__class__.__name__ r = "{}(address='{}')" r = r.format(class_name, self.checksum_address) return r @property def eth_balance(self) -> Decimal: """Return this actors's current ETH balance""" balance = self.blockchain.interface.get_balance(self.checksum_address) return self.blockchain.interface.fromWei(balance, 'ether') @property def token_balance(self) -> NU: """Return this actors's current token balance""" balance = int( self.token_agent.get_balance(address=self.checksum_address)) nu_balance = NU(balance, 'NuNit') return nu_balance
class MinerEscrowDeployer(ContractDeployer): """ Deploys the MinerEscrow ethereum contract to the blockchain. Depends on NucypherTokenAgent """ agency = MinerAgent contract_name = agency.registry_contract_name _upgradeable = True __proxy_deployer = DispatcherDeployer def __init__(self, economics: TokenEconomics = None, *args, **kwargs): super().__init__(*args, **kwargs) self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) if not economics: economics = TokenEconomics() self.__economics = economics def __check_policy_manager(self): result = self.contract.functions.policyManager().call() if result is self.blockchain.NULL_ADDRESS: raise RuntimeError("PolicyManager contract is not initialized.") def deploy(self, secret_hash: bytes, gas_limit: int = None) -> dict: """ Deploy and publish the MinersEscrow contract to the blockchain network specified in self.blockchain.network. Deployment can only ever be executed exactly once! Emits the following blockchain network transactions: - MinerEscrow contract deployment - MinerEscrow dispatcher deployment - Transfer reward tokens origin -> MinerEscrow contract - MinerEscrow contract initialization Returns transaction hashes in a dict. """ # Raise if not all-systems-go self.check_deployment_readiness() # Build deployment arguments origin_args = { 'from': self.deployer_address, 'gasPrice': self.blockchain.interface.w3.eth.gasPrice } if gas_limit: origin_args.update({'gas': gas_limit}) # 1 - Deploy # the_escrow_contract, deploy_txhash, = \ self.blockchain.interface.deploy_contract(self.contract_name, self.token_agent.contract_address, *self.__economics.staking_deployment_parameters) # 2 - Deploy the dispatcher used for updating this contract # dispatcher_deployer = DispatcherDeployer( blockchain=self.blockchain, target_contract=the_escrow_contract, deployer_address=self.deployer_address) dispatcher_deploy_txhashes = dispatcher_deployer.deploy( secret_hash=secret_hash, gas_limit=gas_limit) # Cache the dispatcher contract dispatcher_contract = dispatcher_deployer.contract self.__dispatcher_contract = dispatcher_contract # Wrap the escrow contract wrapped_escrow_contract = self.blockchain.interface._wrap_contract( dispatcher_contract, target_contract=the_escrow_contract) # Switch the contract for the wrapped one the_escrow_contract = wrapped_escrow_contract # 3 - Transfer tokens to the miner escrow # reward_txhash = self.token_agent.contract.functions.transfer( the_escrow_contract.address, self.__economics.erc20_reward_supply).transact(origin_args) _reward_receipt = self.blockchain.wait_for_receipt(reward_txhash) escrow_balance = self.token_agent.get_balance( address=the_escrow_contract.address) # 4 - Initialize the Miner Escrow contract init_txhash = the_escrow_contract.functions.initialize().transact( origin_args) _init_receipt = self.blockchain.wait_for_receipt(init_txhash) # Gather the transaction hashes deployment_transactions = { 'deploy': deploy_txhash, 'dispatcher_deploy': dispatcher_deploy_txhashes['txhash'], 'reward_transfer': reward_txhash, 'initialize': init_txhash } # Set the contract and transaction hashes # self._contract = the_escrow_contract self.deployment_transactions = deployment_transactions return deployment_transactions def upgrade(self, existing_secret_plaintext: bytes, new_secret_hash: bytes): # Raise if not all-systems-go self.check_deployment_readiness() origin_args = { 'from': self.deployer_address, 'gas': 5000000 } # TODO: Gas management existing_bare_contract = self.blockchain.interface.get_contract_by_name( name=self.contract_name, proxy_name=self.__proxy_deployer.contract_name, use_proxy_address=False) dispatcher_deployer = DispatcherDeployer( blockchain=self.blockchain, target_contract=existing_bare_contract, deployer_address=self.deployer_address, bare=True) # acquire agency for the dispatcher itself. # 2 - Deploy new version # the_escrow_contract, deploy_txhash = self.blockchain.interface.deploy_contract( self.contract_name, self.token_agent.contract_address, *self.__economics.staking_deployment_parameters) # 5 - Wrap the escrow contract wrapped_escrow_contract = self.blockchain.interface._wrap_contract( wrapper_contract=dispatcher_deployer.contract, target_contract=the_escrow_contract) self._contract = wrapped_escrow_contract # 4 - Set the new Dispatcher target upgrade_tx_hash = dispatcher_deployer.retarget( new_target=the_escrow_contract.address, existing_secret_plaintext=existing_secret_plaintext, new_secret_hash=new_secret_hash) _upgrade_receipt = self.blockchain.wait_for_receipt(upgrade_tx_hash) # Respond upgrade_transaction = { 'deploy': deploy_txhash, 'retarget': upgrade_tx_hash } return upgrade_transaction def rollback(self, existing_secret_plaintext: bytes, new_secret_hash: bytes): existing_bare_contract = self.blockchain.interface.get_contract_by_name( name=self.contract_name, proxy_name=self.__proxy_deployer.contract_name, use_proxy_address=False) dispatcher_deployer = DispatcherDeployer( blockchain=self.blockchain, target_contract=existing_bare_contract, deployer_address=self.deployer_address, bare=True) # acquire agency for the dispatcher itself. rollback_txhash = dispatcher_deployer.rollback( existing_secret_plaintext=existing_secret_plaintext, new_secret_hash=new_secret_hash) _rollback_receipt = self.blockchain.wait_for_receipt( txhash=rollback_txhash) return rollback_txhash def make_agent(self) -> EthereumContractAgent: self.__check_policy_manager( ) # Ensure the PolicyManager contract has already been initialized agent = self.agency(blockchain=self.blockchain, contract=self._contract) return agent
def test_nucypher_deploy_contracts(click_runner, mock_allocation_infile, token_economics, registry_filepath): # # Main # command = [ 'contracts', '--registry-outfile', registry_filepath, '--provider', TEST_PROVIDER_URI, '--poa' ] user_input = '0\n' + 'Y\n' + (f'{INSECURE_SECRETS[1]}\n' * 8) + 'DEPLOY' result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Ensure there is a report on each contract contract_names = tuple(a.registry_contract_name for a in EthereumContractAgent.__subclasses__()) for registry_name in contract_names: assert registry_name in result.output # Check that the primary contract registry was written # and peek at some of the registered entries assert os.path.isfile(registry_filepath) with open(registry_filepath, 'r') as file: # Ensure every contract's name was written to the file, somehow raw_registry_data = file.read() for registry_name in contract_names: assert registry_name in raw_registry_data # Ensure the Registry is JSON deserializable registry_data = json.loads(raw_registry_data) # and that is has the correct number of entries assert len(registry_data) == 9 # Read several records token_record, escrow_record, dispatcher_record, *other_records = registry_data registered_name, registered_address, registered_abi = token_record # # Agency # registry = LocalContractRegistry(filepath=registry_filepath) token_agent = NucypherTokenAgent(registry=registry) assert token_agent.contract_name == registered_name assert token_agent.registry_contract_name == registered_name assert token_agent.contract_address == registered_address # Now show that we can use contract Agency and read from the blockchain assert token_agent.get_balance() == 0 staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=registry) assert staking_agent.get_current_period() # and at least the others can be instantiated assert PolicyManagerAgent(registry=registry) # This agent wasn't instantiated before, so we have to supply the blockchain blockchain = staking_agent.blockchain assert AdjudicatorAgent(registry=registry)
class StakingEscrowDeployer(ContractDeployer): """ Deploys the StakingEscrow ethereum contract to the blockchain. Depends on NucypherTokenAgent """ agency = StakingEscrowAgent contract_name = agency.registry_contract_name number_of_deployment_transactions = 4 _upgradeable = True __proxy_deployer = DispatcherDeployer def __init__(self, economics: TokenEconomics = None, *args, **kwargs): super().__init__(*args, **kwargs) self.token_agent = NucypherTokenAgent(blockchain=self.blockchain) if not economics: economics = TokenEconomics() self.__economics = economics self.__dispatcher_contract = None def __check_policy_manager(self): result = self.contract.functions.policyManager().call() if result == self.blockchain.NULL_ADDRESS: raise RuntimeError("PolicyManager contract is not initialized.") def _deploy_essential(self, gas_limit: int = None): escrow_constructor_args = ( self.token_agent.contract_address, *self.__economics.staking_deployment_parameters) the_escrow_contract, deploy_receipt = self.blockchain.deploy_contract( self.contract_name, *escrow_constructor_args, gas_limit=gas_limit) return the_escrow_contract, deploy_receipt def deploy(self, secret_hash: bytes, gas_limit: int = None, progress=None) -> dict: """ Deploy and publish the StakingEscrow contract to the blockchain network specified in self.blockchain.network. Deployment can only ever be executed exactly once! Emits the following blockchain network transactions: - StakingEscrow contract deployment - StakingEscrow dispatcher deployment - Transfer reward tokens origin -> StakingEscrow contract - StakingEscrow contract initialization Returns transaction hashes in a dict. """ # Raise if not all-systems-go self.check_deployment_readiness() # Build deployment arguments origin_args = {} if gas_limit: origin_args.update({'gas': gas_limit}) # 1 - Deploy # the_escrow_contract, deploy_receipt = self._deploy_essential( gas_limit=gas_limit) if progress: progress.update(1) # 2 - Deploy the dispatcher used for updating this contract # dispatcher_deployer = DispatcherDeployer( blockchain=self.blockchain, target_contract=the_escrow_contract, deployer_address=self.deployer_address) dispatcher_deploy_receipt = dispatcher_deployer.deploy( secret_hash=secret_hash, gas_limit=gas_limit) if progress: progress.update(1) # Cache the dispatcher contract dispatcher_contract = dispatcher_deployer.contract self.__dispatcher_contract = dispatcher_contract # Wrap the escrow contract wrapped_escrow_contract = self.blockchain._wrap_contract( dispatcher_contract, target_contract=the_escrow_contract) # Switch the contract for the wrapped one the_escrow_contract = wrapped_escrow_contract # 3 - Transfer the reward supply tokens to StakingEscrow # reward_function = self.token_agent.contract.functions.transfer( the_escrow_contract.address, self.__economics.erc20_reward_supply) reward_receipt = self.blockchain.send_transaction( contract_function=reward_function, sender_address=self.deployer_address, payload=origin_args) if progress: progress.update(1) # Make a call. _escrow_balance = self.token_agent.get_balance( address=the_escrow_contract.address) # 4 - Initialize the StakingEscrow contract init_function = the_escrow_contract.functions.initialize() init_receipt = self.blockchain.send_transaction( contract_function=init_function, sender_address=self.deployer_address, payload=origin_args) if progress: progress.update(1) # Gather the transaction hashes deployment_receipts = { 'deploy': deploy_receipt, 'dispatcher_deploy': dispatcher_deploy_receipt['deployment'], 'reward_transfer': reward_receipt, 'initialize': init_receipt } # Set the contract and transaction hashes # self._contract = the_escrow_contract self.deployment_receipts = deployment_receipts return deployment_receipts def upgrade(self, existing_secret_plaintext: bytes, new_secret_hash: bytes, gas_limit: int = None): # 1 - Raise if not all-systems-go # # TODO: Fails when this same object was used previously to deploy self.check_deployment_readiness() existing_bare_contract = self.blockchain.get_contract_by_name( name=self.contract_name, proxy_name=self.__proxy_deployer.contract_name, use_proxy_address=False) dispatcher_deployer = DispatcherDeployer( blockchain=self.blockchain, target_contract=existing_bare_contract, deployer_address=self.deployer_address, bare=True) # acquire agency for the dispatcher itself. # 2 - Deploy new version # new_escrow_contract, deploy_receipt = self._deploy_essential( gas_limit=gas_limit) # 3 - Wrap the escrow contract # wrapped_escrow_contract = self.blockchain._wrap_contract( wrapper_contract=dispatcher_deployer.contract, target_contract=new_escrow_contract) # 4 - Set the new Dispatcher target # upgrade_receipt = dispatcher_deployer.retarget( new_target=new_escrow_contract.address, existing_secret_plaintext=existing_secret_plaintext, new_secret_hash=new_secret_hash, gas_limit=gas_limit) # Respond upgrade_transaction = { 'deploy': deploy_receipt, 'retarget': upgrade_receipt } # Switch the contract for the wrapped one self._contract = wrapped_escrow_contract return upgrade_transaction def rollback(self, existing_secret_plaintext: bytes, new_secret_hash: bytes, gas_limit: int = None): existing_bare_contract = self.blockchain.get_contract_by_name( name=self.contract_name, proxy_name=self.__proxy_deployer.contract_name, use_proxy_address=False) dispatcher_deployer = DispatcherDeployer( blockchain=self.blockchain, target_contract=existing_bare_contract, deployer_address=self.deployer_address, bare=True) # acquire agency for the dispatcher itself. rollback_receipt = dispatcher_deployer.rollback( existing_secret_plaintext=existing_secret_plaintext, new_secret_hash=new_secret_hash, gas_limit=gas_limit) return rollback_receipt def make_agent(self) -> EthereumContractAgent: #self.__check_policy_manager() # Ensure the PolicyManager contract has already been initialized agent = self.agency(blockchain=self.blockchain, contract=self._contract) return agent
def test_nucypher_deploy_contracts(click_runner, token_economics, registry_filepath, testerchain): # # Main # assert not os.path.exists( registry_filepath), f"Registry File '{registry_filepath}' Exists." assert not os.path.lexists( registry_filepath), f"Registry File '{registry_filepath}' Exists." command = [ 'contracts', '--registry-outfile', registry_filepath, '--provider', TEST_PROVIDER_URI, '--se-test-mode' ] user_input = '0\n' + 'Y\n' + 'DEPLOY' result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Ensure there is a report on each primary contract contract_names = tuple( a.contract_name for a in ContractAdministrator.primary_deployer_classes) for registry_name in contract_names: assert registry_name in result.output # Check that the primary contract registry was written # and peek at some of the registered entries assert os.path.isfile(registry_filepath) with open(registry_filepath, 'r') as file: # Ensure every contract's name was written to the file, somehow raw_registry_data = file.read() for registry_name in contract_names: assert registry_name in raw_registry_data # Ensure the Registry is JSON deserializable registry_data = json.loads(raw_registry_data) # and that is has the correct number of entries assert len(registry_data) == 9 # Read several records token_record, escrow_record, dispatcher_record, *other_records = registry_data registered_name, registered_version, registered_address, registered_abi = token_record # # Agency # registry = LocalContractRegistry(filepath=registry_filepath) token_agent = NucypherTokenAgent(registry=registry) assert token_agent.contract_name == registered_name assert token_agent.registry_contract_name == registered_name assert token_agent.contract_address == registered_address assert token_agent.contract.version == registered_version # Now show that we can use contract Agency and read from the blockchain assert token_agent.get_balance() == 0 staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=registry) assert staking_agent.get_current_period() assert staking_agent.contract.functions.isTestContract().call() # and at least the others can be instantiated assert PolicyManagerAgent(registry=registry) assert AdjudicatorAgent(registry=registry)
def select_client_account(emitter, provider_uri: str, prompt: str = None, default: int = 0, registry=None, show_balances: bool = True, show_staking: bool = False, network: str = None, poa: bool = False ) -> str: """ Note: Setting show_balances to True, causes an eager contract and blockchain connection. """ # TODO: Break show_balances into show_eth_balance and show_token_balance if not provider_uri: raise ValueError("Provider URI must be provided to select a wallet account.") # Lazy connect the blockchain interface if not BlockchainInterfaceFactory.is_interface_initialized(provider_uri=provider_uri): BlockchainInterfaceFactory.initialize_interface(provider_uri=provider_uri, poa=poa, emitter=emitter) blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri) # Lazy connect to contracts token_agent = None if show_balances or show_staking: if not registry: registry = InMemoryContractRegistry.from_latest_publication(network=network) token_agent = NucypherTokenAgent(registry=registry) # Real wallet accounts enumerated_accounts = dict(enumerate(blockchain.client.accounts)) if len(enumerated_accounts) < 1: emitter.echo("No ETH accounts were found.", color='red', bold=True) raise click.Abort() # Display account info headers = ['Account'] if show_staking: headers.append('Staking') if show_balances: headers.extend(('', '')) rows = list() for index, account in enumerated_accounts.items(): row = [account] if show_staking: staker = Staker(is_me=True, checksum_address=account, registry=registry) staker.stakes.refresh() is_staking = 'Yes' if bool(staker.stakes) else 'No' row.append(is_staking) if show_balances: token_balance = NU.from_nunits(token_agent.get_balance(address=account)) ether_balance = Web3.fromWei(blockchain.client.get_balance(account=account), 'ether') row.extend((token_balance, f'{ether_balance} ETH')) rows.append(row) emitter.echo(tabulate(rows, headers=headers, showindex='always')) # Prompt the user for selection, and return prompt = prompt or "Select index of account" account_range = click.IntRange(min=0, max=len(enumerated_accounts)-1) choice = click.prompt(prompt, type=account_range, default=default) chosen_account = enumerated_accounts[choice] emitter.echo(f"Selected {choice}: {chosen_account}", color='blue') return chosen_account
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 INIT mode, i.e. stub for StakingEscrow # 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(progress=deployment_progress, deployment_mode=constants.INIT) assert not staking_escrow_deployer.is_deployed() # # Policy Manager # 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(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_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(progress=deployment_progress) assert adjudicator_deployer.is_deployed() adjudicator_agent = adjudicator_deployer.make_agent() assert adjudicator_agent.contract_address == adjudicator_deployer.contract_address # # StakingEscrow - in IDLE mode, i.e. without activation steps (approve_funding and initialize) # 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(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
class StakeHolder(BaseConfiguration): _NAME = 'stakeholder' TRANSACTION_GAS = {} class NoFundingAccount(BaseConfiguration.ConfigurationError): pass class NoStakes(BaseConfiguration.ConfigurationError): pass def __init__(self, blockchain: BlockchainInterface, sync_now: bool = True, *args, **kwargs): super().__init__(*args, **kwargs) self.log = Logger(f"stakeholder") # Blockchain and Contract connection self.blockchain = blockchain self.staking_agent = StakingEscrowAgent(blockchain=blockchain) self.token_agent = NucypherTokenAgent(blockchain=blockchain) self.economics = TokenEconomics() # Mode self.connect(blockchain=blockchain) self.__accounts = list() self.__stakers = dict() self.__transacting_powers = dict() self.__get_accounts() if sync_now: self.read_onchain_stakes() # Stakes # # Configuration # def static_payload(self) -> dict: """Values to read/write from stakeholder JSON configuration files""" payload = dict(provider_uri=self.blockchain.provider_uri, blockchain=self.blockchain.to_dict(), accounts=self.__accounts, stakers=self.__serialize_stakers()) return payload @classmethod def from_configuration_file(cls, filepath: str = None, sync_now: bool = True, **overrides) -> 'StakeHolder': filepath = filepath or cls.default_filepath() payload = cls._read_configuration_file(filepath=filepath) # Sub config blockchain_payload = payload.pop('blockchain') blockchain = BlockchainInterface.from_dict(payload=blockchain_payload) blockchain.connect(sync_now=sync_now) # TODO: Leave this here? payload.update(dict(blockchain=blockchain)) payload.update(overrides) instance = cls(filepath=filepath, **payload) return instance @validate_checksum_address def attach_transacting_power(self, checksum_address: str, password: str = None) -> None: try: transacting_power = self.__transacting_powers[checksum_address] except KeyError: transacting_power = TransactingPower(blockchain=self.blockchain, password=password, account=checksum_address) self.__transacting_powers[checksum_address] = transacting_power transacting_power.activate(password=password) def to_configuration_file(self, *args, **kwargs) -> str: filepath = super().to_configuration_file(*args, **kwargs) return filepath def connect(self, blockchain: BlockchainInterface = None) -> None: """Go Online""" if not self.staking_agent: self.staking_agent = StakingEscrowAgent(blockchain=blockchain) if not self.token_agent: self.token_agent = NucypherTokenAgent(blockchain=blockchain) self.blockchain = self.token_agent.blockchain # # Account Utilities # @property def accounts(self) -> list: return self.__accounts def __get_accounts(self) -> None: accounts = self.blockchain.client.accounts self.__accounts.extend(accounts) # # Staking Utilities # def read_onchain_stakes(self, account: str = None) -> None: if account: accounts = [account] else: accounts = self.__accounts for account in accounts: stakes = list( self.staking_agent.get_all_stakes(staker_address=account)) if stakes: staker = Staker(is_me=True, checksum_address=account, blockchain=self.blockchain) self.__stakers[account] = staker @property def total_stake(self) -> NU: total = sum(staker.locked_tokens() for staker in self.stakers) return total @property def stakers(self) -> List[Staker]: return list(self.__stakers.values()) @property def stakes(self) -> list: payload = list() for staker in self.__stakers.values(): payload.extend(staker.stakes) return payload @property def account_balances(self) -> dict: balances = dict() for account in self.__accounts: funds = { 'ETH': self.blockchain.client.get_balance(account), 'NU': self.token_agent.get_balance(account) } balances.update({account: funds}) return balances @property def staker_balances(self) -> dict: balances = dict() for staker in self.stakers: staker_funds = { 'ETH': staker.eth_balance, 'NU': staker.token_balance } balances[staker.checksum_address] = { staker.checksum_address: staker_funds } return balances def __serialize_stakers(self) -> list: payload = list() for staker in self.stakers: payload.append(staker.to_dict()) return payload def get_active_staker(self, address: str) -> Staker: self.read_onchain_stakes(account=address) try: return self.__stakers[address] except KeyError: raise self.NoStakes(f"{address} does not have any stakes.") def create_worker_configuration(self, staking_address: str, worker_address: str, password: str, **configuration): """Generates a worker JSON configuration file for a given staking address.""" from nucypher.config.characters import UrsulaConfiguration worker_configuration = UrsulaConfiguration.generate( checksum_address=staking_address, worker_address=worker_address, password=password, config_root=self.config_root, federated_only=False, provider_uri=self.blockchain.provider_uri, **configuration) return worker_configuration # # Actions # def set_worker(self, staker_address: str, worker_address: str, password: str = None): self.attach_transacting_power(checksum_address=staker_address, password=password) staker = self.get_active_staker(address=staker_address) receipt = self.staking_agent.set_worker( staker_address=staker.checksum_address, worker_address=worker_address) self.to_configuration_file(override=True) return receipt def initialize_stake( self, amount: NU, duration: int, checksum_address: str, password: str = None, ) -> Stake: # Existing Staker address if not is_checksum_address(checksum_address): raise ValueError( f"{checksum_address} is an invalid EIP-55 checksum address.") try: staker = self.__stakers[checksum_address] except KeyError: if checksum_address not in self.__accounts: raise ValueError( f"{checksum_address} is an unknown wallet address.") else: staker = Staker(is_me=True, checksum_address=checksum_address, blockchain=self.blockchain) # Don the transacting power for the staker's account. self.attach_transacting_power(checksum_address=staker.checksum_address, password=password) new_stake = staker.initialize_stake(amount=amount, lock_periods=duration) # Update local cache and save to disk. self.__stakers[checksum_address] = staker staker.stake_tracker.refresh( checksum_addresses=[staker.checksum_address]) self.to_configuration_file(override=True) return new_stake def divide_stake( self, address: str, index: int, value: NU, duration: int, password: str = None, ): staker = self.get_active_staker(address=address) if not staker.is_staking: raise Stake.StakingError( f"{staker.checksum_address} has no published stakes.") self.attach_transacting_power(checksum_address=staker.checksum_address, password=password) result = staker.divide_stake(stake_index=index, additional_periods=duration, target_value=value) # Save results to disk self.to_configuration_file(override=True) return result def calculate_rewards(self) -> dict: rewards = dict() for staker in self.stakers: reward = staker.calculate_reward() rewards[staker.checksum_address] = reward return rewards def collect_rewards(self, staker_address: str, password: str = None, withdraw_address: str = None, staking: bool = True, policy: bool = True) -> Dict[str, dict]: if not staking and not policy: raise ValueError( "Either staking or policy must be True in order to collect rewards" ) try: staker = self.get_active_staker(address=staker_address) except self.NoStakes: staker = Staker(is_me=True, checksum_address=staker_address, blockchain=self.blockchain) self.attach_transacting_power(checksum_address=staker.checksum_address, password=password) receipts = dict() if staking: receipts['staking_reward'] = staker.collect_staking_reward() if policy: receipts['policy_reward'] = staker.collect_policy_reward( collector_address=withdraw_address) self.to_configuration_file(override=True) return receipts
def test_nucypher_deploy_contracts(click_runner, mock_primary_registry_filepath, mock_allocation_infile, token_economics): Agency.clear() # # Setup # # We start with a blockchain node, and nothing else... if os.path.isfile(mock_primary_registry_filepath): os.remove(mock_primary_registry_filepath) assert not os.path.isfile(mock_primary_registry_filepath) # # Main # command = [ 'contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--poa' ] user_input = '0\n' + 'Y\n' + (f'{INSECURE_SECRETS[1]}\n' * 8) + 'DEPLOY' result = click_runner.invoke(deploy, command, input=user_input, catch_exceptions=False) assert result.exit_code == 0 # Ensure there is a report on each contract for registry_name in Deployer.contract_names: assert registry_name in result.output # Check that the primary contract registry was written # and peek at some of the registered entries assert os.path.isfile(mock_primary_registry_filepath) with open(mock_primary_registry_filepath, 'r') as file: # Ensure every contract's name was written to the file, somehow raw_registry_data = file.read() for registry_name in Deployer.contract_names: assert registry_name in raw_registry_data # Ensure the Registry is JSON deserializable registry_data = json.loads(raw_registry_data) # and that is has the correct number of entries assert len(registry_data) == 9 # Read several records token_record, escrow_record, dispatcher_record, *other_records = registry_data registered_name, registered_address, registered_abi = token_record # # Agency # token_agent = NucypherTokenAgent() assert token_agent.contract_name == registered_name assert token_agent.registry_contract_name == registered_name assert token_agent.contract_address == registered_address # Now show that we can use contract Agency and read from the blockchain assert token_agent.get_balance() == 0 staking_agent = StakingEscrowAgent() assert staking_agent.get_current_period() # and at least the others can be instantiated assert PolicyAgent()