Example #1
0
def GetSecretsFilePath(data_config):
    """
    Return secrets data file location. If it doesnt exist, creates a new one in the 'DefaultPath' specified in config file
    """

    try:
        data_file_path = putils.find_file_in_path(data_config['FileName'],
                                                  data_config['SearchPath'])
        return data_file_path
    except FileNotFoundError as e:
        logger.warn('provisioning secrets data file missing')

    default_file_path = os.path.realpath(
        os.path.join(data_config['DefaultPath'], data_config['FileName']))

    try:
        os.makedirs(os.path.dirname(default_file_path), exist_ok=True)
        open(default_file_path, "w").close()
        logger.debug('save secrets data file to %s', default_file_path)
        return default_file_path
    except Exception as e:
        logger.warning('Error creating new secrets data file; %s', str(e))
        raise e

    return None
Example #2
0
    def create_from_file(cls,
                         name,
                         source_name=None,
                         search_path=['.', '..', './contracts'],
                         interpreter='gipsy',
                         compilation_report=None):
        """Create a code object from a Gipsy source file

        :param name str: the name of the scheme contract class
        :param source_name str: the name of the source file
        :param search_path list of str: directories to search for the source file
        """
        if source_name is None:
            source_name = name
        basename = putils.build_simple_file_name(
            source_name, extension=cls.__extension__[interpreter])
        filename = putils.find_file_in_path(basename, search_path)
        logger.debug('load %s contract from %s', interpreter, filename)

        with open(filename, "r") as cfile:
            code = cfile.read()

        report = compilation_report
        if interpreter == 'wawaka-aot' and report is None:
            report = ContractCompilationReport.create_from_file(
                source_name, search_path)

        return cls(code, name, compilation_report=report)
    def read_from_file(cls, file_name, search_path=['.', './keys']):
        full_file = putils.find_file_in_path(file_name, search_path)
        with open(full_file, "r") as ff:
            hex_encoded_private_key = ff.read()

        priv = binascii.unhexlify(hex_encoded_private_key)
        return cls(secp256k1.PrivateKey(priv))
def __find_enclave_library(config):
    enclave_file_name = 'libpdo-enclave.signed.so'
    enclave_file_path = None

    if config:
        enclave_file_name = config.get('enclave_library', enclave_file_name)
        enclave_file_path = config.get('enclave_library_path',
                                       enclave_file_path)

    if enclave_file_path:
        filep = os.path.join(enclave_file_path, enclave_file_name)
        if os.path.exists(filep):
            return filep
    else:
        script_directory = os.path.abspath(
            os.path.dirname(os.path.realpath(__file__)))
        search_path = [
            script_directory,
            os.path.abspath(os.path.join(script_directory, '..')),
            os.path.abspath(os.path.join(script_directory, '..', 'lib')),
            os.path.abspath(os.path.join(script_directory, '..', '..')),
            os.path.abspath(os.path.join(script_directory, '..', '..', 'lib')),
            os.path.abspath(os.path.join('/usr', 'lib'))
        ]

        return putils.find_file_in_path(enclave_file_name, search_path)
    def do_load_plugin(self, args) :
        """load -- load a new command processor from a file, the file should
        define a function called load_commands
        """
        if self.deferred > 0 : return False

        try :
            pargs = self.__arg_parse__(args)

            parser = argparse.ArgumentParser(prog='load_plugin')
            group = parser.add_mutually_exclusive_group(required=True)
            group.add_argument('-c', '--contract-class', help='load contract plugin from data directory', type=str)
            group.add_argument('-f', '--file', help='file from which to read the plugin', type=str)
            options = parser.parse_args(pargs)

            if options.file :
                plugin_file = options.file

            if options.contract_class :
                contract_paths = self.state.get(['Contract', 'SourceSearchPath'], ['.'])
                plugin_file = find_file_in_path(options.contract_class + '.py', contract_paths)

            with open(plugin_file) as f:
                code = compile(f.read(), plugin_file, 'exec')
                exec(code, globals())
            load_commands(ContractController)

        except SystemExit as se :
            return self.__arg_error__('load_plugin', args, se.code)
        except Exception as e :
            return self.__error__('load_plugin', args, str(e))

        return False
