use blvm_protocol::network::{NetworkAddress, NetworkMessage, VersionMessage};
#[test]
fn test_checksum_calculation() {
let version = VersionMessage {
version: 70001,
services: 1,
timestamp: 1234567890,
addr_recv: NetworkAddress {
services: 1,
ip: [0u8; 16],
port: 8333,
},
addr_from: NetworkAddress {
services: 1,
ip: [0u8; 16],
port: 8333,
},
nonce: 12345,
user_agent: "test/1.0".to_string(),
start_height: 0,
relay: true,
};
let message = NetworkMessage::Version(version);
assert!(matches!(message, NetworkMessage::Version(_)));
}
#[test]
fn test_wire_format_constants() {
let expected_header_size = 24; let expected_max_payload = 32 * 1024 * 1024;
assert_eq!(expected_header_size, 24);
assert_eq!(expected_max_payload, 33_554_432);
}
#[test]
fn test_message_size_limits() {
let max_payload = 32 * 1024 * 1024;
assert_eq!(max_payload, 33_554_432);
assert!(max_payload > 0);
}
#[test]
fn test_magic_bytes_validation() {
let mainnet_magic = [0xf9, 0xbe, 0xb4, 0xd9];
let testnet_magic = [0x0b, 0x11, 0x09, 0x07];
let regtest_magic = [0xfa, 0xbf, 0xb5, 0xda];
assert_ne!(mainnet_magic, testnet_magic);
assert_ne!(mainnet_magic, regtest_magic);
assert_ne!(testnet_magic, regtest_magic);
}
#[test]
fn test_command_encoding() {
let commands = vec![
"version", "verack", "addr", "inv", "getdata", "ping", "pong",
];
for cmd in commands {
assert!(cmd.len() <= 12, "Command '{}' exceeds 12 bytes", cmd);
assert!(cmd.is_ascii(), "Command '{}' is not ASCII", cmd);
}
}
#[test]
fn test_payload_length_encoding() {
let test_lengths = vec![0u32, 1, 100, 1000, 10000, u32::MAX];
for length in test_lengths {
let encoded = length.to_le_bytes();
let decoded = u32::from_le_bytes(encoded);
assert_eq!(
length, decoded,
"Payload length encoding failed for {}",
length
);
}
}
#[test]
fn test_checksum_algorithm() {
use sha2::{Digest, Sha256};
let test_payload = b"test payload";
let hash1 = Sha256::digest(test_payload);
let hash2 = Sha256::digest(hash1);
let checksum: [u8; 4] = hash2[..4].try_into().unwrap();
assert_eq!(checksum.len(), 4);
let hash1_2 = Sha256::digest(test_payload);
let hash2_2 = Sha256::digest(hash1_2);
let checksum2: [u8; 4] = hash2_2[..4].try_into().unwrap();
assert_eq!(checksum, checksum2);
}
#[test]
fn test_empty_payload() {
let empty_payload: Vec<u8> = vec![];
assert_eq!(empty_payload.len(), 0);
use sha2::{Digest, Sha256};
let hash1 = Sha256::digest(&empty_payload);
let hash2 = Sha256::digest(hash1);
let checksum = &hash2[..4];
assert_eq!(checksum.len(), 4);
}
#[test]
fn test_large_payload_boundary() {
let max_payload = 32 * 1024 * 1024;
let over_limit = max_payload + 1;
assert_eq!(max_payload, 33_554_432);
assert!(over_limit > max_payload);
}
#[test]
fn test_message_header_structure() {
let header_size = 4 + 12 + 4 + 4; assert_eq!(header_size, 24);
let magic_size = 4;
let command_size = 12;
let length_size = 4;
let checksum_size = 4;
assert_eq!(
magic_size + command_size + length_size + checksum_size,
header_size
);
}
#[test]
fn test_version_message_serialization_roundtrip() {
use blvm_protocol::wire::{deserialize_version, serialize_version};
let original = VersionMessage {
version: 70015,
services: 0x0000000000000001, timestamp: 1234567890,
addr_recv: NetworkAddress {
services: 0x0000000000000001,
ip: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00,
0x00, 0x01,
], port: 8333,
},
addr_from: NetworkAddress {
services: 0x0000000000000001,
ip: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00,
0x00, 0x01,
], port: 8333,
},
nonce: 0x1234567890abcdef,
user_agent: "/BitcoinCommons:0.1.0/".to_string(),
start_height: 100000,
relay: true,
};
let serialized = serialize_version(&original).expect("Serialization should succeed");
let deserialized = deserialize_version(&serialized).expect("Deserialization should succeed");
assert_eq!(original.version, deserialized.version);
assert_eq!(original.services, deserialized.services);
assert_eq!(original.timestamp, deserialized.timestamp);
assert_eq!(original.addr_recv.services, deserialized.addr_recv.services);
assert_eq!(original.addr_recv.ip, deserialized.addr_recv.ip);
assert_eq!(original.addr_recv.port, deserialized.addr_recv.port);
assert_eq!(original.addr_from.services, deserialized.addr_from.services);
assert_eq!(original.addr_from.ip, deserialized.addr_from.ip);
assert_eq!(original.addr_from.port, deserialized.addr_from.port);
assert_eq!(original.nonce, deserialized.nonce);
assert_eq!(original.user_agent, deserialized.user_agent);
assert_eq!(original.start_height, deserialized.start_height);
assert_eq!(original.relay, deserialized.relay);
}
#[test]
fn test_version_message_wire_format_compatibility() {
use blvm_protocol::wire::serialize_version;
let version = VersionMessage {
version: 70015,
services: 0x0000000000000001, timestamp: 1234567890,
addr_recv: NetworkAddress {
services: 0x0000000000000001,
ip: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00,
0x00, 0x01,
], port: 8333,
},
addr_from: NetworkAddress {
services: 0x0000000000000001,
ip: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00,
0x00, 0x01,
], port: 8333,
},
nonce: 0x1234567890abcdef,
user_agent: "/BitcoinCommons:0.1.0/".to_string(),
start_height: 0,
relay: true,
};
let serialized = serialize_version(&version).expect("Serialization should succeed");
let min_size = 4 + 8 + 8 + 26 + 26 + 8 + 1 + 22 + 4 + 1; assert!(
serialized.len() >= min_size,
"Serialized version message too short"
);
let version_bytes = [serialized[0], serialized[1], serialized[2], serialized[3]];
let decoded_version = i32::from_le_bytes(version_bytes) as u32;
assert_eq!(decoded_version, 70015);
let services_bytes = [
serialized[4],
serialized[5],
serialized[6],
serialized[7],
serialized[8],
serialized[9],
serialized[10],
serialized[11],
];
let decoded_services = u64::from_le_bytes(services_bytes);
assert_eq!(decoded_services, 0x0000000000000001);
let addr_recv_port_start = 20 + 8 + 16; let port_bytes = [
serialized[addr_recv_port_start],
serialized[addr_recv_port_start + 1],
];
let decoded_port = u16::from_be_bytes(port_bytes);
assert_eq!(decoded_port, 8333, "Port should be big-endian");
}
#[test]
fn test_version_message_user_agent_variants() {
use blvm_protocol::wire::{deserialize_version, serialize_version};
let test_cases = vec![
"".to_string(), "/BitcoinCommons:0.1.0/".to_string(), "/BitcoinCommons:0.1.0/".repeat(10), ];
for user_agent in test_cases {
let version = VersionMessage {
version: 70015,
services: 0x0000000000000001,
timestamp: 1234567890,
addr_recv: NetworkAddress {
services: 0,
ip: [0u8; 16],
port: 0,
},
addr_from: NetworkAddress {
services: 0,
ip: [0u8; 16],
port: 0,
},
nonce: 0,
user_agent: user_agent.clone(),
start_height: 0,
relay: false,
};
let serialized = serialize_version(&version).expect("Serialization should succeed");
let deserialized =
deserialize_version(&serialized).expect("Deserialization should succeed");
assert_eq!(
deserialized.user_agent, user_agent,
"User agent should match after round-trip"
);
}
}
#[test]
fn test_version_message_network_address_encoding() {
use blvm_protocol::wire::serialize_version;
let version = VersionMessage {
version: 70015,
services: 0,
timestamp: 0,
addr_recv: NetworkAddress {
services: 0x0102030405060708,
ip: [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x00,
0x00, 0x01,
], port: 0x1234, },
addr_from: NetworkAddress {
services: 0,
ip: [0u8; 16],
port: 0,
},
nonce: 0,
user_agent: "".to_string(),
start_height: 0,
relay: false,
};
let serialized = serialize_version(&version).expect("Serialization should succeed");
let services_start = 20;
let services_bytes = [
serialized[services_start],
serialized[services_start + 1],
serialized[services_start + 2],
serialized[services_start + 3],
serialized[services_start + 4],
serialized[services_start + 5],
serialized[services_start + 6],
serialized[services_start + 7],
];
let decoded_services = u64::from_le_bytes(services_bytes);
assert_eq!(decoded_services, 0x0102030405060708);
let port_start = 20 + 8 + 16;
let port_bytes = [serialized[port_start], serialized[port_start + 1]];
let decoded_port = u16::from_be_bytes(port_bytes);
assert_eq!(decoded_port, 0x1234);
}