iuna

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

commit ff7c727514f561265b8a3bf98288809ae2b26b42
parent 81e4e34090872bd7d361d0583adef3c552396e6f
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Tue, 11 Aug 2026 09:21:25 +0200

Request snapshot for single block fork errors

Diffstat:
Msrc/adapters/p2p/process.rs | 4++++
Msrc/adapters/p2p/tests.rs | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 71 insertions(+), 1 deletion(-)

diff --git a/src/adapters/p2p/process.rs b/src/adapters/p2p/process.rs @@ -131,7 +131,11 @@ pub(super) async fn process_envelope( }, Err(error) => Err(error), }; + let request_snapshot = result.as_ref().err().is_some_and(is_possible_fork_error); record_inbound_result(network, known_peer, remote_addr, result).await; + if request_snapshot { + write_envelope(writer, &GossipEnvelope::ChainSnapshotRequest).await?; + } network.forward_outbox().await; } GossipEnvelope::Blocks { blocks } => { diff --git a/src/adapters/p2p/tests.rs b/src/adapters/p2p/tests.rs @@ -13,7 +13,7 @@ use crate::{ }; use tokio::io::AsyncWriteExt; -use super::test_support::{allocations, node}; +use super::test_support::{allocations, gossip_network, node, queue_plaintext_burn}; #[tokio::test] async fn full_outbound_queue_is_metric_not_peer_error() { @@ -70,6 +70,72 @@ async fn full_outbound_queue_is_metric_not_peer_error() { } #[tokio::test] +async fn single_block_fork_error_requests_chain_snapshot() { + let alice = Wallet::from_seed("single-block-fork-alice"); + let allocations = allocations(std::slice::from_ref(&alice), 1_000); + let mut local_node = node( + "local-single-block-fork", + alice.clone(), + allocations.clone(), + ); + let mut remote_node = node("remote-single-block-fork", alice.clone(), allocations); + + queue_plaintext_burn(&mut local_node, &alice, 1); + local_node.drain_outbox(); + local_node.mine_one_at(1).unwrap(); + local_node.drain_outbox(); + + queue_plaintext_burn(&mut remote_node, &alice, 1); + remote_node.drain_outbox(); + remote_node.mine_one_at(2).unwrap(); + remote_node.drain_outbox(); + queue_plaintext_burn(&mut remote_node, &alice, 1); + remote_node.drain_outbox(); + let remote_block = remote_node.mine_one_at(3).unwrap(); + assert_eq!(remote_block.height, 2); + assert_ne!( + remote_block.prev_hash, + local_node.ledger().tip_hash().to_string() + ); + + let network = gossip_network( + Arc::new(tokio::sync::Mutex::new(local_node)), + Arc::new(tokio::sync::Mutex::new(PeerBook::default())), + "127.0.0.1:9544".parse().unwrap(), + None, + ); + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let client = tokio::net::TcpStream::connect(listener.local_addr().unwrap()) + .await + .unwrap(); + let (server, remote_addr) = listener.accept().await.unwrap(); + let (_server_reader, mut server_writer) = server.into_split(); + let (client_reader, _client_writer) = client.into_split(); + let mut client_reader = super::LimitedLineReader::new(client_reader); + let mut known_peer = None; + + super::process_envelope( + &network, + &mut server_writer, + remote_addr, + &mut known_peer, + GossipEnvelope::Block(remote_block), + ) + .await + .unwrap(); + + let line = tokio::time::timeout(std::time::Duration::from_secs(1), client_reader.read_line()) + .await + .unwrap() + .unwrap() + .unwrap(); + assert!(matches!( + super::parse_envelope(&line).unwrap(), + GossipEnvelope::ChainSnapshotRequest + )); +} + +#[tokio::test] async fn hello_rejects_wrong_network_or_genesis_without_banning() { let alice = Wallet::from_seed("hello-alice"); let allocations = allocations(std::slice::from_ref(&alice), 1_000);