Example #6
0
def parse_configuration_files(cfiles, search_path, variable_map = None) :
    """
    Locate and parse a collection of configuration files stored in a
    TOML format.

    :param list(str) cfiles: list of configuration files to load
    :param list(str) search_path: list of directores where the files may be located
    :param dict variable_map: a set of substitutions for variables in the files
    :return dict:an aggregated dictionary of configuration information
    """
    config = {}
    files_found = []

    try :
        for cfile in cfiles :
            files_found.append(find_file_in_path(cfile, search_path))
    except FileNotFoundError as e :
        raise ConfigurationException(e.filename, e.strerror)

    for filename in files_found :
        try :
            config.update(parse_configuration_file(filename, variable_map))
        except IOError as detail :
            raise ConfigurationException(filename, "IO error; {0}".format(str(detail)))
        except ValueError as detail :
            raise ConfigurationException(filename, "Value error; {0}".format(str(detail)))
        except NameError as detail :
            raise ConfigurationException(filename, "Name error; {0}".format(str(detail)))
        except KeyError as detail :
            raise ConfigurationException(filename, "Key error; {0}".format(str(detail)))
        except :
            raise ConfigurationException(filename, "Unknown error")

    return config
    def create_from_file(cls,
                         report_name,
                         search_path=['.', '..', './contracts']):
        """Create a code object from a wawaka-aot source file

        :param report_name str: the name of the compilation report file
        :param search_path list of str: directories to search for the source file
        """
        if report_name.endswith('.b64'):
            # we may get the full file path for the contract as the report name
            # so drop the extension, so we actually find and load the report file
            report_name = os.path.basename(report_name)  # only works on Linux
            report_name = report_name[:-4]
        basename = putils.build_simple_file_name(report_name, extension='.cdi')
        filename = putils.find_file_in_path(basename, search_path)
        logger.debug('load wawaka-aot compilation report from %s', filename)
        # file is json-encoded
        with open(filename, "r") as rfile:
            contents = rfile.read()
        contents = contents.rstrip('\0')
        report = json.loads(contents)

        logger.debug('loaded report %s', json.dumps(report))

        return cls.init_from_dict(report)
Example #8
0
def LocalMain(config):
    try:
        key_config = config.get('Key', {})
        keyfile = putils.find_file_in_path(key_config['FileName'],
                                           key_config['SearchPath'])
        logger.debug('read key from %s', keyfile)
        with open(keyfile, "r") as f:
            config['SigningKeyPrivate'] = f.read()
    except FileNotFoundError as e:
        logger.warning('Unable to locate signing key file')
        sys.exit(-1)

    try:
        data_config = config.get('ProvisioningData', {})
        config["SecretsFilePath"] = GetSecretsFilePath(data_config)
    except Exception as e:
        logger.warning(
            'Unable to locate or create provisioning secrets data file')
        sys.exit(-1)

    signing_key = pcrypto.SIG_PrivateKey()
    signing_key.Deserialize(config['SigningKeyPrivate'])
    config['SigningKeyPublic'] = signing_key.GetPublicKey().Serialize()

    RunProvisioningService(config)
