-
Notifications
You must be signed in to change notification settings - Fork 17
/
helpers.ts
114 lines (100 loc) · 3.42 KB
/
helpers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { CryptographicParameters } from '../types.js';
import type * as ContractAddress from '../types/ContractAddress.js';
import { bail } from '../util.js';
import {
AttributeType,
StatementAttributeType,
TimestampAttribute,
} from './types.js';
export type VerifyWeb3IdCredentialSignatureInput = {
globalContext: CryptographicParameters;
signature: string;
values: Record<string, AttributeType>;
randomness: Record<string, string>;
holder: string;
issuerPublicKey: string;
issuerContract: ContractAddress.Type;
};
/**
* Compares a and b as field elements.
* if a < b then compareStringAttributes(a,b) = -1;
* if a == b then compareStringAttributes(a,b) = 0;
* if a > b then compareStringAttributes(a,b) = 1;
*/
export function compareStringAttributes(a: string, b: string): number {
const encoder = new TextEncoder();
const aBytes = encoder.encode(a);
const bBytes = encoder.encode(b);
if (aBytes.length < bBytes.length) return -1;
if (aBytes.length > bBytes.length) return 1;
for (const [i, aByte] of aBytes.entries()) {
const bByte = bBytes[i];
if (aByte === bBytes[i]) continue;
return aByte < bByte ? -1 : 1;
}
return 0;
}
/**
* Given a string attribute value and a range [lower, upper[, return whether value is in the range, when converted into field elements.
*/
export function isStringAttributeInRange(
value: string,
lower: string,
upper: string
): boolean {
const lowCmp = compareStringAttributes(value, lower);
if (lowCmp < 0) {
return false;
}
const upCmp = compareStringAttributes(value, upper);
return upCmp < 0;
}
/**
* Converts a timestamp attribute to a Date.
* @param attribute the timestamp attribute
* @returns a Date representing the timestamp
*/
export function timestampToDate(attribute: TimestampAttribute): Date {
return new Date(Date.parse(attribute.timestamp));
}
/**
* Converts a Date to a timestamp attribute.
* @param value the date to convert to an attribute
* @returns the timestamp attribute for the provided date
*/
export function dateToTimestampAttribute(value: Date): TimestampAttribute {
return {
type: 'date-time',
timestamp: value.toISOString(),
};
}
/**
* Converts a statement attribute to an attribute. Statement attributes allow
* for Date which are mapped into timestamp attributes. All other attribute
* types are mapped one-to-one.
* @param statementAttribute the statement attribute to map
* @returns the mapped attribute type
*/
export function statementAttributeTypeToAttributeType(
statementAttribute: StatementAttributeType
): AttributeType {
if (statementAttribute instanceof Date) {
return dateToTimestampAttribute(statementAttribute);
}
return statementAttribute;
}
/**
* Parses a {@linkcode Date} from a string containing a year and month in the form "YYYYMM".
*
* @param yearMonth - The string to parse
* @returns the parsed {@linkcode Date}
* @throws if the date cannot be parsed
*/
export function parseYearMonth(yearMonth: string): Date {
const b = () => bail('Failed to parse date from year-month string');
const [, y, m] = yearMonth.match(/^(\d{4})(\d{2})$/) ?? b();
const year = Number(y);
const month = Number(m) - 1; // `Date` month starts from 0, we expect january to be defined as '01'
if (Number.isNaN(year) || Number.isNaN(month) || month > 11) b();
return new Date(year, month);
}