Verifications Examples

This sections aims to help getting started with the verifications service. Herein are shown a few examples on

  • how to set verifications
  • how to get verifications
  • how to handle verifications for externally owned accounts and contracts
  • how to check verifications for validity

Verifications can be issued with the Verification API. Their smart contract implementation follow the principles outlined in ERC-725 and ERC-735.

About the Code Examples

Many code examples are taken from the verification tests. You can a look at those for more examples or to have a look at in which context the test are run in.

Code examples on this page use an initialized module called verifications, which is an instance of the verification module Verification API. When using a runtime this module is available as runtime.verifications.

Many code examples here use variable naming from the tests. So there are a few assumptions about variables used here:

  • accounts is an array with addresses of externally owned accounts, of which private keys are known to the runtime/executor/signer instance to make transaction with them
  • accounts[0] usually takes the role of issuing verifications
  • accounts[1] is usually an account that responds to actions from accounts[0]
  • contractId refers to the address of a contract, that is owned by accounts[0], this contract usually has a DBCP description

Different Types of Identities

Okay, this page is about berifications, so why does the first heading talk about some kind of identities?
The answer for this is pretty simple simple: Verifications are issued by identities.

So what exactly is an identity?
An identity is an instance, that is able to issue and hold verifications (therefore the technical name VerificationHolder.

Technically speaking, identities are like Wallet Contracts, that have a certificate store attached to them. Identities can be traced back to an externally owned account, that owns this wallet contract and vice versa the matching identity (wallet) contract can be looked up for any given externally owned account, if this account has created and registered an identity contract.

Because of their wallet-like behavior, identity contracts require a calling instance, for example an externally owned account or another wallet to call them for managing their verifications. For example to issue a verification for another identity, a user makes a transaction against its own identity contract, which itself makes a transaction to create a verification in the other identity.

This works pretty well and comes with useful features like preparing “outgoing” transactions to be made from ones own identity, that can be paid by another party, but has some implications when applied as “smart contract identities” instead of “account identities”. These smart contracts would need to be owner of their own identities and working with verifications would require the contracts to have functions for issuing, rejecting, etc. verification or offer wallet like functionalities themselves, which would make it often pretty hard to introduce verifications in an existing smart contract infrastructure and it would often have an impact on these contracts costs as these extra functionalities have to be added to the contract.

To allow for a more easy integration, contract identities have been added as a 32 Bytes identities or pseudonyms, which can be created with a registry contract. These 32 Bytes identities are IDs in a central registry, that offers basically the same functionalities as the single identity contracts. Identities at the registry are owned by the account, that created them (ownership can be transfered) and ownership over such an identity grants the permission to make transactions for it the same way as for the own account identity.

So let’s get to the code examples.

Account Identities

Note that the creation example can be omitted when using an account creating during the visual onboarding flow, as this includes creating an identity.

If you want to check if your account already has an identity, you can try to get its identity contracts address with identityAvailable:

if (await verifications.identityAvailable(accounts[0])) {
  console.log(`account "${accounts[0]}" has an identity`);
} else {
  console.log(`account "${accounts[0]}" does not have an identity`);
}

Account identities can be created straight forward by calling createIdentity and passing the account to it, an identity should be created for.

const identity = await verifications.createIdentity(accounts[0]);
console.log(identity);
// Output:
// 0x1fE5F7235f1989621135466Ff8882287C63A5bae

This returns the 40Bytes contract address of the accounts identity contract.

The given account now has an identity attached to it, which is a requirement for interacting with the rest of the verifications API and only has to be done once per externally owned account.

Contract Identities

Contract identities are created “on behalf of” a contract. An externally owned account, often the owner of the contract, usually does the following:

  • creating an identity for the contract
  • linking contract and identity
  • providing the information of which identity belongs to a contract to other parties

Creating a contract identity registers a new contract identity at the registry, this identity is then owned by the executing accountId.

Linking is done by registering the contract address as the receiver of the identity, this is done at the registry as well.

When third parties want to check verifications of a contract, they need a way to get the identity for a contract. This can be done by adding an extra function to the contract, by setting up a lookup mechanism, etc. DBCP is a description language used throughout evan.network, which provides a way to add meta information to smart contracts. The contract identity is usually added to this DBCP description of a contract.

The aforementioned three steps are covered by the createIdentity function, which can be called with:

const contractIdentity = await verifications.createIdentity(accounts[0], contractId);
console.log(idencontractIdentitytity);
// Output:
// 0x4732281e708aadbae13f0bf4dd616de86df3d3edb3ead21604a354101de45316

When using contracts without descriptions or when handling the relation between contracts and an identity elsewhere, the process of updating the description can be omitted. For this set the updateDescription argument to false:

const contractIdentity = await verifications.createIdentity(accounts[0], contractId, false);
console.log(idencontractIdentitytity);
// Output:
// 0x4732281e708aadbae13f0bf4dd616de86df3d3edb3ead21604a354101de45316

Issue verifications

Verifications are statements, issued by an account called issuer, towards target, called subject. This basically means something like “The person issuer says, a statement applies to subject”. The subject may or may not react to it by confirming or rejecting it. Technically speaking, the issuer identity issues the verification with the statement to subject`s identity and the subjects`s identity may react to it.

Issue verifications for an account

const verificationId = await verifications.setVerification(
  accounts[0], accounts[1], '/example1');
console.log(verificationId);
// Output:
// 0xb4843ed5177433312dd2c7c4f8065ce84f37bf96c04db2775c16c9455ad96270

const issued = await verifications.getVerifications(accounts[1], '/example1');
console.dir(issued);
// Output:
// [ {
//   creationBlock: '186865',
//   creationDate: '1558599441',
//   data: '0x0000000000000000000000000000000000000000000000000000000000000000',
//   description: null,
//   disableSubVerifications: false,
//   expirationDate: null,
//   expired: false,
//   id: '0xb4843ed5177433312dd2c7c4f8065ce84f37bf96c04db2775c16c9455ad96270',
//   issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//   name: '/example1',
//   rejectReason: undefined,
//   signature: '0x6a2b41714c1faac09a5ec06024c8931ad6e3aa902c502e3d1bc5d5c4577288c04e9be136c149b569e0456dfec9d50a2250bf405443ae9bccd460c49a2c4287df1b',
//   status: 0,
//   subject: '0x0030C5e7394585400B1FB193DdbCb45a37Ab916E',
//   topic: '34884897835812838038558016063403566909277437558805531399344559176587016933548',
//   uri: '',
//   valid: true
// } ]

Have a look at getVerifications or the section on this page for the meaning of the returned values, for how to find out, if the returned verification trustworthy, have a look at Validating Verifications.

Issue verifications for a contract with a description

const verificationId = await verifications.setVerification(
  accounts[0], contractId, '/example2');
console.log(verificationId);
// Output:
// 0x2bc6d5fdb937f6808252b837437220d8e16b92a974367f224260d028413e7c6e

const issued = await verifications.getVerifications(contractId, '/example2');
console.dir(issued);
// [ {
//   creationBlock: '187823',
//   creationDate: '1558621998',
//   data: '0x0000000000000000000000000000000000000000000000000000000000000000',
//   description: null,
//   disableSubVerifications: false,
//   expirationDate: null,
//   expired: false,
//   id: '0x2bc6d5fdb937f6808252b837437220d8e16b92a974367f224260d028413e7c6e',
//   issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//   name: '/example2',
//   rejectReason: undefined,
//   signature: '0x0f4f19a369645a0ec2795bd2836fad0857ef82169c7e5800d7a06fb162583c9c14a731f4e942cf30d67fb10a551d9060f71642d25bb6c2c226bae47b3acb13581b',
//   status: 0,
//   subject: '0x005C5FF57D4d6Bf105Bf3bF16ffCd8Ac143B3Ef0',
//   topic: '107276559880603231420598591656057035604273757486333915273364042567965107775848',
//   uri: '',
//   valid: true
// } ]

Have a look at getVerifications or the section on this page for the meaning of the returned values, for how to find out, if the returned verification trustworthy, have a look at Validating Verifications.

Note that for contracts with descriptions the contractId can be given to setVerification and getVerifications. The contract identity is fetched from the contract description automatically.

Issue verifications for a contract without using a description

// assume, we have created an identity for our contract and stored this identity as the variable ``contractIdentity``
const verificationId = await verifications.setVerification(
  accounts[0], contractIdentity, '/example3', 0, null, null, false, true));