Example #9
0
def __parse_pem_file_list(key_list, search_path):
    keys = []
    for key_file in key_list:
        logger.debug('opening key file %s', key_file)
        full_file = putils.find_file_in_path(key_file, search_path)
        with open(full_file, 'r') as k:
            key = k.read()
        assert key.startswith('-----BEGIN PUBLIC KEY-----\n') and key.endswith(
            '\n-----END PUBLIC KEY-----\n'), "Malformed .pem key"
        keys.append(key)
    return keys
    def do_set(self, args) :
        """set -- assign a value to a symbol that can be retrieved with a $expansion
        """
        if self.deferred > 0 : return False

        try :
            pargs = self.__arg_parse__(args)

            parser = argparse.ArgumentParser(prog='set')
            parser.add_argument('-q', '--quiet', help='suppress printing the result', action='store_true')
            parser.add_argument('-s', '--symbol', help='symbol in which to store the identifier', required=True)
            parser.add_argument('-c', '--conditional', help='set the value only if it is undefined', action='store_true')

            eparser = parser.add_mutually_exclusive_group(required=True)
            eparser.add_argument('-i', '--identity', help='identity to use for retrieving public keys')
            eparser.add_argument('-f', '--file', help='name of the file to read for the value')
            eparser.add_argument('-v', '--value', help='string value to associate with the symbol')
            eparser.add_argument('-r', '--random', help='generate a random string', type=int)
            eparser.add_argument('--state', help='pull a value from current state', nargs="+")

            options = parser.parse_args(pargs)

            if options.conditional and self.bindings.isbound(options.symbol) :
                return

            value = options.value

            if options.identity :
                keypath = self.state.get(['Key', 'SearchPath'])
                keyfile = find_file_in_path("{0}_public.pem".format(options.identity), keypath)
                with open (keyfile, "r") as myfile:
                    value = myfile.read()

            if options.file :
                with open (options.file, "r") as myfile:
                    value = myfile.read()

            if options.random :
                value = "{:X}".format(random.getrandbits(options.random))

            if options.state :
                value = self.state.get(options.state)

            self.bindings.bind(options.symbol,value)
            if not options.quiet :
                print("${} = {}".format(options.symbol, value))

        except SystemExit as se :
            return self.__arg_error__('set', args, se.code)
        except Exception as e :
            return self.__error__('set', args, str(e))

        return False
Example #11
0
    def create_from_scheme_file(cls, name, source_name = None, search_path = ['.', '..', './contracts']) :
        """Create a code object from a Gipsy source file

        :param name str: the name of the scheme contract class
        :param source_name str: the name of the source file
        :param search_path list of str: directories to search for the source file
        """
        if source_name is None :
            source_name = name
        basename = putils.build_simple_file_name(source_name, extension=cls.__extension__)
        filename = putils.find_file_in_path(basename, search_path)
        with open(filename, "r") as cfile :
            code = cfile.read()

        return cls(code, name)
Example #12
0
    def create_from_file(cls, name, source_name = None, search_path = ['.', '..', './contracts'], interpreter=None) :
        """Create a code object from a Gipsy source file

        :param name str: the name of the scheme contract class
        :param source_name str: the name of the source file
        :param search_path list of str: directories to search for the source file
        """
        if source_name is None :
            source_name = name
        if interpreter is None :
            interpreter = os.environ.get("PDO_INTERPRETER", "wawaka")

        basename = putils.build_simple_file_name(source_name, extension=cls.__extension__[interpreter])
        filename = putils.find_file_in_path(basename, search_path)
        logger.debug('load %s contract from %s', interpreter, filename)

        with open(filename, "r") as cfile :
            code = cfile.read()

        return cls(code, name)
