Skip to content

Commit 142e209

Browse files
sandtreaderclaude
andcommitted
net-rs: disconnect on LeiosFetch store miss (risk register #10)
CIP-0164 specifies that a server should disconnect if the client requests an EB or its transactions that the server does not have. Replace unwrap_or_default() with explicit Option matching — None now breaks the server loop, triggering disconnect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0135325 commit 142e209

1 file changed

Lines changed: 91 additions & 8 deletions

File tree

net-rs/net-core/src/peer/server_handlers.rs

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -424,21 +424,25 @@ pub async fn serve_leios_fetch(lf_send: CodecSend, lf_recv: CodecRecv, store: Ar
424424
match msg {
425425
LfMsg::MsgLeiosBlockRequest { point } => {
426426
let block = match &point {
427-
Point::Specific { slot, hash } => {
428-
store.get_block(*slot, hash).unwrap_or_default()
429-
}
430-
Point::Origin => Vec::new(),
427+
Point::Specific { slot, hash } => store.get_block(*slot, hash),
428+
Point::Origin => None,
429+
};
430+
let Some(block) = block else {
431+
// CIP-0164: server should disconnect if it doesn't have the requested EB.
432+
break;
431433
};
432434
if runner.send(&LfMsg::MsgLeiosBlock { block }).await.is_err() {
433435
break;
434436
}
435437
}
436438
LfMsg::MsgLeiosBlockTxsRequest { point, bitmap: _ } => {
437439
let transactions = match &point {
438-
Point::Specific { slot, hash } => {
439-
store.get_block_txs(*slot, hash).unwrap_or_default()
440-
}
441-
Point::Origin => Vec::new(),
440+
Point::Specific { slot, hash } => store.get_block_txs(*slot, hash),
441+
Point::Origin => None,
442+
};
443+
let Some(transactions) = transactions else {
444+
// CIP-0164: server should disconnect if it doesn't have the requested EB txs.
445+
break;
442446
};
443447
if runner
444448
.send(&LfMsg::MsgLeiosBlockTxs { transactions })
@@ -732,4 +736,83 @@ mod tests {
732736
mux_a.abort();
733737
mux_b.abort();
734738
}
739+
740+
#[tokio::test]
741+
async fn leios_fetch_disconnects_on_missing_block() {
742+
let lf_proto = ProtocolConfig {
743+
id: leios_fetch::PROTOCOL_ID,
744+
traffic_class: TrafficClass::Priority,
745+
ingress_limit: leios_fetch::INGRESS_LIMIT,
746+
egress_queue_size: 16,
747+
};
748+
749+
let ((client_send, client_recv), (server_send, server_recv), mux_a, mux_b) =
750+
mux_pair_for_protocol(&lf_proto);
751+
752+
// Empty store — no blocks injected.
753+
let (store, _rx) = LeiosStore::new(100);
754+
755+
let server_handle = tokio::spawn(serve_leios_fetch(server_send, server_recv, store));
756+
757+
// Client: request a block that doesn't exist.
758+
let mut client = Runner::<LeiosFetch>::new(Role::Client, client_send, client_recv);
759+
let result = leios_fetch::fetch_block(
760+
&mut client,
761+
Point::Specific {
762+
slot: 99,
763+
hash: [0xFF; 32],
764+
},
765+
)
766+
.await;
767+
768+
// Server should have disconnected — client sees an error.
769+
assert!(
770+
result.is_err(),
771+
"expected error from disconnect, got {result:?}"
772+
);
773+
774+
server_handle.await.ok();
775+
mux_a.abort();
776+
mux_b.abort();
777+
}
778+
779+
#[tokio::test]
780+
async fn leios_fetch_disconnects_on_missing_block_txs() {
781+
let lf_proto = ProtocolConfig {
782+
id: leios_fetch::PROTOCOL_ID,
783+
traffic_class: TrafficClass::Priority,
784+
ingress_limit: leios_fetch::INGRESS_LIMIT,
785+
egress_queue_size: 16,
786+
};
787+
788+
let ((client_send, client_recv), (server_send, server_recv), mux_a, mux_b) =
789+
mux_pair_for_protocol(&lf_proto);
790+
791+
// Empty store — no block txs injected.
792+
let (store, _rx) = LeiosStore::new(100);
793+
794+
let server_handle = tokio::spawn(serve_leios_fetch(server_send, server_recv, store));
795+
796+
// Client: request txs for a block that doesn't exist.
797+
let mut client = Runner::<LeiosFetch>::new(Role::Client, client_send, client_recv);
798+
let result = leios_fetch::fetch_block_txs(
799+
&mut client,
800+
Point::Specific {
801+
slot: 99,
802+
hash: [0xFF; 32],
803+
},
804+
std::collections::BTreeMap::new(),
805+
)
806+
.await;
807+
808+
// Server should have disconnected — client sees an error.
809+
assert!(
810+
result.is_err(),
811+
"expected error from disconnect, got {result:?}"
812+
);
813+
814+
server_handle.await.ok();
815+
mux_a.abort();
816+
mux_b.abort();
817+
}
735818
}

0 commit comments

Comments
 (0)