iuna

iuna

iuna - experimental mainnet-candidate protocol
git clone https://getiuna.org/git/iuna.git
Log | Files | Refs | README | LICENSE

commit 63d83473d43052e898fcbc4a54130793823bf149
parent ef3d672886fc8786870a7dbbeb8954e76d3dc806
Author: Joris Hartog <jorishartog@hotmail.com>
Date:   Wed, 19 Aug 2026 22:41:50 +0200

Cover future block P2P policy

Diffstat:
Msrc/adapters/p2p/tests.rs | 81++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 1 deletion(-)

diff --git a/src/adapters/p2p/tests.rs b/src/adapters/p2p/tests.rs @@ -9,7 +9,7 @@ use crate::{ GossipEnvelope, NETWORK_ID, NodeCore, PROTOCOL_VERSION, PeerBook, PeerDirection, ProtocolHello, }, - domain::{Ledger, Wallet}, + domain::{Ledger, Wallet, run_vdf}, }; use tokio::io::AsyncWriteExt; @@ -136,6 +136,85 @@ async fn single_block_fork_error_requests_chain_snapshot() { } #[tokio::test] +async fn future_block_rejection_does_not_poison_peer_or_later_acceptance() { + let alice = Wallet::from_seed("future-block-p2p-alice"); + let allocations = allocations(std::slice::from_ref(&alice), 1_000); + let mut producer = node("future-block-producer", alice.clone(), allocations.clone()); + queue_plaintext_burn(&mut producer, &alice, 1); + producer.drain_outbox(); + let future_timestamp = crate::app::now_ms().saturating_add(10 * 60 * 1_000); + let prepared = producer + .ledger() + .prepare_next_block(alice.address(), future_timestamp) + .unwrap(); + let vdf_output = run_vdf(prepared.vdf_seed(), prepared.vdf_rounds()); + let future_block = prepared.finish(&alice, vdf_output); + + let local_node = node("future-block-local", alice.clone(), allocations); + let original_tip = local_node.ledger().tip_hash().to_string(); + let peers = Arc::new(tokio::sync::Mutex::new(PeerBook::default())); + let network = gossip_network( + Arc::new(tokio::sync::Mutex::new(local_node)), + Arc::clone(&peers), + "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 peer = remote_addr.to_string(); + let mut known_peer = Some(peer.clone()); + + super::process_envelope( + &network, + &mut server_writer, + remote_addr, + &mut known_peer, + GossipEnvelope::Block(future_block.clone()), + ) + .await + .unwrap(); + + assert_eq!( + network.inner.node.lock().await.ledger().tip_hash(), + original_tip + ); + assert_eq!(network.metrics().rejected_blocks, 1); + let recorded_peer = peers + .lock() + .await + .list() + .into_iter() + .find(|entry| entry.address == peer) + .expect("future block sender should be recorded"); + assert_eq!(recorded_peer.misbehavior_score, 0); + assert!(recorded_peer.banned_until_ms.is_none()); + assert!( + recorded_peer + .last_error + .as_deref() + .is_some_and(|error| error.contains("too far in the future")) + ); + + let verified_block = super::verify_block_vdf(future_block.clone()).await.unwrap(); + network + .inner + .node + .lock() + .await + .receive_preverified_block_at(verified_block, future_block.timestamp_ms) + .unwrap(); + assert_eq!( + network.inner.node.lock().await.ledger().tip_hash(), + future_block.hash + ); +} + +#[tokio::test] async fn chain_snapshot_request_only_writes_snapshot_without_mutating_local_state() { let alice = Wallet::from_seed("snapshot-request-spam-alice"); let allocations = allocations(std::slice::from_ref(&alice), 1_000);