cojson 0.8.0 → 0.8.5
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/.turbo/turbo-build.log +11 -3
- package/CHANGELOG.md +13 -0
- package/dist/native/PeerState.js.map +1 -0
- package/dist/native/PriorityBasedMessageQueue.js.map +1 -0
- package/dist/native/base64url.js.map +1 -0
- package/dist/native/base64url.test.js.map +1 -0
- package/dist/native/coValue.js.map +1 -0
- package/dist/native/coValueCore.js.map +1 -0
- package/dist/native/coValues/account.js.map +1 -0
- package/dist/native/coValues/coList.js.map +1 -0
- package/dist/native/coValues/coMap.js.map +1 -0
- package/dist/native/coValues/coStream.js.map +1 -0
- package/dist/native/coValues/group.js.map +1 -0
- package/dist/native/coreToCoValue.js.map +1 -0
- package/dist/{crypto → native/crypto}/PureJSCrypto.js +4 -0
- package/dist/native/crypto/PureJSCrypto.js.map +1 -0
- package/dist/{crypto → native/crypto}/crypto.js +1 -1
- package/dist/native/crypto/crypto.js.map +1 -0
- package/dist/native/ids.js.map +1 -0
- package/dist/{index.js → native/index.native.js} +3 -4
- package/dist/native/index.native.js.map +1 -0
- package/dist/native/jsonStringify.js.map +1 -0
- package/dist/{jsonValue.js.map → native/jsonValue.js.map} +1 -1
- package/dist/{localNode.js → native/localNode.js} +50 -33
- package/dist/native/localNode.js.map +1 -0
- package/dist/native/media.js.map +1 -0
- package/dist/native/permissions.js.map +1 -0
- package/dist/native/priority.js.map +1 -0
- package/dist/native/storage/FileSystem.js.map +1 -0
- package/dist/{storage → native/storage}/chunksAndKnownStates.js +1 -1
- package/dist/native/storage/chunksAndKnownStates.js.map +1 -0
- package/dist/native/storage/index.js.map +1 -0
- package/dist/native/streamUtils.js.map +1 -0
- package/dist/native/sync.js.map +1 -0
- package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -0
- package/dist/native/typeUtils/expectGroup.js.map +1 -0
- package/dist/native/typeUtils/isAccountID.js.map +1 -0
- package/dist/native/typeUtils/isCoValue.js.map +1 -0
- package/dist/web/PeerState.js +66 -0
- package/dist/web/PeerState.js.map +1 -0
- package/dist/web/PriorityBasedMessageQueue.js +51 -0
- package/dist/web/PriorityBasedMessageQueue.js.map +1 -0
- package/dist/web/base64url.js +59 -0
- package/dist/web/base64url.js.map +1 -0
- package/dist/web/base64url.test.js +25 -0
- package/dist/web/base64url.test.js.map +1 -0
- package/dist/web/coValue.js +19 -0
- package/dist/web/coValue.js.map +1 -0
- package/dist/web/coValueCore.js +693 -0
- package/dist/web/coValueCore.js.map +1 -0
- package/dist/web/coValues/account.js +96 -0
- package/dist/web/coValues/account.js.map +1 -0
- package/dist/web/coValues/coList.js +393 -0
- package/dist/web/coValues/coList.js.map +1 -0
- package/dist/web/coValues/coMap.js +197 -0
- package/dist/web/coValues/coMap.js.map +1 -0
- package/dist/web/coValues/coStream.js +220 -0
- package/dist/web/coValues/coStream.js.map +1 -0
- package/dist/web/coValues/group.js +250 -0
- package/dist/web/coValues/group.js.map +1 -0
- package/dist/web/coreToCoValue.js +42 -0
- package/dist/web/coreToCoValue.js.map +1 -0
- package/dist/web/crypto/PureJSCrypto.js +93 -0
- package/dist/web/crypto/PureJSCrypto.js.map +1 -0
- package/dist/{crypto → web/crypto}/WasmCrypto.js +3 -0
- package/dist/web/crypto/WasmCrypto.js.map +1 -0
- package/dist/web/crypto/crypto.js +154 -0
- package/dist/web/crypto/crypto.js.map +1 -0
- package/dist/web/ids.js +17 -0
- package/dist/web/ids.js.map +1 -0
- package/dist/web/index.web.js +40 -0
- package/dist/web/index.web.js.map +1 -0
- package/dist/web/jsonStringify.js +57 -0
- package/dist/web/jsonStringify.js.map +1 -0
- package/dist/web/jsonValue.js +2 -0
- package/dist/web/jsonValue.js.map +1 -0
- package/dist/web/localNode.js +453 -0
- package/dist/web/localNode.js.map +1 -0
- package/dist/web/media.js +2 -0
- package/dist/web/media.js.map +1 -0
- package/dist/web/permissions.js +206 -0
- package/dist/web/permissions.js.map +1 -0
- package/dist/web/priority.js +31 -0
- package/dist/web/priority.js.map +1 -0
- package/dist/web/storage/FileSystem.js +58 -0
- package/dist/web/storage/FileSystem.js.map +1 -0
- package/dist/web/storage/chunksAndKnownStates.js +100 -0
- package/dist/web/storage/chunksAndKnownStates.js.map +1 -0
- package/dist/web/storage/index.js +348 -0
- package/dist/web/storage/index.js.map +1 -0
- package/dist/web/streamUtils.js +41 -0
- package/dist/web/streamUtils.js.map +1 -0
- package/dist/web/sync.js +504 -0
- package/dist/web/sync.js.map +1 -0
- package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js +5 -0
- package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -0
- package/dist/web/typeUtils/expectGroup.js +13 -0
- package/dist/web/typeUtils/expectGroup.js.map +1 -0
- package/dist/web/typeUtils/isAccountID.js +4 -0
- package/dist/web/typeUtils/isAccountID.js.map +1 -0
- package/dist/web/typeUtils/isCoValue.js +11 -0
- package/dist/web/typeUtils/isCoValue.js.map +1 -0
- package/package.json +25 -8
- package/src/coValue.ts +1 -1
- package/src/coValueCore.ts +4 -1
- package/src/coValues/account.ts +9 -3
- package/src/crypto/PureJSCrypto.ts +5 -0
- package/src/crypto/WasmCrypto.ts +4 -0
- package/src/crypto/crypto.ts +7 -2
- package/src/{index.ts → index.native.ts} +4 -5
- package/src/index.web.ts +152 -0
- package/src/jsonValue.ts +25 -0
- package/src/localNode.ts +74 -55
- package/src/storage/chunksAndKnownStates.ts +1 -1
- package/src/storage/index.ts +1 -1
- package/src/tests/coList.test.ts +1 -1
- package/src/tests/coMap.test.ts +1 -1
- package/src/tests/coStream.test.ts +2 -1
- package/src/tests/cryptoImpl.test.ts +24 -2
- package/src/tests/group.test.ts +6 -8
- package/src/tests/permissions.test.ts +66 -18
- package/src/tests/priority.test.ts +44 -15
- package/tsconfig.json +5 -4
- package/tsconfig.native.json +12 -0
- package/tsconfig.web.json +11 -0
- package/dist/PeerState.js.map +0 -1
- package/dist/PriorityBasedMessageQueue.js.map +0 -1
- package/dist/base64url.js.map +0 -1
- package/dist/base64url.test.js.map +0 -1
- package/dist/coValue.js.map +0 -1
- package/dist/coValueCore.js.map +0 -1
- package/dist/coValues/account.js.map +0 -1
- package/dist/coValues/coList.js.map +0 -1
- package/dist/coValues/coMap.js.map +0 -1
- package/dist/coValues/coStream.js.map +0 -1
- package/dist/coValues/group.js.map +0 -1
- package/dist/coreToCoValue.js.map +0 -1
- package/dist/crypto/PureJSCrypto.js.map +0 -1
- package/dist/crypto/WasmCrypto.js.map +0 -1
- package/dist/crypto/crypto.js.map +0 -1
- package/dist/ids.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jsonStringify.js.map +0 -1
- package/dist/localNode.js.map +0 -1
- package/dist/media.js.map +0 -1
- package/dist/permissions.js.map +0 -1
- package/dist/priority.js.map +0 -1
- package/dist/storage/FileSystem.js.map +0 -1
- package/dist/storage/chunksAndKnownStates.js.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/streamUtils.js.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/tests/PeerState.test.js +0 -80
- package/dist/tests/PeerState.test.js.map +0 -1
- package/dist/tests/PriorityBasedMessageQueue.test.js +0 -97
- package/dist/tests/PriorityBasedMessageQueue.test.js.map +0 -1
- package/dist/tests/account.test.js +0 -58
- package/dist/tests/account.test.js.map +0 -1
- package/dist/tests/coList.test.js +0 -76
- package/dist/tests/coList.test.js.map +0 -1
- package/dist/tests/coMap.test.js +0 -136
- package/dist/tests/coMap.test.js.map +0 -1
- package/dist/tests/coStream.test.js +0 -205
- package/dist/tests/coStream.test.js.map +0 -1
- package/dist/tests/coValueCore.test.js +0 -124
- package/dist/tests/coValueCore.test.js.map +0 -1
- package/dist/tests/crypto.test.js +0 -118
- package/dist/tests/crypto.test.js.map +0 -1
- package/dist/tests/cryptoImpl.test.js +0 -113
- package/dist/tests/cryptoImpl.test.js.map +0 -1
- package/dist/tests/group.test.js +0 -34
- package/dist/tests/group.test.js.map +0 -1
- package/dist/tests/permissions.test.js +0 -1059
- package/dist/tests/permissions.test.js.map +0 -1
- package/dist/tests/priority.test.js +0 -61
- package/dist/tests/priority.test.js.map +0 -1
- package/dist/tests/sync.test.js +0 -1186
- package/dist/tests/sync.test.js.map +0 -1
- package/dist/tests/testUtils.js +0 -59
- package/dist/tests/testUtils.js.map +0 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +0 -1
- package/dist/typeUtils/expectGroup.js.map +0 -1
- package/dist/typeUtils/isAccountID.js.map +0 -1
- package/dist/typeUtils/isCoValue.js.map +0 -1
- /package/dist/{PeerState.js → native/PeerState.js} +0 -0
- /package/dist/{PriorityBasedMessageQueue.js → native/PriorityBasedMessageQueue.js} +0 -0
- /package/dist/{base64url.js → native/base64url.js} +0 -0
- /package/dist/{base64url.test.js → native/base64url.test.js} +0 -0
- /package/dist/{coValue.js → native/coValue.js} +0 -0
- /package/dist/{coValueCore.js → native/coValueCore.js} +0 -0
- /package/dist/{coValues → native/coValues}/account.js +0 -0
- /package/dist/{coValues → native/coValues}/coList.js +0 -0
- /package/dist/{coValues → native/coValues}/coMap.js +0 -0
- /package/dist/{coValues → native/coValues}/coStream.js +0 -0
- /package/dist/{coValues → native/coValues}/group.js +0 -0
- /package/dist/{coreToCoValue.js → native/coreToCoValue.js} +0 -0
- /package/dist/{ids.js → native/ids.js} +0 -0
- /package/dist/{jsonStringify.js → native/jsonStringify.js} +0 -0
- /package/dist/{jsonValue.js → native/jsonValue.js} +0 -0
- /package/dist/{media.js → native/media.js} +0 -0
- /package/dist/{permissions.js → native/permissions.js} +0 -0
- /package/dist/{priority.js → native/priority.js} +0 -0
- /package/dist/{storage → native/storage}/FileSystem.js +0 -0
- /package/dist/{storage → native/storage}/index.js +0 -0
- /package/dist/{streamUtils.js → native/streamUtils.js} +0 -0
- /package/dist/{sync.js → native/sync.js} +0 -0
- /package/dist/{typeUtils → native/typeUtils}/accountOrAgentIDfromSessionID.js +0 -0
- /package/dist/{typeUtils → native/typeUtils}/expectGroup.js +0 -0
- /package/dist/{typeUtils → native/typeUtils}/isAccountID.js +0 -0
- /package/dist/{typeUtils → native/typeUtils}/isCoValue.js +0 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
import { StreamingHash, } from "./crypto/crypto.js";
|
|
2
|
+
import { determineValidTransactions, isKeyForKeyField, } from "./permissions.js";
|
|
3
|
+
import { parseJSON, stableStringify } from "./jsonStringify.js";
|
|
4
|
+
import { coreToCoValue } from "./coreToCoValue.js";
|
|
5
|
+
import { expectGroup } from "./typeUtils/expectGroup.js";
|
|
6
|
+
import { isAccountID } from "./typeUtils/isAccountID.js";
|
|
7
|
+
import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
|
|
8
|
+
import { err, ok } from "neverthrow";
|
|
9
|
+
import { getPriorityFromHeader } from "./priority.js";
|
|
10
|
+
/**
|
|
11
|
+
In order to not block other concurrently syncing CoValues we introduce a maximum size of transactions,
|
|
12
|
+
since they are the smallest unit of progress that can be synced within a CoValue.
|
|
13
|
+
This is particularly important for storing binary data in CoValues, since they are likely to be at least on the order of megabytes.
|
|
14
|
+
This also means that we want to keep signatures roughly after each MAX_RECOMMENDED_TX size chunk,
|
|
15
|
+
to be able to verify partially loaded CoValues or CoValues that are still being created (like a video live stream).
|
|
16
|
+
**/
|
|
17
|
+
export const MAX_RECOMMENDED_TX_SIZE = 100 * 1024;
|
|
18
|
+
export function idforHeader(header, crypto) {
|
|
19
|
+
const hash = crypto.shortHash(header);
|
|
20
|
+
return `co_z${hash.slice("shortHash_z".length)}`;
|
|
21
|
+
}
|
|
22
|
+
const readKeyCache = new WeakMap();
|
|
23
|
+
export class CoValueCore {
|
|
24
|
+
constructor(header, node, internalInitSessions = new Map()) {
|
|
25
|
+
this.listeners = new Set();
|
|
26
|
+
this._decryptionCache = {};
|
|
27
|
+
this.deferredUpdates = 0;
|
|
28
|
+
this.crypto = node.crypto;
|
|
29
|
+
this.id = idforHeader(header, node.crypto);
|
|
30
|
+
this.header = header;
|
|
31
|
+
this._sessionLogs = internalInitSessions;
|
|
32
|
+
this.node = node;
|
|
33
|
+
if (header.ruleset.type == "ownedByGroup") {
|
|
34
|
+
this.node
|
|
35
|
+
.expectCoValueLoaded(header.ruleset.group)
|
|
36
|
+
.subscribe((_groupUpdate) => {
|
|
37
|
+
this._cachedContent = undefined;
|
|
38
|
+
const newContent = this.getCurrentContent();
|
|
39
|
+
for (const listener of this.listeners) {
|
|
40
|
+
listener(newContent);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
get sessionLogs() {
|
|
46
|
+
return this._sessionLogs;
|
|
47
|
+
}
|
|
48
|
+
testWithDifferentAccount(account, currentSessionID) {
|
|
49
|
+
const newNode = this.node.testWithDifferentAccount(account, currentSessionID);
|
|
50
|
+
return newNode.expectCoValueLoaded(this.id);
|
|
51
|
+
}
|
|
52
|
+
knownState() {
|
|
53
|
+
if (this._cachedKnownState) {
|
|
54
|
+
return this._cachedKnownState;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const knownState = this.knownStateUncached();
|
|
58
|
+
this._cachedKnownState = knownState;
|
|
59
|
+
return knownState;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** @internal */
|
|
63
|
+
knownStateUncached() {
|
|
64
|
+
return {
|
|
65
|
+
id: this.id,
|
|
66
|
+
header: true,
|
|
67
|
+
sessions: Object.fromEntries([...this.sessionLogs.entries()].map(([k, v]) => [
|
|
68
|
+
k,
|
|
69
|
+
v.transactions.length,
|
|
70
|
+
])),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
get meta() {
|
|
74
|
+
return this.header?.meta ?? null;
|
|
75
|
+
}
|
|
76
|
+
nextTransactionID() {
|
|
77
|
+
// This is an ugly hack to get a unique but stable session ID for editing the current account
|
|
78
|
+
const sessionID = this.header.meta?.type === "account"
|
|
79
|
+
? this.node.currentSessionID.replace(this.node.account.id, this.node.account
|
|
80
|
+
.currentAgentID()
|
|
81
|
+
._unsafeUnwrap({ withStackTrace: true }))
|
|
82
|
+
: this.node.currentSessionID;
|
|
83
|
+
return {
|
|
84
|
+
sessionID,
|
|
85
|
+
txIndex: this.sessionLogs.get(sessionID)?.transactions.length || 0,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
tryAddTransactions(sessionID, newTransactions, givenExpectedNewHash, newSignature) {
|
|
89
|
+
return this.node
|
|
90
|
+
.resolveAccountAgent(accountOrAgentIDfromSessionID(sessionID), "Expected to know signer of transaction")
|
|
91
|
+
.andThen((agent) => {
|
|
92
|
+
const signerID = this.crypto.getAgentSignerID(agent);
|
|
93
|
+
// const beforeHash = performance.now();
|
|
94
|
+
const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(sessionID, newTransactions);
|
|
95
|
+
// const afterHash = performance.now();
|
|
96
|
+
// console.log(
|
|
97
|
+
// "Hashing took",
|
|
98
|
+
// afterHash - beforeHash
|
|
99
|
+
// );
|
|
100
|
+
if (givenExpectedNewHash &&
|
|
101
|
+
givenExpectedNewHash !== expectedNewHash) {
|
|
102
|
+
return err({
|
|
103
|
+
type: "InvalidHash",
|
|
104
|
+
id: this.id,
|
|
105
|
+
expectedNewHash,
|
|
106
|
+
givenExpectedNewHash,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
// const beforeVerify = performance.now();
|
|
110
|
+
if (!this.crypto.verify(newSignature, expectedNewHash, signerID)) {
|
|
111
|
+
return err({
|
|
112
|
+
type: "InvalidSignature",
|
|
113
|
+
id: this.id,
|
|
114
|
+
newSignature,
|
|
115
|
+
sessionID,
|
|
116
|
+
signerID,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// const afterVerify = performance.now();
|
|
120
|
+
// console.log(
|
|
121
|
+
// "Verify took",
|
|
122
|
+
// afterVerify - beforeVerify
|
|
123
|
+
// );
|
|
124
|
+
this.doAddTransactions(sessionID, newTransactions, newSignature, expectedNewHash, newStreamingHash, "immediate");
|
|
125
|
+
return ok(true);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/*tryAddTransactionsAsync(
|
|
129
|
+
sessionID: SessionID,
|
|
130
|
+
newTransactions: Transaction[],
|
|
131
|
+
givenExpectedNewHash: Hash | undefined,
|
|
132
|
+
newSignature: Signature,
|
|
133
|
+
): ResultAsync<true, TryAddTransactionsError> {
|
|
134
|
+
const currentAsyncAddTransaction = this._currentAsyncAddTransaction;
|
|
135
|
+
let maybeAwaitPrevious:
|
|
136
|
+
| ResultAsync<void, TryAddTransactionsError>
|
|
137
|
+
| undefined;
|
|
138
|
+
let thisDone = () => {};
|
|
139
|
+
|
|
140
|
+
if (currentAsyncAddTransaction) {
|
|
141
|
+
// eslint-disable-next-line neverthrow/must-use-result
|
|
142
|
+
maybeAwaitPrevious = ResultAsync.fromSafePromise(
|
|
143
|
+
currentAsyncAddTransaction,
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
// eslint-disable-next-line neverthrow/must-use-result
|
|
147
|
+
maybeAwaitPrevious = ResultAsync.fromSafePromise(Promise.resolve());
|
|
148
|
+
this._currentAsyncAddTransaction = new Promise((resolve) => {
|
|
149
|
+
thisDone = resolve;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return maybeAwaitPrevious
|
|
154
|
+
.andThen((_previousDone) =>
|
|
155
|
+
this.node
|
|
156
|
+
.resolveAccountAgentAsync(
|
|
157
|
+
accountOrAgentIDfromSessionID(sessionID),
|
|
158
|
+
"Expected to know signer of transaction",
|
|
159
|
+
)
|
|
160
|
+
.andThen((agent) => {
|
|
161
|
+
const signerID = this.crypto.getAgentSignerID(agent);
|
|
162
|
+
|
|
163
|
+
const nTxBefore =
|
|
164
|
+
this.sessionLogs.get(sessionID)?.transactions
|
|
165
|
+
.length ?? 0;
|
|
166
|
+
|
|
167
|
+
// const beforeHash = performance.now();
|
|
168
|
+
return ResultAsync.fromSafePromise(
|
|
169
|
+
this.expectedNewHashAfterAsync(
|
|
170
|
+
sessionID,
|
|
171
|
+
newTransactions,
|
|
172
|
+
),
|
|
173
|
+
).andThen(({ expectedNewHash, newStreamingHash }) => {
|
|
174
|
+
// const afterHash = performance.now();
|
|
175
|
+
// console.log(
|
|
176
|
+
// "Hashing took",
|
|
177
|
+
// afterHash - beforeHash
|
|
178
|
+
// );
|
|
179
|
+
|
|
180
|
+
const nTxAfter =
|
|
181
|
+
this.sessionLogs.get(sessionID)?.transactions
|
|
182
|
+
.length ?? 0;
|
|
183
|
+
|
|
184
|
+
if (nTxAfter !== nTxBefore) {
|
|
185
|
+
const newTransactionLengthBefore =
|
|
186
|
+
newTransactions.length;
|
|
187
|
+
newTransactions = newTransactions.slice(
|
|
188
|
+
nTxAfter - nTxBefore,
|
|
189
|
+
);
|
|
190
|
+
console.warn(
|
|
191
|
+
"Transactions changed while async hashing",
|
|
192
|
+
{
|
|
193
|
+
nTxBefore,
|
|
194
|
+
nTxAfter,
|
|
195
|
+
newTransactionLengthBefore,
|
|
196
|
+
remainingNewTransactions:
|
|
197
|
+
newTransactions.length,
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (
|
|
203
|
+
givenExpectedNewHash &&
|
|
204
|
+
givenExpectedNewHash !== expectedNewHash
|
|
205
|
+
) {
|
|
206
|
+
return err({
|
|
207
|
+
type: "InvalidHash",
|
|
208
|
+
id: this.id,
|
|
209
|
+
expectedNewHash,
|
|
210
|
+
givenExpectedNewHash,
|
|
211
|
+
} satisfies InvalidHashError);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
performance.mark("verifyStart" + this.id);
|
|
215
|
+
if (
|
|
216
|
+
!this.crypto.verify(
|
|
217
|
+
newSignature,
|
|
218
|
+
expectedNewHash,
|
|
219
|
+
signerID,
|
|
220
|
+
)
|
|
221
|
+
) {
|
|
222
|
+
return err({
|
|
223
|
+
type: "InvalidSignature",
|
|
224
|
+
id: this.id,
|
|
225
|
+
newSignature,
|
|
226
|
+
sessionID,
|
|
227
|
+
signerID,
|
|
228
|
+
} satisfies InvalidSignatureError);
|
|
229
|
+
}
|
|
230
|
+
performance.mark("verifyEnd" + this.id);
|
|
231
|
+
performance.measure(
|
|
232
|
+
"verify" + this.id,
|
|
233
|
+
"verifyStart" + this.id,
|
|
234
|
+
"verifyEnd" + this.id,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
this.doAddTransactions(
|
|
238
|
+
sessionID,
|
|
239
|
+
newTransactions,
|
|
240
|
+
newSignature,
|
|
241
|
+
expectedNewHash,
|
|
242
|
+
newStreamingHash,
|
|
243
|
+
"deferred",
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return ok(true as const);
|
|
247
|
+
});
|
|
248
|
+
}),
|
|
249
|
+
)
|
|
250
|
+
.map((trueResult) => {
|
|
251
|
+
thisDone();
|
|
252
|
+
return trueResult;
|
|
253
|
+
})
|
|
254
|
+
.mapErr((err) => {
|
|
255
|
+
thisDone();
|
|
256
|
+
return err;
|
|
257
|
+
});
|
|
258
|
+
}*/
|
|
259
|
+
doAddTransactions(sessionID, newTransactions, newSignature, expectedNewHash, newStreamingHash, notifyMode) {
|
|
260
|
+
if (this.node.crashed) {
|
|
261
|
+
throw new Error("Trying to add transactions after node is crashed");
|
|
262
|
+
}
|
|
263
|
+
const transactions = this.sessionLogs.get(sessionID)?.transactions ?? [];
|
|
264
|
+
transactions.push(...newTransactions);
|
|
265
|
+
const signatureAfter = this.sessionLogs.get(sessionID)?.signatureAfter ?? {};
|
|
266
|
+
const lastInbetweenSignatureIdx = Object.keys(signatureAfter).reduce((max, idx) => (parseInt(idx) > max ? parseInt(idx) : max), -1);
|
|
267
|
+
const sizeOfTxsSinceLastInbetweenSignature = transactions
|
|
268
|
+
.slice(lastInbetweenSignatureIdx + 1)
|
|
269
|
+
.reduce((sum, tx) => sum +
|
|
270
|
+
(tx.privacy === "private"
|
|
271
|
+
? tx.encryptedChanges.length
|
|
272
|
+
: tx.changes.length), 0);
|
|
273
|
+
if (sizeOfTxsSinceLastInbetweenSignature > MAX_RECOMMENDED_TX_SIZE) {
|
|
274
|
+
// console.log(
|
|
275
|
+
// "Saving inbetween signature for tx ",
|
|
276
|
+
// sessionID,
|
|
277
|
+
// transactions.length - 1,
|
|
278
|
+
// sizeOfTxsSinceLastInbetweenSignature
|
|
279
|
+
// );
|
|
280
|
+
signatureAfter[transactions.length - 1] = newSignature;
|
|
281
|
+
}
|
|
282
|
+
this._sessionLogs.set(sessionID, {
|
|
283
|
+
transactions,
|
|
284
|
+
lastHash: expectedNewHash,
|
|
285
|
+
streamingHash: newStreamingHash,
|
|
286
|
+
lastSignature: newSignature,
|
|
287
|
+
signatureAfter: signatureAfter,
|
|
288
|
+
});
|
|
289
|
+
this._cachedContent = undefined;
|
|
290
|
+
this._cachedKnownState = undefined;
|
|
291
|
+
this._cachedDependentOn = undefined;
|
|
292
|
+
this._cachedNewContentSinceEmpty = undefined;
|
|
293
|
+
if (this.listeners.size > 0) {
|
|
294
|
+
if (notifyMode === "immediate") {
|
|
295
|
+
const content = this.getCurrentContent();
|
|
296
|
+
for (const listener of this.listeners) {
|
|
297
|
+
listener(content);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
if (!this.nextDeferredNotify) {
|
|
302
|
+
this.nextDeferredNotify = new Promise((resolve) => {
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
this.nextDeferredNotify = undefined;
|
|
305
|
+
this.deferredUpdates = 0;
|
|
306
|
+
const content = this.getCurrentContent();
|
|
307
|
+
for (const listener of this.listeners) {
|
|
308
|
+
listener(content);
|
|
309
|
+
}
|
|
310
|
+
resolve();
|
|
311
|
+
}, 0);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
this.deferredUpdates++;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
subscribe(listener) {
|
|
319
|
+
this.listeners.add(listener);
|
|
320
|
+
listener(this.getCurrentContent());
|
|
321
|
+
return () => {
|
|
322
|
+
this.listeners.delete(listener);
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
expectedNewHashAfter(sessionID, newTransactions) {
|
|
326
|
+
const streamingHash = this.sessionLogs.get(sessionID)?.streamingHash.clone() ??
|
|
327
|
+
new StreamingHash(this.crypto);
|
|
328
|
+
for (const transaction of newTransactions) {
|
|
329
|
+
streamingHash.update(transaction);
|
|
330
|
+
}
|
|
331
|
+
const newStreamingHash = streamingHash.clone();
|
|
332
|
+
return {
|
|
333
|
+
expectedNewHash: streamingHash.digest(),
|
|
334
|
+
newStreamingHash,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
async expectedNewHashAfterAsync(sessionID, newTransactions) {
|
|
338
|
+
const streamingHash = this.sessionLogs.get(sessionID)?.streamingHash.clone() ??
|
|
339
|
+
new StreamingHash(this.crypto);
|
|
340
|
+
let before = performance.now();
|
|
341
|
+
for (const transaction of newTransactions) {
|
|
342
|
+
streamingHash.update(transaction);
|
|
343
|
+
const after = performance.now();
|
|
344
|
+
if (after - before > 1) {
|
|
345
|
+
// console.log("Hashing blocked for", after - before);
|
|
346
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
347
|
+
before = performance.now();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const newStreamingHash = streamingHash.clone();
|
|
351
|
+
return {
|
|
352
|
+
expectedNewHash: streamingHash.digest(),
|
|
353
|
+
newStreamingHash,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
makeTransaction(changes, privacy) {
|
|
357
|
+
const madeAt = Date.now();
|
|
358
|
+
let transaction;
|
|
359
|
+
if (privacy === "private") {
|
|
360
|
+
const { secret: keySecret, id: keyID } = this.getCurrentReadKey();
|
|
361
|
+
if (!keySecret) {
|
|
362
|
+
throw new Error("Can't make transaction without read key secret");
|
|
363
|
+
}
|
|
364
|
+
const encrypted = this.crypto.encryptForTransaction(changes, keySecret, {
|
|
365
|
+
in: this.id,
|
|
366
|
+
tx: this.nextTransactionID(),
|
|
367
|
+
});
|
|
368
|
+
this._decryptionCache[encrypted] = changes;
|
|
369
|
+
transaction = {
|
|
370
|
+
privacy: "private",
|
|
371
|
+
madeAt,
|
|
372
|
+
keyUsed: keyID,
|
|
373
|
+
encryptedChanges: encrypted,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
transaction = {
|
|
378
|
+
privacy: "trusting",
|
|
379
|
+
madeAt,
|
|
380
|
+
changes: stableStringify(changes),
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
// This is an ugly hack to get a unique but stable session ID for editing the current account
|
|
384
|
+
const sessionID = this.header.meta?.type === "account"
|
|
385
|
+
? this.node.currentSessionID.replace(this.node.account.id, this.node.account
|
|
386
|
+
.currentAgentID()
|
|
387
|
+
._unsafeUnwrap({ withStackTrace: true }))
|
|
388
|
+
: this.node.currentSessionID;
|
|
389
|
+
const { expectedNewHash } = this.expectedNewHashAfter(sessionID, [
|
|
390
|
+
transaction,
|
|
391
|
+
]);
|
|
392
|
+
const signature = this.crypto.sign(this.node.account.currentSignerSecret(), expectedNewHash);
|
|
393
|
+
const success = this.tryAddTransactions(sessionID, [transaction], expectedNewHash, signature)._unsafeUnwrap({ withStackTrace: true });
|
|
394
|
+
if (success) {
|
|
395
|
+
void this.node.syncManager.syncCoValue(this);
|
|
396
|
+
}
|
|
397
|
+
return success;
|
|
398
|
+
}
|
|
399
|
+
getCurrentContent(options) {
|
|
400
|
+
if (!options?.ignorePrivateTransactions && this._cachedContent) {
|
|
401
|
+
return this._cachedContent;
|
|
402
|
+
}
|
|
403
|
+
const newContent = coreToCoValue(this, options);
|
|
404
|
+
if (!options?.ignorePrivateTransactions) {
|
|
405
|
+
this._cachedContent = newContent;
|
|
406
|
+
}
|
|
407
|
+
return newContent;
|
|
408
|
+
}
|
|
409
|
+
getValidSortedTransactions(options) {
|
|
410
|
+
const validTransactions = determineValidTransactions(this);
|
|
411
|
+
const allTransactions = validTransactions
|
|
412
|
+
.flatMap(({ txID, tx }) => {
|
|
413
|
+
if (tx.privacy === "trusting") {
|
|
414
|
+
return {
|
|
415
|
+
txID,
|
|
416
|
+
madeAt: tx.madeAt,
|
|
417
|
+
changes: parseJSON(tx.changes),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
if (options?.ignorePrivateTransactions) {
|
|
422
|
+
return undefined;
|
|
423
|
+
}
|
|
424
|
+
const readKey = this.getReadKey(tx.keyUsed);
|
|
425
|
+
if (!readKey) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
let decrytedChanges = this._decryptionCache[tx.encryptedChanges];
|
|
430
|
+
if (!decrytedChanges) {
|
|
431
|
+
const decryptedString = this.crypto.decryptRawForTransaction(tx.encryptedChanges, readKey, {
|
|
432
|
+
in: this.id,
|
|
433
|
+
tx: txID,
|
|
434
|
+
});
|
|
435
|
+
decrytedChanges =
|
|
436
|
+
decryptedString && parseJSON(decryptedString);
|
|
437
|
+
this._decryptionCache[tx.encryptedChanges] =
|
|
438
|
+
decrytedChanges;
|
|
439
|
+
}
|
|
440
|
+
if (!decrytedChanges) {
|
|
441
|
+
console.error("Failed to decrypt transaction despite having key");
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
txID,
|
|
446
|
+
madeAt: tx.madeAt,
|
|
447
|
+
changes: decrytedChanges,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
.filter((x) => !!x);
|
|
453
|
+
allTransactions.sort((a, b) => a.madeAt - b.madeAt ||
|
|
454
|
+
(a.txID.sessionID < b.txID.sessionID ? -1 : 1) ||
|
|
455
|
+
a.txID.txIndex - b.txID.txIndex);
|
|
456
|
+
return allTransactions;
|
|
457
|
+
}
|
|
458
|
+
getCurrentReadKey() {
|
|
459
|
+
if (this.header.ruleset.type === "group") {
|
|
460
|
+
const content = expectGroup(this.getCurrentContent());
|
|
461
|
+
const currentKeyId = content.get("readKey");
|
|
462
|
+
if (!currentKeyId) {
|
|
463
|
+
throw new Error("No readKey set");
|
|
464
|
+
}
|
|
465
|
+
const secret = this.getReadKey(currentKeyId);
|
|
466
|
+
return {
|
|
467
|
+
secret: secret,
|
|
468
|
+
id: currentKeyId,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
else if (this.header.ruleset.type === "ownedByGroup") {
|
|
472
|
+
return this.node
|
|
473
|
+
.expectCoValueLoaded(this.header.ruleset.group)
|
|
474
|
+
.getCurrentReadKey();
|
|
475
|
+
}
|
|
476
|
+
else {
|
|
477
|
+
throw new Error("Only groups or values owned by groups have read secrets");
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
getReadKey(keyID) {
|
|
481
|
+
let key = readKeyCache.get(this)?.[keyID];
|
|
482
|
+
if (!key) {
|
|
483
|
+
key = this.getUncachedReadKey(keyID);
|
|
484
|
+
if (key) {
|
|
485
|
+
let cache = readKeyCache.get(this);
|
|
486
|
+
if (!cache) {
|
|
487
|
+
cache = {};
|
|
488
|
+
readKeyCache.set(this, cache);
|
|
489
|
+
}
|
|
490
|
+
cache[keyID] = key;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return key;
|
|
494
|
+
}
|
|
495
|
+
getUncachedReadKey(keyID) {
|
|
496
|
+
if (this.header.ruleset.type === "group") {
|
|
497
|
+
const content = expectGroup(this.getCurrentContent({ ignorePrivateTransactions: true }));
|
|
498
|
+
const keyForEveryone = content.get(`${keyID}_for_everyone`);
|
|
499
|
+
if (keyForEveryone)
|
|
500
|
+
return keyForEveryone;
|
|
501
|
+
// Try to find key revelation for us
|
|
502
|
+
const lookupAccountOrAgentID = this.header.meta?.type === "account"
|
|
503
|
+
? this.node.account
|
|
504
|
+
.currentAgentID()
|
|
505
|
+
._unsafeUnwrap({ withStackTrace: true })
|
|
506
|
+
: this.node.account.id;
|
|
507
|
+
const lastReadyKeyEdit = content.lastEditAt(`${keyID}_for_${lookupAccountOrAgentID}`);
|
|
508
|
+
if (lastReadyKeyEdit?.value) {
|
|
509
|
+
const revealer = lastReadyKeyEdit.by;
|
|
510
|
+
const revealerAgent = this.node
|
|
511
|
+
.resolveAccountAgent(revealer, "Expected to know revealer")
|
|
512
|
+
._unsafeUnwrap({ withStackTrace: true });
|
|
513
|
+
const secret = this.crypto.unseal(lastReadyKeyEdit.value, this.node.account.currentSealerSecret(), this.crypto.getAgentSealerID(revealerAgent), {
|
|
514
|
+
in: this.id,
|
|
515
|
+
tx: lastReadyKeyEdit.tx,
|
|
516
|
+
});
|
|
517
|
+
if (secret) {
|
|
518
|
+
return secret;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Try to find indirect revelation through previousKeys
|
|
522
|
+
for (const co of content.keys()) {
|
|
523
|
+
if (isKeyForKeyField(co) && co.startsWith(keyID)) {
|
|
524
|
+
const encryptingKeyID = co.split("_for_")[1];
|
|
525
|
+
const encryptingKeySecret = this.getReadKey(encryptingKeyID);
|
|
526
|
+
if (!encryptingKeySecret) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const encryptedPreviousKey = content.get(co);
|
|
530
|
+
const secret = this.crypto.decryptKeySecret({
|
|
531
|
+
encryptedID: keyID,
|
|
532
|
+
encryptingID: encryptingKeyID,
|
|
533
|
+
encrypted: encryptedPreviousKey,
|
|
534
|
+
}, encryptingKeySecret);
|
|
535
|
+
if (secret) {
|
|
536
|
+
return secret;
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
console.error(`Encrypting ${encryptingKeyID} key didn't decrypt ${keyID}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
return undefined;
|
|
544
|
+
}
|
|
545
|
+
else if (this.header.ruleset.type === "ownedByGroup") {
|
|
546
|
+
return this.node
|
|
547
|
+
.expectCoValueLoaded(this.header.ruleset.group)
|
|
548
|
+
.getReadKey(keyID);
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
throw new Error("Only groups or values owned by groups have read secrets");
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
getGroup() {
|
|
555
|
+
if (this.header.ruleset.type !== "ownedByGroup") {
|
|
556
|
+
throw new Error("Only values owned by groups have groups");
|
|
557
|
+
}
|
|
558
|
+
return expectGroup(this.node
|
|
559
|
+
.expectCoValueLoaded(this.header.ruleset.group)
|
|
560
|
+
.getCurrentContent());
|
|
561
|
+
}
|
|
562
|
+
getTx(txID) {
|
|
563
|
+
return this.sessionLogs.get(txID.sessionID)?.transactions[txID.txIndex];
|
|
564
|
+
}
|
|
565
|
+
newContentSince(knownState) {
|
|
566
|
+
const isKnownStateEmpty = !knownState?.header && !knownState?.sessions;
|
|
567
|
+
if (isKnownStateEmpty && this._cachedNewContentSinceEmpty) {
|
|
568
|
+
return this._cachedNewContentSinceEmpty;
|
|
569
|
+
}
|
|
570
|
+
let currentPiece = {
|
|
571
|
+
action: "content",
|
|
572
|
+
id: this.id,
|
|
573
|
+
header: knownState?.header ? undefined : this.header,
|
|
574
|
+
priority: getPriorityFromHeader(this.header),
|
|
575
|
+
new: {},
|
|
576
|
+
};
|
|
577
|
+
const pieces = [currentPiece];
|
|
578
|
+
const sentState = {};
|
|
579
|
+
let pieceSize = 0;
|
|
580
|
+
let sessionsTodoAgain = "first";
|
|
581
|
+
while (sessionsTodoAgain === "first" ||
|
|
582
|
+
sessionsTodoAgain?.size ||
|
|
583
|
+
0 > 0) {
|
|
584
|
+
if (sessionsTodoAgain === "first") {
|
|
585
|
+
sessionsTodoAgain = undefined;
|
|
586
|
+
}
|
|
587
|
+
const sessionsTodo = sessionsTodoAgain ?? this.sessionLogs.keys();
|
|
588
|
+
for (const sessionIDKey of sessionsTodo) {
|
|
589
|
+
const sessionID = sessionIDKey;
|
|
590
|
+
const log = this.sessionLogs.get(sessionID);
|
|
591
|
+
const knownStateForSessionID = knownState?.sessions[sessionID];
|
|
592
|
+
const sentStateForSessionID = sentState[sessionID];
|
|
593
|
+
const nextKnownSignatureIdx = getNextKnownSignatureIdx(log, knownStateForSessionID, sentStateForSessionID);
|
|
594
|
+
const firstNewTxIdx = sentStateForSessionID ?? knownStateForSessionID ?? 0;
|
|
595
|
+
const afterLastNewTxIdx = nextKnownSignatureIdx === undefined
|
|
596
|
+
? log.transactions.length
|
|
597
|
+
: nextKnownSignatureIdx + 1;
|
|
598
|
+
const nNewTx = Math.max(0, afterLastNewTxIdx - firstNewTxIdx);
|
|
599
|
+
if (nNewTx === 0) {
|
|
600
|
+
sessionsTodoAgain?.delete(sessionID);
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
if (afterLastNewTxIdx < log.transactions.length) {
|
|
604
|
+
if (!sessionsTodoAgain) {
|
|
605
|
+
sessionsTodoAgain = new Set();
|
|
606
|
+
}
|
|
607
|
+
sessionsTodoAgain.add(sessionID);
|
|
608
|
+
}
|
|
609
|
+
const oldPieceSize = pieceSize;
|
|
610
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
611
|
+
const tx = log.transactions[txIdx];
|
|
612
|
+
pieceSize +=
|
|
613
|
+
tx.privacy === "private"
|
|
614
|
+
? tx.encryptedChanges.length
|
|
615
|
+
: tx.changes.length;
|
|
616
|
+
}
|
|
617
|
+
if (pieceSize >= MAX_RECOMMENDED_TX_SIZE) {
|
|
618
|
+
currentPiece = {
|
|
619
|
+
action: "content",
|
|
620
|
+
id: this.id,
|
|
621
|
+
header: undefined,
|
|
622
|
+
new: {},
|
|
623
|
+
priority: getPriorityFromHeader(this.header),
|
|
624
|
+
};
|
|
625
|
+
pieces.push(currentPiece);
|
|
626
|
+
pieceSize = pieceSize - oldPieceSize;
|
|
627
|
+
}
|
|
628
|
+
let sessionEntry = currentPiece.new[sessionID];
|
|
629
|
+
if (!sessionEntry) {
|
|
630
|
+
sessionEntry = {
|
|
631
|
+
after: sentStateForSessionID ??
|
|
632
|
+
knownStateForSessionID ??
|
|
633
|
+
0,
|
|
634
|
+
newTransactions: [],
|
|
635
|
+
lastSignature: "WILL_BE_REPLACED",
|
|
636
|
+
};
|
|
637
|
+
currentPiece.new[sessionID] = sessionEntry;
|
|
638
|
+
}
|
|
639
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
640
|
+
const tx = log.transactions[txIdx];
|
|
641
|
+
sessionEntry.newTransactions.push(tx);
|
|
642
|
+
}
|
|
643
|
+
sessionEntry.lastSignature =
|
|
644
|
+
nextKnownSignatureIdx === undefined
|
|
645
|
+
? log.lastSignature
|
|
646
|
+
: log.signatureAfter[nextKnownSignatureIdx];
|
|
647
|
+
sentState[sessionID] =
|
|
648
|
+
(sentStateForSessionID ?? knownStateForSessionID ?? 0) +
|
|
649
|
+
nNewTx;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const piecesWithContent = pieces.filter((piece) => Object.keys(piece.new).length > 0 || piece.header);
|
|
653
|
+
if (piecesWithContent.length === 0) {
|
|
654
|
+
return undefined;
|
|
655
|
+
}
|
|
656
|
+
if (isKnownStateEmpty) {
|
|
657
|
+
this._cachedNewContentSinceEmpty = piecesWithContent;
|
|
658
|
+
}
|
|
659
|
+
return piecesWithContent;
|
|
660
|
+
}
|
|
661
|
+
getDependedOnCoValues() {
|
|
662
|
+
if (this._cachedDependentOn) {
|
|
663
|
+
return this._cachedDependentOn;
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
const dependentOn = this.getDependedOnCoValuesUncached();
|
|
667
|
+
this._cachedDependentOn = dependentOn;
|
|
668
|
+
return dependentOn;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
/** @internal */
|
|
672
|
+
getDependedOnCoValuesUncached() {
|
|
673
|
+
return this.header.ruleset.type === "group"
|
|
674
|
+
? expectGroup(this.getCurrentContent())
|
|
675
|
+
.keys()
|
|
676
|
+
.filter((k) => k.startsWith("co_"))
|
|
677
|
+
: this.header.ruleset.type === "ownedByGroup"
|
|
678
|
+
? [
|
|
679
|
+
this.header.ruleset.group,
|
|
680
|
+
...new Set([...this.sessionLogs.keys()]
|
|
681
|
+
.map((sessionID) => accountOrAgentIDfromSessionID(sessionID))
|
|
682
|
+
.filter((session) => isAccountID(session) && session !== this.id)),
|
|
683
|
+
]
|
|
684
|
+
: [];
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function getNextKnownSignatureIdx(log, knownStateForSessionID, sentStateForSessionID) {
|
|
688
|
+
return Object.keys(log.signatureAfter)
|
|
689
|
+
.map(Number)
|
|
690
|
+
.sort((a, b) => a - b)
|
|
691
|
+
.find((idx) => idx >= (sentStateForSessionID ?? knownStateForSessionID ?? -1));
|
|
692
|
+
}
|
|
693
|
+
//# sourceMappingURL=coValueCore.js.map
|