#[cfg(feature = "protocol-verification")]
use blvm_spec_lock::spec_locked;
use crate::wire::{
deserialize_addrv2, deserialize_cmpctblock, deserialize_feefilter, deserialize_getblocks,
deserialize_getdata, deserialize_getheaders, deserialize_headers, deserialize_inv,
deserialize_notfound, deserialize_reject, deserialize_sendcmpct, deserialize_tx,
serialize_addrv2, serialize_cmpctblock, serialize_feefilter, serialize_getblocks,
serialize_getdata, serialize_getheaders, serialize_inv, serialize_notfound, serialize_reject,
serialize_sendcmpct, serialize_tx,
};
use crate::{BlockHeader, Hash, Transaction};
use anyhow::Result;
use serde::{Deserialize, Serialize};
pub use crate::p2p_framing::{
BITCOIN_MAGIC_MAINNET, BITCOIN_MAGIC_REGTEST, BITCOIN_MAGIC_TESTNET,
BITCOIN_P2P_MAGIC_MAINNET_LE, MAX_ADDR_TO_SEND, MAX_HEADERS_RESULTS, MAX_INV_SZ,
MAX_PROTOCOL_MESSAGE_LENGTH,
};
pub use crate::service_flags::commons::{
NODE_BAN_LIST_SHARING, NODE_FIBRE, NODE_GOVERNANCE, NODE_PACKAGE_RELAY,
};
#[cfg(feature = "dandelion")]
pub use crate::service_flags::commons::NODE_DANDELION;
#[cfg(feature = "utxo-commitments")]
pub use crate::service_flags::commons::NODE_UTXO_COMMITMENTS;
#[cfg(feature = "erlay")]
pub use crate::service_flags::commons::NODE_ERLAY;
pub use crate::p2p_commands::cmd;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ProtocolMessage {
Version(VersionMessage),
Verack,
Ping(PingMessage),
Pong(PongMessage),
GetHeaders(GetHeadersMessage),
Headers(HeadersMessage),
GetBlocks(GetBlocksMessage),
Block(BlockMessage),
GetData(GetDataMessage),
Inv(InvMessage),
NotFound(NotFoundMessage),
Reject(RejectMessage),
Tx(TxMessage),
FeeFilter(FeeFilterMessage),
MemPool,
SendHeaders,
SendCmpct(SendCmpctMessage),
CmpctBlock(CompactBlockMessage),
GetBlockTxn(GetBlockTxnMessage),
BlockTxn(BlockTxnMessage),
GetUTXOSet(GetUTXOSetMessage),
UTXOSet(UTXOSetMessage),
GetUTXOProof(GetUTXOProofMessage),
UTXOProof(UTXOProofMessage),
GetFilteredBlock(GetFilteredBlockMessage),
FilteredBlock(FilteredBlockMessage),
GetCfilters(GetCfiltersMessage),
Cfilter(CfilterMessage),
GetCfheaders(GetCfheadersMessage),
Cfheaders(CfheadersMessage),
GetCfcheckpt(GetCfcheckptMessage),
Cfcheckpt(CfcheckptMessage),
GetPaymentRequest(GetPaymentRequestMessage),
PaymentRequest(PaymentRequestMessage),
Payment(PaymentMessage),
PaymentACK(PaymentACKMessage),
#[cfg(feature = "ctv")]
PaymentProof(PaymentProofMessage),
SettlementNotification(SettlementNotificationMessage),
SendPkgTxn(SendPkgTxnMessage),
PkgTxn(PkgTxnMessage),
PkgTxnReject(PkgTxnRejectMessage),
GetBanList(GetBanListMessage),
BanList(BanListMessage),
MeshPacket(Vec<u8>), GetAddr,
Addr(AddrMessage),
AddrV2(AddrV2Message),
GetModule(GetModuleMessage),
Module(ModuleMessage),
GetModuleByHash(GetModuleByHashMessage),
ModuleByHash(ModuleByHashMessage),
ModuleInv(ModuleInvMessage),
GetModuleList(GetModuleListMessage),
ModuleList(ModuleListMessage),
#[cfg(feature = "erlay")]
SendTxRcncl(SendTxRcnclMessage),
#[cfg(feature = "erlay")]
ReqRecon(ReqReconMessage),
#[cfg(feature = "erlay")]
ReqSkt(ReqSktMessage),
#[cfg(feature = "erlay")]
Sketch(SketchMessage),
}
pub use crate::network::NetworkAddress;
pub use crate::{
BlockMessage, CompactBlockMessage, FilterPreferences, GetFilteredBlockMessage,
GetUTXOProofMessage, GetUTXOSetMessage, TxMessage, UTXOCommitment, UTXOProofMessage,
UTXOSetMessage,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct VersionMessage {
pub version: i32,
pub services: u64,
pub timestamp: i64,
pub addr_recv: NetworkAddress,
pub addr_from: NetworkAddress,
pub nonce: u64,
pub user_agent: String,
pub start_height: i32,
pub relay: bool,
}
impl VersionMessage {
#[cfg(feature = "utxo-commitments")]
pub fn supports_utxo_commitments(&self) -> bool {
(self.services & NODE_UTXO_COMMITMENTS) != 0
}
pub fn supports_ban_list_sharing(&self) -> bool {
(self.services & NODE_BAN_LIST_SHARING) != 0
}
pub fn supports_compact_filters(&self) -> bool {
use crate::bip157::NODE_COMPACT_FILTERS;
(self.services & NODE_COMPACT_FILTERS) != 0
}
pub fn supports_package_relay(&self) -> bool {
(self.services & NODE_PACKAGE_RELAY) != 0
}
pub fn supports_fibre(&self) -> bool {
(self.services & NODE_FIBRE) != 0
}
#[cfg(feature = "dandelion")]
pub fn supports_dandelion(&self) -> bool {
(self.services & NODE_DANDELION) != 0
}
}
pub use crate::network::{
AddrMessage, AddrV2Message, BlockTxnMessage, FeeFilterMessage, GetBlockTxnMessage,
GetBlocksMessage, GetDataMessage, GetHeadersMessage, HeadersMessage, InvMessage,
InventoryVector, NotFoundMessage, PingMessage, PongMessage, RejectMessage, SendCmpctMessage,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilteredBlockMessage {
pub request_id: u64,
pub header: BlockHeader,
pub commitment: UTXOCommitment,
pub transactions: Vec<Transaction>,
pub transaction_indices: Vec<u32>,
pub spam_summary: SpamSummary,
pub bip158_filter: Option<Bip158FilterData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Bip158FilterData {
pub filter_type: u8,
pub filter_data: Vec<u8>,
pub num_elements: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GetCfiltersMessage {
pub filter_type: u8,
pub start_height: u32,
pub stop_hash: Hash,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CfilterMessage {
pub filter_type: u8,
pub block_hash: Hash,
pub filter_data: Vec<u8>,
pub num_elements: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetCfheadersMessage {
pub filter_type: u8,
pub start_height: u32,
pub stop_hash: Hash,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CfheadersMessage {
pub filter_type: u8,
pub stop_hash: Hash,
pub prev_header: FilterHeaderData,
pub filter_headers: Vec<Hash>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FilterHeaderData {
pub filter_hash: Hash,
pub prev_header_hash: Hash,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetCfcheckptMessage {
pub filter_type: u8,
pub stop_hash: Hash,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CfcheckptMessage {
pub filter_type: u8,
pub stop_hash: Hash,
pub filter_header_hashes: Vec<Hash>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetPaymentRequestMessage {
#[serde(with = "serde_bytes")]
pub merchant_pubkey: Vec<u8>,
#[serde(with = "serde_bytes")]
pub payment_id: Vec<u8>,
pub network: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentRequestMessage {
pub payment_request: crate::payment::PaymentRequest,
#[serde(with = "serde_bytes")]
pub merchant_signature: Vec<u8>,
#[serde(with = "serde_bytes")]
pub merchant_pubkey: Vec<u8>,
#[serde(with = "serde_bytes")]
pub payment_id: Vec<u8>,
#[cfg(feature = "ctv")]
#[serde(default)]
pub covenant_proof: Option<crate::payment::CovenantProof>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentMessage {
pub payment: crate::payment::Payment,
#[serde(with = "serde_bytes")]
pub payment_id: Vec<u8>,
#[serde(with = "serde_bytes")]
pub customer_signature: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentACKMessage {
pub payment_ack: crate::payment::PaymentACK,
#[serde(with = "serde_bytes")]
pub payment_id: Vec<u8>,
#[serde(with = "serde_bytes")]
pub merchant_signature: Vec<u8>,
}
#[cfg(feature = "ctv")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentProofMessage {
pub request_id: u64,
pub payment_request_id: String,
pub covenant_proof: crate::payment::CovenantProof,
pub transaction_template: Option<crate::payment::TransactionTemplate>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SettlementNotificationMessage {
pub payment_request_id: String,
pub transaction_hash: Option<Hash>,
pub confirmation_count: u32,
pub block_hash: Option<Hash>,
pub status: String, }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SendPkgTxnMessage {
#[serde(with = "serde_bytes")]
pub package_id: Vec<u8>,
pub tx_hashes: Vec<Hash>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PkgTxnMessage {
#[serde(with = "serde_bytes")]
pub package_id: Vec<u8>,
pub transactions: Vec<Vec<u8>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PkgTxnRejectMessage {
#[serde(with = "serde_bytes")]
pub package_id: Vec<u8>,
pub reason: u8,
pub reason_text: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetModuleMessage {
pub request_id: u64,
pub name: String,
pub version: Option<String>,
pub payment_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleMessage {
pub request_id: u64,
pub name: String,
pub version: String,
pub hash: Hash,
pub manifest_hash: Hash,
pub binary_hash: Hash,
pub manifest: Vec<u8>,
pub binary: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetModuleByHashMessage {
pub request_id: u64,
pub hash: Hash,
pub include_binary: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleByHashMessage {
pub request_id: u64,
pub hash: Hash,
pub manifest: Vec<u8>,
pub binary: Option<Vec<u8>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInvMessage {
pub modules: Vec<ModuleInventoryItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleInventoryItem {
pub name: String,
pub version: String,
pub hash: Hash,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetModuleListMessage {
pub name_prefix: Option<String>,
pub max_count: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModuleListMessage {
pub modules: Vec<ModuleInventoryItem>,
}
#[cfg(feature = "erlay")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SendTxRcnclMessage {
pub version: u16,
#[serde(with = "serde_bytes")]
pub salt: [u8; 16],
pub min_field_size: u8,
pub max_field_size: u8,
}
#[cfg(feature = "erlay")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReqReconMessage {
#[serde(with = "serde_bytes")]
pub salt: [u8; 16],
pub local_set_size: u32,
pub field_size: u8,
}
#[cfg(feature = "erlay")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ReqSktMessage {
#[serde(with = "serde_bytes")]
pub salt: [u8; 16],
pub remote_set_size: u32,
pub field_size: u8,
}
#[cfg(feature = "erlay")]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SketchMessage {
#[serde(with = "serde_bytes")]
pub salt: [u8; 16],
#[serde(with = "serde_bytes")]
pub sketch: Vec<u8>,
pub field_size: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpamSummary {
pub filtered_count: u32,
pub filtered_size: u64,
pub by_type: SpamBreakdown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpamBreakdown {
pub ordinals: u32,
pub inscriptions: u32,
pub dust: u32,
pub brc20: u32,
}
pub struct TcpFramedParser;
impl TcpFramedParser {
#[cfg_attr(feature = "protocol-verification", spec_locked("10.1.1"))]
pub fn parse_message(data: &[u8], allowed_commands: &[&str]) -> Result<ProtocolMessage> {
use tracing::{debug, warn};
if data.len() >= 4 {
let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
debug!(
"Parsing message: magic=0x{:08x}, total_len={}",
magic,
data.len()
);
if magic != BITCOIN_P2P_MAGIC_MAINNET_LE {
let header_hex: String = data.iter().take(24).map(|b| format!("{b:02x}")).collect();
warn!(
"Invalid magic number 0x{:08x}, expected 0x{:08x}. Header hex: {}",
magic, BITCOIN_P2P_MAGIC_MAINNET_LE, header_hex
);
}
}
let (command, payload) =
crate::p2p_frame::parse_p2p_frame(data, BITCOIN_P2P_MAGIC_MAINNET_LE, |c| {
allowed_commands.contains(&c)
})
.map_err(|e| anyhow::anyhow!("{}", e))?;
debug!("Message command: '{}', data_len={}", command, data.len());
match command {
cmd::VERSION => {
use crate::wire::deserialize_version;
let version_msg = deserialize_version(payload)?;
Ok(ProtocolMessage::Version(VersionMessage {
version: version_msg.version as i32, services: version_msg.services,
timestamp: version_msg.timestamp,
addr_recv: version_msg.addr_recv,
addr_from: version_msg.addr_from,
nonce: version_msg.nonce,
user_agent: version_msg.user_agent,
start_height: version_msg.start_height,
relay: version_msg.relay,
}))
}
cmd::VERACK => Ok(ProtocolMessage::Verack),
cmd::SENDHEADERS => Ok(ProtocolMessage::SendHeaders),
cmd::PING => {
let wire_msg = crate::wire::deserialize_ping(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize ping: {}", e))?;
Ok(ProtocolMessage::Ping(PingMessage {
nonce: wire_msg.nonce,
}))
}
cmd::PONG => {
let wire_msg = crate::wire::deserialize_pong(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize pong: {}", e))?;
Ok(ProtocolMessage::Pong(PongMessage {
nonce: wire_msg.nonce,
}))
}
cmd::GETHEADERS => {
let wire_msg = deserialize_getheaders(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize getheaders: {}", e))?;
Ok(ProtocolMessage::GetHeaders(wire_msg))
}
cmd::HEADERS => {
let wire_msg = deserialize_headers(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize headers: {}", e))?;
Ok(ProtocolMessage::Headers(wire_msg))
}
cmd::GETBLOCKS => {
let wire_msg = deserialize_getblocks(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize getblocks: {}", e))?;
Ok(ProtocolMessage::GetBlocks(wire_msg))
}
cmd::BLOCK => {
let (block, witnesses) =
crate::serialization::deserialize_block_with_witnesses(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize block: {}", e))?;
Ok(ProtocolMessage::Block(BlockMessage { block, witnesses }))
}
cmd::GETDATA => {
let msg = deserialize_getdata(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize getdata: {}", e))?;
Ok(ProtocolMessage::GetData(msg))
}
cmd::INV => {
let msg = deserialize_inv(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize inv: {}", e))?;
Ok(ProtocolMessage::Inv(msg))
}
cmd::NOTFOUND => {
let msg = deserialize_notfound(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize notfound: {}", e))?;
Ok(ProtocolMessage::NotFound(msg))
}
cmd::REJECT => {
let msg = deserialize_reject(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize reject: {}", e))?;
Ok(ProtocolMessage::Reject(msg))
}
cmd::TX => {
let transaction = deserialize_tx(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize tx: {}", e))?;
Ok(ProtocolMessage::Tx(TxMessage { transaction }))
}
cmd::MEMPOOL => Ok(ProtocolMessage::MemPool),
cmd::FEEFILTER => {
let msg = deserialize_feefilter(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize feefilter: {}", e))?;
Ok(ProtocolMessage::FeeFilter(msg))
}
cmd::SENDCMPCT => {
let msg = deserialize_sendcmpct(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize sendcmpct: {}", e))?;
Ok(ProtocolMessage::SendCmpct(msg))
}
cmd::CMPCTBLOCK => {
let wire_msg = deserialize_cmpctblock(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize cmpctblock: {}", e))?;
let compact_block = crate::bip152::CompactBlock::from(&wire_msg);
Ok(ProtocolMessage::CmpctBlock(CompactBlockMessage {
compact_block,
}))
}
cmd::GETBLOCKTXN => {
let wire_msg = crate::wire::deserialize_getblocktxn(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize getblocktxn: {}", e))?;
Ok(ProtocolMessage::GetBlockTxn(wire_msg))
}
cmd::BLOCKTXN => {
let wire_msg = crate::wire::deserialize_blocktxn(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize blocktxn: {}", e))?;
Ok(ProtocolMessage::BlockTxn(wire_msg))
}
cmd::GETUTXOSET => Ok(ProtocolMessage::GetUTXOSet(bincode::deserialize(payload)?)),
cmd::UTXOSET => Ok(ProtocolMessage::UTXOSet(bincode::deserialize(payload)?)),
cmd::GETUTXOPROOF => Ok(ProtocolMessage::GetUTXOProof(bincode::deserialize(
payload,
)?)),
cmd::UTXOPROOF => Ok(ProtocolMessage::UTXOProof(bincode::deserialize(payload)?)),
cmd::GETFILTEREDBLOCK => Ok(ProtocolMessage::GetFilteredBlock(bincode::deserialize(
payload,
)?)),
cmd::FILTEREDBLOCK => Ok(ProtocolMessage::FilteredBlock(bincode::deserialize(
payload,
)?)),
cmd::GETCFILTERS => Ok(ProtocolMessage::GetCfilters(bincode::deserialize(payload)?)),
cmd::CFILTER => Ok(ProtocolMessage::Cfilter(bincode::deserialize(payload)?)),
cmd::GETCFHEADERS => Ok(ProtocolMessage::GetCfheaders(bincode::deserialize(
payload,
)?)),
cmd::CFHEADERS => Ok(ProtocolMessage::Cfheaders(bincode::deserialize(payload)?)),
cmd::GETCFCHECKPT => Ok(ProtocolMessage::GetCfcheckpt(bincode::deserialize(
payload,
)?)),
cmd::CFCHECKPT => Ok(ProtocolMessage::Cfcheckpt(bincode::deserialize(payload)?)),
cmd::GETPAYMENTREQUEST => Ok(ProtocolMessage::GetPaymentRequest(bincode::deserialize(
payload,
)?)),
cmd::PAYMENTREQUEST => Ok(ProtocolMessage::PaymentRequest(bincode::deserialize(
payload,
)?)),
cmd::PAYMENT => Ok(ProtocolMessage::Payment(bincode::deserialize(payload)?)),
cmd::PAYMENTACK => Ok(ProtocolMessage::PaymentACK(bincode::deserialize(payload)?)),
#[cfg(feature = "ctv")]
cmd::PAYMENTPROOF => Ok(ProtocolMessage::PaymentProof(bincode::deserialize(
payload,
)?)),
cmd::SETTLEMENTNOTIFICATION => Ok(ProtocolMessage::SettlementNotification(
bincode::deserialize(payload)?,
)),
cmd::SENDPKGTXN => Ok(ProtocolMessage::SendPkgTxn(bincode::deserialize(payload)?)),
cmd::PKGTXN => Ok(ProtocolMessage::PkgTxn(bincode::deserialize(payload)?)),
cmd::PKGTXNREJECT => Ok(ProtocolMessage::PkgTxnReject(bincode::deserialize(
payload,
)?)),
cmd::GETBANLIST => Ok(ProtocolMessage::GetBanList(bincode::deserialize(payload)?)),
cmd::BANLIST => Ok(ProtocolMessage::BanList(bincode::deserialize(payload)?)),
cmd::GETADDR => Ok(ProtocolMessage::GetAddr),
cmd::ADDR => {
let wire_msg = crate::wire::deserialize_addr(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize addr: {}", e))?;
Ok(ProtocolMessage::Addr(wire_msg))
}
cmd::ADDRV2 => {
let wire_msg = deserialize_addrv2(payload)
.map_err(|e| anyhow::anyhow!("Failed to deserialize addrv2: {}", e))?;
Ok(ProtocolMessage::AddrV2(wire_msg))
}
cmd::GETMODULE => Ok(ProtocolMessage::GetModule(bincode::deserialize(payload)?)),
cmd::MODULE => Ok(ProtocolMessage::Module(bincode::deserialize(payload)?)),
cmd::GETMODULEBYHASH => Ok(ProtocolMessage::GetModuleByHash(bincode::deserialize(
payload,
)?)),
cmd::MODULEBYHASH => Ok(ProtocolMessage::ModuleByHash(bincode::deserialize(
payload,
)?)),
cmd::MODULEINV => Ok(ProtocolMessage::ModuleInv(bincode::deserialize(payload)?)),
cmd::GETMODULELIST => Ok(ProtocolMessage::GetModuleList(bincode::deserialize(
payload,
)?)),
cmd::MODULELIST => Ok(ProtocolMessage::ModuleList(bincode::deserialize(payload)?)),
cmd::MESH => Ok(ProtocolMessage::MeshPacket(payload.to_vec())),
#[cfg(feature = "erlay")]
cmd::SENDTXRCNCL => Ok(ProtocolMessage::SendTxRcncl(bincode::deserialize(payload)?)),
#[cfg(feature = "erlay")]
cmd::REQRECON => Ok(ProtocolMessage::ReqRecon(bincode::deserialize(payload)?)),
#[cfg(feature = "erlay")]
cmd::REQSKT => Ok(ProtocolMessage::ReqSkt(bincode::deserialize(payload)?)),
#[cfg(feature = "erlay")]
cmd::SKETCH => Ok(ProtocolMessage::Sketch(bincode::deserialize(payload)?)),
_ => Err(anyhow::anyhow!("Unknown command: {}", command)),
}
}
pub fn serialize_message(message: &ProtocolMessage) -> Result<Vec<u8>> {
let (command, payload) = match message {
ProtocolMessage::Version(msg) => {
use crate::network::VersionMessage as WireVersionMessage;
use crate::wire::serialize_version;
let version_msg = WireVersionMessage {
version: msg.version as u32,
services: msg.services,
timestamp: msg.timestamp,
addr_recv: msg.addr_recv.clone(),
addr_from: msg.addr_from.clone(),
nonce: msg.nonce,
user_agent: msg.user_agent.clone(),
start_height: msg.start_height,
relay: msg.relay,
};
let payload = serialize_version(&version_msg)?;
(cmd::VERSION, payload)
}
ProtocolMessage::Verack => (cmd::VERACK, vec![]),
ProtocolMessage::SendHeaders => (cmd::SENDHEADERS, vec![]),
ProtocolMessage::Ping(msg) => (
cmd::PING,
crate::wire::serialize_ping(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::Pong(msg) => (
cmd::PONG,
crate::wire::serialize_pong(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::GetHeaders(msg) => (
cmd::GETHEADERS,
serialize_getheaders(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::Headers(msg) => (
cmd::HEADERS,
crate::wire::serialize_headers(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::GetBlocks(msg) => (
cmd::GETBLOCKS,
serialize_getblocks(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::Block(msg) => {
let payload = crate::serialization::serialize_block_with_witnesses(
&msg.block,
&msg.witnesses,
true,
);
(cmd::BLOCK, payload)
}
ProtocolMessage::GetData(msg) => (
cmd::GETDATA,
serialize_getdata(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::Inv(msg) => (
cmd::INV,
serialize_inv(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::NotFound(msg) => (
cmd::NOTFOUND,
serialize_notfound(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::Reject(msg) => (
cmd::REJECT,
serialize_reject(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::Tx(msg) => (
cmd::TX,
serialize_tx(&msg.transaction).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::FeeFilter(msg) => (
cmd::FEEFILTER,
serialize_feefilter(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::MemPool => (cmd::MEMPOOL, vec![]),
ProtocolMessage::SendCmpct(msg) => (
cmd::SENDCMPCT,
serialize_sendcmpct(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::CmpctBlock(msg) => {
use crate::network::CmpctBlockMessage;
let wire_msg = CmpctBlockMessage::try_from(msg.compact_block.clone())
.map_err(|e| anyhow::anyhow!("{}", e))?;
let payload =
serialize_cmpctblock(&wire_msg).map_err(|e| anyhow::anyhow!("{}", e))?;
(cmd::CMPCTBLOCK, payload)
}
ProtocolMessage::GetBlockTxn(msg) => (
cmd::GETBLOCKTXN,
crate::wire::serialize_getblocktxn(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::BlockTxn(msg) => (
cmd::BLOCKTXN,
crate::wire::serialize_blocktxn(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::GetUTXOSet(msg) => (cmd::GETUTXOSET, bincode::serialize(msg)?),
ProtocolMessage::UTXOSet(msg) => (cmd::UTXOSET, bincode::serialize(msg)?),
ProtocolMessage::GetUTXOProof(msg) => (cmd::GETUTXOPROOF, bincode::serialize(msg)?),
ProtocolMessage::UTXOProof(msg) => (cmd::UTXOPROOF, bincode::serialize(msg)?),
ProtocolMessage::GetFilteredBlock(msg) => {
(cmd::GETFILTEREDBLOCK, bincode::serialize(msg)?)
}
ProtocolMessage::FilteredBlock(msg) => (cmd::FILTEREDBLOCK, bincode::serialize(msg)?),
ProtocolMessage::GetCfilters(msg) => (cmd::GETCFILTERS, bincode::serialize(msg)?),
ProtocolMessage::Cfilter(msg) => (cmd::CFILTER, bincode::serialize(msg)?),
ProtocolMessage::GetCfheaders(msg) => (cmd::GETCFHEADERS, bincode::serialize(msg)?),
ProtocolMessage::Cfheaders(msg) => (cmd::CFHEADERS, bincode::serialize(msg)?),
ProtocolMessage::GetCfcheckpt(msg) => (cmd::GETCFCHECKPT, bincode::serialize(msg)?),
ProtocolMessage::Cfcheckpt(msg) => (cmd::CFCHECKPT, bincode::serialize(msg)?),
ProtocolMessage::GetPaymentRequest(msg) => {
(cmd::GETPAYMENTREQUEST, bincode::serialize(msg)?)
}
ProtocolMessage::PaymentRequest(msg) => (cmd::PAYMENTREQUEST, bincode::serialize(msg)?),
ProtocolMessage::Payment(msg) => (cmd::PAYMENT, bincode::serialize(msg)?),
ProtocolMessage::PaymentACK(msg) => (cmd::PAYMENTACK, bincode::serialize(msg)?),
#[cfg(feature = "ctv")]
ProtocolMessage::PaymentProof(msg) => (cmd::PAYMENTPROOF, bincode::serialize(msg)?),
ProtocolMessage::SettlementNotification(msg) => {
(cmd::SETTLEMENTNOTIFICATION, bincode::serialize(msg)?)
}
ProtocolMessage::SendPkgTxn(msg) => (cmd::SENDPKGTXN, bincode::serialize(msg)?),
ProtocolMessage::PkgTxn(msg) => (cmd::PKGTXN, bincode::serialize(msg)?),
ProtocolMessage::PkgTxnReject(msg) => (cmd::PKGTXNREJECT, bincode::serialize(msg)?),
ProtocolMessage::GetBanList(msg) => (cmd::GETBANLIST, bincode::serialize(msg)?),
ProtocolMessage::BanList(msg) => (cmd::BANLIST, bincode::serialize(msg)?),
ProtocolMessage::GetAddr => (cmd::GETADDR, vec![]),
ProtocolMessage::Addr(msg) => (
cmd::ADDR,
crate::wire::serialize_addr(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::AddrV2(msg) => (
cmd::ADDRV2,
serialize_addrv2(msg).map_err(|e| anyhow::anyhow!("{}", e))?,
),
ProtocolMessage::GetModule(msg) => (cmd::GETMODULE, bincode::serialize(msg)?),
ProtocolMessage::Module(msg) => (cmd::MODULE, bincode::serialize(msg)?),
ProtocolMessage::GetModuleByHash(msg) => {
(cmd::GETMODULEBYHASH, bincode::serialize(msg)?)
}
ProtocolMessage::ModuleByHash(msg) => (cmd::MODULEBYHASH, bincode::serialize(msg)?),
ProtocolMessage::ModuleInv(msg) => (cmd::MODULEINV, bincode::serialize(msg)?),
ProtocolMessage::GetModuleList(msg) => (cmd::GETMODULELIST, bincode::serialize(msg)?),
ProtocolMessage::ModuleList(msg) => (cmd::MODULELIST, bincode::serialize(msg)?),
ProtocolMessage::MeshPacket(_) => {
return Err(anyhow::anyhow!("MeshPacket handled separately"))
}
#[cfg(feature = "erlay")]
ProtocolMessage::SendTxRcncl(msg) => (cmd::SENDTXRCNCL, bincode::serialize(msg)?),
#[cfg(feature = "erlay")]
ProtocolMessage::ReqRecon(msg) => (cmd::REQRECON, bincode::serialize(msg)?),
#[cfg(feature = "erlay")]
ProtocolMessage::ReqSkt(msg) => (cmd::REQSKT, bincode::serialize(msg)?),
#[cfg(feature = "erlay")]
ProtocolMessage::Sketch(msg) => (cmd::SKETCH, bincode::serialize(msg)?),
};
crate::p2p_frame::build_p2p_frame(BITCOIN_MAGIC_MAINNET, command, &payload)
.map_err(|e| anyhow::anyhow!("{}", e))
}
#[cfg_attr(feature = "protocol-verification", spec_locked("10.1.1"))]
pub fn calculate_checksum(payload: &[u8]) -> [u8; 4] {
crate::p2p_frame::bitcoin_p2p_payload_checksum(payload)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetBanListMessage {
pub request_full: bool,
pub min_ban_duration: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BanListMessage {
pub is_full: bool,
pub ban_list_hash: Hash,
pub ban_entries: Vec<BanEntry>,
pub timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BanEntry {
pub addr: NetworkAddress,
pub unban_timestamp: u64,
pub reason: Option<String>,
}