def command_create(state, bindings, pargs): """controller command to create a contract """ parser = argparse.ArgumentParser(prog='create') parser.add_argument('-c', '--contract-class', help='Name of the contract class', required=True, type=str) parser.add_argument( '-i', '--interpreter', help='Name of the interpreter used to evaluate the contract', default=state.get(['Contract', 'Interpreter'])) parser.add_argument('-s', '--contract-source', help='File that contains contract source code', required=True, type=str) parser.add_argument('-r', '--compilation-report', help='File that contains contract compilation report', type=str) parser.add_argument('-p', '--pservice-group', help='Name of the provisioning service group to use', default="default") parser.add_argument('-e', '--eservice-group', help='Name of the enclave service group to use', default="default") parser.add_argument('-f', '--save-file', help='File where contract data is stored', type=str) parser.add_argument('--symbol', help='binding symbol for result', type=str) options = parser.parse_args(pargs) contract_class = options.contract_class contract_source = options.contract_source # ---------- load the invoker's keys ---------- try: keyfile = state.private_key_file keypath = state.get(['Key', 'SearchPath']) client_keys = ServiceKeys.read_from_file(keyfile, keypath) except Exception as e: raise Exception('unable to load client keys; {0}'.format(str(e))) # ---------- read the contract source code ---------- try: source_path = state.get(['Contract', 'SourceSearchPath']) report = None if options.compilation_report: report_file = options.compilation_report report = ContractCompilationReport.create_from_file( report_file, source_path) contract_code = ContractCode.create_from_file( contract_class, contract_source, source_path, interpreter=options.interpreter, compilation_report=report) except Exception as e: raise Exception('unable to load contract source; {0}'.format(str(e))) logger.debug('Loaded contract code for %s', contract_class) # ---------- set up the enclave clients ---------- eservice_clients = get_eservice_list(state, options.eservice_group) if len(eservice_clients) == 0: raise Exception('unable to locate enclave services in the group %s', options.eservice_group) preferred_eservice_client = get_eservice( state, eservice_group=options.eservice_group) if preferred_eservice_client.interpreter != options.interpreter: raise Exception( 'enclave interpreter does not match requested contract interpreter %s', options.interpreter) # ---------- set up the provisioning service clients ---------- pservice_clients = get_pservice_list(state, options.pservice_group) if len(pservice_clients) == 0: raise Exception( 'unable to locate provisioning services in the group %s', options.pservice_group) # ---------- register contract ---------- data_directory = state.get(['Contract', 'DataDirectory']) ledger_config = state.get(['Ledger']) try: provisioning_service_keys = [pc.identity for pc in pservice_clients] contract_id = register_contract(ledger_config, client_keys, contract_code, provisioning_service_keys) logger.debug('Registered contract with class %s and id %s', contract_class, contract_id) contract_state = ContractState.create_new_state(contract_id) contract = Contract(contract_code, contract_state, contract_id, client_keys.identity) # must fix this later contract.extra_data[ 'preferred-enclave'] = preferred_eservice_client.enclave_id contract_file = "{0}_{1}.pdo".format(contract_class, contract.short_id) if options.save_file: contract_file = options.save_file contract.save_to_file(contract_file, data_dir=data_directory) except Exception as e: raise Exception('failed to register the contract; {0}'.format(str(e))) # provision the encryption keys to all of the enclaves try: encrypted_state_encryption_keys = __add_enclave_secrets( ledger_config, contract.contract_id, client_keys, eservice_clients, pservice_clients) for enclave_id in encrypted_state_encryption_keys: encrypted_key = encrypted_state_encryption_keys[enclave_id] contract.set_state_encryption_key(enclave_id, encrypted_key) contract.save_to_file(contract_file, data_dir=data_directory) except Exception as e: raise Exception('failed to provisioning the enclaves; {0}'.format( str(e))) # create the initial contract state try: __create_contract(ledger_config, client_keys, preferred_eservice_client, eservice_clients, contract) contract.contract_state.save_to_cache(data_dir=data_directory) contract.save_to_file(contract_file, data_dir=data_directory) except Exception as e: raise Exception( 'failed to create the initial contract state; {0}'.format(str(e))) if contract_id and options.symbol: bindings.bind(options.symbol, contract_id)
def send_to_contract(state, save_file, enclave, message, quiet=False, wait=False, commit=True): # ---------- load the invoker's keys ---------- try: keyfile = state.get(['Key', 'FileName']) keypath = state.get(['Key', 'SearchPath']) client_keys = ServiceKeys.read_from_file(keyfile, keypath) except Exception as e: raise Exception('unable to load client keys; {0}'.format(str(e))) # ---------- read the contract ---------- try: contract = get_contract(state, save_file) except Exception as e: raise Exception('unable to load the contract') # ---------- set up the enclave service ---------- try: enclave_client = get_enclave_service(state, enclave) except Exception as e: raise Exception('unable to connect to enclave service; {0}'.format( str(e))) try: # this is just a sanity check to make sure the selected enclave # has actually been provisioned contract.get_state_encryption_key(enclave_client.enclave_id) except KeyError as ke: logger.error('selected enclave is not provisioned') sys.exit(-1) # ---------- send the message to the enclave service ---------- try: update_request = contract.create_update_request( client_keys, enclave_client, message) update_response = update_request.evaluate() if update_response.status: if not quiet: print(update_response.result) else: print('ERROR: {}'.format(update_response.result)) return None except Exception as e: raise Exception('enclave failed to evaluation expression; {0}'.format( str(e))) data_directory = state.get(['Contract', 'DataDirectory']) ledger_config = state.get(['Sawtooth']) if commit: try: logger.debug("send update to the ledger") extraparams = {} if wait: extraparams['wait'] = 30 txnid = update_response.submit_update_transaction( ledger_config, **extraparams) except Exception as e: raise Exception('failed to save the new state; {0}'.format(str(e))) try: contract.set_state(update_response.encrypted_state) contract.contract_state.save_to_cache(data_dir=data_directory) except Exception as e: logger.exception('BAD RESPONSE: %s, %s', update_response.status, update_response.result) return update_response.result
def command_create(state, bindings, pargs) : """controller command to create a contract """ parser = argparse.ArgumentParser(prog='create') parser.add_argument('-c', '--contract-class', help='Name of the contract class', required = True, type=str) parser.add_argument('-s', '--contract-source', help='File that contains contract source code', required=True, type=str) parser.add_argument('-f', '--save-file', help='File where contract data is stored', type=str) options = parser.parse_args(pargs) contract_class = options.contract_class contract_source = options.contract_source contract_file = "{0}.pdo".format(contract_class) if options.save_file : contract_file = options.save_file # ---------- load the invoker's keys ---------- try : keyfile = state.get(['Key', 'FileName']) keypath = state.get(['Key', 'SearchPath']) client_keys = ServiceKeys.read_from_file(keyfile, keypath) except Exception as e : raise Exception('unable to load client keys; {0}'.format(str(e))) # ---------- read the contract source code ---------- try : source_path = state.get(['Contract', 'SourceSearchPath']) contract_code = ContractCode.create_from_scheme_file(contract_class, contract_source, source_path) except Exception as e : raise Exception('unable to load contract source; {0}'.format(str(e))) logger.info('Loaded contract code for %s', contract_class) # ---------- set up the enclave clients ---------- try : eservice_urls = state.get(['Service', 'EnclaveServiceURLs'], []) if len(eservice_urls) == 0 : raise Exception('no enclave services specified') enclaveclients = [] for url in eservice_urls : enclaveclients.append(EnclaveServiceClient(url)) except Exception as e : raise Exception('unable to contact enclave services; {0}'.format(str(e))) # ---------- set up the provisioning service clients ---------- # This is a dictionary of provisioning service public key : client pairs try : pservice_urls = state.get(['Service', 'ProvisioningServiceURLs']) if len(pservice_urls) == 0 : raise Exception('no provisioning services specified') provclients = [] for url in pservice_urls : provclients.append(ProvisioningServiceClient(url)) except Exception as e : raise Exception('unable to contact provisioning services; {0}'.format(str(e))) # ---------- register contract ---------- data_directory = state.get(['Contract', 'DataDirectory']) ledger_config = state.get(['Sawtooth']) try : provisioning_service_keys = [pc.identity for pc in provclients] contract_id = register_contract( ledger_config, client_keys, contract_code, provisioning_service_keys) logger.info('Registered contract with class %s and id %s', contract_class, contract_id) contract_state = ContractState.create_new_state(contract_id) contract = Contract(contract_code, contract_state, contract_id, client_keys.identity) contract.save_to_file(contract_file, data_dir=data_directory) except Exception as e : raise Exception('failed to register the contract; {0}'.format(str(e))) # provision the encryption keys to all of the enclaves try : encrypted_state_encryption_keys = __add_enclave_secrets( ledger_config, contract.contract_id, client_keys, enclaveclients, provclients) for enclave_id in encrypted_state_encryption_keys : encrypted_key = encrypted_state_encryption_keys[enclave_id] contract.set_state_encryption_key(enclave_id, encrypted_key) contract.save_to_file(contract_file, data_dir=data_directory) except Exception as e : raise Exception('failed to provisioning the enclaves; {0}'.format(str(e))) # create the initial contract state try : __create_contract(ledger_config, client_keys, enclaveclients, contract) contract.contract_state.save_to_cache(data_dir = data_directory) contract.save_to_file(contract_file, data_dir=data_directory) except Exception as e : raise Exception('failed to create the initial contract state; {0}'.format(str(e)))
def LocalMain(config, message): # ---------- check the integrity of the configuration ---------- try: ledger_config = config['Sawtooth'] contract_config = config['Contract'] service_config = config['Service'] key_config = config['Key'] except KeyError as ke: logger.error('missing configuration section %s', str(ke)) sys.exit(-1) # ---------- load the contract information file ---------- try: save_file = contract_config['SaveFile'] data_directory = contract_config['DataDirectory'] logger.info('load contract from %s', save_file) contract = Contract.read_from_file(ledger_config, save_file, data_dir=data_directory) except KeyError as ke: logger.error('missing configuration parameter %s', str(ke)) sys.exit(-1) except Exception as e: logger.error('failed to load the contract; %s', str(e)) sys.exit(-1) # ---------- load the invoker's keys ---------- try: keyfile = key_config['FileName'] keypath = key_config['SearchPath'] contract_invoker_keys = ServiceKeys.read_from_file(keyfile, keypath) except KeyError as ke: logger.error('missing configuration parameter %s', str(ke)) sys.exit(-1) except Exception as e: logger.error('unable to load client keys; %s', str(e)) sys.exit(-1) # ---------- set up the enclave service ---------- try: enclave_url = service_config['PreferredEnclaveService'] enclave_client = EnclaveServiceClient(enclave_url) except KeyError as ke: logger.error('missing configuration parameter %s', str(ke)) sys.exit(-1) except Exception as e: logger.error('unable to connect to enclave service; %s', str(e)) sys.exit(-1) try: # this is just a sanity check to make sure the selected enclave # has actually been provisioned contract.get_state_encryption_key(enclave_client.enclave_id) except KeyError as ke: logger.error('selected enclave is not provisioned') sys.exit(-1) # ---------- process incoming messages ---------- if message: mlist = MessageIterator(message) else: mlist = InputIterator(config.get('Identity', '') + "> ") for msg in mlist: if not msg: continue logger.info('send message <%s> to contract', msg) try: update_request = contract.create_update_request( contract_invoker_keys, enclave_client, msg) update_response = update_request.evaluate() if update_response.status: print(update_response.result) else: print('ERROR: {}'.format(update_response.result)) # continue if this is an interactive session, fail # if we are processing command line messages if message: sys.exit(-1) else: continue except Exception as e: logger.error('enclave failed to evaluation expression; %s', str(e)) sys.exit(-1) try: logger.debug("sending to ledger") txnid = update_response.submit_update_transaction(ledger_config) except Exception as e: logger.error('failed to save the new state; %s', str(e)) sys.exit(-1) contract.set_state(update_response.encrypted_state) contract.contract_state.save_to_cache(data_dir=data_directory) sys.exit(0)
def LocalMain(commands, config): # ---------- load the contract ---------- try: ledger_config = config['Sawtooth'] contract_config = config['Contract'] service_config = config['Service'] key_config = config['Key'] except KeyError as ke: logger.error('missing configuration section %s', str(ke)) sys.exit(-1) # ---------- load the invoker's keys ---------- try: keyfile = key_config['FileName'] keypath = key_config['SearchPath'] client_keys = ServiceKeys.read_from_file(keyfile, keypath) except KeyError as ke: logger.error('missing configuration parameter %s', str(ke)) sys.exit(-1) except Exception as e: logger.error('unable to load client keys; %s', str(e)) sys.exit(-1) # ---------- read the contract source code ---------- try: contract_name = contract_config['Name'] data_directory = contract_config['DataDirectory'] save_file = contract_config['SaveFile'] source_file = contract_config['SourceFile'] source_path = contract_config['SourceSearchPath'] contract_code = ContractCode.create_from_scheme_file( contract_name, source_file, source_path) except KeyError as ke: logger.error('missing configuration parameter %s', str(ke)) sys.exit(-1) except Exception as e: logger.error('unable to load contract source; %s', str(e)) sys.exit(-1) logger.info('Loaded contract data for %s', contract_name) # ---------- set up the enclave clients ---------- try: enclaveclients = [] for url in service_config['EnclaveServiceURLs']: enclaveclients.append(EnclaveServiceClient(url)) except Exception as e: logger.error('unable to setup enclave services; %s', str(e)) sys.exit(-1) # ---------- set up the provisioning service clients ---------- # This is a dictionary of provisioning service public key : client pairs try: provclients = [] for url in service_config['ProvisioningServiceURLs']: provclients.append(ProvisioningServiceClient(url)) except Exception as e: logger.error('unable to setup provisioning services; %s', str(e)) sys.exit(-1) logger.debug("All enclaveclients: %s", enclaveclients) logger.debug("All provclients: %s", provclients) # process the commands to create & register the contract if 'register' in commands: try: provisioning_service_keys = [pc.identity for pc in provclients] contract_id = register_contract(ledger_config, client_keys, contract_code, provisioning_service_keys) logger.info('Registered contract %s with id %s', contract_name, contract_id) contract_state = ContractState.create_new_state(contract_id) contract = Contract(contract_code, contract_state, contract_id, client_keys.identity) contract.save_to_file(save_file, data_dir=data_directory) except Exception as e: logger.error('failed to register the contract; %s', str(e)) sys.exit(-1) else: # need to read the contract from the contract file contract = Contract.read_from_file(ledger_config, contract_name, data_directory) if 'addenclave' in commands: encrypted_state_encryption_keys = AddEnclaveSecrets( ledger_config, contract.contract_id, client_keys, enclaveclients, provclients) for enclave_id in encrypted_state_encryption_keys: encrypted_key = encrypted_state_encryption_keys[enclave_id] contract.set_state_encryption_key(enclave_id, encrypted_key) contract.save_to_file(save_file, data_dir=data_directory) logger.info( 'Successfully added enclave secrets to ledger for contract %s', contract_code.name) if 'create' in commands: CreateContract(ledger_config, client_keys, enclaveclients, contract) contract.contract_state.save_to_cache(data_dir=data_directory) contract.save_to_file(save_file, data_dir=data_directory) print('export CONTRACTID={0}'.format(contract.contract_id))
def send_to_contract(state, save_file, message, eservice_url=None, quiet=False, wait=False, commit=True): # ---------- load the invoker's keys ---------- try: keyfile = state.get(['Key', 'FileName']) keypath = state.get(['Key', 'SearchPath']) client_keys = ServiceKeys.read_from_file(keyfile, keypath) except Exception as e: raise Exception('unable to load client keys; {0}'.format(str(e))) # ---------- read the contract ---------- try: contract = get_contract(state, save_file) except Exception as e: raise Exception('unable to load the contract') # ---------- set up the enclave service ---------- if eservice_url is None: eservice_url = 'preferred' if eservice_url not in ['random', 'preferred']: try: eservice_client = EnclaveServiceClient(eservice_url) except Exception as e: raise Exception('unable to connect to enclave service; {0}'.format( str(e))) if eservice_client.enclave_id not in contract.provisioned_enclaves: raise Exception( 'requested enclave not provisioned for the contract; %s', eservice_url) else: if eservice_url == 'preferred': enclave_id = contract.extra_data.get( 'preferred-enclave', random.choice(contract.provisioned_enclaves)) else: enclave_id = random.choice(contract.provisioned_enclaves) eservice_info = eservice_db.get_by_enclave_id(enclave_id) if eservice_info is None: raise Exception('attempt to use an unknown enclave; %s', enclave_id) try: eservice_client = EnclaveServiceClient(eservice_info.url) except Exception as e: raise Exception('unable to connect to enclave service; {0}'.format( str(e))) # ---------- send the message to the enclave service ---------- try: update_request = contract.create_update_request( client_keys, message, eservice_client) update_response = update_request.evaluate() except Exception as e: raise Exception('enclave failed to evaluate expression; {0}'.format( str(e))) if not update_response.status: # not sure if this should throw an exception which would # terminate the script or if it should just return an # empty string that can be tested for later # if not quiet : # print("FAILED: {0}".format(update_response.invocation_response)) # return '' raise ValueError(update_response.invocation_response) if not quiet: print(update_response.invocation_response) data_directory = state.get(['Contract', 'DataDirectory']) ledger_config = state.get(['Ledger']) if update_response.state_changed and commit: contract.set_state(update_response.raw_state) # asynchronously submit the commit task: (a commit task replicates # change-set and submits the corresponding transaction) try: update_response.commit_asynchronously(ledger_config) except Exception as e: raise Exception('failed to submit commit: %s', str(e)) # wait for the commit to finish. # TDB: # 1. make wait_for_commit a separate shell command. # 2. Add a provision to specify commit dependencies as input to send command. # 3. Return commit_id after send command back to shell so as to use as input # commit_dependency in a future send command try: txn_id = update_response.wait_for_commit() if txn_id is None: raise Exception( "Did not receive txn id for the send operation") except Exception as e: raise Exception("Error while waiting for commit: %s", str(e)) try: contract.contract_state.save_to_cache(data_dir=data_directory) except Exception as e: logger.exception('failed to save the new state in the cache') return update_response.invocation_response
def __init__(self, name, serialized_profile=None): self.name = name if serialized_profile: self.deserialize(serialized_profile) else: self.keys = ServiceKeys.create_service_keys()