Skip to content

Commit 00840b4

Browse files
committed
Fix audit anchor
1 parent e7325f1 commit 00840b4

File tree

6 files changed

+173
-22
lines changed

6 files changed

+173
-22
lines changed

packages/sdk/src/wasm/VerifiablePresentationV1/audit/private.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Buffer } from 'buffer/index.js';
2+
import _JB from 'json-bigint';
23

34
import { ConcordiumGRPCClient } from '../../../grpc/index.js';
45
import { sha256 } from '../../../hash.js';
@@ -13,6 +14,8 @@ import {
1314
import { AccountAddress, DataBlob, TransactionExpiry, TransactionHash } from '../../../types/index.js';
1415
import { VerifiablePresentationRequestV1, VerifiablePresentationV1, VerificationAuditRecord } from '../index.js';
1516

17+
const JSONBig = _JB({ alwaysParseAsBig: true, useNativeBigInt: true });
18+
1619
class PrivateVerificationAuditRecord {
1720
public readonly type = 'ConcordiumVerificationAuditRecord';
1821
public readonly version = 1;
@@ -52,8 +55,8 @@ export function fromJSON(json: JSON): PrivateVerificationAuditRecord {
5255
}
5356

5457
export function toPublic(record: PrivateVerificationAuditRecord, info?: string): VerificationAuditRecord.Type {
55-
const message = Buffer.from(JSON.stringify(record)); // TODO: replace this with proper hashing.. properly from @concordium/rust-bindings
56-
const hash = sha256([message]);
58+
const message = Buffer.from(JSONBig.stringify(record)); // TODO: replace this with proper hashing.. properly from @concordium/rust-bindings
59+
const hash = Uint8Array.from(sha256([message]));
5760
return VerificationAuditRecord.create(hash, info);
5861
}
5962

@@ -72,8 +75,7 @@ export async function registerPublicRecord(
7275
};
7376

7477
const publicRecord = toPublic(privateRecord, info);
75-
const data = new DataBlob(publicRecord.toBytes());
76-
const payload: RegisterDataPayload = { data };
78+
const payload: RegisterDataPayload = { data: new DataBlob(VerificationAuditRecord.createAnchor(publicRecord)) };
7779
const accountTransaction: AccountTransaction = {
7880
header: header,
7981
payload,

packages/sdk/src/wasm/VerifiablePresentationV1/audit/public.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { HexString, cborEncode } from '../../../index.js';
1+
import { Buffer } from 'buffer/index.js';
2+
3+
import { HexString } from '../../../types.js';
4+
import { cborDecode, cborEncode } from '../../../types/cbor.js';
25

36
class VerificationAuditRecord {
47
constructor(
@@ -11,10 +14,6 @@ class VerificationAuditRecord {
1114
if (this.info !== undefined) json.info = this.info;
1215
return json;
1316
}
14-
15-
public toBytes(): Uint8Array {
16-
return cborEncode(this.toJSON());
17-
}
1817
}
1918

2019
export type Type = VerificationAuditRecord;
@@ -25,9 +24,41 @@ export type JSON = {
2524
};
2625

2726
export function fromJSON(json: JSON): VerificationAuditRecord {
28-
return new VerificationAuditRecord(Buffer.from(json.hash, 'hex'), json.info);
27+
return new VerificationAuditRecord(Uint8Array.from(Buffer.from(json.hash, 'hex')), json.info);
2928
}
3029

3130
export function create(hash: Uint8Array, info?: string): VerificationAuditRecord {
3231
return new VerificationAuditRecord(hash, info);
3332
}
33+
34+
export type AnchorData = {
35+
type: 'CCDVAA';
36+
version: number;
37+
hash: Uint8Array;
38+
public?: string;
39+
};
40+
41+
export function createAnchor(value: VerificationAuditRecord, publicInfo?: string): Uint8Array {
42+
const data: AnchorData = {
43+
type: 'CCDVAA',
44+
version: 1,
45+
hash: value.hash,
46+
public: publicInfo,
47+
};
48+
return cborEncode(data);
49+
}
50+
51+
export function decodeAnchor(cbor: Uint8Array): AnchorData {
52+
const value = cborDecode(cbor);
53+
console.log(value);
54+
if (typeof value !== 'object' || value === null) throw new Error('Expected a cbor encoded object');
55+
// required fields
56+
if (!('type' in value) || value.type !== 'CCDVAA') throw new Error('Expected "type" to be "CCDVAA"');
57+
if (!('version' in value) || typeof value.version !== 'number')
58+
throw new Error('Expected "version" to be a number');
59+
if (!('hash' in value) || !(value.hash instanceof Uint8Array))
60+
throw new Error('Expected "hash" to be a Uint8Array');
61+
// optional fields
62+
if ('public' in value && typeof value.public !== 'string') throw new Error('Expected "public" to be a string');
63+
return value as AnchorData;
64+
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
AttributeKey,
88
ConcordiumGRPCClient,
99
CryptographicParameters,
10-
DataBlob,
1110
HexString,
1211
Network,
1312
TransactionKindString,

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

Lines changed: 1 addition & 11 deletions
Large diffs are not rendered by default.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import _JB from 'json-bigint';
2+
3+
import {
4+
PrivateVerificationAuditRecord,
5+
VerifiablePresentationRequestV1,
6+
VerifiablePresentationV1,
7+
VerificationAuditRecord,
8+
} from '../../../src/index.ts';
9+
10+
const JSONBig = _JB({ alwaysParseAsBig: true, useNativeBigInt: true });
11+
12+
const PRESENTATION_REQUEST = VerifiablePresentationRequestV1.fromJSON({
13+
requestContext: {
14+
type: 'ConcordiumContextInformationV1',
15+
given: [
16+
{ label: 'Nonce', context: '00010203' },
17+
{ label: 'ConnectionID', context: '0102010201020102010201020102010201020102010201020102010201020102' },
18+
{ label: 'ContextString', context: 'Wine payment' },
19+
],
20+
requested: ['BlockHash', 'ResourceID'],
21+
},
22+
credentialStatements: [
23+
{
24+
idQualifier: {
25+
type: 'sci',
26+
issuers: [
27+
{ index: 2101n, subindex: 0n },
28+
{ index: 1337n, subindex: 42n },
29+
] as any,
30+
},
31+
statement: [
32+
{ type: 'AttributeInRange', attributeTag: 'b', lower: 80n, upper: 1237n } as any,
33+
{ type: 'AttributeInSet', attributeTag: 'c', set: ['aa', 'ff', 'zz'] },
34+
],
35+
},
36+
{
37+
idQualifier: { type: 'id', issuers: [0, 1, 2] },
38+
statement: [{ type: 'RevealAttribute', attributeTag: 'firstName' }],
39+
},
40+
],
41+
transactionRef: '0102030401020304010203040102030401020304010203040102030401020304',
42+
});
43+
44+
const PRESENTATION = VerifiablePresentationV1.fromJSON({
45+
presentationContext: {
46+
type: 'ConcordiumContextInformationV1',
47+
given: [
48+
{ label: 'Nonce', context: '00010203' },
49+
{ label: 'ConnectionID', context: '0102010201020102010201020102010201020102010201020102010201020102' },
50+
{ label: 'ContextString', context: 'Wine payment' },
51+
],
52+
requested: [
53+
{ label: 'BlockHash', context: '0101010101010101010101010101010101010101010101010101010101010101' },
54+
{ label: 'ResourceID', context: 'https://compliant.shop' },
55+
],
56+
},
57+
verifiableCredential: [
58+
{
59+
type: ['VerifiableCredential', 'ConcordiumVerifiableCredentialV1', 'ConcordiumIDBasedCredential'],
60+
proof: {
61+
type: 'ConcordiumZKProofV4',
62+
createdAt: '2025-10-17T13:14:14.292Z',
63+
proofValue:
64+
'01020102010201020102010201020102010201020102010201020102010201020102010201020102010201020102010201020102010201020102010201020102',
65+
},
66+
issuer: 'ccd:testnet:idp:0',
67+
credentialSubject: {
68+
statement: [
69+
{ attributeTag: 'dob', lower: '81', type: 'AttributeInRange', upper: '1231' },
70+
{ attributeTag: 'firstName', type: 'RevealAttribute' },
71+
],
72+
id: '123456123456123456123456123456123456123456123456',
73+
},
74+
},
75+
],
76+
proof: { created: '2025-10-17T13:14:14.290Z', proofValue: [], type: 'ConcordiumWeakLinkingProofV1' },
77+
});
78+
79+
const PRIVATE_RECORD = PrivateVerificationAuditRecord.create('VERY unique ID', PRESENTATION_REQUEST, PRESENTATION);
80+
const PUBLIC_RECORD = PrivateVerificationAuditRecord.toPublic(PRIVATE_RECORD, 'some public info?');
81+
82+
describe('PrivateVerificationAuditRecord', () => {
83+
it('completes JSON roundtrip', () => {
84+
const json = JSONBig.stringify(PRIVATE_RECORD);
85+
const roundtrip = PrivateVerificationAuditRecord.fromJSON(JSONBig.parse(json));
86+
expect(roundtrip).toEqual(PRIVATE_RECORD);
87+
});
88+
89+
it('creates expected public record', () => {
90+
const publicAuditRecord = PrivateVerificationAuditRecord.toPublic(PRIVATE_RECORD, 'some public info?');
91+
const expected: VerificationAuditRecord.Type = VerificationAuditRecord.fromJSON({
92+
hash: 'fcce3a7222e09bc86f0b4e0186501ff360c5a0abce88b8d1df2aaf7aa3ef8d78',
93+
info: 'some public info?',
94+
});
95+
expect(publicAuditRecord).toEqual(expected);
96+
});
97+
});
98+
99+
describe('VerificationAuditRecord', () => {
100+
it('completes JSON roundtrip', () => {
101+
const json = JSONBig.stringify(PUBLIC_RECORD);
102+
const roundtrip = VerificationAuditRecord.fromJSON(JSONBig.parse(json));
103+
expect(roundtrip).toEqual(PUBLIC_RECORD);
104+
});
105+
106+
it('computes the anchor as expected', () => {
107+
const anchor = VerificationAuditRecord.createAnchor(PUBLIC_RECORD, 'anchor info');
108+
const decoded = VerificationAuditRecord.decodeAnchor(anchor);
109+
const expected: VerificationAuditRecord.AnchorData = {
110+
type: 'CCDVAA',
111+
version: 1,
112+
hash: PUBLIC_RECORD.hash,
113+
public: 'anchor info',
114+
};
115+
expect(decoded).toEqual(expected);
116+
});
117+
});

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

Lines changed: 12 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)