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