cojson 0.17.14 → 0.18.1
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 +1 -1
- package/CHANGELOG.md +34 -0
- package/dist/coValueCore/branching.d.ts +36 -0
- package/dist/coValueCore/branching.d.ts.map +1 -0
- package/dist/coValueCore/branching.js +122 -0
- package/dist/coValueCore/branching.js.map +1 -0
- package/dist/coValueCore/coValueCore.d.ts +71 -5
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +162 -53
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts +3 -0
- package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts.map +1 -0
- package/dist/coValueCore/decodeTransactionChangesAndMeta.js +59 -0
- package/dist/coValueCore/decodeTransactionChangesAndMeta.js.map +1 -0
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValues/account.d.ts +2 -1
- package/dist/coValues/account.d.ts.map +1 -1
- package/dist/coValues/account.js +6 -0
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -3
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +4 -7
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +3 -3
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +6 -6
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +3 -3
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +4 -4
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js.map +1 -1
- package/dist/jsonStringify.d.ts +1 -0
- package/dist/jsonStringify.d.ts.map +1 -1
- package/dist/jsonStringify.js +8 -0
- package/dist/jsonStringify.js.map +1 -1
- package/dist/permissions.d.ts +2 -7
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +72 -70
- package/dist/permissions.js.map +1 -1
- package/dist/sync.d.ts +2 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +16 -5
- package/dist/sync.js.map +1 -1
- package/dist/tests/account.test.js +20 -0
- package/dist/tests/account.test.js.map +1 -1
- package/dist/tests/branching.test.d.ts +2 -0
- package/dist/tests/branching.test.d.ts.map +1 -0
- package/dist/tests/branching.test.js +99 -0
- package/dist/tests/branching.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +2 -3
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/sync.sharding.test.js +63 -0
- package/dist/tests/sync.sharding.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +1 -3
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +1 -3
- package/dist/tests/sync.upload.test.js.map +1 -1
- package/dist/tests/testUtils.d.ts +1 -0
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +1 -1
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +2 -2
- package/src/coValueCore/branching.ts +198 -0
- package/src/coValueCore/coValueCore.ts +255 -72
- package/src/coValueCore/decodeTransactionChangesAndMeta.ts +81 -0
- package/src/coValueCore/verifiedState.ts +1 -1
- package/src/coValues/account.ts +11 -9
- package/src/coValues/coList.ts +8 -10
- package/src/coValues/coMap.ts +8 -11
- package/src/coValues/coStream.ts +7 -8
- package/src/ids.ts +4 -1
- package/src/jsonStringify.ts +8 -0
- package/src/permissions.ts +80 -89
- package/src/sync.ts +21 -6
- package/src/tests/account.test.ts +24 -0
- package/src/tests/branching.test.ts +141 -0
- package/src/tests/coValueCore.test.ts +2 -3
- package/src/tests/sync.sharding.test.ts +72 -0
- package/src/tests/sync.storage.test.ts +3 -3
- package/src/tests/sync.upload.test.ts +3 -3
- package/src/tests/testUtils.ts +2 -1
|
@@ -15,9 +15,9 @@ import {
|
|
|
15
15
|
Signature,
|
|
16
16
|
SignerID,
|
|
17
17
|
} from "../crypto/crypto.js";
|
|
18
|
-
import { RawCoID, SessionID, TransactionID } from "../ids.js";
|
|
18
|
+
import { AgentID, RawCoID, SessionID, TransactionID } from "../ids.js";
|
|
19
19
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
20
|
-
import { parseJSON } from "../jsonStringify.js";
|
|
20
|
+
import { parseJSON, safeParseJSON } from "../jsonStringify.js";
|
|
21
21
|
import { LocalNode, ResolveAccountAgentError } from "../localNode.js";
|
|
22
22
|
import { logger } from "../logger.js";
|
|
23
23
|
import { determineValidTransactions } from "../permissions.js";
|
|
@@ -27,6 +27,15 @@ import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
|
27
27
|
import { getDependedOnCoValuesFromRawData } from "./utils.js";
|
|
28
28
|
import { CoValueHeader, Transaction, VerifiedState } from "./verifiedState.js";
|
|
29
29
|
import { SessionMap } from "./SessionMap.js";
|
|
30
|
+
import {
|
|
31
|
+
MergeCommit,
|
|
32
|
+
createBranch,
|
|
33
|
+
getBranchId,
|
|
34
|
+
getBranchSource,
|
|
35
|
+
mergeBranch,
|
|
36
|
+
} from "./branching.js";
|
|
37
|
+
import { type RawAccountID } from "../coValues/account.js";
|
|
38
|
+
import { decodeTransactionChangesAndMeta } from "./decodeTransactionChangesAndMeta.js";
|
|
30
39
|
|
|
31
40
|
export function idforHeader(
|
|
32
41
|
header: CoValueHeader,
|
|
@@ -36,11 +45,38 @@ export function idforHeader(
|
|
|
36
45
|
return `co_z${hash.slice("shortHash_z".length)}`;
|
|
37
46
|
}
|
|
38
47
|
|
|
48
|
+
export type VerifiedTransaction = {
|
|
49
|
+
// The account or agent that made the transaction
|
|
50
|
+
author: RawAccountID | AgentID;
|
|
51
|
+
// An object containing the session ID and the transaction index
|
|
52
|
+
txID: TransactionID;
|
|
53
|
+
tx: Transaction;
|
|
54
|
+
// The Unix time when the transaction was made
|
|
55
|
+
madeAt: number;
|
|
56
|
+
// Whether the transaction has been validated, used to track if determinedValidTransactions needs to be check this
|
|
57
|
+
isValidated: boolean;
|
|
58
|
+
// The decoded changes of the transaction
|
|
59
|
+
changes: JsonValue[] | undefined;
|
|
60
|
+
// The decoded meta information of the transaction
|
|
61
|
+
meta: JsonObject | undefined;
|
|
62
|
+
|
|
63
|
+
// Whether the transaction is valid, as per membership rules
|
|
64
|
+
isValid: boolean;
|
|
65
|
+
|
|
66
|
+
// True if we encountered an error while decoding the changes
|
|
67
|
+
hasInvalidChanges: boolean;
|
|
68
|
+
// True if we encountered an error while parsing the meta
|
|
69
|
+
hasInvalidMeta: boolean;
|
|
70
|
+
|
|
71
|
+
// True if the meta information has been parsed and loaded in the CoValueCore
|
|
72
|
+
hasMetaBeenParsed: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
39
75
|
export type DecryptedTransaction = {
|
|
40
76
|
txID: TransactionID;
|
|
41
77
|
changes: JsonValue[];
|
|
42
78
|
madeAt: number;
|
|
43
|
-
|
|
79
|
+
tx: Transaction;
|
|
44
80
|
};
|
|
45
81
|
|
|
46
82
|
export type AvailableCoValueCore = CoValueCore & { verified: VerifiedState };
|
|
@@ -82,10 +118,6 @@ export class CoValueCore {
|
|
|
82
118
|
private _cachedContent?: RawCoValue;
|
|
83
119
|
readonly listeners: Set<(core: CoValueCore, unsub: () => void) => void> =
|
|
84
120
|
new Set();
|
|
85
|
-
private readonly _decryptionCache: {
|
|
86
|
-
[key: Encrypted<JsonValue[], JsonValue>]: JsonValue[] | undefined;
|
|
87
|
-
[key: Encrypted<JsonObject, JsonValue>]: JsonObject | undefined;
|
|
88
|
-
} = {};
|
|
89
121
|
private _cachedDependentOn?: Set<RawCoID>;
|
|
90
122
|
private counter: UpDownCounter;
|
|
91
123
|
|
|
@@ -340,13 +372,13 @@ export class CoValueCore {
|
|
|
340
372
|
);
|
|
341
373
|
}
|
|
342
374
|
this._verified = state.clone();
|
|
343
|
-
this.
|
|
344
|
-
this._cachedDependentOn = undefined;
|
|
375
|
+
this.internalShamefullyResetCachedContent();
|
|
345
376
|
}
|
|
346
377
|
|
|
347
378
|
internalShamefullyResetCachedContent() {
|
|
348
379
|
this._cachedContent = undefined;
|
|
349
380
|
this._cachedDependentOn = undefined;
|
|
381
|
+
this.resetParsedTransactions();
|
|
350
382
|
}
|
|
351
383
|
|
|
352
384
|
groupInvalidationSubscription?: () => void;
|
|
@@ -368,7 +400,8 @@ export class CoValueCore {
|
|
|
368
400
|
|
|
369
401
|
if (entry.isAvailable()) {
|
|
370
402
|
this.groupInvalidationSubscription = entry.subscribe((_groupUpdate) => {
|
|
371
|
-
|
|
403
|
+
// When the group is updated, we need to reset the cached content because the transactions validity might have changed
|
|
404
|
+
this.internalShamefullyResetCachedContent();
|
|
372
405
|
this.notifyUpdate("immediate");
|
|
373
406
|
}, false);
|
|
374
407
|
} else {
|
|
@@ -588,14 +621,6 @@ export class CoValueCore {
|
|
|
588
621
|
keySecret,
|
|
589
622
|
meta,
|
|
590
623
|
);
|
|
591
|
-
|
|
592
|
-
if (result.transaction.privacy === "private") {
|
|
593
|
-
this._decryptionCache[result.transaction.encryptedChanges] = changes;
|
|
594
|
-
|
|
595
|
-
if (result.transaction.meta) {
|
|
596
|
-
this._decryptionCache[result.transaction.meta] = meta;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
624
|
} else {
|
|
600
625
|
result = this.verified.makeNewTrustingTransaction(
|
|
601
626
|
sessionID,
|
|
@@ -609,6 +634,9 @@ export class CoValueCore {
|
|
|
609
634
|
|
|
610
635
|
this.node.syncManager.recordTransactionsSize([transaction], "local");
|
|
611
636
|
|
|
637
|
+
// We pre-populate the parsed transactions and meta for the new transaction, to skip the parsing step later
|
|
638
|
+
this.loadVerifiedTransactionsFromLogs({ transaction, changes, meta });
|
|
639
|
+
|
|
612
640
|
const session = this.verified.sessions.get(sessionID);
|
|
613
641
|
const txIdx = session ? session.transactions.length - 1 : 0;
|
|
614
642
|
|
|
@@ -646,86 +674,235 @@ export class CoValueCore {
|
|
|
646
674
|
return newContent;
|
|
647
675
|
}
|
|
648
676
|
|
|
677
|
+
// The starting point of the branch, in case this CoValue is a branch
|
|
678
|
+
branchStart:
|
|
679
|
+
| { branch: CoValueKnownState["sessions"]; madeAt: number }
|
|
680
|
+
| undefined;
|
|
681
|
+
|
|
682
|
+
// The list of merge commits that have been made
|
|
683
|
+
mergeCommits: { commit: MergeCommit; madeAt: number }[] = [];
|
|
684
|
+
|
|
685
|
+
// Reset the parsed transactions and branches, to validate them again from scratch when the group is updated
|
|
686
|
+
resetParsedTransactions() {
|
|
687
|
+
this.branchStart = undefined;
|
|
688
|
+
this.mergeCommits = [];
|
|
689
|
+
|
|
690
|
+
for (const transaction of this.verifiedTransactions) {
|
|
691
|
+
transaction.isValidated = false;
|
|
692
|
+
transaction.hasMetaBeenParsed = false;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
verifiedTransactions: VerifiedTransaction[] = [];
|
|
697
|
+
private verifiedTransactionsKnownSessions: CoValueKnownState["sessions"] = {};
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Loads the new transaction from the SessionMap into verifiedTransactions as a VerifiedTransaction.
|
|
701
|
+
*
|
|
702
|
+
* If the transaction is already loaded from the SessionMap in the past, it will not be loaded again.
|
|
703
|
+
*
|
|
704
|
+
* Used to have a fast way to iterate over the CoValue transactions, and track their validation/decoding state.
|
|
705
|
+
*
|
|
706
|
+
* @param preload - Optional preload object containing the transaction, changes, and meta.
|
|
707
|
+
* If provided, the transaction will be preloaded with the given changes and meta.
|
|
708
|
+
*
|
|
709
|
+
* @internal
|
|
710
|
+
* */
|
|
711
|
+
loadVerifiedTransactionsFromLogs(preload?: {
|
|
712
|
+
transaction: Transaction;
|
|
713
|
+
changes: JsonValue[];
|
|
714
|
+
meta: JsonObject | undefined;
|
|
715
|
+
}) {
|
|
716
|
+
if (!this.verified) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
for (const [sessionID, sessionLog] of this.verified.sessions.entries()) {
|
|
721
|
+
const count = this.verifiedTransactionsKnownSessions[sessionID] ?? 0;
|
|
722
|
+
|
|
723
|
+
sessionLog.transactions.forEach((tx, txIndex) => {
|
|
724
|
+
if (txIndex < count) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
this.verifiedTransactions.push({
|
|
729
|
+
author: accountOrAgentIDfromSessionID(sessionID),
|
|
730
|
+
txID: {
|
|
731
|
+
sessionID,
|
|
732
|
+
txIndex,
|
|
733
|
+
},
|
|
734
|
+
madeAt: tx.madeAt,
|
|
735
|
+
isValidated: false,
|
|
736
|
+
isValid: false,
|
|
737
|
+
changes: tx === preload?.transaction ? preload.changes : undefined,
|
|
738
|
+
meta: tx === preload?.transaction ? preload.meta : undefined,
|
|
739
|
+
hasInvalidChanges: false,
|
|
740
|
+
hasInvalidMeta: false,
|
|
741
|
+
hasMetaBeenParsed: false,
|
|
742
|
+
tx,
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
this.verifiedTransactionsKnownSessions[sessionID] =
|
|
747
|
+
sessionLog.transactions.length;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Iterates over the verifiedTransactions and marks them as valid or invalid, based on the group membership of the authors of the transactions .
|
|
753
|
+
*/
|
|
754
|
+
private determineValidTransactions() {
|
|
755
|
+
determineValidTransactions(this);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Parses the meta information of a transaction, and set the branchStart and mergeCommits.
|
|
760
|
+
*/
|
|
761
|
+
private parseMetaInformation(transaction: VerifiedTransaction) {
|
|
762
|
+
if (
|
|
763
|
+
!transaction.meta ||
|
|
764
|
+
!transaction.isValid ||
|
|
765
|
+
transaction.hasMetaBeenParsed
|
|
766
|
+
) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
transaction.hasMetaBeenParsed = true;
|
|
771
|
+
|
|
772
|
+
if (
|
|
773
|
+
transaction.meta?.["branch"] &&
|
|
774
|
+
(!this.branchStart || transaction.madeAt < this.branchStart.madeAt)
|
|
775
|
+
) {
|
|
776
|
+
this.branchStart = {
|
|
777
|
+
branch: transaction.meta.branch as CoValueKnownState["sessions"],
|
|
778
|
+
madeAt: transaction.madeAt,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
if (transaction.meta?.["merge"]) {
|
|
783
|
+
const mergeCommit = transaction.meta as MergeCommit;
|
|
784
|
+
|
|
785
|
+
this.mergeCommits.push({
|
|
786
|
+
commit: mergeCommit,
|
|
787
|
+
madeAt: transaction.madeAt,
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* Loads the new transactions from SessionMap and:
|
|
794
|
+
* - Validates each transaction based on the group membership of the authors
|
|
795
|
+
* - Decodes the changes & meta for each transaction
|
|
796
|
+
* - Parses the meta information of the transaction
|
|
797
|
+
*/
|
|
798
|
+
private parseNewTransactions(ignorePrivateTransactions: boolean) {
|
|
799
|
+
if (!this.isAvailable()) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
this.loadVerifiedTransactionsFromLogs();
|
|
804
|
+
this.determineValidTransactions();
|
|
805
|
+
|
|
806
|
+
for (const transaction of this.verifiedTransactions) {
|
|
807
|
+
decodeTransactionChangesAndMeta(
|
|
808
|
+
this,
|
|
809
|
+
transaction,
|
|
810
|
+
ignorePrivateTransactions,
|
|
811
|
+
);
|
|
812
|
+
this.parseMetaInformation(transaction);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Returns the valid transactions matching the criteria specified in the options
|
|
818
|
+
*/
|
|
649
819
|
getValidTransactions(options?: {
|
|
650
820
|
ignorePrivateTransactions: boolean;
|
|
651
|
-
|
|
821
|
+
// The range, described as knownState sessions, to filter the transactions returned
|
|
822
|
+
from?: CoValueKnownState["sessions"];
|
|
823
|
+
to?: CoValueKnownState["sessions"];
|
|
824
|
+
|
|
825
|
+
// The transactions that have already been processed, used for the incremental builds of the content views
|
|
826
|
+
knownTransactions?: Set<Transaction>;
|
|
827
|
+
|
|
828
|
+
// If true, the branch source transactions will be skipped. Used to gather the transactions for the merge operation.
|
|
829
|
+
skipBranchSource?: boolean;
|
|
652
830
|
}): DecryptedTransaction[] {
|
|
653
831
|
if (!this.verified) {
|
|
654
|
-
|
|
655
|
-
"CoValueCore: getValidTransactions called on coValue without verified state",
|
|
656
|
-
);
|
|
832
|
+
return [];
|
|
657
833
|
}
|
|
658
834
|
|
|
659
|
-
|
|
660
|
-
this,
|
|
661
|
-
options?.knownTransactions,
|
|
662
|
-
);
|
|
835
|
+
this.parseNewTransactions(options?.ignorePrivateTransactions ?? false);
|
|
663
836
|
|
|
664
|
-
const
|
|
837
|
+
const matchingTransactions: DecryptedTransaction[] = [];
|
|
665
838
|
|
|
666
|
-
for (const
|
|
667
|
-
if (
|
|
839
|
+
for (const transaction of this.verifiedTransactions) {
|
|
840
|
+
if (!isValidTransactionWithChanges(transaction)) {
|
|
668
841
|
continue;
|
|
669
842
|
}
|
|
670
843
|
|
|
671
|
-
|
|
844
|
+
if (options?.knownTransactions?.has(transaction.tx)) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
672
847
|
|
|
673
|
-
|
|
674
|
-
if (options?.ignorePrivateTransactions) {
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
848
|
+
options?.knownTransactions?.add(transaction.tx);
|
|
677
849
|
|
|
678
|
-
|
|
850
|
+
const { txID, madeAt } = transaction;
|
|
679
851
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
852
|
+
const from = options?.from?.[txID.sessionID] ?? -1;
|
|
853
|
+
const to = options?.to?.[txID.sessionID] ?? Infinity;
|
|
683
854
|
|
|
684
|
-
|
|
855
|
+
// The txIndex starts at 0 and from/to are referring to the count of transactions
|
|
856
|
+
if (from > txID.txIndex || to < txID.txIndex) {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
685
859
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
txID.sessionID,
|
|
689
|
-
txID.txIndex,
|
|
690
|
-
readKey,
|
|
691
|
-
);
|
|
860
|
+
matchingTransactions.push(transaction);
|
|
861
|
+
}
|
|
692
862
|
|
|
693
|
-
|
|
694
|
-
}
|
|
863
|
+
const source = getBranchSource(this);
|
|
695
864
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
865
|
+
// If this is a branch, we load the valid transactions from the source
|
|
866
|
+
if (source && this.branchStart && !options?.skipBranchSource) {
|
|
867
|
+
const sourceTransactions = source.getValidTransactions({
|
|
868
|
+
to: this.branchStart.branch,
|
|
869
|
+
ignorePrivateTransactions: options?.ignorePrivateTransactions ?? false,
|
|
870
|
+
knownTransactions: options?.knownTransactions,
|
|
871
|
+
});
|
|
702
872
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
873
|
+
for (const { changes, tx, madeAt, txID } of sourceTransactions) {
|
|
874
|
+
matchingTransactions.push({
|
|
875
|
+
txID: {
|
|
876
|
+
sessionID: `${txID.sessionID}_branch_${source.id}`,
|
|
877
|
+
txIndex: txID.txIndex,
|
|
878
|
+
},
|
|
879
|
+
madeAt,
|
|
880
|
+
changes,
|
|
881
|
+
tx,
|
|
882
|
+
});
|
|
713
883
|
}
|
|
714
|
-
|
|
715
|
-
allTransactions.push({
|
|
716
|
-
txID,
|
|
717
|
-
madeAt: tx.madeAt,
|
|
718
|
-
changes,
|
|
719
|
-
trusting: tx.privacy === "trusting",
|
|
720
|
-
});
|
|
721
884
|
}
|
|
722
885
|
|
|
723
|
-
return
|
|
886
|
+
return matchingTransactions;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
createBranch(name: string, ownerId: RawCoID) {
|
|
890
|
+
return createBranch(this, name, ownerId);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
mergeBranch() {
|
|
894
|
+
return mergeBranch(this);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
getBranchId(name: string, ownerId: RawCoID) {
|
|
898
|
+
return getBranchId(this, name, ownerId);
|
|
724
899
|
}
|
|
725
900
|
|
|
726
901
|
getValidSortedTransactions(options?: {
|
|
727
902
|
ignorePrivateTransactions: boolean;
|
|
728
|
-
|
|
903
|
+
|
|
904
|
+
// The transactions that have already been processed, used for the incremental builds of the content views
|
|
905
|
+
knownTransactions?: Set<Transaction>;
|
|
729
906
|
}): DecryptedTransaction[] {
|
|
730
907
|
const allTransactions = this.getValidTransactions(options);
|
|
731
908
|
|
|
@@ -1001,3 +1178,9 @@ export type TryAddTransactionsError =
|
|
|
1001
1178
|
| ResolveAccountAgentError
|
|
1002
1179
|
| InvalidHashError
|
|
1003
1180
|
| InvalidSignatureError;
|
|
1181
|
+
|
|
1182
|
+
function isValidTransactionWithChanges(
|
|
1183
|
+
transaction: VerifiedTransaction,
|
|
1184
|
+
): transaction is VerifiedTransaction & { changes: JsonValue[] } {
|
|
1185
|
+
return Boolean(transaction.isValid && transaction.changes);
|
|
1186
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AvailableCoValueCore, VerifiedTransaction } from "./coValueCore.js";
|
|
2
|
+
import { safeParseJSON } from "../jsonStringify.js";
|
|
3
|
+
|
|
4
|
+
export function decodeTransactionChangesAndMeta(
|
|
5
|
+
coValue: AvailableCoValueCore,
|
|
6
|
+
transaction: VerifiedTransaction,
|
|
7
|
+
ignorePrivateTransactions: boolean,
|
|
8
|
+
) {
|
|
9
|
+
if (!transaction.isValid) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (transaction.tx.privacy === "private" && ignorePrivateTransactions) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const needsChagesParsing =
|
|
18
|
+
!transaction.hasInvalidChanges && !transaction.changes;
|
|
19
|
+
const needsMetaParsing =
|
|
20
|
+
!transaction.hasInvalidMeta && !transaction.meta && transaction.tx.meta;
|
|
21
|
+
|
|
22
|
+
if (!needsChagesParsing && !needsMetaParsing) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (transaction.tx.privacy === "private") {
|
|
27
|
+
const readKey = coValue.getReadKey(transaction.tx.keyUsed);
|
|
28
|
+
|
|
29
|
+
if (!readKey) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (needsChagesParsing) {
|
|
34
|
+
const changes = coValue.verified.decryptTransaction(
|
|
35
|
+
transaction.txID.sessionID,
|
|
36
|
+
transaction.txID.txIndex,
|
|
37
|
+
readKey,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (!changes) {
|
|
41
|
+
transaction.hasInvalidChanges = true;
|
|
42
|
+
} else {
|
|
43
|
+
transaction.changes = changes;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (needsMetaParsing) {
|
|
48
|
+
const meta = coValue.verified.decryptTransactionMeta(
|
|
49
|
+
transaction.txID.sessionID,
|
|
50
|
+
transaction.txID.txIndex,
|
|
51
|
+
readKey,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (!meta) {
|
|
55
|
+
transaction.hasInvalidMeta = true;
|
|
56
|
+
} else {
|
|
57
|
+
transaction.meta = meta;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
} else {
|
|
61
|
+
if (needsChagesParsing) {
|
|
62
|
+
const changes = safeParseJSON(transaction.tx.changes);
|
|
63
|
+
|
|
64
|
+
if (!changes) {
|
|
65
|
+
transaction.hasInvalidChanges = true;
|
|
66
|
+
} else {
|
|
67
|
+
transaction.changes = changes;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (needsMetaParsing && transaction.tx.meta) {
|
|
72
|
+
const meta = safeParseJSON(transaction.tx.meta);
|
|
73
|
+
|
|
74
|
+
if (!meta) {
|
|
75
|
+
transaction.hasInvalidMeta = true;
|
|
76
|
+
} else {
|
|
77
|
+
transaction.meta = meta;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -21,6 +21,7 @@ import { CoValueKnownState, NewContentMessage } from "../sync.js";
|
|
|
21
21
|
import { TryAddTransactionsError } from "./coValueCore.js";
|
|
22
22
|
import { SessionLog, SessionMap } from "./SessionMap.js";
|
|
23
23
|
import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
24
|
+
import { logger } from "../logger.js";
|
|
24
25
|
|
|
25
26
|
export type CoValueHeader = {
|
|
26
27
|
type: AnyRawCoValue["type"];
|
|
@@ -40,7 +41,6 @@ export type PrivateTransaction = {
|
|
|
40
41
|
encryptedChanges: Encrypted<JsonValue[], { in: RawCoID; tx: TransactionID }>;
|
|
41
42
|
meta?: Encrypted<JsonObject, { in: RawCoID; tx: TransactionID }>;
|
|
42
43
|
};
|
|
43
|
-
|
|
44
44
|
export type TrustingTransaction = {
|
|
45
45
|
privacy: "trusting";
|
|
46
46
|
madeAt: number;
|
package/src/coValues/account.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { CoID, RawCoValue } from "../coValue.js";
|
|
2
|
-
import {
|
|
3
|
-
AvailableCoValueCore,
|
|
4
|
-
CoValueCore,
|
|
5
|
-
} from "../coValueCore/coValueCore.js";
|
|
6
|
-
import {
|
|
7
|
-
CoValueHeader,
|
|
8
|
-
CoValueUniqueness,
|
|
9
|
-
} from "../coValueCore/verifiedState.js";
|
|
2
|
+
import { CoValueHeader } from "../coValueCore/verifiedState.js";
|
|
10
3
|
import {
|
|
11
4
|
AgentSecret,
|
|
12
5
|
CryptoProvider,
|
|
@@ -21,7 +14,7 @@ import { LocalNode } from "../localNode.js";
|
|
|
21
14
|
import { logger } from "../logger.js";
|
|
22
15
|
import type { AccountRole, Role } from "../permissions.js";
|
|
23
16
|
import { RawCoMap } from "./coMap.js";
|
|
24
|
-
import { Everyone, InviteSecret, RawGroup } from "./group.js";
|
|
17
|
+
import { Everyone, EVERYONE, InviteSecret, RawGroup } from "./group.js";
|
|
25
18
|
|
|
26
19
|
export function accountHeaderForInitialAgentSecret(
|
|
27
20
|
agentSecret: AgentSecret,
|
|
@@ -75,6 +68,15 @@ export class RawAccount<
|
|
|
75
68
|
throw new Error("Cannot create invite from an account");
|
|
76
69
|
}
|
|
77
70
|
|
|
71
|
+
override roleOfInternal(
|
|
72
|
+
accountID: RawAccountID | AgentID | typeof EVERYONE,
|
|
73
|
+
): Role | undefined {
|
|
74
|
+
if (accountID === this.id) {
|
|
75
|
+
return "admin";
|
|
76
|
+
}
|
|
77
|
+
return super.roleOfInternal(accountID);
|
|
78
|
+
}
|
|
79
|
+
|
|
78
80
|
override addMember(
|
|
79
81
|
account: RawAccount | ControlledAccountOrAgent | Everyone,
|
|
80
82
|
role: Role,
|
package/src/coValues/coList.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
|
|
|
10
10
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
11
11
|
import { RawAccountID } from "./account.js";
|
|
12
12
|
import { RawGroup } from "./group.js";
|
|
13
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
13
14
|
|
|
14
15
|
export type OpID = TransactionID & { changeIdx: number };
|
|
15
16
|
|
|
@@ -86,8 +87,12 @@ export class RawCoList<
|
|
|
86
87
|
opID: OpID;
|
|
87
88
|
}[];
|
|
88
89
|
/** @internal */
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
knownTransactions: Set<Transaction>;
|
|
91
|
+
|
|
92
|
+
get totalValidTransactions() {
|
|
93
|
+
return this.knownTransactions.size;
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
lastValidTransaction: number | undefined;
|
|
92
97
|
|
|
93
98
|
/** @internal */
|
|
@@ -99,7 +104,7 @@ export class RawCoList<
|
|
|
99
104
|
this.deletionsByInsertion = {};
|
|
100
105
|
this.afterStart = [];
|
|
101
106
|
this.beforeEnd = [];
|
|
102
|
-
this.knownTransactions =
|
|
107
|
+
this.knownTransactions = new Set<Transaction>();
|
|
103
108
|
|
|
104
109
|
this.processNewTransactions();
|
|
105
110
|
}
|
|
@@ -114,7 +119,6 @@ export class RawCoList<
|
|
|
114
119
|
return;
|
|
115
120
|
}
|
|
116
121
|
|
|
117
|
-
this.totalValidTransactions += transactions.length;
|
|
118
122
|
let lastValidTransaction: number | undefined = undefined;
|
|
119
123
|
let oldestValidTransaction: number | undefined = undefined;
|
|
120
124
|
this._cachedEntries = undefined;
|
|
@@ -126,11 +130,6 @@ export class RawCoList<
|
|
|
126
130
|
madeAt,
|
|
127
131
|
);
|
|
128
132
|
|
|
129
|
-
this.knownTransactions[txID.sessionID] = Math.max(
|
|
130
|
-
this.knownTransactions[txID.sessionID] ?? 0,
|
|
131
|
-
txID.txIndex,
|
|
132
|
-
);
|
|
133
|
-
|
|
134
133
|
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
135
134
|
const change = changeUntyped as ListOpPayload<Item>;
|
|
136
135
|
|
|
@@ -624,7 +623,6 @@ export class RawCoList<
|
|
|
624
623
|
this.afterStart = listAfter.afterStart;
|
|
625
624
|
this.beforeEnd = listAfter.beforeEnd;
|
|
626
625
|
this.insertions = listAfter.insertions;
|
|
627
|
-
this.totalValidTransactions = listAfter.totalValidTransactions;
|
|
628
626
|
this.lastValidTransaction = listAfter.lastValidTransaction;
|
|
629
627
|
this.knownTransactions = listAfter.knownTransactions;
|
|
630
628
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
package/src/coValues/coMap.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfrom
|
|
|
10
10
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
11
11
|
import { RawAccountID } from "./account.js";
|
|
12
12
|
import type { RawGroup } from "./group.js";
|
|
13
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
13
14
|
|
|
14
15
|
type MapOp<K extends string, V extends JsonValue | undefined> = {
|
|
15
16
|
txID: TransactionID;
|
|
@@ -57,7 +58,7 @@ export class RawCoMapView<
|
|
|
57
58
|
[Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>[];
|
|
58
59
|
};
|
|
59
60
|
/** @internal */
|
|
60
|
-
knownTransactions:
|
|
61
|
+
knownTransactions: Set<Transaction>;
|
|
61
62
|
|
|
62
63
|
/** @internal */
|
|
63
64
|
ignorePrivateTransactions: boolean;
|
|
@@ -66,7 +67,9 @@ export class RawCoMapView<
|
|
|
66
67
|
/** @category 6. Meta */
|
|
67
68
|
readonly _shape!: Shape;
|
|
68
69
|
|
|
69
|
-
totalValidTransactions
|
|
70
|
+
get totalValidTransactions() {
|
|
71
|
+
return this.knownTransactions.size;
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
/** @internal */
|
|
72
75
|
constructor(
|
|
@@ -83,7 +86,7 @@ export class RawCoMapView<
|
|
|
83
86
|
options?.ignorePrivateTransactions ?? false;
|
|
84
87
|
this.ops = {};
|
|
85
88
|
this.latest = {};
|
|
86
|
-
this.knownTransactions =
|
|
89
|
+
this.knownTransactions = new Set<Transaction>();
|
|
87
90
|
|
|
88
91
|
this.processNewTransactions();
|
|
89
92
|
}
|
|
@@ -113,7 +116,7 @@ export class RawCoMapView<
|
|
|
113
116
|
NonNullable<(typeof ops)[keyof typeof ops]>
|
|
114
117
|
>();
|
|
115
118
|
|
|
116
|
-
for (const { txID, changes, madeAt,
|
|
119
|
+
for (const { txID, changes, madeAt, tx } of newValidTransactions) {
|
|
117
120
|
if (madeAt > this.latestTxMadeAt) {
|
|
118
121
|
this.latestTxMadeAt = madeAt;
|
|
119
122
|
}
|
|
@@ -128,7 +131,7 @@ export class RawCoMapView<
|
|
|
128
131
|
madeAt,
|
|
129
132
|
changeIdx,
|
|
130
133
|
change,
|
|
131
|
-
trusting,
|
|
134
|
+
trusting: tx.privacy === "trusting",
|
|
132
135
|
};
|
|
133
136
|
|
|
134
137
|
const entries = ops[change.key];
|
|
@@ -140,15 +143,9 @@ export class RawCoMapView<
|
|
|
140
143
|
entries.push(entry);
|
|
141
144
|
changedEntries.set(change.key, entries);
|
|
142
145
|
}
|
|
143
|
-
this.knownTransactions[txID.sessionID] = Math.max(
|
|
144
|
-
this.knownTransactions[txID.sessionID] ?? 0,
|
|
145
|
-
txID.txIndex,
|
|
146
|
-
);
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
148
|
|
|
150
|
-
this.totalValidTransactions += newValidTransactions.length;
|
|
151
|
-
|
|
152
149
|
for (const entries of changedEntries.values()) {
|
|
153
150
|
entries.sort(this.core.compareTransactions);
|
|
154
151
|
}
|