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 #[cfg(all(
479 feature = "bip324",
480 any(target_arch = "x86_64", target_arch = "aarch64")
481 ))]
482 pub v2_transport: Option<std::sync::Arc<crate::v2_transport::V2Transport>>,
483 #[cfg(all(
485 feature = "bip324",
486 any(target_arch = "x86_64", target_arch = "aarch64")
487 ))]
488 pub v2_handshake: Option<std::sync::Arc<crate::v2_transport::V2Handshake>>,
489}
490
491impl PeerState {
492 pub fn new() -> Self {
493 Self {
494 version: 0,
495 services: 0,
496 user_agent: String::new(),
497 start_height: 0,
498 handshake_complete: false,
499 known_addresses: Vec::new(),
500 ping_nonce: None,
501 last_pong: None,
502 min_fee_rate: None,
503 #[cfg(all(
504 feature = "bip324",
505 any(target_arch = "x86_64", target_arch = "aarch64")
506 ))]
507 v2_transport: None,
508 #[cfg(all(
509 feature = "bip324",
510 any(target_arch = "x86_64", target_arch = "aarch64")
511 ))]
512 v2_handshake: None,
513 }
514 }
515
516 #[cfg(all(
518 feature = "bip324",
519 any(target_arch = "x86_64", target_arch = "aarch64")
520 ))]
521 pub fn supports_v2_transport(&self) -> bool {
522 use crate::service_flags::{has_flag, standard};
523 has_flag(self.services, standard::NODE_V2_TRANSPORT)
524 }
525
526 #[cfg(all(
528 feature = "bip324",
529 any(target_arch = "x86_64", target_arch = "aarch64")
530 ))]
531 pub fn is_v2_transport_active(&self) -> bool {
532 self.v2_transport.is_some()
533 }
534}
535
536impl Default for PeerState {
537 fn default() -> Self {
538 Self::new()
539 }
540}
541
542#[derive(Debug, Clone)]
544pub enum ChainObject {
545 Block(Arc<Block>),
546 Transaction(Arc<Transaction>),
547}
548
549impl ChainObject {
550 pub fn as_block(&self) -> Option<&Arc<Block>> {
551 match self {
552 ChainObject::Block(block) => Some(block),
553 _ => None,
554 }
555 }
556
557 pub fn as_transaction(&self) -> Option<&Arc<Transaction>> {
558 match self {
559 ChainObject::Transaction(tx) => Some(tx),
560 _ => None,
561 }
562 }
563}
564
565pub trait ChainStateAccess {
571 fn has_object(&self, hash: &Hash) -> bool;
573
574 fn get_object(&self, hash: &Hash) -> Option<ChainObject>;
576
577 fn get_headers_for_locator(&self, locator: &[Hash], stop: &Hash) -> Vec<BlockHeader>;
580
581 fn get_mempool_transactions(&self) -> Vec<Transaction>;
583}
584
585pub fn process_network_message(
603 engine: &BitcoinProtocolEngine,
604 message: &NetworkMessage,
605 peer_state: &mut PeerState,
606 chain_access: Option<&dyn ChainStateAccess>,
607 utxo_set: Option<&UtxoSet>,
608 height: Option<u64>,
609) -> Result<NetworkResponse> {
610 let config = engine.get_config();
611 match message {
612 NetworkMessage::Version(version) => process_version_message(version, peer_state, config),
613 NetworkMessage::VerAck => process_verack_message(peer_state),
614 NetworkMessage::Addr(addr) => process_addr_message(addr, peer_state, config),
615 NetworkMessage::AddrV2(addrv2) => process_addrv2_message(addrv2, peer_state, config),
616 NetworkMessage::Inv(inv) => process_inv_message(inv, chain_access, config),
617 NetworkMessage::GetData(getdata) => process_getdata_message(getdata, chain_access, config),
618 NetworkMessage::GetHeaders(getheaders) => {
619 process_getheaders_message(getheaders, chain_access, config)
620 }
621 NetworkMessage::Headers(headers) => process_headers_message(headers, config),
622 NetworkMessage::Block(block, witnesses) => {
623 process_block_message(engine, block, witnesses, utxo_set, height, config)
624 }
625 NetworkMessage::Tx(tx) => process_tx_message(engine, tx, height),
626 NetworkMessage::Ping(ping) => process_ping_message(ping, peer_state),
627 NetworkMessage::Pong(pong) => process_pong_message(pong, peer_state),
628 NetworkMessage::MemPool => process_mempool_message(chain_access),
629 NetworkMessage::FeeFilter(feefilter) => process_feefilter_message(feefilter, peer_state),
630 NetworkMessage::GetBlocks(getblocks) => {
631 process_getblocks_message(getblocks, chain_access, config)
632 }
633 NetworkMessage::GetAddr => process_getaddr_message(peer_state, config),
634 NetworkMessage::NotFound(notfound) => process_notfound_message(notfound, config),
635 NetworkMessage::Reject(reject) => process_reject_message(reject, config),
636 NetworkMessage::SendHeaders => process_sendheaders_message(peer_state),
637 NetworkMessage::SendCmpct(sendcmpct) => {
638 process_sendcmpct_message(sendcmpct, peer_state, config)
639 }
640 NetworkMessage::CmpctBlock(cmpctblock) => process_cmpctblock_message(cmpctblock),
641 NetworkMessage::GetBlockTxn(getblocktxn) => {
642 process_getblocktxn_message(getblocktxn, chain_access, config)
643 }
644 NetworkMessage::BlockTxn(blocktxn) => process_blocktxn_message(blocktxn),
645 #[cfg(feature = "utxo-commitments")]
646 NetworkMessage::GetUTXOSet(getutxoset) => process_getutxoset_message(getutxoset),
647 #[cfg(feature = "utxo-commitments")]
648 NetworkMessage::UTXOSet(utxoset) => process_utxoset_message(utxoset),
649 #[cfg(feature = "utxo-commitments")]
650 NetworkMessage::GetFilteredBlock(getfiltered) => {
651 process_getfilteredblock_message(getfiltered)
652 }
653 #[cfg(feature = "utxo-commitments")]
654 NetworkMessage::FilteredBlock(filtered) => process_filteredblock_message(filtered),
655 NetworkMessage::GetBanList(getbanlist) => process_getbanlist_message(getbanlist),
656 NetworkMessage::BanList(banlist) => process_banlist_message(banlist),
657 }
658}
659
660fn process_version_message(
662 version: &VersionMessage,
663 peer_state: &mut PeerState,
664 config: &ProtocolConfig,
665) -> Result<NetworkResponse> {
666 if version.version < 70001 {
668 return Ok(NetworkResponse::Reject("Version too old".into()));
669 }
670
671 if version.user_agent.len() > config.network_limits.max_user_agent_length {
673 return Ok(NetworkResponse::Reject(format!(
674 "User agent too long (max {} bytes)",
675 config.network_limits.max_user_agent_length
676 )));
677 }
678
679 peer_state.version = version.version;
681 peer_state.services = version.services;
682 peer_state.user_agent = version.user_agent.clone();
683 peer_state.start_height = version.start_height;
684
685 #[cfg(all(
687 feature = "bip324",
688 any(target_arch = "x86_64", target_arch = "aarch64")
689 ))]
690 {
691 use crate::service_flags::{has_flag, standard};
692 let peer_supports_v2 = has_flag(version.services, standard::NODE_V2_TRANSPORT);
693 let we_support_v2 = config.service_flags.node_v2_transport;
694
695 if peer_supports_v2 && we_support_v2 {
696 let handshake = crate::v2_transport::V2Handshake::new_responder();
698 peer_state.v2_handshake = Some(std::sync::Arc::new(handshake));
699 }
702 }
703
704 Ok(NetworkResponse::SendMessage(Box::new(
706 NetworkMessage::VerAck,
707 )))
708}
709
710fn process_verack_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
712 peer_state.handshake_complete = true;
713 Ok(NetworkResponse::Ok)
714}
715
716fn process_addr_message(
718 addr: &AddrMessage,
719 peer_state: &mut PeerState,
720 config: &ProtocolConfig,
721) -> Result<NetworkResponse> {
722 if addr.addresses.len() > config.network_limits.max_addr_addresses {
724 return Ok(NetworkResponse::Reject(format!(
725 "Too many addresses (max {})",
726 config.network_limits.max_addr_addresses
727 )));
728 }
729
730 peer_state.known_addresses.extend(addr.addresses.clone());
732
733 Ok(NetworkResponse::Ok)
734}
735
736fn process_addrv2_message(
738 addrv2: &AddrV2Message,
739 peer_state: &mut PeerState,
740 config: &ProtocolConfig,
741) -> Result<NetworkResponse> {
742 if addrv2.addresses.len() > config.network_limits.max_addr_addresses {
744 return Ok(NetworkResponse::Reject(format!(
745 "Too many addresses (max {})",
746 config.network_limits.max_addr_addresses
747 )));
748 }
749
750 for addr_v2 in &addrv2.addresses {
752 if let Some(legacy_addr) = addr_v2.to_legacy() {
753 peer_state.known_addresses.push(legacy_addr);
754 }
755 }
758
759 Ok(NetworkResponse::Ok)
760}
761
762fn process_inv_message(
764 inv: &InvMessage,
765 chain_access: Option<&dyn ChainStateAccess>,
766 config: &ProtocolConfig,
767) -> Result<NetworkResponse> {
768 if inv.inventory.len() > config.network_limits.max_inv_items {
770 return Ok(NetworkResponse::Reject(format!(
771 "Too many inventory items (max {})",
772 config.network_limits.max_inv_items
773 )));
774 }
775
776 if let Some(chain) = chain_access {
778 let mut needed_items = Vec::with_capacity(inv.inventory.len());
779 for item in &inv.inventory {
780 if !chain.has_object(&item.hash) {
781 needed_items.push(item.clone());
782 }
783 }
784
785 if !needed_items.is_empty() {
786 return Ok(NetworkResponse::SendMessage(Box::new(
787 NetworkMessage::GetData(GetDataMessage {
788 inventory: needed_items,
789 }),
790 )));
791 }
792 }
793
794 Ok(NetworkResponse::Ok)
795}
796
797fn process_getdata_message(
799 getdata: &GetDataMessage,
800 chain_access: Option<&dyn ChainStateAccess>,
801 config: &ProtocolConfig,
802) -> Result<NetworkResponse> {
803 if getdata.inventory.len() > config.network_limits.max_inv_items {
805 return Ok(NetworkResponse::Reject(format!(
806 "Too many getdata items (max {})",
807 config.network_limits.max_inv_items
808 )));
809 }
810
811 if let Some(chain) = chain_access {
813 let mut responses = Vec::with_capacity(getdata.inventory.len());
814 for item in &getdata.inventory {
815 if let Some(obj) = chain.get_object(&item.hash) {
816 match item.inv_type {
817 1 => {
818 if let Some(tx) = obj.as_transaction() {
820 responses.push(NetworkMessage::Tx(Arc::clone(tx)));
821 }
822 }
823 2 => {
824 if let Some(block) = obj.as_block() {
826 responses.push(NetworkMessage::Block(Arc::clone(block), vec![]));
829 }
830 }
831 _ => {
832 }
834 }
835 }
836 }
837
838 if !responses.is_empty() {
839 return Ok(NetworkResponse::SendMessages(responses));
840 }
841 }
842
843 Ok(NetworkResponse::Ok)
844}
845
846fn process_getheaders_message(
848 getheaders: &GetHeadersMessage,
849 chain_access: Option<&dyn ChainStateAccess>,
850 config: &ProtocolConfig,
851) -> Result<NetworkResponse> {
852 if getheaders.block_locator_hashes.len() > config.validation.max_locator_hashes {
854 return Ok(NetworkResponse::Reject(format!(
855 "Too many locator hashes (max {})",
856 config.validation.max_locator_hashes
857 )));
858 }
859
860 if let Some(chain) = chain_access {
862 let headers =
863 chain.get_headers_for_locator(&getheaders.block_locator_hashes, &getheaders.hash_stop);
864 return Ok(NetworkResponse::SendMessage(Box::new(
865 NetworkMessage::Headers(HeadersMessage { headers }),
866 )));
867 }
868
869 Ok(NetworkResponse::Reject("Chain access not available".into()))
870}
871
872fn process_headers_message(
874 headers: &HeadersMessage,
875 config: &ProtocolConfig,
876) -> Result<NetworkResponse> {
877 if headers.headers.len() > config.network_limits.max_headers {
879 return Ok(NetworkResponse::Reject(format!(
880 "Too many headers (max {})",
881 config.network_limits.max_headers
882 )));
883 }
884
885 Ok(NetworkResponse::Ok)
888}
889
890fn process_block_message(
892 engine: &BitcoinProtocolEngine,
893 block: &Block,
894 witnesses: &[Vec<blvm_consensus::segwit::Witness>],
895 utxo_set: Option<&UtxoSet>,
896 height: Option<u64>,
897 config: &ProtocolConfig,
898) -> Result<NetworkResponse> {
899 if block.transactions.len() > config.validation.max_txs_per_block {
901 return Err(crate::error::ProtocolError::MessageTooLarge {
902 size: block.transactions.len(),
903 max: config.validation.max_txs_per_block,
904 });
905 }
906
907 if let (Some(utxos), Some(h)) = (utxo_set, height) {
909 let context = ProtocolValidationContext::new(engine.get_protocol_version(), h)?;
910 let result = engine
912 .validate_block_with_protocol_and_witnesses(block, witnesses, utxos, h, &context)?;
913
914 match result {
915 ValidationResult::Valid => Ok(NetworkResponse::Ok),
916 ValidationResult::Invalid(reason) => {
917 Ok(NetworkResponse::Reject(format!("Invalid block: {reason}")))
918 }
919 }
920 } else {
921 Err(crate::error::ProtocolError::Configuration(
922 "Missing validation context (utxo_set and height required)".into(),
923 ))
924 }
925}
926
927fn process_tx_message(
929 engine: &BitcoinProtocolEngine,
930 tx: &Transaction,
931 height: Option<u64>,
932) -> Result<NetworkResponse> {
933 let context =
935 ProtocolValidationContext::new(engine.get_protocol_version(), height.unwrap_or(0))?;
936 let result = engine.validate_transaction_with_protocol(tx, &context)?;
937
938 match result {
939 ValidationResult::Valid => Ok(NetworkResponse::Ok),
940 ValidationResult::Invalid(reason) => Ok(NetworkResponse::Reject(format!(
941 "Invalid transaction: {reason}"
942 ))),
943 }
944}
945
946fn process_ping_message(
948 ping: &PingMessage,
949 _peer_state: &mut PeerState,
950) -> Result<NetworkResponse> {
951 let pong = NetworkMessage::Pong(PongMessage { nonce: ping.nonce });
952 Ok(NetworkResponse::SendMessage(Box::new(pong)))
953}
954
955fn process_pong_message(pong: &PongMessage, peer_state: &mut PeerState) -> Result<NetworkResponse> {
957 if peer_state.ping_nonce == Some(pong.nonce) {
959 peer_state.ping_nonce = None;
960 peer_state.last_pong = Some(std::time::SystemTime::now());
961 }
962
963 Ok(NetworkResponse::Ok)
964}
965
966fn process_mempool_message(chain_access: Option<&dyn ChainStateAccess>) -> Result<NetworkResponse> {
968 if let Some(chain) = chain_access {
970 let mempool_txs = chain.get_mempool_transactions();
971 let mut responses = Vec::with_capacity(mempool_txs.len());
972
973 for tx in mempool_txs {
974 responses.push(NetworkMessage::Tx(Arc::new(tx)));
975 }
976
977 if !responses.is_empty() {
978 return Ok(NetworkResponse::SendMessages(responses));
979 }
980 }
981
982 Ok(NetworkResponse::Ok)
983}
984
985fn process_feefilter_message(
987 feefilter: &FeeFilterMessage,
988 peer_state: &mut PeerState,
989) -> Result<NetworkResponse> {
990 peer_state.min_fee_rate = Some(feefilter.feerate);
991 Ok(NetworkResponse::Ok)
992}
993
994fn process_getblocks_message(
996 getblocks: &GetBlocksMessage,
997 chain_access: Option<&dyn ChainStateAccess>,
998 config: &ProtocolConfig,
999) -> Result<NetworkResponse> {
1000 if getblocks.block_locator_hashes.len() > config.validation.max_locator_hashes {
1002 return Ok(NetworkResponse::Reject(format!(
1003 "Too many locator hashes (max {})",
1004 config.validation.max_locator_hashes
1005 )));
1006 }
1007
1008 if let Some(chain) = chain_access {
1012 let mut inventory = Vec::with_capacity(getblocks.block_locator_hashes.len());
1014 for hash in &getblocks.block_locator_hashes {
1015 if chain.has_object(hash) {
1016 inventory.push(InventoryVector {
1017 inv_type: 2, hash: *hash,
1019 });
1020 }
1021 }
1022
1023 if !inventory.is_empty() {
1024 return Ok(NetworkResponse::SendMessage(Box::new(NetworkMessage::Inv(
1025 InvMessage { inventory },
1026 ))));
1027 }
1028 }
1029
1030 Ok(NetworkResponse::Ok)
1031}
1032
1033fn process_getaddr_message(
1035 peer_state: &mut PeerState,
1036 config: &ProtocolConfig,
1037) -> Result<NetworkResponse> {
1038 if !peer_state.known_addresses.is_empty() {
1040 let max_addrs = config
1042 .network_limits
1043 .max_addr_addresses
1044 .min(peer_state.known_addresses.len());
1045 let mut addresses = Vec::with_capacity(max_addrs);
1046 addresses.extend(peer_state.known_addresses.iter().take(max_addrs).cloned());
1047
1048 return Ok(NetworkResponse::SendMessage(Box::new(
1049 NetworkMessage::Addr(AddrMessage { addresses }),
1050 )));
1051 }
1052
1053 Ok(NetworkResponse::Ok)
1054}
1055
1056fn process_notfound_message(
1058 notfound: &NotFoundMessage,
1059 config: &ProtocolConfig,
1060) -> Result<NetworkResponse> {
1061 if notfound.inventory.len() > config.network_limits.max_inv_items {
1063 return Ok(NetworkResponse::Reject(format!(
1064 "Too many notfound items (max {})",
1065 config.network_limits.max_inv_items
1066 )));
1067 }
1068
1069 Ok(NetworkResponse::Ok)
1071}
1072
1073fn process_reject_message(
1075 reject: &RejectMessage,
1076 _config: &ProtocolConfig,
1077) -> Result<NetworkResponse> {
1078 if reject.message.len() > 12 {
1081 return Ok(NetworkResponse::Reject(
1082 "Invalid reject message name".into(),
1083 ));
1084 }
1085
1086 if reject.reason.len() > 111 {
1089 return Ok(NetworkResponse::Reject("Reject reason too long".into()));
1090 }
1091
1092 Ok(NetworkResponse::Ok)
1095}
1096
1097fn process_sendheaders_message(_peer_state: &mut PeerState) -> Result<NetworkResponse> {
1099 Ok(NetworkResponse::Ok)
1103}
1104
1105fn process_sendcmpct_message(
1107 sendcmpct: &SendCmpctMessage,
1108 _peer_state: &mut PeerState,
1109 config: &ProtocolConfig,
1110) -> Result<NetworkResponse> {
1111 let valid_versions = [1, 2];
1113 if !valid_versions.contains(&sendcmpct.version) {
1114 return Ok(NetworkResponse::Reject(
1115 "Invalid compact block version".into(),
1116 ));
1117 }
1118
1119 if !config.compact_blocks.enabled {
1121 return Ok(NetworkResponse::Reject("Compact blocks not enabled".into()));
1122 }
1123
1124 let _ = (sendcmpct.version, sendcmpct.prefer_cmpct);
1127 Ok(NetworkResponse::Ok)
1128}
1129
1130fn process_cmpctblock_message(_cmpctblock: &CmpctBlockMessage) -> Result<NetworkResponse> {
1132 Ok(NetworkResponse::Ok)
1135}
1136
1137fn process_getblocktxn_message(
1139 getblocktxn: &GetBlockTxnMessage,
1140 chain_access: Option<&dyn ChainStateAccess>,
1141 config: &ProtocolConfig,
1142) -> Result<NetworkResponse> {
1143 if getblocktxn.indices.len() > config.compact_blocks.max_blocktxn_indices {
1145 return Ok(NetworkResponse::Reject(format!(
1146 "Too many transaction indices (max {})",
1147 config.compact_blocks.max_blocktxn_indices
1148 )));
1149 }
1150
1151 if let Some(chain) = chain_access {
1153 let mut transactions = Vec::new();
1154 for &index in &getblocktxn.indices {
1155 if let Some(obj) = chain.get_object(&getblocktxn.block_hash) {
1158 if let Some(block) = obj.as_block() {
1159 if (index as usize) < block.transactions.len() {
1160 transactions.push(block.transactions[index as usize].clone());
1161 }
1162 }
1163 }
1164 }
1165
1166 if !transactions.is_empty() {
1167 return Ok(NetworkResponse::SendMessage(Box::new(
1168 NetworkMessage::BlockTxn(BlockTxnMessage {
1169 block_hash: getblocktxn.block_hash,
1170 transactions,
1171 witnesses: None, }),
1173 )));
1174 }
1175 }
1176
1177 Ok(NetworkResponse::Ok)
1178}
1179
1180fn process_blocktxn_message(_blocktxn: &BlockTxnMessage) -> Result<NetworkResponse> {
1182 Ok(NetworkResponse::Ok)
1185}
1186
1187#[cfg(feature = "utxo-commitments")]
1188fn process_getutxoset_message(_getutxoset: &commons::GetUTXOSetMessage) -> Result<NetworkResponse> {
1190 Ok(NetworkResponse::Ok)
1193}
1194
1195#[cfg(feature = "utxo-commitments")]
1196fn process_utxoset_message(_utxoset: &commons::UTXOSetMessage) -> Result<NetworkResponse> {
1198 Ok(NetworkResponse::Ok)
1201}
1202
1203#[cfg(feature = "utxo-commitments")]
1204fn process_getfilteredblock_message(
1206 _getfiltered: &commons::GetFilteredBlockMessage,
1207) -> Result<NetworkResponse> {
1208 Ok(NetworkResponse::Ok)
1211}
1212
1213#[cfg(feature = "utxo-commitments")]
1214fn process_filteredblock_message(
1216 _filtered: &commons::FilteredBlockMessage,
1217) -> Result<NetworkResponse> {
1218 Ok(NetworkResponse::Ok)
1221}
1222
1223fn process_getbanlist_message(_getbanlist: &commons::GetBanListMessage) -> Result<NetworkResponse> {
1225 Ok(NetworkResponse::Ok)
1228}
1229
1230fn process_banlist_message(_banlist: &commons::BanListMessage) -> Result<NetworkResponse> {
1232 Ok(NetworkResponse::Ok)
1235}