示例#1
0
async def test_pool_open(path_home, pool_name, pool_genesis_txn_data, pool_ip):

    print(
        Ink.YELLOW(
            '\n\n== Testing Node Pool Config vs. IP {} =='.format(pool_ip)))

    try:
        NodePool(pool_name, config={'extra': 'not allowed'})
        assert False
    except JSONValidation:
        pass

    # Set up node pool ledger config and wallets, open pool, init anchors
    p_mgr = NodePoolManager()
    if pool_name not in await p_mgr.list():
        await p_mgr.add_config(pool_name, pool_genesis_txn_data)
    pool = p_mgr.get(pool_name)
    async with pool:
        assert pool.handle is not None
    assert pool.handle is None
    await pool.close()  # exercise double-close: should silently carry on

    pool.config['timeout'] = 'should be an integer'
    try:
        async with pool:
            assert False
    except IndyError as x_indy:
        assert x_indy.error_code == ErrorCode.CommonInvalidStructure
    pool.config.pop('timeout')

    print(
        '\n\n== 1 == Pool {} opens and closes OK from existing ledger configuration'
        .format(pool))
示例#2
0
async def admin_delete(ini_path: str, ident: str) -> int:
    """
    Set configuration from file, open node pool and anchor, and request deletion from tails file server
    of tails files that identifier specifies.

    :param ini_path: path to configuration file
    :param ident: identifier to specify in deletion request
    :return: 0 for OK, 1 for failure.
    """

    config = inis2dict(ini_path)
    pool_data = NodePoolData(
        config['Node Pool']['name'],
        config['Node Pool'].get('genesis.txn.path', None) or None)  # nudge empty value from '' to None
    tsan_data = AnchorData(
        Role.USER,
        config['VON Anchor']['name'],
        config['VON Anchor'].get('seed', None) or None,
        None,
        config['VON Anchor'].get('wallet.create', '0').lower() in ['1', 'true', 'yes'],
        config['VON Anchor'].get('wallet.type', None) or None,
        config['VON Anchor'].get('wallet.access', None) or None)

    # Set up node pool ledger config and wallet
    manager = NodePoolManager()
    if pool_data.name not in await manager.list():
        if pool_data.genesis_txn_path:
            await manager.add_config(pool_data.name, pool_data.genesis_txn_path)
        else:
            logging.error(
                'Node pool %s has no ledger configuration but %s specifies no genesis transaction path',
                pool_data.name,
                ini_path)
            return 1

    wallet = await get_wallet(tsan_data)
    async with wallet, (
            manager.get(pool_data.name)) as pool, (
            NominalAnchor(wallet, pool)) as noman:

        host = config['Tails Server']['host']
        port = config['Tails Server']['port']
        epoch = int(time())
        url = 'http://{}:{}/tails/{}/{}'.format(host, port, quote(ident), epoch)
        signature = await noman.sign('{}||{}'.format(epoch, ident))
        try:
            resp = requests.delete(url, data=signature)
            logging.info('DELETE: url %s status %s', url, resp.status_code)
            if resp.status_code != requests.codes.ok:
                return 1
        except RequestsConnectionError:
            logging.error('DELETE connection refused: %s', url)
            return 1
示例#3
0
async def test_pool_open(path_home, pool_name, pool_genesis_txn_data, pool_ip):

    print(
        Ink.YELLOW(
            '\n\n== Testing Node Pool Config vs. IP {} =='.format(pool_ip)))

    try:
        NodePool(pool_name, config={'extra': 'not allowed'})
        assert False
    except JSONValidation:
        pass

    # Set up node pool ledger config and wallets, open pool, init anchors
    manager = NodePoolManager()
    if pool_name not in await manager.list():
        await manager.add_config(pool_name, pool_genesis_txn_data)
    pool = manager.get(pool_name)
    await pool.open()
    assert pool.handle is not None
    await pool.close()

    print(
        '\n\n== 1 == Pool {} opens and closes OK from existing ledger configuration'
        .format(pool_name))
