11import { Buffer } from 'buffer/index.js' ;
2+ import _JB from 'json-bigint' ;
23
34import { sha256 } from '../../hash.js' ;
45import {
@@ -10,13 +11,17 @@ import {
1011 ConcordiumGRPCClient ,
1112 NextAccountNonce ,
1213 RegisterDataPayload ,
14+ cborDecode ,
15+ cborEncode ,
1316 signTransaction ,
1417} from '../../index.js' ;
15- import { DataBlob , TransactionExpiry , TransactionHash } from '../../types/index.js' ;
16- import { CredentialStatement } from '../../web3-id/types.js' ;
18+ import { ContractAddress , DataBlob , TransactionExpiry , TransactionHash } from '../../types/index.js' ;
19+ import { CredentialStatement , StatementProverQualifier } from '../../web3-id/types.js' ;
1720import { GivenContextJSON , givenContextFromJSON , givenContextToJSON } from './internal.js' ;
1821import { CredentialContextLabel , GivenContext } from './types.js' ;
1922
23+ const JSONBig = _JB ( { useNativeBigInt : true , alwaysParseAsBig : true } ) ;
24+
2025// NOTE: renamed from ContextInformation in ADR
2126export type Context = {
2227 type : 'ConcordiumContextInformationV1' ;
@@ -39,12 +44,43 @@ export function createSimpleContext(nonce: Uint8Array, connectionId: string, con
3944 } ) ;
4045}
4146
42- export function computeAnchor ( context : Context , credentialStatements : CredentialStatement [ ] ) : Uint8Array {
47+ export type AnchorData = {
48+ type : 'CCDVRA' ;
49+ version : number ;
50+ hash : Uint8Array ;
51+ public ?: string ;
52+ } ;
53+
54+ export function createAnchor (
55+ context : Context ,
56+ credentialStatements : CredentialStatement [ ] ,
57+ publicInfo ?: string
58+ ) : Uint8Array {
59+ const hash = computeAnchorHash ( context , credentialStatements ) ;
60+ const data : AnchorData = { type : 'CCDVRA' , version : 1 , hash, public : publicInfo } ;
61+ return cborEncode ( data ) ;
62+ }
63+
64+ export function computeAnchorHash ( context : Context , credentialStatements : CredentialStatement [ ] ) : Uint8Array {
4365 // TODO: this is a quick and dirty anchor implementation that needs to be replaced with
4466 // the one from concordium-base when available.
4567 const contextDigest = Buffer . from ( JSON . stringify ( context ) ) ;
46- const statementsDigest = Buffer . from ( JSON . stringify ( credentialStatements ) ) ;
47- return sha256 ( [ contextDigest , statementsDigest ] ) ;
68+ const statementsDigest = Buffer . from ( JSONBig . stringify ( credentialStatements ) ) ;
69+ return Uint8Array . from ( sha256 ( [ contextDigest , statementsDigest ] ) ) ;
70+ }
71+
72+ export function decodeAnchor ( cbor : Uint8Array ) : AnchorData {
73+ const value = cborDecode ( cbor ) ;
74+ if ( typeof value !== 'object' || value === null ) throw new Error ( 'Expected a cbor encoded object' ) ;
75+ // required fields
76+ if ( ! ( 'type' in value ) || value . type !== 'CCDVRA' ) throw new Error ( 'Expected "type" to be "CCDVRA"' ) ;
77+ if ( ! ( 'version' in value ) || typeof value . version !== 'number' )
78+ throw new Error ( 'Expected "version" to be a number' ) ;
79+ if ( ! ( 'hash' in value ) || ! ( value . hash instanceof Uint8Array ) )
80+ throw new Error ( 'Expected "hash" to be a Uint8Array' ) ;
81+ // optional fields
82+ if ( 'public' in value && typeof value . public !== 'string' ) throw new Error ( 'Expected "public" to be a string' ) ;
83+ return value as AnchorData ;
4884}
4985
5086// TODO: Should match the w3c spec for a verifiable presentation request and the corresponding
@@ -77,9 +113,30 @@ export type Type = VerifiablePresentationRequestV1;
77113
78114export function fromJSON ( json : JSON ) : VerifiablePresentationRequestV1 {
79115 const requestContext = { ...json . requestContext , given : json . requestContext . given . map ( givenContextFromJSON ) } ;
116+ const statements : CredentialStatement [ ] = json . credentialStatements . map ( ( { statement, idQualifier } ) => {
117+ let mappedQualifier : StatementProverQualifier ;
118+ switch ( idQualifier . type ) {
119+ case 'id' :
120+ mappedQualifier = { type : 'id' , issuers : idQualifier . issuers . map ( Number ) } ;
121+ break ;
122+ case 'cred' :
123+ mappedQualifier = { type : 'cred' , issuers : idQualifier . issuers . map ( Number ) } ;
124+ break ;
125+ case 'sci' :
126+ mappedQualifier = {
127+ type : 'sci' ,
128+ issuers : idQualifier . issuers . map ( ( c ) => ContractAddress . create ( c . index , c . subindex ) ) ,
129+ } ;
130+ break ;
131+ default :
132+ mappedQualifier = idQualifier ;
133+ }
134+ return { statement, idQualifier : mappedQualifier } as CredentialStatement ;
135+ } ) ;
136+
80137 return new VerifiablePresentationRequestV1 (
81138 requestContext ,
82- json . credentialStatements ,
139+ statements ,
83140 TransactionHash . fromJSON ( json . transactionRef )
84141 ) ;
85142}
@@ -89,10 +146,11 @@ export async function createAndAchor(
89146 sender : AccountAddress . Type ,
90147 signer : AccountSigner ,
91148 context : Omit < Context , 'type' > ,
92- credentialStatements : CredentialStatement [ ]
149+ credentialStatements : CredentialStatement [ ] ,
150+ anchorPublicInfo ?: string
93151) : Promise < VerifiablePresentationRequestV1 > {
94152 const requestContext = createContext ( context ) ;
95- const anchor = computeAnchor ( requestContext , credentialStatements ) ;
153+ const anchor = createAnchor ( requestContext , credentialStatements , anchorPublicInfo ) ;
96154
97155 const nextNonce : NextAccountNonce = await grpc . getNextAccountNonce ( sender ) ;
98156 const header : AccountTransactionHeader = {
0 commit comments