Example #13
0
def ParseCommandLine(config, args):
    global use_ledger, use_pservice, use_eservice

    parser = argparse.ArgumentParser()

    parser.add_argument('--ledger',
                        help='URL for the Sawtooth ledger',
                        type=str)
    parser.add_argument('--no-ledger',
                        help='Do not attempt ledger registration',
                        action="store_true")
    parser.add_argument('--data',
                        help='Path for storing generated files',
                        type=str)
    parser.add_argument('--secret-count',
                        help='Number of secrets to generate',
                        type=int,
                        default=3)
    parser.add_argument('--contract',
                        help='Name of the contract to use',
                        default='integer-key')
    parser.add_argument('--expressions',
                        help='Name of a file to read for expressions',
                        default=None)
    parser.add_argument('--eservice',
                        help='URL of the enclave service to use',
                        type=str)
    parser.add_argument('--pservice',
                        help='URLs for provisioning services to contact',
                        type=str,
                        nargs='+',
                        default=[])

    parser.add_argument(
        '--logfile',
        help='Name of the log file, __screen__ for standard output',
        type=str)
    parser.add_argument('--loglevel', help='Logging level', type=str)

    options = parser.parse_args(args)

    if config.get('Logging') is None:
        config['Logging'] = {'LogFile': '__screen__', 'LogLevel': 'INFO'}
    if options.logfile:
        config['Logging']['LogFile'] = options.logfile
    if options.loglevel:
        config['Logging']['LogLevel'] = options.loglevel.upper()

    if config.get('PDO') is None:
        config['PDO'] = {
            'DataPath': 'mock_data',
            'SchemeSearchPath': ['contracts']
        }
    if options.data:
        config['PDO']['DataPath'] = options.data

    if config.get('Sawtooth') is None:
        config['Sawtooth'] = {
            'LedgerURL': 'http://localhost:8008',
            'Organization': 'Organization'
        }
    if options.ledger:
        config['Sawtooth']['LedgerURL'] = options.ledger

    if options.no_ledger:
        use_ledger = False
        config.pop('Sawtooth', None)

    if options.eservice:
        use_eservice = True
        config['eservice-url'] = options.eservice

    if options.pservice:
        use_pservice = True
        config['pservice-urls'] = options.pservice

    config['secrets'] = options.secret_count
    config['contract'] = options.contract

    if options.expressions:
        expression_file = options.expressions
    else:
        expression_file = config['contract'] + '.exp'

    config['expressions'] = putils.find_file_in_path(expression_file,
                                                     ['.', '..', 'contracts'])
    def read_from_file(cls, file_name, search_path=['.', './keys']):
        full_file = putils.find_file_in_path(file_name, search_path)
        with open(full_file, "r") as ff:
            pem_encoded_signing_key = ff.read()

        return cls(crypto.SIG_PrivateKey(pem_encoded_signing_key))
