Exemple #1
0
    async def create_cred(self,
                          cred_offer_json,
                          cred_req_json: str,
                          cred_attrs: dict,
                          rr_size: int = None) -> (str, str, int):
        """
        Create credential as Issuer out of credential request and dict of key:value (raw, unencoded)
        entries for attributes.

        Return credential json, and if cred def supports revocation, credential revocation identifier
        and revocation registry delta ledger timestamp (epoch seconds).

        If the credential definition supports revocation, and the current revocation registry is full,
        the processing creates a new revocation registry en passant. Depending on the revocation
        registry size (by default starting at 256 and doubling iteratively through 4096), this
        operation may delay credential creation by several seconds.

        :param cred_offer_json: credential offer json as created by Issuer
        :param cred_req_json: credential request json as created by HolderProver
        :param cred_attrs: dict mapping each attribute to its raw value (the operation encodes it); e.g.,

        ::

            {
                'favourite_drink': 'martini',
                'height': 180,
                'last_visit_date': '2017-12-31',
                'weaknesses': None
            }

        :param rr_size: size of new revocation registry (default as per _create_rev_reg()) if necessary
        :return: newly issued credential json; credential revocation identifier (if cred def supports
            revocation, None otherwise), and ledger timestamp (if cred def supports revocation, None otherwise)
        """

        LOGGER.debug(
            'Issuer.create_cred >>> cred_offer_json: %s, cred_req_json: %s, cred_attrs: %s, rr_size: %s',
            cred_offer_json, cred_req_json, cred_attrs, rr_size)

        cd_id = json.loads(cred_offer_json)['cred_def_id']
        if not ok_cred_def_id(cd_id):
            LOGGER.debug('Issuer.create_cred <!< Bad cred def id %s', cd_id)
            raise BadIdentifier('Bad cred def id {}'.format(cd_id))

        cred_def = json.loads(
            await self.get_cred_def(cd_id))  # ensure cred def is in cache

        if 'revocation' in cred_def['value']:
            with REVO_CACHE.lock:
                rr_id = Tails.current_rev_reg_id(self._dir_tails, cd_id)
                tails = REVO_CACHE[rr_id].tails
                assert tails  # at (re)start, at cred def, Issuer sync_revoc() sets this index in revocation cache

                try:
                    (cred_json, cred_revoc_id,
                     rr_delta_json) = await anoncreds.issuer_create_credential(
                         self.wallet.handle, cred_offer_json, cred_req_json,
                         json.dumps({
                             k: cred_attr_value(cred_attrs[k])
                             for k in cred_attrs
                         }), rr_id, tails.reader_handle)
                    # do not create rr delta frame and append to cached delta frames list: timestamp could lag or skew
                    rre_req_json = await ledger.build_revoc_reg_entry_request(
                        self.did, rr_id, 'CL_ACCUM', rr_delta_json)
                    await self._sign_submit(rre_req_json)
                    assert rr_id == tails.rr_id
                    resp_json = await self._sign_submit(rre_req_json)
                    resp = json.loads(resp_json)
                    rv = (cred_json, cred_revoc_id,
                          self.pool.protocol.txn2epoch(resp))

                except IndyError as x_indy:
                    if x_indy.error_code == ErrorCode.AnoncredsRevocationRegistryFullError:
                        (tag, rr_size_suggested) = Tails.next_tag(
                            self._dir_tails, cd_id)
                        rr_id = rev_reg_id(cd_id, tag)
                        await self._create_rev_reg(
                            rr_id, rr_size or rr_size_suggested)
                        REVO_CACHE[rr_id].tails = await Tails(
                            self._dir_tails, cd_id).open()
                        return await self.create_cred(cred_offer_json,
                                                      cred_req_json, cred_attrs
                                                      )  # should be ok now

                    LOGGER.debug(
                        'Issuer.create_cred: <!<  cannot create cred, indy error code %s',
                        x_indy.error_code)
                    raise
        else:
            try:
                (cred_json, _, _) = await anoncreds.issuer_create_credential(
                    self.wallet.handle, cred_offer_json, cred_req_json,
                    json.dumps({
                        k: cred_attr_value(cred_attrs[k])
                        for k in cred_attrs
                    }), None, None)
                rv = (cred_json, _, _)
            except IndyError as x_indy:
                LOGGER.debug(
                    'Issuer.create_cred: <!<  cannot create cred, indy error code %s',
                    x_indy.error_code)
                raise

        LOGGER.debug('Issuer.create_cred <<< %s', rv)
        return rv