console.log(verificationId);
// Output:
// 0x2bc6d5fdb937f6808252b837437220d8e16b92a974367f224260d028413e7c6e

const issued = await verifications.getVerifications(contractIdentity, '/example3', true);
console.dir(issued);
// [ {
//   creationBlock: '187823',
//   creationDate: '1558621998',
//   data: '0x0000000000000000000000000000000000000000000000000000000000000000',
//   description: null,
//   disableSubVerifications: false,
//   expirationDate: null,
//   expired: false,
//   id: '0x2bc6d5fdb937f6808252b837437220d8e16b92a974367f224260d028413e7c6e',
//   issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//   name: '/example2',
//   rejectReason: undefined,
//   signature: '0x0f4f19a369645a0ec2795bd2836fad0857ef82169c7e5800d7a06fb162583c9c14a731f4e942cf30d67fb10a551d9060f71642d25bb6c2c226bae47b3acb13581b',
//   status: 0,
//   subject: '0x005C5FF57D4d6Bf105Bf3bF16ffCd8Ac143B3Ef0',
//   topic: '107276559880603231420598591656057035604273757486333915273364042567965107775848',
//   uri: '',
//   valid: true
// } ]

In case you’re wondering: contractIdentity is the same identity as returned in our example.

Have a look at getVerifications for the meaning of the returned values, for how to find out, if the returned verification trustworthy, have a look at Validating Verifications.