Example #15
0
def Main():
    global use_ledger
    global use_eservice
    global use_pservice

    # parse out the configuration file first
    conffiles = ['pcontract.toml', 'enclave.toml']
    confpaths = [".", "./etc", ContractEtc]

    parser = argparse.ArgumentParser()

    parser.add_argument('--config', help='configuration file', nargs='+')
    parser.add_argument(
        '--config-dir',
        help='directories to search for the configuration file',
        nargs='+')

    parser.add_argument('-i',
                        '--identity',
                        help='Identity to use for the process',
                        default='test-request',
                        type=str)

    parser.add_argument(
        '--logfile',
        help='Name of the log file, __screen__ for standard output',
        type=str)
    parser.add_argument('--loglevel', help='Logging level', type=str)

    parser.add_argument('--ledger',
                        help='URL for the Sawtooth ledger',
                        type=str)
    parser.add_argument('--no-ledger',
                        help='Do not attempt ledger registration',
                        action="store_true")

    parser.add_argument('--data-dir',
                        help='Directory for storing generated files',
                        type=str)
    parser.add_argument('--source-dir',
                        help='Directories to search for contract source',
                        nargs='+',
                        type=str)
    parser.add_argument('--key-dir',
                        help='Directories to search for key files',
                        nargs='+')

    parser.add_argument('--eservice-url',
                        help='List of enclave service URLs to use',
                        nargs='+')
    parser.add_argument('--randomize-eservice',
                        help="Randomize eservice used for each update",
                        action="store_true")

    parser.add_argument('--pservice-url',
                        help='List of provisioning service URLs to use',
                        nargs='+')

    parser.add_argument('--block-store',
                        help='Name of the file where blocks are stored',
                        type=str)

    parser.add_argument('--secret-count',
                        help='Number of secrets to generate',
                        type=int,
                        default=3)
    parser.add_argument('--contract',
                        help='Name of the contract to use',
                        default='mock-contract')
    parser.add_argument('--interpreter',
                        help='Name of the contract to to require',
                        default=ContractInterpreter)
    parser.add_argument('--expressions',
                        help='Name of a file to read for expressions',
                        default=None)

    parser.add_argument(
        '--num-provable-replicas',
        help='Number of sservice signatures needed for proof of replication',
        type=int,
        default=1)
    parser.add_argument(
        '--availability-duration',
        help=
        'duration (in seconds) for which the replicas are stored at storage service',
        type=int,
        default=60)

    options = parser.parse_args()

    # first process the options necessary to load the default configuration
    if options.config:
        conffiles = options.config

    if options.config_dir:
        confpaths = options.config_dir

    # customize the configuration file for the current request
    global config_map

    config_map['identity'] = options.identity

    if options.data_dir:
        config_map['data'] = options.data_dir

    config_map['contract'] = options.contract

    # parse the configuration file
    try:
        config = pconfig.parse_configuration_files(conffiles, confpaths,
                                                   config_map)
    except pconfig.ConfigurationException as e:
        logger.error(str(e))
        sys.exit(-1)

    # set up the logging configuration
    if config.get('Logging') is None:
        config['Logging'] = {'LogFile': '__screen__', 'LogLevel': 'INFO'}
    if options.logfile:
        config['Logging']['LogFile'] = options.logfile
    if options.loglevel:
        config['Logging']['LogLevel'] = options.loglevel.upper()

    plogger.setup_loggers(config.get('Logging', {}))
    sys.stdout = plogger.stream_to_logger(logging.getLogger('STDOUT'),
                                          logging.DEBUG)
    sys.stderr = plogger.stream_to_logger(logging.getLogger('STDERR'),
                                          logging.WARN)

    # set up the ledger configuration
    if config.get('Sawtooth') is None:
        config['Sawtooth'] = {
            'LedgerURL': 'http://localhost:8008',
        }
    if options.ledger:
        config['Sawtooth']['LedgerURL'] = options.ledger
    if options.no_ledger or not config['Sawtooth']['LedgerURL']:
        use_ledger = False
        config.pop('Sawtooth', None)

    # set up the key search paths
    if config.get('Key') is None:
        config['Key'] = {
            'SearchPath': ['.', './keys', ContractKeys],
            'FileName': options.identity + ".pem"
        }
    if options.key_dir:
        config['Key']['SearchPath'] = options.key_dir

    # set up the service configuration
    if config.get('Service') is None:
        config['Service'] = {
            'EnclaveServiceURLs': [],
            'ProvisioningServiceURLs': [],
            'EnclaveServiceDatabaseFile': None,
            'Randomize_Eservice': False
        }

    if options.randomize_eservice:
        config['Service']['Randomize_Eservice'] = True
    else:
        config['Service']['Randomize_Eservice'] = False
    if options.eservice_url:
        use_eservice = True
        config['Service']['EnclaveServiceURLs'] = options.eservice_url
    if options.pservice_url:
        use_pservice = True
        config['Service']['ProvisioningServiceURLs'] = options.pservice_url

    # replication parameters
    if options.num_provable_replicas:
        config['Replication'][
            'NumProvableReplicas'] = options.num_provable_replicas
    if options.availability_duration:
        config['Replication']['Duration'] = options.availability_duration

    # set up the data paths
    if config.get('Contract') is None:
        config['Contract'] = {
            'DataDirectory':
            ContractData,
            'SourceSearchPath':
            [".", "./contract",
             os.path.join(ContractHome, 'contracts')]
        }

    if config['Contract'].get('Name') is None:
        config['Contract']['Name'] = options.contract
        config['Contract']['SourceFile'] = '_{0}'.format(options.contract)

    config['Contract']['Interpreter'] = options.interpreter

    if options.data_dir:
        config['Contract']['DataDirectory'] = options.data_dir
    if options.source_dir:
        config['Contract']['SourceSearchPath'] = options.source_dir

    putils.set_default_data_directory(config['Contract']['DataDirectory'])

    # set up the storage service configuration
    if config.get('StorageService') is None:
        config['StorageService'] = {
            'BlockStore':
            os.path.join(config['Contract']['DataDirectory'],
                         options.identity + '.mdb'),
        }
    if options.block_store:
        config['StorageService']['BlockStore'] = options.block_store

    config['secrets'] = options.secret_count

    if options.expressions:
        expression_file = options.expressions
    else:
        expression_file = putils.build_simple_file_name(
            options.contract, '.exp')

    try:
        config['expressions'] = putils.find_file_in_path(
            expression_file, ['.', '..', 'tests'])
    except FileNotFoundError as fe:
        logger.error('unable to locate expression file "%s"', expression_file)
        sys.exit(-1)

    LocalMain(config)
    def do_set(self, args):
        """
        set -- assign a value to a symbol that can be retrieved with a $expansion
        """

        pargs = shlex.split(self.bindings.expand(args))

        try:
            parser = argparse.ArgumentParser(prog='set')
            parser.add_argument('-q',
                                '--quiet',
                                help='suppress printing the result',
                                action='store_true')
            parser.add_argument('-s',
                                '--symbol',
                                help='symbol in which to store the identifier',
                                required=True)
            parser.add_argument('-c',
                                '--conditional',
                                help='set the value only if it is undefined',
                                action='store_true')

            eparser = parser.add_mutually_exclusive_group(required=True)
            eparser.add_argument(
                '-i',
                '--identity',
                help='identity to use for retrieving public keys')
            eparser.add_argument('-f',
                                 '--file',
                                 help='name of the file to read for the value')
            eparser.add_argument(
                '-v',
                '--value',
                help='string value to associate with the symbol')

            options = parser.parse_args(pargs)

            if options.conditional and self.bindings.isbound(options.symbol):
                return

            value = options.value

            if options.identity:
                keypath = self.state.get(['Key', 'SearchPath'])
                keyfile = find_file_in_path(
                    "{0}_public.pem".format(options.identity), keypath)
                with open(keyfile, "r") as myfile:
                    value = myfile.read()

            if options.file:
                with open(options.file, "r") as myfile:
                    value = myfile.read()

            self.bindings.bind(options.symbol, value)
            if not options.quiet:
                print("${} = {}".format(options.symbol, value))
            return
        except SystemExit as se:
            if se.code > 0:
                print('An error occurred processing {0}: {1}'.format(
                    args, str(se)))
            return

        except Exception as e:
            print('An error occurred processing {0}: {1}'.format(args, str(e)))
            return
