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(feature = "bip324")]
24pub mod v2_transport {
25 pub use crate::v2_transport::*;
26}
27
28#[cfg(test)]
29mod bip155_tests;
30
31#[derive(Debug, Clone, PartialEq)]
35pub enum NetworkMessage {
36 Version(VersionMessage),
37 VerAck,
38 Addr(AddrMessage),
39 AddrV2(AddrV2Message), Inv(InvMessage),
41 GetData(GetDataMessage),
42 GetHeaders(GetHeadersMessage),
43 Headers(HeadersMessage),
44 Block(Arc<Block>),
45 Tx(Arc<Transaction>),
46 Ping(PingMessage),
47 Pong(PongMessage),
48 MemPool,
49 FeeFilter(FeeFilterMessage),
50 GetBlocks(GetBlocksMessage),
52 GetAddr,
53 NotFound(NotFoundMessage),
54 Reject(RejectMessage),
55 SendHeaders,
56 SendCmpct(SendCmpctMessage),
58 CmpctBlock(CmpctBlockMessage),
59 GetBlockTxn(GetBlockTxnMessage),
60 BlockTxn(BlockTxnMessage),
61 #[cfg(feature = "utxo-commitments")]
63 GetUTXOSet(commons::GetUTXOSetMessage),
64 #[cfg(feature = "utxo-commitments")]
65 UTXOSet(commons::UTXOSetMessage),
66 #[cfg(feature = "utxo-commitments")]
67 GetFilteredBlock(commons::GetFilteredBlockMessage),
68 #[cfg(feature = "utxo-commitments")]
69 FilteredBlock(commons::FilteredBlockMessage),
70 GetBanList(commons::GetBanListMessage),
71 BanList(commons::BanListMessage),
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
76pub struct VersionMessage {
77 pub version: u32,
78 pub services: u64,
79 pub timestamp: i64,
80 pub addr_recv: NetworkAddress,
81 pub addr_from: NetworkAddress,
82 pub nonce: u64,
83 pub user_agent: String,
84 pub start_height: i32,
85 pub relay: bool,
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
94pub struct BlockMessage {
95 pub block: Block,
96 #[serde(skip_serializing_if = "Vec::is_empty", default)]
98 pub witnesses: Vec<Vec<blvm_consensus::segwit::Witness>>,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
103pub struct TxMessage {
104 pub transaction: Transaction,
105}
106
107#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
112pub struct CompactBlockMessage {
113 pub compact_block: crate::bip152::CompactBlock,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
118pub struct AddrMessage {
119 pub addresses: Vec<NetworkAddress>,
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
124pub struct InvMessage {
125 pub inventory: Vec<InventoryVector>,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
130pub struct GetDataMessage {
131 pub inventory: Vec<InventoryVector>,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
136pub struct GetHeadersMessage {
137 pub version: u32,
138 pub block_locator_hashes: Vec<Hash>,
139 pub hash_stop: Hash,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
144pub struct HeadersMessage {
145 pub headers: Vec<BlockHeader>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
150pub struct PingMessage {
151 pub nonce: u64,
152}
153
154#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
156pub struct PongMessage {
157 pub nonce: u64,
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
162pub struct FeeFilterMessage {
163 pub feerate: u64,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
168pub struct GetBlocksMessage {
169 pub version: u32,
170 pub block_locator_hashes: Vec<Hash>,
171 pub hash_stop: Hash,
172}
173
174#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
176pub struct NotFoundMessage {
177 pub inventory: Vec<InventoryVector>,
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
182pub struct RejectMessage {
183 pub message: String, pub code: u8, pub reason: String, pub extra_data: Option<Hash>, }
188
189#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
191pub struct SendCmpctMessage {
192 pub version: u64,
194 pub prefer_cmpct: u8,
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
200pub struct CmpctBlockMessage {
201 pub header: BlockHeader,
203 pub nonce: u64,
205 pub short_ids: Vec<ShortTxId>,
207 pub prefilled_txs: Vec<PrefilledTransaction>,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
213pub struct PrefilledTransaction {
214 pub index: u16,
216 pub tx: Transaction,
218 pub witness: Option<Vec<blvm_consensus::segwit::Witness>>,
220}
221
222#[derive(Debug, Clone, PartialEq, Eq, Error)]
224pub enum CompactBlockWireConvertError {
225 #[error("prefilled transaction index {0} does not fit in BIP152 u16 index")]
226 PrefilledIndexTooLarge(usize),
227 #[error("duplicate prefilled transaction index {0}")]
228 DuplicatePrefilledIndex(usize),
229}
230
231impl TryFrom<crate::bip152::CompactBlock> for CmpctBlockMessage {
232 type Error = CompactBlockWireConvertError;
233
234 fn try_from(mut value: crate::bip152::CompactBlock) -> std::result::Result<Self, Self::Error> {
239 value.prefilled_txs.sort_by_key(|(i, _)| *i);
240 let mut prefilled_txs = Vec::with_capacity(value.prefilled_txs.len());
241 let mut prev_idx: Option<usize> = None;
242 for (idx, tx) in value.prefilled_txs {
243 if prev_idx == Some(idx) {
244 return Err(CompactBlockWireConvertError::DuplicatePrefilledIndex(idx));
245 }
246 prev_idx = Some(idx);
247 let index = u16::try_from(idx)
248 .map_err(|_| CompactBlockWireConvertError::PrefilledIndexTooLarge(idx))?;
249 prefilled_txs.push(PrefilledTransaction {
250 index,
251 tx,
252 witness: None,
253 });
254 }
255 Ok(CmpctBlockMessage {
256 header: value.header,
257 nonce: value.nonce,
258 short_ids: value.short_ids,
259 prefilled_txs,
260 })
261 }
262}
263
264impl From<&CmpctBlockMessage> for crate::bip152::CompactBlock {
265 fn from(msg: &CmpctBlockMessage) -> Self {
266 Self {
267 header: msg.header.clone(),
268 nonce: msg.nonce,
269 short_ids: msg.short_ids.clone(),
270 prefilled_txs: msg
271 .prefilled_txs
272 .iter()
273 .map(|p| (usize::from(p.index), p.tx.clone()))
274 .collect(),
275 }
276 }
277}
278
279impl From<CmpctBlockMessage> for crate::bip152::CompactBlock {
280 fn from(msg: CmpctBlockMessage) -> Self {
281 Self {
282 header: msg.header,
283 nonce: msg.nonce,
284 short_ids: msg.short_ids,
285 prefilled_txs: msg
286 .prefilled_txs
287 .into_iter()
288 .map(|p| (usize::from(p.index), p.tx))
289 .collect(),
290 }
291 }
292}
293
294#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
296pub struct GetBlockTxnMessage {
297 pub block_hash: Hash,
299 pub indices: Vec<u16>,
301}
302
303#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
305pub struct BlockTxnMessage {
306 pub block_hash: Hash,
308 pub transactions: Vec<Transaction>,
310 pub witnesses: Option<Vec<Vec<blvm_consensus::segwit::Witness>>>,
312}
313
314#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
316pub struct NetworkAddress {
317 pub services: u64,
318 pub ip: [u8; 16], pub port: u16,
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
324#[repr(u8)]
325pub enum AddressType {
326 IPv4 = 1,
327 IPv6 = 2,
328 TorV2 = 3,
329 TorV3 = 4,
330 I2P = 5,
331 CJDNS = 6,
332}
333
334impl AddressType {
335 pub fn address_length(&self) -> usize {
337 match self {
338 AddressType::IPv4 => 4,
339 AddressType::IPv6 => 16,
340 AddressType::TorV2 => 10,
341 AddressType::TorV3 => 32,
342 AddressType::I2P => 32,
343 AddressType::CJDNS => 16,
344 }
345 }
346
347 pub fn from_u8(value: u8) -> Option<Self> {
349 match value {
350 1 => Some(AddressType::IPv4),
351 2 => Some(AddressType::IPv6),
352 3 => Some(AddressType::TorV2),
353 4 => Some(AddressType::TorV3),
354 5 => Some(AddressType::I2P),
355 6 => Some(AddressType::CJDNS),
356 _ => None,
357 }
358 }
359}
360
361#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
363pub struct AddrV2Message {
364 pub addresses: Vec<NetworkAddressV2>,
365}
366
367#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
369pub struct NetworkAddressV2 {
370 pub time: u32,
371 pub services: u64,
372 pub address_type: AddressType,
373 pub address: Vec<u8>, pub port: u16,
375}
376
377impl NetworkAddressV2 {
378 pub fn new(
380 time: u32,
381 services: u64,
382 address_type: AddressType,
383 address: Vec<u8>,
384 port: u16,
385 ) -> Result<Self> {
386 let expected_len = address_type.address_length();
387 if address.len() != expected_len {
388 return Err(ProtocolError::Consensus(ConsensusError::Serialization(
389 std::borrow::Cow::Owned(format!(
390 "Invalid address length for type {:?}: expected {}, got {}",
391 address_type,
392 expected_len,
393 address.len()
394 )),
395 )));
396 }
397 Ok(Self {
398 time,
399 services,
400 address_type,
401 address,
402 port,
403 })
404 }
405
406 pub fn to_legacy(&self) -> Option<NetworkAddress> {
408 match self.address_type {
409 AddressType::IPv4 => {
410 if self.address.len() == 4 {
411 let mut ipv6 = [0u8; 16];
413 ipv6[10] = 0xff;
414 ipv6[11] = 0xff;
415 ipv6[12..16].copy_from_slice(&self.address);
416 Some(NetworkAddress {
417 services: self.services,
418 ip: ipv6,
419 port: self.port,
420 })
421 } else {
422 None
423 }
424 }
425 AddressType::IPv6 => {
426 if self.address.len() == 16 {
427 let mut ipv6 = [0u8; 16];
428 ipv6.copy_from_slice(&self.address);
429 Some(NetworkAddress {
430 services: self.services,
431 ip: ipv6,
432 port: self.port,
433 })
434 } else {
435 None
436 }
437 }
438 _ => None, }
440 }
441}
442
443#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
445pub struct InventoryVector {
446 pub inv_type: u32,
447 pub hash: Hash,
448}
449
450#[derive(Debug, Clone)]
452pub enum NetworkResponse {
453 Ok,
454 SendMessage(Box<NetworkMessage>),
455 SendMessages(Vec<NetworkMessage>),
456 Reject(String),
457}
458
459#[derive(Clone)]
461pub struct PeerState {
462 pub version: u32,
463 pub services: u64,
464 pub user_agent: String,
465 pub start_height: i32,
466 pub handshake_complete: bool,
467 pub known_addresses: Vec<NetworkAddress>,
468 pub ping_nonce: Option<u64>,
469 pub last_pong: Option<std::time::SystemTime>,
470 pub min_fee_rate: Option<u64>,
471 #[cfg(feature = "bip324")]
474 pub v2_transport: Option<std::sync::Arc<crate::v2_transport::V2Transport>>,
475 #[cfg(feature = "bip324")]
477 pub v2_handshake: Option<std::sync::Arc<crate::v2_transport::V2Handshake>>,
478}
479
480impl PeerState {
481 pub fn new() -> Self {
482 Self {
483 version: 0,
484 services: 0,
485 user_agent: String::new(),
486 start_height: 0,
487 handshake_complete: false,
488 known_addresses: Vec::new(),
489 ping_nonce: None,
490 last_pong: None,
491 min_fee_rate: None,
492 #[cfg(feature = "bip324")]
493 v2_transport: None,
494 #[cfg(feature = "bip324")]
495 v2_handshake: None,
496 }
497 }
498
499 #[cfg(feature = "bip324")]
501 pub fn supports_v2_transport(&self) -> bool {
502 use crate::service_flags::{has_flag, standard};
503 has_flag(self.services, standard::NODE_V2_TRANSPORT)
504 }
505
506 #[cfg(feature = "bip324")]
508 pub fn is_v2_transport_active(&self) -> bool {
509 self.v2_transport.is_some()
510 }
511}
512
513impl Default for PeerState {
514 fn default() -> Self {
515 Self::new()
516 }
517}
518
519#[derive(Debug, Clone)]
521pub enum ChainObject {
522 Block(Arc<Block>),
523 Transaction(Arc<Transaction>),
524}
525
526impl ChainObject {
527 pub fn as_block(&self) -> Option<&Arc<Block>> {
528 match self {
529 ChainObject::Block(block) => Some(block),
530 _ => None,
531 }
532 }
533
534 pub fn as_transaction(&self) -> Option<&Arc<Transaction>> {
535 match self {
536 ChainObject::Transaction(tx) => Some(tx),
537 _ => None,
538 }
539 }
540}
541
542pub trait ChainStateAccess {
548 fn has_object(&self, hash: &Hash) -> bool;
550
551 fn get_object(&self, hash: &Hash) -> Option<ChainObject>;
553
554 fn get_headers_for_locator(&self, locator: &[Hash], stop: &Hash) -> Vec<BlockHeader>;
557
558 fn get_mempool_transactions(&self) -> Vec<Transaction>;
560}
561
562pub fn process_network_message(
580 engine: &BitcoinProtocolEngine,
581 message: &NetworkMessage,
582 peer_state: &mut PeerState,
583 chain_access: Option<&dyn ChainStateAccess>,
584 utxo_set: Option<&UtxoSet>,
585 height: Option<u64>,
586) -> Result<NetworkResponse> {
587 let config = engine.get_config();
588 match message {
589 NetworkMessage::Version(version) => process_version_message(version, peer_state, config),
590 NetworkMessage::VerAck => process_verack_message(peer_state),
591 NetworkMessage::Addr(addr) => process_addr_message(addr, peer_state, config),
592 NetworkMessage::AddrV2(addrv2) => process_addrv2_message(addrv2, peer_state, config),
593 NetworkMessage::Inv(inv) => process_inv_message(inv, chain_access, config),
594 NetworkMessage::GetData(getdata) => process_getdata_message(getdata, chain_access, config),
595 NetworkMessage::GetHeaders(getheaders) => {
596 process_getheaders_message(getheaders, chain_access, config)
597 }
598 NetworkMessage::Headers(headers) => process_headers_message(headers, config),
599 NetworkMessage::Block(block) => {
600 process_block_message(engine, block, utxo_set, height, config)
601 }
602 NetworkMessage::Tx(tx) => process_tx_message(engine, tx, height),
603 NetworkMessage::Ping(ping) => process_ping_message(ping, peer_state),
604 NetworkMessage::Pong(pong) => process_pong_message(pong, peer_state),
605 NetworkMessage::MemPool => process_mempool_message(chain_access),
606 NetworkMessage::FeeFilter(feefilter) => process_feefilter_message(feefilter, peer_state),
607 NetworkMessage::GetBlocks(getblocks) => {
608 process_getblocks_message(getblocks, chain_access, config)
609 }
610 NetworkMessage::GetAddr => process_getaddr_message(peer_state, config),
611 NetworkMessage::NotFound(notfound) => process_notfound_message(notfound, config),
612 NetworkMessage::Reject(reject) => process_reject_message(reject, config),
613 NetworkMessage::SendHeaders => process_sendheaders_message(peer_state),
614 NetworkMessage::SendCmpct(sendcmpct) => {
615 process_sendcmpct_message(sendcmpct, peer_state, config)
616 }
617 NetworkMessage::CmpctBlock(cmpctblock) => process_cmpctblock_message(cmpctblock),
618 NetworkMessage::GetBlockTxn(getblocktxn) => {
619 process_getblocktxn_message(getblocktxn, chain_access, config)
620 }
621 NetworkMessage::BlockTxn(blocktxn) => process_blocktxn_message(blocktxn),
622 #[cfg(feature = "utxo-commitments")]
623 NetworkMessage::GetUTXOSet(getutxoset) => process_getutxoset_message(getutxoset),
624 #[cfg(feature = "utxo-commitments")]
625 NetworkMessage::UTXOSet(utxoset) => process_utxoset_message(utxoset),
626 #[cfg(feature = "utxo-commitments")]
627 NetworkMessage::GetFilteredBlock(getfiltered) => {
628 process_getfilteredblock_message(getfiltered)
629 }
630 #[cfg(feature = "utxo-commitments")]
631 NetworkMessage::FilteredBlock(filtered) => process_filteredblock_message(filtered),
632 NetworkMessage::GetBanList(getbanlist) => process_getbanlist_message(getbanlist),
633 NetworkMessage::BanList(banlist) => process_banlist_message(banlist),
634 }
635}
636
637fn process_version_message(
639 version: &VersionMessage,
640 peer_state: &mut PeerState,
641 config: &ProtocolConfig,
642) -> Result<NetworkResponse> {
643 if version.version < 70001 {
645 return Ok(NetworkResponse::Reject("Version too old".into()));
646 }
647
648 if version.user_agent.len() > config.network_limits.max_user_agent_length {
650 return Ok(NetworkResponse::Reject(format!(
651 "User agent too long (max {} bytes)",
652 config.network_limits.max_user_agent_length
653 )));
654 }
655
656 peer_state.version = version.version;
658 peer_state.services = version.services;
659 peer_state.user_agent = version.user_agent.clone();
660 peer_state.start_height = version.start_height;
661
662 #[cfg(feature = "bip324")]
664 {
665 use crate::service_flags::{has_flag, standard};
666 let peer_supports_v2 = has_flag(version.services, standard::NODE_V2_TRANSPORT);
667 let we_support_v2 = config.service_flags.node_v2_transport;
668
669 if peer_supports_v2 && we_support_v2 {
670 let handshake = crate::v2_transport::V2Handshake::new_responder();
672 peer_state.v2_handshake = Some(std::sync::Arc::new(handshake));
673 }
676 }
677
678 Ok(NetworkResponse::SendMessage(Box::new(
680 NetworkMessage::VerAck,
681 )))
682}
683
684fn process_verack_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
686 peer_state.handshake_complete = true;
687 Ok(NetworkResponse::Ok)
688}
689
690fn process_addr_message(
692 addr: &AddrMessage,
693 peer_state: &mut PeerState,
694 config: &ProtocolConfig,
695) -> Result<NetworkResponse> {
696 if addr.addresses.len() > config.network_limits.max_addr_addresses {
698 return Ok(NetworkResponse::Reject(format!(
699 "Too many addresses (max {})",
700 config.network_limits.max_addr_addresses
701 )));
702 }
703
704 peer_state.known_addresses.extend(addr.addresses.clone());
706
707 Ok(NetworkResponse::Ok)
708}
709
710fn process_addrv2_message(
712 addrv2: &AddrV2Message,
713 peer_state: &mut PeerState,
714 config: &ProtocolConfig,
715) -> Result<NetworkResponse> {
716 if addrv2.addresses.len() > config.network_limits.max_addr_addresses {
718 return Ok(NetworkResponse::Reject(format!(
719 "Too many addresses (max {})",
720 config.network_limits.max_addr_addresses
721 )));
722 }
723
724 for addr_v2 in &addrv2.addresses {
726 if let Some(legacy_addr) = addr_v2.to_legacy() {
727 peer_state.known_addresses.push(legacy_addr);
728 }
729 }
732
733 Ok(NetworkResponse::Ok)
734}
735
736fn process_inv_message(
738 inv: &InvMessage,
739 chain_access: Option<&dyn ChainStateAccess>,
740 config: &ProtocolConfig,
741) -> Result<NetworkResponse> {
742 if inv.inventory.len() > config.network_limits.max_inv_items {
744 return Ok(NetworkResponse::Reject(format!(
745 "Too many inventory items (max {})",
746 config.network_limits.max_inv_items
747 )));
748 }
749
750 if let Some(chain) = chain_access {
752 let mut needed_items = Vec::with_capacity(inv.inventory.len());
753 for item in &inv.inventory {
754 if !chain.has_object(&item.hash) {
755 needed_items.push(item.clone());
756 }
757 }
758
759 if !needed_items.is_empty() {
760 return Ok(NetworkResponse::SendMessage(Box::new(
761 NetworkMessage::GetData(GetDataMessage {
762 inventory: needed_items,
763 }),
764 )));
765 }
766 }
767
768 Ok(NetworkResponse::Ok)
769}
770
771fn process_getdata_message(
773 getdata: &GetDataMessage,
774 chain_access: Option<&dyn ChainStateAccess>,
775 config: &ProtocolConfig,
776) -> Result<NetworkResponse> {
777 if getdata.inventory.len() > config.network_limits.max_inv_items {
779 return Ok(NetworkResponse::Reject(format!(
780 "Too many getdata items (max {})",
781 config.network_limits.max_inv_items
782 )));
783 }
784
785 if let Some(chain) = chain_access {
787 let mut responses = Vec::with_capacity(getdata.inventory.len());
788 for item in &getdata.inventory {
789 if let Some(obj) = chain.get_object(&item.hash) {
790 match item.inv_type {
791 1 => {
792 if let Some(tx) = obj.as_transaction() {
794 responses.push(NetworkMessage::Tx(Arc::clone(tx)));
795 }
796 }
797 2 => {
798 if let Some(block) = obj.as_block() {
800 responses.push(NetworkMessage::Block(Arc::clone(block)));
801 }
802 }
803 _ => {
804 }
806 }
807 }
808 }
809
810 if !responses.is_empty() {
811 return Ok(NetworkResponse::SendMessages(responses));
812 }
813 }
814
815 Ok(NetworkResponse::Ok)
816}
817
818fn process_getheaders_message(
820 getheaders: &GetHeadersMessage,
821 chain_access: Option<&dyn ChainStateAccess>,
822 config: &ProtocolConfig,
823) -> Result<NetworkResponse> {
824 if getheaders.block_locator_hashes.len() > config.validation.max_locator_hashes {
826 return Ok(NetworkResponse::Reject(format!(
827 "Too many locator hashes (max {})",
828 config.validation.max_locator_hashes
829 )));
830 }
831
832 if let Some(chain) = chain_access {
834 let headers =
835 chain.get_headers_for_locator(&getheaders.block_locator_hashes, &getheaders.hash_stop);
836 return Ok(NetworkResponse::SendMessage(Box::new(
837 NetworkMessage::Headers(HeadersMessage { headers }),
838 )));
839 }
840
841 Ok(NetworkResponse::Reject("Chain access not available".into()))
842}
843
844fn process_headers_message(
846 headers: &HeadersMessage,
847 config: &ProtocolConfig,
848) -> Result<NetworkResponse> {
849 if headers.headers.len() > config.network_limits.max_headers {
851 return Ok(NetworkResponse::Reject(format!(
852 "Too many headers (max {})",
853 config.network_limits.max_headers
854 )));
855 }
856
857 Ok(NetworkResponse::Ok)
860}
861
862fn process_block_message(
864 engine: &BitcoinProtocolEngine,
865 block: &Block,
866 utxo_set: Option<&UtxoSet>,
867 height: Option<u64>,
868 config: &ProtocolConfig,
869) -> Result<NetworkResponse> {
870 if block.transactions.len() > config.validation.max_txs_per_block {
872 return Err(crate::error::ProtocolError::MessageTooLarge {
873 size: block.transactions.len(),
874 max: config.validation.max_txs_per_block,
875 });
876 }
877
878 if let (Some(utxos), Some(h)) = (utxo_set, height) {
880 let context = ProtocolValidationContext::new(engine.get_protocol_version(), h)?;
881 let result = engine.validate_block_with_protocol(block, utxos, h, &context)?;
882
883 match result {
884 ValidationResult::Valid => Ok(NetworkResponse::Ok),
885 ValidationResult::Invalid(reason) => {
886 Ok(NetworkResponse::Reject(format!("Invalid block: {reason}")))
887 }
888 }
889 } else {
890 Err(crate::error::ProtocolError::Configuration(
891 "Missing validation context (utxo_set and height required)".into(),
892 ))
893 }
894}
895
896fn process_tx_message(
898 engine: &BitcoinProtocolEngine,
899 tx: &Transaction,
900 height: Option<u64>,
901) -> Result<NetworkResponse> {
902 let context =
904 ProtocolValidationContext::new(engine.get_protocol_version(), height.unwrap_or(0))?;
905 let result = engine.validate_transaction_with_protocol(tx, &context)?;
906
907 match result {
908 ValidationResult::Valid => Ok(NetworkResponse::Ok),
909 ValidationResult::Invalid(reason) => Ok(NetworkResponse::Reject(format!(
910 "Invalid transaction: {reason}"
911 ))),
912 }
913}
914
915fn process_ping_message(
917 ping: &PingMessage,
918 _peer_state: &mut PeerState,
919) -> Result<NetworkResponse> {
920 let pong = NetworkMessage::Pong(PongMessage { nonce: ping.nonce });
921 Ok(NetworkResponse::SendMessage(Box::new(pong)))
922}
923
924fn process_pong_message(pong: &PongMessage, peer_state: &mut PeerState) -> Result<NetworkResponse> {
926 if peer_state.ping_nonce == Some(pong.nonce) {
928 peer_state.ping_nonce = None;
929 peer_state.last_pong = Some(std::time::SystemTime::now());
930 }
931
932 Ok(NetworkResponse::Ok)
933}
934
935fn process_mempool_message(chain_access: Option<&dyn ChainStateAccess>) -> Result<NetworkResponse> {
937 if let Some(chain) = chain_access {
939 let mempool_txs = chain.get_mempool_transactions();
940 let mut responses = Vec::with_capacity(mempool_txs.len());
941
942 for tx in mempool_txs {
943 responses.push(NetworkMessage::Tx(Arc::new(tx)));
944 }
945
946 if !responses.is_empty() {
947 return Ok(NetworkResponse::SendMessages(responses));
948 }
949 }
950
951 Ok(NetworkResponse::Ok)
952}
953
954fn process_feefilter_message(
956 feefilter: &FeeFilterMessage,
957 peer_state: &mut PeerState,
958) -> Result<NetworkResponse> {
959 peer_state.min_fee_rate = Some(feefilter.feerate);
960 Ok(NetworkResponse::Ok)
961}
962
963fn process_getblocks_message(
965 getblocks: &GetBlocksMessage,
966 chain_access: Option<&dyn ChainStateAccess>,
967 config: &ProtocolConfig,
968) -> Result<NetworkResponse> {
969 if getblocks.block_locator_hashes.len() > config.validation.max_locator_hashes {
971 return Ok(NetworkResponse::Reject(format!(
972 "Too many locator hashes (max {})",
973 config.validation.max_locator_hashes
974 )));
975 }
976
977 if let Some(chain) = chain_access {
981 let mut inventory = Vec::with_capacity(getblocks.block_locator_hashes.len());
983 for hash in &getblocks.block_locator_hashes {
984 if chain.has_object(hash) {
985 inventory.push(InventoryVector {
986 inv_type: 2, hash: *hash,
988 });
989 }
990 }
991
992 if !inventory.is_empty() {
993 return Ok(NetworkResponse::SendMessage(Box::new(NetworkMessage::Inv(
994 InvMessage { inventory },
995 ))));
996 }
997 }
998
999 Ok(NetworkResponse::Ok)
1000}
1001
1002fn process_getaddr_message(
1004 peer_state: &mut PeerState,
1005 config: &ProtocolConfig,
1006) -> Result<NetworkResponse> {
1007 if !peer_state.known_addresses.is_empty() {
1009 let max_addrs = config
1011 .network_limits
1012 .max_addr_addresses
1013 .min(peer_state.known_addresses.len());
1014 let mut addresses = Vec::with_capacity(max_addrs);
1015 addresses.extend(peer_state.known_addresses.iter().take(max_addrs).cloned());
1016
1017 return Ok(NetworkResponse::SendMessage(Box::new(
1018 NetworkMessage::Addr(AddrMessage { addresses }),
1019 )));
1020 }
1021
1022 Ok(NetworkResponse::Ok)
1023}
1024
1025fn process_notfound_message(
1027 notfound: &NotFoundMessage,
1028 config: &ProtocolConfig,
1029) -> Result<NetworkResponse> {
1030 if notfound.inventory.len() > config.network_limits.max_inv_items {
1032 return Ok(NetworkResponse::Reject(format!(
1033 "Too many notfound items (max {})",
1034 config.network_limits.max_inv_items
1035 )));
1036 }
1037
1038 Ok(NetworkResponse::Ok)
1040}
1041
1042fn process_reject_message(
1044 reject: &RejectMessage,
1045 _config: &ProtocolConfig,
1046) -> Result<NetworkResponse> {
1047 if reject.message.len() > 12 {
1050 return Ok(NetworkResponse::Reject(
1051 "Invalid reject message name".into(),
1052 ));
1053 }
1054
1055 if reject.reason.len() > 111 {
1058 return Ok(NetworkResponse::Reject("Reject reason too long".into()));
1059 }
1060
1061 Ok(NetworkResponse::Ok)
1064}
1065
1066fn process_sendheaders_message(_peer_state: &mut PeerState) -> Result<NetworkResponse> {
1068 Ok(NetworkResponse::Ok)
1072}
1073
1074fn process_sendcmpct_message(
1076 sendcmpct: &SendCmpctMessage,
1077 _peer_state: &mut PeerState,
1078 config: &ProtocolConfig,
1079) -> Result<NetworkResponse> {
1080 let valid_versions = [1, 2];
1082 if !valid_versions.contains(&sendcmpct.version) {
1083 return Ok(NetworkResponse::Reject(
1084 "Invalid compact block version".into(),
1085 ));
1086 }
1087
1088 if !config.compact_blocks.enabled {
1090 return Ok(NetworkResponse::Reject("Compact blocks not enabled".into()));
1091 }
1092
1093 let _ = (sendcmpct.version, sendcmpct.prefer_cmpct);
1096 Ok(NetworkResponse::Ok)
1097}
1098
1099fn process_cmpctblock_message(_cmpctblock: &CmpctBlockMessage) -> Result<NetworkResponse> {
1101 Ok(NetworkResponse::Ok)
1104}
1105
1106fn process_getblocktxn_message(
1108 getblocktxn: &GetBlockTxnMessage,
1109 chain_access: Option<&dyn ChainStateAccess>,
1110 config: &ProtocolConfig,
1111) -> Result<NetworkResponse> {
1112 if getblocktxn.indices.len() > config.compact_blocks.max_blocktxn_indices {
1114 return Ok(NetworkResponse::Reject(format!(
1115 "Too many transaction indices (max {})",
1116 config.compact_blocks.max_blocktxn_indices
1117 )));
1118 }
1119
1120 if let Some(chain) = chain_access {
1122 let mut transactions = Vec::new();
1123 for &index in &getblocktxn.indices {
1124 if let Some(obj) = chain.get_object(&getblocktxn.block_hash) {
1127 if let Some(block) = obj.as_block() {
1128 if (index as usize) < block.transactions.len() {
1129 transactions.push(block.transactions[index as usize].clone());
1130 }
1131 }
1132 }
1133 }
1134
1135 if !transactions.is_empty() {
1136 return Ok(NetworkResponse::SendMessage(Box::new(
1137 NetworkMessage::BlockTxn(BlockTxnMessage {
1138 block_hash: getblocktxn.block_hash,
1139 transactions,
1140 witnesses: None, }),
1142 )));
1143 }
1144 }
1145
1146 Ok(NetworkResponse::Ok)
1147}
1148
1149fn process_blocktxn_message(_blocktxn: &BlockTxnMessage) -> Result<NetworkResponse> {
1151 Ok(NetworkResponse::Ok)
1154}
1155
1156#[cfg(feature = "utxo-commitments")]
1157fn process_getutxoset_message(_getutxoset: &commons::GetUTXOSetMessage) -> Result<NetworkResponse> {
1159 Ok(NetworkResponse::Ok)
1162}
1163
1164#[cfg(feature = "utxo-commitments")]
1165fn process_utxoset_message(_utxoset: &commons::UTXOSetMessage) -> Result<NetworkResponse> {
1167 Ok(NetworkResponse::Ok)
1170}
1171
1172#[cfg(feature = "utxo-commitments")]
1173fn process_getfilteredblock_message(
1175 _getfiltered: &commons::GetFilteredBlockMessage,
1176) -> Result<NetworkResponse> {
1177 Ok(NetworkResponse::Ok)
1180}
1181
1182#[cfg(feature = "utxo-commitments")]
1183fn process_filteredblock_message(
1185 _filtered: &commons::FilteredBlockMessage,
1186) -> Result<NetworkResponse> {
1187 Ok(NetworkResponse::Ok)
1190}
1191
1192fn process_getbanlist_message(_getbanlist: &commons::GetBanListMessage) -> Result<NetworkResponse> {
1194 Ok(NetworkResponse::Ok)
1197}
1198
1199fn process_banlist_message(_banlist: &commons::BanListMessage) -> Result<NetworkResponse> {
1201 Ok(NetworkResponse::Ok)
1204}