Exemple #2
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()
Exemple #3
0
    async def create_cred(self,
                          cred_offer_json,
                          cred_req_json: str,
                          cred_attrs: dict,
                          rr_size: int = None) -> (str, str):
        """
        Create credential as Issuer out of credential request and dict of key:value (raw, unencoded)
        entries for attributes.

        Return credential json, and if cred def supports revocation, credential revocation identifier.
        Raise WalletState for closed wallet.

        If the credential definition supports revocation, and the current revocation registry is full,
        the processing creates a new revocation registry en passant. Depending on the revocation
        registry size (by default starting at 64 and doubling iteratively through a maximum of 100000)
        and the revocation registry builder posture (see RevRegBuilder.__init__()), this operation may
        delay credential creation by several seconds. The use of an external revocation registry builder
        runs a parallel process, skirting this delay, but is more costly at initialization.

        :param cred_offer_json: credential offer json as created by Issuer
        :param cred_req_json: credential request json as created by HolderProver
        :param cred_attrs: dict mapping each attribute to its original value (the operation encodes it); e.g.,

        ::

            {
                'favourite_drink': 'martini',
                'height': 180,
                'last_visit_date': '2017-12-31',
                'weaknesses': None
            }

        :param rr_size: size of new revocation registry (default as per RevRegBuilder.create_rev_reg()) if necessary
        :return: tuple with newly issued credential json, credential revocation identifier (if cred def
            supports revocation, None otherwise).
        """

        LOGGER.debug(
            'Issuer.create_cred >>> cred_offer_json: %s, cred_req_json: %s, cred_attrs: %s, rr_size: %s',
            cred_offer_json, cred_req_json, cred_attrs, rr_size)

        if not self.wallet.handle:
            LOGGER.debug('Issuer.create_cred <!< Wallet %s is closed',
                         self.name)
            raise WalletState('Wallet {} is closed'.format(self.name))

        cd_id = json.loads(cred_offer_json)['cred_def_id']
        if not ok_cred_def_id(cd_id):
            LOGGER.debug('Issuer.create_cred <!< Bad cred def id %s', cd_id)
            raise BadIdentifier('Bad cred def id {}'.format(cd_id))

        cred_def = json.loads(
            await self.get_cred_def(cd_id))  # ensure cred def is in cache

        if 'revocation' in cred_def['value']:
            with REVO_CACHE.lock:
                rr_id = Tails.current_rev_reg_id(self.dir_tails, cd_id)
                tails = REVO_CACHE[rr_id].tails
                assert tails  # at (re)start, at cred def, Issuer sync_revoc_for_issue() sets this index in revo cache

                try:
                    (
                        cred_json, cred_revoc_id, _
                    ) = await anoncreds.issuer_create_credential(  # issue by default to rr
                        self.wallet.handle, cred_offer_json, cred_req_json,
                        json.dumps({
                            k: cred_attr_value(cred_attrs[k])
                            for k in cred_attrs
                        }), rr_id, tails.reader_handle)
                    rv = (cred_json, cred_revoc_id)

                except IndyError as x_indy:
                    if x_indy.error_code == ErrorCode.AnoncredsRevocationRegistryFullError:
                        (tag, rr_size_suggested) = Tails.next_tag(
                            self.dir_tails, cd_id)
                        rr_id = rev_reg_id(cd_id, tag)
                        if self.rrbx:
                            await self._set_rev_reg(rr_id, rr_size)
                        else:
                            await self.rrb.create_rev_reg(
                                rr_id, rr_size or rr_size_suggested)
                            await self._send_rev_reg_def(rr_id)

                        REVO_CACHE[rr_id].tails = await Tails(
                            self.dir_tails, cd_id).open()  # symlink OK now
                        return await self.create_cred(cred_offer_json,
                                                      cred_req_json,
                                                      cred_attrs)

                    LOGGER.debug(
                        'Issuer.create_cred <!< cannot create cred, indy error code %s',
                        x_indy.error_code)
                    raise
        else:
            try:
                (cred_json, _, _) = await anoncreds.issuer_create_credential(
                    self.wallet.handle, cred_offer_json, cred_req_json,
                    json.dumps({
                        k: cred_attr_value(cred_attrs[k])
                        for k in cred_attrs
                    }), None, None)
                rv = (cred_json, None)
            except IndyError as x_indy:
                LOGGER.debug(
                    'Issuer.create_cred <!< cannot create cred, indy error code %s',
                    x_indy.error_code)
                raise

        LOGGER.debug('Issuer.create_cred <<< %s', rv)
        return rv