def sign(general_config, blockchain_options, multisig_options, proposal): """ Sign a proposed transaction before being sent to the MultiSig contract for execution """ # Init emitter = general_config.emitter #_ensure_config_root(actor_options.config_root) blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = get_registry(network=blockchain_options.network) proposal = Proposal.from_file(proposal) if not multisig_options.checksum_address: multisig_options.checksum_address = select_client_account(emitter=emitter, provider_uri=blockchain_options.provider_uri, poa=blockchain_options.poa, network=blockchain_options.network, registry=registry, show_balances=True) name, version, address, abi = registry.search(contract_address=proposal.target_address) # TODO: This assumes that we're always signing proxy retargetting. For the moment is true. proxy_contract = blockchain.client.w3.eth.contract(abi=abi, address=address, version=version, ContractFactoryClass=blockchain._CONTRACT_FACTORY) paint_multisig_proposed_transaction(emitter, proposal, proxy_contract) click.confirm(PROMPT_CONFIRM_MULTISIG_SIGNATURE, abort=True) executive = multisig_options.create_transactingless_executive(registry) # FIXME: Since we use a signer, don't ask for PW authorization = executive.authorize_proposal(proposal) emitter.echo(MULTISIG_SIGNATURE_RECEIVED.format(recovered_address=authorization.recover_executive_address(proposal))) emitter.echo(f"{authorization.serialize().hex()}\n", bold=True, color='green')
def setup(self, general_config) -> tuple: emitter = setup_emitter(general_config) registry = get_registry(network=self.network, registry_filepath=self.registry_filepath) blockchain = connect_to_blockchain(emitter=emitter, provider_uri=self.provider_uri) return emitter, registry, blockchain
def inspect(general_config, blockchain_options): """Show information of the MultiSig contract""" emitter = general_config.emitter _blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = get_registry(network=blockchain_options.network) multisig_agent = ContractAgency.get_agent(MultiSigAgent, registry=registry) token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=registry) paint_multisig_contract_info(emitter, multisig_agent, token_agent)
def setup(self, general_config) -> tuple: emitter = setup_emitter(general_config) registry = get_registry(network=self.network, registry_filepath=self.registry_filepath) blockchain = initialize_deployer_interface(emitter=emitter, provider_uri=self.provider_uri, poa=False, ignore_solidity_check=True) return emitter, registry, blockchain
def unbond(registry_filepath, eth_provider_uri, signer_uri, staking_provider, network, force): """Unbonds an operator from an authorized staking provider.""" # # Setup # emitter = StdoutEmitter() if not signer_uri: emitter.message('--signer is required', color='red') raise click.Abort() if not network: network = select_network(emitter=emitter, network_type=NetworksInventory.ETH) connect_to_blockchain(eth_provider_uri=eth_provider_uri, emitter=emitter) registry = get_registry(network=network, registry_filepath=registry_filepath) agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry) signer = Signer.from_signer_uri(signer_uri) transacting_power = TransactingPower(account=staking_provider, signer=signer) # # Check # bonded, onchain_operator_address = is_bonded( agent=agent, staking_provider=staking_provider, return_address=True) if not bonded: emitter.message(NOT_BONDED.format(provider=staking_provider), color='red') raise click.Abort() check_bonding_requirements(emitter=emitter, agent=agent, staking_provider=staking_provider) # # Unbond # if not force: click.confirm(CONFIRM_UNBONDING.format( provider=staking_provider, operator=onchain_operator_address), abort=True) transacting_power.unlock(password=get_client_password( checksum_address=staking_provider, envvar=NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD)) emitter.echo(UNBONDING.format(operator=onchain_operator_address)) receipt = agent.bond_operator(operator=NULL_ADDRESS, transacting_power=transacting_power, staking_provider=staking_provider) paint_receipt_summary(receipt=receipt, emitter=emitter)
def propose(general_config, blockchain_options, multisig_options): """Create a proposal of MultiSig transaction""" # TODO: Extend this command to cover this list of proposals # - Add new MultiSig owner # - Remove MultiSig owner # - Change threshold of MultiSig # - Upgrade contract (in particular, retarget to a deployed one) # - Transfer ownership of contract # - Send ETH from MultiSig # - Send tokens from MultiSig # - Change global fee range in PolicyManager # - Send raw transaction # Init emitter = general_config.emitter #_ensure_config_root(actor_options.config_root) # TODO: Review this commented out line blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = get_registry(network=blockchain_options.network) if not multisig_options.checksum_address: multisig_options.checksum_address = select_client_account( emitter=emitter, provider_uri=blockchain_options.provider_uri, poa=blockchain_options.poa, network=blockchain_options.network, registry=registry, show_balances=True) # FIXME: Unexpected input trustee = multisig_options.create_transactingless_trustee(registry) # As a PoC, this command only allows to change the threshold # TODO: Think in the UX for choosing between different types of proposals new_threshold = click.prompt(PROMPT_NEW_MULTISIG_THRESHOLD, type=click.INT) proposal = trustee.propose_changing_threshold(new_threshold) paint_multisig_proposed_transaction(emitter=emitter, proposal=proposal, registry=registry) filepath = Path( f'proposal-changeThreshold-{trustee.multisig_agent.contract_address[:8]}-TX-{proposal.nonce}.json' ) proposal.write(filepath=filepath) emitter.echo( SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL.format(filepath=filepath), color='blue', bold=True)
def execute(general_config, blockchain_options, multisig_options, proposal): """ Collect authorizations from executives and execute transaction through MultiSig contract """ # Init emitter = general_config.emitter #_ensure_config_root(actor_options.config_root) blockchain = blockchain_options.connect_blockchain(emitter, general_config.debug) registry = get_registry(network=blockchain_options.network) proposal = Proposal.from_file(proposal) if not multisig_options.checksum_address: multisig_options.checksum_address = select_client_account( emitter=emitter, provider_uri=blockchain_options.provider_uri, poa=blockchain_options.poa, network=blockchain_options.network, registry=registry, show_balances=True) # FIXME: Unexpected argument!! name, version, address, abi = registry.search( contract_address=proposal.target_address) # TODO: This assumes that we're always signing proxy retargetting. For the moment is true. proxy_contract = blockchain.client.w3.eth.contract( abi=abi, address=address, version=version, ContractFactoryClass=blockchain._CONTRACT_FACTORY) paint_multisig_proposed_transaction(emitter, proposal, proxy_contract) trustee = multisig_options.create_trustee(registry) threshold = trustee.multisig_agent.threshold while len(trustee.authorizations) < threshold: auth_hex = click.prompt(PROMPT_FOR_RAW_SIGNATURE, type=click.STRING) authorization = Authorization.from_hex(auth_hex) executive_address = trustee.add_authorization(authorization, proposal) emitter.echo(SUCCESSFUL_MULTISIG_AUTHORIZATION.format( executive_address=executive_address), color='green') click.confirm(CONFIRM_EXECUTE_MULTISIG_TRANSACTION, abort=True) receipt = trustee.execute(proposal) paint_receipt_summary(emitter, receipt)
def bond(registry_filepath, eth_provider_uri, signer_uri, operator_address, staking_provider, network, force): """ Bond an operator to a staking provider. The staking provider must be authorized to use the PREApplication. """ # # Setup # emitter = StdoutEmitter() connect_to_blockchain(eth_provider_uri=eth_provider_uri, emitter=emitter) if not signer_uri: emitter.message('--signer is required', color='red') raise click.Abort() if not network: network = select_network(emitter=emitter) signer = Signer.from_signer_uri(signer_uri) transacting_power = TransactingPower(account=staking_provider, signer=signer) registry = get_registry(network=network, registry_filepath=registry_filepath) agent = ContractAgency.get_agent(PREApplicationAgent, registry=registry) # # Checks # # Check for authorization is_authorized(emitter=emitter, agent=agent, staking_provider=staking_provider) # Check bonding if is_bonded(agent=agent, staking_provider=staking_provider, return_address=False): # operator is already set - check timing check_bonding_requirements(emitter=emitter, agent=agent, staking_provider=staking_provider) # Check for pre-existing staking providers for this operator onchain_staking_provider = agent.get_staking_provider_from_operator( operator_address=operator_address) if onchain_staking_provider != NULL_ADDRESS: emitter.message(ALREADY_BONDED.format( provider=onchain_staking_provider, operator=operator_address), color='red') raise click.Abort() # dont steal bananas # Check that operator is not human if staking_provider != operator_address: # if the operator has a beneficiary it is the staking provider. beneficiary = agent.get_beneficiary(staking_provider=operator_address) if beneficiary != NULL_ADDRESS: emitter.message(UNEXPECTED_HUMAN_OPERATOR, color='red') raise click.Abort() # # Bond # if not force: click.confirm(CONFIRM_BONDING.format(provider=staking_provider, operator=operator_address), abort=True) transacting_power.unlock(password=get_client_password( checksum_address=staking_provider, envvar=NUCYPHER_ENVVAR_STAKING_PROVIDER_ETH_PASSWORD)) emitter.echo(BONDING.format(operator=operator_address)) receipt = agent.bond_operator(operator=operator_address, transacting_power=transacting_power, staking_provider=staking_provider) paint_receipt_summary(receipt=receipt, emitter=emitter)
def run(general_config, network, provider_uri, federated_only, teacher_uri, registry_filepath, min_stake, http_port, tls_certificate_filepath, tls_key_filepath, basic_auth_filepath, allow_origins, dry_run, eager): """Start Porter's Web controller.""" emitter = setup_emitter(general_config, banner=Porter.BANNER) # HTTP/HTTPS if bool(tls_key_filepath) ^ bool(tls_certificate_filepath): raise click.BadOptionUsage( option_name='--tls-key-filepath, --tls-certificate-filepath', message=PORTER_BOTH_TLS_KEY_AND_CERTIFICATION_MUST_BE_PROVIDED) is_https = (tls_key_filepath and tls_certificate_filepath) # check authentication if basic_auth_filepath and not is_https: raise click.BadOptionUsage(option_name='--basic-auth-filepath', message=PORTER_BASIC_AUTH_REQUIRES_HTTPS) if federated_only: if not teacher_uri: raise click.BadOptionUsage( option_name='--teacher', message="--teacher is required for federated porter.") teacher = Ursula.from_teacher_uri( teacher_uri=teacher_uri, federated_only=True, min_stake=min_stake) # min stake is irrelevant for federated PORTER = Porter(domain=TEMPORARY_DOMAIN, start_learning_now=eager, known_nodes={teacher}, verify_node_bonding=False, federated_only=True) else: # decentralized/blockchain if not provider_uri: raise click.BadOptionUsage( option_name='--provider', message="--provider is required for decentralized porter.") if not network: # should never happen - network defaults to 'mainnet' if not specified raise click.BadOptionUsage( option_name='--network', message="--network is required for decentralized porter.") registry = get_registry(network=network, registry_filepath=registry_filepath) teacher = None if teacher_uri: teacher = Ursula.from_teacher_uri( teacher_uri=teacher_uri, federated_only=False, # always False min_stake=min_stake, registry=registry) PORTER = Porter(domain=network, known_nodes={teacher} if teacher else None, registry=registry, start_learning_now=eager, provider_uri=provider_uri) # RPC if general_config.json_ipc: rpc_controller = PORTER.make_rpc_controller() _transport = rpc_controller.make_control_transport() rpc_controller.start() return emitter.message(f"Network: {PORTER.domain.capitalize()}", color='green') if not federated_only: emitter.message(f"Provider: {provider_uri}", color='green') # firm up falsy status (i.e. change specified empty string to None) allow_origins = allow_origins if allow_origins else None # covert to list of strings/regexes allow_origins_list = None if allow_origins: allow_origins_list = allow_origins.split( ",") # split into list of origins to allow emitter.message(PORTER_CORS_ALLOWED_ORIGINS.format( allow_origins=allow_origins_list), color='green') if basic_auth_filepath: emitter.message(PORTER_BASIC_AUTH_ENABLED, color='green') controller = PORTER.make_web_controller( crash_on_error=False, htpasswd_filepath=basic_auth_filepath, cors_allow_origins_list=allow_origins_list) http_scheme = "https" if is_https else "http" message = PORTER_RUN_MESSAGE.format(http_scheme=http_scheme, http_port=http_port) emitter.message(message, color='green', bold=True) return controller.start(port=http_port, tls_key_filepath=tls_key_filepath, tls_certificate_filepath=tls_certificate_filepath, dry_run=dry_run)