Skip to content

Commit e4c67c4

Browse files
committed
Revert "Remove web3 ID from VPV1 implementation"
This reverts commit 0902f1e.
1 parent 0902f1e commit e4c67c4

File tree

7 files changed

+253
-20
lines changed

7 files changed

+253
-20
lines changed

docs/pages/verifiable-presentations.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This document describes how to create v1 verifiable presentations and how to ver
66
<!--toc:start-->
77
- [Build Statement](#build-statement)
88
- [Identity/account credential statements](#identityaccount-credential-statements)
9+
- [Web3 ID credential statements](#web3-id-credential-statements)
910
- [JSON representation](#json-representation)
1011
- [Verifiable Presentation Request (proof request)](#verifiable-presentation-request-proof-request)
1112
- [Verifiable Presentation (proof)](#verifiable-presentation-proof)
@@ -129,6 +130,51 @@ Note that this type of statement is only allowed for the following attributes:
129130
- IdDocIssuer
130131
- IdDocType
131132

133+
### Web3 ID credential statements
134+
135+
To build a statement against a Web3 ID, the builder has exposes an entrypoint `addWeb3IdStatement`,
136+
which has a function signature similar to those used for [identity/account statements](#identityaccount-credential-statements)
137+
138+
1. A list smart contract addresses the Web3 ID must be created from
139+
2. A callback function which should be used to add statements for the credential
140+
141+
#### Reveal statement
142+
143+
State that a given attribute should be revealed as part of the proof.
144+
145+
Example: reveal the education degree of an education ID.
146+
147+
```ts
148+
build.revealAttribute('degree');
149+
```
150+
151+
#### Range statement
152+
153+
State that a given attribute should be between 2 given values.
154+
155+
Example: add the statement that the prover must be hired between January 1,
156+
2015 and Februar 2, 2005.
157+
158+
```ts
159+
build.addRange('hired', 20150101, 20050202);
160+
```
161+
162+
#### Membership statement
163+
164+
Example: add the statement that the prover's position in a company is either "engineer" or "designer"
165+
166+
```ts
167+
build.addMembership('position', ['engineer', 'designer']);
168+
```
169+
170+
#### Non membership statement
171+
172+
Example: add the statement that the prover's position in a company is _not_ "manager":
173+
174+
```ts
175+
build.addNonMembership('position', ['manager']);
176+
```
177+
132178
## JSON representation
133179

134180
The `VerifiablePresentationRequestV1`, `VerifiablePresentationV1`, and `VerifiableAuditRecordV1` can be represented as
@@ -217,6 +263,9 @@ const contextValues: GivenContext[] = [{label: 'ResourceID', context: ...}];
217263
// used as input to the presentation. The difference between the two statement types boil down to the presence of an ID
218264
// qualifier vs. an ID (selected by the application based on the id qualifier).
219265
const statements: VerifiablePresentationV1.Statement[] = presentationRequest.credentialStatements.map((entry) => {
266+
if (entry.type === 'web3Id')
267+
return {id: createWeb3IdDID(...), statement: entry.statement};
268+
220269
// prioritize creating identity based proofs, as these are more privacy-preserving
221270
if (entry.source.includes('identity'))
222271
return {id: createIdentityStatementDID(...), statement: entry.statement};
@@ -228,6 +277,7 @@ const statements: VerifiablePresentationV1.Statement[] = presentationRequest.cre
228277
const inputs: CommitmentInput[] = [
229278
createIdentityCommitmentInputWithHdWallet(...),
230279
createAccountCommitmentInputWithHdWallet(...),
280+
createWeb3CommitmentInputWithHdWallet(...)
231281
];
232282

233283
const presentation = await VerifiablePresentationV1.createFromAnchor(

examples/nodejs/common/verifiable-credential-statements.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
import { IdentityProviderDID, VerifiablePresentationRequestV1 } from '@concordium/web-sdk';
1+
import {
2+
ContractAddress,
3+
ContractInstanceDID,
4+
IdentityProviderDID,
5+
VerifiablePresentationRequestV1,
6+
} from '@concordium/web-sdk';
27

38
// #region documentation-snippet
49
let builder = VerifiablePresentationRequestV1.statementBuilder();
510

11+
// Add a web3 ID credential statements
12+
builder = builder.addWeb3IdStatement([new ContractInstanceDID('Testnet', ContractAddress.create(123))], (b) =>
13+
b.addMembership('position', ['engineer'])
14+
);
15+
616
// Add an identity credential statement. Alternatively, if the proof produced from the
717
// statement should be tied to an account, use `builder.addAccountStatement`.
818
// A third option `builder.addAccountOrIdentityStatement`exists where it's up to the

packages/sdk/src/wasm/VerifiablePresentationV1/proof.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ import {
2424
AtomicStatementV2,
2525
CredentialsInputsAccount,
2626
CredentialsInputsIdentity,
27+
CredentialsInputsWeb3,
2728
DIDString,
2829
IdentityCommitmentInput,
2930
CommitmentInput as OldCommitmentInputs,
3031
Web3IdProofRequest,
32+
Web3IssuerCommitmentInput,
3133
} from '../../web3-id/index.js';
3234
import { getVerifiablePresentation } from '../VerifiablePresentation.js';
3335
import { GivenContextJSON, givenContextFromJSON, givenContextToJSON } from './internal.js';
@@ -60,11 +62,17 @@ export type AccountStatement = { id: DIDString; statement: AtomicStatementV2<Att
6062
*/
6163
export type IdentityStatement = { id: DIDString; statement: AtomicStatementV2<AttributeKey>[] };
6264

65+
/**
66+
* Statement proving attributes from a Web3 ID credential.
67+
* Contains the Web3 ID DID, atomic statements about Web3 attributes, and credential type information.
68+
*/
69+
export type Web3IdStatement = { id: DIDString; statement: AtomicStatementV2<string>[]; type: string[] };
70+
6371
/**
6472
* Union type representing all supported statement types in a verifiable presentation.
6573
* Each statement contains proofs about specific credential attributes.
6674
*/
67-
export type Statement = IdentityStatement | AccountStatement;
75+
export type Statement = IdentityStatement | Web3IdStatement | AccountStatement;
6876

6977
function isSpecifiedAccountCredentialStatement(statement: Statement): statement is AccountStatement {
7078
return statement.id.includes(':cred:');
@@ -160,13 +168,34 @@ export type AccountBasedCredential = {
160168
issuer: DIDString;
161169
};
162170

171+
/**
172+
* A verifiable credential based on Web3 ID smart contract information.
173+
* This credential type contains zero-knowledge proofs about smart contract
174+
* issued credentials and their attributes.
175+
*/
176+
export type Web3BasedCredential = {
177+
/** Type identifiers for this credential format */
178+
type: ['VerifiableCredential', 'ConcordiumVerifiableCredentialV1', 'ConcordiumWeb3BasedCredential'];
179+
/** The credential subject containing Web3 ID statements */
180+
credentialSubject: {
181+
/** The account credential identifier as a DID */
182+
id: DIDString;
183+
/** Statements about Web3 ID attributes (should match request) */
184+
statement: AtomicStatementV2<string>[];
185+
};
186+
/** The zero-knowledge proof for attestation */
187+
proof: ZKProofV4;
188+
/** The issuer of the ID credential used to open the account credential */
189+
issuer: DIDString;
190+
};
191+
163192
/**
164193
* Union type representing all supported verifiable credential formats
165194
* in Concordium verifiable presentations.
166195
*
167196
* The structure is reminiscent of a w3c verifiable credential
168197
*/
169-
export type Credential = IdentityBasedCredential | AccountBasedCredential;
198+
export type Credential = IdentityBasedCredential | AccountBasedCredential | Web3BasedCredential;
170199

171200
/**
172201
* A verifiable presentation containing zero-knowledge proofs of credential statements.
@@ -251,7 +280,7 @@ export function fromJSON(value: JSON): VerifiablePresentationV1 {
251280
* These inputs contain the secret information needed to create proofs without revealing
252281
* the actual credential data.
253282
*/
254-
export type CommitmentInput = IdentityCommitmentInput | AccountCommitmentInput;
283+
export type CommitmentInput = IdentityCommitmentInput | Web3IssuerCommitmentInput | AccountCommitmentInput;
255284

256285
/**
257286
* Creates a verifiable presentation from an anchored presentation request.
@@ -312,7 +341,7 @@ export async function createFromAnchor(
312341
*
313342
* This function generates zero-knowledge proofs for the requested credential statements
314343
* using the provided commitment inputs and proof context. It handles different types
315-
* of credentials (identity-based, account-based) and creates appropriate
344+
* of credentials (identity-based, account-based, Web3-based) and creates appropriate
316345
* proofs for each.
317346
*
318347
* @param requestStatements - The credential statements to prove
@@ -355,18 +384,21 @@ export function create(
355384
request,
356385
});
357386
// Map the output to match the format of the V1 protocol.
358-
const compatibleCredentials: Credential[] = verifiableCredential.map<Credential>((c) => {
359-
const { proof, id, statement } = c.credentialSubject;
387+
const compatibleCredentials: Credential[] = verifiableCredential.map<Credential>((c, i) => {
388+
const { proof, ...credentialSubject } = c.credentialSubject;
360389
const { created, type: _type, ...proofValues } = proof;
390+
const type = isSpecifiedAccountCredentialStatement(compatibleStatements[i])
391+
? 'ConcordiumAccountBasedCredential'
392+
: 'ConcordiumWeb3BasedCredential';
361393
return {
362394
proof: {
363395
createdAt: created,
364396
type: 'ConcordiumZKProofV4',
365397
proofValue: Buffer.from(cborEncode(proofValues)).toString('hex'),
366398
},
367399
issuer: c.issuer,
368-
type: ['VerifiableCredential', 'ConcordiumVerifiableCredentialV1', 'ConcordiumAccountBasedCredential'],
369-
credentialSubject: { id, statement: statement as unknown as AtomicStatementV2<AttributeKey>[] },
400+
type: ['VerifiableCredential', 'ConcordiumVerifiableCredentialV1', type] as any,
401+
credentialSubject,
370402
};
371403
});
372404
// and add stubbed ID credentials in
@@ -400,7 +432,7 @@ export type VerificationResult = { type: 'success' } | { type: 'failed'; error:
400432
* Union type of all credential input types used for verification.
401433
* These inputs contain the public credential data needed to verify proofs.
402434
*/
403-
export type CredentialsInputs = CredentialsInputsAccount | CredentialsInputsIdentity;
435+
export type CredentialsInputs = CredentialsInputsWeb3 | CredentialsInputsAccount | CredentialsInputsIdentity;
404436

405437
/**
406438
* Verifies a verifiable presentation against its corresponding request.

packages/sdk/src/wasm/VerifiablePresentationV1/request.ts

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@ import {
1414
signTransaction,
1515
} from '../../index.js';
1616
import { DataBlob, TransactionExpiry, TransactionHash } from '../../types/index.js';
17-
import { AccountStatementBuild } from '../../web3-id/proofs.js';
18-
import { AtomicStatementV2, DIDString, IDENTITY_SUBJECT_SCHEMA, IdentityProviderDID } from '../../web3-id/types.js';
17+
import { AccountStatementBuild, AtomicStatementBuilder } from '../../web3-id/proofs.js';
18+
import {
19+
AtomicStatementV2,
20+
ContractInstanceDID,
21+
CredentialSchemaSubject,
22+
DIDString,
23+
IDENTITY_SUBJECT_SCHEMA,
24+
IdentityProviderDID,
25+
} from '../../web3-id/types.js';
1926
import { GivenContextJSON, givenContextFromJSON, givenContextToJSON } from './internal.js';
2027
import { CredentialContextLabel, GivenContext } from './types.js';
2128

@@ -199,15 +206,27 @@ export type IdentityStatement = {
199206
issuers: IdentityProviderDID[];
200207
};
201208

209+
/**
210+
* Statement requesting proofs from Web3 ID credentials issued by smart contracts.
211+
*/
212+
export type Web3IdStatement = {
213+
/** Type discriminator for Web3 ID statements */
214+
type: 'web3Id';
215+
/** Atomic statements about Web3 ID attributes to prove */
216+
statement: AtomicStatementV2<string>[];
217+
/** Valid smart contract issuers for this statement */
218+
issuers: ContractInstanceDID[];
219+
};
220+
202221
/**
203222
* Union type representing all supported statement types in a verifiable presentation request.
204223
*/
205-
export type Statement = IdentityStatement;
224+
export type Statement = IdentityStatement | Web3IdStatement;
206225

207226
/**
208227
* JSON representation of statements with issuer DIDs serialized as strings.
209228
*/
210-
type StatementJSON = Omit<IdentityStatement, 'issuers'> & {
229+
type StatementJSON = (Omit<IdentityStatement, 'issuers'> | Omit<Web3IdStatement, 'issuers'>) & {
211230
issuers: DIDString[];
212231
};
213232

@@ -219,6 +238,30 @@ class StatementBuilder {
219238
/** Array of credential statements being built. */
220239
private statements: Statement[] = [];
221240

241+
/**
242+
* Add statements for Web3 ID credentials.
243+
*
244+
* @param validContracts Array of smart contract identifiers that are valid issuers
245+
* @param builderCallback Callback function to build the statements using the provided builder
246+
* @param schema Optional credential schema for validation
247+
*
248+
* @returns The updated builder instance
249+
*/
250+
addWeb3IdStatement(
251+
validContracts: ContractInstanceDID[],
252+
builderCallback: (builder: AtomicStatementBuilder<string>) => void,
253+
schema?: CredentialSchemaSubject
254+
): StatementBuilder {
255+
const builder = new AtomicStatementBuilder<string>(schema);
256+
builderCallback(builder);
257+
this.statements.push({
258+
type: 'web3Id',
259+
issuers: validContracts,
260+
statement: builder.getStatement(),
261+
});
262+
return this;
263+
}
264+
222265
/**
223266
* Add statements for identity credentials.
224267
*
@@ -366,6 +409,11 @@ export function fromJSON(json: JSON): VerifiablePresentationRequestV1 {
366409
const requestContext = { ...json.requestContext, given: json.requestContext.given.map(givenContextFromJSON) };
367410
const statements: Statement[] = json.credentialStatements.map((statement) => {
368411
switch (statement.type) {
412+
case 'web3Id':
413+
return {
414+
...statement,
415+
issuers: statement.issuers.map(ContractInstanceDID.fromJSON),
416+
};
369417
case 'identity':
370418
return {
371419
...statement,

packages/sdk/test/ci/wasm/VerifiablePresentationRequestV1.test.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import _JB from 'json-bigint';
22

3-
import { AttributeKeyString, TransactionHash } from '../../../src/pub/types.ts';
3+
import { AttributeKeyString, ContractAddress, TransactionHash } from '../../../src/pub/types.ts';
44
import { VerifiablePresentationRequestV1 } from '../../../src/pub/wasm.ts';
5-
import { IdentityProviderDID } from '../../../src/pub/web3-id.ts';
5+
import { ContractInstanceDID, IdentityProviderDID } from '../../../src/pub/web3-id.ts';
66

77
const JSONBig = _JB({ alwaysParseAsBig: true, useNativeBigInt: true });
88

@@ -14,6 +14,12 @@ describe('VerifiablePresentationRequestV1', () => {
1414
'Wine payment'
1515
);
1616
const statement = VerifiablePresentationRequestV1.statementBuilder()
17+
.addWeb3IdStatement(
18+
[ContractAddress.create(2101), ContractAddress.create(1337, 42)].map(
19+
(c) => new ContractInstanceDID('Testnet', c)
20+
),
21+
(b) => b.addRange('b', 80n, 1237n).addMembership('c', ['aa', 'ff', 'zz'])
22+
)
1723
.addIdentityStatement(
1824
[0, 1, 2].map((i) => new IdentityProviderDID('Testnet', i)),
1925
(b) => b.revealAttribute(AttributeKeyString.firstName)
@@ -34,6 +40,12 @@ describe('VerifiablePresentationRequestV1', () => {
3440
'Wine payment'
3541
);
3642
const statement = VerifiablePresentationRequestV1.statementBuilder()
43+
.addWeb3IdStatement(
44+
[ContractAddress.create(2101), ContractAddress.create(1337, 42)].map(
45+
(c) => new ContractInstanceDID('Testnet', c)
46+
),
47+
(b) => b.addRange('b', 80n, 1237n).addMembership('c', ['aa', 'ff', 'zz'])
48+
)
3749
.addIdentityStatement(
3850
[0, 1, 2].map((i) => new IdentityProviderDID('Testnet', i)),
3951
(b) => b.revealAttribute(AttributeKeyString.firstName)
@@ -43,7 +55,7 @@ describe('VerifiablePresentationRequestV1', () => {
4355
somePulicInfo: 'public info',
4456
});
4557
const expectedAnchor =
46-
'a464686173685820acd94ad63951d7c1c48655ddfcf3dbc23a6e11ff20728be1c7f5e6a8b991349d647479706566434344565241667075626c6963a16d736f6d6550756c6963496e666f6b7075626c696320696e666f6776657273696f6e01';
58+
'a4646861736858200e601a47ce5f0b34154959e39ab717d51386cda140eb4f6ef12c53aa0a33b3cf647479706566434344565241667075626c6963a16d736f6d6550756c6963496e666f6b7075626c696320696e666f6776657273696f6e01';
4759
expect(Buffer.from(anchor).toString('hex')).toEqual(expectedAnchor);
4860

4961
const roundtrip = VerifiablePresentationRequestV1.decodeAnchor(anchor);

0 commit comments

Comments
 (0)