Example #17
0
def Main():
    global use_ledger
    global use_eservice
    global use_pservice

    import pdo.common.config as pconfig
    import pdo.common.logger as plogger

    # parse out the configuration file first
    conffiles = ['pcontract.toml', 'enclave.toml']
    confpaths = [".", "./etc", ContractEtc]

    parser = argparse.ArgumentParser()

    parser.add_argument('--config', help='configuration file', nargs='+')
    parser.add_argument(
        '--config-dir',
        help='directories to search for the configuration file',
        nargs='+')

    parser.add_argument('-i',
                        '--identity',
                        help='Identity to use for the process',
                        type=str)

    parser.add_argument(
        '--logfile',
        help='Name of the log file, __screen__ for standard output',
        type=str)
    parser.add_argument('--loglevel', help='Logging level', type=str)

    parser.add_argument('--ledger',
                        help='URL for the Sawtooth ledger',
                        type=str)
    parser.add_argument('--no-ledger',
                        help='Do not attempt ledger registration',
                        action="store_true")

    parser.add_argument('--data-dir',
                        help='Directory for storing generated files',
                        type=str)
    parser.add_argument('--source-dir',
                        help='Directories to search for contract source',
                        nargs='+',
                        type=str)
    parser.add_argument('--key-dir',
                        help='Directories to search for key files',
                        nargs='+')

    parser.add_argument('--eservice-url',
                        help='List of enclave service URLs to use',
                        nargs='+')
    parser.add_argument('--pservice-url',
                        help='List of provisioning service URLs to use',
                        nargs='+')

    parser.add_argument('--secret-count',
                        help='Number of secrets to generate',
                        type=int,
                        default=3)
    parser.add_argument('--contract',
                        help='Name of the contract to use',
                        default='integer-key')
    parser.add_argument('--expressions',
                        help='Name of a file to read for expressions',
                        default=None)

    options = parser.parse_args()

    # first process the options necessary to load the default configuration
    if options.config:
        conffiles = options.config

    if options.config_dir:
        confpaths = options.config_dir

    # customize the configuration file for the current request
    global config_map

    config_map['identity'] = 'test-request'
    if options.identity:
        config_map['identity'] = options.identity

    if options.data_dir:
        config_map['data'] = options.data_dir

    config_map['contract'] = options.contract

    # parse the configuration file
    try:
        config = pconfig.parse_configuration_files(conffiles, confpaths,
                                                   config_map)
    except pconfig.ConfigurationException as e:
        logger.error(str(e))
        sys.exit(-1)

    # set up the logging configuration
    if config.get('Logging') is None:
        config['Logging'] = {'LogFile': '__screen__', 'LogLevel': 'INFO'}
    if options.logfile:
        config['Logging']['LogFile'] = options.logfile
    if options.loglevel:
        config['Logging']['LogLevel'] = options.loglevel.upper()

    plogger.setup_loggers(config.get('Logging', {}))
    sys.stdout = plogger.stream_to_logger(logging.getLogger('STDOUT'),
                                          logging.DEBUG)
    sys.stderr = plogger.stream_to_logger(logging.getLogger('STDERR'),
                                          logging.WARN)

    # set up the ledger configuration
    if config.get('Sawtooth') is None:
        config['Sawtooth'] = {
            'LedgerURL': 'http://localhost:8008',
        }
    if options.ledger:
        config['Sawtooth']['LedgerURL'] = options.ledger

    # set up the key search paths
    if config.get('Key') is None:
        config['Key'] = {
            'SearchPath': ['.', './keys', ContractKeys],
            'FileName': options.identity + ".pem"
        }
    if options.key_dir:
        config['Key']['SearchPath'] = options.key_dir

    # set up the service configuration
    if config.get('Service') is None:
        config['Service'] = {
            'EnclaveServiceURLs': [],
            'ProvisioningServiceURLs': []
        }
    if options.eservice_url:
        use_eservice = True
        config['Service']['EnclaveServiceURLs'] = options.eservice_url
    if options.pservice_url:
        use_pservice = True
        config['Service']['ProvisioningServiceURLs'] = options.pservice_url

    # set up the data paths
    if config.get('Contract') is None:
        config['Contract'] = {
            'DataDirectory':
            ContractData,
            'SourceSearchPath':
            [".", "./contract",
             os.path.join(ContractHome, 'contracts')]
        }

    if options.data_dir:
        config['Contract']['DataDirectory'] = options.data_dir
    if options.source_dir:
        config['Contract']['SourceSearchPath'] = options.source_dir

    putils.set_default_data_directory(config['Contract']['DataDirectory'])

    if options.no_ledger or not config['Sawtooth']['LedgerURL']:
        use_ledger = False
        config.pop('Sawtooth', None)

    config['secrets'] = options.secret_count

    if options.expressions:
        expression_file = options.expressions
    else:
        expression_file = putils.build_simple_file_name(
            options.contract, '.exp')

    try:
        config['expressions'] = putils.find_file_in_path(
            expression_file, ['.', '..', 'tests'])
    except FileNotFoundError as fe:
        logger.error('unable to locate expression file "%s"', expression_file)
        sys.exit(-1)

    LocalMain(config)