@quo-systems/quo 0.2.15 → 0.2.16
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/README.md +28 -17
- package/dist/harbor/memory.d.ts +1 -0
- package/dist/harbor/memory.js +7 -1
- package/dist/ward/arithmetic.d.ts +4 -0
- package/dist/ward/arithmetic.js +82 -12
- package/dist/ward/seal.js +1 -1
- package/dist/ward/ward.js +8 -4
- package/package.json +4 -5
- package/{SPEC.md → protocol/SPEC.md} +16 -4
- package/protocol/vectors/door.json +345 -0
- package/quo-kit.md +83 -11
- package/src/harbor/memory.ts +7 -1
- package/src/ward/arithmetic.ts +83 -14
- package/src/ward/seal.ts +1 -1
- package/src/ward/ward.ts +8 -4
- /package/{vectors → protocol/vectors}/arithmetic.json +0 -0
- /package/{vectors → protocol/vectors}/framing.json +0 -0
- /package/{vectors → protocol/vectors}/wire.json +0 -0
package/README.md
CHANGED
|
@@ -28,12 +28,13 @@ no dependencies. The package ships JavaScript with declarations, emitted by
|
|
|
28
28
|
types nowhere under `node_modules`.
|
|
29
29
|
|
|
30
30
|
```
|
|
31
|
+
protocol/ the shelf a kit in any language reads: SPEC.md and the vectors, no code
|
|
32
|
+
protocol/vectors/ fixed inputs and outputs, so another language proves its bytes
|
|
31
33
|
src/being/ the Being side: what a being author imports, if anything
|
|
32
34
|
src/ward/ the ward: the Ground contract, door, seal, arithmetic, heirs, stance, allowance
|
|
33
35
|
src/harbor/ MemoryHarbor, and the harbor core with its store, reach and dialer
|
|
34
36
|
src/conformance/ behaviours any ward must show, written against the truth
|
|
35
37
|
test/ the suites
|
|
36
|
-
vectors/ fixed inputs and outputs, so another language proves its bytes
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
## Requirements
|
|
@@ -77,27 +78,37 @@ import { conform } from '@quo-systems/quo/conformance';
|
|
|
77
78
|
|
|
78
79
|
## Another language
|
|
79
80
|
|
|
80
|
-
The hand to a kit in another language is
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
The hand to a kit in another language is one folder, `protocol/`, and it
|
|
82
|
+
holds two things of two kinds. `SPEC.md` is the protocol, and it assumes
|
|
83
|
+
nothing: a kit is written against it and against nothing else here. The
|
|
84
|
+
vectors beside it are the byte-level hand, everything a stranger can
|
|
85
|
+
observe: `protocol/vectors/arithmetic.json`, the primitives the seal rests
|
|
86
|
+
on, SHA-256, Ed25519, X25519, HKDF and AES-256-GCM;
|
|
87
|
+
`protocol/vectors/framing.json`, Quo's own, the ward pk, the digest, the
|
|
88
|
+
signed ask body, the sealed shapes, the invitation and the knock;
|
|
89
|
+
`protocol/vectors/wire.json`, the frames on a socket and the one request a
|
|
90
|
+
door takes; and `protocol/vectors/door.json`, the door's thirteen cases,
|
|
91
|
+
each one an arrival a ward will not answer, with the bytes that arrive, the
|
|
92
|
+
bytes that leave and the partition's digest on both sides of the judgement.
|
|
93
|
+
They import by name,
|
|
94
|
+
`@quo-systems/quo/protocol/vectors/framing.json`, so a kit's own suite can
|
|
95
|
+
read them from the package. A kit reproduces them or it is not this
|
|
96
|
+
protocol.
|
|
97
|
+
|
|
98
|
+
Nothing outside that folder is the protocol. `src/` is this kit's
|
|
99
|
+
interpretation, and `src/conformance/` is a checklist a kit ports rather
|
|
100
|
+
than a harness it runs: it imports the base class, the silence spelling and
|
|
101
|
+
this kit's store and reach, so the beings it is shown with run only in a
|
|
102
|
+
TypeScript ward. `quo-kit.md` "The three shelves" says which is which.
|
|
92
103
|
|
|
93
104
|
## Publishing
|
|
94
105
|
|
|
95
106
|
Published from 0.1.0, the first version under this name, with no
|
|
96
107
|
compatibility promise before 1.0.0: the words may still move. `npm pack
|
|
97
|
-
--dry-run` shows what ships: the emitted `dist/`, the source, the
|
|
98
|
-
the spec, this file, the licence and the notice, and no
|
|
99
|
-
configs. The spec ships because it is the truth the source and
|
|
100
|
-
are read against. Publishing is gated from the root of the repository:
|
|
108
|
+
--dry-run` shows what ships: the emitted `dist/`, the source, the protocol
|
|
109
|
+
shelf with the spec inside it, this file, the licence and the notice, and no
|
|
110
|
+
tests and no configs. The spec ships because it is the truth the source and
|
|
111
|
+
the vectors are read against. Publishing is gated from the root of the repository:
|
|
101
112
|
`npm run release:quo` there runs both gates, `check` and `check:terrain`,
|
|
102
113
|
and publishes on green; `npm publish` inside this package refuses and says
|
|
103
114
|
so. This package carries the version of its own work and is bound to no
|
package/dist/harbor/memory.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class MemoryHarbor {
|
|
|
17
17
|
readonly partitions: Map<string, Record<string, unknown>>;
|
|
18
18
|
readonly wards: Map<string, Booted>;
|
|
19
19
|
readonly objects: WeakMap<object, BeingLike>;
|
|
20
|
+
random: Ground['random'];
|
|
20
21
|
route(farPk: string, bytes: Uint8Array): Promise<Uint8Array | undefined>;
|
|
21
22
|
link(harbor: MemoryHarbor): void;
|
|
22
23
|
boot(seed: string, Ward: WardFactory, classes: Record<string, BeingClass>, lend?: Lend): Promise<Booted>;
|
package/dist/harbor/memory.js
CHANGED
|
@@ -10,6 +10,12 @@ export class MemoryHarbor {
|
|
|
10
10
|
partitions = new Map(); // seed -> memory. the harbor keeps it and reads nothing
|
|
11
11
|
wards = new Map(); // seed -> pointers
|
|
12
12
|
objects = new WeakMap(); // cells -> being object. what instantiate constructed. a hand for tests, never the ward's; it follows the cells out when she is unbooted
|
|
13
|
+
// Where the wards this harbor boots draw their entropy. The device's own
|
|
14
|
+
// by default. A harbor may hand a fixed stream instead, which is what pins
|
|
15
|
+
// bytes: every key a ward mints and every nonce it seals under comes from
|
|
16
|
+
// here, so a ward on a known seed with a known stream answers the same
|
|
17
|
+
// bytes every run, and a corpus of them is a corpus and not a sample.
|
|
18
|
+
random = entropy;
|
|
13
19
|
// the directory: its own doors, else a peer it is linked to. Quo says nothing about how.
|
|
14
20
|
async route(farPk, bytes) {
|
|
15
21
|
if (this.down.has(farPk))
|
|
@@ -55,7 +61,7 @@ export class MemoryHarbor {
|
|
|
55
61
|
const back = await this.route(farPk, new Uint8Array(bytes));
|
|
56
62
|
return back === undefined ? undefined : new Uint8Array(back);
|
|
57
63
|
},
|
|
58
|
-
random:
|
|
64
|
+
random: (n) => this.random(n),
|
|
59
65
|
};
|
|
60
66
|
const w = await Ward(ground);
|
|
61
67
|
const booted = { ...w, pk: await learnPk(w) };
|
|
@@ -7,6 +7,10 @@ export declare function unhex(text: string): Uint8Array;
|
|
|
7
7
|
export declare function concat(parts: Uint8Array[]): Uint8Array;
|
|
8
8
|
export declare function sameBytes(a: Uint8Array, b: Uint8Array): boolean;
|
|
9
9
|
export declare const smallOrder: (pk: Uint8Array) => boolean;
|
|
10
|
+
export declare const heldKeys: () => {
|
|
11
|
+
held: number;
|
|
12
|
+
bound: number;
|
|
13
|
+
};
|
|
10
14
|
export declare function sha256(...parts: Uint8Array[]): Promise<Uint8Array>;
|
|
11
15
|
export type Pair = {
|
|
12
16
|
secret: Uint8Array;
|
package/dist/ward/arithmetic.js
CHANGED
|
@@ -86,27 +86,97 @@ const key32 = (value, what) => {
|
|
|
86
86
|
return value;
|
|
87
87
|
};
|
|
88
88
|
const pkcs8 = (prefix, value, what) => concat([prefix, key32(value, what)]);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
89
|
+
// Importing a key is the most expensive thing on the path of an ask, and most
|
|
90
|
+
// of the imports are the same key again: a ward signs every reply with the one
|
|
91
|
+
// key, opens every ask with the one padlock, and verifies a relation under the
|
|
92
|
+
// key it verified it under last time. Measured over a round trip, fifteen of
|
|
93
|
+
// the twenty-five imports were bytes already imported once.
|
|
94
|
+
//
|
|
95
|
+
// So an imported key is kept, by the bytes it was imported from. A CryptoKey
|
|
96
|
+
// cannot be changed once it exists, so handing the same one out twice is
|
|
97
|
+
// handing out what a second import would have built. Nothing here is a
|
|
98
|
+
// decision a peer can see: two wards that cache differently, or not at all,
|
|
99
|
+
// speak the same bytes.
|
|
100
|
+
//
|
|
101
|
+
// It is bounded, and that is not a detail. A relation mints a fresh key on
|
|
102
|
+
// every ask, so a ward that talked all day would otherwise hold a key for
|
|
103
|
+
// every ask it ever made. Past the bound the least recently used goes, which
|
|
104
|
+
// is the key of a relation that has fallen quiet, and importing it again
|
|
105
|
+
// costs what it cost the first time.
|
|
106
|
+
//
|
|
107
|
+
// The secret keys in here are the ones the partition already holds in this
|
|
108
|
+
// process, as seeds. The cache is another shape of what the ward is already
|
|
109
|
+
// standing on, and never a second place a secret comes from.
|
|
110
|
+
const KEYS = 512;
|
|
111
|
+
const imported = new Map();
|
|
112
|
+
const keep = (id, make) => {
|
|
113
|
+
const had = imported.get(id);
|
|
114
|
+
if (had !== undefined) {
|
|
115
|
+
imported.delete(id); // and set again below: the most recently used goes last
|
|
116
|
+
imported.set(id, had);
|
|
117
|
+
return had;
|
|
118
|
+
}
|
|
119
|
+
const made = make();
|
|
120
|
+
// A key that would not import is not kept: the next call asks subtle again
|
|
121
|
+
// and hears the same refusal, rather than reading one this cache remembered.
|
|
122
|
+
// Node takes any thirty-two bytes as a public key and finds out at verify,
|
|
123
|
+
// so nothing here reaches this line; a terrain that checks the point at the
|
|
124
|
+
// import does, and a refusal it remembered would be a relation killed for
|
|
125
|
+
// good by one bad arrival.
|
|
126
|
+
made.catch(() => imported.delete(id));
|
|
127
|
+
imported.set(id, made);
|
|
128
|
+
// One in, at most one out: a map keeps what was put in the order it was put,
|
|
129
|
+
// so the first key it names is the one used longest ago.
|
|
130
|
+
if (imported.size > KEYS)
|
|
131
|
+
imported.delete(imported.keys().next().value);
|
|
132
|
+
return made;
|
|
133
|
+
};
|
|
134
|
+
// How many imported keys are held, and the bound they are held under. Nothing
|
|
135
|
+
// in the ward reads either: they are here to be looked at, and for the suite
|
|
136
|
+
// that holds the bound to what it says.
|
|
137
|
+
export const heldKeys = () => ({ held: imported.size, bound: KEYS });
|
|
138
|
+
const secretKey = (alg, prefix, value, what, uses) => {
|
|
139
|
+
const bytes = pkcs8(prefix, value, what);
|
|
140
|
+
return keep(`${alg.name}|${uses.join('+')}|${hex(bytes)}`, () => subtle().importKey('pkcs8', bytes, alg, true, uses));
|
|
141
|
+
};
|
|
142
|
+
const publicKey = (alg, value, what, uses) => {
|
|
143
|
+
const bytes = key32(value, what);
|
|
144
|
+
return keep(`${alg.name}|${uses.join('+')}|pk|${hex(bytes)}`, () => subtle().importKey('raw', bytes, alg, true, uses));
|
|
145
|
+
};
|
|
146
|
+
// Subtle exports the public half of a private key only through a JWK, where
|
|
147
|
+
// `x` is the 32 raw bytes in base64url. The answer is a fact about the key and
|
|
148
|
+
// never changes, so it is kept beside the key it was read from and goes when
|
|
149
|
+
// the key does.
|
|
150
|
+
const publics = new WeakMap();
|
|
151
|
+
function rawPublic(secret) {
|
|
152
|
+
const had = publics.get(secret);
|
|
153
|
+
if (had !== undefined)
|
|
154
|
+
return had;
|
|
155
|
+
const read = (async () => {
|
|
156
|
+
const jwk = await subtle().exportKey('jwk', secret);
|
|
157
|
+
const binary = atob(jwk.x.replaceAll('-', '+').replaceAll('_', '/'));
|
|
158
|
+
const out = new Uint8Array(binary.length);
|
|
159
|
+
for (let at = 0; at < binary.length; at += 1)
|
|
160
|
+
out[at] = binary.charCodeAt(at);
|
|
161
|
+
return out;
|
|
162
|
+
})();
|
|
163
|
+
publics.set(secret, read);
|
|
164
|
+
return read;
|
|
99
165
|
}
|
|
100
166
|
export async function sha256(...parts) {
|
|
101
167
|
return new Uint8Array(await subtle().digest('SHA-256', concat(parts)));
|
|
102
168
|
}
|
|
169
|
+
// Both halves are copies. The seed and the public key are kept behind the two
|
|
170
|
+
// caches above, and a pair is handed to whoever asked for it: what she does
|
|
171
|
+
// with the bytes in her hand is hers, and must not reach what the next caller
|
|
172
|
+
// is given.
|
|
103
173
|
export async function signingPair(seed) {
|
|
104
174
|
const secret = await secretKey(ED, ED_SECRET, seed, 'seed', ['sign']);
|
|
105
|
-
return { secret: Uint8Array.from(seed), pk: await rawPublic(secret) };
|
|
175
|
+
return { secret: Uint8Array.from(seed), pk: Uint8Array.from(await rawPublic(secret)) };
|
|
106
176
|
}
|
|
107
177
|
export async function sealingPair(seed) {
|
|
108
178
|
const secret = await secretKey(X, X_SECRET, seed, 'seed', ['deriveBits']);
|
|
109
|
-
return { secret: Uint8Array.from(seed), pk: await rawPublic(secret) };
|
|
179
|
+
return { secret: Uint8Array.from(seed), pk: Uint8Array.from(await rawPublic(secret)) };
|
|
110
180
|
}
|
|
111
181
|
export async function sign(message, secret) {
|
|
112
182
|
const key = await secretKey(ED, ED_SECRET, secret, 'secret', ['sign']);
|
package/dist/ward/seal.js
CHANGED
|
@@ -26,7 +26,7 @@ export const SIZE = 1024 * 1024;
|
|
|
26
26
|
// designs and no separation at all: one secret would be doing two jobs with
|
|
27
27
|
// nothing said about it, and a second kit would have to reproduce a
|
|
28
28
|
// construction nobody named. HKDF-SHA-256 under a label is the separation
|
|
29
|
-
// said out loud, and it is what `vectors/framing.json` pins.
|
|
29
|
+
// said out loud, and it is what `protocol/vectors/framing.json` pins.
|
|
30
30
|
//
|
|
31
31
|
// Bytes are key material and text is not. A seed handed in as bytes of the
|
|
32
32
|
// key length is taken as it stands, which is what a harbor mints; anything
|
package/dist/ward/ward.js
CHANGED
|
@@ -279,10 +279,14 @@ class Self {
|
|
|
279
279
|
return unreached();
|
|
280
280
|
}
|
|
281
281
|
const { bytes, ephemeral } = sealed;
|
|
282
|
-
// The wait is bounded, and
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
//
|
|
282
|
+
// The wait is bounded here, and again around the lane in the stance. Two
|
|
283
|
+
// bounds and not one, because they end two different things: the stance's
|
|
284
|
+
// ends the wait a being is held in, and this one ends the occupancy of the
|
|
285
|
+
// relation's lane. A being holds three answers and a wait that does not
|
|
286
|
+
// end is none of them, and a lane nobody ever leaves is a relation the
|
|
287
|
+
// next ask never reaches. Only a bound on the wire breaks the second, and
|
|
288
|
+
// taking it out would leave one quiet far side holding the lane for good.
|
|
289
|
+
// What comes back late is not read.
|
|
286
290
|
//
|
|
287
291
|
// A wait that ran out is `late`, never unreached. Unreached promises
|
|
288
292
|
// nothing was delivered and is safe to retry; a bound that expired knows
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quo-systems/quo",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
4
4
|
"description": "Quo: an object asks another object and gets an answer, without knowing where it is. Being, Ward, Harbor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quo",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"types": "./dist/conformance/index.d.ts",
|
|
36
36
|
"default": "./dist/conformance/index.js"
|
|
37
37
|
},
|
|
38
|
-
"./
|
|
38
|
+
"./protocol/*": "./protocol/*",
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json && cp ../../papers/SPEC.md SPEC.md && cp ../../papers/quo-kit.md quo-kit.md",
|
|
42
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && cp ../../papers/SPEC.md protocol/SPEC.md && cp ../../papers/quo-kit.md quo-kit.md",
|
|
43
43
|
"test": "node --test \"test/*.test.ts\"",
|
|
44
44
|
"check:terrain": "node --test \"test/terrain/*.test.ts\"",
|
|
45
45
|
"prepublishOnly": "test \"$QUO_GATED\" = 1 || { echo 'publish from the root, gated once: npm run release:quo' >&2; exit 1; }"
|
|
@@ -50,8 +50,7 @@
|
|
|
50
50
|
"files": [
|
|
51
51
|
"dist",
|
|
52
52
|
"src",
|
|
53
|
-
"
|
|
54
|
-
"SPEC.md",
|
|
53
|
+
"protocol",
|
|
55
54
|
"quo-kit.md",
|
|
56
55
|
"README.md",
|
|
57
56
|
"LICENSE",
|
|
@@ -710,6 +710,12 @@ spent, so bytes leave and the door's silence comes back: after take the
|
|
|
710
710
|
invitation names the standing and nothing else, so it is exactly as alive as
|
|
711
711
|
the standing is.
|
|
712
712
|
|
|
713
|
+
A lane is released by the bound and by nothing else. An ask that never comes
|
|
714
|
+
back holds its relation's lane, and every later ask on that relation waits
|
|
715
|
+
behind it, so the bound ends an occupancy and not only a wait. A kit that
|
|
716
|
+
bounds where a being is waiting, and not where the lane is held, has a
|
|
717
|
+
relation that one quiet far side closes for good.
|
|
718
|
+
|
|
713
719
|
### The public being
|
|
714
720
|
|
|
715
721
|
A ward may have one public being, and no more. She is an ordinary being,
|
|
@@ -898,7 +904,8 @@ the default would leave her only the narrowing half, and would give the ward
|
|
|
898
904
|
no way to allow one long piece of work without making every ask that
|
|
899
905
|
patient.
|
|
900
906
|
|
|
901
|
-
The sender's wait has an end, and
|
|
907
|
+
The sender's wait has an end, and time is the only thing the ward measures:
|
|
908
|
+
no count of doors, no count of bytes, no count of tries. The
|
|
902
909
|
bound covers the whole of an ask, from the moment she calls: a relation that
|
|
903
910
|
comes back round on itself is stopped at its own lane, before a byte is
|
|
904
911
|
sealed, and a bound that watched only the wire would never see it. This is
|
|
@@ -1045,9 +1052,14 @@ in pieces, by a being who knows how her own work divides.
|
|
|
1045
1052
|
|
|
1046
1053
|
The hand to a kit in another language is two things, and they are of two
|
|
1047
1054
|
kinds. `vectors/` is the byte-level hand: fixed inputs and outputs for
|
|
1048
|
-
everything a stranger can observe,
|
|
1049
|
-
the
|
|
1050
|
-
|
|
1055
|
+
everything a stranger can observe, in four areas. `arithmetic.json` is the
|
|
1056
|
+
primitives the seal rests on. `framing.json` is the ward pk, the digest, the
|
|
1057
|
+
signed body, the sealed shapes, the invitation and the knock.
|
|
1058
|
+
`wire.json` is the frames on a socket and the one request a door takes.
|
|
1059
|
+
`door.json` is the door's thirteen cases, each one an arrival: the bytes that
|
|
1060
|
+
come in, the bytes that go out, what those bytes open to where a hand holds
|
|
1061
|
+
the lid, and the partition's digest before and after, so `nothing written`
|
|
1062
|
+
is a value a kit checks and never a sentence it reads. A kit reproduces them
|
|
1051
1063
|
or it is not this protocol. The conformance suite is the behavioural hand,
|
|
1052
1064
|
and it is a checklist and not a harness: one ward is one runtime and one
|
|
1053
1065
|
language, so the beings a suite is shown with run only in the ward its kit
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
{
|
|
2
|
+
"corpus": "@quo-systems/quo",
|
|
3
|
+
"encoding": "hex",
|
|
4
|
+
"area": "door",
|
|
5
|
+
"note": "The door's thirteen cases as fixed bytes: what a ward answers to every arrival it will not answer, and what it writes while doing so. Each record is one arrival at one door: the ask exactly as it arrives, the reply exactly as it leaves, what that reply opens to where the hand holds the lid's secret, and the ward partition's digest before and after, so `nothing written` is a value a kit checks and not a sentence it reads. Every random is fixed. The world is two harbors linked as a wire, wards on the seeds A, B and P, and every byte of entropy any of them draws comes from SplitMix64 seeded at 20250901: the state starts at the seed, each draw adds 0x9e3779b97f4a7c15, mixes with two xor-shift-multiplies, and the sixty-four bit result is spent eight bytes at a time, least significant byte first. The stream is reset at the start of every record, so a record is reproduced without running the ones before it. D1 noise is the one record whose reply nobody can open: the door seals it to the first thirty-two bytes of the noise, and no one holds a secret for those. That is the case, not a gap in it. A word to a bound key is a different length from a silence to a stranger, on purpose. The law of one silence equalizes what strangers see and nothing else, so every record of kind `silence` is one length and every record of kind `word` or `object` is not. `ward` is the seed of the ward whose door judged the arrival. `state` says what that ward was in the middle of when it did, which is the only thing a kit needs beyond the bytes to stand a door in the same place.",
|
|
6
|
+
"vectors": [
|
|
7
|
+
{
|
|
8
|
+
"case": "D1",
|
|
9
|
+
"name": "wrong padlock",
|
|
10
|
+
"state": "a heir the door holds, sealed to another ward's padlock",
|
|
11
|
+
"ward": "B",
|
|
12
|
+
"kind": "silence",
|
|
13
|
+
"heard": false,
|
|
14
|
+
"ask": "d1fc81101fc0d82fb5c7298d598aadd7146299554890bd22c140350d5efabd3a17c7fe10142dec3537204579e051a006349247a4078cd3723d854b8ca07afd4a7b17fca2078eef5a624baa41381687774e11164b4a60ae5ecef6b93f6f793bc3445be287c45e83d8fd03e210637aeaa177a418ab6f21db665af8a03c7d2ccc390b3c90d8526a883f28acd478109f4b3691ad6f0e07d111fc1a2517899c3874219edf5f14b97801924296115153b18e9fdaf74ba9124e3e0ff2793189af09f25c6c0793f4241303b3ada7529eabb144c28c8545c7c8421d06cbb4fcdcdc18c682fd36462747cd6d1ae3f59de12b072d7357293f2e71c48af57bbecefb19df2439d99ae1233e20bf7d77fbc1dad12681913f2ee386c1fec9e836e3f1f5456ed4c53fb1dfff6a7689ab4929d46cd33532073eb545739b4efe216a1f46757c4ea0b1",
|
|
15
|
+
"reply": "84637ecbb7afd6785f3d54e8a1f8f03161d14cdadae513c3a0fd8524f8836220e2e9b1e962a94de7d12632b93c811f561f8cf7ab1071afe44abbd59d18272319624a3ad1c2630c1ea8f02f3364869642ed30f568d6885f42db8a68e14f77549098683dd5c826a5abf0c658eff571e2786d9f808bd613e4c7b580d1c19e933c6d",
|
|
16
|
+
"opens": {
|
|
17
|
+
"silence": true
|
|
18
|
+
},
|
|
19
|
+
"before": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
20
|
+
"after": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
21
|
+
"wrote": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"case": "D1",
|
|
25
|
+
"name": "noise",
|
|
26
|
+
"state": "bytes that are not a sealed ask at all",
|
|
27
|
+
"ward": "B",
|
|
28
|
+
"kind": "silence",
|
|
29
|
+
"heard": false,
|
|
30
|
+
"ask": "01c0b7c9e315dc00c2913141db31df59a760f3cea15ad41d5c51eadb39d927b4035fe63be4f7794aa739a173fea2c4c0606717001e28c219b638bec66a9d299f9da2f1ef860034d7e23a6e203f8139996d912d084d4df8923d5745aa53cfce040b9b16cfb47ffb4dd79a9b391c116cd235",
|
|
31
|
+
"reply": "eea19c6ed20cc8562606ce2695bd54d5524a3b0cbf3a224fd5a944a920dba85440682158d87d5f046a69925d12015bac1416b51fe89e1fa7f10f69478f29234a0eda4ab1468d813c3f00562c2a740d187ecf7c11fb15857a3c8023fa991dbd59bd605dec985ae25631ce152bc3e2596e6e0481f78218ba2e27859d00dc3c0d8e",
|
|
32
|
+
"opens": null,
|
|
33
|
+
"before": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
34
|
+
"after": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
35
|
+
"wrote": false
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"case": "D2",
|
|
39
|
+
"name": "method is not a string",
|
|
40
|
+
"state": "a bound key, one field of the wrong shape",
|
|
41
|
+
"ward": "B",
|
|
42
|
+
"kind": "silence",
|
|
43
|
+
"heard": false,
|
|
44
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c639ddf51cda073d6ace5734b0dabebbca1ae8b0bacf1cfe3a555ddcc7d497c9deb4b89ebc5ff0bcc14ed95d0c73f5fc98456e9daca100b9e68bbd19caec646792e270abb88105bc93c34ea5c08f44a3ddd29d2f3c016cc6239e158fa",
|
|
45
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
46
|
+
"opens": {
|
|
47
|
+
"silence": true
|
|
48
|
+
},
|
|
49
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
50
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
51
|
+
"wrote": false
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"case": "D2",
|
|
55
|
+
"name": "args is not an object",
|
|
56
|
+
"state": "a bound key, one field of the wrong shape",
|
|
57
|
+
"ward": "B",
|
|
58
|
+
"kind": "silence",
|
|
59
|
+
"heard": false,
|
|
60
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76d9985cd3a822d8b4ff7c51daef31e729c80716985e463fb7ba473e8e191954a84f44681e490edfc9f2998df9794c9136df18841541da782c9e49ccd859161e3d2ca93fdc1f5fa5160f8d578e54b2ee94318f415685278f49295cb4ea3da40eae",
|
|
61
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
62
|
+
"opens": {
|
|
63
|
+
"silence": true
|
|
64
|
+
},
|
|
65
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
66
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
67
|
+
"wrote": false
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"case": "D2",
|
|
71
|
+
"name": "seq is zero",
|
|
72
|
+
"state": "a bound key, one field of the wrong shape",
|
|
73
|
+
"ward": "B",
|
|
74
|
+
"kind": "silence",
|
|
75
|
+
"heard": false,
|
|
76
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b34ed657218484803f2fe5c827ba406d909e7a27df418437687efd338dd921293e56186f1ed2c0cd2b0767052ead4c22c91120e7934a9e4a6846af69cfa849fba769e872443dfeaea6a42209de7ea7bf1b671312ed95d3d4b60024c2944b43d66c5cd8edd6a2398c6ff9f319ee5c5886d96111ad5b7d2216f8249",
|
|
77
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
78
|
+
"opens": {
|
|
79
|
+
"silence": true
|
|
80
|
+
},
|
|
81
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
82
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
83
|
+
"wrote": false
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"case": "D2",
|
|
87
|
+
"name": "time is negative",
|
|
88
|
+
"state": "a bound key, one field of the wrong shape",
|
|
89
|
+
"ward": "B",
|
|
90
|
+
"kind": "silence",
|
|
91
|
+
"heard": false,
|
|
92
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8866a506d909e7a27df418437687efd338dd921293e56186f1ed2c0cd2b076d82048d224068df7986c2df5654e7121a0262e0a5b71110a8401cd9845c8ff6d34128fa6767a47b43a9aeaf3a28f194a98860a2b870e25e8d404df48144b0e92c707111060764444fdbfe6cfe8d52dd5",
|
|
93
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
94
|
+
"opens": {
|
|
95
|
+
"silence": true
|
|
96
|
+
},
|
|
97
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
98
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
99
|
+
"wrote": false
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"case": "D2",
|
|
103
|
+
"name": "by is not a key",
|
|
104
|
+
"state": "a bound key, one field of the wrong shape",
|
|
105
|
+
"ward": "B",
|
|
106
|
+
"kind": "silence",
|
|
107
|
+
"heard": false,
|
|
108
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cd81a87f82978e703cd1af5e88c66f32e9a99a659a90ae5b351ad235d2971643caf163308c76d7ac09d1971f6e4a988881cf9d982ebfc6508bf7520eba44bce53ffb1338e6b8c4535b70444541a3fa4bf589b8ff6961343d221866eede55bb7497d0ad205966d315a04d0207d9bf4baa822cbf5c5d0946fb86b2e8bb3e29168f025f052078b09dfc6556ea5542afebb2c0bdf5ef0056",
|
|
109
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
110
|
+
"opens": {
|
|
111
|
+
"silence": true
|
|
112
|
+
},
|
|
113
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
114
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
115
|
+
"wrote": false
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"case": "D2",
|
|
119
|
+
"name": "hops at zero",
|
|
120
|
+
"state": "a bound key, and a chain that has run out",
|
|
121
|
+
"ward": "B",
|
|
122
|
+
"kind": "silence",
|
|
123
|
+
"heard": false,
|
|
124
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76d9985cd3a822d8b4ff7c51daef31ab29c03b7882cf8d868ab1391fcaf6e8cf7ea3c990d262268ea4d28fe61810e87190c30f41d68644d3511cf3a58cab78a278cca9229a50fbbeb2fb0e6607face7ecd82419c65ff2baa6607094e06eb80acb728fb3bd6b628be45a4b9",
|
|
125
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
126
|
+
"opens": {
|
|
127
|
+
"silence": true
|
|
128
|
+
},
|
|
129
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
130
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
131
|
+
"wrote": false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"case": "D3",
|
|
135
|
+
"name": "for nobody, nobody home",
|
|
136
|
+
"state": "a ward with no public being",
|
|
137
|
+
"ward": "B",
|
|
138
|
+
"kind": "silence",
|
|
139
|
+
"heard": false,
|
|
140
|
+
"ask": "84637ecbb7afd6785f3d54e8a1f8f03161d14cdadae513c3a0fd8524f8836220ba17e2a2e1fde39c1b7f215273348cf6d0e5e0bdf2045199fea841fcb99ae6842d14b40710c9e7f2a9ad106487cb41e16a1b8bc35ac660911cf7e35c3ae1c9a3e600dd40e8a85d32d1a78b05c185e12496169c6fefe493e9e631bf439f3314cb61ce8c5c2cf328ca15247763bed2a5c072e758578504aded3f75b8923939307aeb59eaf924a4e7ef1df23ad8eef50ad880b960185795b762f38b9bb7ea9629505c25296244b5665e9c0d1d9a2c1690c02d77c167882a2f1f9180ca8f2b11a37bbd263dc9fbff020b31812ca12b76930dfbb06e6ef00074ff05614bf62d14b22530d3",
|
|
141
|
+
"reply": "5f1426463a519ded7693446c34eb6751015d932d4d929557abf3ee32ecfdcc192baea2488556aacc13b62ccbb07b7888dc2af01a2a7e59152b9546b1e185e0c50b34a4c7e9665fc5592cdb61993f571bebc20a713c733b58acbd703408206c5a42f2b644ad19e79ce07ec8b51064c369635a3ce7353dc49a112788ef910aad10",
|
|
142
|
+
"opens": {
|
|
143
|
+
"silence": true
|
|
144
|
+
},
|
|
145
|
+
"before": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
146
|
+
"after": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
147
|
+
"wrote": false
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"case": "D4",
|
|
151
|
+
"name": "for nobody, the signature fails",
|
|
152
|
+
"state": "a ward with a public being, a body signed by another key",
|
|
153
|
+
"ward": "P",
|
|
154
|
+
"kind": "silence",
|
|
155
|
+
"heard": false,
|
|
156
|
+
"ask": "5f1426463a519ded7693446c34eb6751015d932d4d929557abf3ee32ecfdcc19f7cb3d88928e737cc1502a253c5a301097cad7810345026dc168c010c0ae3e2e01ad94f6278407c1874cfea971c97e46d2adf623ca548ba6f972fcd20bdffa32b7a25faaf36f8f41053079df352547ca562be02c22e4ace975d9c4926e82152e0c24eb7de188b7ce11e81a2bed397363832c78ae09ad96e09e53b024eabf686c89ccb453cfb35fa368f46f31d321a67eb16a9c67788272def37a1dc55d6a5489d4aae9b132f446fffaa89d0e09f03f33e70cddfe1eb50e7254dd85821c9d3265d0c3362fb0499e7bde0a277b7d2021fd7aa970edc2cea02560bbaee91624d2f1d9ee",
|
|
157
|
+
"reply": "9f1bf06332529ed4df06d6000f037f02e2d822d0e8c6256842817e568383c372f50e74b15f3135af6b35a9b3776809dc400adf30b1fc380b2d77c40544424dd0fcb48c5808822b51a7260219bcda18d1321c626a71351958d941833ec7c62b9615a7120992ddddfeecc26276a833d3975021d2906a4ec771350770e925a01463",
|
|
158
|
+
"opens": {
|
|
159
|
+
"silence": true
|
|
160
|
+
},
|
|
161
|
+
"before": "cc8ba34d418b81459d6654b95f81ddd6f9a5296bb88d8a405614b1db45dd6ed9",
|
|
162
|
+
"after": "cc8ba34d418b81459d6654b95f81ddd6f9a5296bb88d8a405614b1db45dd6ed9",
|
|
163
|
+
"wrote": false
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"case": "D5",
|
|
167
|
+
"name": "a heir not held",
|
|
168
|
+
"state": "a heir this ward never minted",
|
|
169
|
+
"ward": "B",
|
|
170
|
+
"kind": "silence",
|
|
171
|
+
"heard": false,
|
|
172
|
+
"ask": "84637ecbb7afd6785f3d54e8a1f8f03161d14cdadae513c3a0fd8524f8836220ba17e2a2e1fdaf8f11756b16772bc8aa94b7b6b8a55b54cfaeaf14fcbdc8b7db2f10ea584098e5a7f7a91430809517e63f49db9c0d9660c64ca3e30b38bfc9a6b0518944b7ad0c26c9e1db1d87dcf276c5578174e2b1ddfcf76db302c4665c8b2a92cb4972f12098463a342fe7def3d67eed5951d305b7fd3421a8c2303e2277fb53b6f67ffdf1a70df22cdcacb15381d1e6acc757bf4535f4c28e1283269e8139b10710ecd0648e63ef3aa6536a694eb28ef6662d14d7f9492015e9ef85734c6e420dc0eef0fbe560d2bf4029fb5d379660b94644eed49986ea9ad9642fb391dd4fca23035ffef81a2e5543ccf5dd2f0991e9e97fed86d0d86fba15b2c2026f2a5b04c96212583a38c9b1870bcf40cd2b64ed6c706c2a2415513e255f10869d",
|
|
173
|
+
"reply": "5f1426463a519ded7693446c34eb6751015d932d4d929557abf3ee32ecfdcc192baea2488556aacc13b62ccbb07b7888dc2af01a2a7e59152b9546b1e185e0c50b34a4c7e9665fc5592cdb61993f571bebc20a713c733b58acbd703408206c5a42f2b644ad19e79ce07ec8b51064c369635a3ce7353dc49a112788ef910aad10",
|
|
174
|
+
"opens": {
|
|
175
|
+
"silence": true
|
|
176
|
+
},
|
|
177
|
+
"before": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
178
|
+
"after": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
179
|
+
"wrote": false
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"case": "D6",
|
|
183
|
+
"name": "a key not admitted",
|
|
184
|
+
"state": "a heir the door holds, a key nobody announced",
|
|
185
|
+
"ward": "B",
|
|
186
|
+
"kind": "silence",
|
|
187
|
+
"heard": false,
|
|
188
|
+
"ask": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a34b31ba0403a808d63af3f5b706095ed7def0893fe00e2fc7c2aabe76cea0b61af4c594718fb40c235b6a35696800ea29d4d9277fcd470671c7bcc5e18c79f9aa5c0afcd7f83274ec57f1730594ac30b836507c76e4b814465a7c0e1085d834389bdc1caeca09ce3c77137a4f46b54982cdcfe313c35ac39d9f44efd792103b87ba4c808383ed50f77534c9e4d801160d5ca2fc87a91e007aed7993d25ce4255c89372e3b5be9aa263f2bb34bf0f1732d0c7ca6be29b5c2737a888f480b3e86e83dd1ce923fc5954409cfedb9727d6db42ef81e7a22d571dbbf7e66221e161fb37305c424ea1bcf03686fc0cac293144c507a27a15b1f949f4268e13bf918f2fb0a84b5866c4658aeea297519181c4301b349fb79224575ed898dbf9ceac9f724e",
|
|
189
|
+
"reply": "dd9a4e87c36a6c2db4a3d6b33bd5a9d471d2ac9410cc2596cb769cb57407125051baef6786856eb312eaea0b6709252f32a9ea831eadc8fcad6d1d031f01484c70dcfe83061a57e421d9321ab846aa33a213e89dd8f6aa116af8d5bb447abcd04f4edd8edf63bf2b6a606f9f21a72181ab5555a4cd13ce7cb5224077254972e8",
|
|
190
|
+
"opens": {
|
|
191
|
+
"silence": true
|
|
192
|
+
},
|
|
193
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
194
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
195
|
+
"wrote": false
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"case": "D7",
|
|
199
|
+
"name": "the signature fails under an admitted key",
|
|
200
|
+
"state": "a bound key named in by, a body signed by another",
|
|
201
|
+
"ward": "B",
|
|
202
|
+
"kind": "silence",
|
|
203
|
+
"heard": false,
|
|
204
|
+
"ask": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a34b31ba0403a808d63af3f5b706095ed7def0893fe00e2fc7c2aabe76cea0b61af4c594718fb40c235b6a35696800ea29d4d9277fcd470671c7bcc5e18c79f9aa5c0afcd7f83274ec57f1730594ac30b826751c16b4a8b4735aa94b25e0cd5128fbfc3c8e1a1cfe0c57f64f2a16a5cc82ddca9333735fd67daa94dfb2a720dee7bf39a026a36d40774074a984ed11b65d5ca2fc87a91e007aed7993d25ce4255c89372e3b5be9aa263f2bb34bf0f1732d0c7ca6be29b5c2737a888f480b3e86e83dd1ce923fc5954409cfedb9727d6db424d264e8cdcbd7dc8e2b6af09e8f2d7c189cf2d1f3dfd05ffa99e48d7d8e1d79f89a11e15fef1a8124bf13cd07a1036b806f46f16e87ad790496b579ac490db131979d762571fe600cba5a04f6a695b9c",
|
|
205
|
+
"reply": "dd9a4e87c36a6c2db4a3d6b33bd5a9d471d2ac9410cc2596cb769cb57407125051baef6786856eb312eaea0b6709252f32a9ea831eadc8fcad6d1d031f01484c70dcfe83061a57e421d9321ab846aa33a213e89dd8f6aa116af8d5bb447abcd04f4edd8edf63bf2b6a606f9f21a72181ab5555a4cd13ce7cb5224077254972e8",
|
|
206
|
+
"opens": {
|
|
207
|
+
"silence": true
|
|
208
|
+
},
|
|
209
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
210
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
211
|
+
"wrote": false
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"case": "D8",
|
|
215
|
+
"name": "she is not there",
|
|
216
|
+
"state": "the ward restarted without her class: her heirs stand and she does not",
|
|
217
|
+
"ward": "B",
|
|
218
|
+
"kind": "word",
|
|
219
|
+
"heard": false,
|
|
220
|
+
"ask": "d1fc81101fc0d82fb5c7298d598aadd7146299554890bd22c140350d5efabd3af181a0bd75844672e9f707bb65dc951964f73a82e009ef628bcfed3bc81502e96dc21f4a8d206183b3613631a09475b93505b8e0d549a5e3c5c0172ec4a4bd79961dd1772ed0a49d4ff4b0524de8e7f35995367237ea4e8429089ef618f29f2bc7e59350d694cb7f1019a5e456c72ef608f0040461e8aff6acc8b3b934936d09655450287f7083e2f1ab2a8677ee52e7ecc97fdc0671fe65f0895685ce567091ab8888eeb8d37259c0efb78e4418839ea7f4f8c68909f0cdcacfc8669547c7524600d57180a97435ec91f033c05a8782bc1df6755f31770522eeff58a23da34ca3036797fa411083f210af546844ba40b640bd05383e4f4beeda12149741faaf2db903acdc81712c3c6c1bd1fd2ce5c049f84bbdd5d3eea4ac5e9180d62c913fbe6b48974783e08e6cbc5768adce3c753ac2058c5f00a06896c1e90acef9580a20c1d03b3e1a7260c52bba5194164a3d1a04c2cb4fde6d8b0265b7e1f28c",
|
|
221
|
+
"reply": "84637ecbb7afd6785f3d54e8a1f8f03161d14cdadae513c3a0fd8524f8836220e2e9b1e962a94de7d12632b93c811f561f8cf7ab1071afe44abbd59d18272319624a3ad1c2630c1ea8f02f3364869642ed30f568d6885f42db8a68e14f77549098683dd5c826a5abf0c658eff571e2786d9f808bd613e4c7b580d1c19e933c6d",
|
|
222
|
+
"opens": {
|
|
223
|
+
"silence": true
|
|
224
|
+
},
|
|
225
|
+
"before": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
226
|
+
"after": "0cef327059b38467e3f7bb2ccc494150ef80e690a31e633181ffd64534b2df32",
|
|
227
|
+
"wrote": false
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"case": "D8",
|
|
231
|
+
"name": "she is there and the record is not",
|
|
232
|
+
"state": "the being is booted and the occupant her heir was opened for is gone",
|
|
233
|
+
"ward": "B",
|
|
234
|
+
"kind": "word",
|
|
235
|
+
"heard": true,
|
|
236
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76d9985cd3a822d8b4ff7c51daef31ab2991225bfd71f871729fd0e6ec4fdc6d81cc0db739af5c768653bb2757b20dce0d210bbbacacb37779203717be35a85f937a266cbb9f563ef35ab6dfb2aa268b825f3f1d5ec5cc1a4e2bc03299c34149b180",
|
|
237
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74ee2dfde200e3e70c38b987a1e08811d4d79177fad4387bd22187dcfb6a0d200e61decd85bebb9a83822d7bfda170d83c5baada11f4787f46b41e998375e5e800bb3751a9a8a66a70172edc19447a3975841b33edd84d64f66952b6d0582c5",
|
|
238
|
+
"opens": {
|
|
239
|
+
"quo": "removed"
|
|
240
|
+
},
|
|
241
|
+
"before": "ac56803213dec9a818db15c7b0b946fad9b05ee6e81f7f9388450fbd8648958f",
|
|
242
|
+
"after": "ac56803213dec9a818db15c7b0b946fad9b05ee6e81f7f9388450fbd8648958f",
|
|
243
|
+
"wrote": false
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"case": "D9",
|
|
247
|
+
"name": "a heir that announces nothing",
|
|
248
|
+
"state": "a fresh heir, and no next in the body",
|
|
249
|
+
"ward": "B",
|
|
250
|
+
"kind": "word",
|
|
251
|
+
"heard": true,
|
|
252
|
+
"ask": "84637ecbb7afd6785f3d54e8a1f8f03161d14cdadae513c3a0fd8524f8836220ba17e2a2e1fdafdd47703c49727d98adc1b7b2eaf40456cbf0f044adbf9de9df2b44ed06169fb0f5a7f6436080c247b23f1ed9c20d93369718a7bc0e69ef9ba4e4568b41e0ad0926c9e1db1d87dcf276c5578174e2b1ddfcf76db302c4665c8b2a92cb4972f12098463a342fe7def3d67eed5951d305b7fd3421a8c2303e2277fb53b6f67ffdf1a70df22cdcacb15381d1e6acc757bf4535f4c28e1283269e8139b10710ecd0648e63ef3aa6536a694eb28ef6662d14d7f9492015e9ef85734c6e420dc0eef0fbe560d2bf4029fb5d37e38972cbf17e9d9b7809b4d4200ad6098aba8fca967288509acc3c22056a7cef0da21ab8ac7fa279061d5b824464a21b63704485aead00bca83c724e4999b7c247130a027532de913cdd64ebf9b0ce3c",
|
|
253
|
+
"reply": "5f1426463a519ded7693446c34eb6751015d932d4d929557abf3ee32ecfdcc192baea0548611fe8d03fa77d1ac61689b8ba92c3370c3e2d832328ac1595d9d421c8d48e32b9a0b246bf24614a286d0df0ad99cdbd637102870d1e8e95c620498fac0bcd1ba582435b1327de076f8d2e22679b9c1e0a1e14527e1bed1941a6f582141b7b5b5",
|
|
254
|
+
"opens": {
|
|
255
|
+
"quo": "unannounced"
|
|
256
|
+
},
|
|
257
|
+
"before": "d0eef8ac1466acb55969bc26b3abe1efc7da1536279b8f5579429308a5ce0367",
|
|
258
|
+
"after": "d0eef8ac1466acb55969bc26b3abe1efc7da1536279b8f5579429308a5ce0367",
|
|
259
|
+
"wrote": false
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"case": "D9",
|
|
263
|
+
"name": "a bound key that announces nothing",
|
|
264
|
+
"state": "a key the door already vouched for, and no next in the body",
|
|
265
|
+
"ward": "B",
|
|
266
|
+
"kind": "object",
|
|
267
|
+
"heard": true,
|
|
268
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76d9985cd3a822d8b4ff7c51daef31ab2991225bfd71f871729fd0e6ec4fdc6d81cc0db739af5c768653bb2757b20dce0d210bbbacacb37779203717be35a85f937a266cbb9f563ef35ab6dfb2aa268b825f3f1d5ec5cc1a4e2bc03299c34149b180",
|
|
269
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a750f5da9979586e2fd5c686765810de652eae81f6d65bb704d6820eb59123f51202a9f7b952bfe24eaa05da66b584ad9697dd8c5f86f18e8124a50d54b06a30061bc9a8e219ec0dd8fac9d312991f9b5c12240d44f14f84d6bb8fa3daf822b9dbc48ca9552a86d8bf1b12a22468ca6fcd43192b26040e9b78b8b7a17532c7c9775ce18a7c26b0a3f8af2d65b15682c82cb3a804c77072bbe1a14562af60fdfe99204e7cab5271fbf3edeb1d3ffc71",
|
|
270
|
+
"opens": {
|
|
271
|
+
"object": {
|
|
272
|
+
"hi": "g1"
|
|
273
|
+
},
|
|
274
|
+
"seen": "b51674a53e5fa1e707b2cf1f322ffaefa54bde51436f12b5853741570771618f"
|
|
275
|
+
},
|
|
276
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
277
|
+
"after": "eb0d99882afb6353165fdad8799f31414f8f9fba268bd077993b096654588f31",
|
|
278
|
+
"wrote": true
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"case": "D10",
|
|
282
|
+
"name": "a number already spent",
|
|
283
|
+
"state": "a bound key, a seq at or below the mark",
|
|
284
|
+
"ward": "B",
|
|
285
|
+
"kind": "word",
|
|
286
|
+
"heard": true,
|
|
287
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35ed657218484803f2fe5c827ba406d909e7a27df418437687efd338dd921293e56186f1ed2c0cd2b07648aacadc56034e397a15c8399fed53930390d4119cdd52312169f1ece0b84c02aaa408ccdd1acb079879ea0f2d2ebb15a15394e7a0e22ded4fe77d6879f23a982d28bef4aee07a2241e7fab279af3254",
|
|
288
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74ee2dfde200e3e70de818f6b1f4ede7fc3dd501392b9f23dfff145b7d1fd60c8dbb0ab26ff7dd66f6363e24d2c6a2efa26e468654c0ee8cfe8f033fd70096bad181116f4d9af00e1f81746a28b31fd67038b4536107326ff221138148bc59727",
|
|
289
|
+
"opens": {
|
|
290
|
+
"quo": "repeated"
|
|
291
|
+
},
|
|
292
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
293
|
+
"after": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
294
|
+
"wrote": false
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"case": "D11",
|
|
298
|
+
"name": "she threw",
|
|
299
|
+
"state": "a bound key on an ask that raises",
|
|
300
|
+
"ward": "B",
|
|
301
|
+
"kind": "word",
|
|
302
|
+
"heard": true,
|
|
303
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76d3925fd2e52cd6f7ec69458bf770ad295d490ae9cb2baaeb0e491098845f38d60f1801811937181f651667622b8f02054378fb442a71e81bf80b4255a068975b7f6a0e95bb906d7ced6000349b06859a29f516bd2070aec3d53bc9c42747cf62",
|
|
304
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74ee2dfde200e387ddc81993d07f5ad7025106aae5e4741c9be9d82f9e9ad5d90b806f1d8303ee0c75f106f7652a73c6a1312d11b7947537198eeca32946be853e37d96a62ce2d2b95f54519faef2dc35227d4becd78da6cb5d940ca1e4",
|
|
305
|
+
"opens": {
|
|
306
|
+
"quo": "threw"
|
|
307
|
+
},
|
|
308
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
309
|
+
"after": "0af31511f1fb9f98fc84fd817efa847e6be922f7e537f83f60b9a7298306ebd0",
|
|
310
|
+
"wrote": true
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"case": "D12",
|
|
314
|
+
"name": "she said silence",
|
|
315
|
+
"state": "a bound key on an ask that answers the value silence",
|
|
316
|
+
"ward": "B",
|
|
317
|
+
"kind": "silence",
|
|
318
|
+
"heard": true,
|
|
319
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76c08859dab322d8b4ff7c51daef31ab29915344dccc90cf697d08ea0b2bb00e6776c286ab75b4da33844f177a096a0d955fb83096bbb748bb099b0eb1b1f6ae519aa3f1d93795c190ecee293e03ec3ef159bd0ce64ea70728540a68ed570e031c24",
|
|
320
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74cfedc99744f293794909c6a1f57c8e4a787dbe3591c2f543539f1013d8a86f6810e7f24e485766763b2db3642dedf361f92900101a4e9a8cdded78fe397e952f7e7f446149cbb238ab3596b772ee9767b9f8d2ecb1dfa3ba33d75cad543",
|
|
321
|
+
"opens": {
|
|
322
|
+
"silence": true
|
|
323
|
+
},
|
|
324
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
325
|
+
"after": "0af31511f1fb9f98fc84fd817efa847e6be922f7e537f83f60b9a7298306ebd0",
|
|
326
|
+
"wrote": true
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"case": "D13",
|
|
330
|
+
"name": "a word out of her",
|
|
331
|
+
"state": "a bound key on an ask that returns a shape only a ward may make",
|
|
332
|
+
"ward": "B",
|
|
333
|
+
"kind": "word",
|
|
334
|
+
"heard": true,
|
|
335
|
+
"ask": "3f43ed1fa30e3cfa3b4fd56b428e3d3c9f8561a11a6a20c76c38332f468ead0124c1b80f327342ce552b30a7ace0af22f5bb02737198288e8342a5bcfb41321c078f47c0f4c288681e134f1aa1c6262ce862b0a391196060d3107054933b5794d3c099133add19e1532fed4f8609cdd6f13d83d492606286e34bcccb6523b0f2d02188ccef483c4fd53283d8570ec0f837248276d7fe5fd7df0a361aded88bc98bc735b39b0adffd1410ac10acbb79fb0561f5a04e1990f3cedc9b13352805c8038b1b35f177345d075948a5a84e8878a41acb54aef478fe080923c1a58c76d39c549deb2295e4f97d1493b676adbc9ac462bc76aeca060a3dfb5b547dde4b43504507eaceb2ea2829389ec62c359df96cf6e0fbf0c5edb96ffc5f254994cce731764d7cf5adc66ef71afb41b3d29788ef4b6c2803ae9627306220e5ffeb",
|
|
336
|
+
"reply": "a8cf51bfc797db309b9d1d0ff388ab9681f00d6d96c4a32fc5c4f2c71b130a3402a74ee2dfde200e387ddc81993d07f5ad7025106aae5e4741c9be9d82f9e9ad5d90b806f1d8303ee0c75f106f7652a73c6a1312d11b7947537198eeca32946be853e37d96a62ce2d2b95f54519faef2dc35227d4becd78da6cb5d940ca1e4",
|
|
337
|
+
"opens": {
|
|
338
|
+
"quo": "threw"
|
|
339
|
+
},
|
|
340
|
+
"before": "d2330d40c0e9dc0cba9f5f6a5a1a6a79cb02d3b93f91642d1f19225e1c53a743",
|
|
341
|
+
"after": "0af31511f1fb9f98fc84fd817efa847e6be922f7e537f83f60b9a7298306ebd0",
|
|
342
|
+
"wrote": true
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
}
|
package/quo-kit.md
CHANGED
|
@@ -33,24 +33,67 @@ which is how a consumer does. `test/package.test.ts` is the one
|
|
|
33
33
|
suite that meets the package as a stranger: it packs the tarball,
|
|
34
34
|
installs it into an empty folder, and imports every entry point under
|
|
35
35
|
plain Node. `npm pack --dry-run` shows what ships: `dist/`, the four
|
|
36
|
-
source folders, the
|
|
37
|
-
the notice, and nothing else. Two papers ship
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
source folders, the protocol shelf, this paper, the README, the licence and
|
|
37
|
+
the notice, and nothing else. Two papers ship and the build copies both in,
|
|
38
|
+
each to where it belongs: the spec into `protocol/`, beside the vectors it
|
|
39
|
+
is read against and away from every line of TypeScript, because that folder
|
|
40
|
+
is what a kit in another language is written against and nothing else; this
|
|
41
|
+
paper to the package root, where it says which of what a reader sees here
|
|
42
|
+
was a choice. Publishing runs both
|
|
42
43
|
gates first, `npm run check` and `npm run check:terrain`, and refuses on a
|
|
43
44
|
failure.
|
|
44
45
|
|
|
45
46
|
```
|
|
47
|
+
protocol/ the shelf a second kit reads. SPEC.md, copied in by the build, and the vectors beside it.
|
|
48
|
+
protocol/vectors/ fixed inputs and outputs: the arithmetic, Quo's own framing, the frames on the wire, the door's thirteen cases.
|
|
46
49
|
src/being/ the Being side. types, the base class, silence, the digest.
|
|
47
50
|
src/ward/ the ward. the Ground contract, door, seal, arithmetic, heirs, stance, owner, partition, cells, allowance.
|
|
48
51
|
src/harbor/ the memory harbor, the store, the reach, the dialer and the harbor core.
|
|
49
52
|
src/conformance/ the behaviours any ward must show, and the fixed beings they are shown with.
|
|
50
53
|
test/ the suites.
|
|
51
|
-
vectors/ fixed inputs and outputs: the arithmetic, Quo's own framing, the frames on the wire.
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
## The three shelves
|
|
57
|
+
|
|
58
|
+
The package holds three kinds of thing and they are not interchangeable.
|
|
59
|
+
Knowing which shelf a file is on decides whether a kit in another language
|
|
60
|
+
may read it, whether this paper or the spec governs it, and whether it
|
|
61
|
+
travels the day a second kit exists.
|
|
62
|
+
|
|
63
|
+
| shelf | what stands there | language |
|
|
64
|
+
| -------- | ---------------------------------------------------- | ----------------------- |
|
|
65
|
+
| protocol | `protocol/`: `SPEC.md` and the vectors | none, prose and bytes |
|
|
66
|
+
| kit | `src/being/`, `src/ward/`, `src/harbor/`, this paper | TypeScript, chosen here |
|
|
67
|
+
| harness | `src/conformance/`, `test/` | TypeScript, ours |
|
|
68
|
+
|
|
69
|
+
The protocol shelf is the only one a kit in Rust or Go reads. It is a
|
|
70
|
+
document that assumes nothing and four files of fixed bytes, and it is
|
|
71
|
+
written so that folder alone, lifted into an empty repository, is the whole
|
|
72
|
+
protocol. Nothing on it imports anything, because nothing on it is code.
|
|
73
|
+
|
|
74
|
+
The kit shelf is this interpretation and another kit may refuse every line
|
|
75
|
+
of it: a base class, a spelling for silence, a store interface, a reach, a
|
|
76
|
+
dialer, a file layout.
|
|
77
|
+
|
|
78
|
+
The harness shelf proves the kit shelf, and it is not the protocol however
|
|
79
|
+
much it reads like it. `src/conformance/` imports `Being`, `silence.ts`,
|
|
80
|
+
`digest.ts` and the kit's own `Store` and `Reach`, so a kit in another
|
|
81
|
+
language cannot run one line of it. It is exported all the same, as
|
|
82
|
+
`@quo-systems/quo/conformance`, because every harbor the dock stands on is
|
|
83
|
+
a TypeScript harbor and the suite is how one is accepted. The day a second
|
|
84
|
+
kit exists, the conformance suite does not run there. It is ported: a kit
|
|
85
|
+
writes the six obligations of the probe, carries the suite and its beings
|
|
86
|
+
into its own language, and reads them beside its own tests.
|
|
87
|
+
|
|
88
|
+
The hand to that kit is two things of two kinds, as the spec has it. The
|
|
89
|
+
vectors are the byte-level hand and the ported checklist is the
|
|
90
|
+
behavioural one. The suite does not become data, because the beings it is
|
|
91
|
+
shown with run only in the ward its kit wrote. The four areas are the
|
|
92
|
+
whole byte-level hand: `arithmetic.json`, `framing.json`, `wire.json` and
|
|
93
|
+
`door.json`, the last being the door's thirteen cases, each an arrival with
|
|
94
|
+
the bytes in, the bytes out and the partition's digest on both sides of the
|
|
95
|
+
judgement. The shelf owes nothing beyond them.
|
|
96
|
+
|
|
54
97
|
Entry points: `@quo-systems/quo` is the Being side, `@quo-systems/quo/ward`
|
|
55
98
|
is `Ward`, `@quo-systems/quo/harbor` is `MemoryHarbor`, `Harbor`, the
|
|
56
99
|
store, the reach and the dialer, and `@quo-systems/quo/conformance` is
|
|
@@ -295,6 +338,19 @@ The seal is `src/ward/seal.ts` and the arithmetic is `src/ward/arithmetic.ts`.
|
|
|
295
338
|
before a reply is. `test/ward.test.ts` reads every byte string that crossed,
|
|
296
339
|
so that nothing inner is readable in the bytes.
|
|
297
340
|
|
|
341
|
+
An ask over two wards on two harbors spends thirty-four calls into
|
|
342
|
+
`crypto.subtle`, fourteen of them key imports, and `test/cost.test.ts` holds
|
|
343
|
+
it to forty and sixteen. Importing a key is the most expensive thing on that
|
|
344
|
+
path and most of the imports are the same key again: a ward signs every reply
|
|
345
|
+
with one key, opens every ask with one padlock, and verifies a relation under
|
|
346
|
+
the key it verified it under last time. So the arithmetic keeps an imported
|
|
347
|
+
key by the bytes it was imported from, and the public half beside the key it
|
|
348
|
+
was read from. The cache is bounded at `KEYS`, five hundred and twelve, least
|
|
349
|
+
recently used out, because a relation mints a fresh key on every ask and one
|
|
350
|
+
that only grew would hold a key for every ask a ward ever made. None of it is
|
|
351
|
+
a decision a peer can see: a kit that caches nothing speaks the same bytes,
|
|
352
|
+
which is why this is the kit's paper and not the protocol's.
|
|
353
|
+
|
|
298
354
|
## The harbor side
|
|
299
355
|
|
|
300
356
|
The **store** is `src/harbor/store.ts`, the **reach** is
|
|
@@ -378,7 +434,15 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
378
434
|
by its numbers: the five cases and two unreacheds of her ward, the thirteen
|
|
379
435
|
cases of the door with a partition snapshot under each, seven strangers
|
|
380
436
|
met with one silence and six bound keys hearing their word, hops refused
|
|
381
|
-
at zero, and the six lines of the law of one silence.
|
|
437
|
+
at zero, and the six lines of the law of one silence. The world it runs in
|
|
438
|
+
and the thirteen cases as records are `test/door.ts`, which draws no
|
|
439
|
+
entropy from the device: every byte comes from one SplitMix64 stream, so
|
|
440
|
+
the cases are the same bytes twice. The suite reads
|
|
441
|
+
`protocol/vectors/door.json` and holds the tree to it, and the two laws it
|
|
442
|
+
states over the whole list, one length for every stranger and one
|
|
443
|
+
plaintext under the box, run over the corpus rather than a table beside
|
|
444
|
+
it. `test/write-door-vectors.ts` writes that corpus, by a hand and never
|
|
445
|
+
by the gate: a diff in it is a protocol change.
|
|
382
446
|
- `test/blueprint.test.ts`: blueprints and instantiation through a real
|
|
383
447
|
ward. What a boot leaves behind, what a restart brings back when the code
|
|
384
448
|
moved under the cells, the gate and the door, the reserved ids.
|
|
@@ -393,9 +457,9 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
393
457
|
again under the same id; a taker that will not take leaves the faculty no
|
|
394
458
|
occupant and no key, and the next lend stands; and a being taken out of her
|
|
395
459
|
ward lends nothing.
|
|
396
|
-
- `test/seal.test.ts`: the arithmetic against `vectors/arithmetic.json`,
|
|
460
|
+
- `test/seal.test.ts`: the arithmetic against `protocol/vectors/arithmetic.json`,
|
|
397
461
|
then the seal, round trip, and what it refuses; then the framing against
|
|
398
|
-
`vectors/framing.json`. The arithmetic is standard and any language has
|
|
462
|
+
`protocol/vectors/framing.json`. The arithmetic is standard and any language has
|
|
399
463
|
it. The framing is Quo's own, and a kit that reproduces the hashes and
|
|
400
464
|
not the ward pk, the digest, the signed ask body, the two sealed shapes,
|
|
401
465
|
the invitation or the knock is not this protocol. Every seed in that file
|
|
@@ -441,7 +505,7 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
441
505
|
the store with the hints; the dialer over a stubbed line, announce, a claim proven
|
|
442
506
|
at a real door before it binds and a claim nobody there holds left
|
|
443
507
|
unbound, fallback, unbind and the wait before it dials again; and the frames against
|
|
444
|
-
`vectors/wire.json`, the ask, the reply, nothing delivered and the
|
|
508
|
+
`protocol/vectors/wire.json`, the ask, the reply, nothing delivered and the
|
|
445
509
|
announce, so a kit reproduces the bytes on a socket; and the request
|
|
446
510
|
reach against the request record in the same file, over a fetch that
|
|
447
511
|
sees what a listener would, one POST with the suite in its header, the
|
|
@@ -461,6 +525,14 @@ passes the same suite. Those tests are the checklist, not the mock.
|
|
|
461
525
|
one by one; that the arithmetic spends every one of them; and the two ways
|
|
462
526
|
a terrain can be short -- no `crypto.subtle` at all, and a subtle without
|
|
463
527
|
the curves -- each failing at the first call, in one sentence.
|
|
528
|
+
- `test/cost.test.ts`: what an ask costs. Every other suite is blind to it:
|
|
529
|
+
a change that doubles the work per ask leaves all of them green. The
|
|
530
|
+
assertion is the count of calls into `crypto.subtle`, which is the same
|
|
531
|
+
number on every terrain and does not move because another lane of the gate
|
|
532
|
+
is running; a regression in it is somebody deriving again what the ward
|
|
533
|
+
already holds. Beside it, that the kept keys stay under their bound however
|
|
534
|
+
long a ward talks, and one wall clock set an order of magnitude wide, which
|
|
535
|
+
is a smoke alarm and not a measurement.
|
|
464
536
|
- `test/package.test.ts`: the package as a stranger meets it. The tarball
|
|
465
537
|
is packed, installed into an empty folder with nothing but Node, and
|
|
466
538
|
every entry point the exports map names is imported. Every other suite
|
package/src/harbor/memory.ts
CHANGED
|
@@ -21,6 +21,12 @@ export class MemoryHarbor {
|
|
|
21
21
|
readonly partitions = new Map<string, Record<string, unknown>>(); // seed -> memory. the harbor keeps it and reads nothing
|
|
22
22
|
readonly wards = new Map<string, Booted>(); // seed -> pointers
|
|
23
23
|
readonly objects = new WeakMap<object, BeingLike>(); // cells -> being object. what instantiate constructed. a hand for tests, never the ward's; it follows the cells out when she is unbooted
|
|
24
|
+
// Where the wards this harbor boots draw their entropy. The device's own
|
|
25
|
+
// by default. A harbor may hand a fixed stream instead, which is what pins
|
|
26
|
+
// bytes: every key a ward mints and every nonce it seals under comes from
|
|
27
|
+
// here, so a ward on a known seed with a known stream answers the same
|
|
28
|
+
// bytes every run, and a corpus of them is a corpus and not a sample.
|
|
29
|
+
random: Ground['random'] = entropy;
|
|
24
30
|
|
|
25
31
|
// the directory: its own doors, else a peer it is linked to. Quo says nothing about how.
|
|
26
32
|
async route(farPk: string, bytes: Uint8Array): Promise<Uint8Array | undefined> {
|
|
@@ -66,7 +72,7 @@ export class MemoryHarbor {
|
|
|
66
72
|
const back = await this.route(farPk, new Uint8Array(bytes));
|
|
67
73
|
return back === undefined ? undefined : new Uint8Array(back);
|
|
68
74
|
},
|
|
69
|
-
random:
|
|
75
|
+
random: (n) => this.random(n),
|
|
70
76
|
};
|
|
71
77
|
const w = await Ward(ground);
|
|
72
78
|
const booted: Booted = { ...w, pk: await learnPk(w) };
|
package/src/ward/arithmetic.ts
CHANGED
|
@@ -85,18 +85,83 @@ const key32 = (value: Uint8Array, what: string): Uint8Array => {
|
|
|
85
85
|
return value;
|
|
86
86
|
};
|
|
87
87
|
const pkcs8 = (prefix: Uint8Array, value: Uint8Array, what: string) => concat([prefix, key32(value, what)]);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
|
|
89
|
+
// Importing a key is the most expensive thing on the path of an ask, and most
|
|
90
|
+
// of the imports are the same key again: a ward signs every reply with the one
|
|
91
|
+
// key, opens every ask with the one padlock, and verifies a relation under the
|
|
92
|
+
// key it verified it under last time. Measured over a round trip, fifteen of
|
|
93
|
+
// the twenty-five imports were bytes already imported once.
|
|
94
|
+
//
|
|
95
|
+
// So an imported key is kept, by the bytes it was imported from. A CryptoKey
|
|
96
|
+
// cannot be changed once it exists, so handing the same one out twice is
|
|
97
|
+
// handing out what a second import would have built. Nothing here is a
|
|
98
|
+
// decision a peer can see: two wards that cache differently, or not at all,
|
|
99
|
+
// speak the same bytes.
|
|
100
|
+
//
|
|
101
|
+
// It is bounded, and that is not a detail. A relation mints a fresh key on
|
|
102
|
+
// every ask, so a ward that talked all day would otherwise hold a key for
|
|
103
|
+
// every ask it ever made. Past the bound the least recently used goes, which
|
|
104
|
+
// is the key of a relation that has fallen quiet, and importing it again
|
|
105
|
+
// costs what it cost the first time.
|
|
106
|
+
//
|
|
107
|
+
// The secret keys in here are the ones the partition already holds in this
|
|
108
|
+
// process, as seeds. The cache is another shape of what the ward is already
|
|
109
|
+
// standing on, and never a second place a secret comes from.
|
|
110
|
+
const KEYS = 512;
|
|
111
|
+
const imported = new Map<string, Promise<CryptoKey>>();
|
|
112
|
+
const keep = (id: string, make: () => Promise<CryptoKey>): Promise<CryptoKey> => {
|
|
113
|
+
const had = imported.get(id);
|
|
114
|
+
if (had !== undefined) {
|
|
115
|
+
imported.delete(id); // and set again below: the most recently used goes last
|
|
116
|
+
imported.set(id, had);
|
|
117
|
+
return had;
|
|
118
|
+
}
|
|
119
|
+
const made = make();
|
|
120
|
+
// A key that would not import is not kept: the next call asks subtle again
|
|
121
|
+
// and hears the same refusal, rather than reading one this cache remembered.
|
|
122
|
+
// Node takes any thirty-two bytes as a public key and finds out at verify,
|
|
123
|
+
// so nothing here reaches this line; a terrain that checks the point at the
|
|
124
|
+
// import does, and a refusal it remembered would be a relation killed for
|
|
125
|
+
// good by one bad arrival.
|
|
126
|
+
made.catch(() => imported.delete(id));
|
|
127
|
+
imported.set(id, made);
|
|
128
|
+
// One in, at most one out: a map keeps what was put in the order it was put,
|
|
129
|
+
// so the first key it names is the one used longest ago.
|
|
130
|
+
if (imported.size > KEYS) imported.delete(imported.keys().next().value!);
|
|
131
|
+
return made;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// How many imported keys are held, and the bound they are held under. Nothing
|
|
135
|
+
// in the ward reads either: they are here to be looked at, and for the suite
|
|
136
|
+
// that holds the bound to what it says.
|
|
137
|
+
export const heldKeys = (): { held: number; bound: number } => ({ held: imported.size, bound: KEYS });
|
|
138
|
+
|
|
139
|
+
const secretKey = (alg: { name: string }, prefix: Uint8Array, value: Uint8Array, what: string, uses: KeyUsage[]) => {
|
|
140
|
+
const bytes = pkcs8(prefix, value, what);
|
|
141
|
+
return keep(`${alg.name}|${uses.join('+')}|${hex(bytes)}`, () => subtle().importKey('pkcs8', bytes as BufferSource, alg, true, uses));
|
|
142
|
+
};
|
|
143
|
+
const publicKey = (alg: { name: string }, value: Uint8Array, what: string, uses: KeyUsage[]) => {
|
|
144
|
+
const bytes = key32(value, what);
|
|
145
|
+
return keep(`${alg.name}|${uses.join('+')}|pk|${hex(bytes)}`, () => subtle().importKey('raw', bytes as BufferSource, alg, true, uses));
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Subtle exports the public half of a private key only through a JWK, where
|
|
149
|
+
// `x` is the 32 raw bytes in base64url. The answer is a fact about the key and
|
|
150
|
+
// never changes, so it is kept beside the key it was read from and goes when
|
|
151
|
+
// the key does.
|
|
152
|
+
const publics = new WeakMap<CryptoKey, Promise<Uint8Array>>();
|
|
153
|
+
function rawPublic(secret: CryptoKey): Promise<Uint8Array> {
|
|
154
|
+
const had = publics.get(secret);
|
|
155
|
+
if (had !== undefined) return had;
|
|
156
|
+
const read = (async () => {
|
|
157
|
+
const jwk = await subtle().exportKey('jwk', secret);
|
|
158
|
+
const binary = atob(jwk.x!.replaceAll('-', '+').replaceAll('_', '/'));
|
|
159
|
+
const out = new Uint8Array(binary.length);
|
|
160
|
+
for (let at = 0; at < binary.length; at += 1) out[at] = binary.charCodeAt(at);
|
|
161
|
+
return out;
|
|
162
|
+
})();
|
|
163
|
+
publics.set(secret, read);
|
|
164
|
+
return read;
|
|
100
165
|
}
|
|
101
166
|
|
|
102
167
|
export async function sha256(...parts: Uint8Array[]): Promise<Uint8Array> {
|
|
@@ -104,13 +169,17 @@ export async function sha256(...parts: Uint8Array[]): Promise<Uint8Array> {
|
|
|
104
169
|
}
|
|
105
170
|
|
|
106
171
|
export type Pair = { secret: Uint8Array; pk: Uint8Array };
|
|
172
|
+
// Both halves are copies. The seed and the public key are kept behind the two
|
|
173
|
+
// caches above, and a pair is handed to whoever asked for it: what she does
|
|
174
|
+
// with the bytes in her hand is hers, and must not reach what the next caller
|
|
175
|
+
// is given.
|
|
107
176
|
export async function signingPair(seed: Uint8Array): Promise<Pair> {
|
|
108
177
|
const secret = await secretKey(ED, ED_SECRET, seed, 'seed', ['sign']);
|
|
109
|
-
return { secret: Uint8Array.from(seed), pk: await rawPublic(secret) };
|
|
178
|
+
return { secret: Uint8Array.from(seed), pk: Uint8Array.from(await rawPublic(secret)) };
|
|
110
179
|
}
|
|
111
180
|
export async function sealingPair(seed: Uint8Array): Promise<Pair> {
|
|
112
181
|
const secret = await secretKey(X, X_SECRET, seed, 'seed', ['deriveBits']);
|
|
113
|
-
return { secret: Uint8Array.from(seed), pk: await rawPublic(secret) };
|
|
182
|
+
return { secret: Uint8Array.from(seed), pk: Uint8Array.from(await rawPublic(secret)) };
|
|
114
183
|
}
|
|
115
184
|
|
|
116
185
|
export async function sign(message: Uint8Array, secret: Uint8Array): Promise<Uint8Array> {
|
package/src/ward/seal.ts
CHANGED
|
@@ -54,7 +54,7 @@ export const SIZE = 1024 * 1024;
|
|
|
54
54
|
// designs and no separation at all: one secret would be doing two jobs with
|
|
55
55
|
// nothing said about it, and a second kit would have to reproduce a
|
|
56
56
|
// construction nobody named. HKDF-SHA-256 under a label is the separation
|
|
57
|
-
// said out loud, and it is what `vectors/framing.json` pins.
|
|
57
|
+
// said out loud, and it is what `protocol/vectors/framing.json` pins.
|
|
58
58
|
//
|
|
59
59
|
// Bytes are key material and text is not. A seed handed in as bytes of the
|
|
60
60
|
// key length is taken as it stands, which is what a harbor mints; anything
|
package/src/ward/ward.ts
CHANGED
|
@@ -289,10 +289,14 @@ class Self implements BeingLike {
|
|
|
289
289
|
return unreached();
|
|
290
290
|
}
|
|
291
291
|
const { bytes, ephemeral } = sealed;
|
|
292
|
-
// The wait is bounded, and
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
292
|
+
// The wait is bounded here, and again around the lane in the stance. Two
|
|
293
|
+
// bounds and not one, because they end two different things: the stance's
|
|
294
|
+
// ends the wait a being is held in, and this one ends the occupancy of the
|
|
295
|
+
// relation's lane. A being holds three answers and a wait that does not
|
|
296
|
+
// end is none of them, and a lane nobody ever leaves is a relation the
|
|
297
|
+
// next ask never reaches. Only a bound on the wire breaks the second, and
|
|
298
|
+
// taking it out would leave one quiet far side holding the lane for good.
|
|
299
|
+
// What comes back late is not read.
|
|
296
300
|
//
|
|
297
301
|
// A wait that ran out is `late`, never unreached. Unreached promises
|
|
298
302
|
// nothing was delivered and is safe to retry; a bound that expired knows
|
|
File without changes
|
|
File without changes
|
|
File without changes
|