@optimystic/db-p2p 0.24.2 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{readme.md → README.md} +688 -668
- package/dist/src/cluster/block-transfer-service.d.ts +133 -10
- package/dist/src/cluster/block-transfer-service.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer-service.js +155 -9
- package/dist/src/cluster/block-transfer-service.js.map +1 -1
- package/dist/src/cluster/block-transfer.d.ts +67 -7
- package/dist/src/cluster/block-transfer.d.ts.map +1 -1
- package/dist/src/cluster/block-transfer.js +127 -24
- package/dist/src/cluster/block-transfer.js.map +1 -1
- package/dist/src/cluster/certified-claims.d.ts +170 -0
- package/dist/src/cluster/certified-claims.d.ts.map +1 -0
- package/dist/src/cluster/certified-claims.js +200 -0
- package/dist/src/cluster/certified-claims.js.map +1 -0
- package/dist/src/cluster/client-signature-verifier.d.ts +23 -0
- package/dist/src/cluster/client-signature-verifier.d.ts.map +1 -0
- package/dist/src/cluster/client-signature-verifier.js +33 -0
- package/dist/src/cluster/client-signature-verifier.js.map +1 -0
- package/dist/src/cluster/cluster-policy.d.ts +9 -3
- package/dist/src/cluster/cluster-policy.d.ts.map +1 -1
- package/dist/src/cluster/cluster-policy.js +111 -27
- package/dist/src/cluster/cluster-policy.js.map +1 -1
- package/dist/src/cluster/cluster-repo.d.ts +140 -11
- package/dist/src/cluster/cluster-repo.d.ts.map +1 -1
- package/dist/src/cluster/cluster-repo.js +485 -29
- package/dist/src/cluster/cluster-repo.js.map +1 -1
- package/dist/src/cluster/commit-proof.d.ts +134 -0
- package/dist/src/cluster/commit-proof.d.ts.map +1 -0
- package/dist/src/cluster/commit-proof.js +263 -0
- package/dist/src/cluster/commit-proof.js.map +1 -0
- package/dist/src/cluster/quorum-restore.d.ts +95 -8
- package/dist/src/cluster/quorum-restore.d.ts.map +1 -1
- package/dist/src/cluster/quorum-restore.js +113 -14
- package/dist/src/cluster/quorum-restore.js.map +1 -1
- package/dist/src/cluster/rebalance-monitor.d.ts +84 -0
- package/dist/src/cluster/rebalance-monitor.d.ts.map +1 -1
- package/dist/src/cluster/rebalance-monitor.js +207 -5
- package/dist/src/cluster/rebalance-monitor.js.map +1 -1
- package/dist/src/cluster/reconcile-block.d.ts +27 -7
- package/dist/src/cluster/reconcile-block.d.ts.map +1 -1
- package/dist/src/cluster/reconcile-block.js +179 -38
- package/dist/src/cluster/reconcile-block.js.map +1 -1
- package/dist/src/cluster/service.d.ts +13 -4
- package/dist/src/cluster/service.d.ts.map +1 -1
- package/dist/src/cluster/service.js +17 -18
- package/dist/src/cluster/service.js.map +1 -1
- package/dist/src/cluster/spread-on-churn.d.ts +8 -2
- package/dist/src/cluster/spread-on-churn.d.ts.map +1 -1
- package/dist/src/cluster/spread-on-churn.js +7 -7
- package/dist/src/cluster/spread-on-churn.js.map +1 -1
- package/dist/src/cohort-topic/host.d.ts.map +1 -1
- package/dist/src/cohort-topic/host.js +8 -7
- package/dist/src/cohort-topic/host.js.map +1 -1
- package/dist/src/cohort-topic/stream-util.d.ts +7 -6
- package/dist/src/cohort-topic/stream-util.d.ts.map +1 -1
- package/dist/src/cohort-topic/stream-util.js +14 -41
- package/dist/src/cohort-topic/stream-util.js.map +1 -1
- package/dist/src/dispute/cascade.d.ts +7 -8
- package/dist/src/dispute/cascade.d.ts.map +1 -1
- package/dist/src/dispute/cascade.js +24 -7
- package/dist/src/dispute/cascade.js.map +1 -1
- package/dist/src/dispute/dispute-service.d.ts +5 -0
- package/dist/src/dispute/dispute-service.d.ts.map +1 -1
- package/dist/src/dispute/dispute-service.js +13 -3
- package/dist/src/dispute/dispute-service.js.map +1 -1
- package/dist/src/dispute/invalidation.d.ts +33 -15
- package/dist/src/dispute/invalidation.d.ts.map +1 -1
- package/dist/src/dispute/invalidation.js +127 -34
- package/dist/src/dispute/invalidation.js.map +1 -1
- package/dist/src/dispute/service.d.ts.map +1 -1
- package/dist/src/dispute/service.js +2 -1
- package/dist/src/dispute/service.js.map +1 -1
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/libp2p-key-network.d.ts +14 -10
- package/dist/src/libp2p-key-network.d.ts.map +1 -1
- package/dist/src/libp2p-key-network.js +28 -58
- package/dist/src/libp2p-key-network.js.map +1 -1
- package/dist/src/libp2p-node-base.d.ts +38 -7
- package/dist/src/libp2p-node-base.d.ts.map +1 -1
- package/dist/src/libp2p-node-base.js +148 -63
- package/dist/src/libp2p-node-base.js.map +1 -1
- package/dist/src/network/open-protocol-stream.d.ts +56 -0
- package/dist/src/network/open-protocol-stream.d.ts.map +1 -0
- package/dist/src/network/open-protocol-stream.js +60 -0
- package/dist/src/network/open-protocol-stream.js.map +1 -0
- package/dist/src/network/register-protocol-handler.d.ts +66 -0
- package/dist/src/network/register-protocol-handler.d.ts.map +1 -0
- package/dist/src/network/register-protocol-handler.js +51 -0
- package/dist/src/network/register-protocol-handler.js.map +1 -0
- package/dist/src/peer-address-book.d.ts +60 -2
- package/dist/src/peer-address-book.d.ts.map +1 -1
- package/dist/src/peer-address-book.js +89 -1
- package/dist/src/peer-address-book.js.map +1 -1
- package/dist/src/pend-validation.d.ts +65 -0
- package/dist/src/pend-validation.d.ts.map +1 -0
- package/dist/src/pend-validation.js +69 -0
- package/dist/src/pend-validation.js.map +1 -0
- package/dist/src/reactivity/notify-transport.d.ts.map +1 -1
- package/dist/src/reactivity/notify-transport.js +2 -1
- package/dist/src/reactivity/notify-transport.js.map +1 -1
- package/dist/src/reactivity/push-state-gossip.d.ts.map +1 -1
- package/dist/src/reactivity/push-state-gossip.js +2 -1
- package/dist/src/reactivity/push-state-gossip.js.map +1 -1
- package/dist/src/repo/cluster-coordinator.d.ts +32 -6
- package/dist/src/repo/cluster-coordinator.d.ts.map +1 -1
- package/dist/src/repo/cluster-coordinator.js +44 -10
- package/dist/src/repo/cluster-coordinator.js.map +1 -1
- package/dist/src/repo/coordinator-repo.d.ts +181 -25
- package/dist/src/repo/coordinator-repo.d.ts.map +1 -1
- package/dist/src/repo/coordinator-repo.js +650 -62
- package/dist/src/repo/coordinator-repo.js.map +1 -1
- package/dist/src/repo/served-repo-proxy.d.ts +30 -0
- package/dist/src/repo/served-repo-proxy.d.ts.map +1 -0
- package/dist/src/repo/served-repo-proxy.js +50 -0
- package/dist/src/repo/served-repo-proxy.js.map +1 -0
- package/dist/src/repo/service.d.ts +16 -1
- package/dist/src/repo/service.d.ts.map +1 -1
- package/dist/src/repo/service.js +18 -16
- package/dist/src/repo/service.js.map +1 -1
- package/dist/src/reputation/types.d.ts +3 -1
- package/dist/src/reputation/types.d.ts.map +1 -1
- package/dist/src/reputation/types.js +3 -1
- package/dist/src/reputation/types.js.map +1 -1
- package/dist/src/rn.d.ts +24 -2
- package/dist/src/rn.d.ts.map +1 -1
- package/dist/src/rn.js +33 -4
- package/dist/src/rn.js.map +1 -1
- package/dist/src/storage/block-archive.d.ts +153 -0
- package/dist/src/storage/block-archive.d.ts.map +1 -0
- package/dist/src/storage/block-archive.js +194 -0
- package/dist/src/storage/block-archive.js.map +1 -0
- package/dist/src/storage/block-latch.d.ts +92 -0
- package/dist/src/storage/block-latch.d.ts.map +1 -0
- package/dist/src/storage/block-latch.js +132 -0
- package/dist/src/storage/block-latch.js.map +1 -0
- package/dist/src/storage/block-storage.d.ts +169 -12
- package/dist/src/storage/block-storage.d.ts.map +1 -1
- package/dist/src/storage/block-storage.js +415 -152
- package/dist/src/storage/block-storage.js.map +1 -1
- package/dist/src/storage/cached-raw-storage.d.ts +5 -1
- package/dist/src/storage/cached-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/cached-raw-storage.js +16 -1
- package/dist/src/storage/cached-raw-storage.js.map +1 -1
- package/dist/src/storage/cached-store-driver.d.ts +14 -4
- package/dist/src/storage/cached-store-driver.d.ts.map +1 -1
- package/dist/src/storage/cached-store-driver.js +39 -5
- package/dist/src/storage/cached-store-driver.js.map +1 -1
- package/dist/src/storage/i-block-storage.d.ts +70 -19
- package/dist/src/storage/i-block-storage.d.ts.map +1 -1
- package/dist/src/storage/i-block-storage.js +21 -1
- package/dist/src/storage/i-block-storage.js.map +1 -1
- package/dist/src/storage/i-raw-storage.d.ts +38 -0
- package/dist/src/storage/i-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.d.ts +14 -8
- package/dist/src/storage/kv-raw-storage.d.ts.map +1 -1
- package/dist/src/storage/kv-raw-storage.js +29 -9
- package/dist/src/storage/kv-raw-storage.js.map +1 -1
- package/dist/src/storage/memory-store-driver.d.ts +4 -1
- package/dist/src/storage/memory-store-driver.d.ts.map +1 -1
- package/dist/src/storage/memory-store-driver.js +22 -2
- package/dist/src/storage/memory-store-driver.js.map +1 -1
- package/dist/src/storage/raw-store-codec.js +2 -2
- package/dist/src/storage/raw-store-driver.d.ts +44 -3
- package/dist/src/storage/raw-store-driver.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.d.ts +60 -3
- package/dist/src/storage/shared-cache-pool.d.ts.map +1 -1
- package/dist/src/storage/shared-cache-pool.js +68 -4
- package/dist/src/storage/shared-cache-pool.js.map +1 -1
- package/dist/src/storage/storage-repo.d.ts +204 -26
- package/dist/src/storage/storage-repo.d.ts.map +1 -1
- package/dist/src/storage/storage-repo.js +422 -119
- package/dist/src/storage/storage-repo.js.map +1 -1
- package/dist/src/storage/store-identity.d.ts +21 -0
- package/dist/src/storage/store-identity.d.ts.map +1 -0
- package/dist/src/storage/store-identity.js +33 -0
- package/dist/src/storage/store-identity.js.map +1 -0
- package/dist/src/storage/struct.d.ts +15 -0
- package/dist/src/storage/struct.d.ts.map +1 -1
- package/dist/src/storage/with-read-cache.d.ts +92 -0
- package/dist/src/storage/with-read-cache.d.ts.map +1 -0
- package/dist/src/storage/with-read-cache.js +132 -0
- package/dist/src/storage/with-read-cache.js.map +1 -0
- package/dist/src/sync/service.d.ts +13 -3
- package/dist/src/sync/service.d.ts.map +1 -1
- package/dist/src/sync/service.js +7 -30
- package/dist/src/sync/service.js.map +1 -1
- package/dist/src/testing/mesh-harness.d.ts +75 -2
- package/dist/src/testing/mesh-harness.d.ts.map +1 -1
- package/dist/src/testing/mesh-harness.js +193 -81
- package/dist/src/testing/mesh-harness.js.map +1 -1
- package/dist/src/testing/raw-storage-conformance.d.ts +8 -0
- package/dist/src/testing/raw-storage-conformance.d.ts.map +1 -1
- package/dist/src/testing/raw-storage-conformance.js +97 -2
- package/dist/src/testing/raw-storage-conformance.js.map +1 -1
- package/package.json +5 -6
- package/src/cluster/block-transfer-service.ts +528 -312
- package/src/cluster/block-transfer.ts +547 -424
- package/src/cluster/certified-claims.ts +319 -0
- package/src/cluster/client-signature-verifier.ts +34 -0
- package/src/cluster/cluster-policy.ts +294 -203
- package/src/cluster/cluster-repo.ts +2501 -2034
- package/src/cluster/commit-proof.ts +344 -0
- package/src/cluster/quorum-restore.ts +348 -223
- package/src/cluster/rebalance-monitor.ts +539 -254
- package/src/cluster/reconcile-block.ts +404 -203
- package/src/cluster/service.ts +23 -20
- package/src/cluster/spread-on-churn.ts +352 -346
- package/src/cohort-topic/host.ts +8 -7
- package/src/cohort-topic/stream-util.ts +14 -43
- package/src/dispute/cascade.ts +31 -15
- package/src/dispute/dispute-service.ts +706 -696
- package/src/dispute/invalidation.ts +162 -52
- package/src/dispute/service.ts +126 -125
- package/src/index.ts +7 -0
- package/src/libp2p-key-network.ts +1204 -1235
- package/src/libp2p-node-base.ts +185 -70
- package/src/network/open-protocol-stream.ts +87 -0
- package/src/network/register-protocol-handler.ts +85 -0
- package/src/peer-address-book.ts +96 -2
- package/src/pend-validation.ts +89 -0
- package/src/reactivity/notify-transport.ts +2 -1
- package/src/reactivity/push-state-gossip.ts +2 -1
- package/src/repo/cluster-coordinator.ts +65 -11
- package/src/repo/coordinator-repo.ts +729 -71
- package/src/repo/served-repo-proxy.ts +55 -0
- package/src/repo/service.ts +25 -16
- package/src/reputation/types.ts +3 -1
- package/src/rn.ts +34 -4
- package/src/storage/block-archive.ts +242 -0
- package/src/storage/block-latch.ts +144 -0
- package/src/storage/block-storage.ts +787 -498
- package/src/storage/cached-raw-storage.ts +21 -1
- package/src/storage/cached-store-driver.ts +898 -859
- package/src/storage/i-block-storage.ts +172 -116
- package/src/storage/i-raw-storage.ts +40 -0
- package/src/storage/kv-raw-storage.ts +34 -9
- package/src/storage/memory-store-driver.ts +26 -2
- package/src/storage/raw-store-codec.ts +42 -42
- package/src/storage/raw-store-driver.ts +45 -3
- package/src/storage/shared-cache-pool.ts +516 -452
- package/src/storage/storage-repo.ts +1421 -1014
- package/src/storage/store-identity.ts +41 -0
- package/src/storage/struct.ts +52 -34
- package/src/storage/with-read-cache.ts +182 -0
- package/src/sync/service.ts +178 -198
- package/src/testing/mesh-harness.ts +524 -344
- package/src/testing/raw-storage-conformance.ts +114 -2
|
@@ -1,346 +1,352 @@
|
|
|
1
|
-
import type { Startable, Libp2p } from '@libp2p/interface'
|
|
2
|
-
import type {
|
|
3
|
-
import { hashKey } from 'p2p-fret'
|
|
4
|
-
import type { FretService } from 'p2p-fret'
|
|
5
|
-
import { peerIdFromString } from '@libp2p/peer-id'
|
|
6
|
-
import type { PartitionDetector } from './partition-detector.js'
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
private readonly
|
|
100
|
-
private readonly
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
1
|
+
import type { Startable, Libp2p } from '@libp2p/interface'
|
|
2
|
+
import type { IPeerNetwork } from '@optimystic/db-core'
|
|
3
|
+
import { hashKey } from 'p2p-fret'
|
|
4
|
+
import type { FretService } from 'p2p-fret'
|
|
5
|
+
import { peerIdFromString } from '@libp2p/peer-id'
|
|
6
|
+
import type { PartitionDetector } from './partition-detector.js'
|
|
7
|
+
import type { ProofRetainingRepo } from '../storage/block-archive.js'
|
|
8
|
+
import { BlockTransferClient, sourceBlockCertification } from './block-transfer-service.js'
|
|
9
|
+
import { createLogger } from '../logger.js'
|
|
10
|
+
|
|
11
|
+
const log = createLogger('spread-on-churn')
|
|
12
|
+
const textEncoder = new TextEncoder()
|
|
13
|
+
|
|
14
|
+
// ── Types ────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
export interface SpreadOnChurnConfig {
|
|
17
|
+
/** Enable the churn-resilient spread protocol. Default: true */
|
|
18
|
+
enabled: boolean
|
|
19
|
+
/** Number of middle-closest peers eligible to spread (d). Default: 3 */
|
|
20
|
+
spreadDistance: number
|
|
21
|
+
/** Enable dynamic d scaling based on cluster health. Default: true */
|
|
22
|
+
dynamicSpreadDistance: boolean
|
|
23
|
+
/** Cluster size ratio below which spread becomes more aggressive. Default: 0.6 */
|
|
24
|
+
healthThreshold: number
|
|
25
|
+
/** Debounce window for departure detection (ms). Default: 5000 */
|
|
26
|
+
departureDebounceMs: number
|
|
27
|
+
/** Number of peers beyond cluster boundary to target. Default: 4 */
|
|
28
|
+
expansionStep: number
|
|
29
|
+
/**
|
|
30
|
+
* Per-target dial deadline (ms) for churn pushes. A target that never accepts
|
|
31
|
+
* the connection fails fast instead of stalling the sequential spread pass.
|
|
32
|
+
* Default: 3000 (matches the codebase's other dial caps).
|
|
33
|
+
*/
|
|
34
|
+
pushDialTimeoutMs: number
|
|
35
|
+
/**
|
|
36
|
+
* Per-target response deadline (ms) for churn pushes. A target that dials OK but
|
|
37
|
+
* never replies (connects, then goes silent) is given up on — the push throws
|
|
38
|
+
* `ResponseTimeoutError`, is recorded as failed, and the loop continues to the
|
|
39
|
+
* next target/block rather than hanging the whole pass. Default: 10000.
|
|
40
|
+
*/
|
|
41
|
+
pushResponseTimeoutMs: number
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SpreadOnChurnDeps {
|
|
45
|
+
libp2p: Libp2p
|
|
46
|
+
fret: FretService
|
|
47
|
+
partitionDetector: PartitionDetector
|
|
48
|
+
/**
|
|
49
|
+
* The node's OWN store, proof accessor REQUIRED — see the same requirement on
|
|
50
|
+
* `BlockTransferCoordinator`: a repo without it spreads meta-only pushes that every
|
|
51
|
+
* default-configured receiver refuses, with no type error to say so.
|
|
52
|
+
*/
|
|
53
|
+
repo: ProofRetainingRepo
|
|
54
|
+
peerNetwork: IPeerNetwork
|
|
55
|
+
clusterSize: number
|
|
56
|
+
protocolPrefix?: string
|
|
57
|
+
/**
|
|
58
|
+
* The owned-block tracked set. When provided (e.g. the shared `ownedBlocks` set wired in
|
|
59
|
+
* `libp2p-node-base`), the monitor references this exact `Set` instead of constructing its own,
|
|
60
|
+
* so it stays in lock-step with the `RebalanceMonitor` that shares it. Omit for standalone
|
|
61
|
+
* construction (unit tests) — a fresh private `Set` preserves all existing behavior.
|
|
62
|
+
*/
|
|
63
|
+
trackedBlocks?: Set<string>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface SpreadEvent {
|
|
67
|
+
/** Blocks that were spread */
|
|
68
|
+
spread: Array<{
|
|
69
|
+
blockId: string
|
|
70
|
+
targets: string[]
|
|
71
|
+
succeeded: string[]
|
|
72
|
+
failed: string[]
|
|
73
|
+
}>
|
|
74
|
+
/** Current effective d */
|
|
75
|
+
effectiveD: number
|
|
76
|
+
/** Timestamp of the departure that triggered this */
|
|
77
|
+
triggeredAt: number
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type SpreadHandler = (event: SpreadEvent) => void
|
|
81
|
+
|
|
82
|
+
// ── Defaults ─────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
const DEFAULT_CONFIG: SpreadOnChurnConfig = {
|
|
85
|
+
enabled: true,
|
|
86
|
+
spreadDistance: 3,
|
|
87
|
+
dynamicSpreadDistance: true,
|
|
88
|
+
healthThreshold: 0.6,
|
|
89
|
+
departureDebounceMs: 5000,
|
|
90
|
+
expansionStep: 4,
|
|
91
|
+
pushDialTimeoutMs: 3000,
|
|
92
|
+
pushResponseTimeoutMs: 10000,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Monitor ──────────────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
export class SpreadOnChurnMonitor implements Startable {
|
|
98
|
+
private running = false
|
|
99
|
+
private readonly trackedBlocks: Set<string>
|
|
100
|
+
private readonly handlers: SpreadHandler[] = []
|
|
101
|
+
private debounceTimer: ReturnType<typeof setTimeout> | null = null
|
|
102
|
+
private departureTimestamps: number[] = []
|
|
103
|
+
private departureTimestamp = 0
|
|
104
|
+
|
|
105
|
+
private readonly config: SpreadOnChurnConfig
|
|
106
|
+
private readonly onConnectionClose: () => void
|
|
107
|
+
|
|
108
|
+
constructor(
|
|
109
|
+
private readonly deps: SpreadOnChurnDeps,
|
|
110
|
+
config: Partial<SpreadOnChurnConfig> = {}
|
|
111
|
+
) {
|
|
112
|
+
// Share the injected owned-block set when present (so spread + rebalance never drift);
|
|
113
|
+
// otherwise own a private set (standalone construction / unit tests).
|
|
114
|
+
this.trackedBlocks = deps.trackedBlocks ?? new Set<string>()
|
|
115
|
+
this.config = { ...DEFAULT_CONFIG, ...config }
|
|
116
|
+
this.onConnectionClose = () => this.handleDeparture()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Startable ────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
async start(): Promise<void> {
|
|
122
|
+
if (this.running) return
|
|
123
|
+
this.running = true
|
|
124
|
+
|
|
125
|
+
this.deps.libp2p.addEventListener('connection:close', this.onConnectionClose)
|
|
126
|
+
|
|
127
|
+
log('started, tracking %d blocks', this.trackedBlocks.size)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async stop(): Promise<void> {
|
|
131
|
+
if (!this.running) return
|
|
132
|
+
this.running = false
|
|
133
|
+
|
|
134
|
+
this.deps.libp2p.removeEventListener('connection:close', this.onConnectionClose)
|
|
135
|
+
|
|
136
|
+
if (this.debounceTimer) {
|
|
137
|
+
clearTimeout(this.debounceTimer)
|
|
138
|
+
this.debounceTimer = null
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log('stopped')
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Public API ───────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
onSpread(handler: SpreadHandler): void {
|
|
147
|
+
this.handlers.push(handler)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
trackBlock(blockId: string): void {
|
|
151
|
+
this.trackedBlocks.add(blockId)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
untrackBlock(blockId: string): void {
|
|
155
|
+
this.trackedBlocks.delete(blockId)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getTrackedBlockCount(): number {
|
|
159
|
+
return this.trackedBlocks.size
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Force an immediate spread check (useful for testing). */
|
|
163
|
+
async checkNow(): Promise<SpreadEvent | null> {
|
|
164
|
+
return this.performSpread(Date.now())
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── Internal ─────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
private handleDeparture(): void {
|
|
170
|
+
if (!this.running) return
|
|
171
|
+
if (!this.config.enabled) return
|
|
172
|
+
|
|
173
|
+
if (!this.departureTimestamp) {
|
|
174
|
+
this.departureTimestamp = Date.now()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Record for dynamic-d sliding window
|
|
178
|
+
this.departureTimestamps.push(Date.now())
|
|
179
|
+
|
|
180
|
+
if (this.debounceTimer) {
|
|
181
|
+
clearTimeout(this.debounceTimer)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this.debounceTimer = setTimeout(() => {
|
|
185
|
+
this.debounceTimer = null
|
|
186
|
+
const ts = this.departureTimestamp
|
|
187
|
+
this.departureTimestamp = 0
|
|
188
|
+
if (this.running) {
|
|
189
|
+
this.performSpread(ts).catch(err => {
|
|
190
|
+
log('spread error: %O', err)
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
}, this.config.departureDebounceMs)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private async performSpread(triggeredAt: number): Promise<SpreadEvent | null> {
|
|
197
|
+
if (!this.config.enabled) return null
|
|
198
|
+
|
|
199
|
+
if (this.deps.partitionDetector.detectPartition()) {
|
|
200
|
+
log('partition detected, suppressing spread')
|
|
201
|
+
return null
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (this.trackedBlocks.size === 0) return null
|
|
205
|
+
|
|
206
|
+
const selfId = this.deps.libp2p.peerId.toString()
|
|
207
|
+
const effectiveD = this.computeEffectiveD()
|
|
208
|
+
const spreadResults: SpreadEvent['spread'] = []
|
|
209
|
+
|
|
210
|
+
for (const blockId of this.trackedBlocks) {
|
|
211
|
+
const key = textEncoder.encode(blockId)
|
|
212
|
+
const coord = await hashKey(key)
|
|
213
|
+
|
|
214
|
+
// Check eligibility: only middle peers spread
|
|
215
|
+
const rank = this.deps.fret.neighborDistance(selfId, coord, this.deps.clusterSize)
|
|
216
|
+
if (rank >= effectiveD) continue
|
|
217
|
+
|
|
218
|
+
// Get current cohort and expansion targets
|
|
219
|
+
const cohort = this.deps.fret.assembleCohort(coord, this.deps.clusterSize)
|
|
220
|
+
const cohortSet = new Set(cohort)
|
|
221
|
+
const expanded = this.deps.fret.expandCohort(
|
|
222
|
+
cohort, coord, this.config.expansionStep
|
|
223
|
+
)
|
|
224
|
+
const targets = expanded.filter(id => !cohortSet.has(id) && id !== selfId)
|
|
225
|
+
if (targets.length === 0) continue
|
|
226
|
+
|
|
227
|
+
// Read block data from local storage
|
|
228
|
+
const result = await this.deps.repo.get({ blockIds: [blockId] })
|
|
229
|
+
const blockResult = result[blockId]
|
|
230
|
+
if (blockResult?.unavailable !== undefined) {
|
|
231
|
+
// The repo could not work out whether it still holds this block (unmaterializable
|
|
232
|
+
// history / failed restore). Untracking on that answer would silently drop the block
|
|
233
|
+
// from the spread set on a guess, and only a later re-commit would put it back — so
|
|
234
|
+
// keep it tracked and let the next sweep (or a heal) settle it.
|
|
235
|
+
log('unavailable block=%s reason=%s (keeping tracked)', blockId, blockResult.unavailable)
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
if (!blockResult?.block) {
|
|
239
|
+
// The block has left local storage. No deletion event exists today to evict it
|
|
240
|
+
// from the tracked set, so prune here. Deleting the current element of a Set mid
|
|
241
|
+
// for...of is safe - it does not disturb the rest of the iteration. A later
|
|
242
|
+
// re-commit re-adds the block via the owned-block feed.
|
|
243
|
+
log('no-local-data block=%s (untracking)', blockId)
|
|
244
|
+
this.trackedBlocks.delete(blockId)
|
|
245
|
+
continue
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const blockData = textEncoder.encode(JSON.stringify(blockResult.block))
|
|
249
|
+
|
|
250
|
+
// Carry the source's revision metadata so the replica's `latest` matches the
|
|
251
|
+
// source instead of being fabricated as rev 1 on the receiver, plus the cohort commit
|
|
252
|
+
// proof for that same revision — a receiver running the default `requirePushCertificate`
|
|
253
|
+
// rejects an uncertified push, so a block this node holds no proof for simply fails to
|
|
254
|
+
// spread (logged by the receiver as `push:reject-uncertified`) rather than being planted.
|
|
255
|
+
const certification = await sourceBlockCertification(this.deps.repo, blockId, blockResult)
|
|
256
|
+
|
|
257
|
+
// Push to each target
|
|
258
|
+
const succeeded: string[] = []
|
|
259
|
+
const failed: string[] = []
|
|
260
|
+
|
|
261
|
+
for (const targetId of targets) {
|
|
262
|
+
try {
|
|
263
|
+
const peerId = peerIdFromString(targetId)
|
|
264
|
+
const client = new BlockTransferClient(
|
|
265
|
+
peerId,
|
|
266
|
+
this.deps.peerNetwork,
|
|
267
|
+
this.deps.protocolPrefix
|
|
268
|
+
)
|
|
269
|
+
// Bound both the dial and the response read. A target that connects but
|
|
270
|
+
// never replies would otherwise hang this sequential loop forever during
|
|
271
|
+
// churn (exactly when slow/dying peers are common); the deadline makes the
|
|
272
|
+
// push throw, which the catch below records as `failed` so the pass advances.
|
|
273
|
+
const response = await client.pushBlocks([blockId], [blockData], 'replication', certification, {
|
|
274
|
+
dialTimeoutMs: this.config.pushDialTimeoutMs,
|
|
275
|
+
responseTimeoutMs: this.config.pushResponseTimeoutMs,
|
|
276
|
+
})
|
|
277
|
+
// A round-trip that does not throw still does not mean the replica
|
|
278
|
+
// landed: handlePush reports a block in `missing` when it could not
|
|
279
|
+
// parse or persist it. Treat that as a failed push so the resilience
|
|
280
|
+
// mechanism does not falsely count the new owner as holding the block.
|
|
281
|
+
if (response.missing.includes(blockId)) {
|
|
282
|
+
failed.push(targetId)
|
|
283
|
+
log('push:rejected block=%s target=%s (receiver did not persist)', blockId, targetId)
|
|
284
|
+
} else {
|
|
285
|
+
succeeded.push(targetId)
|
|
286
|
+
log('push:ok block=%s target=%s', blockId, targetId)
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
failed.push(targetId)
|
|
290
|
+
log('push:fail block=%s target=%s err=%s',
|
|
291
|
+
blockId, targetId, (err as Error).message)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
spreadResults.push({ blockId, targets, succeeded, failed })
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (spreadResults.length === 0) return null
|
|
299
|
+
|
|
300
|
+
const event: SpreadEvent = {
|
|
301
|
+
spread: spreadResults,
|
|
302
|
+
effectiveD,
|
|
303
|
+
triggeredAt,
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
this.emitEvent(event)
|
|
307
|
+
return event
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private computeEffectiveD(): number {
|
|
311
|
+
const d = this.config.spreadDistance
|
|
312
|
+
if (!this.config.dynamicSpreadDistance) return d
|
|
313
|
+
|
|
314
|
+
const maxD = Math.max(d, Math.floor(this.deps.clusterSize / 2))
|
|
315
|
+
const windowMs = this.config.departureDebounceMs * 4
|
|
316
|
+
const now = Date.now()
|
|
317
|
+
|
|
318
|
+
// Prune old departure timestamps
|
|
319
|
+
this.departureTimestamps = this.departureTimestamps.filter(
|
|
320
|
+
ts => now - ts < windowMs
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
// Rapid churn: 3+ departures in window → increase d by 1
|
|
324
|
+
if (this.departureTimestamps.length >= 3) {
|
|
325
|
+
return Math.min(d + 1, maxD)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Low cluster health: observed cohort shrunk relative to expected
|
|
329
|
+
// We approximate observed cohort size from FRET diagnostics
|
|
330
|
+
const diag: any = (this.deps.fret as any).getDiagnostics?.()
|
|
331
|
+
const estimate = diag?.estimate ?? diag?.n
|
|
332
|
+
if (typeof estimate === 'number' && Number.isFinite(estimate) && estimate > 0) {
|
|
333
|
+
const ratio = estimate / this.deps.clusterSize
|
|
334
|
+
if (ratio < this.config.healthThreshold) {
|
|
335
|
+
const scaled = Math.ceil(d * (this.deps.clusterSize / estimate))
|
|
336
|
+
return Math.min(scaled, maxD)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return d
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private emitEvent(event: SpreadEvent): void {
|
|
344
|
+
for (const handler of this.handlers) {
|
|
345
|
+
try {
|
|
346
|
+
handler(event)
|
|
347
|
+
} catch (err) {
|
|
348
|
+
log('handler error: %O', err)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|