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}
111
112#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
117pub struct CompactBlockMessage {
118 pub compact_block: crate::bip152::CompactBlock,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
123pub struct AddrMessage {
124 pub addresses: Vec<NetworkAddress>,
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
129pub struct InvMessage {
130 pub inventory: Vec<InventoryVector>,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
135pub struct GetDataMessage {
136 pub inventory: Vec<InventoryVector>,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
141pub struct GetHeadersMessage {
142 pub version: u32,
143 pub block_locator_hashes: Vec<Hash>,
144 pub hash_stop: Hash,
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
149pub struct HeadersMessage {
150 pub headers: Vec<BlockHeader>,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
155pub struct PingMessage {
156 pub nonce: u64,
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
161pub struct PongMessage {
162 pub nonce: u64,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
167pub struct FeeFilterMessage {
168 pub feerate: u64,
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
173pub struct GetBlocksMessage {
174 pub version: u32,
175 pub block_locator_hashes: Vec<Hash>,
176 pub hash_stop: Hash,
177}
178
179#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
181pub struct NotFoundMessage {
182 pub inventory: Vec<InventoryVector>,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
187pub struct RejectMessage {
188 pub message: String, pub code: u8, pub reason: String, pub extra_data: Option<Hash>, }
193
194#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
196pub struct SendCmpctMessage {
197 pub version: u64,
199 pub prefer_cmpct: u8,
201}
202
203#[derive(Debug, Clone, PartialEq, Eq)]
205pub struct CmpctBlockMessage {
206 pub header: BlockHeader,
208 pub nonce: u64,
210 pub short_ids: Vec<ShortTxId>,
212 pub prefilled_txs: Vec<PrefilledTransaction>,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq)]
218pub struct PrefilledTransaction {
219 pub index: u16,
221 pub tx: Transaction,
223 pub witness: Option<Vec<blvm_consensus::segwit::Witness>>,
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, Error)]
229pub enum CompactBlockWireConvertError {
230 #[error("prefilled transaction index {0} does not fit in BIP152 u16 index")]
231 PrefilledIndexTooLarge(usize),
232 #[error("duplicate prefilled transaction index {0}")]
233 DuplicatePrefilledIndex(usize),
234}
235
236impl TryFrom<crate::bip152::CompactBlock> for CmpctBlockMessage {
237 type Error = CompactBlockWireConvertError;
238
239 fn try_from(mut value: crate::bip152::CompactBlock) -> std::result::Result<Self, Self::Error> {
244 value.prefilled_txs.sort_by_key(|(i, _)| *i);
245 let mut prefilled_txs = Vec::with_capacity(value.prefilled_txs.len());
246 let mut prev_idx: Option<usize> = None;
247 for (idx, tx) in value.prefilled_txs {
248 if prev_idx == Some(idx) {
249 return Err(CompactBlockWireConvertError::DuplicatePrefilledIndex(idx));
250 }
251 prev_idx = Some(idx);
252 let index = u16::try_from(idx)
253 .map_err(|_| CompactBlockWireConvertError::PrefilledIndexTooLarge(idx))?;
254 prefilled_txs.push(PrefilledTransaction {
255 index,
256 tx,
257 witness: None,
258 });
259 }
260 Ok(CmpctBlockMessage {
261 header: value.header,
262 nonce: value.nonce,
263 short_ids: value.short_ids,
264 prefilled_txs,
265 })
266 }
267}
268
269impl From<&CmpctBlockMessage> for crate::bip152::CompactBlock {
270 fn from(msg: &CmpctBlockMessage) -> Self {
271 Self {
272 header: msg.header.clone(),
273 nonce: msg.nonce,
274 short_ids: msg.short_ids.clone(),
275 prefilled_txs: msg
276 .prefilled_txs
277 .iter()
278 .map(|p| (usize::from(p.index), p.tx.clone()))
279 .collect(),
280 }
281 }
282}
283
284impl From<CmpctBlockMessage> for crate::bip152::CompactBlock {
285 fn from(msg: CmpctBlockMessage) -> Self {
286 Self {
287 header: msg.header,
288 nonce: msg.nonce,
289 short_ids: msg.short_ids,
290 prefilled_txs: msg
291 .prefilled_txs
292 .into_iter()
293 .map(|p| (usize::from(p.index), p.tx))
294 .collect(),
295 }
296 }
297}
298
299#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
301pub struct GetBlockTxnMessage {
302 pub block_hash: Hash,
304 pub indices: Vec<u16>,
306}
307
308#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
310pub struct BlockTxnMessage {
311 pub block_hash: Hash,
313 pub transactions: Vec<Transaction>,
315 pub witnesses: Option<Vec<Vec<blvm_consensus::segwit::Witness>>>,
317}
318
319#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
321pub struct NetworkAddress {
322 pub services: u64,
323 pub ip: [u8; 16], pub port: u16,
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
329#[repr(u8)]
330pub enum AddressType {
331 IPv4 = 1,
332 IPv6 = 2,
333 TorV2 = 3,
334 TorV3 = 4,
335 I2P = 5,
336 CJDNS = 6,
337}
338
339impl AddressType {
340 pub fn address_length(&self) -> usize {
342 match self {
343 AddressType::IPv4 => 4,
344 AddressType::IPv6 => 16,
345 AddressType::TorV2 => 10,
346 AddressType::TorV3 => 32,
347 AddressType::I2P => 32,
348 AddressType::CJDNS => 16,
349 }
350 }
351
352 pub fn from_u8(value: u8) -> Option<Self> {
354 match value {
355 1 => Some(AddressType::IPv4),
356 2 => Some(AddressType::IPv6),
357 3 => Some(AddressType::TorV2),
358 4 => Some(AddressType::TorV3),
359 5 => Some(AddressType::I2P),
360 6 => Some(AddressType::CJDNS),
361 _ => None,
362 }
363 }
364}
365
366#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
368pub struct AddrV2Message {
369 pub addresses: Vec<NetworkAddressV2>,
370}
371
372#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
374pub struct NetworkAddressV2 {
375 pub time: u32,
376 pub services: u64,
377 pub address_type: AddressType,
378 pub address: Vec<u8>, pub port: u16,
380}
381
382impl NetworkAddressV2 {
383 pub fn new(
385 time: u32,
386 services: u64,
387 address_type: AddressType,
388 address: Vec<u8>,
389 port: u16,
390 ) -> Result<Self> {
391 let expected_len = address_type.address_length();
392 if address.len() != expected_len {
393 return Err(ProtocolError::Consensus(ConsensusError::Serialization(
394 std::borrow::Cow::Owned(format!(
395 "Invalid address length for type {:?}: expected {}, got {}",
396 address_type,
397 expected_len,
398 address.len()
399 )),
400 )));
401 }
402 Ok(Self {
403 time,
404 services,
405 address_type,
406 address,
407 port,
408 })
409 }
410
411 pub fn to_legacy(&self) -> Option<NetworkAddress> {
413 match self.address_type {
414 AddressType::IPv4 => {
415 if self.address.len() == 4 {
416 let mut ipv6 = [0u8; 16];
418 ipv6[10] = 0xff;
419 ipv6[11] = 0xff;
420 ipv6[12..16].copy_from_slice(&self.address);
421 Some(NetworkAddress {
422 services: self.services,
423 ip: ipv6,
424 port: self.port,
425 })
426 } else {
427 None
428 }
429 }
430 AddressType::IPv6 => {
431 if self.address.len() == 16 {
432 let mut ipv6 = [0u8; 16];
433 ipv6.copy_from_slice(&self.address);
434 Some(NetworkAddress {
435 services: self.services,
436 ip: ipv6,
437 port: self.port,
438 })
439 } else {
440 None
441 }
442 }
443 _ => None, }
445 }
446}
447
448#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
450pub struct InventoryVector {
451 pub inv_type: u32,
452 pub hash: Hash,
453}
454
455#[derive(Debug, Clone)]
457pub enum NetworkResponse {
458 Ok,
459 SendMessage(Box<NetworkMessage>),
460 SendMessages(Vec<NetworkMessage>),
461 Reject(String),
462}
463
464#[derive(Clone)]
466pub struct PeerState {
467 pub version: u32,
468 pub services: u64,
469 pub user_agent: String,
470 pub start_height: i32,
471 pub handshake_complete: bool,
472 pub known_addresses: Vec<NetworkAddress>,
473 pub ping_nonce: Option<u64>,
474 pub last_pong: Option<std::time::SystemTime>,
475 pub min_fee_rate: Option<u64>,
476 pub prefer_headers: bool,
478 pub compact_block_version: Option<u8>,
480 pub prefer_compact_block: bool,
482 #[cfg(all(
485 feature = "bip324",
486 any(target_arch = "x86_64", target_arch = "aarch64")
487 ))]
488 pub v2_transport: Option<std::sync::Arc<crate::v2_transport::V2Transport>>,
489 #[cfg(all(
491 feature = "bip324",
492 any(target_arch = "x86_64", target_arch = "aarch64")
493 ))]
494 pub v2_handshake: Option<std::sync::Arc<crate::v2_transport::V2Handshake>>,
495}
496
497impl PeerState {
498 pub fn new() -> Self {
499 Self {
500 version: 0,
501 services: 0,
502 user_agent: String::new(),
503 start_height: 0,
504 handshake_complete: false,
505 known_addresses: Vec::new(),
506 ping_nonce: None,
507 last_pong: None,
508 min_fee_rate: None,
509 prefer_headers: false,
510 compact_block_version: None,
511 prefer_compact_block: false,
512 #[cfg(all(
513 feature = "bip324",
514 any(target_arch = "x86_64", target_arch = "aarch64")
515 ))]
516 v2_transport: None,
517 #[cfg(all(
518 feature = "bip324",
519 any(target_arch = "x86_64", target_arch = "aarch64")
520 ))]
521 v2_handshake: None,
522 }
523 }
524
525 #[cfg(all(
527 feature = "bip324",
528 any(target_arch = "x86_64", target_arch = "aarch64")
529 ))]
530 pub fn supports_v2_transport(&self) -> bool {
531 use crate::service_flags::{has_flag, standard};
532 has_flag(self.services, standard::NODE_V2_TRANSPORT)
533 }
534
535 #[cfg(all(
537 feature = "bip324",
538 any(target_arch = "x86_64", target_arch = "aarch64")
539 ))]
540 pub fn is_v2_transport_active(&self) -> bool {
541 self.v2_transport.is_some()
542 }
543}
544
545impl Default for PeerState {
546 fn default() -> Self {
547 Self::new()
548 }
549}
550
551#[derive(Debug, Clone)]
553pub enum ChainObject {
554 Block(Arc<Block>),
555 Transaction(Arc<Transaction>),
556}
557
558impl ChainObject {
559 pub fn as_block(&self) -> Option<&Arc<Block>> {
560 match self {
561 ChainObject::Block(block) => Some(block),
562 _ => None,
563 }
564 }
565
566 pub fn as_transaction(&self) -> Option<&Arc<Transaction>> {
567 match self {
568 ChainObject::Transaction(tx) => Some(tx),
569 _ => None,
570 }
571 }
572}
573
574pub trait ChainStateAccess {
580 fn has_object(&self, hash: &Hash) -> bool;
582
583 fn get_object(&self, hash: &Hash) -> Option<ChainObject>;
585
586 fn get_headers_for_locator(&self, locator: &[Hash], stop: &Hash) -> Vec<BlockHeader>;
589
590 fn get_mempool_transactions(&self) -> Vec<Transaction>;
592
593 fn get_block_witnesses(
595 &self,
596 _hash: &Hash,
597 ) -> Option<Vec<Vec<blvm_consensus::segwit::Witness>>> {
598 None
599 }
600}
601
602pub fn process_network_message(
620 engine: &BitcoinProtocolEngine,
621 message: &NetworkMessage,
622 peer_state: &mut PeerState,
623 chain_access: Option<&dyn ChainStateAccess>,
624 utxo_set: Option<&UtxoSet>,
625 height: Option<u64>,
626) -> Result<NetworkResponse> {
627 let config = engine.get_config();
628 match message {
629 NetworkMessage::Version(version) => process_version_message(version, peer_state, config),
630 NetworkMessage::VerAck => process_verack_message(peer_state),
631 NetworkMessage::Addr(addr) => process_addr_message(addr, peer_state, config),
632 NetworkMessage::AddrV2(addrv2) => process_addrv2_message(addrv2, peer_state, config),
633 NetworkMessage::Inv(inv) => process_inv_message(inv, chain_access, config),
634 NetworkMessage::GetData(getdata) => process_getdata_message(getdata, chain_access, config),
635 NetworkMessage::GetHeaders(getheaders) => {
636 process_getheaders_message(getheaders, chain_access, config)
637 }
638 NetworkMessage::Headers(headers) => process_headers_message(headers, config),
639 NetworkMessage::Block(block, witnesses) => {
640 process_block_message(engine, block, witnesses, utxo_set, height, config)
641 }
642 NetworkMessage::Tx(tx) => process_tx_message(engine, tx, height),
643 NetworkMessage::Ping(ping) => process_ping_message(ping, peer_state),
644 NetworkMessage::Pong(pong) => process_pong_message(pong, peer_state),
645 NetworkMessage::MemPool => process_mempool_message(chain_access),
646 NetworkMessage::FeeFilter(feefilter) => process_feefilter_message(feefilter, peer_state),
647 NetworkMessage::GetBlocks(getblocks) => {
648 process_getblocks_message(getblocks, chain_access, config)
649 }
650 NetworkMessage::GetAddr => process_getaddr_message(peer_state, config),
651 NetworkMessage::NotFound(notfound) => process_notfound_message(notfound, config),
652 NetworkMessage::Reject(reject) => process_reject_message(reject, config),
653 NetworkMessage::SendHeaders => process_sendheaders_message(peer_state),
654 NetworkMessage::SendCmpct(sendcmpct) => {
655 process_sendcmpct_message(sendcmpct, peer_state, config)
656 }
657 NetworkMessage::CmpctBlock(cmpctblock) => process_cmpctblock_message(cmpctblock),
658 NetworkMessage::GetBlockTxn(getblocktxn) => {
659 process_getblocktxn_message(getblocktxn, chain_access, config)
660 }
661 NetworkMessage::BlockTxn(blocktxn) => process_blocktxn_message(blocktxn),
662 #[cfg(feature = "utxo-commitments")]
663 NetworkMessage::GetUTXOSet(getutxoset) => process_getutxoset_message(getutxoset),
664 #[cfg(feature = "utxo-commitments")]
665 NetworkMessage::UTXOSet(utxoset) => process_utxoset_message(utxoset),
666 #[cfg(feature = "utxo-commitments")]
667 NetworkMessage::GetFilteredBlock(getfiltered) => {
668 process_getfilteredblock_message(getfiltered)
669 }
670 #[cfg(feature = "utxo-commitments")]
671 NetworkMessage::FilteredBlock(filtered) => process_filteredblock_message(filtered),
672 NetworkMessage::GetBanList(getbanlist) => process_getbanlist_message(getbanlist),
673 NetworkMessage::BanList(banlist) => process_banlist_message(banlist),
674 }
675}
676
677fn process_version_message(
679 version: &VersionMessage,
680 peer_state: &mut PeerState,
681 config: &ProtocolConfig,
682) -> Result<NetworkResponse> {
683 if version.version < 70001 {
685 return Ok(NetworkResponse::Reject("Version too old".into()));
686 }
687
688 if version.user_agent.len() > config.network_limits.max_user_agent_length {
690 return Ok(NetworkResponse::Reject(format!(
691 "User agent too long (max {} bytes)",
692 config.network_limits.max_user_agent_length
693 )));
694 }
695
696 peer_state.version = version.version;
698 peer_state.services = version.services;
699 peer_state.user_agent = version.user_agent.clone();
700 peer_state.start_height = version.start_height;
701
702 #[cfg(all(
704 feature = "bip324",
705 any(target_arch = "x86_64", target_arch = "aarch64")
706 ))]
707 {
708 use crate::service_flags::{has_flag, standard};
709 let peer_supports_v2 = has_flag(version.services, standard::NODE_V2_TRANSPORT);
710 let we_support_v2 = config.service_flags.node_v2_transport;
711
712 if peer_supports_v2 && we_support_v2 {
713 let handshake = crate::v2_transport::V2Handshake::new_responder();
715 peer_state.v2_handshake = Some(std::sync::Arc::new(handshake));
716 }
719 }
720
721 Ok(NetworkResponse::SendMessage(Box::new(
723 NetworkMessage::VerAck,
724 )))
725}
726
727fn process_verack_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
729 peer_state.handshake_complete = true;
730 Ok(NetworkResponse::Ok)
731}
732
733fn process_addr_message(
735 addr: &AddrMessage,
736 peer_state: &mut PeerState,
737 config: &ProtocolConfig,
738) -> Result<NetworkResponse> {
739 if addr.addresses.len() > config.network_limits.max_addr_addresses {
741 return Ok(NetworkResponse::Reject(format!(
742 "Too many addresses (max {})",
743 config.network_limits.max_addr_addresses
744 )));
745 }
746
747 peer_state.known_addresses.extend(addr.addresses.clone());
749
750 Ok(NetworkResponse::Ok)
751}
752
753fn process_addrv2_message(
755 addrv2: &AddrV2Message,
756 peer_state: &mut PeerState,
757 config: &ProtocolConfig,
758) -> Result<NetworkResponse> {
759 if addrv2.addresses.len() > config.network_limits.max_addr_addresses {
761 return Ok(NetworkResponse::Reject(format!(
762 "Too many addresses (max {})",
763 config.network_limits.max_addr_addresses
764 )));
765 }
766
767 for addr_v2 in &addrv2.addresses {
769 if let Some(legacy_addr) = addr_v2.to_legacy() {
770 peer_state.known_addresses.push(legacy_addr);
771 }
772 }
775
776 Ok(NetworkResponse::Ok)
777}
778
779fn process_inv_message(
781 inv: &InvMessage,
782 chain_access: Option<&dyn ChainStateAccess>,
783 config: &ProtocolConfig,
784) -> Result<NetworkResponse> {
785 if inv.inventory.len() > config.network_limits.max_inv_items {
787 return Ok(NetworkResponse::Reject(format!(
788 "Too many inventory items (max {})",
789 config.network_limits.max_inv_items
790 )));
791 }
792
793 if let Some(chain) = chain_access {
795 let mut needed_items = Vec::with_capacity(inv.inventory.len());
796 for item in &inv.inventory {
797 if !chain.has_object(&item.hash) {
798 needed_items.push(item.clone());
799 }
800 }
801
802 if !needed_items.is_empty() {
803 return Ok(NetworkResponse::SendMessage(Box::new(
804 NetworkMessage::GetData(GetDataMessage {
805 inventory: needed_items,
806 }),
807 )));
808 }
809 }
810
811 Ok(NetworkResponse::Ok)
812}
813
814fn process_getdata_message(
816 getdata: &GetDataMessage,
817 chain_access: Option<&dyn ChainStateAccess>,
818 config: &ProtocolConfig,
819) -> Result<NetworkResponse> {
820 if getdata.inventory.len() > config.network_limits.max_inv_items {
822 return Ok(NetworkResponse::Reject(format!(
823 "Too many getdata items (max {})",
824 config.network_limits.max_inv_items
825 )));
826 }
827
828 if let Some(chain) = chain_access {
830 let mut responses = Vec::with_capacity(getdata.inventory.len());
831 for item in &getdata.inventory {
832 if let Some(obj) = chain.get_object(&item.hash) {
833 match item.inv_type {
834 1 => {
835 if let Some(tx) = obj.as_transaction() {
837 responses.push(NetworkMessage::Tx(Arc::clone(tx)));
838 }
839 }
840 2 => {
841 if let Some(block) = obj.as_block() {
843 let Some(witnesses) = chain.get_block_witnesses(&item.hash) else {
844 continue;
847 };
848 responses.push(NetworkMessage::Block(Arc::clone(block), witnesses));
849 }
850 }
851 _ => {
852 }
854 }
855 }
856 }
857
858 if !responses.is_empty() {
859 return Ok(NetworkResponse::SendMessages(responses));
860 }
861 }
862
863 Ok(NetworkResponse::Ok)
864}
865
866fn process_getheaders_message(
868 getheaders: &GetHeadersMessage,
869 chain_access: Option<&dyn ChainStateAccess>,
870 config: &ProtocolConfig,
871) -> Result<NetworkResponse> {
872 if getheaders.block_locator_hashes.len() > config.validation.max_locator_hashes {
874 return Ok(NetworkResponse::Reject(format!(
875 "Too many locator hashes (max {})",
876 config.validation.max_locator_hashes
877 )));
878 }
879
880 if let Some(chain) = chain_access {
882 let headers =
883 chain.get_headers_for_locator(&getheaders.block_locator_hashes, &getheaders.hash_stop);
884 return Ok(NetworkResponse::SendMessage(Box::new(
885 NetworkMessage::Headers(HeadersMessage { headers }),
886 )));
887 }
888
889 Ok(NetworkResponse::Reject("Chain access not available".into()))
890}
891
892fn process_headers_message(
894 headers: &HeadersMessage,
895 config: &ProtocolConfig,
896) -> Result<NetworkResponse> {
897 if headers.headers.len() > config.network_limits.max_headers {
899 return Ok(NetworkResponse::Reject(format!(
900 "Too many headers (max {})",
901 config.network_limits.max_headers
902 )));
903 }
904
905 Ok(NetworkResponse::Ok)
908}
909
910fn process_block_message(
912 engine: &BitcoinProtocolEngine,
913 block: &Block,
914 witnesses: &[Vec<blvm_consensus::segwit::Witness>],
915 utxo_set: Option<&UtxoSet>,
916 height: Option<u64>,
917 config: &ProtocolConfig,
918) -> Result<NetworkResponse> {
919 if block.transactions.len() > config.validation.max_txs_per_block {
921 return Err(crate::error::ProtocolError::MessageTooLarge {
922 size: block.transactions.len(),
923 max: config.validation.max_txs_per_block,
924 });
925 }
926
927 if let (Some(utxos), Some(h)) = (utxo_set, height) {
929 let context = ProtocolValidationContext::new(engine.get_protocol_version(), h)?;
930 let result = engine
932 .validate_block_with_protocol_and_witnesses(block, witnesses, utxos, h, &context)?;
933
934 match result {
935 ValidationResult::Valid => Ok(NetworkResponse::Ok),
936 ValidationResult::Invalid(reason) => {
937 Ok(NetworkResponse::Reject(format!("Invalid block: {reason}")))
938 }
939 }
940 } else {
941 Err(crate::error::ProtocolError::Configuration(
942 "Missing validation context (utxo_set and height required)".into(),
943 ))
944 }
945}
946
947fn process_tx_message(
949 engine: &BitcoinProtocolEngine,
950 tx: &Transaction,
951 height: Option<u64>,
952) -> Result<NetworkResponse> {
953 let context =
955 ProtocolValidationContext::new(engine.get_protocol_version(), height.unwrap_or(0))?;
956 let result = engine.validate_transaction_with_protocol(tx, &context)?;
957
958 match result {
959 ValidationResult::Valid => Ok(NetworkResponse::Ok),
960 ValidationResult::Invalid(reason) => Ok(NetworkResponse::Reject(format!(
961 "Invalid transaction: {reason}"
962 ))),
963 }
964}
965
966fn process_ping_message(
968 ping: &PingMessage,
969 _peer_state: &mut PeerState,
970) -> Result<NetworkResponse> {
971 let pong = NetworkMessage::Pong(PongMessage { nonce: ping.nonce });
972 Ok(NetworkResponse::SendMessage(Box::new(pong)))
973}
974
975fn process_pong_message(pong: &PongMessage, peer_state: &mut PeerState) -> Result<NetworkResponse> {
977 if peer_state.ping_nonce == Some(pong.nonce) {
979 peer_state.ping_nonce = None;
980 peer_state.last_pong = Some(std::time::SystemTime::now());
981 }
982
983 Ok(NetworkResponse::Ok)
984}
985
986fn process_mempool_message(chain_access: Option<&dyn ChainStateAccess>) -> Result<NetworkResponse> {
988 if let Some(chain) = chain_access {
990 let mempool_txs = chain.get_mempool_transactions();
991 let mut responses = Vec::with_capacity(mempool_txs.len());
992
993 for tx in mempool_txs {
994 responses.push(NetworkMessage::Tx(Arc::new(tx)));
995 }
996
997 if !responses.is_empty() {
998 return Ok(NetworkResponse::SendMessages(responses));
999 }
1000 }
1001
1002 Ok(NetworkResponse::Ok)
1003}
1004
1005fn process_feefilter_message(
1007 feefilter: &FeeFilterMessage,
1008 peer_state: &mut PeerState,
1009) -> Result<NetworkResponse> {
1010 peer_state.min_fee_rate = Some(feefilter.feerate);
1011 Ok(NetworkResponse::Ok)
1012}
1013
1014fn process_getblocks_message(
1016 getblocks: &GetBlocksMessage,
1017 chain_access: Option<&dyn ChainStateAccess>,
1018 config: &ProtocolConfig,
1019) -> Result<NetworkResponse> {
1020 if getblocks.block_locator_hashes.len() > config.validation.max_locator_hashes {
1022 return Ok(NetworkResponse::Reject(format!(
1023 "Too many locator hashes (max {})",
1024 config.validation.max_locator_hashes
1025 )));
1026 }
1027
1028 if let Some(chain) = chain_access {
1029 use blvm_consensus::block::block_header_hash;
1030
1031 const MAX_GETBLOCKS_INV: usize = 500;
1033 let headers =
1034 chain.get_headers_for_locator(&getblocks.block_locator_hashes, &getblocks.hash_stop);
1035 if headers.is_empty() {
1036 return Ok(NetworkResponse::Ok);
1037 }
1038
1039 let inventory: Vec<InventoryVector> = headers
1040 .into_iter()
1041 .take(MAX_GETBLOCKS_INV)
1042 .map(|header| InventoryVector {
1043 inv_type: 2, hash: block_header_hash(&header),
1045 })
1046 .collect();
1047
1048 return Ok(NetworkResponse::SendMessage(Box::new(NetworkMessage::Inv(
1049 InvMessage { inventory },
1050 ))));
1051 }
1052
1053 Ok(NetworkResponse::Ok)
1054}
1055
1056fn process_getaddr_message(
1058 peer_state: &mut PeerState,
1059 config: &ProtocolConfig,
1060) -> Result<NetworkResponse> {
1061 if !peer_state.known_addresses.is_empty() {
1063 let max_addrs = config
1065 .network_limits
1066 .max_addr_addresses
1067 .min(peer_state.known_addresses.len());
1068 let mut addresses = Vec::with_capacity(max_addrs);
1069 addresses.extend(peer_state.known_addresses.iter().take(max_addrs).cloned());
1070
1071 return Ok(NetworkResponse::SendMessage(Box::new(
1072 NetworkMessage::Addr(AddrMessage { addresses }),
1073 )));
1074 }
1075
1076 Ok(NetworkResponse::Ok)
1077}
1078
1079fn process_notfound_message(
1081 notfound: &NotFoundMessage,
1082 config: &ProtocolConfig,
1083) -> Result<NetworkResponse> {
1084 if notfound.inventory.len() > config.network_limits.max_inv_items {
1086 return Ok(NetworkResponse::Reject(format!(
1087 "Too many notfound items (max {})",
1088 config.network_limits.max_inv_items
1089 )));
1090 }
1091
1092 Ok(NetworkResponse::Ok)
1094}
1095
1096fn process_reject_message(
1098 reject: &RejectMessage,
1099 _config: &ProtocolConfig,
1100) -> Result<NetworkResponse> {
1101 if reject.message.len() > 12 {
1104 return Ok(NetworkResponse::Reject(
1105 "Invalid reject message name".into(),
1106 ));
1107 }
1108
1109 if reject.reason.len() > 111 {
1112 return Ok(NetworkResponse::Reject("Reject reason too long".into()));
1113 }
1114
1115 Ok(NetworkResponse::Ok)
1118}
1119
1120fn process_sendheaders_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
1122 peer_state.prefer_headers = true;
1123 Ok(NetworkResponse::Ok)
1124}
1125
1126fn process_sendcmpct_message(
1128 sendcmpct: &SendCmpctMessage,
1129 peer_state: &mut PeerState,
1130 config: &ProtocolConfig,
1131) -> Result<NetworkResponse> {
1132 let valid_versions = [1, 2];
1134 if !valid_versions.contains(&sendcmpct.version) {
1135 return Ok(NetworkResponse::Reject(
1136 "Invalid compact block version".into(),
1137 ));
1138 }
1139
1140 if !config.compact_blocks.enabled {
1142 return Ok(NetworkResponse::Reject("Compact blocks not enabled".into()));
1143 }
1144
1145 peer_state.compact_block_version = Some(sendcmpct.version as u8);
1147 peer_state.prefer_compact_block = sendcmpct.prefer_cmpct != 0;
1148 Ok(NetworkResponse::Ok)
1149}
1150
1151fn process_cmpctblock_message(_cmpctblock: &CmpctBlockMessage) -> Result<NetworkResponse> {
1153 Ok(NetworkResponse::Reject(
1154 "Compact block reconstruction not implemented".into(),
1155 ))
1156}
1157
1158fn process_getblocktxn_message(
1160 getblocktxn: &GetBlockTxnMessage,
1161 chain_access: Option<&dyn ChainStateAccess>,
1162 config: &ProtocolConfig,
1163) -> Result<NetworkResponse> {
1164 if getblocktxn.indices.len() > config.compact_blocks.max_blocktxn_indices {
1166 return Ok(NetworkResponse::Reject(format!(
1167 "Too many transaction indices (max {})",
1168 config.compact_blocks.max_blocktxn_indices
1169 )));
1170 }
1171
1172 if let Some(chain) = chain_access {
1174 if let Some(obj) = chain.get_object(&getblocktxn.block_hash) {
1175 if let Some(block) = obj.as_block() {
1176 let stored_witnesses = chain.get_block_witnesses(&getblocktxn.block_hash);
1177 let mut transactions = Vec::with_capacity(getblocktxn.indices.len());
1178 let mut req_witnesses = Vec::with_capacity(getblocktxn.indices.len());
1179
1180 for &index in &getblocktxn.indices {
1181 let idx = index as usize;
1182 if idx >= block.transactions.len() {
1183 continue;
1184 }
1185 transactions.push(block.transactions[idx].clone());
1186 let tx_witness = stored_witnesses
1187 .as_ref()
1188 .and_then(|w| w.get(idx).cloned())
1189 .unwrap_or_else(|| {
1190 block.transactions[idx]
1191 .inputs
1192 .iter()
1193 .map(|_| Vec::new())
1194 .collect()
1195 });
1196 req_witnesses.push(tx_witness);
1197 }
1198
1199 if !transactions.is_empty() {
1200 return Ok(NetworkResponse::SendMessage(Box::new(
1201 NetworkMessage::BlockTxn(BlockTxnMessage {
1202 block_hash: getblocktxn.block_hash,
1203 transactions,
1204 witnesses: Some(req_witnesses),
1205 }),
1206 )));
1207 }
1208 }
1209 }
1210 }
1211
1212 Ok(NetworkResponse::Ok)
1213}
1214
1215fn process_blocktxn_message(_blocktxn: &BlockTxnMessage) -> Result<NetworkResponse> {
1217 Ok(NetworkResponse::Reject(
1218 "Compact block reconstruction not implemented".into(),
1219 ))
1220}
1221
1222#[cfg(feature = "utxo-commitments")]
1223fn process_getutxoset_message(_getutxoset: &commons::GetUTXOSetMessage) -> Result<NetworkResponse> {
1225 Ok(NetworkResponse::Reject(
1226 "UTXO set relay not implemented".into(),
1227 ))
1228}
1229
1230#[cfg(feature = "utxo-commitments")]
1231fn process_utxoset_message(_utxoset: &commons::UTXOSetMessage) -> Result<NetworkResponse> {
1233 Ok(NetworkResponse::Reject(
1234 "UTXO set relay not implemented".into(),
1235 ))
1236}
1237
1238#[cfg(feature = "utxo-commitments")]
1239fn process_getfilteredblock_message(
1241 _getfiltered: &commons::GetFilteredBlockMessage,
1242) -> Result<NetworkResponse> {
1243 Ok(NetworkResponse::Reject(
1244 "Filtered block relay not implemented".into(),
1245 ))
1246}
1247
1248#[cfg(feature = "utxo-commitments")]
1249fn process_filteredblock_message(
1251 _filtered: &commons::FilteredBlockMessage,
1252) -> Result<NetworkResponse> {
1253 Ok(NetworkResponse::Reject(
1254 "Filtered block relay not implemented".into(),
1255 ))
1256}
1257
1258fn process_getbanlist_message(_getbanlist: &commons::GetBanListMessage) -> Result<NetworkResponse> {
1260 Ok(NetworkResponse::Reject(
1261 "Ban list relay not implemented".into(),
1262 ))
1263}
1264
1265fn process_banlist_message(_banlist: &commons::BanListMessage) -> Result<NetworkResponse> {
1267 Ok(NetworkResponse::Reject(
1268 "Ban list relay not implemented".into(),
1269 ))
1270}