Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create IndyVdrPoolService in AFJ #1108

Closed
TimoGlastra opened this issue Nov 17, 2022 · 5 comments · Fixed by #1160
Closed

Create IndyVdrPoolService in AFJ #1108

TimoGlastra opened this issue Nov 17, 2022 · 5 comments · Fixed by #1160
Assignees

Comments

@TimoGlastra
Copy link
Contributor

TimoGlastra commented Nov 17, 2022

Create an IndyVdrPoolService using the indy-vdr library that can be used to write and read objects from the ledger.

Create the @aries-framework/indy-vdr package

As the Indy VDR code will be hosted outside of the AFJ core package we need to create a new indy-vdr package first.

General steps to create a new pacakge:

  1. Copy an existing 'simple' package (e.g. action-menu) protocol.
  2. Rename all occurences of the previous package name (Action Menu, action-menu, etc..) to indy-vdr.
  3. Remove dependencies that are specific to the old package (not sure if there's any for action menu), and delete all files from the src/ directory except for an empty src/index.ts file
  4. Add the indy-vdr-test-shared package to both the peerDependencies and devDependencies and set the indy-vdr-test-shared peer dependency as optional (https://classic.yarnpkg.com/en/docs/package-json#toc-peerdependenciesmeta).
  5. For now in the index.ts file add the following code to the top of the file to dynamically register the nodejs bindings. This is to prevent a hard dependency on the node implementation and will be improved upon in the future. You can eslint disable any eslint errors you're getting. Now whenver in the code you're importing something, make sure to import it from the shared library, not the nodejs library.
try {
  require("indy-vdr-test-nodejs");
} catch (error) {
  throw new Error("Error registering nodejs bindings for Indy VDR");
}

Done!

The indy-vdr-test-shared and indy-vdr-test-nodejs will need to be updated to the non test package once these are released to npm. Also, the indy-vdr-react-native should be added as optional peer dependency once this is released to npm.

Create the IndyVdrPool

The IndyVdrPool is a class that wraps the PoolCreate object from the indy-vdr library and allow us to connect to a pool. The IndyVdrPool class manages a single pool. The IndyVdrPoolService that will be created in the next step will be able to manage multiple IndyVdrPool instance to achive multi-ledger support.

The interface of the IndyVdrPool should look as follows. This doesn't have to be an interface, but just so it's clear which methods should be exposed on the class. You can look at the implementation of IndyPool currently present in AFJ ,however do not this is based on Indy SDK which has quite a different api.

interface TransactionAuthorAgreement {
  version: `${number}.${number}` | `${number}`;
  acceptanceMechanism: string;
}

interface IndyVdrPoolConfig {
  genesisTransactions: string;
  isProduction: boolean;
  indyNamespace: string;
  transactionAuthorAgreement?: TransactionAuthorAgreement;
}

interface IndyVdrPool {
  get config(): IndyVdrPoolConfig;
  get indyNamespace(): string;
  connect(): void;
  close(): void;

  submitReadRequest<Request extends IndyVdrRequest>(
    request: Request
  ): Promise<RequestResponseType<Request>>;

  submitWriteRequest<Request extends IndyVdrRequest>(
    request: Request
  ): Promise<RequestResponseType<Request>>;
}
  • Create the pool services and related files in the pool directory in the src. This shouldn't be exported from the pacakge. Do create an index.ts in the pool directory to export the files from that directory.
  • Add the methods from the interface above. Here's some pointers on how to implement the different method:
    • the constructor takes in the poolConfig (IndyVdrPoolConfig and the logger)
    • config getter just returns the config
    • indyNameSpace getter returns the indyNameSpace from the config
    • connect should create instance of PoolCreate from the indy vdr library, and pass the transactions from the config to it. The pool should be stored as private property in the class
    • close should call close on the pool that is stored as private property (if defiend, could be undefined if pool is not created yet)
    • submitReadRequest should call pool.submitRequest and pass the request to it.
    • submitWriteRequest should call pool.submitRequest and pass the request to it.

That's it!

Creating the IndyVdrPoolService

The IndyVdrPoolService is going to manage all pool instances for the different ledgers we want to connect to. The IndyVdrPoolService can be based on the IndyPoolService currently present in AFJ Core.

One of the most complex parts of the IndyVdrPoolService is going to handle legacy identifiers that don't specify which ledger an object should be retrieved from. You can copy this for the most part from the IndyPoolService. What is important to know:
New identifier types are structured like this:
did:indy:sovrin:7Tqg6BwSSWapxgUDm9KKgg

Old identifier types are structured like this:
7Tqg6BwSSWapxgUDm9KKgg

The old identifier type doesn't hint at from which ledger (sovrin in this case) we need to get the did. The old service contains complex logic to fetch them from all configured pools and then determine the best ledger.

  • Start from the IndyPoolService currently present in AFJ and update this to the IndyVdrPoolService
  • Update all references to the IndyVdrPool and remove unneeded old code
  • Remove the setPools call, for now you can hardcode an arrray of IndyVdrPools (will address in next steps / other issue)
  • remove the ledgerWritePool call
  • move the logic of the getPoolForDid to a new private getPoolForLegacyDid method
  • In the getPoolForDid method check if the did starts with did:indy, if this is the case parse the did and extract the namespace (https://hyperledger.github.io/indy-did-method/#indy-did-method-identifiers) from it. Then look for a pool where the pool.indyNamespace matches this namespace. If no pool is found, throw an error. If the did doesn't start with did:indy, call the getPoolForLegacyDid and return the value.
  • make the indyNamespace property required in the getPoolForNamespace method. We don't want to take the first pool autmoatically anymore.
  • Update the getDidFromPool to use the indy vdr library
  • Move the getTransactionAuthorAgreement and appendTaa methods to the IndyVdrPool class and use them in the submitWriteRequest (like is currently done in the IndyPoolService.submitWriteRequest).
  • Remove the submitReadRequest and submitWriteRequest methods from the IndyVdrPoolService. The user will call pool.submitWriteRequest directly.
  • Remove the signRequest method from the IndyVdrPool class
  • Update the submitWriteRequest method from the IndyVdrPool (not service as that is deleted) to sign the request. (after the taa acceptance). This can be done by getting the signing input using request.signatureInput and passing that to agentContext.wallet.sign. (TODO: we need to pass the verkey to the wallet, but we only have the did here. We need a way to get the verkey based on a did, but in a generic way. Best way is prob through the did repository created records -- this.didRepository.getCreatedDids(this.agentContext, { method }) -- but that won't work for dids that are not created using the did registrar. Should we add a method to get a did from the wallet? It would need to be temporary - TDB)

Note on getTaa / appendTaa

to get the taa using the indy-vdr library you can use the GetTransactionAuthorAgreementRequest. to get the acceptance mechanism list you can use the GetAcceptanceMechanismsRequest. to append the TAA is a bit more complex. You can do so using the following code. indyVdr can be imported from indy-vdr-test-shared. The values can be populated based on the TAA and current code.

const acceptance = indyVdr.prepareTxnAuthorAgreementAcceptance({})

request.setTransactionAuthorAgreementAcceptance({
  acceptance
})

Tests

TODO

@Vickysomtee
Copy link
Contributor

Vickysomtee commented Nov 23, 2022

@TimoGlastra were you intentional about the code below cus' I assume it is supposed to be an import instead of a require?

try {
  require("indy-vdr-test-nodejs");
} catch (error) {
  throw new Error("Error registering nodejs bindings for Indy VDR");
}

@TimoGlastra
Copy link
Contributor Author

I don't think you can do conditional imports as we'rs using node modules. So using require is sort of an hack

@TimoGlastra
Copy link
Contributor Author

@Vickysomtee updated the issue with more details and also made an amendment to a previous step. First it mentioned submitWriteRequest should call pool.submitAction, but that should actually be _submitWriteRequest should call pool.submitRequest `

@Vickysomtee
Copy link
Contributor

@Vickysomtee updated the issue with more details and also made an amendment to a previous step. First it mentioned submitWriteRequest should call pool.submitAction, but that should actually be _submitWriteRequest should call pool.submitRequest `

Should it be the same for submitReadRequest?

@TimoGlastra
Copy link
Contributor Author

Both should call submitRequest. submitAction is used for some specific calls that we aren't using

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants