use anyhow::Result;
use blvm_node::network::{
protocol::{NetworkAddress, ProtocolMessage, ProtocolParser, VersionMessage},
NetworkManager,
};
use blvm_protocol::genesis;
use blvm_protocol::serialization::serialize_block_with_witnesses;
use std::net::SocketAddr;
fn build_version_wire_message() -> Vec<u8> {
let version_msg = VersionMessage {
version: 70015,
services: 1,
timestamp: 1_234_567_890,
addr_recv: NetworkAddress {
services: 1,
ip: [0; 16],
port: 8333,
},
addr_from: NetworkAddress {
services: 1,
ip: [0; 16],
port: 8333,
},
nonce: 0x9A8B7C6D5E4F3021,
user_agent: "block-connection-test/1.0".to_string(),
start_height: 0,
relay: true,
};
ProtocolParser::serialize_message(&ProtocolMessage::Version(version_msg))
.expect("serialize Version")
}
fn build_block_wire_message(block: &blvm_protocol::Block) -> Vec<u8> {
let witnesses: Vec<Vec<blvm_protocol::segwit::Witness>> =
(0..block.transactions.len()).map(|_| vec![]).collect();
let payload = serialize_block_with_witnesses(block, &witnesses, false);
let mut msg = Vec::new();
msg.extend_from_slice(&[0xf9u8, 0xbe, 0xb4, 0xd9]);
let mut cmd = [0u8; 12];
cmd[..5].copy_from_slice(b"block");
msg.extend_from_slice(&cmd);
msg.extend_from_slice(&(payload.len() as u32).to_le_bytes());
let checksum = ProtocolParser::calculate_checksum(&payload);
msg.extend_from_slice(&checksum);
msg.extend_from_slice(&payload);
msg
}
fn block_hash_from_header(header: &blvm_protocol::BlockHeader) -> blvm_protocol::Hash {
use blvm_node::storage::hashing::double_sha256;
let mut header_bytes = Vec::with_capacity(80);
header_bytes.extend_from_slice(&(header.version as i32).to_le_bytes());
header_bytes.extend_from_slice(&header.prev_block_hash);
header_bytes.extend_from_slice(&header.merkle_root);
header_bytes.extend_from_slice(&(header.timestamp as u32).to_le_bytes());
header_bytes.extend_from_slice(&(header.bits as u32).to_le_bytes());
header_bytes.extend_from_slice(&(header.nonce as u32).to_le_bytes());
let h = double_sha256(&header_bytes);
let mut out = [0u8; 32];
out.copy_from_slice(&h);
out
}
#[tokio::test(flavor = "multi_thread")]
async fn test_block_request_completion_first_connection() -> Result<()> {
let genesis = genesis::mainnet_genesis();
let block_hash = block_hash_from_header(&genesis.header);
let block_wire = build_block_wire_message(&genesis);
let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
let network = NetworkManager::with_config(
addr,
5,
blvm_node::network::transport::TransportPreference::TCP_ONLY,
None,
);
let peer_addr: SocketAddr = "127.0.0.1:18444".parse().unwrap();
let block_rx = network.register_block_request(peer_addr, block_hash);
network
.handle_incoming_wire_tcp(peer_addr, build_version_wire_message())
.await?;
network
.handle_incoming_wire_tcp(peer_addr, block_wire)
.await?;
let (received_block, witnesses) =
tokio::time::timeout(std::time::Duration::from_secs(5), block_rx)
.await
.map_err(|_| anyhow::anyhow!("Block response timeout"))?
.map_err(|_| anyhow::anyhow!("Block channel closed"))?;
assert_eq!(
received_block.header.merkle_root,
genesis.header.merkle_root
);
assert_eq!(
received_block.transactions.len(),
genesis.transactions.len()
);
assert_eq!(witnesses.len(), genesis.transactions.len());
Ok(())
}