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