iuna

iuna - experimental devnet protocol
git clone https://iuna.jhx.app/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 431e14fab3371c27e4ebc813bfebd89c8e673683
parent 40a1500136eed2904d0fe8d667e8d5d7a2b5715e
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Fri, 31 Jul 2026 20:43:58 +0200

Close transient P2P verification sessions

Diffstat:
Msrc/adapters/p2p.rs | 91++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 90 insertions(+), 1 deletion(-)

diff --git a/src/adapters/p2p.rs b/src/adapters/p2p.rs @@ -580,7 +580,6 @@ impl GossipNetwork { } Err(mpsc::error::TrySendError::Closed(_)) => { P2pMetricsCounters::inc(&self.inner.metrics.outbound_queue_closed); - self.inner.sessions.lock().await.remove(&peer); } } } @@ -1062,6 +1061,12 @@ async fn session_loop( .await?; peer_status = Some(status); maybe_request_catchup(&network, &mut writer, peer_status.as_ref().unwrap()).await?; + } else if respond_to_peer_verification_challenge(&network, &mut writer, &envelope) + .await? + { + if known_peer.is_none() { + return Ok(()); + } } else { process_envelope( &network, @@ -1170,6 +1175,12 @@ async fn session_loop( continue; } + if respond_to_peer_verification_challenge(&network, &mut writer, &envelope).await? { + if known_peer.is_none() { + return Ok(()); + } + continue; + } process_envelope( &network, &mut writer, @@ -1185,6 +1196,20 @@ async fn session_loop( } } +async fn respond_to_peer_verification_challenge( + network: &GossipNetwork, + writer: &mut OwnedWriteHalf, + envelope: &GossipEnvelope, +) -> Result<bool> { + let GossipEnvelope::PeerVerificationChallenge { address, nonce } = envelope else { + return Ok(false); + }; + if let Some(response) = peer_verification_response(network, address, nonce) { + write_envelope(writer, &response).await?; + } + Ok(true) +} + async fn peer_is_configured_outbound(network: &GossipNetwork, peer: &str) -> bool { network .inner @@ -4048,6 +4073,70 @@ mod tests { } #[tokio::test] + async fn inbound_verification_only_session_closes_after_response() { + let alice = Wallet::from_seed("verification-only-close-alice"); + let allocations = allocations(std::slice::from_ref(&alice), 1_000); + let node = Arc::new(tokio::sync::Mutex::new(node("alice", alice, allocations))); + let peers = Arc::new(tokio::sync::Mutex::new(PeerBook::default())); + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let listen_addr = listener.local_addr().unwrap(); + drop(listener); + let network = super::GossipNetwork::start(node, peers, listen_addr, None, true) + .await + .unwrap(); + + let stream = tokio::net::TcpStream::connect(listen_addr).await.unwrap(); + let (reader, mut writer) = stream.into_split(); + let mut reader = super::LimitedLineReader::new(reader); + let hello_line = reader.read_line().await.unwrap().unwrap(); + let node_id = match super::parse_envelope(&hello_line).unwrap() { + GossipEnvelope::Hello(hello) => hello.node_id.unwrap(), + other => panic!("expected hello, got {other:?}"), + }; + let nonce = super::new_verification_nonce(); + super::write_envelope( + &mut writer, + &GossipEnvelope::PeerVerificationChallenge { + address: listen_addr.to_string(), + nonce: nonce.clone(), + }, + ) + .await + .unwrap(); + + let response_line = + tokio::time::timeout(std::time::Duration::from_secs(1), reader.read_line()) + .await + .unwrap() + .unwrap() + .unwrap(); + match super::parse_envelope(&response_line).unwrap() { + GossipEnvelope::PeerVerificationResponse { + address, + nonce: response_nonce, + node_id: response_node_id, + signature, + } => assert!(super::peer_verification_response_is_valid( + &address, + &response_nonce, + &response_node_id, + &signature, + &listen_addr.to_string(), + &nonce, + &node_id, + )), + other => panic!("expected verification response, got {other:?}"), + } + + let closed = tokio::time::timeout(std::time::Duration::from_secs(1), reader.read_line()) + .await + .unwrap() + .unwrap(); + assert!(closed.is_none()); + network.set_accept_inbound(false).await.unwrap(); + } + + #[tokio::test] async fn setup_placeholder_accepts_remote_genesis_and_adopts_snapshot() { let local_wallet = Wallet::from_seed("setup-placeholder-local"); let local_ledger = Ledger::new(BTreeMap::new(), 1);