@optimystic/db-core 0.14.0 → 0.16.2
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/dist/src/blocks/block-store.d.ts +18 -1
- package/dist/src/blocks/block-store.d.ts.map +1 -1
- package/dist/src/blocks/helpers.d.ts +2 -2
- package/dist/src/blocks/helpers.d.ts.map +1 -1
- package/dist/src/blocks/helpers.js +2 -2
- package/dist/src/blocks/helpers.js.map +1 -1
- package/dist/src/btree/btree.d.ts +27 -6
- package/dist/src/btree/btree.d.ts.map +1 -1
- package/dist/src/btree/btree.js +104 -61
- package/dist/src/btree/btree.js.map +1 -1
- package/dist/src/chain/chain.d.ts.map +1 -1
- package/dist/src/chain/chain.js +11 -1
- package/dist/src/chain/chain.js.map +1 -1
- package/dist/src/cluster/index.d.ts +1 -0
- package/dist/src/cluster/index.d.ts.map +1 -1
- package/dist/src/cluster/index.js +1 -0
- package/dist/src/cluster/index.js.map +1 -1
- package/dist/src/cluster/membership.d.ts +49 -0
- package/dist/src/cluster/membership.d.ts.map +1 -0
- package/dist/src/cluster/membership.js +75 -0
- package/dist/src/cluster/membership.js.map +1 -0
- package/dist/src/cluster/structs.d.ts +50 -1
- package/dist/src/cluster/structs.d.ts.map +1 -1
- package/dist/src/cluster/structs.js +9 -1
- package/dist/src/cluster/structs.js.map +1 -1
- package/dist/src/cohort-topic/addressing.d.ts +8 -2
- package/dist/src/cohort-topic/addressing.d.ts.map +1 -1
- package/dist/src/cohort-topic/addressing.js +11 -3
- package/dist/src/cohort-topic/addressing.js.map +1 -1
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts +13 -7
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.d.ts.map +1 -1
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js +17 -6
- package/dist/src/cohort-topic/antidos/bootstrap-evidence.js.map +1 -1
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts +17 -0
- package/dist/src/cohort-topic/antidos/replay-guard.d.ts.map +1 -1
- package/dist/src/cohort-topic/antidos/replay-guard.js +33 -0
- package/dist/src/cohort-topic/antidos/replay-guard.js.map +1 -1
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts +7 -0
- package/dist/src/cohort-topic/antidos/topic-budget.d.ts.map +1 -1
- package/dist/src/cohort-topic/antidos/topic-budget.js +8 -2
- package/dist/src/cohort-topic/antidos/topic-budget.js.map +1 -1
- package/dist/src/cohort-topic/coldstart.d.ts +34 -14
- package/dist/src/cohort-topic/coldstart.d.ts.map +1 -1
- package/dist/src/cohort-topic/coldstart.js +25 -11
- package/dist/src/cohort-topic/coldstart.js.map +1 -1
- package/dist/src/cohort-topic/gossip/bus.d.ts +12 -1
- package/dist/src/cohort-topic/gossip/bus.d.ts.map +1 -1
- package/dist/src/cohort-topic/gossip/bus.js +27 -1
- package/dist/src/cohort-topic/gossip/bus.js.map +1 -1
- package/dist/src/cohort-topic/gossip/view.d.ts +8 -0
- package/dist/src/cohort-topic/gossip/view.d.ts.map +1 -1
- package/dist/src/cohort-topic/gossip/view.js +17 -1
- package/dist/src/cohort-topic/gossip/view.js.map +1 -1
- package/dist/src/cohort-topic/member-engine.d.ts +10 -3
- package/dist/src/cohort-topic/member-engine.d.ts.map +1 -1
- package/dist/src/cohort-topic/member-engine.js +29 -14
- package/dist/src/cohort-topic/member-engine.js.map +1 -1
- package/dist/src/cohort-topic/membership/publisher.d.ts +14 -7
- package/dist/src/cohort-topic/membership/publisher.d.ts.map +1 -1
- package/dist/src/cohort-topic/membership/publisher.js +15 -27
- package/dist/src/cohort-topic/membership/publisher.js.map +1 -1
- package/dist/src/cohort-topic/membership/verifier.d.ts +35 -0
- package/dist/src/cohort-topic/membership/verifier.d.ts.map +1 -1
- package/dist/src/cohort-topic/membership/verifier.js +88 -3
- package/dist/src/cohort-topic/membership/verifier.js.map +1 -1
- package/dist/src/cohort-topic/promotion.d.ts +7 -0
- package/dist/src/cohort-topic/promotion.d.ts.map +1 -1
- package/dist/src/cohort-topic/promotion.js +16 -2
- package/dist/src/cohort-topic/promotion.js.map +1 -1
- package/dist/src/cohort-topic/registration/renewal.d.ts +18 -0
- package/dist/src/cohort-topic/registration/renewal.d.ts.map +1 -1
- package/dist/src/cohort-topic/registration/renewal.js +54 -0
- package/dist/src/cohort-topic/registration/renewal.js.map +1 -1
- package/dist/src/cohort-topic/registration/types.d.ts +12 -0
- package/dist/src/cohort-topic/registration/types.d.ts.map +1 -1
- package/dist/src/cohort-topic/registration/types.js +14 -0
- package/dist/src/cohort-topic/registration/types.js.map +1 -1
- package/dist/src/cohort-topic/service.d.ts +4 -3
- package/dist/src/cohort-topic/service.d.ts.map +1 -1
- package/dist/src/cohort-topic/service.js +38 -12
- package/dist/src/cohort-topic/service.js.map +1 -1
- package/dist/src/cohort-topic/sig/payloads.d.ts +23 -7
- package/dist/src/cohort-topic/sig/payloads.d.ts.map +1 -1
- package/dist/src/cohort-topic/sig/payloads.js +22 -6
- package/dist/src/cohort-topic/sig/payloads.js.map +1 -1
- package/dist/src/cohort-topic/traffic.d.ts +6 -0
- package/dist/src/cohort-topic/traffic.d.ts.map +1 -1
- package/dist/src/cohort-topic/traffic.js +16 -1
- package/dist/src/cohort-topic/traffic.js.map +1 -1
- package/dist/src/cohort-topic/walk.d.ts +22 -7
- package/dist/src/cohort-topic/walk.d.ts.map +1 -1
- package/dist/src/cohort-topic/walk.js +78 -18
- package/dist/src/cohort-topic/walk.js.map +1 -1
- package/dist/src/cohort-topic/wire/codec.d.ts +3 -1
- package/dist/src/cohort-topic/wire/codec.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/codec.js +7 -1
- package/dist/src/cohort-topic/wire/codec.js.map +1 -1
- package/dist/src/cohort-topic/wire/index.d.ts +1 -1
- package/dist/src/cohort-topic/wire/index.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/index.js +1 -1
- package/dist/src/cohort-topic/wire/index.js.map +1 -1
- package/dist/src/cohort-topic/wire/payloads.d.ts +5 -4
- package/dist/src/cohort-topic/wire/payloads.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/payloads.js +14 -5
- package/dist/src/cohort-topic/wire/payloads.js.map +1 -1
- package/dist/src/cohort-topic/wire/primitives.d.ts +65 -0
- package/dist/src/cohort-topic/wire/primitives.d.ts.map +1 -0
- package/dist/src/cohort-topic/wire/primitives.js +173 -0
- package/dist/src/cohort-topic/wire/primitives.js.map +1 -0
- package/dist/src/cohort-topic/wire/types.d.ts +132 -5
- package/dist/src/cohort-topic/wire/types.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/validate.d.ts +15 -5
- package/dist/src/cohort-topic/wire/validate.d.ts.map +1 -1
- package/dist/src/cohort-topic/wire/validate.js +146 -123
- package/dist/src/cohort-topic/wire/validate.js.map +1 -1
- package/dist/src/collection/collection.d.ts +23 -12
- package/dist/src/collection/collection.d.ts.map +1 -1
- package/dist/src/collection/collection.js +111 -35
- package/dist/src/collection/collection.js.map +1 -1
- package/dist/src/collection/struct.d.ts +37 -2
- package/dist/src/collection/struct.d.ts.map +1 -1
- package/dist/src/collection/struct.js +16 -1
- package/dist/src/collection/struct.js.map +1 -1
- package/dist/src/collections/tree/tree.d.ts +18 -1
- package/dist/src/collections/tree/tree.d.ts.map +1 -1
- package/dist/src/collections/tree/tree.js +36 -6
- package/dist/src/collections/tree/tree.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/log/log.d.ts +26 -3
- package/dist/src/log/log.d.ts.map +1 -1
- package/dist/src/log/log.js +34 -7
- package/dist/src/log/log.js.map +1 -1
- package/dist/src/log/struct.d.ts +5 -4
- package/dist/src/log/struct.d.ts.map +1 -1
- package/dist/src/log/struct.js.map +1 -1
- package/dist/src/matchmaking/wire.d.ts +2 -1
- package/dist/src/matchmaking/wire.d.ts.map +1 -1
- package/dist/src/matchmaking/wire.js +13 -92
- package/dist/src/matchmaking/wire.js.map +1 -1
- package/dist/src/network/i-key-network.d.ts +9 -0
- package/dist/src/network/i-key-network.d.ts.map +1 -1
- package/dist/src/network/i-repo.d.ts +8 -0
- package/dist/src/network/i-repo.d.ts.map +1 -1
- package/dist/src/network/struct.d.ts +10 -0
- package/dist/src/network/struct.d.ts.map +1 -1
- package/dist/src/reactivity/push-state.d.ts.map +1 -1
- package/dist/src/reactivity/push-state.js +4 -33
- package/dist/src/reactivity/push-state.js.map +1 -1
- package/dist/src/reactivity/wire-validate.d.ts +7 -22
- package/dist/src/reactivity/wire-validate.d.ts.map +1 -1
- package/dist/src/reactivity/wire-validate.js +7 -73
- package/dist/src/reactivity/wire-validate.js.map +1 -1
- package/dist/src/reactivity/wire.js +3 -3
- package/dist/src/reactivity/wire.js.map +1 -1
- package/dist/src/testing/async-wait.d.ts +23 -0
- package/dist/src/testing/async-wait.d.ts.map +1 -0
- package/dist/src/testing/async-wait.js +42 -0
- package/dist/src/testing/async-wait.js.map +1 -0
- package/dist/src/testing/index.d.ts +3 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +3 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/testing/test-transactor.d.ts +44 -0
- package/dist/src/testing/test-transactor.d.ts.map +1 -0
- package/dist/src/testing/test-transactor.js +423 -0
- package/dist/src/testing/test-transactor.js.map +1 -0
- package/dist/src/transaction/actions-engine.d.ts +6 -3
- package/dist/src/transaction/actions-engine.d.ts.map +1 -1
- package/dist/src/transaction/actions-engine.js +8 -7
- package/dist/src/transaction/actions-engine.js.map +1 -1
- package/dist/src/transaction/coordinator.d.ts +43 -22
- package/dist/src/transaction/coordinator.d.ts.map +1 -1
- package/dist/src/transaction/coordinator.js +443 -178
- package/dist/src/transaction/coordinator.js.map +1 -1
- package/dist/src/transaction/errors.d.ts +82 -0
- package/dist/src/transaction/errors.d.ts.map +1 -0
- package/dist/src/transaction/errors.js +93 -0
- package/dist/src/transaction/errors.js.map +1 -0
- package/dist/src/transaction/index.d.ts +6 -3
- package/dist/src/transaction/index.d.ts.map +1 -1
- package/dist/src/transaction/index.js +4 -2
- package/dist/src/transaction/index.js.map +1 -1
- package/dist/src/transaction/operations-hash.d.ts +119 -0
- package/dist/src/transaction/operations-hash.d.ts.map +1 -0
- package/dist/src/transaction/operations-hash.js +180 -0
- package/dist/src/transaction/operations-hash.js.map +1 -0
- package/dist/src/transaction/read-dependency-collector.d.ts +43 -0
- package/dist/src/transaction/read-dependency-collector.d.ts.map +1 -0
- package/dist/src/transaction/read-dependency-collector.js +69 -0
- package/dist/src/transaction/read-dependency-collector.js.map +1 -0
- package/dist/src/transaction/session.d.ts +12 -3
- package/dist/src/transaction/session.d.ts.map +1 -1
- package/dist/src/transaction/session.js +35 -9
- package/dist/src/transaction/session.js.map +1 -1
- package/dist/src/transaction/transaction.d.ts +162 -1
- package/dist/src/transaction/transaction.d.ts.map +1 -1
- package/dist/src/transaction/transaction.js +85 -3
- package/dist/src/transaction/transaction.js.map +1 -1
- package/dist/src/transaction/validator.d.ts +3 -11
- package/dist/src/transaction/validator.d.ts.map +1 -1
- package/dist/src/transaction/validator.js +69 -28
- package/dist/src/transaction/validator.js.map +1 -1
- package/dist/src/transactor/network-transactor.d.ts +35 -0
- package/dist/src/transactor/network-transactor.d.ts.map +1 -1
- package/dist/src/transactor/network-transactor.js +146 -40
- package/dist/src/transactor/network-transactor.js.map +1 -1
- package/dist/src/transactor/transactor-source.d.ts +19 -5
- package/dist/src/transactor/transactor-source.d.ts.map +1 -1
- package/dist/src/transactor/transactor-source.js +43 -11
- package/dist/src/transactor/transactor-source.js.map +1 -1
- package/dist/src/transform/atomic-proxy.d.ts +35 -5
- package/dist/src/transform/atomic-proxy.d.ts.map +1 -1
- package/dist/src/transform/atomic-proxy.js +39 -7
- package/dist/src/transform/atomic-proxy.js.map +1 -1
- package/dist/src/transform/cache-source.d.ts +36 -5
- package/dist/src/transform/cache-source.d.ts.map +1 -1
- package/dist/src/transform/cache-source.js +84 -8
- package/dist/src/transform/cache-source.js.map +1 -1
- package/dist/src/transform/helpers.d.ts.map +1 -1
- package/dist/src/transform/helpers.js +8 -2
- package/dist/src/transform/helpers.js.map +1 -1
- package/dist/src/transform/tracker.d.ts +17 -2
- package/dist/src/transform/tracker.d.ts.map +1 -1
- package/dist/src/transform/tracker.js +66 -11
- package/dist/src/transform/tracker.js.map +1 -1
- package/dist/src/utility/backoff.d.ts +48 -0
- package/dist/src/utility/backoff.d.ts.map +1 -0
- package/dist/src/utility/backoff.js +60 -0
- package/dist/src/utility/backoff.js.map +1 -0
- package/dist/src/utility/pending.d.ts +1 -0
- package/dist/src/utility/pending.d.ts.map +1 -1
- package/dist/src/utility/pending.js +3 -1
- package/dist/src/utility/pending.js.map +1 -1
- package/package.json +4 -4
- package/src/blocks/block-store.ts +19 -1
- package/src/blocks/helpers.ts +3 -3
- package/src/btree/btree.ts +108 -63
- package/src/chain/chain.ts +11 -1
- package/src/cluster/index.ts +1 -0
- package/src/cluster/membership.ts +85 -0
- package/src/cluster/structs.ts +51 -1
- package/src/cohort-topic/addressing.ts +12 -4
- package/src/cohort-topic/antidos/bootstrap-evidence.ts +21 -9
- package/src/cohort-topic/antidos/replay-guard.ts +37 -0
- package/src/cohort-topic/antidos/topic-budget.ts +17 -2
- package/src/cohort-topic/coldstart.ts +48 -15
- package/src/cohort-topic/gossip/bus.ts +39 -2
- package/src/cohort-topic/gossip/view.ts +26 -1
- package/src/cohort-topic/member-engine.ts +36 -15
- package/src/cohort-topic/membership/publisher.ts +25 -33
- package/src/cohort-topic/membership/verifier.ts +119 -3
- package/src/cohort-topic/promotion.ts +23 -2
- package/src/cohort-topic/registration/renewal.ts +73 -1
- package/src/cohort-topic/registration/types.ts +15 -0
- package/src/cohort-topic/service.ts +41 -15
- package/src/cohort-topic/sig/payloads.ts +29 -9
- package/src/cohort-topic/traffic.ts +23 -1
- package/src/cohort-topic/walk.ts +95 -24
- package/src/cohort-topic/wire/codec.ts +12 -0
- package/src/cohort-topic/wire/index.ts +2 -0
- package/src/cohort-topic/wire/payloads.ts +14 -5
- package/src/cohort-topic/wire/primitives.ts +188 -0
- package/src/cohort-topic/wire/types.ts +136 -4
- package/src/cohort-topic/wire/validate.ts +168 -134
- package/src/collection/collection.ts +123 -40
- package/src/collection/struct.ts +44 -2
- package/src/collections/tree/tree.ts +42 -5
- package/src/index.ts +1 -0
- package/src/log/log.ts +37 -9
- package/src/log/struct.ts +5 -4
- package/src/matchmaking/wire.ts +25 -102
- package/src/network/i-key-network.ts +10 -0
- package/src/network/i-repo.ts +8 -0
- package/src/network/struct.ts +10 -0
- package/src/reactivity/push-state.ts +4 -37
- package/src/reactivity/wire-validate.ts +7 -81
- package/src/reactivity/wire.ts +3 -3
- package/src/testing/async-wait.ts +65 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/test-transactor.ts +468 -0
- package/src/transaction/actions-engine.ts +8 -6
- package/src/transaction/coordinator.ts +498 -229
- package/src/transaction/errors.ts +91 -0
- package/src/transaction/index.ts +23 -3
- package/src/transaction/operations-hash.ts +196 -0
- package/src/transaction/read-dependency-collector.ts +78 -0
- package/src/transaction/session.ts +37 -9
- package/src/transaction/transaction.ts +211 -4
- package/src/transaction/validator.ts +73 -48
- package/src/transactor/network-transactor.ts +164 -49
- package/src/transactor/transactor-source.ts +47 -12
- package/src/transform/atomic-proxy.ts +92 -49
- package/src/transform/cache-source.ts +87 -9
- package/src/transform/helpers.ts +8 -2
- package/src/transform/tracker.ts +69 -11
- package/src/utility/backoff.ts +95 -0
- package/src/utility/pending.ts +3 -1
- package/src/transaction/context.ts +0 -103
|
@@ -17,6 +17,16 @@ export type TransactionStamp = {
|
|
|
17
17
|
engineId: string;
|
|
18
18
|
/** Absolute ms epoch after which transaction is invalid */
|
|
19
19
|
expiration: number;
|
|
20
|
+
/**
|
|
21
|
+
* Random per-transaction nonce, folded into {@link TransactionStamp.id}.
|
|
22
|
+
*
|
|
23
|
+
* Anti-replay for read-free transactions: a transaction with no reads has nothing
|
|
24
|
+
* binding it to a point in history beyond {@link expiration}, so without this a
|
|
25
|
+
* captured signed read-free transaction could be re-submitted within the TTL window.
|
|
26
|
+
* The nonce makes two otherwise-identical transactions from the same peer at the
|
|
27
|
+
* same millisecond produce different ids (hence different signed bytes).
|
|
28
|
+
*/
|
|
29
|
+
nonce: string;
|
|
20
30
|
/** Hash of the stamp fields (computed) - stable identifier throughout transaction */
|
|
21
31
|
id: string;
|
|
22
32
|
};
|
|
@@ -41,6 +51,42 @@ export type Transaction = {
|
|
|
41
51
|
* Final transaction identity, used in logs
|
|
42
52
|
*/
|
|
43
53
|
id: string;
|
|
54
|
+
/**
|
|
55
|
+
* Optional client signature (base64url) over {@link clientSignaturePayload}
|
|
56
|
+
* `(stamp.id, statements, reads)`, produced at commit by an injected
|
|
57
|
+
* {@link TransactionSigner}.
|
|
58
|
+
*
|
|
59
|
+
* A node that wires a {@link ClientSignatureVerifier} into its
|
|
60
|
+
* {@link TransactionValidator} verifies this at pend; where no verifier is wired,
|
|
61
|
+
* unsigned transactions are still accepted (migration / single-node-dev posture).
|
|
62
|
+
* Plain field on the transaction, so it rides along through serialization/persistence
|
|
63
|
+
* for later recovery-time re-verification.
|
|
64
|
+
*
|
|
65
|
+
* NOTE: preservation is verified only through JSON round-trip and an in-memory session commit —
|
|
66
|
+
* NOT yet through the db-p2p cluster-repo on-disk transaction-log serialization. No recovery
|
|
67
|
+
* reader exists today; when one lands, confirm this field survives that path so `verifyPeerSig`
|
|
68
|
+
* can re-run at recovery time.
|
|
69
|
+
*/
|
|
70
|
+
signature?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Aged, advisory retry priority (default 0 when absent). Rises by one per failed retry
|
|
73
|
+
* attempt (attempt-count-derived — deterministic, no wall clock, clock-skew-free) and is
|
|
74
|
+
* capped at {@link MaxPriority}. A cluster member uses it ONLY as the first tiebreak in a
|
|
75
|
+
* concurrent race between two simultaneously-pending conflicting transactions
|
|
76
|
+
* (`resolveRace`), so a transaction that keeps losing races eventually out-ranks fresh
|
|
77
|
+
* (priority-0) rivals instead of losing at random forever.
|
|
78
|
+
*
|
|
79
|
+
* FAIRNESS-ONLY — MUST NOT influence validity, the operations hash, stale-read checks, the
|
|
80
|
+
* transaction id, or the client signature. It is deliberately excluded from
|
|
81
|
+
* {@link createTransactionId} and {@link clientSignaturePayload} so two attempts of the same
|
|
82
|
+
* logical transaction (differing only in priority) keep the same id and signature; folding it
|
|
83
|
+
* into either would churn identity/anti-replay on every retry. It rides inside the signed
|
|
84
|
+
* cluster `message` (via the pend operation), so it is integrity-protected in transit — a
|
|
85
|
+
* relaying peer cannot strip or inflate it without invalidating the message hash — but the
|
|
86
|
+
* originating coordinator can still self-assert it (a fairness concern, not a safety one; see
|
|
87
|
+
* `docs/correctness.md` Theorem 9).
|
|
88
|
+
*/
|
|
89
|
+
priority?: number;
|
|
44
90
|
};
|
|
45
91
|
/**
|
|
46
92
|
* Read dependency for optimistic concurrency control.
|
|
@@ -58,8 +104,34 @@ export type ReadDependency = {
|
|
|
58
104
|
export type TransactionRef = string;
|
|
59
105
|
/** Default transaction time-to-live in milliseconds (30 seconds). */
|
|
60
106
|
export declare const DEFAULT_TRANSACTION_TTL_MS = 30000;
|
|
107
|
+
/**
|
|
108
|
+
* Upper bound on a transaction's aged {@link Transaction.priority}. Capping is essential: it
|
|
109
|
+
* bounds the fairness effect (an aged transaction cannot accrue unlimited advantage) and bounds
|
|
110
|
+
* the throughput cost (it can impose at most `MaxPriority` extra race-losses on rivals before it
|
|
111
|
+
* commits). Two transactions capped out at `MaxPriority` fall back to the existing promise-count /
|
|
112
|
+
* message-hash tiebreak, so behavior degrades gracefully to the pre-priority status quo.
|
|
113
|
+
*/
|
|
114
|
+
export declare const MaxPriority = 8;
|
|
115
|
+
/**
|
|
116
|
+
* Clamp an attempt-derived priority into `[0, MaxPriority]`. Total: any non-finite / negative
|
|
117
|
+
* input collapses to 0, anything above the cap to {@link MaxPriority}, and the result is floored
|
|
118
|
+
* to an integer. Used both where priority is *set* (the retry loops) and where it is *read* (a
|
|
119
|
+
* cluster member's `resolveRace`), so a Byzantine coordinator self-asserting an out-of-range value
|
|
120
|
+
* cannot exceed the cap.
|
|
121
|
+
*/
|
|
122
|
+
export declare function clampPriority(priority: number | undefined): number;
|
|
61
123
|
/** Check whether a transaction stamp has expired. */
|
|
62
124
|
export declare function isTransactionExpired(stamp: TransactionStamp): boolean;
|
|
125
|
+
/** The stamp fields that feed the id hash (everything except the id itself). */
|
|
126
|
+
export type StampFields = Omit<TransactionStamp, 'id'>;
|
|
127
|
+
/**
|
|
128
|
+
* Compute the canonical stamp id from its fields. MUST match the historical field order
|
|
129
|
+
* (peerId, timestamp, schemaHash, engineId, expiration, nonce) — changing it churns every
|
|
130
|
+
* existing stamp id and client signature. Both {@link createTransactionStamp} and the
|
|
131
|
+
* validator's integrity check derive the id through this one function, so a validating node
|
|
132
|
+
* can re-derive the id and reject a stamp whose fields were tampered while id stayed fixed.
|
|
133
|
+
*/
|
|
134
|
+
export declare function computeStampId(fields: StampFields): Promise<string>;
|
|
63
135
|
/**
|
|
64
136
|
* Create a transaction stamp with computed id.
|
|
65
137
|
* The id is a hash of the stamp fields (including expiration).
|
|
@@ -68,8 +140,51 @@ export declare function createTransactionStamp(peerId: string, timestamp: number
|
|
|
68
140
|
/**
|
|
69
141
|
* Create a transaction id from stamp id, statements, and reads.
|
|
70
142
|
* This is the final transaction identity used in logs.
|
|
143
|
+
*
|
|
144
|
+
* NOTE: reads are serialized in their given order here (non-canonical). This differs
|
|
145
|
+
* from {@link clientSignaturePayload}, which canonicalises reads before hashing. That is
|
|
146
|
+
* intentional for this ticket — the signature payload is self-contained — but the
|
|
147
|
+
* non-canonical ordering here is a separate hygiene concern (see
|
|
148
|
+
* design-consensus-hygiene-notes), NOT fixed here to avoid changing existing tx ids.
|
|
149
|
+
*
|
|
150
|
+
* NOTE: {@link Transaction.priority} is deliberately absent from this preimage — the id is a pure
|
|
151
|
+
* function of `(stampId, statements, reads)`, so two retry attempts that differ only in aged
|
|
152
|
+
* priority produce the SAME id. Do not add priority here.
|
|
71
153
|
*/
|
|
72
154
|
export declare function createTransactionId(stampId: string, statements: string[], reads: ReadDependency[]): Promise<string>;
|
|
155
|
+
/**
|
|
156
|
+
* Version prefix for the client-signature payload, so the signed form can evolve
|
|
157
|
+
* without ambiguity. Distinct from (but consistent in spirit with) the operations-hash
|
|
158
|
+
* canonical form — this covers transaction INPUTS, not operations.
|
|
159
|
+
*/
|
|
160
|
+
export declare const CLIENT_SIG_VERSION = "txsig:v1";
|
|
161
|
+
/**
|
|
162
|
+
* Canonical bytes a client signs and a node verifies. Deterministic: reads are sorted
|
|
163
|
+
* (blockId, then revision); statements keep their sequential order.
|
|
164
|
+
*
|
|
165
|
+
* Binds three things at once: the CLIENT IDENTITY (via `stampId`, which hashes peerId +
|
|
166
|
+
* nonce), the EXACT statements, and the OCC read set (blockId + revision — "tail
|
|
167
|
+
* binding" for anti-replay). Both signer and verifier derive the bytes from
|
|
168
|
+
* `transaction.stamp.id` + `transaction.statements` + `transaction.reads` through this
|
|
169
|
+
* one function, so they reproduce identical bytes regardless of read ordering.
|
|
170
|
+
*
|
|
171
|
+
* NOTE: {@link Transaction.priority} is deliberately absent from the signed bytes — a retry that
|
|
172
|
+
* only bumps priority keeps the same signature, so anti-replay / OCC binding never churns. Do not
|
|
173
|
+
* add priority here.
|
|
174
|
+
*/
|
|
175
|
+
export declare function clientSignaturePayload(stampId: string, statements: readonly string[], reads: readonly ReadDependency[]): Uint8Array;
|
|
176
|
+
/**
|
|
177
|
+
* Signs the canonical client-signature payload, returning base64url. Async to allow
|
|
178
|
+
* libp2p `PrivateKey.sign`; tests pass a synchronous fake.
|
|
179
|
+
*/
|
|
180
|
+
export type TransactionSigner = (payload: Uint8Array) => Promise<string> | string;
|
|
181
|
+
/**
|
|
182
|
+
* Returns true iff `signature` (base64url) is a valid client signature over `payload`
|
|
183
|
+
* for signer identity `peerId`. Total: returns false, never throws, on any malformed
|
|
184
|
+
* input. The p2p wiring backs this with verifyPeerSig + peerIdBindsPublicKey; tests
|
|
185
|
+
* pass a fake.
|
|
186
|
+
*/
|
|
187
|
+
export type ClientSignatureVerifier = (peerId: string, payload: Uint8Array, signature: string) => boolean;
|
|
73
188
|
/**
|
|
74
189
|
* Transaction engine interface.
|
|
75
190
|
* Pluggable engines implement this to process transaction statements.
|
|
@@ -78,8 +193,40 @@ export declare function createTransactionId(stampId: string, statements: string[
|
|
|
78
193
|
* 1. Parsing the engine-specific statements
|
|
79
194
|
* 2. Executing/re-executing to produce actions
|
|
80
195
|
* 3. Returning the resulting actions per collection
|
|
196
|
+
*
|
|
197
|
+
* CONTRACT — an engine must never cause the same action to be applied twice. The
|
|
198
|
+
* caller (a {@link TransactionSession} on the no-pre-supplied-actions branch, or a
|
|
199
|
+
* {@link TransactionCoordinator} in `execute()`) applies whatever non-empty
|
|
200
|
+
* `CollectionActions[]` this method RETURNS. Therefore `execute()` must satisfy
|
|
201
|
+
* exactly ONE of:
|
|
202
|
+
*
|
|
203
|
+
* (a) PURE TRANSLATOR (preferred; how {@link ActionsEngine} works) — parse the
|
|
204
|
+
* statements, RETURN the actions, and do NOT apply them / do NOT call
|
|
205
|
+
* `coordinator.applyActions` / do NOT mutate any collection state. The caller
|
|
206
|
+
* applies them exactly once.
|
|
207
|
+
*
|
|
208
|
+
* (b) SIDE-EFFECT APPLY — apply the actions itself while translating (e.g. the
|
|
209
|
+
* Quereus vtab path stages rows into the coordinator during `db.exec`) and then
|
|
210
|
+
* RETURN an EMPTY actions array, so the caller re-applies nothing.
|
|
211
|
+
*
|
|
212
|
+
* An engine that BOTH applies as a side effect AND returns those same actions
|
|
213
|
+
* double-applies (the caller re-applies the returned actions). Additionally, a
|
|
214
|
+
* side-effecting engine used for validation must apply only to the validator's
|
|
215
|
+
* ISOLATED coordinator — applying to the main coordinator leaks into the validating
|
|
216
|
+
* node's live state.
|
|
81
217
|
*/
|
|
82
218
|
export interface ITransactionEngine {
|
|
219
|
+
/**
|
|
220
|
+
* Stable identifier for this engine.
|
|
221
|
+
*
|
|
222
|
+
* This is the value stamped into {@link TransactionStamp.engineId} when a
|
|
223
|
+
* {@link TransactionSession} builds a transaction, and the key a
|
|
224
|
+
* {@link TransactionValidator} resolves the engine by. It MUST match the key the
|
|
225
|
+
* engine is registered under in the validator's `engines` map — otherwise a
|
|
226
|
+
* validating node rejects the transaction as `Unknown engine: <id>` before it
|
|
227
|
+
* ever re-executes.
|
|
228
|
+
*/
|
|
229
|
+
readonly id: string;
|
|
83
230
|
/**
|
|
84
231
|
* Process a transaction statements to produce actions.
|
|
85
232
|
*
|
|
@@ -104,6 +251,16 @@ export type ExecutionResult = {
|
|
|
104
251
|
results?: Map<CollectionId, any[]>;
|
|
105
252
|
/** Error message if execution failed */
|
|
106
253
|
error?: string;
|
|
254
|
+
/**
|
|
255
|
+
* On a PARTIAL multi-collection commit failure, the collections that DID durably
|
|
256
|
+
* commit through consensus before the failure (and thus CANNOT be rolled back —
|
|
257
|
+
* reconciliation is required). Absent/empty means nothing durably committed, so
|
|
258
|
+
* the caller may treat the failure as a clean abort. See
|
|
259
|
+
* {@link CoordinatorPartialCommitError} for the session-mode (commit) analog.
|
|
260
|
+
*/
|
|
261
|
+
committedCollections?: CollectionId[];
|
|
262
|
+
/** On a partial multi-collection commit failure, the collections that failed to commit. */
|
|
263
|
+
failedCollections?: CollectionId[];
|
|
107
264
|
};
|
|
108
265
|
/**
|
|
109
266
|
* Actions for a specific collection resulting from transaction execution.
|
|
@@ -148,7 +305,11 @@ export interface ITransactionValidator {
|
|
|
148
305
|
/**
|
|
149
306
|
* Validate a transaction by re-executing and comparing operations hash.
|
|
150
307
|
*
|
|
151
|
-
* Validation steps:
|
|
308
|
+
* Validation steps (see TransactionValidator.validate for the authoritative order):
|
|
309
|
+
* 0.0. Verify stamp.id is the true hash of the stamp fields (integrity — runs first,
|
|
310
|
+
* since every later step trusts a stamp field)
|
|
311
|
+
* 0. Verify the stamp has not expired
|
|
312
|
+
* 0.5. Verify the client signature (only if a signature verifier is wired)
|
|
152
313
|
* 1. Verify stamp.engineId matches a known engine
|
|
153
314
|
* 2. Verify stamp.schemaHash matches local schema
|
|
154
315
|
* 3. Verify read dependencies (no stale reads)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../../src/transaction/transaction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../../src/transaction/transaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,28 +1,110 @@
|
|
|
1
|
+
import { randomBytes } from "@noble/hashes/utils.js";
|
|
2
|
+
import { toString as uint8ArrayToString } from "uint8arrays/to-string";
|
|
1
3
|
import { hashString } from "../utility/hash-string.js";
|
|
2
4
|
/** Default transaction time-to-live in milliseconds (30 seconds). */
|
|
3
5
|
export const DEFAULT_TRANSACTION_TTL_MS = 30_000;
|
|
6
|
+
/**
|
|
7
|
+
* Upper bound on a transaction's aged {@link Transaction.priority}. Capping is essential: it
|
|
8
|
+
* bounds the fairness effect (an aged transaction cannot accrue unlimited advantage) and bounds
|
|
9
|
+
* the throughput cost (it can impose at most `MaxPriority` extra race-losses on rivals before it
|
|
10
|
+
* commits). Two transactions capped out at `MaxPriority` fall back to the existing promise-count /
|
|
11
|
+
* message-hash tiebreak, so behavior degrades gracefully to the pre-priority status quo.
|
|
12
|
+
*/
|
|
13
|
+
export const MaxPriority = 8;
|
|
14
|
+
/**
|
|
15
|
+
* Clamp an attempt-derived priority into `[0, MaxPriority]`. Total: any non-finite / negative
|
|
16
|
+
* input collapses to 0, anything above the cap to {@link MaxPriority}, and the result is floored
|
|
17
|
+
* to an integer. Used both where priority is *set* (the retry loops) and where it is *read* (a
|
|
18
|
+
* cluster member's `resolveRace`), so a Byzantine coordinator self-asserting an out-of-range value
|
|
19
|
+
* cannot exceed the cap.
|
|
20
|
+
*/
|
|
21
|
+
export function clampPriority(priority) {
|
|
22
|
+
if (typeof priority !== 'number' || Number.isNaN(priority) || priority <= 0) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
// A positive out-of-range value (including +Infinity, or a Byzantine self-asserted number) clamps
|
|
26
|
+
// DOWN to the cap; anything strictly inside the range is floored to an integer.
|
|
27
|
+
if (priority >= MaxPriority) {
|
|
28
|
+
return MaxPriority;
|
|
29
|
+
}
|
|
30
|
+
return Math.floor(priority);
|
|
31
|
+
}
|
|
4
32
|
/** Check whether a transaction stamp has expired. */
|
|
5
33
|
export function isTransactionExpired(stamp) {
|
|
6
34
|
return Date.now() > stamp.expiration;
|
|
7
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Compute the canonical stamp id from its fields. MUST match the historical field order
|
|
38
|
+
* (peerId, timestamp, schemaHash, engineId, expiration, nonce) — changing it churns every
|
|
39
|
+
* existing stamp id and client signature. Both {@link createTransactionStamp} and the
|
|
40
|
+
* validator's integrity check derive the id through this one function, so a validating node
|
|
41
|
+
* can re-derive the id and reject a stamp whose fields were tampered while id stayed fixed.
|
|
42
|
+
*/
|
|
43
|
+
export async function computeStampId(fields) {
|
|
44
|
+
// Destructure and rebuild in fixed order rather than passing `fields` straight to
|
|
45
|
+
// JSON.stringify — this ignores any stray `id` (or extra) property a caller slipped in,
|
|
46
|
+
// so the serialized bytes depend on exactly the six fields and nothing else.
|
|
47
|
+
const { peerId, timestamp, schemaHash, engineId, expiration, nonce } = fields;
|
|
48
|
+
const stampData = JSON.stringify({ peerId, timestamp, schemaHash, engineId, expiration, nonce });
|
|
49
|
+
return `stamp:${await hashString(stampData)}`;
|
|
50
|
+
}
|
|
8
51
|
/**
|
|
9
52
|
* Create a transaction stamp with computed id.
|
|
10
53
|
* The id is a hash of the stamp fields (including expiration).
|
|
11
54
|
*/
|
|
12
55
|
export async function createTransactionStamp(peerId, timestamp, schemaHash, engineId, ttlMs = DEFAULT_TRANSACTION_TTL_MS) {
|
|
13
56
|
const expiration = timestamp + ttlMs;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
57
|
+
// Cross-platform CSPRNG (Node + browser) via @noble/hashes — matches the rest of
|
|
58
|
+
// db-core (see collection.ts, transactor-source.ts). The nonce is folded into the
|
|
59
|
+
// id hash so two otherwise-identical stamps diverge.
|
|
60
|
+
const nonce = uint8ArrayToString(randomBytes(16), 'base64url');
|
|
61
|
+
const id = await computeStampId({ peerId, timestamp, schemaHash, engineId, expiration, nonce });
|
|
62
|
+
return { peerId, timestamp, schemaHash, engineId, expiration, nonce, id };
|
|
17
63
|
}
|
|
18
64
|
/**
|
|
19
65
|
* Create a transaction id from stamp id, statements, and reads.
|
|
20
66
|
* This is the final transaction identity used in logs.
|
|
67
|
+
*
|
|
68
|
+
* NOTE: reads are serialized in their given order here (non-canonical). This differs
|
|
69
|
+
* from {@link clientSignaturePayload}, which canonicalises reads before hashing. That is
|
|
70
|
+
* intentional for this ticket — the signature payload is self-contained — but the
|
|
71
|
+
* non-canonical ordering here is a separate hygiene concern (see
|
|
72
|
+
* design-consensus-hygiene-notes), NOT fixed here to avoid changing existing tx ids.
|
|
73
|
+
*
|
|
74
|
+
* NOTE: {@link Transaction.priority} is deliberately absent from this preimage — the id is a pure
|
|
75
|
+
* function of `(stampId, statements, reads)`, so two retry attempts that differ only in aged
|
|
76
|
+
* priority produce the SAME id. Do not add priority here.
|
|
21
77
|
*/
|
|
22
78
|
export async function createTransactionId(stampId, statements, reads) {
|
|
23
79
|
const txData = JSON.stringify({ stampId, statements, reads });
|
|
24
80
|
return `tx:${await hashString(txData)}`;
|
|
25
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Version prefix for the client-signature payload, so the signed form can evolve
|
|
84
|
+
* without ambiguity. Distinct from (but consistent in spirit with) the operations-hash
|
|
85
|
+
* canonical form — this covers transaction INPUTS, not operations.
|
|
86
|
+
*/
|
|
87
|
+
export const CLIENT_SIG_VERSION = 'txsig:v1';
|
|
88
|
+
/**
|
|
89
|
+
* Canonical bytes a client signs and a node verifies. Deterministic: reads are sorted
|
|
90
|
+
* (blockId, then revision); statements keep their sequential order.
|
|
91
|
+
*
|
|
92
|
+
* Binds three things at once: the CLIENT IDENTITY (via `stampId`, which hashes peerId +
|
|
93
|
+
* nonce), the EXACT statements, and the OCC read set (blockId + revision — "tail
|
|
94
|
+
* binding" for anti-replay). Both signer and verifier derive the bytes from
|
|
95
|
+
* `transaction.stamp.id` + `transaction.statements` + `transaction.reads` through this
|
|
96
|
+
* one function, so they reproduce identical bytes regardless of read ordering.
|
|
97
|
+
*
|
|
98
|
+
* NOTE: {@link Transaction.priority} is deliberately absent from the signed bytes — a retry that
|
|
99
|
+
* only bumps priority keeps the same signature, so anti-replay / OCC binding never churns. Do not
|
|
100
|
+
* add priority here.
|
|
101
|
+
*/
|
|
102
|
+
export function clientSignaturePayload(stampId, statements, reads) {
|
|
103
|
+
const canonicalReads = [...reads].sort((a, b) => a.blockId < b.blockId ? -1 : a.blockId > b.blockId ? 1
|
|
104
|
+
: a.revision - b.revision);
|
|
105
|
+
const body = JSON.stringify({ stampId, statements, reads: canonicalReads });
|
|
106
|
+
return new TextEncoder().encode(`${CLIENT_SIG_VERSION}:${body}`);
|
|
107
|
+
}
|
|
26
108
|
/**
|
|
27
109
|
* Helper to create an actions-based transaction statements array.
|
|
28
110
|
* Each CollectionActions becomes a separate JSON-encoded statement.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../src/transaction/transaction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"","sources":["../../../src/transaction/transaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlockId, CollectionId, Transforms } from '../index.js';
|
|
2
|
-
import type { Transaction, ITransactionEngine, ITransactionValidator, ValidationResult, CollectionActions } from './transaction.js';
|
|
2
|
+
import type { Transaction, ITransactionEngine, ITransactionValidator, ValidationResult, CollectionActions, ClientSignatureVerifier } from './transaction.js';
|
|
3
3
|
import type { BlockActionState } from '../network/struct.js';
|
|
4
4
|
/**
|
|
5
5
|
* Engine registration for validation.
|
|
@@ -37,17 +37,9 @@ export declare class TransactionValidator implements ITransactionValidator {
|
|
|
37
37
|
private readonly engines;
|
|
38
38
|
private readonly createValidationCoordinator;
|
|
39
39
|
private readonly blockStateProvider?;
|
|
40
|
-
|
|
40
|
+
private readonly verifyClientSignature?;
|
|
41
|
+
constructor(engines: Map<string, EngineRegistration>, createValidationCoordinator: ValidationCoordinatorFactory, blockStateProvider?: BlockStateProvider | undefined, verifyClientSignature?: ClientSignatureVerifier | undefined);
|
|
41
42
|
validate(transaction: Transaction, operationsHash: string): Promise<ValidationResult>;
|
|
42
43
|
getSchemaHash(engineId: string): Promise<string | undefined>;
|
|
43
|
-
/**
|
|
44
|
-
* Collect all operations from transforms.
|
|
45
|
-
*/
|
|
46
|
-
private collectOperations;
|
|
47
|
-
/**
|
|
48
|
-
* Compute hash of all operations.
|
|
49
|
-
* Must match TransactionCoordinator.hashOperations for consistent validation.
|
|
50
|
-
*/
|
|
51
|
-
private hashOperations;
|
|
52
44
|
}
|
|
53
45
|
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/transaction/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/transaction/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC7J,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAI7D;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAChC,sCAAsC;IACtC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,kDAAkD;IAClD,aAAa,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACrC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,4BAA4B,GAAG,MAAM;IAChD,qDAAqD;IACrD,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,mDAAmD;IACnD,aAAa,IAAI,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IAC/C,4CAA4C;IAC5C,OAAO,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;AAE7F;;;;;GAKG;AACH,qBAAa,oBAAqB,YAAW,qBAAqB;IAEhE,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,2BAA2B;IAC5C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAC;gBAHtB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACxC,2BAA2B,EAAE,4BAA4B,EACzD,kBAAkB,CAAC,EAAE,kBAAkB,YAAA,EACvC,qBAAqB,CAAC,EAAE,uBAAuB,YAAA;IAG3D,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA8IrF,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAIlE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isTransactionExpired } from './transaction.js';
|
|
2
|
-
import {
|
|
1
|
+
import { isTransactionExpired, clientSignaturePayload, computeStampId } from './transaction.js';
|
|
2
|
+
import { collectOperations, hashOperations, opsHashVersion, OPS_HASH_VERSION } from './operations-hash.js';
|
|
3
3
|
/**
|
|
4
4
|
* Transaction validator implementation.
|
|
5
5
|
*
|
|
@@ -10,20 +10,43 @@ export class TransactionValidator {
|
|
|
10
10
|
engines;
|
|
11
11
|
createValidationCoordinator;
|
|
12
12
|
blockStateProvider;
|
|
13
|
-
|
|
13
|
+
verifyClientSignature;
|
|
14
|
+
constructor(engines, createValidationCoordinator, blockStateProvider, verifyClientSignature) {
|
|
14
15
|
this.engines = engines;
|
|
15
16
|
this.createValidationCoordinator = createValidationCoordinator;
|
|
16
17
|
this.blockStateProvider = blockStateProvider;
|
|
18
|
+
this.verifyClientSignature = verifyClientSignature;
|
|
17
19
|
}
|
|
18
20
|
async validate(transaction, operationsHash) {
|
|
19
21
|
const { stamp } = transaction;
|
|
20
|
-
// 0.
|
|
22
|
+
// 0.0. Integrity: the id must be the true hash of the stamp fields. Runs FIRST because
|
|
23
|
+
// every later step (expiration, signature, engine/schema selection) trusts a stamp field.
|
|
24
|
+
// Always-on (one SHA-256, cheap) and independent of whether a signature verifier is wired —
|
|
25
|
+
// a tampered stamp corrupts expiration and engine selection regardless of signatures.
|
|
26
|
+
const expectedId = await computeStampId(stamp);
|
|
27
|
+
if (expectedId !== stamp.id) {
|
|
28
|
+
return { valid: false, reason: 'Tampered transaction stamp' };
|
|
29
|
+
}
|
|
30
|
+
// 0. Check expiration before any other work. Ordered BEFORE the signature check so
|
|
31
|
+
// an attacker cannot learn signature-validity for an already-expired transaction.
|
|
21
32
|
if (isTransactionExpired(stamp)) {
|
|
22
33
|
return {
|
|
23
34
|
valid: false,
|
|
24
35
|
reason: `Transaction expired at ${stamp.expiration}`
|
|
25
36
|
};
|
|
26
37
|
}
|
|
38
|
+
// 0.5. Verify the client signature, if a verifier port is wired. When the port is
|
|
39
|
+
// absent (migration / single-node-dev posture) unsigned AND signed transactions
|
|
40
|
+
// both pass — the p2p enforcement flag decides whether to inject the port at all.
|
|
41
|
+
if (this.verifyClientSignature) {
|
|
42
|
+
if (transaction.signature === undefined) {
|
|
43
|
+
return { valid: false, reason: 'Missing client signature' };
|
|
44
|
+
}
|
|
45
|
+
const payload = clientSignaturePayload(stamp.id, transaction.statements, transaction.reads);
|
|
46
|
+
if (!this.verifyClientSignature(stamp.peerId, payload, transaction.signature)) {
|
|
47
|
+
return { valid: false, reason: 'Invalid client signature' };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
27
50
|
// 1. Verify engine exists
|
|
28
51
|
const registration = this.engines.get(stamp.engineId);
|
|
29
52
|
if (!registration) {
|
|
@@ -56,7 +79,24 @@ export class TransactionValidator {
|
|
|
56
79
|
// 4. Create isolated validation coordinator
|
|
57
80
|
const validationCoordinator = this.createValidationCoordinator();
|
|
58
81
|
try {
|
|
59
|
-
// 5. Re-execute transaction through engine
|
|
82
|
+
// 5. Re-execute transaction through engine.
|
|
83
|
+
//
|
|
84
|
+
// Which of the two ITransactionEngine models the registered engine uses decides
|
|
85
|
+
// where isolation comes from (see the contract in transaction.ts):
|
|
86
|
+
//
|
|
87
|
+
// (a) PURE TRANSLATOR (ActionsEngine) — execute() parses statements into
|
|
88
|
+
// actions and mutates NOTHING, so even an engine constructed against the
|
|
89
|
+
// MAIN coordinator cannot leak into main state here. The returned actions
|
|
90
|
+
// are applied once, on the isolated validationCoordinator at step 6.
|
|
91
|
+
//
|
|
92
|
+
// (b) SIDE-EFFECT APPLY (QuereusEngine) — execute() applies while translating
|
|
93
|
+
// (db.exec drives the vtab into coordinator.applyActions) and returns EMPTY
|
|
94
|
+
// actions. That application lands on whatever coordinator the engine/vtab is
|
|
95
|
+
// bound to, NOT on validationCoordinator, so isolation is the caller's job:
|
|
96
|
+
// the createValidationCoordinator wiring must bind/reset an isolated world
|
|
97
|
+
// (see quereus-validator, which resets the coordinator before each run and
|
|
98
|
+
// makes its validationCoordinator.applyActions a no-op). Step 6 is then
|
|
99
|
+
// skipped by the empty-actions guard.
|
|
60
100
|
const result = await registration.engine.execute(transaction);
|
|
61
101
|
if (!result.success) {
|
|
62
102
|
return {
|
|
@@ -64,17 +104,36 @@ export class TransactionValidator {
|
|
|
64
104
|
reason: `Re-execution failed: ${result.error}`
|
|
65
105
|
};
|
|
66
106
|
}
|
|
67
|
-
// 6. Apply actions to validation coordinator (builds transforms)
|
|
107
|
+
// 6. Apply actions to the isolated validation coordinator (builds transforms).
|
|
108
|
+
// Reached only for pure-translator engines that RETURNED actions; a side-effect
|
|
109
|
+
// engine returns empty and already applied during execute() (see above).
|
|
68
110
|
if (result.actions && result.actions.length > 0) {
|
|
69
111
|
await validationCoordinator.applyActions(result.actions, stamp.id);
|
|
70
112
|
}
|
|
71
113
|
// 7. Collect operations from validation coordinator
|
|
72
114
|
const transforms = validationCoordinator.getTransforms();
|
|
73
|
-
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
115
|
+
// 8. Compute hash via the shared operations-hash module — the SAME collect +
|
|
116
|
+
// canonicalise + hash the coordinator ran, so an honest sender and this
|
|
117
|
+
// validator cannot diverge on ordering.
|
|
118
|
+
const computedHash = await hashOperations(collectOperations(transforms));
|
|
119
|
+
// 9. Compare with sender's hash. Split the failure into two distinct causes so
|
|
120
|
+
// version skew is diagnosable rather than looking like a content fault:
|
|
121
|
+
// - VERSION SKEW: the sender's token carries a format version this node does not
|
|
122
|
+
// produce (a foreign vN, a bare legacy `ops:`, or an unparseable token → null).
|
|
123
|
+
// These bytes are not even comparable, so surface a clear "unsupported format
|
|
124
|
+
// version" error. This is DETECTION only — the node cannot cross-compute the
|
|
125
|
+
// peer's format, so a mixed-version cluster fails legibly, not silently.
|
|
126
|
+
// - CONTENT MISMATCH: same format version, different bytes — a genuine operations
|
|
127
|
+
// disagreement (or a Byzantine lie); the existing error is unchanged.
|
|
77
128
|
if (computedHash !== operationsHash) {
|
|
129
|
+
const senderVersion = opsHashVersion(operationsHash);
|
|
130
|
+
if (senderVersion !== OPS_HASH_VERSION) {
|
|
131
|
+
return {
|
|
132
|
+
valid: false,
|
|
133
|
+
reason: `Unsupported operations-hash format version: local=${OPS_HASH_VERSION}, sender=${senderVersion ?? 'unrecognized'}`,
|
|
134
|
+
computedHash
|
|
135
|
+
};
|
|
136
|
+
}
|
|
78
137
|
return {
|
|
79
138
|
valid: false,
|
|
80
139
|
reason: `Operations hash mismatch`,
|
|
@@ -91,23 +150,5 @@ export class TransactionValidator {
|
|
|
91
150
|
const registration = this.engines.get(engineId);
|
|
92
151
|
return registration ? await registration.getSchemaHash() : undefined;
|
|
93
152
|
}
|
|
94
|
-
/**
|
|
95
|
-
* Collect all operations from transforms.
|
|
96
|
-
*/
|
|
97
|
-
collectOperations(transforms) {
|
|
98
|
-
return Array.from(transforms.entries()).flatMap(([collectionId, t]) => [
|
|
99
|
-
...Object.entries(t.inserts ?? {}).map(([blockId, block]) => ({ type: 'insert', collectionId, blockId, block })),
|
|
100
|
-
...Object.entries(t.updates ?? {}).map(([blockId, operations]) => ({ type: 'update', collectionId, blockId, operations })),
|
|
101
|
-
...(t.deletes ?? []).map(blockId => ({ type: 'delete', collectionId, blockId }))
|
|
102
|
-
]);
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Compute hash of all operations.
|
|
106
|
-
* Must match TransactionCoordinator.hashOperations for consistent validation.
|
|
107
|
-
*/
|
|
108
|
-
async hashOperations(operations) {
|
|
109
|
-
const operationsData = JSON.stringify(operations);
|
|
110
|
-
return `ops:${await hashString(operationsData)}`;
|
|
111
|
-
}
|
|
112
153
|
}
|
|
113
154
|
//# sourceMappingURL=validator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/transaction/validator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/transaction/validator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AA+B3G;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IAEd;IACA;IACA;IACA;IAJlB,YACkB,OAAwC,EACxC,2BAAyD,EACzD,kBAAuC,EACvC,qBAA+C;QAH/C,YAAO,GAAP,OAAO,CAAiC;QACxC,gCAA2B,GAA3B,2BAA2B,CAA8B;QACzD,uBAAkB,GAAlB,kBAAkB,CAAqB;QACvC,0BAAqB,GAArB,qBAAqB,CAA0B;IAC9D,CAAC;IAEJ,KAAK,CAAC,QAAQ,CAAC,WAAwB,EAAE,cAAsB;QAC9D,MAAM,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC;QAE9B,uFAAuF;QACvF,0FAA0F;QAC1F,4FAA4F;QAC5F,sFAAsF;QACtF,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,UAAU,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;YAC7B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,4BAA4B,EAAE,CAAC;QAC/D,CAAC;QAED,mFAAmF;QACnF,kFAAkF;QAClF,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO;gBACN,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,0BAA0B,KAAK,CAAC,UAAU,EAAE;aACpD,CAAC;QACH,CAAC;QAED,kFAAkF;QAClF,gFAAgF;QAChF,kFAAkF;QAClF,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAChC,IAAI,WAAW,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBACzC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;YAC7D,CAAC;YACD,MAAM,OAAO,GAAG,sBAAsB,CAAC,KAAK,CAAC,EAAE,EAAE,WAAW,CAAC,UAAU,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;YAC5F,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC/E,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAC;YAC7D,CAAC;QACF,CAAC;QAED,0BAA0B;QAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtD,IAAI,CAAC,YAAY,EAAE,CAAC;YACnB,OAAO;gBACN,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,mBAAmB,KAAK,CAAC,QAAQ,EAAE;aAC3C,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,aAAa,EAAE,CAAC;QAC3D,IAAI,eAAe,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YAC1C,OAAO;gBACN,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,0BAA0B,eAAe,YAAY,KAAK,CAAC,UAAU,EAAE;aAC/E,CAAC;QACH,CAAC;QAED,uDAAuD;QACvD,IAAI,IAAI,CAAC,kBAAkB,IAAI,WAAW,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7D,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;gBACtC,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACjE,MAAM,UAAU,GAAG,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;gBAClD,IAAI,UAAU,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAClC,OAAO;wBACN,KAAK,EAAE,KAAK;wBACZ,MAAM,EAAE,qBAAqB,IAAI,CAAC,OAAO,oBAAoB,IAAI,CAAC,QAAQ,kBAAkB,UAAU,EAAE;qBACxG,CAAC;gBACH,CAAC;YACF,CAAC;QACF,CAAC;QAED,4CAA4C;QAC5C,MAAM,qBAAqB,GAAG,IAAI,CAAC,2BAA2B,EAAE,CAAC;QAEjE,IAAI,CAAC;YACJ,4CAA4C;YAC5C,EAAE;YACF,gFAAgF;YAChF,mEAAmE;YACnE,EAAE;YACF,2EAA2E;YAC3E,+EAA+E;YAC/E,gFAAgF;YAChF,2EAA2E;YAC3E,EAAE;YACF,gFAAgF;YAChF,kFAAkF;YAClF,mFAAmF;YACnF,kFAAkF;YAClF,iFAAiF;YACjF,iFAAiF;YACjF,8EAA8E;YAC9E,4CAA4C;YAC5C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC9D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO;oBACN,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,wBAAwB,MAAM,CAAC,KAAK,EAAE;iBAC9C,CAAC;YACH,CAAC;YAED,+EAA+E;YAC/E,gFAAgF;YAChF,yEAAyE;YACzE,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjD,MAAM,qBAAqB,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YACpE,CAAC;YAED,oDAAoD;YACpD,MAAM,UAAU,GAAG,qBAAqB,CAAC,aAAa,EAAE,CAAC;YAEzD,6EAA6E;YAC7E,wEAAwE;YACxE,wCAAwC;YACxC,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;YAEzE,+EAA+E;YAC/E,wEAAwE;YACxE,mFAAmF;YACnF,oFAAoF;YACpF,kFAAkF;YAClF,iFAAiF;YACjF,6EAA6E;YAC7E,oFAAoF;YACpF,0EAA0E;YAC1E,IAAI,YAAY,KAAK,cAAc,EAAE,CAAC;gBACrC,MAAM,aAAa,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;gBACrD,IAAI,aAAa,KAAK,gBAAgB,EAAE,CAAC;oBACxC,OAAO;wBACN,KAAK,EAAE,KAAK;wBACZ,MAAM,EAAE,qDAAqD,gBAAgB,YAAY,aAAa,IAAI,cAAc,EAAE;wBAC1H,YAAY;qBACZ,CAAC;gBACH,CAAC;gBACD,OAAO;oBACN,KAAK,EAAE,KAAK;oBACZ,MAAM,EAAE,0BAA0B;oBAClC,YAAY;iBACZ,CAAC;YACH,CAAC;YAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QACtC,CAAC;gBAAS,CAAC;YACV,qBAAqB,CAAC,OAAO,EAAE,CAAC;QACjC,CAAC;IACF,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAgB;QACnC,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAChD,OAAO,YAAY,CAAC,CAAC,CAAC,MAAM,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,CAAC;CACD"}
|
|
@@ -31,6 +31,25 @@ export declare class NetworkTransactor implements ITransactor, IBlockChangeNotif
|
|
|
31
31
|
private readonly dialTimeoutMs;
|
|
32
32
|
private readonly getRepo;
|
|
33
33
|
private readonly localChangeNotifier;
|
|
34
|
+
/**
|
|
35
|
+
* Per-transaction coordinator cache: `actionId → (blockId → resolved coordinator)`.
|
|
36
|
+
* {@link pend} populates it from its final (retry-adjusted) batch assignment; commit
|
|
37
|
+
* reads it via {@link resolveCoordinator} before falling back to a live
|
|
38
|
+
* `findCoordinator`, so a block's coordinator is resolved once per transaction across
|
|
39
|
+
* the pend→commit window instead of once at pend and again at commit.
|
|
40
|
+
*
|
|
41
|
+
* Keyed by `actionId`, which is unique per transaction, so an entry is only ever read
|
|
42
|
+
* by commits of the SAME transaction — the ones that immediately follow its pend. Once
|
|
43
|
+
* those finish, nothing reads the entry again (a later transaction has a fresh
|
|
44
|
+
* actionId), so it carries no cross-transaction staleness even if it lingers. The TTL
|
|
45
|
+
* and size cap in {@link txnCoordinatorsFor} are therefore only a memory backstop that
|
|
46
|
+
* reclaims entries from transactions that pend but never commit — NOT a staleness
|
|
47
|
+
* bound. This is why keying by actionId gives the same "thrown away when the
|
|
48
|
+
* transaction ends" safety as threading a Map through the call, without touching the
|
|
49
|
+
* ITransactor contract.
|
|
50
|
+
*/
|
|
51
|
+
private readonly txnCoordinatorCache;
|
|
52
|
+
private static readonly MAX_TXN_COORDINATOR_CACHE_ENTRIES;
|
|
34
53
|
constructor(init: NetworkTransactorInit);
|
|
35
54
|
/**
|
|
36
55
|
* Subscribe to commits landing on the local node for `collectionId`, delegating
|
|
@@ -66,6 +85,22 @@ export declare class NetworkTransactor implements ITransactor, IBlockChangeNotif
|
|
|
66
85
|
private commitBlocks;
|
|
67
86
|
/** Creates batches for a given payload, grouped by the coordinating peer for each block id */
|
|
68
87
|
private batchesForPayload;
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the coordinator for `blockId`, preferring one this transaction already
|
|
90
|
+
* resolved during pend (the per-transaction cache) before falling back to a live
|
|
91
|
+
* `findCoordinator`. A cached coordinator that is in `excludedPeers` (already tried and
|
|
92
|
+
* failed during a retry) is skipped so a retry can't loop on a dead coordinator. A
|
|
93
|
+
* cache miss — including every call with no `actionId` (get/cancel) — never fails; it
|
|
94
|
+
* always falls through to live resolution.
|
|
95
|
+
*/
|
|
96
|
+
private resolveCoordinator;
|
|
97
|
+
/**
|
|
98
|
+
* Get (creating if absent) the per-transaction coordinator map for `actionId`,
|
|
99
|
+
* refreshing its expiry and lazily sweeping expired sibling entries. See
|
|
100
|
+
* {@link txnCoordinatorCache} for why the TTL/size cap here is a memory backstop and
|
|
101
|
+
* not a staleness bound.
|
|
102
|
+
*/
|
|
103
|
+
private txnCoordinatorsFor;
|
|
69
104
|
/** Cancels a pending transaction by canceling all blocks associated with the transaction, including failed peers */
|
|
70
105
|
private cancelBatch;
|
|
71
106
|
private formatBatchStatuses;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-transactor.d.ts","sourceRoot":"","sources":["../../../src/transactor/network-transactor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAA6B,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAc,aAAa,EAA+B,qBAAqB,EAAE,YAAY,EAAU,MAAM,aAAa,CAAC;AAClU,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAS3F,KAAK,qBAAqB,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,UAAU,EAAE,WAAW,CAAC;IACxB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;IACnC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;CAC3C,CAAA;AAWD,qBAAa,iBAAkB,YAAW,WAAW,EAAE,oBAAoB;IAC1E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmC;
|
|
1
|
+
{"version":3,"file":"network-transactor.d.ts","sourceRoot":"","sources":["../../../src/transactor/network-transactor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAA6B,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAc,aAAa,EAA+B,qBAAqB,EAAE,YAAY,EAAU,MAAM,aAAa,CAAC;AAClU,OAAO,KAAK,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAS3F,KAAK,qBAAqB,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,UAAU,EAAE,WAAW,CAAC;IACxB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC;IACnC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,oBAAoB,CAAC;CAC3C,CAAA;AAWD,qBAAa,iBAAkB,YAAW,WAAW,EAAE,oBAAoB;IAC1E,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAmC;IAEvE;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgF;IACpH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iCAAiC,CAAQ;gBAGhE,IAAI,EAAE,qBAAqB;IAc5B;;;;;OAKG;IACH,kBAAkB,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,wBAAwB,GAAG,MAAM,IAAI;IAQxF,GAAG,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,eAAe,CAAC;IAsInD,SAAS,CAAC,YAAY,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA0C3E;;;;;;;;;;;OAWG;YACW,yBAAyB;IA6BvC,yGAAyG;YAC3F,sBAAsB;YAuBtB,uBAAuB;IA+F/B,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAiHnD,MAAM,CAAC,SAAS,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9C,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAOtE,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;YAyC7C,WAAW;IAqBzB,0EAA0E;YAC5D,YAAY;IAsC1B,8FAA8F;YAChF,iBAAiB;IAiB/B;;;;;;;OAOG;YACW,kBAAkB;IAchC;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IA2B1B,oHAAoH;YACtG,WAAW;IAqBzB,OAAO,CAAC,mBAAmB;CAY3B;AAiDD;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAIlG"}
|