def confirm_airdrop(_results): recipient = testerchain.interface.w3.eth.accounts[-1] miner = Miner(checksum_address=recipient, blockchain=testerchain, is_me=True) assert miner.token_balance == NU(15000, 'NU')
def miner(testerchain, three_agents): token_agent, miner_agent, policy_agent = three_agents origin, *everybody_else = testerchain.interface.w3.eth.accounts token_airdrop(token_agent, origin=testerchain.etherbase_account, addresses=everybody_else, amount=DEVELOPMENT_TOKEN_AIRDROP_AMOUNT) miner = Miner(checksum_address=everybody_else[0], is_me=True) return miner
def miner(self, testerchain, three_agents): token_agent, miner_agent, policy_agent = three_agents origin, *everybody_else = testerchain.interface.w3.eth.accounts token_airdrop(token_agent, origin=origin, addresses=everybody_else, amount=1000000 * constants.M) miner = Miner(miner_agent=miner_agent, checksum_address=everybody_else[0]) return miner
def confirm_airdrop(_results): recipient = testerchain.interface.w3.eth.accounts[-1] miner = Miner(checksum_address=recipient, blockchain=testerchain, is_me=True) assert miner.token_balance == NU(15000, 'NU') new_eth_balance = original_eth_balance + testerchain.interface.w3.fromWei( Felix.ETHER_AIRDROP_AMOUNT, 'ether') assert miner.eth_balance == new_eth_balance
def spawn_random_staking_ursulas(miner_agent, addresses: list) -> list: """ Deposit and lock a random amount of tokens in the miner escrow from each address, "spawning" new Miners. """ from nucypher.blockchain.eth.actors import Miner miners = list() for address in addresses: miner = Miner(miner_agent=miner_agent, checksum_address=address) miners.append(miner) # stake a random amount min_stake, balance = constants.MIN_ALLOWED_LOCKED, miner.token_balance amount = random.randint(min_stake, balance) # for a random lock duration min_locktime, max_locktime = constants.MIN_LOCKED_PERIODS, constants.MAX_MINTING_PERIODS periods = random.randint(min_locktime, max_locktime) miner.initialize_stake(amount=amount, lock_periods=periods) return miners
def spawn_random_miners(self, addresses: list) -> list: """ Deposit and lock a random amount of tokens in the miner escrow from each address, "spawning" new Miners. """ from nucypher.blockchain.eth.actors import Miner miners = list() for address in addresses: miner = Miner(miner_agent=self, address=address) miners.append(miner) # stake a random amount min_stake, balance = self.min_allowed_locked, miner.token_balance() amount = random.randint(min_stake, balance) # for a random lock duration min_locktime, max_locktime = self.min_locked_periods, self.max_minting_periods periods = random.randint(min_locktime, max_locktime) miner.stake(amount=amount, lock_periods=periods) return miners
def get_arrangement(self, arrangement_id: bytes) -> BlockchainArrangement: """Fetch a published arrangement from the blockchain""" blockchain_record = self.policy_agent.read().policies(arrangement_id) author_address, miner_address, rate, start_block, end_block, downtime_index = blockchain_record duration = end_block - start_block miner = Miner(address=miner_address, miner_agent=self.policy_agent.miner_agent) arrangement = BlockchainArrangement(author=self, miner=miner, lock_periods=duration) arrangement.is_published = True return arrangement
def get_arrangement(self, arrangement_id: bytes) -> BlockchainArrangement: """Fetch published arrangements from the blockchain""" blockchain_record = self.author.policy_agent.read().policies(arrangement_id) author_address, miner_address, rate, start_block, end_block, downtime_index = blockchain_record duration = end_block - start_block miner = Miner(address=miner_address, miner_agent=self.author.policy_agent.miner_agent) arrangement = BlockchainArrangement(author=self.author, miner=miner, value=rate*duration, # TODO Check the math/types here lock_periods=duration, expiration=end_block) # TODO: fix missing argument here arrangement.is_published = True return arrangement
def test_collect_rewards_integration(click_runner, configuration_file_location, blockchain_alice, blockchain_bob, random_policy_label, staking_participant, token_economics, policy_value, policy_rate): blockchain = staking_participant.blockchain half_stake_time = token_economics.minimum_locked_periods // 2 # Test setup logger = staking_participant.log # Enter the Teacher's Logger, and current_period = 1 # State the initial period for incrementing miner = Miner(is_me=True, checksum_address=staking_participant.checksum_address, blockchain=blockchain) # The miner is staking. assert miner.stakes assert miner.is_staking pre_stake_token_balance = miner.token_balance # Confirm for half the first stake duration for _ in range(half_stake_time): current_period += 1 logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") blockchain.time_travel(periods=1) miner.confirm_activity() # Alice creates a policy and grants Bob access blockchain_alice.selection_buffer = 1 M, N = 1, 1 expiration = maya.now() + datetime.timedelta(days=3) blockchain_policy = blockchain_alice.grant( bob=blockchain_bob, label=random_policy_label, m=M, n=N, value=policy_value, expiration=expiration, handpicked_ursulas={staking_participant}) # Ensure that the handpicked Ursula was selected for the policy arrangement = list(blockchain_policy._accepted_arrangements)[0] assert arrangement.ursula == staking_participant # Bob learns about the new staker and joins the policy blockchain_bob.start_learning_loop() blockchain_bob.remember_node(node=staking_participant) 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} <<<<<<<<<<<<<<<<") # Encrypt random_data = os.urandom(random.randrange(20, 100)) ciphertext, signature = enrico.encrypt_message(message=random_data) # Decrypt cleartexts = blockchain_bob.retrieve(message_kit=ciphertext, data_source=enrico, alice_verifying_key=verifying_key, label=random_policy_label) assert random_data == cleartexts[0] # Ursula Staying online and the clock advancing blockchain.time_travel(periods=1) miner.confirm_activity() current_period += 1 # Finish the passage of time for the first Stake for _ in range(5): # plus the extended periods from stake division current_period += 1 logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") blockchain.time_travel(periods=1) miner.confirm_activity() # # WHERES THE MONEY URSULA?? - Collecting Rewards # # The address the client wants Ursula to send rewards to burner_wallet = blockchain.interface.w3.eth.account.create( INSECURE_DEVELOPMENT_PASSWORD) # The rewards wallet is initially empty, because it is freshly created assert blockchain.interface.w3.eth.getBalance(burner_wallet.address) == 0 # Snag a random teacher from the fleet collection_args = ('--mock-networking', 'ursula', 'collect-reward', '--config-file', configuration_file_location, '--withdraw-address', burner_wallet.address, '--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 = blockchain.interface.w3.eth.getBalance( burner_wallet.address) expected_collection = policy_rate * 30 assert collected_policy_reward == expected_collection # Finish the passage of time... once and for all # Extended periods from stake division for _ in range(9): current_period += 1 logger.debug( f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<") blockchain.time_travel(periods=1) miner.confirm_activity() # Staking Reward calculated_reward = miner.miner_agent.calculate_staking_reward( checksum_address=miner.checksum_address) assert calculated_reward assert miner.token_balance > pre_stake_token_balance
def simulate(config, action, nodes, federated_only, geth): """ Simulate the nucypher blockchain network Arguments ========== action - Which action to perform; The choices are: - start: Start a multi-process nucypher network simulation - stop: Stop a running simulation gracefully Options ======== --nodes - The quantity of nodes (processes) to execute during the simulation --duration = The number of periods to run the simulation before termination """ if action == 'start': # # Blockchain Connection # if not federated_only: if geth: test_provider_uri = "ipc:///tmp/geth.ipc" else: test_provider_uri = "pyevm://tester" simulation_registry = TemporaryEthereumContractRegistry() simulation_interface = BlockchainDeployerInterface(provider_uri=test_provider_uri, registry=simulation_registry, compiler=SolidityCompiler()) blockchain = TesterBlockchain(interface=simulation_interface, test_accounts=nodes, airdrop=False) accounts = blockchain.interface.w3.eth.accounts origin, *everyone_else = accounts # Set the deployer address from the freshly created test account simulation_interface.deployer_address = origin # # Blockchain Action # blockchain.ether_airdrop(amount=DEVELOPMENT_ETH_AIRDROP_AMOUNT) click.confirm("Deploy all nucypher contracts to {}?".format(test_provider_uri), abort=True) click.echo("Bootstrapping simulated blockchain network") # Deploy contracts token_deployer = NucypherTokenDeployer(blockchain=blockchain, deployer_address=origin) token_deployer.arm() token_deployer.deploy() token_agent = token_deployer.make_agent() miners_escrow_secret = os.urandom(DISPATCHER_SECRET_LENGTH) miner_escrow_deployer = MinerEscrowDeployer(token_agent=token_agent, deployer_address=origin, secret_hash=miners_escrow_secret) miner_escrow_deployer.arm() miner_escrow_deployer.deploy() miner_agent = miner_escrow_deployer.make_agent() policy_manager_secret = os.urandom(DISPATCHER_SECRET_LENGTH) policy_manager_deployer = PolicyManagerDeployer(miner_agent=miner_agent, deployer_address=origin, secret_hash=policy_manager_secret) policy_manager_deployer.arm() policy_manager_deployer.deploy() policy_agent = policy_manager_deployer.make_agent() airdrop_amount = DEVELOPMENT_TOKEN_AIRDROP_AMOUNT click.echo("Airdropping tokens {} to {} addresses".format(airdrop_amount, len(everyone_else))) _receipts = token_airdrop(token_agent=token_agent, origin=origin, addresses=everyone_else, amount=airdrop_amount) # Commit the current state of deployment to a registry file. click.echo("Writing filesystem registry") _sim_registry_name = blockchain.interface.registry.commit(filepath=DEFAULT_SIMULATION_REGISTRY_FILEPATH) click.echo("Ready to run swarm.") # # Swarm # # Select a port range to use on localhost for sim servers if not federated_only: sim_addresses = everyone_else else: sim_addresses = NotImplemented start_port = 8787 counter = 0 for sim_port_number, sim_address in enumerate(sim_addresses, start=start_port): # # Parse ursula parameters # rest_port = sim_port_number db_name = 'sim-{}'.format(rest_port) cli_exec = os.path.join(BASE_DIR, 'cli', 'main.py') python_exec = 'python' proc_params = ''' python3 {} run_ursula --rest-port {} --db-name {} '''.format(python_exec, cli_exec, rest_port, db_name).split() if federated_only: proc_params.append('--federated-only') else: token_agent = NucypherTokenAgent(blockchain=blockchain) miner_agent = MinerAgent(token_agent=token_agent) miner = Miner(miner_agent=miner_agent, checksum_address=sim_address) # stake a random amount min_stake, balance = MIN_ALLOWED_LOCKED, miner.token_balance value = random.randint(min_stake, balance) # for a random lock duration min_locktime, max_locktime = MIN_LOCKED_PERIODS, MAX_MINTING_PERIODS periods = random.randint(min_locktime, max_locktime) miner.initialize_stake(amount=value, lock_periods=periods) click.echo("{} Initialized new stake: {} tokens for {} periods".format(sim_address, value, periods)) proc_params.extend('--checksum-address {}'.format(sim_address).split()) # Spawn click.echo("Spawning node #{}".format(counter+1)) processProtocol = UrsulaProcessProtocol(command=proc_params) cli_exec = os.path.join(BASE_DIR, 'cli', 'main.py') ursula_proc = reactor.spawnProcess(processProtocol, cli_exec, proc_params) # # post-spawnProcess # # Start with some basic status data, then build on it rest_uri = "http://{}:{}".format('localhost', rest_port) sim_data = "Started simulated Ursula | ReST {}".format(rest_uri) rest_uri = "{host}:{port}".format(host='localhost', port=str(sim_port_number)) sim_data.format(rest_uri) # if not federated_only: # stake_infos = tuple(config.miner_agent.get_all_stakes(miner_address=sim_address)) # sim_data += '| ETH address {}'.format(sim_address) # sim_data += '| {} Active stakes '.format(len(stake_infos)) click.echo(sim_data) counter += 1 click.echo("Starting the reactor") click.confirm("Start the reactor?", abort=True) try: reactor.run() finally: if not federated_only: click.echo("Removing simulation registry") os.remove(DEFAULT_SIMULATION_REGISTRY_FILEPATH) click.echo("Stopping simulated Ursula processes") for process in config.sim_processes: os.kill(process.pid, 9) click.echo("Killed {}".format(process)) click.echo("Simulation completed") elif action == 'stop': # Kill the simulated ursulas for process in config.ursula_processes: process.transport.signalProcess('KILL') elif action == 'status': if not config.simulation_running: status_message = "Simulation not running." else: ursula_processes = len(config.ursula_processes) status_message = """ | Node Swarm Simulation Status | Simulation processes .............. {} """.format(ursula_processes) click.echo(status_message) elif action == 'demo': """Run the finnegans wake demo""" demo_exec = os.path.join(BASE_DIR, 'cli', 'demos', 'finnegans-wake-demo.py') process_args = [sys.executable, demo_exec] if federated_only: process_args.append('--federated-only') subprocess.run(process_args, stdout=subprocess.PIPE)
def test_collect_rewards_integration( click_runner, funded_blockchain, configuration_file_location, alice_blockchain_test_config, bob_blockchain_test_config, charlie_blockchain_test_config, random_policy_label, blockchain_ursulas, staking_participant): blockchain = staking_participant.blockchain # Alice creates a policy and grants Bob access alice = alice_blockchain_test_config.produce( blockchain=funded_blockchain, network_middleware=MockRestMiddleware(), known_nodes=blockchain_ursulas) bob = bob_blockchain_test_config.produce( blockchain=blockchain, network_middleware=MockRestMiddleware(), known_nodes=blockchain_ursulas) # # Back to the Ursulas... # half_stake_time = MIN_LOCKED_PERIODS // 2 # Test setup logger = staking_participant.log # Enter the Teacher's Logger, and current_period = 1 # State the initial period for incrementing miner = Miner(checksum_address=staking_participant.checksum_public_address, blockchain=blockchain, is_me=True) pre_stake_eth_balance = miner.eth_balance # Finish the passage of time... once and for all for _ in range(half_stake_time): current_period += 1 logger.debug(f"period {current_period}") blockchain.time_travel(periods=1) miner.confirm_activity() M, N = 1, 1 expiration = maya.now() + datetime.timedelta(days=3) blockchain_policy = alice.grant(bob=bob, label=random_policy_label, m=M, n=1, value=POLICY_VALUE, expiration=expiration, handpicked_ursulas={staking_participant}) # Bob joins the policy bob.join_policy(random_policy_label, bytes(alice.stamp)) # Enrico Encrypts (of course) enrico = Enrico(policy_encrypting_key=blockchain_policy.public_key, network_middleware=MockRestMiddleware()) for index, _period in enumerate(range(half_stake_time - 5)): logger.debug(f"period {current_period}") alphabet = string.ascii_letters + string.digits # Random Request Periods if not random.choice((True, False)): continue # maybe re-encrypt max_reencryptions_per_period = 5 quantity = random.choice(range(max_reencryptions_per_period + 1)) quantity *= index # factorial or 0 verifying_key = UmbralPublicKey.from_bytes(bytes(alice.stamp)) # Random Re-encryptions for _i in range(quantity): # Encrypt random_data = ''.join( secrets.choice(alphabet) for i in range(secrets.choice(range(20, 100)))) ciphertext, signature = enrico.encrypt_message( message=bytes(random_data, encoding='utf-8')) # Retrieve payload = dict(message_kit=ciphertext, data_source=enrico, alice_verifying_key=verifying_key, label=random_policy_label) _cleartext = bob.retrieve(**payload) # Ursula Staying online and the clock advancing blockchain.time_travel(periods=1) miner.confirm_activity() current_period += 1 # Finish the passage of time... once and for all for _ in range(5): current_period += 1 logger.debug(f"period {current_period}") blockchain.time_travel(periods=1) miner.confirm_activity() # # WHERES THE MONEY URSULA?? - Collecting Rewards # # The address the client wants Ursula to send rewards to burner_wallet = blockchain.interface.w3.eth.account.create( INSECURE_DEVELOPMENT_PASSWORD) assert blockchain.interface.w3.eth.getBalance(burner_wallet.address) == 0 # Snag a random teacher from the fleet random_teacher = list(blockchain_ursulas).pop() collection_args = ('--mock-networking', 'ursula', 'collect-reward', '--teacher-uri', random_teacher.rest_interface, '--config-file', configuration_file_location, '--withdraw-address', burner_wallet.address, '--poa', '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 collected_reward = blockchain.interface.w3.eth.getBalance( burner_wallet.address) assert collected_reward != 0 expected_reward = Web3.toWei(21, 'gwei') * 30 * M assert collected_reward == expected_reward assert miner.eth_balance == pre_stake_eth_balance
def __init__( self, # Ursula rest_host: str, rest_port: int, domains: Set = ( GLOBAL_DOMAIN, ), # For now, serving and learning domains will be the same. certificate: Certificate = None, certificate_filepath: str = None, db_filepath: str = None, is_me: bool = True, interface_signature=None, timestamp=None, # Blockchain identity_evidence: bytes = constants.NOT_SIGNED, checksum_public_address: str = None, # Character password: str = None, abort_on_learning_error: bool = False, federated_only: bool = False, start_learning_now: bool = None, crypto_power=None, tls_curve: EllipticCurve = None, known_nodes: Iterable = None, **character_kwargs) -> None: # # Character # self._work_orders = list() Character.__init__(self, is_me=is_me, checksum_public_address=checksum_public_address, start_learning_now=start_learning_now, federated_only=federated_only, crypto_power=crypto_power, abort_on_learning_error=abort_on_learning_error, known_nodes=known_nodes, domains=domains, **character_kwargs) # # Self-Ursula # if is_me is True: # TODO: 340 self._stored_treasure_maps = dict() # # Staking Ursula # if not federated_only: Miner.__init__(self, is_me=is_me, checksum_address=checksum_public_address) # Access staking node via node's transacting keys TODO: Better handle ephemeral staking self ursula blockchain_power = BlockchainPower( blockchain=self.blockchain, account=self.checksum_public_address) self._crypto_power.consume_power_up(blockchain_power) # Use blockchain power to substantiate stamp, instead of signing key self.substantiate_stamp( password=password) # TODO: Derive from keyring # # ProxyRESTServer and TLSHostingPower # TODO: Maybe we want _power_ups to be public after all? # if not crypto_power or (TLSHostingPower not in crypto_power._power_ups): # # Ephemeral Self-Ursula # if is_me: self.suspicious_activities_witnessed = { 'vladimirs': [], 'bad_treasure_maps': [] } # # REST Server (Ephemeral Self-Ursula) # rest_app, datastore = make_rest_app( db_filepath=db_filepath, network_middleware=self.network_middleware, federated_only=self.federated_only, # TODO: 466 treasure_map_tracker=self.treasure_maps, node_tracker=self.known_nodes, node_bytes_caster=self.__bytes__, work_order_tracker=self._work_orders, node_recorder=self.remember_node, stamp=self.stamp, verifier=self.verify_from, suspicious_activity_tracker=self. suspicious_activities_witnessed, serving_domains=domains, ) # # TLSHostingPower (Ephemeral Self-Ursula) # tls_hosting_keypair = HostingKeypair( curve=tls_curve, host=rest_host, checksum_public_address=self.checksum_public_address) tls_hosting_power = TLSHostingPower( keypair=tls_hosting_keypair, host=rest_host) self.rest_server = ProxyRESTServer( rest_host=rest_host, rest_port=rest_port, rest_app=rest_app, datastore=datastore, hosting_power=tls_hosting_power) # # Stranger-Ursula # else: # TLSHostingPower if certificate or certificate_filepath: tls_hosting_power = TLSHostingPower( host=rest_host, public_certificate_filepath=certificate_filepath, public_certificate=certificate) else: tls_hosting_keypair = HostingKeypair( curve=tls_curve, host=rest_host, generate_certificate=False) tls_hosting_power = TLSHostingPower( host=rest_host, keypair=tls_hosting_keypair) # REST Server # Unless the caller passed a crypto power we'll make our own TLSHostingPower for this stranger. self.rest_server = ProxyRESTServer( rest_host=rest_host, rest_port=rest_port, hosting_power=tls_hosting_power) # # OK - Now we have a ProxyRestServer and a TLSHostingPower for some Ursula # self._crypto_power.consume_power_up(tls_hosting_power) # Consume! # # Verifiable Node # certificate_filepath = self._crypto_power.power_ups( TLSHostingPower).keypair.certificate_filepath certificate = self._crypto_power.power_ups( TLSHostingPower).keypair.certificate Teacher.__init__( self, domains=domains, certificate=certificate, certificate_filepath=certificate_filepath, interface_signature=interface_signature, timestamp=timestamp, identity_evidence=identity_evidence, substantiate_immediately=is_me and not federated_only, ) # # Logging / Updating # if is_me: self.known_nodes.record_fleet_state( additional_nodes_to_track=[self]) message = "THIS IS YOU: {}: {}".format(self.__class__.__name__, self) self.log.info(message) else: message = "Initialized Stranger {} | {}".format( self.__class__.__name__, self) self.log.debug(message)
def simulate(config, action, nodes, federated_only): """ Simulate the nucypher blockchain network Arguments ========== action - Which action to perform; The choices are: - start: Start a multi-process nucypher network simulation - stop: Stop a running simulation gracefully Options ======== --nodes - The quantity of nodes (processes) to execute during the simulation --duration = The number of periods to run the simulation before termination """ if action == 'init': # OK, Try connecting to the blockchain click.echo("Connecting to provider endpoint") config.connect_to_blockchain() # Actual simulation setup logic one_million_eth = 10**6 * 10**18 click.echo("Airdropping {} ETH to {} test accounts".format( one_million_eth, len(config.accounts))) config.blockchain.ether_airdrop( amount=one_million_eth) # wei -> ether | 1 Million ETH # Fin click.echo("Blockchain initialized") if action == 'deploy': if config.simulation_running is True: raise RuntimeError("Network simulation already running") if not federated_only: config.connect_to_blockchain() click.confirm("Deploy all nucypher contracts to blockchain?", abort=True) click.echo("Bootstrapping simulated blockchain network") blockchain = TesterBlockchain.from_config() # TODO: Enforce Saftey - ensure this is "fake" # conditions = () assert True # Parse addresses etherbase, *everybody_else = blockchain.interface.w3.eth.accounts # Deploy contracts token_deployer = NucypherTokenDeployer(blockchain=blockchain, deployer_address=etherbase) token_deployer.arm() token_deployer.deploy() token_agent = token_deployer.make_agent() click.echo("Deployed {}:{}".format(token_agent.contract_name, token_agent.contract_address)) miner_escrow_deployer = MinerEscrowDeployer( token_agent=token_agent, deployer_address=etherbase) miner_escrow_deployer.arm() miner_escrow_deployer.deploy() miner_agent = miner_escrow_deployer.make_agent() click.echo("Deployed {}:{}".format(miner_agent.contract_name, miner_agent.contract_address)) policy_manager_deployer = PolicyManagerDeployer( miner_agent=miner_agent, deployer_address=etherbase) policy_manager_deployer.arm() policy_manager_deployer.deploy() policy_agent = policy_manager_deployer.make_agent() click.echo("Deployed {}:{}".format(policy_agent.contract_name, policy_agent.contract_address)) airdrop_amount = 1000000 * int(constants.M) click.echo("Airdropping tokens {} to {} addresses".format( airdrop_amount, len(everybody_else))) _receipts = token_airdrop(token_agent=token_agent, origin=etherbase, addresses=everybody_else, amount=airdrop_amount) click.echo("Connecting to deployed contracts") config.connect_to_contracts() # Commit the current state of deployment to a registry file. click.echo("Writing filesystem registry") _sim_registry_name = config.blockchain.interface._registry.commit( filepath=DEFAULT_SIMULATION_REGISTRY_FILEPATH) # Fin click.echo("Ready to simulate decentralized swarm.") else: click.echo("Ready to run federated swarm.") elif action == 'swarm': if not federated_only: config.connect_to_blockchain() config.connect_to_contracts(simulation=True) localhost = 'localhost' # Select a port range to use on localhost for sim servers start_port, stop_port = DEFAULT_SIMULATION_PORT, DEFAULT_SIMULATION_PORT + int( nodes) port_range = range(start_port, stop_port) click.echo("Selected local ports {}-{}".format(start_port, stop_port)) for index, sim_port_number in enumerate(port_range): # # Parse ursula parameters # rest_port, dht_port = sim_port_number, sim_port_number + 100 db_name = 'sim-{}'.format(rest_port) proc_params = ''' run_ursula --host {} --rest-port {} --dht-port {} --db-name {} '''.format(localhost, rest_port, dht_port, db_name).split() if federated_only: click.echo("Setting federated operating mode") proc_params.append('--federated-only') else: sim_address = config.accounts[index + 1] miner = Miner(miner_agent=config.miner_agent, checksum_address=sim_address) # stake a random amount min_stake, balance = constants.MIN_ALLOWED_LOCKED, miner.token_balance value = random.randint(min_stake, balance) # for a random lock duration min_locktime, max_locktime = constants.MIN_LOCKED_PERIODS, constants.MAX_MINTING_PERIODS periods = random.randint(min_locktime, max_locktime) miner.initialize_stake(amount=value, lock_periods=periods) click.echo( "{} Initialized new stake: {} tokens for {} periods". format(sim_address, value, periods)) proc_params.extend( '--checksum-address {}'.format(sim_address).split()) # Spawn click.echo("Spawning node #{}".format(index + 1)) processProtocol = UrsulaProcessProtocol(command=proc_params) ursula_proc = reactor.spawnProcess(processProtocol, "nucypher-cli", proc_params) config.sim_processes.append(ursula_proc) # # post-spawnProcess # # Start with some basic status data, then build on it rest_uri = "http://{}:{}".format(localhost, rest_port) dht_uri = "http://{}:{}".format(localhost, dht_port) sim_data = "Started simulated Ursula | ReST {} | DHT {} ".format( rest_uri, dht_uri) rest_uri = "{host}:{port}".format(host=localhost, port=str(sim_port_number)) dht_uri = '{host}:{port}'.format(host=localhost, port=dht_port) sim_data.format(rest_uri, dht_uri) if not federated_only: stake_infos = tuple( config.miner_agent.get_all_stakes( miner_address=sim_address)) sim_data += '| ETH address {}'.format(sim_address) sim_data += '| {} Active stakes '.format(len(stake_infos)) click.echo(sim_data) config.simulation_running = True click.echo("Starting the reactor") try: reactor.run() finally: if config.operating_mode == 'decentralized': click.echo("Removing simulation registry") os.remove(config.sim_registry_filepath) click.echo("Stopping simulated Ursula processes") for process in config.sim_processes: os.kill(process.pid, 9) click.echo("Killed {}".format(process)) config.simulation_running = False click.echo("Simulation stopped") elif action == 'stop': # Kill the simulated ursulas TODO: read PIDs from storage? if config.simulation_running is not True: raise RuntimeError("Network simulation is not running") for process in config.ursula_processes: process.transport.signalProcess('KILL') else: # TODO: Confirm they are dead config.simulation_running = False elif action == 'status': if not config.simulation_running: status_message = "Simulation not running." else: ursula_processes = len(config.ursula_processes) status_message = """ | Node Swarm Simulation Status | Simulation processes .............. {} """.format(ursula_processes) click.echo(status_message) elif action == 'demo': """Run the finnegans wake demo""" demo_exec = os.path.join(BASE_DIR, 'nucypher_cli', 'demos', 'finnegans-wake-demo.py') subprocess.run([sys.executable, demo_exec], stdout=subprocess.PIPE)
def __init__(self, # Ursula rest_host: str, rest_port: int, certificate: Certificate = None, # TODO: from_certificate classmethod instead, use only filepath..? certificate_filepath: str = None, db_name: str = None, # TODO: deprecate db_name, use only filepath.? db_filepath: str = None, is_me: bool = True, interface_signature=None, # Blockchain miner_agent=None, checksum_address: str = None, registry_filepath: str = None, # Character abort_on_learning_error: bool = False, federated_only: bool = False, start_learning_now: bool = None, crypto_power=None, tls_curve: EllipticCurve = None, tls_private_key=None, # TODO: config here. #361 known_nodes: Iterable = None, **character_kwargs ) -> None: self._work_orders = [] Character.__init__(self, is_me=is_me, checksum_address=checksum_address, start_learning_now=start_learning_now, federated_only=federated_only, crypto_power=crypto_power, abort_on_learning_error=abort_on_learning_error, known_nodes=known_nodes, **character_kwargs) if not federated_only: Miner.__init__(self, is_me=is_me, miner_agent=miner_agent, checksum_address=checksum_address, registry_filepath=registry_filepath) blockchain_power = BlockchainPower(blockchain=self.blockchain, account=self.checksum_public_address) self._crypto_power.consume_power_up(blockchain_power) if is_me is True: # TODO: 340 self._stored_treasure_maps = {} if not federated_only: self.substantiate_stamp() if not crypto_power or (TLSHostingPower not in crypto_power._power_ups): # TODO: Maybe we want _power_ups to be public after all? # We'll hook all the TLS stuff up unless the crypto_power was already passed. if is_me: self.suspicious_activities_witnessed = {'vladimirs': [], 'bad_treasure_maps': []} rest_routes = ProxyRESTRoutes( db_name=db_name, db_filepath=db_filepath, network_middleware=self.network_middleware, federated_only=self.federated_only, treasure_map_tracker=self.treasure_maps, node_tracker=self.known_nodes, node_bytes_caster=self.__bytes__, work_order_tracker=self._work_orders, node_recorder=self.remember_node, stamp=self.stamp, verifier=self.verify_from, suspicious_activity_tracker=self.suspicious_activities_witnessed, certificate_dir=self.known_certificates_dir, ) rest_server = ProxyRESTServer( rest_host=rest_host, rest_port=rest_port, routes=rest_routes, ) self.rest_url = rest_server.rest_url self.datastore = rest_routes.datastore # TODO: Maybe organize this better? tls_hosting_keypair = HostingKeypair( common_name=self.checksum_public_address, private_key=tls_private_key, curve=tls_curve, host=rest_host, certificate=certificate, certificate_dir=self.known_certificates_dir) tls_hosting_power = TLSHostingPower(rest_server=rest_server, keypair=tls_hosting_keypair) else: # Unless the caller passed a crypto power, we'll make our own TLSHostingPower for this stranger. rest_server = ProxyRESTServer( rest_host=rest_host, rest_port=rest_port ) if certificate or certificate_filepath: tls_hosting_power = TLSHostingPower(rest_server=rest_server, certificate_filepath=certificate_filepath, certificate=certificate, certificate_dir=self.known_certificates_dir, common_name=self.checksum_public_address,) else: tls_hosting_keypair = HostingKeypair( common_name=self.checksum_public_address, curve=tls_curve, host=rest_host, certificate_filepath=certificate_filepath, certificate_dir=self.known_certificates_dir) tls_hosting_power = TLSHostingPower(rest_server=rest_server, keypair=tls_hosting_keypair) self._crypto_power.consume_power_up(tls_hosting_power) # Make this work for not me for certificate to work else: self.log.info("Not adhering rest_server; we'll use the one on crypto_power..") certificate_filepath = self._crypto_power.power_ups(TLSHostingPower).keypair.certificate_filepath certificate = self._crypto_power.power_ups(TLSHostingPower).keypair.certificate # VerifiableNode.from_tls_hosting_power(tls_hosting_power=self._crypto_power.power_ups(TLSHostingPower)) # TODO: use classmethod VerifiableNode.__init__(self, certificate=certificate, certificate_filepath=certificate_filepath, interface_signature=interface_signature) if is_me: message = "Initialized Self {} | {}".format(self.__class__.__name__, self.checksum_public_address) self.log.info(message) else: message = "Initialized Stranger {} | {}".format(self.__class__.__name__, self.checksum_public_address) self.log.debug(message)
def miner(self, chain, mock_token_agent, mock_miner_agent): mock_token_agent.token_airdrop(amount=100000 * mock_token_agent.M) _origin, ursula, *everybody_else = chain.interface.w3.eth.accounts miner = Miner(miner_agent=mock_miner_agent, address=ursula) return miner
def __init__( self, # Ursula rest_host: str, rest_port: int, certificate: Certificate = None, certificate_filepath: str = None, tls_private_key=None, # TODO: Derive from keyring db_name: str = None, db_filepath: str = None, is_me: bool = True, interface_signature=None, timestamp=None, # Blockchain miner_agent=None, checksum_address: str = None, # Character passphrase: str = None, abort_on_learning_error: bool = False, federated_only: bool = False, start_learning_now: bool = None, crypto_power=None, tls_curve: EllipticCurve = None, known_nodes: Iterable = None, **character_kwargs) -> None: # # Character # self._work_orders = list() Character.__init__(self, is_me=is_me, checksum_address=checksum_address, start_learning_now=start_learning_now, federated_only=federated_only, crypto_power=crypto_power, abort_on_learning_error=abort_on_learning_error, known_nodes=known_nodes, **character_kwargs) # # Self-Ursula # if is_me is True: # TODO: 340 self._stored_treasure_maps = dict() if not federated_only: Miner.__init__(self, is_me=is_me, miner_agent=miner_agent, checksum_address=checksum_address) # Access staking node via node's transacting keys blockchain_power = BlockchainPower( blockchain=self.blockchain, account=self.checksum_public_address) self._crypto_power.consume_power_up(blockchain_power) # Use blockchain power to substantiate stamp self.substantiate_stamp(passphrase=passphrase) # # ProxyRESTServer and TLSHostingPower # if not crypto_power or (TLSHostingPower not in crypto_power._power_ups): # TODO: Maybe we want _power_ups to be public after all? # We'll hook all the TLS stuff up unless the crypto_power was already passed. # # Self-Ursula # if is_me: self.suspicious_activities_witnessed = { 'vladimirs': [], 'bad_treasure_maps': [] } # # REST Server # rest_routes = ProxyRESTRoutes( db_name=db_name, db_filepath=db_filepath, network_middleware=self.network_middleware, federated_only=self.federated_only, # TODO: 466 treasure_map_tracker=self.treasure_maps, node_tracker=self.known_nodes, node_bytes_caster=self.__bytes__, work_order_tracker=self._work_orders, node_recorder=self.remember_node, stamp=self.stamp, verifier=self.verify_from, suspicious_activity_tracker=self. suspicious_activities_witnessed, certificate_dir=self.known_certificates_dir, ) rest_server = ProxyRESTServer( rest_host=rest_host, rest_port=rest_port, routes=rest_routes, ) self.rest_url = rest_server.rest_url self.datastore = rest_routes.datastore # TODO: Maybe organize this better? # # TLSHostingPower # tls_hosting_keypair = HostingKeypair( curve=tls_curve, host=rest_host, certificate=certificate, certificate_filepath=certificate_filepath, private_key=tls_private_key) tls_hosting_power = TLSHostingPower( rest_server=rest_server, keypair=tls_hosting_keypair) # # Stranger-Ursula # else: # REST Server # Unless the caller passed a crypto power, # we'll make our own TLSHostingPower for this stranger. rest_server = ProxyRESTServer(rest_host=rest_host, rest_port=rest_port) # # TLSHostingPower # if certificate or certificate_filepath: tls_hosting_power = TLSHostingPower( rest_server=rest_server, certificate_filepath=certificate_filepath, certificate=certificate) else: tls_hosting_keypair = HostingKeypair( curve=tls_curve, host=rest_host, certificate_filepath=certificate_filepath) tls_hosting_power = TLSHostingPower( rest_server=rest_server, keypair=tls_hosting_keypair) # OK - Now we have a ProxyRestServer and a TLSHostingPower for some Ursula self._crypto_power.consume_power_up(tls_hosting_power) # Consume! else: self.log.info( "Not adhering rest_server; Using the one on crypto_power.") # # Verifiable Node # certificate_filepath = self._crypto_power.power_ups( TLSHostingPower).keypair.certificate_filepath certificate = self._crypto_power.power_ups( TLSHostingPower).keypair.certificate VerifiableNode.__init__(self, certificate=certificate, certificate_filepath=certificate_filepath, interface_signature=interface_signature, timestamp=timestamp) # # Logging # if is_me: message = "Initialized Self {} | {}".format( self.__class__.__name__, self.checksum_public_address) self.log.info(message) else: message = "Initialized Stranger {} | {}".format( self.__class__.__name__, self.checksum_public_address) self.log.debug(message)
def test_run_felix(click_runner, testerchain, deploy_user_input, mock_primary_registry_filepath): clock = Clock() Felix._CLOCK = clock Felix.DISTRIBUTION_INTERVAL = 5 # seconds Felix.DISBURSEMENT_INTERVAL = 0.01 # hours Felix.STAGING_DELAY = 2 # seconds # Main thread (Flask) os.environ['NUCYPHER_FELIX_DB_SECRET'] = INSECURE_DEVELOPMENT_PASSWORD # Test subproc (Click) envvars = { 'NUCYPHER_KEYRING_PASSWORD': INSECURE_DEVELOPMENT_PASSWORD, 'NUCYPHER_FELIX_DB_SECRET': INSECURE_DEVELOPMENT_PASSWORD, 'FLASK_DEBUG': '1' } # Deploy contracts deploy_args = ('contracts', '--registry-outfile', mock_primary_registry_filepath, '--provider-uri', TEST_PROVIDER_URI, '--poa') result = click_runner.invoke(deploy.deploy, deploy_args, input=deploy_user_input, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # Felix creates a system configuration init_args = ('felix', 'init', '--checksum-address', testerchain.interface.w3.eth.accounts[0], '--config-root', MOCK_CUSTOM_INSTALLATION_PATH_2, '--network', TEMPORARY_DOMAIN, '--no-registry', '--provider-uri', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, init_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 configuration_file_location = os.path.join(MOCK_CUSTOM_INSTALLATION_PATH_2, 'felix.config') # Felix Creates a Database db_args = ('felix', 'createdb', '--config-file', configuration_file_location, '--provider-uri', TEST_PROVIDER_URI) result = click_runner.invoke(nucypher_cli, db_args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 # Felix Runs Web Services def run_felix(): args = ('--debug', 'felix', 'run', '--config-file', configuration_file_location, '--provider-uri', TEST_PROVIDER_URI, '--dry-run', '--no-registry') result = click_runner.invoke(nucypher_cli, args, catch_exceptions=False, env=envvars) assert result.exit_code == 0 return result # A (mocked) client requests Felix's services def request_felix_landing_page(_result): # Init an equal Felix to the already running one. felix_config = FelixConfiguration.from_configuration_file( filepath=configuration_file_location) felix_config.keyring.unlock(password=INSECURE_DEVELOPMENT_PASSWORD) felix = felix_config.produce() # Make a flask app web_app = felix.make_web_app() test_client = web_app.test_client() # Load the landing page response = test_client.get('/') assert response.status_code == 200 # Register a new recipient response = test_client.post( '/register', data={'address': felix.blockchain.interface.w3.eth.accounts[-1]}) assert response.status_code == 200 return def time_travel(_result): clock.advance(amount=60) # Record starting ether balance recipient = testerchain.interface.w3.eth.accounts[-1] miner = Miner(checksum_address=recipient, blockchain=testerchain, is_me=True) original_eth_balance = miner.eth_balance # Run the callbacks d = threads.deferToThread(run_felix) d.addCallback(request_felix_landing_page) d.addCallback(time_travel) yield d def confirm_airdrop(_results): recipient = testerchain.interface.w3.eth.accounts[-1] miner = Miner(checksum_address=recipient, blockchain=testerchain, is_me=True) assert miner.token_balance == NU(15000, 'NU') new_eth_balance = original_eth_balance + testerchain.interface.w3.fromWei( Felix.ETHER_AIRDROP_AMOUNT, 'ether') assert miner.eth_balance == new_eth_balance staged_airdrops = Felix._AIRDROP_QUEUE next_airdrop = staged_airdrops[0] next_airdrop.addCallback(confirm_airdrop) yield next_airdrop
def test_collect_rewards_integration(click_runner, configuration_file_location, blockchain_alice, blockchain_bob, random_policy_label, blockchain_ursulas, staking_participant, token_economics, policy_value, policy_rate): blockchain = staking_participant.blockchain half_stake_time = token_economics.minimum_locked_periods // 2 # Test setup logger = staking_participant.log # Enter the Teacher's Logger, and current_period = 1 # State the initial period for incrementing miner = Miner(checksum_address=staking_participant.checksum_public_address, blockchain=blockchain, is_me=True) pre_stake_eth_balance = miner.eth_balance # Finish the passage of time... once and for all for _ in range(half_stake_time): current_period += 1 logger.debug(f"period {current_period}") blockchain.time_travel(periods=1) miner.confirm_activity() # Alice creates a policy and grants Bob access M, N = 1, 1 expiration = maya.now() + datetime.timedelta(days=3) blockchain_policy = blockchain_alice.grant( bob=blockchain_bob, label=random_policy_label, m=M, n=N, value=policy_value, expiration=expiration, handpicked_ursulas={staking_participant}) # Bob joins the policy 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"period {current_period}") random_data = os.urandom(random.randrange(20, 100)) ciphertext, signature = enrico.encrypt_message(message=random_data) # Retrieve cleartexts = blockchain_bob.retrieve(message_kit=ciphertext, data_source=enrico, alice_verifying_key=verifying_key, label=random_policy_label) assert random_data == cleartexts[0] # Ursula Staying online and the clock advancing blockchain.time_travel(periods=1) miner.confirm_activity() current_period += 1 # Finish the passage of time... once and for all for _ in range(5): current_period += 1 logger.debug(f"period {current_period}") blockchain.time_travel(periods=1) miner.confirm_activity() # # WHERES THE MONEY URSULA?? - Collecting Rewards # # The address the client wants Ursula to send rewards to burner_wallet = blockchain.interface.w3.eth.account.create( INSECURE_DEVELOPMENT_PASSWORD) # The rewards wallet is initially empty assert blockchain.interface.w3.eth.getBalance(burner_wallet.address) == 0 # Snag a random teacher from the fleet random_teacher = list(blockchain_ursulas).pop() collection_args = ('--mock-networking', 'ursula', 'collect-reward', '--teacher-uri', random_teacher.rest_interface, '--config-file', configuration_file_location, '--withdraw-address', burner_wallet.address, '--poa', '--force') result = click_runner.invoke(nucypher_cli, collection_args, input=INSECURE_DEVELOPMENT_PASSWORD, catch_exceptions=False) assert result.exit_code == 0 collected_reward = blockchain.interface.w3.eth.getBalance( burner_wallet.address) assert collected_reward != 0 expected_reward = policy_rate * 30 assert collected_reward == expected_reward assert miner.eth_balance == pre_stake_eth_balance