1use crate::bip152::ShortTxId;
8use crate::error::ProtocolError;
9use crate::validation::ProtocolValidationContext;
10use crate::{BitcoinProtocolEngine, ProtocolConfig, Result};
11use blvm_consensus::error::ConsensusError;
12use blvm_consensus::types::UtxoSet;
13use blvm_consensus::types::{Block, BlockHeader, Hash, Transaction, ValidationResult};
14use std::sync::Arc;
15use thiserror::Error;
16
17pub mod commons {
19 pub use crate::commons::*;
20}
21
22#[cfg(all(
24 feature = "bip324",
25 any(target_arch = "x86_64", target_arch = "aarch64")
26))]
27pub mod v2_transport {
28 pub use crate::v2_transport::*;
29}
30
31#[cfg(test)]
32mod bip155_tests;
33
34#[derive(Debug, Clone, PartialEq)]
38pub enum NetworkMessage {
39 Version(VersionMessage),
40 VerAck,
41 Addr(AddrMessage),
42 AddrV2(AddrV2Message), Inv(InvMessage),
44 GetData(GetDataMessage),
45 GetHeaders(GetHeadersMessage),
46 Headers(HeadersMessage),
47 Block(Arc<Block>, Vec<Vec<blvm_consensus::segwit::Witness>>),
50 Tx(Arc<Transaction>),
51 Ping(PingMessage),
52 Pong(PongMessage),
53 MemPool,
54 FeeFilter(FeeFilterMessage),
55 GetBlocks(GetBlocksMessage),
57 GetAddr,
58 NotFound(NotFoundMessage),
59 Reject(RejectMessage),
60 SendHeaders,
61 SendCmpct(SendCmpctMessage),
63 CmpctBlock(CmpctBlockMessage),
64 GetBlockTxn(GetBlockTxnMessage),
65 BlockTxn(BlockTxnMessage),
66 #[cfg(feature = "utxo-commitments")]
68 GetUTXOSet(commons::GetUTXOSetMessage),
69 #[cfg(feature = "utxo-commitments")]
70 UTXOSet(commons::UTXOSetMessage),
71 #[cfg(feature = "utxo-commitments")]
72 GetFilteredBlock(commons::GetFilteredBlockMessage),
73 #[cfg(feature = "utxo-commitments")]
74 FilteredBlock(commons::FilteredBlockMessage),
75 GetBanList(commons::GetBanListMessage),
76 BanList(commons::BanListMessage),
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
81pub struct VersionMessage {
82 pub version: u32,
83 pub services: u64,
84 pub timestamp: i64,
85 pub addr_recv: NetworkAddress,
86 pub addr_from: NetworkAddress,
87 pub nonce: u64,
88 pub user_agent: String,
89 pub start_height: i32,
90 pub relay: bool,
91}
92
93#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
99pub struct BlockMessage {
100 pub block: Block,
101 #[serde(skip_serializing_if = "Vec::is_empty", default)]
103 pub witnesses: Vec<Vec<blvm_consensus::segwit::Witness>>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
108pub struct TxMessage {
109 pub transaction: Transaction,
110 #[serde(skip_serializing_if = "Vec::is_empty", default)]
112 pub witnesses: Vec<crate::segwit::Witness>,
113}
114
115#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
120pub struct CompactBlockMessage {
121 pub compact_block: crate::bip152::CompactBlock,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
126pub struct AddrMessage {
127 pub addresses: Vec<NetworkAddress>,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
132pub struct InvMessage {
133 pub inventory: Vec<InventoryVector>,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
138pub struct GetDataMessage {
139 pub inventory: Vec<InventoryVector>,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
144pub struct GetHeadersMessage {
145 pub version: u32,
146 pub block_locator_hashes: Vec<Hash>,
147 pub hash_stop: Hash,
148}
149
150#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
152pub struct HeadersMessage {
153 pub headers: Vec<BlockHeader>,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
158pub struct PingMessage {
159 pub nonce: u64,
160}
161
162#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
164pub struct PongMessage {
165 pub nonce: u64,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
170pub struct FeeFilterMessage {
171 pub feerate: u64,
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
176pub struct GetBlocksMessage {
177 pub version: u32,
178 pub block_locator_hashes: Vec<Hash>,
179 pub hash_stop: Hash,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
184pub struct NotFoundMessage {
185 pub inventory: Vec<InventoryVector>,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
190pub struct RejectMessage {
191 pub message: String, pub code: u8, pub reason: String, pub extra_data: Option<Hash>, }
196
197#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
199pub struct SendCmpctMessage {
200 pub version: u64,
202 pub prefer_cmpct: u8,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct CmpctBlockMessage {
209 pub header: BlockHeader,
211 pub nonce: u64,
213 pub short_ids: Vec<ShortTxId>,
215 pub prefilled_txs: Vec<PrefilledTransaction>,
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
221pub struct PrefilledTransaction {
222 pub index: u16,
224 pub tx: Transaction,
226 pub witness: Option<Vec<blvm_consensus::segwit::Witness>>,
228}
229
230#[derive(Debug, Clone, PartialEq, Eq, Error)]
232pub enum CompactBlockWireConvertError {
233 #[error("prefilled transaction index {0} does not fit in BIP152 u16 index")]
234 PrefilledIndexTooLarge(usize),
235 #[error("duplicate prefilled transaction index {0}")]
236 DuplicatePrefilledIndex(usize),
237}
238
239impl TryFrom<crate::bip152::CompactBlock> for CmpctBlockMessage {
240 type Error = CompactBlockWireConvertError;
241
242 fn try_from(mut value: crate::bip152::CompactBlock) -> std::result::Result<Self, Self::Error> {
247 value.prefilled_txs.sort_by_key(|(i, _, _)| *i);
248 let mut prefilled_txs = Vec::with_capacity(value.prefilled_txs.len());
249 let mut prev_idx: Option<usize> = None;
250 for (idx, tx, witness) in value.prefilled_txs {
251 if prev_idx == Some(idx) {
252 return Err(CompactBlockWireConvertError::DuplicatePrefilledIndex(idx));
253 }
254 prev_idx = Some(idx);
255 let index = u16::try_from(idx)
256 .map_err(|_| CompactBlockWireConvertError::PrefilledIndexTooLarge(idx))?;
257 prefilled_txs.push(PrefilledTransaction { index, tx, witness });
258 }
259 Ok(CmpctBlockMessage {
260 header: value.header,
261 nonce: value.nonce,
262 short_ids: value.short_ids,
263 prefilled_txs,
264 })
265 }
266}
267
268impl From<&CmpctBlockMessage> for crate::bip152::CompactBlock {
269 fn from(msg: &CmpctBlockMessage) -> Self {
270 Self {
271 header: msg.header.clone(),
272 nonce: msg.nonce,
273 short_ids: msg.short_ids.clone(),
274 prefilled_txs: msg
275 .prefilled_txs
276 .iter()
277 .map(|p| (usize::from(p.index), p.tx.clone(), p.witness.clone()))
278 .collect(),
279 }
280 }
281}
282
283impl From<CmpctBlockMessage> for crate::bip152::CompactBlock {
284 fn from(msg: CmpctBlockMessage) -> Self {
285 Self {
286 header: msg.header,
287 nonce: msg.nonce,
288 short_ids: msg.short_ids,
289 prefilled_txs: msg
290 .prefilled_txs
291 .into_iter()
292 .map(|p| (usize::from(p.index), p.tx, p.witness))
293 .collect(),
294 }
295 }
296}
297
298#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
300pub struct GetBlockTxnMessage {
301 pub block_hash: Hash,
303 pub indices: Vec<u16>,
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
309pub struct BlockTxnMessage {
310 pub block_hash: Hash,
312 pub transactions: Vec<Transaction>,
314 pub witnesses: Option<Vec<Vec<blvm_consensus::segwit::Witness>>>,
316}
317
318#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
320pub struct NetworkAddress {
321 pub services: u64,
322 pub ip: [u8; 16], pub port: u16,
324 pub time: u32,
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
330#[repr(u8)]
331pub enum AddressType {
332 IPv4 = 1,
333 IPv6 = 2,
334 TorV2 = 3,
335 TorV3 = 4,
336 I2P = 5,
337 CJDNS = 6,
338}
339
340impl AddressType {
341 pub fn address_length(&self) -> usize {
343 match self {
344 AddressType::IPv4 => 4,
345 AddressType::IPv6 => 16,
346 AddressType::TorV2 => 10,
347 AddressType::TorV3 => 32,
348 AddressType::I2P => 32,
349 AddressType::CJDNS => 16,
350 }
351 }
352
353 pub fn from_u8(value: u8) -> Option<Self> {
355 match value {
356 1 => Some(AddressType::IPv4),
357 2 => Some(AddressType::IPv6),
358 3 => Some(AddressType::TorV2),
359 4 => Some(AddressType::TorV3),
360 5 => Some(AddressType::I2P),
361 6 => Some(AddressType::CJDNS),
362 _ => None,
363 }
364 }
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
369pub struct AddrV2Message {
370 pub addresses: Vec<NetworkAddressV2>,
371}
372
373#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
375pub struct NetworkAddressV2 {
376 pub time: u32,
377 pub services: u64,
378 pub address_type: AddressType,
379 pub address: Vec<u8>, pub port: u16,
381}
382
383impl NetworkAddressV2 {
384 pub fn new(
386 time: u32,
387 services: u64,
388 address_type: AddressType,
389 address: Vec<u8>,
390 port: u16,
391 ) -> Result<Self> {
392 let expected_len = address_type.address_length();
393 if address.len() != expected_len {
394 return Err(ProtocolError::Consensus(ConsensusError::Serialization(
395 std::borrow::Cow::Owned(format!(
396 "Invalid address length for type {:?}: expected {}, got {}",
397 address_type,
398 expected_len,
399 address.len()
400 )),
401 )));
402 }
403 Ok(Self {
404 time,
405 services,
406 address_type,
407 address,
408 port,
409 })
410 }
411
412 pub fn to_legacy(&self) -> Option<NetworkAddress> {
414 match self.address_type {
415 AddressType::IPv4 => {
416 if self.address.len() == 4 {
417 let mut ipv6 = [0u8; 16];
419 ipv6[10] = 0xff;
420 ipv6[11] = 0xff;
421 ipv6[12..16].copy_from_slice(&self.address);
422 Some(NetworkAddress {
423 time: self.time,
424 services: self.services,
425 ip: ipv6,
426 port: self.port,
427 })
428 } else {
429 None
430 }
431 }
432 AddressType::IPv6 => {
433 if self.address.len() == 16 {
434 let mut ipv6 = [0u8; 16];
435 ipv6.copy_from_slice(&self.address);
436 Some(NetworkAddress {
437 time: self.time,
438 services: self.services,
439 ip: ipv6,
440 port: self.port,
441 })
442 } else {
443 None
444 }
445 }
446 _ => None, }
448 }
449}
450
451#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
453pub struct InventoryVector {
454 pub inv_type: u32,
455 pub hash: Hash,
456}
457
458#[derive(Debug, Clone)]
460pub enum NetworkResponse {
461 Ok,
462 SendMessage(Box<NetworkMessage>),
463 SendMessages(Vec<NetworkMessage>),
464 Reject(String),
465}
466
467#[derive(Clone)]
469pub struct PeerState {
470 pub version: u32,
471 pub services: u64,
472 pub user_agent: String,
473 pub start_height: i32,
474 pub handshake_complete: bool,
475 pub known_addresses: Vec<NetworkAddress>,
476 pub ping_nonce: Option<u64>,
477 pub last_pong: Option<std::time::SystemTime>,
478 pub min_fee_rate: Option<u64>,
480 pub relay_tx: bool,
483 pub prefer_headers: bool,
485 pub compact_block_version: Option<u8>,
487 pub prefer_compact_block: bool,
489 #[cfg(all(
492 feature = "bip324",
493 any(target_arch = "x86_64", target_arch = "aarch64")
494 ))]
495 pub v2_transport: Option<std::sync::Arc<crate::v2_transport::V2Transport>>,
496 #[cfg(all(
498 feature = "bip324",
499 any(target_arch = "x86_64", target_arch = "aarch64")
500 ))]
501 pub v2_handshake: Option<std::sync::Arc<crate::v2_transport::V2Handshake>>,
502}
503
504impl PeerState {
505 pub fn new() -> Self {
506 Self {
507 version: 0,
508 services: 0,
509 user_agent: String::new(),
510 start_height: 0,
511 handshake_complete: false,
512 known_addresses: Vec::new(),
513 ping_nonce: None,
514 last_pong: None,
515 min_fee_rate: None,
516 relay_tx: true,
517 prefer_headers: false,
518 compact_block_version: None,
519 prefer_compact_block: false,
520 #[cfg(all(
521 feature = "bip324",
522 any(target_arch = "x86_64", target_arch = "aarch64")
523 ))]
524 v2_transport: None,
525 #[cfg(all(
526 feature = "bip324",
527 any(target_arch = "x86_64", target_arch = "aarch64")
528 ))]
529 v2_handshake: None,
530 }
531 }
532
533 #[cfg(all(
535 feature = "bip324",
536 any(target_arch = "x86_64", target_arch = "aarch64")
537 ))]
538 pub fn supports_v2_transport(&self) -> bool {
539 use crate::service_flags::{has_flag, standard};
540 has_flag(self.services, standard::NODE_V2_TRANSPORT)
541 }
542
543 #[cfg(all(
545 feature = "bip324",
546 any(target_arch = "x86_64", target_arch = "aarch64")
547 ))]
548 pub fn is_v2_transport_active(&self) -> bool {
549 self.v2_transport.is_some()
550 }
551}
552
553impl Default for PeerState {
554 fn default() -> Self {
555 Self::new()
556 }
557}
558
559#[derive(Debug, Clone)]
561pub enum ChainObject {
562 Block(Arc<Block>),
563 Transaction(Arc<Transaction>),
564}
565
566impl ChainObject {
567 pub fn as_block(&self) -> Option<&Arc<Block>> {
568 match self {
569 ChainObject::Block(block) => Some(block),
570 _ => None,
571 }
572 }
573
574 pub fn as_transaction(&self) -> Option<&Arc<Transaction>> {
575 match self {
576 ChainObject::Transaction(tx) => Some(tx),
577 _ => None,
578 }
579 }
580}
581
582pub trait ChainStateAccess {
588 fn has_object(&self, hash: &Hash) -> bool;
590
591 fn get_object(&self, hash: &Hash) -> Option<ChainObject>;
593
594 fn get_headers_for_locator(&self, locator: &[Hash], stop: &Hash) -> Vec<BlockHeader>;
597
598 fn get_mempool_transactions(&self) -> Vec<Transaction>;
600
601 fn get_block_witnesses(
603 &self,
604 _hash: &Hash,
605 ) -> Option<Vec<Vec<blvm_consensus::segwit::Witness>>> {
606 None
607 }
608}
609
610pub fn process_network_message(
628 engine: &BitcoinProtocolEngine,
629 message: &NetworkMessage,
630 peer_state: &mut PeerState,
631 chain_access: Option<&dyn ChainStateAccess>,
632 utxo_set: Option<&UtxoSet>,
633 height: Option<u64>,
634) -> Result<NetworkResponse> {
635 let config = engine.get_config();
636 match message {
637 NetworkMessage::Version(version) => process_version_message(version, peer_state, config),
638 NetworkMessage::VerAck => process_verack_message(peer_state),
639 NetworkMessage::Addr(addr) => process_addr_message(addr, peer_state, config),
640 NetworkMessage::AddrV2(addrv2) => process_addrv2_message(addrv2, peer_state, config),
641 NetworkMessage::Inv(inv) => process_inv_message(inv, chain_access, config),
642 NetworkMessage::GetData(getdata) => process_getdata_message(getdata, chain_access, config),
643 NetworkMessage::GetHeaders(getheaders) => {
644 process_getheaders_message(getheaders, chain_access, config)
645 }
646 NetworkMessage::Headers(headers) => process_headers_message(headers, config),
647 NetworkMessage::Block(block, witnesses) => {
648 process_block_message(engine, block, witnesses, utxo_set, height, config)
649 }
650 NetworkMessage::Tx(tx) => process_tx_message(engine, tx, height),
651 NetworkMessage::Ping(ping) => process_ping_message(ping, peer_state),
652 NetworkMessage::Pong(pong) => process_pong_message(pong, peer_state),
653 NetworkMessage::MemPool => process_mempool_message(chain_access),
654 NetworkMessage::FeeFilter(feefilter) => process_feefilter_message(feefilter, peer_state),
655 NetworkMessage::GetBlocks(getblocks) => {
656 process_getblocks_message(getblocks, chain_access, config)
657 }
658 NetworkMessage::GetAddr => process_getaddr_message(peer_state, config),
659 NetworkMessage::NotFound(notfound) => process_notfound_message(notfound, config),
660 NetworkMessage::Reject(reject) => process_reject_message(reject, config),
661 NetworkMessage::SendHeaders => process_sendheaders_message(peer_state),
662 NetworkMessage::SendCmpct(sendcmpct) => {
663 process_sendcmpct_message(sendcmpct, peer_state, config)
664 }
665 NetworkMessage::CmpctBlock(cmpctblock) => process_cmpctblock_message(cmpctblock),
666 NetworkMessage::GetBlockTxn(getblocktxn) => {
667 process_getblocktxn_message(getblocktxn, chain_access, config)
668 }
669 NetworkMessage::BlockTxn(blocktxn) => process_blocktxn_message(blocktxn),
670 #[cfg(feature = "utxo-commitments")]
671 NetworkMessage::GetUTXOSet(getutxoset) => process_getutxoset_message(getutxoset),
672 #[cfg(feature = "utxo-commitments")]
673 NetworkMessage::UTXOSet(utxoset) => process_utxoset_message(utxoset),
674 #[cfg(feature = "utxo-commitments")]
675 NetworkMessage::GetFilteredBlock(getfiltered) => {
676 process_getfilteredblock_message(getfiltered)
677 }
678 #[cfg(feature = "utxo-commitments")]
679 NetworkMessage::FilteredBlock(filtered) => process_filteredblock_message(filtered),
680 NetworkMessage::GetBanList(getbanlist) => process_getbanlist_message(getbanlist),
681 NetworkMessage::BanList(banlist) => process_banlist_message(banlist),
682 }
683}
684
685fn process_version_message(
687 version: &VersionMessage,
688 peer_state: &mut PeerState,
689 config: &ProtocolConfig,
690) -> Result<NetworkResponse> {
691 if version.version < 70001 {
693 return Ok(NetworkResponse::Reject("Version too old".into()));
694 }
695
696 if version.user_agent.len() > config.network_limits.max_user_agent_length {
698 return Ok(NetworkResponse::Reject(format!(
699 "User agent too long (max {} bytes)",
700 config.network_limits.max_user_agent_length
701 )));
702 }
703
704 peer_state.version = version.version;
706 peer_state.services = version.services;
707 peer_state.user_agent = version.user_agent.clone();
708 peer_state.start_height = version.start_height;
709
710 #[cfg(all(
712 feature = "bip324",
713 any(target_arch = "x86_64", target_arch = "aarch64")
714 ))]
715 {
716 use crate::service_flags::{has_flag, standard};
717 let peer_supports_v2 = has_flag(version.services, standard::NODE_V2_TRANSPORT);
718 let we_support_v2 = config.service_flags.node_v2_transport;
719
720 if peer_supports_v2 && we_support_v2 {
721 let handshake = crate::v2_transport::V2Handshake::new_responder();
723 peer_state.v2_handshake = Some(std::sync::Arc::new(handshake));
724 }
727 }
728
729 Ok(NetworkResponse::SendMessage(Box::new(
731 NetworkMessage::VerAck,
732 )))
733}
734
735fn process_verack_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
737 peer_state.handshake_complete = true;
738 Ok(NetworkResponse::Ok)
739}
740
741fn process_addr_message(
743 addr: &AddrMessage,
744 peer_state: &mut PeerState,
745 config: &ProtocolConfig,
746) -> Result<NetworkResponse> {
747 if addr.addresses.len() > config.network_limits.max_addr_addresses {
749 return Ok(NetworkResponse::Reject(format!(
750 "Too many addresses (max {})",
751 config.network_limits.max_addr_addresses
752 )));
753 }
754
755 peer_state.known_addresses.extend(addr.addresses.clone());
757
758 Ok(NetworkResponse::Ok)
759}
760
761fn process_addrv2_message(
763 addrv2: &AddrV2Message,
764 peer_state: &mut PeerState,
765 config: &ProtocolConfig,
766) -> Result<NetworkResponse> {
767 if addrv2.addresses.len() > config.network_limits.max_addr_addresses {
769 return Ok(NetworkResponse::Reject(format!(
770 "Too many addresses (max {})",
771 config.network_limits.max_addr_addresses
772 )));
773 }
774
775 for addr_v2 in &addrv2.addresses {
777 if let Some(legacy_addr) = addr_v2.to_legacy() {
778 peer_state.known_addresses.push(legacy_addr);
779 }
780 }
783
784 Ok(NetworkResponse::Ok)
785}
786
787fn process_inv_message(
789 inv: &InvMessage,
790 chain_access: Option<&dyn ChainStateAccess>,
791 config: &ProtocolConfig,
792) -> Result<NetworkResponse> {
793 if inv.inventory.len() > config.network_limits.max_inv_items {
795 return Ok(NetworkResponse::Reject(format!(
796 "Too many inventory items (max {})",
797 config.network_limits.max_inv_items
798 )));
799 }
800
801 if let Some(chain) = chain_access {
803 let mut needed_items = Vec::with_capacity(inv.inventory.len());
804 for item in &inv.inventory {
805 if !chain.has_object(&item.hash) {
806 needed_items.push(item.clone());
807 }
808 }
809
810 if !needed_items.is_empty() {
811 return Ok(NetworkResponse::SendMessage(Box::new(
812 NetworkMessage::GetData(GetDataMessage {
813 inventory: needed_items,
814 }),
815 )));
816 }
817 }
818
819 Ok(NetworkResponse::Ok)
820}
821
822fn process_getdata_message(
824 getdata: &GetDataMessage,
825 chain_access: Option<&dyn ChainStateAccess>,
826 config: &ProtocolConfig,
827) -> Result<NetworkResponse> {
828 if getdata.inventory.len() > config.network_limits.max_inv_items {
830 return Ok(NetworkResponse::Reject(format!(
831 "Too many getdata items (max {})",
832 config.network_limits.max_inv_items
833 )));
834 }
835
836 if let Some(chain) = chain_access {
838 let mut responses = Vec::with_capacity(getdata.inventory.len());
839 for item in &getdata.inventory {
840 if let Some(obj) = chain.get_object(&item.hash) {
841 match item.inv_type {
842 1 => {
843 if let Some(tx) = obj.as_transaction() {
845 responses.push(NetworkMessage::Tx(Arc::clone(tx)));
846 }
847 }
848 2 => {
849 if let Some(block) = obj.as_block() {
851 let Some(witnesses) = chain.get_block_witnesses(&item.hash) else {
852 continue;
855 };
856 responses.push(NetworkMessage::Block(Arc::clone(block), witnesses));
857 }
858 }
859 _ => {
860 }
862 }
863 }
864 }
865
866 if !responses.is_empty() {
867 return Ok(NetworkResponse::SendMessages(responses));
868 }
869 }
870
871 Ok(NetworkResponse::Ok)
872}
873
874fn process_getheaders_message(
876 getheaders: &GetHeadersMessage,
877 chain_access: Option<&dyn ChainStateAccess>,
878 config: &ProtocolConfig,
879) -> Result<NetworkResponse> {
880 if getheaders.block_locator_hashes.len() > config.validation.max_locator_hashes {
882 return Ok(NetworkResponse::Reject(format!(
883 "Too many locator hashes (max {})",
884 config.validation.max_locator_hashes
885 )));
886 }
887
888 if let Some(chain) = chain_access {
890 let headers =
891 chain.get_headers_for_locator(&getheaders.block_locator_hashes, &getheaders.hash_stop);
892 return Ok(NetworkResponse::SendMessage(Box::new(
893 NetworkMessage::Headers(HeadersMessage { headers }),
894 )));
895 }
896
897 Ok(NetworkResponse::Reject("Chain access not available".into()))
898}
899
900fn process_headers_message(
902 headers: &HeadersMessage,
903 config: &ProtocolConfig,
904) -> Result<NetworkResponse> {
905 if headers.headers.len() > config.network_limits.max_headers {
907 return Ok(NetworkResponse::Reject(format!(
908 "Too many headers (max {})",
909 config.network_limits.max_headers
910 )));
911 }
912
913 Ok(NetworkResponse::Ok)
916}
917
918fn process_block_message(
920 engine: &BitcoinProtocolEngine,
921 block: &Block,
922 witnesses: &[Vec<blvm_consensus::segwit::Witness>],
923 utxo_set: Option<&UtxoSet>,
924 height: Option<u64>,
925 config: &ProtocolConfig,
926) -> Result<NetworkResponse> {
927 if block.transactions.len() > config.validation.max_txs_per_block {
929 return Err(crate::error::ProtocolError::MessageTooLarge {
930 size: block.transactions.len(),
931 max: config.validation.max_txs_per_block,
932 });
933 }
934
935 if let (Some(utxos), Some(h)) = (utxo_set, height) {
937 let context = ProtocolValidationContext::new(engine.get_protocol_version(), h)?;
938 let result = engine
940 .validate_block_with_protocol_and_witnesses(block, witnesses, utxos, h, &context)?;
941
942 match result {
943 ValidationResult::Valid => Ok(NetworkResponse::Ok),
944 ValidationResult::Invalid(reason) => {
945 Ok(NetworkResponse::Reject(format!("Invalid block: {reason}")))
946 }
947 }
948 } else {
949 Err(crate::error::ProtocolError::Configuration(
950 "Missing validation context (utxo_set and height required)".into(),
951 ))
952 }
953}
954
955fn process_tx_message(
957 engine: &BitcoinProtocolEngine,
958 tx: &Transaction,
959 height: Option<u64>,
960) -> Result<NetworkResponse> {
961 let context =
963 ProtocolValidationContext::new(engine.get_protocol_version(), height.unwrap_or(0))?;
964 let result = engine.validate_transaction_with_protocol(tx, &context)?;
965
966 match result {
967 ValidationResult::Valid => Ok(NetworkResponse::Ok),
968 ValidationResult::Invalid(reason) => Ok(NetworkResponse::Reject(format!(
969 "Invalid transaction: {reason}"
970 ))),
971 }
972}
973
974fn process_ping_message(
976 ping: &PingMessage,
977 _peer_state: &mut PeerState,
978) -> Result<NetworkResponse> {
979 let pong = NetworkMessage::Pong(PongMessage { nonce: ping.nonce });
980 Ok(NetworkResponse::SendMessage(Box::new(pong)))
981}
982
983fn process_pong_message(pong: &PongMessage, peer_state: &mut PeerState) -> Result<NetworkResponse> {
985 if peer_state.ping_nonce == Some(pong.nonce) {
987 peer_state.ping_nonce = None;
988 peer_state.last_pong = Some(std::time::SystemTime::now());
989 }
990
991 Ok(NetworkResponse::Ok)
992}
993
994fn process_mempool_message(chain_access: Option<&dyn ChainStateAccess>) -> Result<NetworkResponse> {
996 if let Some(chain) = chain_access {
998 let mempool_txs = chain.get_mempool_transactions();
999 let mut responses = Vec::with_capacity(mempool_txs.len());
1000
1001 for tx in mempool_txs {
1002 responses.push(NetworkMessage::Tx(Arc::new(tx)));
1003 }
1004
1005 if !responses.is_empty() {
1006 return Ok(NetworkResponse::SendMessages(responses));
1007 }
1008 }
1009
1010 Ok(NetworkResponse::Ok)
1011}
1012
1013fn process_feefilter_message(
1015 feefilter: &FeeFilterMessage,
1016 peer_state: &mut PeerState,
1017) -> Result<NetworkResponse> {
1018 peer_state.min_fee_rate = Some(feefilter.feerate);
1019 Ok(NetworkResponse::Ok)
1020}
1021
1022fn process_getblocks_message(
1024 getblocks: &GetBlocksMessage,
1025 chain_access: Option<&dyn ChainStateAccess>,
1026 config: &ProtocolConfig,
1027) -> Result<NetworkResponse> {
1028 if getblocks.block_locator_hashes.len() > config.validation.max_locator_hashes {
1030 return Ok(NetworkResponse::Reject(format!(
1031 "Too many locator hashes (max {})",
1032 config.validation.max_locator_hashes
1033 )));
1034 }
1035
1036 if let Some(chain) = chain_access {
1037 use blvm_consensus::block::block_header_hash;
1038
1039 const MAX_GETBLOCKS_INV: usize = 500;
1041 let headers =
1042 chain.get_headers_for_locator(&getblocks.block_locator_hashes, &getblocks.hash_stop);
1043 if headers.is_empty() {
1044 return Ok(NetworkResponse::Ok);
1045 }
1046
1047 let inventory: Vec<InventoryVector> = headers
1048 .into_iter()
1049 .take(MAX_GETBLOCKS_INV)
1050 .map(|header| InventoryVector {
1051 inv_type: 2, hash: block_header_hash(&header),
1053 })
1054 .collect();
1055
1056 return Ok(NetworkResponse::SendMessage(Box::new(NetworkMessage::Inv(
1057 InvMessage { inventory },
1058 ))));
1059 }
1060
1061 Ok(NetworkResponse::Ok)
1062}
1063
1064fn process_getaddr_message(
1066 peer_state: &mut PeerState,
1067 config: &ProtocolConfig,
1068) -> Result<NetworkResponse> {
1069 if !peer_state.known_addresses.is_empty() {
1071 let max_addrs = config
1073 .network_limits
1074 .max_addr_addresses
1075 .min(peer_state.known_addresses.len());
1076 let mut addresses = Vec::with_capacity(max_addrs);
1077 addresses.extend(peer_state.known_addresses.iter().take(max_addrs).cloned());
1078
1079 return Ok(NetworkResponse::SendMessage(Box::new(
1080 NetworkMessage::Addr(AddrMessage { addresses }),
1081 )));
1082 }
1083
1084 Ok(NetworkResponse::Ok)
1085}
1086
1087fn process_notfound_message(
1089 notfound: &NotFoundMessage,
1090 config: &ProtocolConfig,
1091) -> Result<NetworkResponse> {
1092 if notfound.inventory.len() > config.network_limits.max_inv_items {
1094 return Ok(NetworkResponse::Reject(format!(
1095 "Too many notfound items (max {})",
1096 config.network_limits.max_inv_items
1097 )));
1098 }
1099
1100 Ok(NetworkResponse::Ok)
1102}
1103
1104fn process_reject_message(
1106 reject: &RejectMessage,
1107 _config: &ProtocolConfig,
1108) -> Result<NetworkResponse> {
1109 if reject.message.len() > 12 {
1112 return Ok(NetworkResponse::Reject(
1113 "Invalid reject message name".into(),
1114 ));
1115 }
1116
1117 if reject.reason.len() > 111 {
1120 return Ok(NetworkResponse::Reject("Reject reason too long".into()));
1121 }
1122
1123 Ok(NetworkResponse::Ok)
1126}
1127
1128fn process_sendheaders_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
1130 peer_state.prefer_headers = true;
1131 Ok(NetworkResponse::Ok)
1132}
1133
1134fn process_sendcmpct_message(
1136 sendcmpct: &SendCmpctMessage,
1137 peer_state: &mut PeerState,
1138 config: &ProtocolConfig,
1139) -> Result<NetworkResponse> {
1140 let valid_versions = [1, 2];
1142 if !valid_versions.contains(&sendcmpct.version) {
1143 return Ok(NetworkResponse::Reject(
1144 "Invalid compact block version".into(),
1145 ));
1146 }
1147
1148 if !config.compact_blocks.enabled {
1150 return Ok(NetworkResponse::Reject("Compact blocks not enabled".into()));
1151 }
1152
1153 peer_state.compact_block_version = Some(sendcmpct.version as u8);
1155 peer_state.prefer_compact_block = sendcmpct.prefer_cmpct != 0;
1156 Ok(NetworkResponse::Ok)
1157}
1158
1159fn process_cmpctblock_message(_cmpctblock: &CmpctBlockMessage) -> Result<NetworkResponse> {
1161 Ok(NetworkResponse::Reject(
1162 "Compact block reconstruction not implemented".into(),
1163 ))
1164}
1165
1166fn process_getblocktxn_message(
1168 getblocktxn: &GetBlockTxnMessage,
1169 chain_access: Option<&dyn ChainStateAccess>,
1170 config: &ProtocolConfig,
1171) -> Result<NetworkResponse> {
1172 if getblocktxn.indices.len() > config.compact_blocks.max_blocktxn_indices {
1174 return Ok(NetworkResponse::Reject(format!(
1175 "Too many transaction indices (max {})",
1176 config.compact_blocks.max_blocktxn_indices
1177 )));
1178 }
1179
1180 if let Some(chain) = chain_access {
1182 if let Some(obj) = chain.get_object(&getblocktxn.block_hash) {
1183 if let Some(block) = obj.as_block() {
1184 let stored_witnesses = chain.get_block_witnesses(&getblocktxn.block_hash);
1185 let mut transactions = Vec::with_capacity(getblocktxn.indices.len());
1186 let mut req_witnesses = Vec::with_capacity(getblocktxn.indices.len());
1187
1188 for &index in &getblocktxn.indices {
1189 let idx = index as usize;
1190 if idx >= block.transactions.len() {
1191 return Ok(NetworkResponse::Reject(
1192 "getblocktxn index past end of block".into(),
1193 ));
1194 }
1195 transactions.push(block.transactions[idx].clone());
1196 let tx_witness = stored_witnesses
1197 .as_ref()
1198 .and_then(|w| w.get(idx).cloned())
1199 .unwrap_or_else(|| {
1200 block.transactions[idx]
1201 .inputs
1202 .iter()
1203 .map(|_| Vec::new())
1204 .collect()
1205 });
1206 req_witnesses.push(tx_witness);
1207 }
1208
1209 if !transactions.is_empty() {
1210 return Ok(NetworkResponse::SendMessage(Box::new(
1211 NetworkMessage::BlockTxn(BlockTxnMessage {
1212 block_hash: getblocktxn.block_hash,
1213 transactions,
1214 witnesses: Some(req_witnesses),
1215 }),
1216 )));
1217 }
1218 }
1219 }
1220 }
1221
1222 Ok(NetworkResponse::Ok)
1223}
1224
1225fn process_blocktxn_message(_blocktxn: &BlockTxnMessage) -> Result<NetworkResponse> {
1227 Ok(NetworkResponse::Reject(
1228 "Compact block reconstruction not implemented".into(),
1229 ))
1230}
1231
1232#[cfg(feature = "utxo-commitments")]
1233fn process_getutxoset_message(_getutxoset: &commons::GetUTXOSetMessage) -> Result<NetworkResponse> {
1235 Ok(NetworkResponse::Reject(
1236 "UTXO set relay not implemented".into(),
1237 ))
1238}
1239
1240#[cfg(feature = "utxo-commitments")]
1241fn process_utxoset_message(_utxoset: &commons::UTXOSetMessage) -> Result<NetworkResponse> {
1243 Ok(NetworkResponse::Reject(
1244 "UTXO set relay not implemented".into(),
1245 ))
1246}
1247
1248#[cfg(feature = "utxo-commitments")]
1249fn process_getfilteredblock_message(
1251 _getfiltered: &commons::GetFilteredBlockMessage,
1252) -> Result<NetworkResponse> {
1253 Ok(NetworkResponse::Reject(
1254 "Filtered block relay not implemented".into(),
1255 ))
1256}
1257
1258#[cfg(feature = "utxo-commitments")]
1259fn process_filteredblock_message(
1261 _filtered: &commons::FilteredBlockMessage,
1262) -> Result<NetworkResponse> {
1263 Ok(NetworkResponse::Reject(
1264 "Filtered block relay not implemented".into(),
1265 ))
1266}
1267
1268fn process_getbanlist_message(_getbanlist: &commons::GetBanListMessage) -> Result<NetworkResponse> {
1270 Ok(NetworkResponse::Reject(
1271 "Ban list relay not implemented".into(),
1272 ))
1273}
1274
1275fn process_banlist_message(_banlist: &commons::BanListMessage) -> Result<NetworkResponse> {
1277 Ok(NetworkResponse::Reject(
1278 "Ban list relay not implemented".into(),
1279 ))
1280}
1281
1282#[cfg(test)]
1283fn assert_getblocktxn_past_end_rejects() {
1284 struct TwoTx;
1285 impl ChainStateAccess for TwoTx {
1286 fn has_object(&self, _hash: &Hash) -> bool {
1287 true
1288 }
1289 fn get_object(&self, hash: &Hash) -> Option<ChainObject> {
1290 let tx = Transaction {
1291 version: 1,
1292 inputs: vec![].into(),
1293 outputs: vec![].into(),
1294 lock_time: 0,
1295 };
1296 let block = Block {
1297 header: BlockHeader {
1298 version: 1,
1299 prev_block_hash: [0; 32],
1300 merkle_root: [0; 32],
1301 timestamp: 1,
1302 bits: 1,
1303 nonce: 1,
1304 },
1305 transactions: vec![tx.clone(), tx].into_boxed_slice(),
1306 };
1307 if hash == &[9u8; 32] {
1308 Some(ChainObject::Block(Arc::new(block)))
1309 } else {
1310 None
1311 }
1312 }
1313 fn get_headers_for_locator(&self, _locator: &[Hash], _stop: &Hash) -> Vec<BlockHeader> {
1314 Vec::new()
1315 }
1316 fn get_mempool_transactions(&self) -> Vec<Transaction> {
1317 Vec::new()
1318 }
1319 }
1320 let msg = GetBlockTxnMessage {
1321 block_hash: [9u8; 32],
1322 indices: vec![5],
1323 };
1324 let response =
1325 process_getblocktxn_message(&msg, Some(&TwoTx), &ProtocolConfig::default()).unwrap();
1326 assert!(
1327 matches!(response, NetworkResponse::Reject(_)),
1328 "expected reject, got {response:?}"
1329 );
1330}
1331
1332#[cfg(test)]
1333#[test]
1334fn getblocktxn_index_past_block_returns_reject_and_no_blocktxn() {
1335 assert_getblocktxn_past_end_rejects();
1336}