Skip to content

Commit 07ffb75

Browse files
authored
Merge pull request #169 from docknetwork/cd-soundness-checker
Create a composite claim soundness checker
2 parents c1df500 + 5e93a05 commit 07ffb75

13 files changed

+1786
-9
lines changed

.eslintrc.json

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,33 @@
1313
"sourceType": "module"
1414
},
1515
"rules": {
16-
"no-use-before-define": ["error", { "functions": false, "classes": true, "variables": true }],
16+
"no-use-before-define": [
17+
"error",
18+
{
19+
"functions": false,
20+
"classes": true,
21+
"variables": true
22+
}
23+
],
1724
"class-methods-use-this": "off",
1825
"no-return-await": "off",
1926
"no-plusplus": "off",
2027
"max-len": "off",
21-
"no-console": "off"
28+
"no-console": "off",
29+
"no-restricted-syntax": [
30+
"error",
31+
{
32+
"selector": "ForInStatement",
33+
"message": "for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array."
34+
},
35+
{
36+
"selector": "LabeledStatement",
37+
"message": "Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand."
38+
},
39+
{
40+
"selector": "WithStatement",
41+
"message": "`with` is disallowed in strict mode because it makes code impossible to predict and optimize."
42+
}
43+
]
2244
}
2345
}

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"name": "@docknetwork/sdk",
3-
"version": "0.3.4",
3+
"version": "0.3.5",
44
"main": "index.js",
55
"license": "MIT",
66
"repository": {
77
"type": "git",
88
"url": "https://github.com/docknetwork/sdk"
99
},
1010
"engines": {
11-
"node": ">=13.0.0"
11+
"node": ">=13.0.0"
1212
},
1313
"resolutions": {
1414
"jsonld-signatures": "https://github.com/docknetwork/jsonld-signatures",
@@ -79,6 +79,7 @@
7979
"axios": "^0.19.2",
8080
"bs58": "^4.0.1",
8181
"credentials-context": "^1.0.0",
82+
"deep-equal": "^2.0.3",
8283
"did-resolver": "^1.1.0",
8384
"dotenv": "^8.2.0",
8485
"elliptic": "^6.5.3",

src/utils/canonicalize.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// The js interface to rify accepts RDF nodes as strings.
2+
// This module deals with the conversions between the js representation
3+
// of RDF nodes ({ Iri: 'https://example.com' }) and the rify-js representation
4+
// AKA strings.
5+
6+
import { assert } from '@polkadot/util';
7+
import { assertType, assertValidNode } from './common';
8+
9+
// Convert a Node into a cononicalized string representation.
10+
//
11+
// ∀ A, B ∈ Node: canon(A) = canon(B) <-> A = B
12+
export function canon(node) {
13+
assertValidNode(node);
14+
return JSON.stringify(orderKeys(node));
15+
}
16+
17+
/// Canonicalize all the nodes in a ruleset.
18+
export function canonRules(rule) {
19+
return rule.map(({ if_all: ifAll, then }) => ({
20+
if_all: ifAll.map((claim) => claim.map(canonAtom)),
21+
then: then.map((claim) => claim.map(canonAtom)),
22+
}));
23+
}
24+
25+
/// Canonicalize a rule atom.
26+
/// An atom may be either { Bound: Node } or { Unbound: 'string' }
27+
// expect(canonAtom({ Bound: { Iri: 'https://example.com' } }))
28+
// .toEqual({ Bound: "{\"Iri\":\"https://example.com\"}" });
29+
// expect(canonAtom({ Unbound: "heyo" }))
30+
// .toEqual({ Unbound: "heyo" });
31+
function canonAtom(atom) {
32+
assert(Object.keys(atom).length === 1, 'enum must have exactly one tag');
33+
switch (Object.keys(atom)[0]) {
34+
case 'Bound':
35+
assertType(atom.Bound, 'object');
36+
return { Bound: canon(atom.Bound) };
37+
case 'Unbound':
38+
assertType(atom.Unbound, 'string');
39+
return { Unbound: atom.Unbound };
40+
default:
41+
throw new TypeError(`expected bound or unbound rule atom got ${atom}`);
42+
}
43+
}
44+
45+
/// Canonicalize all the nodes in a proof.
46+
export function canonProof(proof) {
47+
return proof.map(({ rule_index: ruleIndex, instantiations }) => ({
48+
rule_index: ruleIndex,
49+
instantiations: instantiations.map(canon),
50+
}));
51+
}
52+
53+
/// Parse all the nodes in a conaonicalized proof.
54+
export function decanonProof(proof) {
55+
return proof.map(({ rule_index: ruleIndex, instantiations }) => ({
56+
rule_index: ruleIndex,
57+
instantiations: instantiations.map(JSON.parse),
58+
}));
59+
}
60+
61+
/// Canonicalize all the nodes in a claimgraph.
62+
export function canonClaimGraph(cg) {
63+
return cg.map((claim) => claim.map(canon));
64+
}
65+
66+
/// Parse all the nodes in a canonicalized claimgraph.
67+
export function decanonClaimGraph(cg) {
68+
return cg.map((claim) => claim.map(JSON.parse));
69+
}
70+
71+
// recursively lexically sort the keys in an object
72+
// expect(JSON.stringify(orderKeys(
73+
// { b: '', a: '' }
74+
// ))).toEqual(JSON.stringify(
75+
// { a: '', b: '' }
76+
// ));
77+
// expect(JSON.stringify(orderKeys(
78+
// { b: '', a: { c: '', b: '', a: '' } }
79+
// ))).toEqual(JSON.stringify(
80+
// { a: { a: '', b: '', c: '' }, b: '' }
81+
// ));
82+
function orderKeys(a) {
83+
let keys;
84+
let ret;
85+
switch (typeof a) {
86+
case 'string':
87+
return a;
88+
case 'object':
89+
keys = Object.keys(a);
90+
keys.sort();
91+
ret = {};
92+
for (const k of keys) {
93+
ret[k] = orderKeys(a[k]);
94+
}
95+
return ret;
96+
default:
97+
throw new TypeError(`type error: orderKeys() does not accept type ${typeof a}`);
98+
}
99+
}

0 commit comments

Comments
 (0)