-
Notifications
You must be signed in to change notification settings - Fork 28
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
refactor!: fetch attestation as part of verifyCredential #708
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So now verifying a delegation and the list of trusted attesters would be outside of any function calls, is that correct?
* @returns Information on the attester and revocation status of the on-chain attestation, as well as info on which trust policy led to acceptance of the credential. | ||
*/ | ||
export async function verifyAttested( | ||
credential: ICredential, | ||
allowedAuthorities?: TrustPolicies, | ||
allowRevoked = false | ||
throwOnRevoked = true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doesn't this function also returns a VerifiedCredential
? Plus, I agree that removing the throwOnRevoked
flag is more future-proof and less prone to mistakes, as people might want to check other stuff as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it does not actually verify a credential. Just checks the attestation. Name suggestions for that function are welcome.
Correct! And given you agree that the revocation check can go to, so would be checking if revoked === false. |
+1 for this solution then. I like it. Not sure if the additional type would be more confusing for the users. Maybe @weichweich can provide his opinion on this a well. Maybe @arty-name also? |
Yes, would love to hear their opinions. Compare this one as well against having a separate function for checking the attestation (i.e. not changing verifyCredential at all - see for example #709, but could also be a regular function) and let me know which of the three you think is best. |
I like this much more than the proposal I have seen previously! The alternative of having a separate function does not appeal to me either. |
So to summarise, this seems to be the preferred solution rn, and we'd probably drop the If the type CredentialStatus = {
attester: DidUri
revoked: boolean
}
async function refreshRevocationStatus(
credential: ICredential,
lastStatus: CredentialStatus
): Promise<CredentialStatus> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly comments. The code looks good.
/** | ||
* Verifies data structure & data integrity of a credential object. | ||
* This combines all offline sanity checks that can be performed on an ICredential object. | ||
* A credential is valid only iff it is well formed and there is an onchain attestation record referencing its root hash. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* A credential is valid only iff it is well formed and there is an onchain attestation record referencing its root hash. | |
* A credential is valid only if it is well formed and there is an on-chain attestation record referencing its root hash. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not the only one thrown off by the usage of "iff" in the sense "if and only if" :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this even true? If I have a well structured credential with an on-chain attestation record it can still be invalid? E.g. if the delegation record is wrong or doesn't exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, this isn't complete; the attestation also needs to match the credential, i.e. delegation and ctype ids need to be identical to what was committed to in the credential. BTW that's something we should have designed differently; the runtime should combine at least delegation & ctype info into the credential hash so there is no chance these data do not match if the hash is identical. Ideally also the attester and block number, just to make sure the same attestation is never re-created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, we would do these checks as part of verifyCredential now.
There was a problem hiding this comment.
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 the discussion for credentials v2. We can build on the current limitations and do better.
): Promise<VerifiedCredential> { | ||
const { revoked, attester } = await verifyAttested( | ||
verifiedCredential, | ||
throwOnRevoked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we not want this always to be the case? We decided that if there was an error, we'd always throw it.
Is there a reason why we might not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The consensus was actually to get rid of this flag entirely, so that it never throws when the attestation is revoked. This was to control whether or not you consider a revoked attestation invalid for the sake of you application.
*/ | ||
isDelegator?: boolean | ||
type VerifyOptions = { | ||
ctype?: ICType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it strange that CType isn't required. Is there a specific reason why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see previous comment
@arty-name @ntn-x2 @weichweich @Dudleyneedham |
Just a point, that relying too much on Typescript could leave Javascript developers in the dark. But I like the name |
cdd2782
to
8a1f011
Compare
I think both options are fine, and I have a slight preference for the current VerifiedCredential |
I hope that can be resolved with good docstrings. |
I agree with Antonio. In JS code i would just use
And that's always true. |
And thaaaaaat's why we banished you to the Rust dungeon! Please don't ever do that ever. You're calling an async function, so it returns a Promise which is always truthy, even if the function throws an exception, and even if the value it wraps is itself falsy. So I don't think the average JS developer works on the basis of 'just call some function and assume it returns a boolean'. You read the docstring at the very least, how else would you know what it does, when it throws, and what it returns? The same approach would fail miserably with vc.js's verify function signature (https://github.com/digitalbazaar/vc#verifying-a-verifiable-presentation), where you get the final result from the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very good to me! I could not find any mention in the JSDocs about the consumer actually having to check the trusted attester list and the delegation information.
Again, you don't have to match the attester to some list of trusted attesters in your application; your application could also limit itself to simply displaying the attester DID or web3name to the user, like most of our apps do. That's why this solution is better than the previous candidate, which I think was too opinionated. I could add something like "this data can be used in logic deciding whether to accept or reject a credential submission", but a naive user would probably need more context to understand what that means, which they can only get from the docs - the docstring wouldn't be the right place to explain conceptual fundamentals, would it? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed a VERY old version of this PR, somehow. The new changes look way better, sorry about that. Great work!
fixes https://github.com/KILTprotocol/ticket/issues/2397
also fixes https://github.com/KILTprotocol/ticket/issues/2305
This changes the function signature of
verifyCredential
andverifyPresentation
so that all verification steps are covered, including querying the attestation. They now return the credential enriched with attester and revocation info. Assertions about both are NOT made, but these depend a lot on your application logic and are trivial to do with the data returned from these functions.I like the idea of having a data model for a
VerifiedCredential
bc it allows introducing another function, calledrefreshRevocationStatus
here, which queries the attestation again and throws not only failing if the attestation has been deleted or has been recreated with incoherent data, but also if it has been recreated by a different attester. This avoids potential exploits where a verifier who only checks the revoked flag once a credential has been verified is tricked by a claimer who simply self-attests their credential after the original attester had deleted the attestation.If preferred, I could return just revocation status and attester though, which would then mean
refreshRevocationStatus
would take both the credential and the last status as input parameters.Usage Example
How to test:
Test coverage in unit tests and integration tests has been added.
Checklist: