cojson 0.0.6 → 0.0.8
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/.eslintrc.cjs +11 -8
- package/dist/coValue.d.ts +97 -0
- package/dist/coValue.js +341 -401
- package/dist/coValue.js.map +1 -0
- package/dist/coValue.test.d.ts +1 -0
- package/dist/coValue.test.js +69 -113
- package/dist/coValue.test.js.map +1 -0
- package/dist/contentType.d.ts +15 -0
- package/dist/contentType.js +5 -5
- package/dist/contentType.js.map +1 -0
- package/dist/contentType.test.d.ts +1 -0
- package/dist/contentType.test.js +138 -168
- package/dist/contentType.test.js.map +1 -0
- package/dist/contentTypes/coList.d.ts +11 -0
- package/dist/contentTypes/coList.js +14 -16
- package/dist/contentTypes/coList.js.map +1 -0
- package/dist/contentTypes/coMap.d.ts +56 -0
- package/dist/contentTypes/coMap.js +112 -112
- package/dist/contentTypes/coMap.js.map +1 -0
- package/dist/contentTypes/coStream.d.ts +11 -0
- package/dist/contentTypes/coStream.js +14 -16
- package/dist/contentTypes/coStream.js.map +1 -0
- package/dist/contentTypes/static.d.ts +11 -0
- package/dist/contentTypes/static.js +12 -14
- package/dist/contentTypes/static.js.map +1 -0
- package/dist/crypto.d.ts +97 -0
- package/dist/crypto.js +100 -151
- package/dist/crypto.js.map +1 -0
- package/dist/crypto.test.d.ts +1 -0
- package/dist/crypto.test.js +94 -134
- package/dist/crypto.test.js.map +1 -0
- package/dist/ids.d.ts +7 -0
- package/dist/ids.js +2 -1
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +10 -18
- package/dist/index.js.map +1 -0
- package/dist/jsonValue.d.ts +7 -0
- package/dist/jsonValue.js +2 -1
- package/dist/jsonValue.js.map +1 -0
- package/dist/node.d.ts +33 -0
- package/dist/node.js +102 -133
- package/dist/node.js.map +1 -0
- package/dist/permissions.d.ts +54 -0
- package/dist/permissions.js +202 -228
- package/dist/permissions.js.map +1 -0
- package/dist/permissions.test.d.ts +1 -0
- package/dist/permissions.test.js +724 -915
- package/dist/permissions.test.js.map +1 -0
- package/dist/sync.d.ts +80 -0
- package/dist/sync.js +247 -294
- package/dist/sync.js.map +1 -0
- package/dist/sync.test.d.ts +1 -0
- package/dist/sync.test.js +763 -798
- package/dist/sync.test.js.map +1 -0
- package/package.json +7 -4
- package/src/coValue.test.ts +3 -4
- package/src/coValue.ts +11 -17
- package/src/contentType.test.ts +3 -3
- package/src/contentType.ts +6 -6
- package/src/contentTypes/coList.ts +4 -4
- package/src/contentTypes/coMap.ts +6 -6
- package/src/contentTypes/coStream.ts +4 -4
- package/src/contentTypes/static.ts +5 -5
- package/src/crypto.test.ts +1 -1
- package/src/crypto.ts +2 -2
- package/src/index.ts +9 -7
- package/src/jsonValue.ts +1 -1
- package/src/node.ts +6 -7
- package/src/permissions.test.ts +5 -5
- package/src/permissions.ts +7 -7
- package/src/sync.test.ts +7 -7
- package/src/sync.ts +6 -6
- package/tsconfig.json +1 -7
package/dist/coValue.js
CHANGED
|
@@ -1,441 +1,381 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
import { randomBytes } from "@noble/hashes/utils";
|
|
3
|
-
import { Static } from
|
|
4
|
-
import { CoStream } from
|
|
5
|
-
import { CoMap } from
|
|
6
|
-
import {
|
|
7
|
-
StreamingHash,
|
|
8
|
-
getRecipientID,
|
|
9
|
-
getSignatoryID,
|
|
10
|
-
newRandomRecipient,
|
|
11
|
-
newRandomSignatory,
|
|
12
|
-
openAs,
|
|
13
|
-
shortHash,
|
|
14
|
-
sign,
|
|
15
|
-
verify,
|
|
16
|
-
encryptForTransaction,
|
|
17
|
-
decryptForTransaction,
|
|
18
|
-
unsealKeySecret,
|
|
19
|
-
signatorySecretToBytes,
|
|
20
|
-
recipientSecretToBytes,
|
|
21
|
-
signatorySecretFromBytes,
|
|
22
|
-
recipientSecretFromBytes
|
|
23
|
-
} from "./crypto";
|
|
2
|
+
import { Static } from './contentTypes/static.js';
|
|
3
|
+
import { CoStream } from './contentTypes/coStream.js';
|
|
4
|
+
import { CoMap } from './contentTypes/coMap.js';
|
|
5
|
+
import { StreamingHash, getRecipientID, getSignatoryID, newRandomRecipient, newRandomSignatory, openAs, shortHash, sign, verify, encryptForTransaction, decryptForTransaction, unsealKeySecret, signatorySecretToBytes, recipientSecretToBytes, signatorySecretFromBytes, recipientSecretFromBytes, } from './crypto.js';
|
|
24
6
|
import { base58 } from "@scure/base";
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
determineValidTransactions,
|
|
28
|
-
expectTeamContent
|
|
29
|
-
} from "./permissions";
|
|
30
|
-
import { CoList } from "./contentTypes/coList";
|
|
7
|
+
import { Team, determineValidTransactions, expectTeamContent, } from './permissions.js';
|
|
8
|
+
import { CoList } from './contentTypes/coList.js';
|
|
31
9
|
function coValueIDforHeader(header) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
10
|
+
const hash = shortHash(header);
|
|
11
|
+
if (header.publicNickname) {
|
|
12
|
+
return `co_${header.publicNickname}_z${hash.slice("shortHash_z".length)}`;
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
return `co_z${hash.slice("shortHash_z".length)}`;
|
|
16
|
+
}
|
|
40
17
|
}
|
|
41
18
|
export function agentIDfromSessionID(sessionID) {
|
|
42
|
-
|
|
19
|
+
return sessionID.split("_session")[0];
|
|
43
20
|
}
|
|
44
21
|
export function newRandomSessionID(agentID) {
|
|
45
|
-
|
|
22
|
+
return `${agentID}_session_z${base58.encode(randomBytes(8))}`;
|
|
46
23
|
}
|
|
47
24
|
export class CoValue {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
constructor(header, node) {
|
|
55
|
-
this.id = coValueIDforHeader(header);
|
|
56
|
-
this.header = header;
|
|
57
|
-
this.sessions = {};
|
|
58
|
-
this.node = node;
|
|
59
|
-
}
|
|
60
|
-
testWithDifferentCredentials(agentCredential, ownSessionID) {
|
|
61
|
-
const newNode = this.node.testWithDifferentCredentials(
|
|
62
|
-
agentCredential,
|
|
63
|
-
ownSessionID
|
|
64
|
-
);
|
|
65
|
-
return newNode.expectCoValueLoaded(this.id);
|
|
66
|
-
}
|
|
67
|
-
knownState() {
|
|
68
|
-
return {
|
|
69
|
-
coValueID: this.id,
|
|
70
|
-
header: true,
|
|
71
|
-
sessions: Object.fromEntries(
|
|
72
|
-
Object.entries(this.sessions).map(([k, v]) => [
|
|
73
|
-
k,
|
|
74
|
-
v.transactions.length
|
|
75
|
-
])
|
|
76
|
-
)
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
get meta() {
|
|
80
|
-
var _a;
|
|
81
|
-
return ((_a = this.header) == null ? void 0 : _a.meta) ?? null;
|
|
82
|
-
}
|
|
83
|
-
nextTransactionID() {
|
|
84
|
-
var _a;
|
|
85
|
-
const sessionID = this.node.ownSessionID;
|
|
86
|
-
return {
|
|
87
|
-
sessionID,
|
|
88
|
-
txIndex: ((_a = this.sessions[sessionID]) == null ? void 0 : _a.transactions.length) || 0
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
tryAddTransactions(sessionID, newTransactions, newHash, newSignature) {
|
|
92
|
-
var _a;
|
|
93
|
-
const signatoryID = this.node.expectAgentLoaded(
|
|
94
|
-
agentIDfromSessionID(sessionID),
|
|
95
|
-
"Expected to know signatory of transaction"
|
|
96
|
-
).signatoryID;
|
|
97
|
-
if (!signatoryID) {
|
|
98
|
-
console.warn("Unknown agent", agentIDfromSessionID(sessionID));
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(
|
|
102
|
-
sessionID,
|
|
103
|
-
newTransactions
|
|
104
|
-
);
|
|
105
|
-
if (newHash !== expectedNewHash) {
|
|
106
|
-
console.warn("Invalid hash", { newHash, expectedNewHash });
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
if (!verify(newSignature, newHash, signatoryID)) {
|
|
110
|
-
console.warn(
|
|
111
|
-
"Invalid signature",
|
|
112
|
-
newSignature,
|
|
113
|
-
newHash,
|
|
114
|
-
signatoryID
|
|
115
|
-
);
|
|
116
|
-
return false;
|
|
25
|
+
constructor(header, node) {
|
|
26
|
+
this.listeners = new Set();
|
|
27
|
+
this.id = coValueIDforHeader(header);
|
|
28
|
+
this.header = header;
|
|
29
|
+
this.sessions = {};
|
|
30
|
+
this.node = node;
|
|
117
31
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
this.sessions[sessionID] = {
|
|
122
|
-
transactions,
|
|
123
|
-
lastHash: newHash,
|
|
124
|
-
streamingHash: newStreamingHash,
|
|
125
|
-
lastSignature: newSignature
|
|
126
|
-
};
|
|
127
|
-
this.content = void 0;
|
|
128
|
-
console.log("transactions after", this.id, transactions.length, this.getValidSortedTransactions().length);
|
|
129
|
-
const content = this.getCurrentContent();
|
|
130
|
-
for (const listener of this.listeners) {
|
|
131
|
-
console.log("Calling listener (update)", this.id, content.toJSON());
|
|
132
|
-
listener(content);
|
|
32
|
+
testWithDifferentCredentials(agentCredential, ownSessionID) {
|
|
33
|
+
const newNode = this.node.testWithDifferentCredentials(agentCredential, ownSessionID);
|
|
34
|
+
return newNode.expectCoValueLoaded(this.id);
|
|
133
35
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
expectedNewHashAfter(sessionID, newTransactions) {
|
|
145
|
-
var _a;
|
|
146
|
-
const streamingHash = ((_a = this.sessions[sessionID]) == null ? void 0 : _a.streamingHash.clone()) ?? new StreamingHash();
|
|
147
|
-
for (const transaction of newTransactions) {
|
|
148
|
-
streamingHash.update(transaction);
|
|
36
|
+
knownState() {
|
|
37
|
+
return {
|
|
38
|
+
coValueID: this.id,
|
|
39
|
+
header: true,
|
|
40
|
+
sessions: Object.fromEntries(Object.entries(this.sessions).map(([k, v]) => [
|
|
41
|
+
k,
|
|
42
|
+
v.transactions.length,
|
|
43
|
+
])),
|
|
44
|
+
};
|
|
149
45
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
expectedNewHash: streamingHash.digest(),
|
|
153
|
-
newStreamingHash
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
makeTransaction(changes, privacy) {
|
|
157
|
-
const madeAt = Date.now();
|
|
158
|
-
let transaction;
|
|
159
|
-
if (privacy === "private") {
|
|
160
|
-
const { secret: keySecret, id: keyID } = this.getCurrentReadKey();
|
|
161
|
-
if (!keySecret) {
|
|
162
|
-
throw new Error(
|
|
163
|
-
"Can't make transaction without read key secret"
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
transaction = {
|
|
167
|
-
privacy: "private",
|
|
168
|
-
madeAt,
|
|
169
|
-
keyUsed: keyID,
|
|
170
|
-
encryptedChanges: encryptForTransaction(changes, keySecret, {
|
|
171
|
-
in: this.id,
|
|
172
|
-
tx: this.nextTransactionID()
|
|
173
|
-
})
|
|
174
|
-
};
|
|
175
|
-
} else {
|
|
176
|
-
transaction = {
|
|
177
|
-
privacy: "trusting",
|
|
178
|
-
madeAt,
|
|
179
|
-
changes
|
|
180
|
-
};
|
|
46
|
+
get meta() {
|
|
47
|
+
return this.header?.meta ?? null;
|
|
181
48
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
expectedNewHash
|
|
189
|
-
);
|
|
190
|
-
const success = this.tryAddTransactions(
|
|
191
|
-
sessionID,
|
|
192
|
-
[transaction],
|
|
193
|
-
expectedNewHash,
|
|
194
|
-
signature
|
|
195
|
-
);
|
|
196
|
-
if (success) {
|
|
197
|
-
void this.node.sync.syncCoValue(this);
|
|
49
|
+
nextTransactionID() {
|
|
50
|
+
const sessionID = this.node.ownSessionID;
|
|
51
|
+
return {
|
|
52
|
+
sessionID,
|
|
53
|
+
txIndex: this.sessions[sessionID]?.transactions.length || 0,
|
|
54
|
+
};
|
|
198
55
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
56
|
+
tryAddTransactions(sessionID, newTransactions, newHash, newSignature) {
|
|
57
|
+
const signatoryID = this.node.expectAgentLoaded(agentIDfromSessionID(sessionID), "Expected to know signatory of transaction").signatoryID;
|
|
58
|
+
if (!signatoryID) {
|
|
59
|
+
console.warn("Unknown agent", agentIDfromSessionID(sessionID));
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(sessionID, newTransactions);
|
|
63
|
+
if (newHash !== expectedNewHash) {
|
|
64
|
+
console.warn("Invalid hash", { newHash, expectedNewHash });
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (!verify(newSignature, newHash, signatoryID)) {
|
|
68
|
+
console.warn("Invalid signature", newSignature, newHash, signatoryID);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const transactions = this.sessions[sessionID]?.transactions ?? [];
|
|
72
|
+
transactions.push(...newTransactions);
|
|
73
|
+
this.sessions[sessionID] = {
|
|
74
|
+
transactions,
|
|
75
|
+
lastHash: newHash,
|
|
76
|
+
streamingHash: newStreamingHash,
|
|
77
|
+
lastSignature: newSignature,
|
|
78
|
+
};
|
|
79
|
+
this.content = undefined;
|
|
80
|
+
const content = this.getCurrentContent();
|
|
81
|
+
for (const listener of this.listeners) {
|
|
82
|
+
listener(content);
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
204
85
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
} else if (this.header.type === "static") {
|
|
212
|
-
this.content = new Static(this);
|
|
213
|
-
} else {
|
|
214
|
-
throw new Error(`Unknown coValue type ${this.header.type}`);
|
|
86
|
+
subscribe(listener) {
|
|
87
|
+
this.listeners.add(listener);
|
|
88
|
+
listener(this.getCurrentContent());
|
|
89
|
+
return () => {
|
|
90
|
+
this.listeners.delete(listener);
|
|
91
|
+
};
|
|
215
92
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
93
|
+
expectedNewHashAfter(sessionID, newTransactions) {
|
|
94
|
+
const streamingHash = this.sessions[sessionID]?.streamingHash.clone() ??
|
|
95
|
+
new StreamingHash();
|
|
96
|
+
for (const transaction of newTransactions) {
|
|
97
|
+
streamingHash.update(transaction);
|
|
98
|
+
}
|
|
99
|
+
const newStreamingHash = streamingHash.clone();
|
|
222
100
|
return {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
changes: tx.changes
|
|
101
|
+
expectedNewHash: streamingHash.digest(),
|
|
102
|
+
newStreamingHash,
|
|
226
103
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
{
|
|
236
|
-
in: this.id,
|
|
237
|
-
tx: txID
|
|
104
|
+
}
|
|
105
|
+
makeTransaction(changes, privacy) {
|
|
106
|
+
const madeAt = Date.now();
|
|
107
|
+
let transaction;
|
|
108
|
+
if (privacy === "private") {
|
|
109
|
+
const { secret: keySecret, id: keyID } = this.getCurrentReadKey();
|
|
110
|
+
if (!keySecret) {
|
|
111
|
+
throw new Error("Can't make transaction without read key secret");
|
|
238
112
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
113
|
+
transaction = {
|
|
114
|
+
privacy: "private",
|
|
115
|
+
madeAt,
|
|
116
|
+
keyUsed: keyID,
|
|
117
|
+
encryptedChanges: encryptForTransaction(changes, keySecret, {
|
|
118
|
+
in: this.id,
|
|
119
|
+
tx: this.nextTransactionID(),
|
|
120
|
+
}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
transaction = {
|
|
125
|
+
privacy: "trusting",
|
|
126
|
+
madeAt,
|
|
127
|
+
changes,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const sessionID = this.node.ownSessionID;
|
|
131
|
+
const { expectedNewHash } = this.expectedNewHashAfter(sessionID, [
|
|
132
|
+
transaction,
|
|
133
|
+
]);
|
|
134
|
+
const signature = sign(this.node.agentCredential.signatorySecret, expectedNewHash);
|
|
135
|
+
const success = this.tryAddTransactions(sessionID, [transaction], expectedNewHash, signature);
|
|
136
|
+
if (success) {
|
|
137
|
+
void this.node.sync.syncCoValue(this);
|
|
251
138
|
}
|
|
252
|
-
|
|
253
|
-
}).filter((x) => !!x);
|
|
254
|
-
allTransactions.sort(
|
|
255
|
-
(a, b) => a.madeAt - b.madeAt || (a.txID.sessionID < b.txID.sessionID ? -1 : 1) || a.txID.txIndex - b.txID.txIndex
|
|
256
|
-
);
|
|
257
|
-
return allTransactions;
|
|
258
|
-
}
|
|
259
|
-
getCurrentReadKey() {
|
|
260
|
-
var _a;
|
|
261
|
-
if (this.header.ruleset.type === "team") {
|
|
262
|
-
const content = expectTeamContent(this.getCurrentContent());
|
|
263
|
-
const currentKeyId = (_a = content.get("readKey")) == null ? void 0 : _a.keyID;
|
|
264
|
-
if (!currentKeyId) {
|
|
265
|
-
throw new Error("No readKey set");
|
|
266
|
-
}
|
|
267
|
-
const secret = this.getReadKey(currentKeyId);
|
|
268
|
-
return {
|
|
269
|
-
secret,
|
|
270
|
-
id: currentKeyId
|
|
271
|
-
};
|
|
272
|
-
} else if (this.header.ruleset.type === "ownedByTeam") {
|
|
273
|
-
return this.node.expectCoValueLoaded(this.header.ruleset.team).getCurrentReadKey();
|
|
274
|
-
} else {
|
|
275
|
-
throw new Error(
|
|
276
|
-
"Only teams or values owned by teams have read secrets"
|
|
277
|
-
);
|
|
139
|
+
return success;
|
|
278
140
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
|
|
288
|
-
const revealerAgent = this.node.expectAgentLoaded(
|
|
289
|
-
revealer,
|
|
290
|
-
"Expected to know revealer"
|
|
291
|
-
);
|
|
292
|
-
const secret = openAs(
|
|
293
|
-
entry.value.revelation,
|
|
294
|
-
this.node.agentCredential.recipientSecret,
|
|
295
|
-
revealerAgent.recipientID,
|
|
296
|
-
{
|
|
297
|
-
in: this.id,
|
|
298
|
-
tx: entry.txID
|
|
299
|
-
}
|
|
300
|
-
);
|
|
301
|
-
if (secret)
|
|
302
|
-
return secret;
|
|
141
|
+
getCurrentContent() {
|
|
142
|
+
if (this.content) {
|
|
143
|
+
return this.content;
|
|
144
|
+
}
|
|
145
|
+
if (this.header.type === "comap") {
|
|
146
|
+
this.content = new CoMap(this);
|
|
147
|
+
}
|
|
148
|
+
else if (this.header.type === "colist") {
|
|
149
|
+
this.content = new CoList(this);
|
|
303
150
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const encryptedPreviousKey = (_c = (_b = entry.value) == null ? void 0 : _b.previousKeys) == null ? void 0 : _c[keyID];
|
|
307
|
-
if (entry.value && encryptedPreviousKey) {
|
|
308
|
-
const sealingKeyID = entry.value.keyID;
|
|
309
|
-
const sealingKeySecret = this.getReadKey(sealingKeyID);
|
|
310
|
-
if (!sealingKeySecret) {
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
const secret = unsealKeySecret(
|
|
314
|
-
{
|
|
315
|
-
sealed: keyID,
|
|
316
|
-
sealing: sealingKeyID,
|
|
317
|
-
encrypted: encryptedPreviousKey
|
|
318
|
-
},
|
|
319
|
-
sealingKeySecret
|
|
320
|
-
);
|
|
321
|
-
if (secret) {
|
|
322
|
-
return secret;
|
|
323
|
-
} else {
|
|
324
|
-
console.error(
|
|
325
|
-
`Sealing ${sealingKeyID} key didn't unseal ${keyID}`
|
|
326
|
-
);
|
|
327
|
-
}
|
|
151
|
+
else if (this.header.type === "costream") {
|
|
152
|
+
this.content = new CoStream(this);
|
|
328
153
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
);
|
|
154
|
+
else if (this.header.type === "static") {
|
|
155
|
+
this.content = new Static(this);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
throw new Error(`Unknown coValue type ${this.header.type}`);
|
|
159
|
+
}
|
|
160
|
+
return this.content;
|
|
337
161
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
162
|
+
getValidSortedTransactions() {
|
|
163
|
+
const validTransactions = determineValidTransactions(this);
|
|
164
|
+
const allTransactions = validTransactions
|
|
165
|
+
.map(({ txID, tx }) => {
|
|
166
|
+
if (tx.privacy === "trusting") {
|
|
167
|
+
return {
|
|
168
|
+
txID,
|
|
169
|
+
madeAt: tx.madeAt,
|
|
170
|
+
changes: tx.changes,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const readKey = this.getReadKey(tx.keyUsed);
|
|
175
|
+
if (!readKey) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const decrytedChanges = decryptForTransaction(tx.encryptedChanges, readKey, {
|
|
180
|
+
in: this.id,
|
|
181
|
+
tx: txID,
|
|
182
|
+
});
|
|
183
|
+
if (!decrytedChanges) {
|
|
184
|
+
console.error("Failed to decrypt transaction despite having key");
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
txID,
|
|
189
|
+
madeAt: tx.madeAt,
|
|
190
|
+
changes: decrytedChanges,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
.filter((x) => !!x);
|
|
196
|
+
allTransactions.sort((a, b) => a.madeAt - b.madeAt ||
|
|
197
|
+
(a.txID.sessionID < b.txID.sessionID ? -1 : 1) ||
|
|
198
|
+
a.txID.txIndex - b.txID.txIndex);
|
|
199
|
+
return allTransactions;
|
|
342
200
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
getTx(txID) {
|
|
351
|
-
var _a;
|
|
352
|
-
return (_a = this.sessions[txID.sessionID]) == null ? void 0 : _a.transactions[txID.txIndex];
|
|
353
|
-
}
|
|
354
|
-
newContentSince(knownState) {
|
|
355
|
-
const newContent = {
|
|
356
|
-
action: "newContent",
|
|
357
|
-
coValueID: this.id,
|
|
358
|
-
header: (knownState == null ? void 0 : knownState.header) ? void 0 : this.header,
|
|
359
|
-
newContent: Object.fromEntries(
|
|
360
|
-
Object.entries(this.sessions).map(([sessionID, log]) => {
|
|
361
|
-
const newTransactions = log.transactions.slice(
|
|
362
|
-
(knownState == null ? void 0 : knownState.sessions[sessionID]) || 0
|
|
363
|
-
);
|
|
364
|
-
if (newTransactions.length === 0 || !log.lastHash || !log.lastSignature) {
|
|
365
|
-
return void 0;
|
|
366
|
-
}
|
|
367
|
-
return [
|
|
368
|
-
sessionID,
|
|
369
|
-
{
|
|
370
|
-
after: (knownState == null ? void 0 : knownState.sessions[sessionID]) || 0,
|
|
371
|
-
newTransactions,
|
|
372
|
-
lastHash: log.lastHash,
|
|
373
|
-
lastSignature: log.lastSignature
|
|
201
|
+
getCurrentReadKey() {
|
|
202
|
+
if (this.header.ruleset.type === "team") {
|
|
203
|
+
const content = expectTeamContent(this.getCurrentContent());
|
|
204
|
+
const currentKeyId = content.get("readKey")?.keyID;
|
|
205
|
+
if (!currentKeyId) {
|
|
206
|
+
throw new Error("No readKey set");
|
|
374
207
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
208
|
+
const secret = this.getReadKey(currentKeyId);
|
|
209
|
+
return {
|
|
210
|
+
secret: secret,
|
|
211
|
+
id: currentKeyId,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
else if (this.header.ruleset.type === "ownedByTeam") {
|
|
215
|
+
return this.node
|
|
216
|
+
.expectCoValueLoaded(this.header.ruleset.team)
|
|
217
|
+
.getCurrentReadKey();
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
throw new Error("Only teams or values owned by teams have read secrets");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
getReadKey(keyID) {
|
|
224
|
+
if (this.header.ruleset.type === "team") {
|
|
225
|
+
const content = expectTeamContent(this.getCurrentContent());
|
|
226
|
+
const readKeyHistory = content.getHistory("readKey");
|
|
227
|
+
// Try to find direct relevation of key for us
|
|
228
|
+
for (const entry of readKeyHistory) {
|
|
229
|
+
if (entry.value?.keyID === keyID) {
|
|
230
|
+
const revealer = agentIDfromSessionID(entry.txID.sessionID);
|
|
231
|
+
const revealerAgent = this.node.expectAgentLoaded(revealer, "Expected to know revealer");
|
|
232
|
+
const secret = openAs(entry.value.revelation, this.node.agentCredential.recipientSecret, revealerAgent.recipientID, {
|
|
233
|
+
in: this.id,
|
|
234
|
+
tx: entry.txID,
|
|
235
|
+
});
|
|
236
|
+
if (secret)
|
|
237
|
+
return secret;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// Try to find indirect revelation through previousKeys
|
|
241
|
+
for (const entry of readKeyHistory) {
|
|
242
|
+
const encryptedPreviousKey = entry.value?.previousKeys?.[keyID];
|
|
243
|
+
if (entry.value && encryptedPreviousKey) {
|
|
244
|
+
const sealingKeyID = entry.value.keyID;
|
|
245
|
+
const sealingKeySecret = this.getReadKey(sealingKeyID);
|
|
246
|
+
if (!sealingKeySecret) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const secret = unsealKeySecret({
|
|
250
|
+
sealed: keyID,
|
|
251
|
+
sealing: sealingKeyID,
|
|
252
|
+
encrypted: encryptedPreviousKey,
|
|
253
|
+
}, sealingKeySecret);
|
|
254
|
+
if (secret) {
|
|
255
|
+
return secret;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
console.error(`Sealing ${sealingKeyID} key didn't unseal ${keyID}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return undefined;
|
|
263
|
+
}
|
|
264
|
+
else if (this.header.ruleset.type === "ownedByTeam") {
|
|
265
|
+
return this.node
|
|
266
|
+
.expectCoValueLoaded(this.header.ruleset.team)
|
|
267
|
+
.getReadKey(keyID);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
throw new Error("Only teams or values owned by teams have read secrets");
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
getTeam() {
|
|
274
|
+
if (this.header.ruleset.type !== "ownedByTeam") {
|
|
275
|
+
throw new Error("Only values owned by teams have teams");
|
|
276
|
+
}
|
|
277
|
+
return new Team(expectTeamContent(this.node
|
|
278
|
+
.expectCoValueLoaded(this.header.ruleset.team)
|
|
279
|
+
.getCurrentContent()), this.node);
|
|
280
|
+
}
|
|
281
|
+
getTx(txID) {
|
|
282
|
+
return this.sessions[txID.sessionID]?.transactions[txID.txIndex];
|
|
283
|
+
}
|
|
284
|
+
newContentSince(knownState) {
|
|
285
|
+
const newContent = {
|
|
286
|
+
action: "newContent",
|
|
287
|
+
coValueID: this.id,
|
|
288
|
+
header: knownState?.header ? undefined : this.header,
|
|
289
|
+
newContent: Object.fromEntries(Object.entries(this.sessions)
|
|
290
|
+
.map(([sessionID, log]) => {
|
|
291
|
+
const newTransactions = log.transactions.slice(knownState?.sessions[sessionID] || 0);
|
|
292
|
+
if (newTransactions.length === 0 ||
|
|
293
|
+
!log.lastHash ||
|
|
294
|
+
!log.lastSignature) {
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
return [
|
|
298
|
+
sessionID,
|
|
299
|
+
{
|
|
300
|
+
after: knownState?.sessions[sessionID] || 0,
|
|
301
|
+
newTransactions,
|
|
302
|
+
lastHash: log.lastHash,
|
|
303
|
+
lastSignature: log.lastSignature,
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
})
|
|
307
|
+
.filter((x) => !!x)),
|
|
308
|
+
};
|
|
309
|
+
if (!newContent.header &&
|
|
310
|
+
Object.keys(newContent.newContent).length === 0) {
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
return newContent;
|
|
314
|
+
}
|
|
315
|
+
getDependedOnCoValues() {
|
|
316
|
+
return this.header.ruleset.type === "team"
|
|
317
|
+
? expectTeamContent(this.getCurrentContent())
|
|
318
|
+
.keys()
|
|
319
|
+
.filter((k) => k.startsWith("co_agent"))
|
|
320
|
+
: this.header.ruleset.type === "ownedByTeam"
|
|
321
|
+
? [this.header.ruleset.team]
|
|
322
|
+
: [];
|
|
381
323
|
}
|
|
382
|
-
return newContent;
|
|
383
|
-
}
|
|
384
|
-
getDependedOnCoValues() {
|
|
385
|
-
return this.header.ruleset.type === "team" ? expectTeamContent(this.getCurrentContent()).keys().filter((k) => k.startsWith("co_agent")) : this.header.ruleset.type === "ownedByTeam" ? [this.header.ruleset.team] : [];
|
|
386
|
-
}
|
|
387
324
|
}
|
|
388
325
|
export function getAgent(agentCredential) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
326
|
+
return {
|
|
327
|
+
signatoryID: getSignatoryID(agentCredential.signatorySecret),
|
|
328
|
+
recipientID: getRecipientID(agentCredential.recipientSecret),
|
|
329
|
+
publicNickname: agentCredential.publicNickname,
|
|
330
|
+
};
|
|
394
331
|
}
|
|
395
332
|
export function getAgentCoValueHeader(agent) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
333
|
+
return {
|
|
334
|
+
type: "comap",
|
|
335
|
+
ruleset: {
|
|
336
|
+
type: "agent",
|
|
337
|
+
initialSignatoryID: agent.signatoryID,
|
|
338
|
+
initialRecipientID: agent.recipientID,
|
|
339
|
+
},
|
|
340
|
+
meta: null,
|
|
341
|
+
createdAt: null,
|
|
342
|
+
uniqueness: null,
|
|
343
|
+
publicNickname: "agent" + (agent.publicNickname ? `-${agent.publicNickname}` : ""),
|
|
344
|
+
};
|
|
408
345
|
}
|
|
409
346
|
export function getAgentID(agent) {
|
|
410
|
-
|
|
347
|
+
return coValueIDforHeader(getAgentCoValueHeader(agent));
|
|
411
348
|
}
|
|
412
349
|
export function newRandomAgentCredential(publicNickname) {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
350
|
+
const signatorySecret = newRandomSignatory();
|
|
351
|
+
const recipientSecret = newRandomRecipient();
|
|
352
|
+
return { signatorySecret, recipientSecret, publicNickname };
|
|
416
353
|
}
|
|
417
354
|
export function agentCredentialToBytes(cred) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
355
|
+
if (cred.publicNickname) {
|
|
356
|
+
throw new Error("Can't convert agent credential with publicNickname");
|
|
357
|
+
}
|
|
358
|
+
const bytes = new Uint8Array(64);
|
|
359
|
+
const signatorySecretBytes = signatorySecretToBytes(cred.signatorySecret);
|
|
360
|
+
if (signatorySecretBytes.length !== 32) {
|
|
361
|
+
throw new Error("Invalid signatorySecret length");
|
|
362
|
+
}
|
|
363
|
+
bytes.set(signatorySecretBytes);
|
|
364
|
+
const recipientSecretBytes = recipientSecretToBytes(cred.recipientSecret);
|
|
365
|
+
if (recipientSecretBytes.length !== 32) {
|
|
366
|
+
throw new Error("Invalid recipientSecret length");
|
|
367
|
+
}
|
|
368
|
+
bytes.set(recipientSecretBytes, 32);
|
|
369
|
+
return bytes;
|
|
433
370
|
}
|
|
434
371
|
export function agentCredentialFromBytes(bytes) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
372
|
+
if (bytes.length !== 64) {
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
const signatorySecret = signatorySecretFromBytes(bytes.slice(0, 32));
|
|
376
|
+
const recipientSecret = recipientSecretFromBytes(bytes.slice(32));
|
|
377
|
+
return { signatorySecret, recipientSecret };
|
|
441
378
|
}
|
|
379
|
+
// type Role = "admin" | "writer" | "reader";
|
|
380
|
+
// type PermissionsDef = CJMap<AgentID, Role, {[agent: AgentID]: Role}>;
|
|
381
|
+
//# sourceMappingURL=coValue.js.map
|