Note that for contracts without descriptions contractIdentity is given and the last argument (isIdentity) is set to true. The functions setVerification and getVerifications support passing a contract identity to them as well and they also have the argument isIdentity, which is set to true, when passing contract identities to them.


Validating Verifications

Verifications can be retrieved with two different functions:

  • getVerifications: simple “fetch all” verifications for a topic, returns all validations and detailed validity checks have to be made by hand
  • getNestedVerification: return verifications with default checks and inspects parent verifications as well, used for verifications, that should be traced back to a trusted root verifier

getVerifications

The example for getVerifications is the same we used when creating a verification for and account:

const verificationId = await verifications.setVerification(
  accounts[0], accounts[1], '/example1');
console.log(verificationId);
// Output:
// 0xb4843ed5177433312dd2c7c4f8065ce84f37bf96c04db2775c16c9455ad96270

const issued = await verifications.getVerifications(accounts[1], '/example1');
console.dir(issued);
// Output:
// [ {
//   creationBlock: '186865',
//   creationDate: '1558599441',
//   data: '0x0000000000000000000000000000000000000000000000000000000000000000',
//   description: null,
//   disableSubVerifications: false,
//   expirationDate: null,
//   expired: false,
//   id: '0xb4843ed5177433312dd2c7c4f8065ce84f37bf96c04db2775c16c9455ad96270',
//   issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//   name: '/example1',
//   rejectReason: undefined,
//   signature: '0x6a2b41714c1faac09a5ec06024c8931ad6e3aa902c502e3d1bc5d5c4577288c04e9be136c149b569e0456dfec9d50a2250bf405443ae9bccd460c49a2c4287df1b',
//   status: 0,
//   subject: '0x0030C5e7394585400B1FB193DdbCb45a37Ab916E',
//   topic: '34884897835812838038558016063403566909277437558805531399344559176587016933548',
//   uri: '',
//   valid: true
// } ]

As stated above, only basic validations have been made on the data of the verifications, so conclusions have to be drawn by based on the data returned here. For a full list of explanations to the properties have a look at the API documentation, but the ones you will be most probably using the most are:

  • status and rejectReason:

    • status - number:
    • 0 (Issued) || 1 (Confirmed) || 2 (Rejected)
    • reflects how the subject responded (1|2) to the verification or if no response has been made (0)
    • rejectReason - any: object with information from subject about rejection
  • valid - boolean:

    • true if issuer has been correctly confirmed as the signer of signature
    • also checks if provided signature has been correctly built as checksum over subject, topic and data
  • expired and expirationDate:

    • expired - boolean: ticket expiration state
    • expirationDate - string: UNIX timestamp (in seconds), null if verification does not expire
  • issuer - string:

    • account address of issuers identity contract, can be used to check if the issuer is an account, that you trust
  • data and uri:

    • data - string: 32Bytes hash of data stored in DFS
    • uri - string: link to ipfs file of data
    • these two properties point to data, that has been attached to your verification (attaching data is optional)
    • the data referred here is the data provided as verificationValue in setVerification
    • data content handling, especially encryption and key management has be be handled in custom logic and is not covered in here

A sample, on how these properties can be used to determine the trustworthiness of a verification can be found at hem workshop project.

getNestedVerifications

For this section we take the last example and issue two subverifications. We add /example1/exable1_child as the direct child of it and /example1/example1_child/example1_grandchild as a subverification below the first this child.

const verificationId = await verifications.setVerification(
  accounts[0], accounts[0], '/example4');
const verificationId = await verifications.setVerification(
  accounts[0], accounts[0], '/example4/child');
const verificationId = await verifications.setVerification(
  accounts[0], accounts[0], '/example4/child/grandchild');