示例#4
0
async def test_manager(path_home, pool_genesis_txn_data, pool_ip):

    print(
        Ink.YELLOW(
            '\n\n== Testing Node Pool Manager vs. IP {} =='.format(pool_ip)))

    # Create node pool manager
    p_mgr = NodePoolManager()
    p_mgr.protocol = Protocol.V_18
    assert p_mgr.protocol == Protocol.DEFAULT

    # Create new pool on raw data
    name = 'pool-{}'.format(int(time()))
    assert name not in await p_mgr.list()
    print('\n\n== 1 == Pool {} not initially configured'.format(name))

    try:  # exercise bad pool addition
        await p_mgr.add_config(name, 'Not genesis transaction data')
        assert False
    except AbsentPool:
        pass

    await p_mgr.add_config(name, pool_genesis_txn_data)
    assert name in await p_mgr.list()
    print(
        '\n\n== 2 == Added pool {} configuration on genesis transaction data'.
        format(name))

    try:
        await p_mgr.add_config(name, pool_genesis_txn_data)
        assert False
    except ExtantPool:
        pass

    try:
        pool = p_mgr.get('no-such-pool.{}'.format(int(time())))
        await pool.open()
        assert False
    except AbsentPool:
        pass

    pool = p_mgr.get(name)
    await pool.open()
    await pool.refresh()
    assert pool.handle is not None
    await pool.close()
    print(
        '\n\n== 3 == Opened, refreshed, and closed pool {} on default configuration'
        .format(name))

    pool = p_mgr.get(name, {'timeout': 3600, 'extended_timeout': 7200})
    await pool.open()
    await pool.refresh()
    assert pool.handle is not None
    await pool.close()
    print(
        '\n\n== 4 == Opened, refreshed, and closed pool {} on explicit configuration'
        .format(name))

    await p_mgr.remove(name)
    assert name not in await p_mgr.list()
    print('\n\n== 5 == Removed pool {} configuration'.format(name))

    with NamedTemporaryFile(mode='w+b', buffering=0) as fh_gen:
        fh_gen.write(pool_genesis_txn_data.encode())
        await p_mgr.add_config(name, fh_gen.name)
    assert name in await p_mgr.list()
    print(
        '\n\n== 6 == Added pool {} configuration on genesis transaction file'.
        format(name))

    pool = p_mgr.get(name, {'timeout': 3600, 'extended_timeout': 7200})
    await pool.open()
    await pool.refresh()
    assert pool.handle is not None
    try:
        await p_mgr.remove(name)  # exercise non-removal of open pool
        assert False
    except ExtantPool:
        pass
    await pool.close()
    print(
        '\n\n== 7 == Opened, refreshed, and closed pool {} on explicit configuration'
        .format(name))

    await p_mgr.remove(name)
    assert name not in await p_mgr.list()
    print('\n\n== 8 == Removed pool {} configuration'.format(name))
示例#5
0
async def test_anchors_tails_load(
        pool_name,
        pool_genesis_txn_data,
        seed_trustee1):

    rrbx = True
    print(Ink.YELLOW('\n\n== Load-testing tails on {}ternal rev reg builder ==').format("ex" if rrbx else "in"))

    await RevRegBuilder.stop(WALLET_NAME)  # in case of re-run

    # Set up node pool ledger config and wallets, open pool, init anchors
    p_mgr = NodePoolManager()
    if pool_name not in await p_mgr.list():
        await p_mgr.add_config(pool_name, pool_genesis_txn_data)
    pool = p_mgr.get(pool_name)
    await pool.open()

    w_mgr = WalletManager()
    wallets = {
        'trustee-anchor': {
            'seed': seed_trustee1,
            'storage_type': None,
            'config': None,
            'access_creds': None
        },
        WALLET_NAME: {
            'seed': 'Superstar-Anchor-000000000000000',
            'storage_type': None,
            'config': None,
            'access_creds': {
                'key': 'rrbx-test'
            }
        }
    }
    for (name, wdata) in wallets.items():
        try:
            wdata['wallet'] = await w_mgr.create({
                'id': name,
                'seed': wdata['seed']
            })
        except ExtantWallet:
            wdata['wallet'] = w_mgr.get({'id': name})
        finally:
            await wdata['wallet'].open()

    tan = TrusteeAnchor(wallets['trustee-anchor']['wallet'], pool)
    no_prox = rrbx_prox()
    san = OrgHubAnchor(wallets[WALLET_NAME]['wallet'], pool, rrbx=rrbx)
    if rrbx:
        await beep('external rev reg builder process on {}'.format(WALLET_NAME), 15)
        if rrbx_prox() != no_prox + 1:
            await RevRegBuilder.stop(WALLET_NAME)
            assert False, "External rev reg builder process did not start"
        async with OrgHubAnchor(
                wallets[WALLET_NAME]['wallet'],
                pool,
                rrbx=rrbx):  # check for exactly 1 external rev reg builder process
            await beep('external rev reg builder process uniqueness test on {}'.format(WALLET_NAME), 5)
            if rrbx_prox() != no_prox + 1:
                await RevRegBuilder.stop(WALLET_NAME)
                assert False, "External rev reg builder process was not unique"

    assert pool.handle

    await tan.open()
    await san.open()

    # Publish anchor particulars to ledger if not yet present
    for an in (tan, san):
        if not json.loads(await tan.get_nym(an.did)):
            await tan.send_nym(an.did, an.verkey, an.wallet.name, an.least_role())

    nyms = {
        'tan': json.loads(await tan.get_nym(tan.did)),
        'san': json.loads(await tan.get_nym(san.did))
    }
    print('\n\n== 1 == nyms: {}'.format(ppjson(nyms)))

    for k in nyms:
        assert 'dest' in nyms[k]

    # Publish schema to ledger if not yet present; get from ledger
    S_ID = schema_id(san.did, 'tails_load', '{}.0'.format(int(time.time())))
    S_KEY = schema_key(S_ID)

    schema_data = {
        'name': schema_key(S_ID).name,
        'version': schema_key(S_ID).version,
        'attr_names': [
            'number',
            'remainder'
        ]
    }

    try:
        await san.get_schema(S_KEY)  # may exist (almost certainly not)
    except AbsentSchema:
        await san.send_schema(json.dumps(schema_data))
    schema_json = await san.get_schema(S_KEY)
    schema = json.loads(schema_json)
    assert schema  # should exist now
    print('\n\n== 2 == SCHEMA [{} v{}]: {}'.format(S_KEY.name, S_KEY.version, ppjson(schema)))

    # Setup link secret for creation of cred req or proof
    await san.create_link_secret('LinkSecret')

    # SRI anchor create, store, publish cred definitions to ledger; create cred offers
    await san.send_cred_def(S_ID, revo=True)
    cd_id = cred_def_id(S_KEY.origin_did, schema['seqNo'], pool.protocol)

    assert ((not Tails.unlinked(san.dir_tails)) and
        [f for f in Tails.links(san.dir_tails, san.did) if cd_id in f])

    cred_def_json = await san.get_cred_def(cd_id)  # ought to exist now
    cred_def = json.loads(cred_def_json)
    print('\n\n== 3.0 == Cred def [{} v{}]: {}'.format(
        S_KEY.name,
        S_KEY.version,
        ppjson(json.loads(cred_def_json))))
    assert cred_def.get('schemaId', None) == str(schema['seqNo'])

    cred_offer_json = await san.create_cred_offer(schema['seqNo'])
    print('\n\n== 3.1 == Credential offer [{} v{}]: {}'.format(
        S_KEY.name,
        S_KEY.version,
        ppjson(cred_offer_json)))

    (cred_req_json, cred_req_metadata_json) = await san.create_cred_req(cred_offer_json, cd_id)
    print('\n\n== 4 == Credential request [{} v{}]: metadata {}, cred-req {}'.format(
        S_KEY.name,
        S_KEY.version,
        ppjson(cred_req_metadata_json),
        ppjson(cred_req_json)))
    assert json.loads(cred_req_json)

    # BC Reg anchor (as Issuer) issues creds and stores at HolderProver: get cred req, create cred, store cred
    CREDS = 4034  # enough to kick off rev reg on size 4096 and issue two creds in it: 1 needing set-rev-reg, 1 not
    print('\n\n== 5 == creating {} credentials'.format(CREDS))
    swatch = Stopwatch(2)
    optima = {}  # per rev-reg, fastest/slowest pairs
    for number in range(CREDS):
        swatch.mark()
        (cred_json, _) = await san.create_cred(
            cred_offer_json,
            cred_req_json,
            {
                'number': str(number),
                'remainder': str(number % 100)
            })
        elapsed = swatch.mark()
        tag = rev_reg_id2tag(Tails.current_rev_reg_id(san.dir_tails, cd_id))
        if tag not in optima:
            optima[tag] = (elapsed, elapsed)
        else:
            optima[tag] = (min(optima[tag][0], elapsed), max(optima[tag][1], elapsed))
        print('.', end='', flush=True)
        if ((number + 1) % 100) == 0:
            print('{}: #{}: {:.2f}-{:.2f}s'.format(number + 1, tag, *optima[tag]), flush=True)

        assert json.loads(cred_json)
    print('{}: #{}: {:.2f}-{:.2f}s'.format(number + 1, tag, *optima[tag]), flush=True)

    print('\n\n== 6 == best, worst times by revocation registry: {}'.format(ppjson(optima)))
    assert (not rrbx) or (max(optima[tag][1] for tag in optima) <
        4 * min(optima[tag][1] for tag in optima if int(tag) > 0))  # if waiting on rr beyond #0, sizes increase as 2^n

    await san.close()
    if rrbx:
        await RevRegBuilder.stop(WALLET_NAME)
    await tan.close()
    for (name, wdata) in wallets.items():
        await wdata['wallet'].close()
    await pool.close()
