@datumesh/verify 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +56 -2
- package/dist/cjs/ed25519.d.ts +2 -0
- package/dist/cjs/ed25519.js +82 -0
- package/dist/cjs/envelope.d.ts +38 -0
- package/dist/cjs/envelope.js +243 -0
- package/dist/cjs/errors.d.ts +14 -0
- package/dist/cjs/errors.js +65 -0
- package/dist/cjs/hash.d.ts +6 -0
- package/dist/cjs/hash.js +27 -0
- package/dist/cjs/index.d.ts +37 -0
- package/dist/cjs/index.js +101 -0
- package/dist/cjs/jcs.d.ts +5 -0
- package/dist/cjs/jcs.js +25 -0
- package/dist/cjs/json.d.ts +29 -0
- package/dist/cjs/json.js +325 -0
- package/dist/cjs/keys.d.ts +73 -0
- package/dist/cjs/keys.js +257 -0
- package/dist/cjs/lexical.d.ts +69 -0
- package/dist/cjs/lexical.js +231 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/unicode-data.d.ts +8 -0
- package/dist/cjs/unicode-data.js +14 -0
- package/dist/cjs/unicode.d.ts +12 -0
- package/dist/cjs/unicode.js +192 -0
- package/dist/cjs/units-data.d.ts +4 -0
- package/dist/cjs/units-data.js +11 -0
- package/dist/cjs/units.d.ts +24 -0
- package/dist/cjs/units.js +131 -0
- package/dist/cjs/uri.d.ts +43 -0
- package/dist/cjs/uri.js +209 -0
- package/dist/cjs/utf8.d.ts +10 -0
- package/dist/cjs/utf8.js +101 -0
- package/dist/cjs/validate.d.ts +25 -0
- package/dist/cjs/validate.js +408 -0
- package/dist/cjs/verify.d.ts +57 -0
- package/dist/cjs/verify.js +232 -0
- package/dist/esm/ed25519.d.ts +2 -0
- package/dist/esm/ed25519.js +79 -0
- package/dist/esm/envelope.d.ts +38 -0
- package/dist/esm/envelope.js +240 -0
- package/dist/esm/errors.d.ts +14 -0
- package/dist/esm/errors.js +60 -0
- package/dist/esm/hash.d.ts +6 -0
- package/dist/esm/hash.js +21 -0
- package/dist/esm/index.d.ts +37 -0
- package/dist/esm/index.js +79 -0
- package/dist/esm/jcs.d.ts +5 -0
- package/dist/esm/jcs.js +18 -0
- package/dist/esm/json.d.ts +29 -0
- package/dist/esm/json.js +313 -0
- package/dist/esm/keys.d.ts +73 -0
- package/dist/esm/keys.js +249 -0
- package/dist/esm/lexical.d.ts +69 -0
- package/dist/esm/lexical.js +209 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/unicode-data.d.ts +8 -0
- package/dist/esm/unicode-data.js +11 -0
- package/dist/esm/unicode.d.ts +12 -0
- package/dist/esm/unicode.js +184 -0
- package/dist/esm/units-data.d.ts +4 -0
- package/dist/esm/units-data.js +8 -0
- package/dist/esm/units.d.ts +24 -0
- package/dist/esm/units.js +123 -0
- package/dist/esm/uri.d.ts +43 -0
- package/dist/esm/uri.js +204 -0
- package/dist/esm/utf8.d.ts +10 -0
- package/dist/esm/utf8.js +96 -0
- package/dist/esm/validate.d.ts +25 -0
- package/dist/esm/validate.js +397 -0
- package/dist/esm/verify.d.ts +57 -0
- package/dist/esm/verify.js +227 -0
- package/package.json +54 -4
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KINDS = void 0;
|
|
4
|
+
exports.structSignature = structSignature;
|
|
5
|
+
exports.canonSignature = canonSignature;
|
|
6
|
+
exports.returnType = returnType;
|
|
7
|
+
exports.validate = validate;
|
|
8
|
+
exports.cellUnit = cellUnit;
|
|
9
|
+
exports.receiptStructure = receiptStructure;
|
|
10
|
+
exports.receiptCanonicalAndConsistent = receiptCanonicalAndConsistent;
|
|
11
|
+
exports.inferKind = inferKind;
|
|
12
|
+
// Structure (SPEC 10.1 step 2, E_SCHEMA), canonical form (step 3) and receipt consistency
|
|
13
|
+
// (step 4) for every kind of Datumesh object. Within a step members are examined in JCS order,
|
|
14
|
+
// depth first; at each object its member set is checked before its members.
|
|
15
|
+
const errors_js_1 = require("./errors.js");
|
|
16
|
+
const json_js_1 = require("./json.js");
|
|
17
|
+
const lexical_js_1 = require("./lexical.js");
|
|
18
|
+
const units_js_1 = require("./units.js");
|
|
19
|
+
exports.KINDS = ['scalar', 'cell', 'ref', 'function', 'receipt', 'jcs'];
|
|
20
|
+
const SLICE = '*';
|
|
21
|
+
// ---------------------------------------------------------------- step 2: structure
|
|
22
|
+
function expectObject(v, path) {
|
|
23
|
+
if (!(0, json_js_1.isObject)(v))
|
|
24
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'expected an object', path);
|
|
25
|
+
return v;
|
|
26
|
+
}
|
|
27
|
+
function members(o, required, optional, path) {
|
|
28
|
+
const names = new Set([...Object.keys(o), ...required]);
|
|
29
|
+
for (const n of [...names].sort()) {
|
|
30
|
+
if (!(0, json_js_1.has)(o, n))
|
|
31
|
+
(0, errors_js_1.fail)('E_SCHEMA', `missing member ${JSON.stringify(n)}`, path);
|
|
32
|
+
if (!required.includes(n) && !optional.includes(n))
|
|
33
|
+
(0, errors_js_1.fail)('E_SCHEMA', `unexpected member ${JSON.stringify(n)}`, path);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function expectString(o, k, path) {
|
|
37
|
+
if (typeof o[k] !== 'string')
|
|
38
|
+
(0, errors_js_1.fail)('E_SCHEMA', `${k} must be a string`, (0, json_js_1.memberPath)(path, k));
|
|
39
|
+
}
|
|
40
|
+
function structScalar(v, path) {
|
|
41
|
+
const o = expectObject(v, path);
|
|
42
|
+
members(o, ['type', 'value'], [], path);
|
|
43
|
+
expectString(o, 'type', path);
|
|
44
|
+
if (typeof o.value !== 'string' && typeof o.value !== 'boolean') {
|
|
45
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'value must be a string or a boolean', (0, json_js_1.memberPath)(path, 'value'));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function structArgs(v, path) {
|
|
49
|
+
const o = expectObject(v, path);
|
|
50
|
+
for (const k of (0, json_js_1.sortedKeys)(o)) {
|
|
51
|
+
if (o[k] === SLICE)
|
|
52
|
+
continue;
|
|
53
|
+
structScalar(o[k], (0, json_js_1.memberPath)(path, k));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function structCell(v, path, ref) {
|
|
57
|
+
const o = expectObject(v, path);
|
|
58
|
+
if (ref)
|
|
59
|
+
members(o, ['args', 'fn', 'ns'], ['asof'], path);
|
|
60
|
+
else
|
|
61
|
+
members(o, ['args', 'fn', 'fn_hash', 'ns'], [], path);
|
|
62
|
+
for (const k of (0, json_js_1.sortedKeys)(o)) {
|
|
63
|
+
if (k === 'args')
|
|
64
|
+
structArgs(o.args, (0, json_js_1.memberPath)(path, 'args'));
|
|
65
|
+
else
|
|
66
|
+
expectString(o, k, path);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function structFunction(v, path) {
|
|
70
|
+
const o = expectObject(v, path);
|
|
71
|
+
members(o, ['code', 'inputs', 'signature'], [], path);
|
|
72
|
+
const codePath = (0, json_js_1.memberPath)(path, 'code');
|
|
73
|
+
const code = expectObject(o.code, codePath);
|
|
74
|
+
members(code, ['body', 'lang'], [], codePath);
|
|
75
|
+
expectString(code, 'body', codePath);
|
|
76
|
+
expectString(code, 'lang', codePath);
|
|
77
|
+
const inputs = o.inputs;
|
|
78
|
+
if (!Array.isArray(inputs) || inputs.some((x) => typeof x !== 'string')) {
|
|
79
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'inputs must be an array of strings', (0, json_js_1.memberPath)(path, 'inputs'));
|
|
80
|
+
}
|
|
81
|
+
structSignature(o.signature, (0, json_js_1.memberPath)(path, 'signature'));
|
|
82
|
+
}
|
|
83
|
+
function structSignature(v, path) {
|
|
84
|
+
const sig = expectObject(v, path);
|
|
85
|
+
members(sig, ['args', 'returns'], [], path);
|
|
86
|
+
const argsPath = (0, json_js_1.memberPath)(path, 'args');
|
|
87
|
+
const args = expectObject(sig.args, argsPath);
|
|
88
|
+
for (const k of (0, json_js_1.sortedKeys)(args)) {
|
|
89
|
+
const p = (0, json_js_1.memberPath)(argsPath, k);
|
|
90
|
+
const d = expectObject(args[k], p);
|
|
91
|
+
members(d, ['type', 'valid'], ['unit'], p);
|
|
92
|
+
expectString(d, 'type', p);
|
|
93
|
+
if ((0, json_js_1.has)(d, 'unit'))
|
|
94
|
+
(0, units_js_1.structUnit)(d.unit, (0, json_js_1.memberPath)(p, 'unit'));
|
|
95
|
+
if (typeof d.valid !== 'boolean')
|
|
96
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'valid must be a boolean', (0, json_js_1.memberPath)(p, 'valid'));
|
|
97
|
+
}
|
|
98
|
+
// SPEC 7: a type name, or {"type", "unit"} (the unit's absence is E_CANON_UNIT, in step 3).
|
|
99
|
+
const rp = (0, json_js_1.memberPath)(path, 'returns');
|
|
100
|
+
const r = sig.returns;
|
|
101
|
+
if (typeof r === 'string')
|
|
102
|
+
return;
|
|
103
|
+
if (!(0, json_js_1.isObject)(r))
|
|
104
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'returns must be a type name or an object', rp);
|
|
105
|
+
members(r, ['type'], ['unit'], rp);
|
|
106
|
+
expectString(r, 'type', rp);
|
|
107
|
+
if ((0, json_js_1.has)(r, 'unit'))
|
|
108
|
+
(0, units_js_1.structUnit)(r.unit, (0, json_js_1.memberPath)(rp, 'unit'));
|
|
109
|
+
}
|
|
110
|
+
const RECEIPT_MEMBERS = [
|
|
111
|
+
'asof',
|
|
112
|
+
'cell',
|
|
113
|
+
'computed_by',
|
|
114
|
+
'inputs',
|
|
115
|
+
'issued',
|
|
116
|
+
'licence',
|
|
117
|
+
'proto',
|
|
118
|
+
'publisher',
|
|
119
|
+
'sig',
|
|
120
|
+
'summary',
|
|
121
|
+
'tx',
|
|
122
|
+
'type',
|
|
123
|
+
'valid',
|
|
124
|
+
'value',
|
|
125
|
+
];
|
|
126
|
+
function structReceipt(v, path) {
|
|
127
|
+
const o = expectObject(v, path);
|
|
128
|
+
members(o, RECEIPT_MEMBERS, [], path);
|
|
129
|
+
for (const k of (0, json_js_1.sortedKeys)(o)) {
|
|
130
|
+
const p = (0, json_js_1.memberPath)(path, k);
|
|
131
|
+
switch (k) {
|
|
132
|
+
case 'cell':
|
|
133
|
+
structCell(o.cell, p, false);
|
|
134
|
+
break;
|
|
135
|
+
case 'inputs': {
|
|
136
|
+
const a = o.inputs;
|
|
137
|
+
if (!Array.isArray(a) || a.some((x) => typeof x !== 'string'))
|
|
138
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'inputs must be an array of strings', p);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'valid':
|
|
142
|
+
structArgs(o.valid, p);
|
|
143
|
+
break;
|
|
144
|
+
case 'value':
|
|
145
|
+
if (typeof o.value !== 'string' && typeof o.value !== 'boolean')
|
|
146
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'value must be a string or a boolean', p);
|
|
147
|
+
break;
|
|
148
|
+
case 'proto':
|
|
149
|
+
expectString(o, k, path);
|
|
150
|
+
if (o.proto !== 'datumesh/1')
|
|
151
|
+
(0, errors_js_1.fail)('E_SCHEMA', "proto must be 'datumesh/1'", p);
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
expectString(o, k, path);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------- step 3: canonical form
|
|
159
|
+
function canonScalar(o, path) {
|
|
160
|
+
const t = o.type;
|
|
161
|
+
if (!(0, lexical_js_1.isTypeName)(t))
|
|
162
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', `unknown type ${JSON.stringify(t)}`, (0, json_js_1.memberPath)(path, 'type'));
|
|
163
|
+
(0, lexical_js_1.checkTypedValue)(t, o.value, (0, json_js_1.memberPath)(path, 'value'));
|
|
164
|
+
}
|
|
165
|
+
function canonArgs(o, path) {
|
|
166
|
+
if (Object.keys(o).length > lexical_js_1.LIMITS.args)
|
|
167
|
+
(0, errors_js_1.fail)('E_CANON_LIMIT', `more than ${lexical_js_1.LIMITS.args} arguments`, path);
|
|
168
|
+
for (const k of (0, json_js_1.sortedKeys)(o)) {
|
|
169
|
+
const p = (0, json_js_1.memberPath)(path, k);
|
|
170
|
+
(0, lexical_js_1.checkArgName)(k, p);
|
|
171
|
+
if (o[k] !== SLICE)
|
|
172
|
+
canonScalar(o[k], p);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function canonCell(o, path) {
|
|
176
|
+
for (const k of (0, json_js_1.sortedKeys)(o)) {
|
|
177
|
+
const p = (0, json_js_1.memberPath)(path, k);
|
|
178
|
+
const v = o[k];
|
|
179
|
+
if (k === 'args')
|
|
180
|
+
canonArgs(o.args, p);
|
|
181
|
+
else if (k === 'asof')
|
|
182
|
+
(0, lexical_js_1.checkInstant)(v, p);
|
|
183
|
+
else if (k === 'fn')
|
|
184
|
+
(0, lexical_js_1.checkName)(v, p);
|
|
185
|
+
else if (k === 'fn_hash')
|
|
186
|
+
(0, lexical_js_1.checkHex)(v, 64, p);
|
|
187
|
+
else if (k === 'ns')
|
|
188
|
+
(0, lexical_js_1.checkNamespace)(v, p);
|
|
189
|
+
}
|
|
190
|
+
if (o.ns.length + 1 + o.fn.length > lexical_js_1.LIMITS.function_name) {
|
|
191
|
+
(0, errors_js_1.fail)('E_CANON_LIMIT', 'function name too long', path);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function canonStringSet(a, path, each) {
|
|
195
|
+
a.forEach((s, i) => each(s, `${path}[${i}]`));
|
|
196
|
+
for (let i = 1; i < a.length; i++) {
|
|
197
|
+
if (!(a[i - 1] < a[i]))
|
|
198
|
+
(0, errors_js_1.fail)('E_CANON_SET', 'entries must be strictly ascending', path);
|
|
199
|
+
}
|
|
200
|
+
if (a.length > lexical_js_1.LIMITS.inputs)
|
|
201
|
+
(0, errors_js_1.fail)('E_CANON_LIMIT', `more than ${lexical_js_1.LIMITS.inputs} inputs`, path);
|
|
202
|
+
}
|
|
203
|
+
function canonSignature(sig, path) {
|
|
204
|
+
const argsPath = (0, json_js_1.memberPath)(path, 'args');
|
|
205
|
+
const args = sig.args;
|
|
206
|
+
if (Object.keys(args).length > lexical_js_1.LIMITS.args)
|
|
207
|
+
(0, errors_js_1.fail)('E_CANON_LIMIT', `more than ${lexical_js_1.LIMITS.args} arguments`, argsPath);
|
|
208
|
+
for (const k of (0, json_js_1.sortedKeys)(args)) {
|
|
209
|
+
const p = (0, json_js_1.memberPath)(argsPath, k);
|
|
210
|
+
(0, lexical_js_1.checkArgName)(k, p);
|
|
211
|
+
const d = args[k];
|
|
212
|
+
// SPEC 7: type, then unit, then valid.
|
|
213
|
+
if (!(0, lexical_js_1.isTypeName)(d.type))
|
|
214
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', `unknown type ${JSON.stringify(d.type)}`, (0, json_js_1.memberPath)(p, 'type'));
|
|
215
|
+
if (d.type === 'number') {
|
|
216
|
+
if (!(0, json_js_1.has)(d, 'unit'))
|
|
217
|
+
(0, errors_js_1.fail)('E_CANON_UNIT', 'a number argument declares its unit', p);
|
|
218
|
+
(0, units_js_1.canonUnit)(d.unit, args, (0, json_js_1.memberPath)(p, 'unit'));
|
|
219
|
+
}
|
|
220
|
+
else if ((0, json_js_1.has)(d, 'unit')) {
|
|
221
|
+
(0, errors_js_1.fail)('E_CANON_UNIT', 'only number arguments have a unit', (0, json_js_1.memberPath)(p, 'unit'));
|
|
222
|
+
}
|
|
223
|
+
if (d.valid === true && !lexical_js_1.VALID_TIME_TYPES.includes(d.type)) {
|
|
224
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', 'a valid-time argument must be a date, instant or string', p);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const rp = (0, json_js_1.memberPath)(path, 'returns');
|
|
228
|
+
const r = sig.returns;
|
|
229
|
+
if (typeof r === 'string') {
|
|
230
|
+
if (!(0, lexical_js_1.isTypeName)(r))
|
|
231
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', `unknown type ${JSON.stringify(r)}`, rp);
|
|
232
|
+
if (r === 'number')
|
|
233
|
+
(0, errors_js_1.fail)('E_CANON_UNIT', 'a number return is {"type": "number", "unit": ...}, not the bare name', rp);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const o = r;
|
|
237
|
+
if (!(0, lexical_js_1.isTypeName)(o.type))
|
|
238
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', `unknown type ${JSON.stringify(o.type)}`, (0, json_js_1.memberPath)(rp, 'type'));
|
|
239
|
+
if (o.type !== 'number')
|
|
240
|
+
(0, errors_js_1.fail)('E_CANON_UNIT', 'only a number return is written as an object', rp);
|
|
241
|
+
if (!(0, json_js_1.has)(o, 'unit'))
|
|
242
|
+
(0, errors_js_1.fail)('E_CANON_UNIT', 'a number return declares its unit', rp);
|
|
243
|
+
(0, units_js_1.canonUnit)(o.unit, args, (0, json_js_1.memberPath)(rp, 'unit'));
|
|
244
|
+
}
|
|
245
|
+
/** The return type named by a valid signature's `returns` (SPEC 10.1 step 7.4). */
|
|
246
|
+
function returnType(sig) {
|
|
247
|
+
const r = sig.returns;
|
|
248
|
+
return typeof r === 'string' ? r : r.type;
|
|
249
|
+
}
|
|
250
|
+
function canonFunction(o, path) {
|
|
251
|
+
const code = o.code;
|
|
252
|
+
const codePath = (0, json_js_1.memberPath)(path, 'code');
|
|
253
|
+
(0, lexical_js_1.checkString)(code.body, (0, json_js_1.memberPath)(codePath, 'body'), { body: true });
|
|
254
|
+
(0, lexical_js_1.checkName)(code.lang, (0, json_js_1.memberPath)(codePath, 'lang'));
|
|
255
|
+
canonStringSet(o.inputs, (0, json_js_1.memberPath)(path, 'inputs'), lexical_js_1.checkFunctionName);
|
|
256
|
+
canonSignature(o.signature, (0, json_js_1.memberPath)(path, 'signature'));
|
|
257
|
+
}
|
|
258
|
+
function canonReceipt(o, path) {
|
|
259
|
+
for (const k of (0, json_js_1.sortedKeys)(o)) {
|
|
260
|
+
const p = (0, json_js_1.memberPath)(path, k);
|
|
261
|
+
const v = o[k];
|
|
262
|
+
switch (k) {
|
|
263
|
+
case 'asof':
|
|
264
|
+
case 'issued':
|
|
265
|
+
case 'tx':
|
|
266
|
+
(0, lexical_js_1.checkInstant)(v, p);
|
|
267
|
+
break;
|
|
268
|
+
case 'cell':
|
|
269
|
+
canonCell(v, p);
|
|
270
|
+
break;
|
|
271
|
+
case 'computed_by':
|
|
272
|
+
(0, lexical_js_1.checkKeyRef)(v, p);
|
|
273
|
+
break;
|
|
274
|
+
case 'inputs':
|
|
275
|
+
canonStringSet(v, p, (s, q) => (0, lexical_js_1.checkHex)(s, 64, q));
|
|
276
|
+
break;
|
|
277
|
+
case 'licence':
|
|
278
|
+
(0, lexical_js_1.checkLicence)(v, p);
|
|
279
|
+
break;
|
|
280
|
+
case 'publisher':
|
|
281
|
+
(0, lexical_js_1.checkNamespace)(v, p);
|
|
282
|
+
break;
|
|
283
|
+
case 'sig':
|
|
284
|
+
(0, lexical_js_1.checkHex)(v, 128, p);
|
|
285
|
+
break;
|
|
286
|
+
case 'summary':
|
|
287
|
+
if (v === '')
|
|
288
|
+
(0, errors_js_1.fail)('E_CANON_STRING', 'summary must not be empty', p);
|
|
289
|
+
(0, lexical_js_1.checkString)(v, p, { limit: lexical_js_1.LIMITS.summary });
|
|
290
|
+
break;
|
|
291
|
+
case 'type':
|
|
292
|
+
if (!(0, lexical_js_1.isTypeName)(v))
|
|
293
|
+
(0, errors_js_1.fail)('E_CANON_TYPE', `unknown type ${JSON.stringify(v)}`, p);
|
|
294
|
+
break;
|
|
295
|
+
case 'valid':
|
|
296
|
+
canonArgs(v, p);
|
|
297
|
+
break;
|
|
298
|
+
case 'value':
|
|
299
|
+
(0, lexical_js_1.checkTypedValue)(o.type, v, p);
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// ---------------------------------------------------------------- step 4: consistency
|
|
305
|
+
function consistency(o, path) {
|
|
306
|
+
const cell = o.cell;
|
|
307
|
+
const args = cell.args;
|
|
308
|
+
const valid = o.valid;
|
|
309
|
+
const argsPath = (0, json_js_1.memberPath)((0, json_js_1.memberPath)(path, 'cell'), 'args');
|
|
310
|
+
const validPath = (0, json_js_1.memberPath)(path, 'valid');
|
|
311
|
+
for (const k of (0, json_js_1.sortedKeys)(args)) {
|
|
312
|
+
if (args[k] === SLICE)
|
|
313
|
+
(0, errors_js_1.fail)('E_SLICE', 'a receipt is for one cell, not a slice', (0, json_js_1.memberPath)(argsPath, k));
|
|
314
|
+
}
|
|
315
|
+
for (const k of (0, json_js_1.sortedKeys)(valid)) {
|
|
316
|
+
if (valid[k] === SLICE)
|
|
317
|
+
(0, errors_js_1.fail)('E_SLICE', 'a receipt is for one cell, not a slice', (0, json_js_1.memberPath)(validPath, k));
|
|
318
|
+
}
|
|
319
|
+
if (o.publisher !== cell.ns)
|
|
320
|
+
(0, errors_js_1.fail)('E_PUBLISHER_MISMATCH', 'publisher must equal cell.ns', (0, json_js_1.memberPath)(path, 'publisher'));
|
|
321
|
+
for (const k of (0, json_js_1.sortedKeys)(valid)) {
|
|
322
|
+
const v = valid[k];
|
|
323
|
+
const a = (0, json_js_1.has)(args, k) ? args[k] : null;
|
|
324
|
+
if (!a || a.type !== v.type || a.value !== v.value) {
|
|
325
|
+
(0, errors_js_1.fail)('E_VALID_MISMATCH', `valid.${k} does not echo cell.args.${k}`, validPath);
|
|
326
|
+
}
|
|
327
|
+
if (!lexical_js_1.VALID_TIME_TYPES.includes(v.type)) {
|
|
328
|
+
(0, errors_js_1.fail)('E_VALID_MISMATCH', 'valid-time arguments are dates, instants or strings', validPath);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
const tx = o.tx;
|
|
332
|
+
const asof = o.asof;
|
|
333
|
+
const issued = o.issued;
|
|
334
|
+
// Instants are fixed width, so string order is time order (SPEC 3.3).
|
|
335
|
+
if (!(tx <= asof && asof <= issued))
|
|
336
|
+
(0, errors_js_1.fail)('E_TIME_ORDER', 'requires tx <= asof <= issued', path);
|
|
337
|
+
}
|
|
338
|
+
// ---------------------------------------------------------------- entry points
|
|
339
|
+
/** Validate `v` as `kind` (steps 2-3; for receipts also step 4). Throws DatumeshError. */
|
|
340
|
+
function validate(v, kind, path = '$') {
|
|
341
|
+
switch (kind) {
|
|
342
|
+
case 'jcs':
|
|
343
|
+
return;
|
|
344
|
+
case 'scalar':
|
|
345
|
+
structScalar(v, path);
|
|
346
|
+
canonScalar(v, path);
|
|
347
|
+
return;
|
|
348
|
+
case 'cell':
|
|
349
|
+
case 'ref':
|
|
350
|
+
structCell(v, path, kind === 'ref');
|
|
351
|
+
canonCell(v, path);
|
|
352
|
+
return;
|
|
353
|
+
case 'function':
|
|
354
|
+
structFunction(v, path);
|
|
355
|
+
canonFunction(v, path);
|
|
356
|
+
return;
|
|
357
|
+
case 'receipt':
|
|
358
|
+
structReceipt(v, path);
|
|
359
|
+
canonReceipt(v, path);
|
|
360
|
+
consistency(v, path);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* The unit of a cell's value (SPEC 7.1, units.md 3). Order: the cell (structure, canonical
|
|
366
|
+
* form), fn_hash against the descriptor (E_FN_HASH_MISMATCH), the descriptor (structure,
|
|
367
|
+
* canonical form), then substitution.
|
|
368
|
+
*/
|
|
369
|
+
function cellUnit(desc, cell, fnHash) {
|
|
370
|
+
structCell(cell, 'cell', false);
|
|
371
|
+
canonCell(cell, 'cell');
|
|
372
|
+
if (fnHash(desc) !== cell.fn_hash)
|
|
373
|
+
(0, errors_js_1.fail)('E_FN_HASH_MISMATCH', 'the descriptor does not hash to cell.fn_hash', 'descriptor');
|
|
374
|
+
structFunction(desc, 'descriptor');
|
|
375
|
+
canonFunction(desc, 'descriptor');
|
|
376
|
+
const sig = desc.signature;
|
|
377
|
+
return (0, units_js_1.resolveUnit)(sig.returns, cell.args, 'descriptor.signature.returns', 'cell.args');
|
|
378
|
+
}
|
|
379
|
+
/** Receipt step 2 only: true when the receipt has the right members and JSON types. */
|
|
380
|
+
function receiptStructure(v) {
|
|
381
|
+
structReceipt(v, '$');
|
|
382
|
+
}
|
|
383
|
+
/** Receipt steps 3 and 4, after receiptStructure has passed. */
|
|
384
|
+
function receiptCanonicalAndConsistent(v) {
|
|
385
|
+
canonReceipt(v, '$');
|
|
386
|
+
consistency(v, '$');
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Infer the kind of an object from its members when the caller gives none: a receipt has
|
|
390
|
+
* `sig`, `proto` or `computed_by`; a descriptor `signature` or `code`; a cell `fn_hash`; a cell
|
|
391
|
+
* reference `fn`, `ns` or `args`; a typed scalar `type` or `value`. Anything else is E_SCHEMA.
|
|
392
|
+
*/
|
|
393
|
+
function inferKind(v) {
|
|
394
|
+
if (!(0, json_js_1.isObject)(v))
|
|
395
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'cannot infer the kind of a non-object; pass kind', '$');
|
|
396
|
+
const h = (k) => (0, json_js_1.has)(v, k);
|
|
397
|
+
if (h('sig') || h('proto') || h('computed_by'))
|
|
398
|
+
return 'receipt';
|
|
399
|
+
if (h('signature') || h('code'))
|
|
400
|
+
return 'function';
|
|
401
|
+
if (h('fn_hash'))
|
|
402
|
+
return 'cell';
|
|
403
|
+
if (h('fn') || h('ns') || h('args'))
|
|
404
|
+
return 'ref';
|
|
405
|
+
if (h('type') || h('value'))
|
|
406
|
+
return 'scalar';
|
|
407
|
+
(0, errors_js_1.fail)('E_SCHEMA', 'cannot infer the kind from the members; pass kind', '$');
|
|
408
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type ErrorCode } from './errors.js';
|
|
2
|
+
import { type JsonObject, type JsonValue } from './json.js';
|
|
3
|
+
import { type Sources } from './keys.js';
|
|
4
|
+
export interface VerifyResult {
|
|
5
|
+
/** True when the receipt is signature-valid (and, if recompute was given, fully verified). */
|
|
6
|
+
ok: boolean;
|
|
7
|
+
/** "OK" or a SPEC 10.2 error code. */
|
|
8
|
+
code: 'OK' | ErrorCode;
|
|
9
|
+
/** "signed" (steps 1-6) or "recomputed" (steps 1-7) when ok, else null. */
|
|
10
|
+
level: 'signed' | 'recomputed' | null;
|
|
11
|
+
/** The receipt hash, once the receipt has the structure of a receipt (step 2), else null. */
|
|
12
|
+
hash: string | null;
|
|
13
|
+
/** The `computed_by` key reference when ok, else null. */
|
|
14
|
+
key: string | null;
|
|
15
|
+
/** Human-readable explanation. */
|
|
16
|
+
detail: string;
|
|
17
|
+
/** Where the failure was found (a JSON path like `$.cell.args.base`), or null. */
|
|
18
|
+
path: string | null;
|
|
19
|
+
/** True only when blob content was supplied and step 7.7 ran and passed. Does not change `level`. */
|
|
20
|
+
blobChecked: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** Input for SPEC 10.1 step 7. The library does not evaluate functions; `value` is the result. */
|
|
23
|
+
export interface Recompute {
|
|
24
|
+
/** The function descriptor for cell.fn_hash (object or JSON text); absent: E_FN_UNAVAILABLE. */
|
|
25
|
+
descriptor?: unknown;
|
|
26
|
+
/** Input receipts by receipt hash, as JSON text (or bytes, or parsed objects). */
|
|
27
|
+
inputs?: Record<string, unknown>;
|
|
28
|
+
/** What evaluating the function produced: a typed scalar. */
|
|
29
|
+
value: {
|
|
30
|
+
type: string;
|
|
31
|
+
value: string | boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
declare class Stop extends Error {
|
|
35
|
+
readonly code: ErrorCode;
|
|
36
|
+
readonly detail: string;
|
|
37
|
+
readonly path: string | null;
|
|
38
|
+
constructor(code: ErrorCode, detail: string, path: string | null);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Verify a receipt (SPEC 10.1). `receipt` is JSON text, UTF-8 bytes or a parsed object (only
|
|
42
|
+
* text and bytes allow duplicate-member detection). `keys` is a key document, a KeysResult, a
|
|
43
|
+
* {keyId: publicKeyHex} map, or an array of these. With `recompute`, step 7 runs too. With
|
|
44
|
+
* `blob`, the content of a blob value, step 7.7 runs last (after step 7 when recomputing,
|
|
45
|
+
* otherwise right after step 6): E_SCHEMA if the value is not a blob, E_BLOB_MISMATCH if the
|
|
46
|
+
* content does not match. It does not change `level`; `blobChecked` reports it.
|
|
47
|
+
*
|
|
48
|
+
* Never throws for an invalid receipt or bad key material; throws TypeError only when an
|
|
49
|
+
* argument is of the wrong kind altogether.
|
|
50
|
+
*/
|
|
51
|
+
export declare function verify(receipt: unknown, keys: unknown, recomputeArg?: Recompute | null, blob?: Uint8Array | ArrayBuffer | null): VerifyResult;
|
|
52
|
+
/** Steps 1-6 for an already-parsed receipt (used by checkEnvelope). Throws a Stop. */
|
|
53
|
+
export declare function verifyEmbedded(receipt: JsonValue, sources: Sources): {
|
|
54
|
+
receipt: JsonObject;
|
|
55
|
+
hash: string;
|
|
56
|
+
};
|
|
57
|
+
export { Stop };
|