const issued = await verifications.getNestedVerifications(accounts[0], '/example1/example1_child/example1_grandchild');
console.dir(issued);
// Output:
// [ {
//     name: '/example4/child/grandchild',
//     parent: '/example4/child',
//     warnings: [ 'issued', 'selfIssued', 'parentUntrusted' ],
//     id: '0x1adef760f5a8d153aeeeda7a6e4f8c950fa93b0cb5d3218c6a9389cd05f5f7f6',
//     issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//     status: 0,
//     subject: '0x001De828935e8c7e4cb56Fe610495cAe63fb2612',
//     subjectIdentity: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//     subjectType: 'account',
//     issuerAccount: '0x001De828935e8c7e4cb56Fe610495cAe63fb2612',
//     parents:
//      [ {
//          name: '/example4/child',
//          parent: '/example4',
//          warnings: [ 'issued', 'selfIssued', 'parentUntrusted' ],
//          id: '0x28e1df758883bb3d4d5e7e0fa978ff673bc749ade0a3d78ad952a30d0a0e2a01',
//          issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//          status: 0,
//          subject: '0x001De828935e8c7e4cb56Fe610495cAe63fb2612',
//          subjectIdentity: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//          subjectType: 'account',
//          issuerAccount: '0x001De828935e8c7e4cb56Fe610495cAe63fb2612',
//          parents:
//           [ {
//               name: '/example4',
//               parent: '',
//               warnings: [ 'issued' ],
//               id: '0x18fb0ef05d96cba2a57c6de6d8cfd031e16367f6484f20797a39d25a3e76e20a',
//               issuer: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//               status: 0,
//               subject: '0x001De828935e8c7e4cb56Fe610495cAe63fb2612',
//               subjectIdentity: '0xe560eF0954A2d61D6006E8547EC769fAc322bbCE',
//               subjectType: 'account',
//               issuerAccount: '0x001De828935e8c7e4cb56Fe610495cAe63fb2612',
//               parents: [],
//          } ],
//     } ],
// } ]

The output above has been heavily trimmed down to show differences between both functions and highlight parent to child relations and warnings. To view full output have a look at the full output.

To create a simple chain of verifications, we have used the following structure: - accounts[0] creates a verification for itself, called /example4 - then creates a subverification under called /example4/child for itself under this - then creates another subverification (under the first subverification) called /example4/child/grandchild for itself

The call verifications.getNestedVerifications(accounts[0], '/example1/example1_child/example1_grandchild') now inspects what verifications and possible relations to parent verifications exits and it finds the following possible issues:

  • /example4/child/grandchild: warnings: [ 'issued', 'selfIssued', 'parentUntrusted' ]

    • issued means, that is is only issued and not confirmed, we can see its status is 0, so yes, it is unconfirmed (and if we look above, we actually didn’t confirm the verification)
    • selfIssued, yes, issuer equals subjectIndentity, therefore selfIssued and thinking back, we did issue this verification to ourself
    • parentUntrusted, this means that the parent verification hasn’t been accepted, its status is 0, so yes, only issued and not confirmed
  • following the parent verifications, we find basically the same on the next level

    • /example4/child`: warnings: [ 'issued', 'selfIssued', 'parentUntrusted' ]
    • the same reasons and explanations apply here, so let’s continue to the last on in the verification path
  • /example4: warnings: [ 'issued' ]

    • issued: yep, status is 0 therefore it is only issued and not confirmed

    • no parentUntrusted? as this verification is a root verification, there is not parent

    • no selfIssued?

      • the path is /example4, which makes this verification a root verification

      • root verifications can be issued by any party without being flagged as selfIssued, to allow building own verification chains

      • to narrow this down to a limited set of trusts, there are basically two solutions:

        • own checks can be made, e.g. check if the issuer of the root verification is a well known and trusted account
        • use /evan derived verification paths, the root verification /evan is only trusted, if it is issued by a trusted root issuer, get in contact with us via info@evan.team for details on how to obtain a subverification like /evan/myOwnTrustedVerification, that can be used for building widely accepted verification paths

Warnings in Verifications

getNestedVerification returns a set of different warnings, that can be used to decide if a certification is valid or not. Those warnings are stored in the .warnings property, warnings, that can be returned are:

  • disableSubVerifications: parent verification does not allow subverifications
  • expired: verification has expired
  • invalid: signature does not match requirements, this could be because it hasn’t been signed by correct account or underlying checksum does not match subject, topic and data
  • issued: verification has been issued, but not accepted or rejected by subject
  • missing: verification has not been issued
  • noIdentity: given subject has no identity
  • notEnsRootOwner: verification path has a trusted root verification topic, but this verification is not signed by a trusted instance
  • parentMissing: parent verification is missing in path
  • parentUntrusted: verification path cannot be traced back to a trusted root verification
  • rejected: verification has been issued and then rejected by subject
  • selfIssued: verification issuer is the same account as the subject