@feltdb/core 0.4.11 → 0.4.13
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/cli/commands.js +5 -2
- package/dist/cli/index.js +1 -1
- package/dist/create/cli.js +16 -1
- package/dist/create/create.js +30 -16
- package/dist/create/docker-compose-generator.js +48 -9
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/Cargo.lock +2238 -0
- package/dist/create/server-source/Cargo.toml +7 -0
- package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
- package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
- package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
- package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
- package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
- package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
- package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
- package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
- package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
- package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
- package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
- package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
- package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
- package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
- package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
- package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
- package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
- package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
- package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
- package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
- package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
- package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
- package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
- package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
- package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
- package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
- package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
- package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
- package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
- package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
- package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
- package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
- package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
- package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
- package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
- package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
- package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
- package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
- package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
- package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
- package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
- package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
- package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
- package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
- package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
- package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
- package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
- package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
- package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
- package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
- package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
- package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
- package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
- package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
- package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
- package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
- package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
- package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
- package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
- package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
- package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
- package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
- package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
- package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
- package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
- package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
- package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
- package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
- package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
- package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
- package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
- package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
- package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
- package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
- package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
- package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
- package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
- package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
- package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
- package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
- package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
- package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
- package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
- package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
- package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
- package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
- package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
- package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
- package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
- package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
- package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
- package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
- package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
- package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
- package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
- package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
- package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
- package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
- package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
- package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
- package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
- package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
- package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
- package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
- package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
- package/dist/flowspec.d.ts +2 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +28 -4
- package/dist/state-contract.d.ts +7 -1
- package/dist/state-contract.d.ts.map +1 -1
- package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
- package/dist/studio/components/index.js +1 -1
- package/dist/studio/{components-q43NSTTH.js → components-bHTARBin.js} +160 -89
- package/dist/studio/index.js +31 -28
- package/dist/studio-app/assets/{index-DZpBoVMp.js → index-BqKquLuU.js} +8 -8
- package/dist/studio-app/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
/// Adversarial Transport Layer (Gate 8)
|
|
2
|
+
///
|
|
3
|
+
/// Network fault injection that proves FeltDB converges despite:
|
|
4
|
+
/// delivery delays, reordering, duplication, drops,
|
|
5
|
+
/// partitions, asymmetric connectivity, and arbitrary restarts
|
|
6
|
+
///
|
|
7
|
+
/// Critical constraint: Transport has NO knowledge of FeltDB's replication logic.
|
|
8
|
+
/// Transport only controls what happens to a message:
|
|
9
|
+
/// send → maybe delay → maybe duplicate → maybe drop → eventually deliver
|
|
10
|
+
///
|
|
11
|
+
/// FeltDB must converge itself. Simulator cannot help.
|
|
12
|
+
|
|
13
|
+
use std::collections::{HashMap, VecDeque};
|
|
14
|
+
|
|
15
|
+
/// Reproducible fault seed
|
|
16
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
|
17
|
+
pub struct FaultSeed(pub u64);
|
|
18
|
+
|
|
19
|
+
/// Unique message identifier (for tracking through faults)
|
|
20
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
21
|
+
pub struct MessageId(pub u64);
|
|
22
|
+
|
|
23
|
+
/// Node identifier
|
|
24
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
|
25
|
+
pub struct NodeId(pub u64);
|
|
26
|
+
|
|
27
|
+
/// Connection between two nodes
|
|
28
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
|
29
|
+
pub struct ConnectionId {
|
|
30
|
+
pub from: NodeId,
|
|
31
|
+
pub to: NodeId,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/// State of a network link
|
|
35
|
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
36
|
+
pub enum LinkState {
|
|
37
|
+
/// Connected, messages delivered
|
|
38
|
+
Connected,
|
|
39
|
+
/// Partitioned, all messages dropped
|
|
40
|
+
Partitioned,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Explicit network event (deterministic from seed)
|
|
44
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
45
|
+
pub enum NetworkEvent {
|
|
46
|
+
/// Delay a message by N ticks
|
|
47
|
+
Delay {
|
|
48
|
+
message_id: MessageId,
|
|
49
|
+
ticks: u64,
|
|
50
|
+
},
|
|
51
|
+
/// Drop a message permanently
|
|
52
|
+
Drop {
|
|
53
|
+
message_id: MessageId,
|
|
54
|
+
},
|
|
55
|
+
/// Duplicate a message
|
|
56
|
+
Duplicate {
|
|
57
|
+
message_id: MessageId,
|
|
58
|
+
},
|
|
59
|
+
/// Partition a directional link
|
|
60
|
+
Partition {
|
|
61
|
+
from: NodeId,
|
|
62
|
+
to: NodeId,
|
|
63
|
+
},
|
|
64
|
+
/// Heal a directional link
|
|
65
|
+
Heal {
|
|
66
|
+
from: NodeId,
|
|
67
|
+
to: NodeId,
|
|
68
|
+
},
|
|
69
|
+
/// Reorder messages on a connection
|
|
70
|
+
Reorder {
|
|
71
|
+
connection: ConnectionId,
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// Deterministic network schedule (fully reproducible from seed)
|
|
76
|
+
#[derive(Clone, Debug)]
|
|
77
|
+
pub struct NetworkSchedule {
|
|
78
|
+
pub seed: FaultSeed,
|
|
79
|
+
pub events: Vec<NetworkEvent>,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
impl NetworkSchedule {
|
|
83
|
+
pub fn new(seed: FaultSeed) -> Self {
|
|
84
|
+
Self {
|
|
85
|
+
seed,
|
|
86
|
+
events: Vec::new(),
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
pub fn add_event(&mut self, event: NetworkEvent) {
|
|
91
|
+
self.events.push(event);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
pub fn get_seed(&self) -> FaultSeed {
|
|
95
|
+
self.seed
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// In-flight message
|
|
100
|
+
#[derive(Clone, Debug)]
|
|
101
|
+
struct InFlightMessage {
|
|
102
|
+
id: MessageId,
|
|
103
|
+
from: NodeId,
|
|
104
|
+
to: NodeId,
|
|
105
|
+
payload: Vec<u8>,
|
|
106
|
+
delivery_tick: u64,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// Connectivity matrix for asymmetric partitions
|
|
110
|
+
#[derive(Clone, Debug)]
|
|
111
|
+
pub struct ConnectivityMatrix {
|
|
112
|
+
links: HashMap<ConnectionId, LinkState>,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
impl ConnectivityMatrix {
|
|
116
|
+
pub fn new() -> Self {
|
|
117
|
+
Self {
|
|
118
|
+
links: HashMap::new(),
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
pub fn can_reach(&self, from: NodeId, to: NodeId) -> bool {
|
|
123
|
+
let conn = ConnectionId { from, to };
|
|
124
|
+
matches!(self.links.get(&conn), Some(LinkState::Connected) | None)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
pub fn partition(&mut self, from: NodeId, to: NodeId) {
|
|
128
|
+
self.links.insert(ConnectionId { from, to }, LinkState::Partitioned);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
pub fn heal(&mut self, from: NodeId, to: NodeId) {
|
|
132
|
+
self.links.insert(ConnectionId { from, to }, LinkState::Connected);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
pub fn get_state(&self, from: NodeId, to: NodeId) -> LinkState {
|
|
136
|
+
let conn = ConnectionId { from, to };
|
|
137
|
+
*self.links.get(&conn).unwrap_or(&LinkState::Connected)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
impl Default for ConnectivityMatrix {
|
|
142
|
+
fn default() -> Self {
|
|
143
|
+
Self::new()
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Trait: Replication transport (decoupled from FeltDB logic)
|
|
148
|
+
pub trait ReplicationTransport: Send + Sync {
|
|
149
|
+
/// Send a message from one node to another
|
|
150
|
+
fn send(&mut self, from: NodeId, to: NodeId, message: Vec<u8>) -> MessageId;
|
|
151
|
+
|
|
152
|
+
/// Advance simulation by one tick
|
|
153
|
+
fn tick(&mut self);
|
|
154
|
+
|
|
155
|
+
/// Receive all messages waiting for a node
|
|
156
|
+
fn receive(&mut self, node: NodeId) -> Vec<(NodeId, Vec<u8>)>;
|
|
157
|
+
|
|
158
|
+
/// Apply a fault event
|
|
159
|
+
fn apply_fault(&mut self, event: NetworkEvent);
|
|
160
|
+
|
|
161
|
+
/// Get current tick
|
|
162
|
+
fn current_tick(&self) -> u64;
|
|
163
|
+
|
|
164
|
+
/// Get connectivity status
|
|
165
|
+
fn get_connectivity(&self, from: NodeId, to: NodeId) -> LinkState;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// In-memory transport with no faults (baseline)
|
|
169
|
+
pub struct InMemoryTransport {
|
|
170
|
+
current_tick: u64,
|
|
171
|
+
message_counter: u64,
|
|
172
|
+
queues: HashMap<NodeId, VecDeque<(NodeId, Vec<u8>)>>,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
impl InMemoryTransport {
|
|
176
|
+
pub fn new() -> Self {
|
|
177
|
+
Self {
|
|
178
|
+
current_tick: 0,
|
|
179
|
+
message_counter: 0,
|
|
180
|
+
queues: HashMap::new(),
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
impl Default for InMemoryTransport {
|
|
186
|
+
fn default() -> Self {
|
|
187
|
+
Self::new()
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
impl ReplicationTransport for InMemoryTransport {
|
|
192
|
+
fn send(&mut self, from: NodeId, to: NodeId, message: Vec<u8>) -> MessageId {
|
|
193
|
+
let id = MessageId(self.message_counter);
|
|
194
|
+
self.message_counter += 1;
|
|
195
|
+
|
|
196
|
+
self.queues
|
|
197
|
+
.entry(to)
|
|
198
|
+
.or_insert_with(VecDeque::new)
|
|
199
|
+
.push_back((from, message));
|
|
200
|
+
|
|
201
|
+
id
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
fn tick(&mut self) {
|
|
205
|
+
self.current_tick += 1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
fn receive(&mut self, node: NodeId) -> Vec<(NodeId, Vec<u8>)> {
|
|
209
|
+
self.queues
|
|
210
|
+
.remove(&node)
|
|
211
|
+
.map(|q| q.into_iter().collect())
|
|
212
|
+
.unwrap_or_default()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
fn apply_fault(&mut self, _event: NetworkEvent) {
|
|
216
|
+
// No-op for in-memory transport
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
fn current_tick(&self) -> u64 {
|
|
220
|
+
self.current_tick
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
fn get_connectivity(&self, _from: NodeId, _to: NodeId) -> LinkState {
|
|
224
|
+
LinkState::Connected
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/// Adversarial transport with full fault injection
|
|
229
|
+
pub struct AdversarialTransport {
|
|
230
|
+
current_tick: u64,
|
|
231
|
+
message_counter: u64,
|
|
232
|
+
in_flight: Vec<InFlightMessage>,
|
|
233
|
+
delivered_queues: HashMap<NodeId, VecDeque<(NodeId, Vec<u8>)>>,
|
|
234
|
+
connectivity: ConnectivityMatrix,
|
|
235
|
+
dropped_messages: std::collections::HashSet<MessageId>,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
impl AdversarialTransport {
|
|
239
|
+
pub fn new() -> Self {
|
|
240
|
+
Self {
|
|
241
|
+
current_tick: 0,
|
|
242
|
+
message_counter: 0,
|
|
243
|
+
in_flight: Vec::new(),
|
|
244
|
+
delivered_queues: HashMap::new(),
|
|
245
|
+
connectivity: ConnectivityMatrix::new(),
|
|
246
|
+
dropped_messages: std::collections::HashSet::new(),
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
pub fn get_in_flight_count(&self) -> usize {
|
|
251
|
+
self.in_flight.len()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
pub fn get_delivered_count(&self, node: NodeId) -> usize {
|
|
255
|
+
self.delivered_queues
|
|
256
|
+
.get(&node)
|
|
257
|
+
.map(|q| q.len())
|
|
258
|
+
.unwrap_or(0)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
impl Default for AdversarialTransport {
|
|
263
|
+
fn default() -> Self {
|
|
264
|
+
Self::new()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
impl ReplicationTransport for AdversarialTransport {
|
|
269
|
+
fn send(&mut self, from: NodeId, to: NodeId, message: Vec<u8>) -> MessageId {
|
|
270
|
+
let id = MessageId(self.message_counter);
|
|
271
|
+
self.message_counter += 1;
|
|
272
|
+
|
|
273
|
+
// Check if link is partitioned
|
|
274
|
+
if !self.connectivity.can_reach(from, to) {
|
|
275
|
+
// Message is dropped by partition
|
|
276
|
+
self.dropped_messages.insert(id);
|
|
277
|
+
return id;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Message enters in-flight, scheduled for immediate delivery
|
|
281
|
+
self.in_flight.push(InFlightMessage {
|
|
282
|
+
id,
|
|
283
|
+
from,
|
|
284
|
+
to,
|
|
285
|
+
payload: message,
|
|
286
|
+
delivery_tick: self.current_tick,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
id
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
fn tick(&mut self) {
|
|
293
|
+
self.current_tick += 1;
|
|
294
|
+
|
|
295
|
+
// Deliver messages that are ready
|
|
296
|
+
let mut to_deliver = Vec::new();
|
|
297
|
+
let mut remaining = Vec::new();
|
|
298
|
+
|
|
299
|
+
for msg in self.in_flight.drain(..) {
|
|
300
|
+
if msg.delivery_tick <= self.current_tick {
|
|
301
|
+
to_deliver.push(msg);
|
|
302
|
+
} else {
|
|
303
|
+
remaining.push(msg);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
self.in_flight = remaining;
|
|
308
|
+
|
|
309
|
+
// Queue delivered messages
|
|
310
|
+
for msg in to_deliver {
|
|
311
|
+
if !self.dropped_messages.contains(&msg.id) {
|
|
312
|
+
self.delivered_queues
|
|
313
|
+
.entry(msg.to)
|
|
314
|
+
.or_insert_with(VecDeque::new)
|
|
315
|
+
.push_back((msg.from, msg.payload));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fn receive(&mut self, node: NodeId) -> Vec<(NodeId, Vec<u8>)> {
|
|
321
|
+
self.delivered_queues
|
|
322
|
+
.remove(&node)
|
|
323
|
+
.map(|q| q.into_iter().collect())
|
|
324
|
+
.unwrap_or_default()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
fn apply_fault(&mut self, event: NetworkEvent) {
|
|
328
|
+
match event {
|
|
329
|
+
NetworkEvent::Delay {
|
|
330
|
+
message_id,
|
|
331
|
+
ticks,
|
|
332
|
+
} => {
|
|
333
|
+
// Find message and delay delivery
|
|
334
|
+
for msg in &mut self.in_flight {
|
|
335
|
+
if msg.id == message_id {
|
|
336
|
+
msg.delivery_tick += ticks;
|
|
337
|
+
break;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
NetworkEvent::Drop { message_id } => {
|
|
342
|
+
self.dropped_messages.insert(message_id);
|
|
343
|
+
}
|
|
344
|
+
NetworkEvent::Duplicate { message_id } => {
|
|
345
|
+
// Clone the in-flight message
|
|
346
|
+
if let Some(orig) = self.in_flight.iter().find(|m| m.id == message_id) {
|
|
347
|
+
let dup = InFlightMessage {
|
|
348
|
+
id: MessageId(self.message_counter),
|
|
349
|
+
from: orig.from,
|
|
350
|
+
to: orig.to,
|
|
351
|
+
payload: orig.payload.clone(),
|
|
352
|
+
delivery_tick: orig.delivery_tick,
|
|
353
|
+
};
|
|
354
|
+
self.message_counter += 1;
|
|
355
|
+
self.in_flight.push(dup);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
NetworkEvent::Partition { from, to } => {
|
|
359
|
+
self.connectivity.partition(from, to);
|
|
360
|
+
}
|
|
361
|
+
NetworkEvent::Heal { from, to } => {
|
|
362
|
+
self.connectivity.heal(from, to);
|
|
363
|
+
}
|
|
364
|
+
NetworkEvent::Reorder { connection } => {
|
|
365
|
+
// Reorder messages on this connection
|
|
366
|
+
let mut msgs_on_conn: Vec<_> = self
|
|
367
|
+
.in_flight
|
|
368
|
+
.iter()
|
|
369
|
+
.enumerate()
|
|
370
|
+
.filter(|(_, m)| m.from == connection.from && m.to == connection.to)
|
|
371
|
+
.collect();
|
|
372
|
+
|
|
373
|
+
if msgs_on_conn.len() > 1 {
|
|
374
|
+
// Reverse order of messages on this connection
|
|
375
|
+
msgs_on_conn.reverse();
|
|
376
|
+
let indices: Vec<_> = msgs_on_conn.iter().map(|(i, _)| *i).collect();
|
|
377
|
+
|
|
378
|
+
for (new_pos, &old_pos) in indices.iter().enumerate() {
|
|
379
|
+
if new_pos != old_pos {
|
|
380
|
+
self.in_flight.swap(new_pos, old_pos);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
fn current_tick(&self) -> u64 {
|
|
389
|
+
self.current_tick
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
fn get_connectivity(&self, from: NodeId, to: NodeId) -> LinkState {
|
|
393
|
+
self.connectivity.get_state(from, to)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
#[cfg(test)]
|
|
398
|
+
mod tests {
|
|
399
|
+
use super::*;
|
|
400
|
+
|
|
401
|
+
#[test]
|
|
402
|
+
fn test_in_memory_transport_basic() {
|
|
403
|
+
let mut transport = InMemoryTransport::new();
|
|
404
|
+
|
|
405
|
+
let n1 = NodeId(1);
|
|
406
|
+
let n2 = NodeId(2);
|
|
407
|
+
|
|
408
|
+
transport.send(n1, n2, vec![1, 2, 3]);
|
|
409
|
+
let messages = transport.receive(n2);
|
|
410
|
+
|
|
411
|
+
assert_eq!(messages.len(), 1);
|
|
412
|
+
assert_eq!(messages[0].0, n1);
|
|
413
|
+
assert_eq!(messages[0].1, vec![1, 2, 3]);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
#[test]
|
|
417
|
+
fn test_adversarial_transport_basic() {
|
|
418
|
+
let mut transport = AdversarialTransport::new();
|
|
419
|
+
|
|
420
|
+
let n1 = NodeId(1);
|
|
421
|
+
let n2 = NodeId(2);
|
|
422
|
+
|
|
423
|
+
let msg_id = transport.send(n1, n2, vec![1, 2, 3]);
|
|
424
|
+
assert!(msg_id.0 >= 0);
|
|
425
|
+
|
|
426
|
+
transport.tick();
|
|
427
|
+
let messages = transport.receive(n2);
|
|
428
|
+
|
|
429
|
+
assert_eq!(messages.len(), 1);
|
|
430
|
+
assert_eq!(messages[0].0, n1);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
#[test]
|
|
434
|
+
fn test_partition() {
|
|
435
|
+
let mut transport = AdversarialTransport::new();
|
|
436
|
+
|
|
437
|
+
let n1 = NodeId(1);
|
|
438
|
+
let n2 = NodeId(2);
|
|
439
|
+
|
|
440
|
+
// Partition n1 → n2
|
|
441
|
+
transport.apply_fault(NetworkEvent::Partition {
|
|
442
|
+
from: n1,
|
|
443
|
+
to: n2,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
// Message should be dropped
|
|
447
|
+
let _msg_id = transport.send(n1, n2, vec![1, 2, 3]);
|
|
448
|
+
transport.tick();
|
|
449
|
+
|
|
450
|
+
let messages = transport.receive(n2);
|
|
451
|
+
assert_eq!(messages.len(), 0);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
#[test]
|
|
455
|
+
fn test_delay() {
|
|
456
|
+
let mut transport = AdversarialTransport::new();
|
|
457
|
+
|
|
458
|
+
let n1 = NodeId(1);
|
|
459
|
+
let n2 = NodeId(2);
|
|
460
|
+
|
|
461
|
+
let msg_id = transport.send(n1, n2, vec![1, 2, 3]);
|
|
462
|
+
|
|
463
|
+
// Delay the message by 5 ticks
|
|
464
|
+
transport.apply_fault(NetworkEvent::Delay {
|
|
465
|
+
message_id: msg_id,
|
|
466
|
+
ticks: 5,
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Tick 1-4: no messages
|
|
470
|
+
for _ in 0..4 {
|
|
471
|
+
transport.tick();
|
|
472
|
+
let messages = transport.receive(n2);
|
|
473
|
+
assert_eq!(messages.len(), 0);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Tick 5: message delivered
|
|
477
|
+
transport.tick();
|
|
478
|
+
let messages = transport.receive(n2);
|
|
479
|
+
assert_eq!(messages.len(), 1);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
#[test]
|
|
483
|
+
fn test_duplicate() {
|
|
484
|
+
let mut transport = AdversarialTransport::new();
|
|
485
|
+
|
|
486
|
+
let n1 = NodeId(1);
|
|
487
|
+
let n2 = NodeId(2);
|
|
488
|
+
|
|
489
|
+
let msg_id = transport.send(n1, n2, vec![1, 2, 3]);
|
|
490
|
+
|
|
491
|
+
// Duplicate the message
|
|
492
|
+
transport.apply_fault(NetworkEvent::Duplicate {
|
|
493
|
+
message_id: msg_id,
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
transport.tick();
|
|
497
|
+
let messages = transport.receive(n2);
|
|
498
|
+
|
|
499
|
+
assert_eq!(messages.len(), 2);
|
|
500
|
+
assert_eq!(messages[0].1, vec![1, 2, 3]);
|
|
501
|
+
assert_eq!(messages[1].1, vec![1, 2, 3]);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
#[test]
|
|
505
|
+
fn test_drop() {
|
|
506
|
+
let mut transport = AdversarialTransport::new();
|
|
507
|
+
|
|
508
|
+
let n1 = NodeId(1);
|
|
509
|
+
let n2 = NodeId(2);
|
|
510
|
+
|
|
511
|
+
let msg_id = transport.send(n1, n2, vec![1, 2, 3]);
|
|
512
|
+
|
|
513
|
+
transport.apply_fault(NetworkEvent::Drop {
|
|
514
|
+
message_id: msg_id,
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
transport.tick();
|
|
518
|
+
let messages = transport.receive(n2);
|
|
519
|
+
|
|
520
|
+
assert_eq!(messages.len(), 0);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
#[test]
|
|
524
|
+
fn test_asymmetric_partition() {
|
|
525
|
+
let mut transport = AdversarialTransport::new();
|
|
526
|
+
|
|
527
|
+
let n1 = NodeId(1);
|
|
528
|
+
let n2 = NodeId(2);
|
|
529
|
+
|
|
530
|
+
// Partition n1 → n2 only (asymmetric)
|
|
531
|
+
transport.apply_fault(NetworkEvent::Partition {
|
|
532
|
+
from: n1,
|
|
533
|
+
to: n2,
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
// n1 → n2 blocked
|
|
537
|
+
let _msg_id = transport.send(n1, n2, vec![1, 2, 3]);
|
|
538
|
+
transport.tick();
|
|
539
|
+
assert_eq!(transport.receive(n2).len(), 0);
|
|
540
|
+
|
|
541
|
+
// n2 → n1 allowed
|
|
542
|
+
let msg_id = transport.send(n2, n1, vec![4, 5, 6]);
|
|
543
|
+
assert!(msg_id.0 >= 0);
|
|
544
|
+
transport.tick();
|
|
545
|
+
assert_eq!(transport.receive(n1).len(), 1);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
#[test]
|
|
549
|
+
fn test_connectivity_matrix() {
|
|
550
|
+
let mut connectivity = ConnectivityMatrix::new();
|
|
551
|
+
|
|
552
|
+
let n1 = NodeId(1);
|
|
553
|
+
let n2 = NodeId(2);
|
|
554
|
+
|
|
555
|
+
// Initially connected
|
|
556
|
+
assert!(connectivity.can_reach(n1, n2));
|
|
557
|
+
|
|
558
|
+
// Partition
|
|
559
|
+
connectivity.partition(n1, n2);
|
|
560
|
+
assert!(!connectivity.can_reach(n1, n2));
|
|
561
|
+
|
|
562
|
+
// Heal
|
|
563
|
+
connectivity.heal(n1, n2);
|
|
564
|
+
assert!(connectivity.can_reach(n1, n2));
|
|
565
|
+
}
|
|
566
|
+
}
|