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

feature/SDK-8 #27

Merged
merged 37 commits into from
May 10, 2024
Merged

feature/SDK-8 #27

merged 37 commits into from
May 10, 2024

Conversation

zoemaas
Copy link
Contributor

@zoemaas zoemaas commented Apr 5, 2024

No description provided.

@zoemaas zoemaas marked this pull request as ready for review April 5, 2024 17:14
@zoemaas zoemaas requested a review from nklomp April 5, 2024 17:15
@zoemaas zoemaas self-assigned this Apr 5, 2024
Copy link
Contributor

@nklomp nklomp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See remarks

const query = `?${[offset && `page[after]=${offset}`, size && `page[size]=${size}`, controller && `controller=${controller}`]
.filter(Boolean)
.join('&')}`
return await (await fetch(`https://api-pilot.ebsi.eu/did-registry/v5/identifiers${query}`)).json()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure that all the static links to EBSI are an optional parameter from a predefined set as well as string (string union). Then have a default value you can configure on the plugin for the EBSI environment. That way it will always use the default, and you can optionally provide a different env

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added query url to getUrls method

*/
export const listDidDocuments = async (args: GetDidDocumentsParams): Promise<GetDidDocumentsResponse> => {
const { offset, size, controller } = args
const query = `?${[offset && `page[after]=${offset}`, size && `page[size]=${size}`, controller && `controller=${controller}`]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really confusing construct to read, especially because of the string interpolation happening from the beginning. Please make that better readable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted it to if clauses

if (!did) {
throw new Error('did parameter is required')
}
const query = `?${validAt && `valid_at=${validAt}`}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use string interpolation within string interpolation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted nested string interpolation into ternary

id: number
}): Promise<Response> => {
const { params, id } = args
const options = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract these into a method. We now have the same things all over the place, with only a different method. If we need to change something in the future the chance of introducing bugs is high

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved the code to the buildFetchOptions(...) method

packages/did-provider-ebsi/src/services/EbsiRPCService.ts Outdated Show resolved Hide resolved
* @constant {string} jsonrpc
*/
const jsonrpc = '2.0' // optional param of plugin?
const baseUrl = 'https://api-pilot.ebsi.eu/did-registry/v5/jsonrpc' // optional param of plugin?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be part of a string union containing the different EBSI environments. Then we should concatenate the version and did-registry.

Copy link
Contributor Author

@zoemaas zoemaas May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created EbsiEnvironment type which is a string intersection of different environments and moved it to src/types.ts. Created a function getUrls to build the url and moved it to src/function.ts. Adjusted the service methods and the plugin constructor to receive an additional optional parameter: apiOpts

return SigningKey.computePublicKey(bytes, false)
}
case 'Secp256r1': {
const jwk: JsonWebKey = toJwk(key.publicKeyHex, type, { use: JwkKeyUse.Signature, key })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to convert the public key hex to JWK and then back to public key hex?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, what is the difference? If there is one, please describe it

Copy link
Contributor Author

@zoemaas zoemaas May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is explained in the documentation: "Public key as hex string. For an ES256K key, it must be in uncompressed format prefixed with "0x04". For other algorithms, it must be the JWK transformed to string and then to hex format."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reread the implementation (which will be wrong anyway, as that is not how you convert a jwk to hex to begin with. We have functions for these conversions). The comment reads it should be hex and the last thing you do is convert to base16 which is hex.

As mentioned why would you need to do it to begin with? If the comment is correct then you already had the hex key, if not then you need to adjust, but converting a jwk to base16 directly always is wrong, you need to get the public key component first (and we have functions for these)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. It seems they really are doing a conversion on the JWK to hex, which is rather odd without a codec to begin with and thus prone to errors across platforms, but so be it. Please create a comment explaining what is happening and why, as I would immediately start working on this code if an error occurs, because it looks so of

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment explaining what and why is happening.

"debug": "^4.3.4",
"did-resolver": "^4.1.0",
"ethers": "^6.11.1",
"jose": "^4.14.6",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please merge other branch, because jose is still (or again?) listed as dependency and that for sure isn't compatible with RN

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed jose dependency

Copy link
Contributor

@nklomp nklomp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see remarks

@@ -53,6 +94,125 @@ export class EbsiDidProvider extends AbstractIdentifierProvider {
throw Error(`Type ${type} not supported`)
}

async createEbsiDid(
args: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create an interface/type out of this like done elsewhere

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extracted CreateEbsiDidParams interface

async createEbsiDid(
args: {
identifier: Omit<IIdentifier, 'provider'>
secp256k1ManagedKeyInfo: ManagedKeyInfo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason we expect ManagedKeyInfos instead of kids for these? This is a public function and in most situations we would use a kid for them

Copy link
Contributor Author

@zoemaas zoemaas May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved it to functions.ts as part of another comment

)
}

private sendTransaction = async (args: { docTransactionResponse: Response; kid: string; id: number; apiOpts?: ApiOpts }, context: IContext) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move a lot of these methods to functions elsewhere. They are now all in the Ebsi DID provider, which is following the Veramo contract. A lot of these methods are stateless and thus could become external functions, so they are reusable also outside the provider

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the Veramo unrelated methods to functions.ts

args: {
did: string
document: Partial<DIDDocument>
options?: { [p: string]: any }
},
context: IAgentContext<IKeyManager>
// TODO extract a type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create the type. The TODO was more work than creating the type itself

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted interface UpdateIdentifierParams

@@ -117,14 +277,31 @@ export class EbsiDidProvider extends AbstractIdentifierProvider {
throw Error(`Not (yet) implemented for the EBSI did provider`)
}

updateIdentifier(
// TODO How does it work? Not inferable from the api: https://hub.ebsi.eu/apis/pilot/did-registry/v5/post-jsonrpc#updatebasedocument
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean with not inferable? Of course I know what the did is. Also this is an optional method to implement once you want to work with passing in updated (partial) did documents. The regular way would be the add/remove service endpoint and add/remove keys methods

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What will actually change in the did? That's the request body:

{
  "jsonrpc": "2.0",
  "method": "updateBaseDocument",
  "params": [
    {
      "from": "0xc9765eC58E49e6E386549CEAA34FB0d8bB69C320",
      "did": "did:ebsi:z23eve8qDNzYuNVqfSaErpuJ",
      "baseDocument": "{\"@context\":[\"https://www.w3.org/ns/did/v1\",\"https://w3id.org/security/suites/jws-2020/v1\"]}"
    }
  ],
  "id": 524
}

And there is no did document.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thenit is likely this method should not have been implemented to begin with and throw an error instead. Veramo uses add and delete services and keys methods to manage updates to the identifier and did document. It recently added a method to pass in a new (partial) did document. If however a did provider cannot work with that it should throw an error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just throwing an error then

const { key, type } = args
switch (type) {
case 'Secp256k1': {
const bytes = getBytes('0x' + key.publicKeyHex, 'key')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you check whether we are not already storing the key in uncompressed form?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, our keys are 66 bytes long and start with 02 or 03. We are looking for a key that is 130 bytes long and start with 04

return SigningKey.computePublicKey(bytes, false)
}
case 'Secp256r1': {
const jwk: JsonWebKey = toJwk(key.publicKeyHex, type, { use: JwkKeyUse.Signature, key })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. It seems they really are doing a conversion on the JWK to hex, which is rather odd without a codec to begin with and thus prone to errors across platforms, but so be it. Please create a comment explaining what is happening and why, as I would immediately start working on this code if an error occurs, because it looks so of

return base64url(sha256(data))
}

const check = (value: unknown, description: string) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the name more descriptive like assertPresent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed it to checkPresent

Copy link
Contributor

@nklomp nklomp May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, checkPresent leaves the reader to guess whether it is returning booleans, doing some other logic or doing assertions. Why would you go with a less optimal name than suggested? The suggestion made really clear that the method would be throwing errors

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed to assert present

token: string
apiOpts?: ApiOpts
}): Promise<Response> => {
const { params, id, token, apiOpts } = args
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still the same comment as I made earlier about the method being different. We now have 4 or 5 times 3 lines which litteraly are the same except for the method value being different. Please create one single method and call that with the params, as this can only lead to bugs in th efuture

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored it to a single function callRpcMethod

"@sphereon/ssi-sdk-ext.did-resolver-ebsi": "workspace:*",
"@sphereon/ssi-sdk-ext.did-resolver-ebsi": "workspace:^",
"@sphereon/ssi-sdk-ext.key-utils": "workspace:^",
"@transmute/did-key-bls12381": "0.3.0-unstable.10",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this dep added?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used anywhere, removed

@nklomp nklomp merged commit 381cc08 into develop May 10, 2024
2 checks passed
@nklomp nklomp deleted the feature/SDK-8 branch May 10, 2024 19:04
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 this pull request may close these issues.

2 participants