async def test_setnym(pool_ip, pool_name, pool_genesis_txn_data, seed_trustee1,
                      path_setnym_ini, setnym_ini_file):

    print(
        Ink.YELLOW('\n\n== Testing setnym operation on node pool {} =='.format(
            pool_ip)))

    with open(path_setnym_ini, 'r') as cfg_fh:
        print('\n\n== 1 == Initial configuration:\n{}'.format(cfg_fh.read()))
    cfg = inis2dict(str(path_setnym_ini))

    # Set up node pool ledger config and wallets, open pool, init anchors
    p_mgr = NodePoolManager()
    if pool_name not in await p_mgr.list():
        await p_mgr.add_config(pool_name, pool_genesis_txn_data)

    wallets = await get_wallets(
        {
            'trustee-anchor': {
                'seed': seed_trustee1
            },
            cfg['VON Anchor']['name']: {
                'seed': cfg['VON Anchor']['seed']
            },
            'x-anchor': {
                'seed': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
            }
        },
        open_all=True)

    try:
        async with NominalAnchor(wallets['x-anchor']) as xan:
            await xan.get_nym()
    except AbsentPool:
        pass
    wallets.pop('x-anchor')

    # Open pool, check if nym already present
    pool = p_mgr.get(pool_name)
    await pool.open()
    assert pool.handle

    tan = TrusteeAnchor(wallets['trustee-anchor'], pool)
    await tan.open()

    noman = NominalAnchor(wallets[cfg['VON Anchor']['name']], pool)

    nym = json.loads(await noman.get_nym(noman.did))
    print('\n\n== 2 == Nym {} on ledger for anchor {} on DID {}'.format(
        '{} already'.format(ppjson(nym)) if nym else 'not yet',
        noman.wallet.name, noman.did))

    await tan.close()
    await pool.close()

    # Run setnym on initial configuration, check ledger
    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 3 == Set nym with TRUST_ANCHOR role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    assert nym and Role.get(nym['role']) == Role.TRUST_ANCHOR
    print('\n\n== 4 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    # Run setnym on configuration with DID and explicit storage type, check ledger
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if section == 'VON Anchor':
                    if key == 'seed':
                        print('did={}'.format(noman.did), file=ini_fh)
                    elif key == 'wallet.type':
                        print('wallet.type=default', file=ini_fh)
                    else:
                        print('{}={}'.format(key, value), file=ini_fh)
                else:
                    print('{}={}'.format(key, value), file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 5 == Next configuration, on DID instead of seed and explicit wallet type:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 6 == Set nym with TRUST_ANCHOR role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    assert nym and Role.get(nym['role']) == Role.TRUST_ANCHOR
    last_nym_seqno = nym['seqNo']
    print('\n\n== 7 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    # Run setnym on configuration with no seeds nor VON Anchor role, check ledger
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if key in ('seed', 'genesis.txn.path'):
                    continue
                print('{}={}'.format(
                    key, '${X_ROLE:-}' if key == 'role' else value),
                      file=ini_fh)  # exercise default
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 8 == Next configuration, no seeds, no VON Anchor role:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 9 == Set nym with USER role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    assert nym and Role.get(nym['role']) == Role.USER
    last_nym_seqno = nym['seqNo']
    print('\n\n== 10 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    # Run again to check idempotence
    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 11 == Set nym again with default role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    last_nym_seqno = nym['seqNo']
    print(
        '\n\n== 12 == Got (same) nym transaction from ledger for DID {} ({}): {}'
        .format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    # Run setnym on configuration with no seeds and bad VON Anchor role, check ledger
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if key in ('seed', 'genesis.txn.path'):
                    continue
                print('{}={}'.format(key,
                                     'BAD_ROLE' if key == 'role' else value),
                      file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 13 == Next configuration, no seeds, bad VON Anchor role:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert sub_proc.returncode
    assert str(int(ErrorCode.BadRole)) in sub_proc.stdout.decode()
    print(
        '\n\n== 14 == Called to set bad role for {}, got error text {}'.format(
            noman.wallet.name, sub_proc.stdout.decode()))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    noman_role = await noman.get_nym_role()
    assert nym and nym['seqNo'] == last_nym_seqno
    await noman.close()
    await pool.close()
    print('\n\n== 15 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))

    # Exercise reseed, ensure no side effect to role on ledger
    await pool.open()
    pan = ProctorAnchor(wallets[cfg['VON Anchor']['name']], pool, rrbx=False)
    await pan.open()
    next_seed = '{}000000000000VonAnchor1'.format(int(time()) + 1)
    await pan.reseed(next_seed)
    nym = json.loads(await pan.get_nym(noman.did))
    pan_role = await pan.get_nym_role()
    await pool.close()
    assert nym and nym['seqNo'] != last_nym_seqno
    assert pan_role == noman_role
    print(
        '\n\n== 16 == As Proctor Anchor, reseeded, then got nym transaction from ledger for DID {} ({}): {}'
        .format(pan.did, pan.wallet.name, ppjson(nym)))
    last_nym_seqno = nym['seqNo']

    # Run setnym on configuration with same wallet for trustee and VON anchor
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if section == 'VON Anchor' and key == 'name':
                    print('{}={}'.format(key, cfg['Trustee Anchor']['name']),
                          file=ini_fh)
                else:
                    print('{}={}'.format(key, value), file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 17 == Next configuration, same wallet for trustee anchor and VON anchor:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert sub_proc.returncode
    assert str(int(ErrorCode.ExtantWallet)) in sub_proc.stdout.decode()
    print(
        '\n\n== 18 == Called with same wallet for trustee anchor and VON anchor, got error text {}'
        .format(sub_proc.stdout.decode()))

    # Run setnym on configuration with new ledger node pool configuration
    genesis_tmp = NamedTemporaryFile(mode='w+b', buffering=0, delete=False)
    with genesis_tmp:
        genesis_tmp.write(pool_genesis_txn_data.encode())
    pool_copy = '{}.{}'.format(cfg['Node Pool']['name'], int(time()))
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if section == 'Node Pool':
                    if key == 'name':
                        print('name={}'.format(pool_copy), file=ini_fh)
                    elif key == 'genesis.txn.path':
                        print('genesis.txn.path={}'.format(genesis_tmp.name),
                              file=ini_fh)  # includes /tmp/ path
                    else:
                        print('{}={}.xxx'.format(key, value), file=ini_fh)
                else:
                    print('{}={}'.format(key, value), file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 19 == Next configuration, calling for copy of node pool ledger config:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 20 == Called for new copy {} of node pool ledger config'.
          format(pool_copy))

    unlink(genesis_tmp.name)
    await p_mgr.remove(pool_copy)
    await pool.open()
    await pan.open()
    nym = json.loads(await pan.get_nym(pan.did))
    assert nym and Role.get(nym['role']) == Role.TRUST_ANCHOR
    assert nym and nym['seqNo'] != last_nym_seqno
    print('\n\n== 21 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(pan.did, pan.wallet.name, ppjson(nym)))
    await pan.close()
    await pool.close()

    # Run setnym on configuration with wrong genesis transaction path
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if section == 'Node Pool':
                    print('{}={}.xxx'.format(key, value), file=ini_fh)
                else:
                    print('{}={}'.format(key, value), file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 22 == Next configuration, missing pool and bad genesis txn path:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert sub_proc.returncode
    assert str(int(ErrorCode.AbsentPool)) in sub_proc.stdout.decode()
    print(
        '\n\n== 23 == Called with missing pool and bad genesis txn path, got error text {}'
        .format(sub_proc.stdout.decode()))

    # Run setnym on configuration with no node pool ledger configuration
    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if section == 'Node Pool':
                    if key == 'name':
                        print('{}={}.xxx'.format(key, value), file=ini_fh)
                    elif key == 'genesis.txn.path':
                        print('genesis.txn.path=', file=ini_fh)
                    else:
                        print('{}={}'.format(key, value), file=ini_fh)
                else:
                    print('{}={}'.format(key, value), file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 24 == Next configuration, missing pool and no genesis txn path:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert sub_proc.returncode
    assert str(int(ErrorCode.AbsentPool)) in sub_proc.stdout.decode()
    print(
        '\n\n== 25 == Called with missing pool and no genesis txn path, got error text {}'
        .format(sub_proc.stdout.decode()))

    # Run again without trustee anchor wallet present
    await wallets['trustee-anchor'].close()
    await wallets['trustee-anchor'].remove()
    wallets.pop('trustee-anchor')
    noman = NominalAnchor(wallets[cfg['VON Anchor']['name']], pool)

    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                print('{}={}'.format(key, value), file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 26 == Set VON anchor configuration, no Trustee anchor wallet a priori:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 27 == Set nym with TRUST_ANCHOR role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    assert nym and Role.get(nym['role']) == Role.TRUST_ANCHOR
    print('\n\n== 28 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    await pan.close()
    for name in wallets:
        await wallets[name].close()
示例#7
0
async def setnym(ini_path: str) -> int:
    """
    Set configuration. Open pool, trustee anchor, and wallet of anchor whose nym to send.
    Register exit hooks to close pool and trustee anchor.

    Engage trustee anchor to send nym for VON anchor, if it differs on the ledger from configuration.

    :param ini_path: path to configuration file
    :return: 0 for OK, 1 for failure
    """

    config = inis2dict(ini_path)
    if config['Trustee Anchor']['name'] == config['VON Anchor']['name']:
        raise ExtantWallet(
            'Wallet names must differ between VON Anchor and Trustee Anchor')

    cfg_van_role = config['VON Anchor'].get(
        'role', None) or None  # nudge empty value from '' to None
    if not ok_role(cfg_van_role):
        raise BadRole('Configured role {} is not valid'.format(cfg_van_role))

    pool_data = NodePoolData(
        config['Node Pool']['name'],
        config['Node Pool'].get('genesis.txn.path', None) or None)

    an_data = {
        'tan':
        AnchorData(
            Role.TRUSTEE, config['Trustee Anchor']['name'],
            config['Trustee Anchor'].get('seed', None) or None,
            config['Trustee Anchor'].get('did', None) or None,
            config['Trustee Anchor'].get('wallet.create',
                                         '0').lower() in ['1', 'true', 'yes'],
            config['Trustee Anchor'].get('wallet.type', None) or None,
            config['Trustee Anchor'].get('wallet.access', None) or None),
        'van':
        AnchorData(
            Role.get(cfg_van_role), config['VON Anchor']['name'],
            config['VON Anchor'].get('seed', None) or None,
            config['VON Anchor'].get('did', None) or None,
            config['VON Anchor'].get('wallet.create',
                                     '0').lower() in ['1', 'true', 'yes'],
            config['VON Anchor'].get('wallet.type', None) or None,
            config['VON Anchor'].get('wallet.access', None) or None)
    }

    an_wallet = await _set_wallets(an_data)

    p_mgr = NodePoolManager()
    if pool_data.name not in await p_mgr.list():
        if pool_data.genesis_txn_path:
            await p_mgr.add_config(pool_data.name, pool_data.genesis_txn_path)
        else:
            raise AbsentPool(
                'Node pool {} has no ledger configuration, but {} specifies no genesis txn path'
                .format(pool_data.name, ini_path))

    async with an_wallet['tan'] as w_tan, (an_wallet['van']) as w_van, (
            p_mgr.get(pool_data.name)) as pool, (TrusteeAnchor(
                w_tan, pool)) as tan, (NominalAnchor(w_van, pool)) as van:

        send_verkey = van.verkey
        try:
            nym_role = await tan.get_nym_role(van.did)
            if an_data['van'].role == nym_role:
                return 0  # ledger is as per configuration
            send_verkey = None  # only owner can touch verkey
            if nym_role != Role.USER:  # only remove role when it is not already None on the ledger
                await tan.send_nym(van.did, send_verkey, van.wallet.name,
                                   Role.ROLE_REMOVE)
        except AbsentNym:
            pass  # cryptonym not there yet, fall through

        await tan.send_nym(van.did, send_verkey, van.wallet.name,
                           an_data['van'].role)

    return 0
示例#8
0
def boot() -> None:
    """
    Boot the service: instantiate tails server anchor. Raise AbsentPool if node pool ledger configuration
    neither present nor sufficiently specified; raise AbsentNym if tails server anchor nym is not on the ledger.
    """

    config = do_wait(MEM_CACHE.get('config'))

    # setup pool and wallet
    pool_data = NodePoolData(config['Node Pool']['name'],
                             config['Node Pool'].get('genesis.txn.path', None)
                             or None)  # nudge empty value from '' to None
    p_mgr = NodePoolManager()
    if pool_data.name not in do_wait(p_mgr.list()):
        if pool_data.genesis_txn_path:
            do_wait(
                p_mgr.add_config(pool_data.name, pool_data.genesis_txn_path))
        else:
            LOGGER.debug(
                'Node pool %s has no ledger configuration but %s specifies no genesis txn path',
                pool_data.name, do_wait(MEM_CACHE.get('config.ini')))
            raise AbsentPool(
                'Node pool {} has no ledger configuration but {} specifies no genesis txn path'
                .format(pool_data.name, do_wait(MEM_CACHE.get('config.ini'))))

    pool = p_mgr.get(pool_data.name)
    do_wait(pool.open())
    do_wait(MEM_CACHE.set('pool', pool))

    # instantiate tails server anchor
    tsan_data = AnchorData(
        Role.USER, config['VON Anchor']['name'],
        config['VON Anchor'].get('seed', None) or None, None,
        config['VON Anchor'].get('wallet.create', '0').lower()
        in ['1', 'true', 'yes'], config['VON Anchor'].get('wallet.type', None)
        or None, config['VON Anchor'].get('wallet.access', None) or None)

    w_mgr = WalletManager()
    wallet = None

    wallet_config = {'id': tsan_data.name}
    if tsan_data.wallet_type:
        wallet_config['storage_type'] = tsan_data.wallet_type
    if tsan_data.wallet_create:
        if tsan_data.seed:
            wallet_config['seed'] = tsan_data.seed
        try:
            wallet = do_wait(
                w_mgr.create(wallet_config, access=tsan_data.wallet_access))
            LOGGER.info('Created wallet %s', tsan_data.name)
        except ExtantWallet:
            wallet = w_mgr.get(wallet_config, access=tsan_data.wallet_access)
            LOGGER.warning(
                'Wallet %s already exists: remove seed and wallet.create from config file',
                tsan_data.name)
    else:
        wallet = w_mgr.get(wallet_config, access=tsan_data.wallet_access)

    do_wait(wallet.open())
    tsan = NominalAnchor(wallet, pool)
    do_wait(tsan.open())
    if not json.loads(do_wait(tsan.get_nym())):
        LOGGER.debug('Anchor %s has no cryptonym on ledger %s',
                     tsan_data.wallet_name, pool_data.name)
        raise AbsentNym('Anchor {} has no cryptonym on ledger {}'.format(
            tsan_data.wallet_name, pool_data.name))

    do_wait(MEM_CACHE.set('tsan', tsan))
示例#9
0
async def test_manager(path_home, pool_genesis_txn_data, pool_ip):

    print(
        Ink.YELLOW(
            '\n\n== Testing Node Pool Manager vs. IP {} =='.format(pool_ip)))

    # Create node pool manager
    manager = NodePoolManager()
    assert manager.protocol == Protocol.DEFAULT

    # Create new pool on raw data
    name = 'pool-{}'.format(int(time()))
    assert name not in await manager.list()
    print('\n\n== 1 == Pool {} not initially configured'.format(name))

    await manager.add_config(name, pool_genesis_txn_data)
    assert name in await manager.list()
    print(
        '\n\n== 2 == Added pool {} configuration on genesis transaction data'.
        format(name))

    try:
        await manager.add_config(name, pool_genesis_txn_data)
        assert False
    except ExtantPool:
        pass

    try:
        pool = manager.get('no-such-pool.{}'.format(int(time())))
        await pool.open()
        assert False
    except AbsentPool:
        pass

    pool = manager.get(name)
    await pool.open()
    await pool.refresh()
    assert pool.handle is not None
    await pool.close()
    print(
        '\n\n== 3 == Opened, refreshed, and closed pool {} on default configuration'
        .format(name))

    cache_id = pool.cache_id
    sleep(1)
    x_name = 'pool-{}'.format(int(time()))
    await manager.add_config(
        'pool-{}'.format(int(time())),
        '\n'.join(pool_genesis_txn_data.split('\n')[::-1]))
    x_pool = manager.get(x_name)
    assert x_pool.cache_id == cache_id
    await manager.remove(x_name)
    print('\n\n== 4 == Confirmed cache id consistency: {}'.format(cache_id))

    pool = manager.get(name, {'timeout': 3600, 'extended_timeout': 7200})
    await pool.open()
    await pool.refresh()
    assert pool.handle is not None
    await pool.close()
    print(
        '\n\n== 5 == Opened, refreshed, and closed pool {} on explicit configuration'
        .format(name))

    await manager.remove(name)
    assert name not in await manager.list()
    print('\n\n== 6 == Removed pool {} configuration'.format(name))

    with NamedTemporaryFile(mode='w+b', buffering=0) as fh_gen:
        fh_gen.write(pool_genesis_txn_data.encode())
        await manager.add_config(name, fh_gen.name)
    assert name in await manager.list()
    print(
        '\n\n== 7 == Added pool {} configuration on genesis transaction file'.
        format(name))

    pool = manager.get(name, {'timeout': 3600, 'extended_timeout': 7200})
    await pool.open()
    await pool.refresh()
    assert pool.handle is not None
    await pool.close()
    print(
        '\n\n== 8 == Opened, refreshed, and closed pool {} on explicit configuration'
        .format(name))

    await manager.remove(name)
    assert name not in await manager.list()
    print('\n\n== 9 == Removed pool {} configuration'.format(name))
示例#10
0
async def setnym(ini_path: str) -> int:
    """
    Set configuration. Open pool, trustee anchor, and wallet of anchor whose nym to send.
    Register exit hooks to close pool and trustee anchor.

    Engage trustee anchor to send nym for VON anchor, if it differs on the ledger from configuration.

    :param ini_path: path to configuration file
    :return: 0 for OK, 1 for failure
    """

    config = inis2dict(ini_path)
    cfg_van_role = config['VON Anchor'].get(
        'role', None) or None  # nudge empty value from '' to None
    if not ok_role(cfg_van_role):
        raise BadRole('Configured role {} is not valid'.format(cfg_van_role))

    if config['Trustee Anchor']['wallet.name'] == config['VON Anchor'][
            'wallet.name']:
        raise ExtantWallet(
            'Wallet names must differ between VON Anchor and Trustee Anchor')

    pool_data = NodePoolData(
        config['Node Pool']['name'],
        config['Node Pool'].get('genesis.txn.path', None) or None)
    an_data = {
        'tan':
        AnchorData(Role.TRUSTEE, config['Trustee Anchor'].get('seed', None)
                   or None, config['Trustee Anchor']['wallet.name'],
                   config['Trustee Anchor'].get('wallet.type', None) or None,
                   config['Trustee Anchor'].get('wallet.key', None) or None),
        'van':
        AnchorData(Role.get(cfg_van_role),
                   config['VON Anchor'].get('seed', None) or None,
                   config['VON Anchor']['wallet.name'],
                   config['VON Anchor'].get('wallet.type', None) or None,
                   config['VON Anchor'].get('wallet.key', None) or None)
    }
    an_wallet = {
        an: Wallet(an_data[an].wallet_name, an_data[an].wallet_type, None,
                   {'key': an_data[an].wallet_key}
                   if an_data[an].wallet_key else None)
        for an in an_data
    }

    for anchor in an_data:  # create wallet if seed configured, silently continue if extant
        if an_data[anchor].seed:
            try:
                await an_wallet[anchor].create(an_data[anchor].seed)
            except ExtantWallet:
                pass

    manager = NodePoolManager()
    if pool_data.name not in await manager.list():
        if pool_data.genesis_txn_path:
            await manager.add_config(pool_data.name,
                                     pool_data.genesis_txn_path)
        else:
            raise AbsentPool(
                'Node pool {} has no ledger configuration, but {} specifies no genesis txn path'
                .format(pool_data.name, ini_path))

    async with an_wallet['tan'] as w_tan, (an_wallet['van']) as w_van, (
            manager.get(pool_data.name)) as pool, (TrusteeAnchor(
                w_tan, pool)) as tan, (NominalAnchor(w_van, pool)) as van:

        send_verkey = van.verkey
        try:
            nym_role = await tan.get_nym_role(van.did)
            if an_data['van'].role == nym_role:
                return 0  # ledger is as per configuration
            send_verkey = None  # only owner can touch verkey
            await tan.send_nym(van.did, send_verkey, van.wallet.name,
                               Role.ROLE_REMOVE)
        except AbsentNym:
            pass  # cryptonym not there yet, fall through

        await tan.send_nym(van.did, send_verkey, van.wallet.name,
                           an_data['van'].role)

    return 0
示例#11
0
async def setup(ini_path: str) -> tuple:
    """
    Set configuration from file. If configured profile is issuer, open and return node pool and anchor,
    then register both for shutdown at program exit.

    :param ini_path: path to configuration file
    :return: tuple (profile, issuer anchor) for issuer or (profile, None) for prover.
    """

    global CONFIG
    CONFIG = inis2dict(ini_path)

    profile = Profile.get(CONFIG['Tails Client']['profile'])
    if profile != Profile.ISSUER:
        return (profile, None)

    pool_data = NodePoolData(CONFIG['Node Pool']['name'],
                             CONFIG['Node Pool'].get('genesis.txn.path', None)
                             or None)  # nudge empty value from '' to None

    # Set up node pool ledger config and wallet
    manager = NodePoolManager()
    if pool_data.name not in await manager.list():
        if pool_data.genesis_txn_path:
            await manager.add_config(pool_data.name,
                                     pool_data.genesis_txn_path)
        else:
            logging.error(
                'Node pool %s has no ledger configuration but %s specifies no genesis txn path',
                pool_data.name, ini_path)
            return (None, None)

    pool = manager.get(pool_data.name)
    await pool.open()
    atexit.register(close_pool, pool)

    noman_data = AnchorData(
        Role.USER, CONFIG['VON Anchor']['name'],
        CONFIG['VON Anchor'].get('seed', None) or None, None,
        CONFIG['VON Anchor'].get('wallet.create', '0').lower()
        in ['1', 'true', 'yes'], CONFIG['VON Anchor'].get('wallet.type', None)
        or None, CONFIG['VON Anchor'].get('wallet.access', None) or None)

    w_mgr = WalletManager()
    wallet = None

    wallet_config = {'id': noman_data.name}
    if noman_data.wallet_type:
        wallet_config['storage_type'] = noman_data.wallet_type
    if noman_data.wallet_create:
        if noman_data.seed:
            wallet_config['seed'] = noman_data.seed
        try:
            wallet = await w_mgr.create(wallet_config,
                                        access=noman_data.wallet_access)
            logging.info('Created wallet %s', noman_data.name)
        except ExtantWallet:
            wallet = w_mgr.get(wallet_config, access=noman_data.wallet_access)
            logging.warning(
                'Wallet %s already exists: remove seed and wallet.create from config file',
                noman_data.name)
    else:
        wallet = w_mgr.get(wallet_config, access=noman_data.wallet_access)

    await wallet.open()
    noman = NominalAnchor(wallet, pool)
    await noman.open()
    atexit.register(close_anchor, noman)

    return (profile, noman)
示例#12
0
async def test_setnym(pool_ip, pool_name, pool_genesis_txn_data, seed_trustee1,
                      path_setnym_ini, setnym_ini_file):

    print(
        Ink.YELLOW('\n\n== Testing setnym operation on node pool {} =='.format(
            pool_ip)))

    with open(path_setnym_ini, 'r') as cfg_fh:
        print('\n\n== 1 == Initial configuration:\n{}'.format(cfg_fh.read()))
    cfg = inis2dict(str(path_setnym_ini))

    # Set up node pool ledger config and wallets, open pool, init anchors
    manager = NodePoolManager()
    if pool_name not in await manager.list():
        await manager.add_config(pool_name, pool_genesis_txn_data)

    seeds = {
        'trustee-anchor': seed_trustee1,
        cfg['VON Anchor']['wallet.name']: cfg['VON Anchor']['seed'],
        'x-anchor': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    }
    wallets = await get_wallets(seeds, True)

    try:
        async with NominalAnchor(wallets['x-anchor']) as xan:
            await xan.get_nym()
    except AbsentPool:
        pass
    wallets.pop('x-anchor')

    # Open pool, check if nym already present
    pool = manager.get(pool_name)
    await pool.open()
    assert pool.handle

    tan = TrusteeAnchor(wallets['trustee-anchor'], pool)
    await tan.open()

    noman = NominalAnchor(wallets[cfg['VON Anchor']['wallet.name']], pool)

    nym = json.loads(await noman.get_nym(noman.did))
    print('\n\n== 2 == Nym {} on ledger for anchor {} on DID {}'.format(
        '{} already'.format(ppjson(nym)) if nym else 'not yet',
        noman.wallet.name, noman.did))

    await tan.close()
    await pool.close()

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 3 == Set nym with TRUST_ANCHOR role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    assert nym and Role.get(nym['role']) == Role.TRUST_ANCHOR
    print('\n\n== 4 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if key in ('seed', 'genesis.txn.path'):
                    continue
                print('{}={}'.format(
                    key, '${X_ROLE:-}' if key == 'role' else value),
                      file=ini_fh)  # exercise default
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 5 == Next configuration, no seeds, no VON Anchor role:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 6 == Set nym with default role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    assert nym and Role.get(nym['role']) == Role.USER
    last_nym_seqno = nym['seqNo']
    print('\n\n== 7 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    sub_proc = subprocess.run(  #  do it again
        [
            'python',
            join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor',
                 'op', 'setnym.py'),
            str(path_setnym_ini)
        ],
        stdout=subprocess.PIPE,
        stderr=subprocess.DEVNULL)
    assert not sub_proc.returncode
    print('\n\n== 8 == Set nym again with default role on {} for {}'.format(
        noman.did, noman.wallet.name))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    last_nym_seqno = nym['seqNo']
    print(
        '\n\n== 9 == Got (same) nym transaction from ledger for DID {} ({}): {}'
        .format(noman.did, noman.wallet.name, ppjson(nym)))
    await noman.close()
    await pool.close()

    with open(path_setnym_ini, 'w+') as ini_fh:
        for section in cfg:
            print('[{}]'.format(section), file=ini_fh)
            for (key, value) in cfg[section].items():
                if key in ('seed', 'genesis.txn.path'):
                    continue
                print('{}={}'.format(key,
                                     'BAD_ROLE' if key == 'role' else value),
                      file=ini_fh)
            print(file=ini_fh)
    with open(path_setnym_ini, 'r') as cfg_fh:
        print(
            '\n\n== 10 == Next configuration, no seeds, bad VON Anchor role:\n{}'
            .format(cfg_fh.read()))

    sub_proc = subprocess.run([
        'python',
        join(dirname(dirname(dirname(realpath(__file__)))), 'von_anchor', 'op',
             'setnym.py'),
        str(path_setnym_ini)
    ],
                              stdout=subprocess.PIPE,
                              stderr=subprocess.DEVNULL)
    assert sub_proc.returncode
    print(
        '\n\n== 11 == Called to set bad role for {}, got error text {}'.format(
            noman.wallet.name, sub_proc.stdout.decode()))

    await pool.open()
    await noman.open()
    nym = json.loads(await noman.get_nym(noman.did))
    noman_role = await noman.get_nym_role()
    assert nym and nym['seqNo'] == last_nym_seqno
    await noman.close()
    await pool.close()

    print('\n\n== 12 == Got nym transaction from ledger for DID {} ({}): {}'.
          format(noman.did, noman.wallet.name, ppjson(nym)))

    await pool.open()
    san = SRIAnchor(wallets[cfg['VON Anchor']['wallet.name']], pool)
    await san.open()
    next_seed = "{}000000000000VonAnchor1".format(int(time()) + 1)
    await san.reseed(next_seed)
    nym = json.loads(await san.get_nym(noman.did))
    san_role = await san.get_nym_role()
    assert nym and nym['seqNo'] != last_nym_seqno
    assert san_role == noman_role  # ensure that reseed does not side-effect role on ledger

    print(
        '\n\n== 13 == As SRI Anchor, reseeded, then got nym transaction from ledger for DID {} ({}): {}'
        .format(san.did, san.wallet.name, ppjson(nym)))

    await san.close()
    await pool.close()
    for name in wallets:
        await wallets[name].close()