use crate::error::ProtocolError;
use crate::validation::ProtocolValidationContext;
use crate::{BitcoinProtocolEngine, ProtocolConfig, Result};
use blvm_consensus::error::ConsensusError;
use blvm_consensus::types::UtxoSet;
use blvm_consensus::types::{Block, BlockHeader, Hash, Transaction, ValidationResult};
use std::sync::Arc;
pub mod commons {
pub use crate::commons::*;
}
#[cfg(feature = "bip324")]
pub mod v2_transport {
pub use crate::v2_transport::*;
}
#[cfg(test)]
mod bip155_tests;
#[derive(Debug, Clone, PartialEq)]
pub enum NetworkMessage {
Version(VersionMessage),
VerAck,
Addr(AddrMessage),
AddrV2(AddrV2Message), Inv(InvMessage),
GetData(GetDataMessage),
GetHeaders(GetHeadersMessage),
Headers(HeadersMessage),
Block(Arc<Block>),
Tx(Arc<Transaction>),
Ping(PingMessage),
Pong(PongMessage),
MemPool,
FeeFilter(FeeFilterMessage),
GetBlocks(GetBlocksMessage),
GetAddr,
NotFound(NotFoundMessage),
Reject(RejectMessage),
SendHeaders,
SendCmpct(SendCmpctMessage),
CmpctBlock(CmpctBlockMessage),
GetBlockTxn(GetBlockTxnMessage),
BlockTxn(BlockTxnMessage),
#[cfg(feature = "utxo-commitments")]
GetUTXOSet(commons::GetUTXOSetMessage),
#[cfg(feature = "utxo-commitments")]
UTXOSet(commons::UTXOSetMessage),
#[cfg(feature = "utxo-commitments")]
GetFilteredBlock(commons::GetFilteredBlockMessage),
#[cfg(feature = "utxo-commitments")]
FilteredBlock(commons::FilteredBlockMessage),
GetBanList(commons::GetBanListMessage),
BanList(commons::BanListMessage),
EconomicNodeRegistration(commons::EconomicNodeRegistrationMessage),
EconomicNodeVeto(commons::EconomicNodeVetoMessage),
EconomicNodeStatus(commons::EconomicNodeStatusMessage),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersionMessage {
pub version: u32,
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,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrMessage {
pub addresses: Vec<NetworkAddress>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct InvMessage {
pub inventory: Vec<InventoryVector>,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct GetDataMessage {
pub inventory: Vec<InventoryVector>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GetHeadersMessage {
pub version: u32,
pub block_locator_hashes: Vec<Hash>,
pub hash_stop: Hash,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeadersMessage {
pub headers: Vec<BlockHeader>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PingMessage {
pub nonce: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PongMessage {
pub nonce: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FeeFilterMessage {
pub feerate: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GetBlocksMessage {
pub version: u32,
pub block_locator_hashes: Vec<Hash>,
pub hash_stop: Hash,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct NotFoundMessage {
pub inventory: Vec<InventoryVector>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RejectMessage {
pub message: String, pub code: u8, pub reason: String, pub extra_data: Option<Hash>, }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SendCmpctMessage {
pub version: u64,
pub prefer_cmpct: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CmpctBlockMessage {
pub header: BlockHeader,
pub nonce: u64,
pub short_ids: Vec<[u8; 6]>,
pub prefilled_txs: Vec<PrefilledTransaction>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrefilledTransaction {
pub index: u16,
pub tx: Transaction,
pub witness: Option<Vec<blvm_consensus::segwit::Witness>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GetBlockTxnMessage {
pub block_hash: Hash,
pub indices: Vec<u16>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockTxnMessage {
pub block_hash: Hash,
pub transactions: Vec<Transaction>,
pub witnesses: Option<Vec<Vec<blvm_consensus::segwit::Witness>>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NetworkAddress {
pub services: u64,
pub ip: [u8; 16], pub port: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum AddressType {
IPv4 = 1,
IPv6 = 2,
TorV2 = 3,
TorV3 = 4,
I2P = 5,
CJDNS = 6,
}
impl AddressType {
pub fn address_length(&self) -> usize {
match self {
AddressType::IPv4 => 4,
AddressType::IPv6 => 16,
AddressType::TorV2 => 10,
AddressType::TorV3 => 32,
AddressType::I2P => 32,
AddressType::CJDNS => 16,
}
}
pub fn from_u8(value: u8) -> Option<Self> {
match value {
1 => Some(AddressType::IPv4),
2 => Some(AddressType::IPv6),
3 => Some(AddressType::TorV2),
4 => Some(AddressType::TorV3),
5 => Some(AddressType::I2P),
6 => Some(AddressType::CJDNS),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AddrV2Message {
pub addresses: Vec<NetworkAddressV2>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NetworkAddressV2 {
pub time: u32,
pub services: u64,
pub address_type: AddressType,
pub address: Vec<u8>, pub port: u16,
}
impl NetworkAddressV2 {
pub fn new(
time: u32,
services: u64,
address_type: AddressType,
address: Vec<u8>,
port: u16,
) -> Result<Self> {
let expected_len = address_type.address_length();
if address.len() != expected_len {
return Err(ProtocolError::Consensus(ConsensusError::Serialization(
std::borrow::Cow::Owned(format!(
"Invalid address length for type {:?}: expected {}, got {}",
address_type,
expected_len,
address.len()
)),
)));
}
Ok(Self {
time,
services,
address_type,
address,
port,
})
}
pub fn to_legacy(&self) -> Option<NetworkAddress> {
match self.address_type {
AddressType::IPv4 => {
if self.address.len() == 4 {
let mut ipv6 = [0u8; 16];
ipv6[10] = 0xff;
ipv6[11] = 0xff;
ipv6[12..16].copy_from_slice(&self.address);
Some(NetworkAddress {
services: self.services,
ip: ipv6,
port: self.port,
})
} else {
None
}
}
AddressType::IPv6 => {
if self.address.len() == 16 {
let mut ipv6 = [0u8; 16];
ipv6.copy_from_slice(&self.address);
Some(NetworkAddress {
services: self.services,
ip: ipv6,
port: self.port,
})
} else {
None
}
}
_ => None, }
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct InventoryVector {
pub inv_type: u32,
pub hash: Hash,
}
#[derive(Debug, Clone)]
pub enum NetworkResponse {
Ok,
SendMessage(Box<NetworkMessage>),
SendMessages(Vec<NetworkMessage>),
Reject(String),
}
#[derive(Clone)]
pub struct PeerState {
pub version: u32,
pub services: u64,
pub user_agent: String,
pub start_height: i32,
pub handshake_complete: bool,
pub known_addresses: Vec<NetworkAddress>,
pub ping_nonce: Option<u64>,
pub last_pong: Option<std::time::SystemTime>,
pub min_fee_rate: Option<u64>,
#[cfg(feature = "bip324")]
pub v2_transport: Option<std::sync::Arc<crate::v2_transport::V2Transport>>,
#[cfg(feature = "bip324")]
pub v2_handshake: Option<std::sync::Arc<crate::v2_transport::V2Handshake>>,
}
impl PeerState {
pub fn new() -> Self {
Self {
version: 0,
services: 0,
user_agent: String::new(),
start_height: 0,
handshake_complete: false,
known_addresses: Vec::new(),
ping_nonce: None,
last_pong: None,
min_fee_rate: None,
#[cfg(feature = "bip324")]
v2_transport: None,
#[cfg(feature = "bip324")]
v2_handshake: None,
}
}
#[cfg(feature = "bip324")]
pub fn supports_v2_transport(&self) -> bool {
use crate::service_flags::{has_flag, standard};
has_flag(self.services, standard::NODE_V2_TRANSPORT)
}
#[cfg(feature = "bip324")]
pub fn is_v2_transport_active(&self) -> bool {
self.v2_transport.is_some()
}
}
impl Default for PeerState {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub enum ChainObject {
Block(Arc<Block>),
Transaction(Arc<Transaction>),
}
impl ChainObject {
pub fn as_block(&self) -> Option<&Arc<Block>> {
match self {
ChainObject::Block(block) => Some(block),
_ => None,
}
}
pub fn as_transaction(&self) -> Option<&Arc<Transaction>> {
match self {
ChainObject::Transaction(tx) => Some(tx),
_ => None,
}
}
}
pub trait ChainStateAccess {
fn has_object(&self, hash: &Hash) -> bool;
fn get_object(&self, hash: &Hash) -> Option<ChainObject>;
fn get_headers_for_locator(&self, locator: &[Hash], stop: &Hash) -> Vec<BlockHeader>;
fn get_mempool_transactions(&self) -> Vec<Transaction>;
}
pub fn process_network_message(
engine: &BitcoinProtocolEngine,
message: &NetworkMessage,
peer_state: &mut PeerState,
chain_access: Option<&dyn ChainStateAccess>,
utxo_set: Option<&UtxoSet>,
height: Option<u64>,
) -> Result<NetworkResponse> {
let config = engine.get_config();
match message {
NetworkMessage::Version(version) => process_version_message(version, peer_state, config),
NetworkMessage::VerAck => process_verack_message(peer_state),
NetworkMessage::Addr(addr) => process_addr_message(addr, peer_state, config),
NetworkMessage::AddrV2(addrv2) => process_addrv2_message(addrv2, peer_state, config),
NetworkMessage::Inv(inv) => process_inv_message(inv, chain_access, config),
NetworkMessage::GetData(getdata) => process_getdata_message(getdata, chain_access, config),
NetworkMessage::GetHeaders(getheaders) => {
process_getheaders_message(getheaders, chain_access, config)
}
NetworkMessage::Headers(headers) => process_headers_message(headers, config),
NetworkMessage::Block(block) => {
process_block_message(engine, block, utxo_set, height, config)
}
NetworkMessage::Tx(tx) => process_tx_message(engine, tx, height),
NetworkMessage::Ping(ping) => process_ping_message(ping, peer_state),
NetworkMessage::Pong(pong) => process_pong_message(pong, peer_state),
NetworkMessage::MemPool => process_mempool_message(chain_access),
NetworkMessage::FeeFilter(feefilter) => process_feefilter_message(feefilter, peer_state),
NetworkMessage::GetBlocks(getblocks) => {
process_getblocks_message(getblocks, chain_access, config)
}
NetworkMessage::GetAddr => process_getaddr_message(peer_state, config),
NetworkMessage::NotFound(notfound) => process_notfound_message(notfound, config),
NetworkMessage::Reject(reject) => process_reject_message(reject, config),
NetworkMessage::SendHeaders => process_sendheaders_message(peer_state),
NetworkMessage::SendCmpct(sendcmpct) => {
process_sendcmpct_message(sendcmpct, peer_state, config)
}
NetworkMessage::CmpctBlock(cmpctblock) => process_cmpctblock_message(cmpctblock),
NetworkMessage::GetBlockTxn(getblocktxn) => {
process_getblocktxn_message(getblocktxn, chain_access, config)
}
NetworkMessage::BlockTxn(blocktxn) => process_blocktxn_message(blocktxn),
#[cfg(feature = "utxo-commitments")]
NetworkMessage::GetUTXOSet(getutxoset) => process_getutxoset_message(getutxoset),
#[cfg(feature = "utxo-commitments")]
NetworkMessage::UTXOSet(utxoset) => process_utxoset_message(utxoset),
#[cfg(feature = "utxo-commitments")]
NetworkMessage::GetFilteredBlock(getfiltered) => {
process_getfilteredblock_message(getfiltered)
}
#[cfg(feature = "utxo-commitments")]
NetworkMessage::FilteredBlock(filtered) => process_filteredblock_message(filtered),
NetworkMessage::GetBanList(getbanlist) => process_getbanlist_message(getbanlist),
NetworkMessage::BanList(banlist) => process_banlist_message(banlist),
NetworkMessage::EconomicNodeRegistration(_) => Ok(NetworkResponse::Ok),
NetworkMessage::EconomicNodeVeto(_) => Ok(NetworkResponse::Ok),
NetworkMessage::EconomicNodeStatus(_) => Ok(NetworkResponse::Ok),
}
}
fn process_version_message(
version: &VersionMessage,
peer_state: &mut PeerState,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if version.version < 70001 {
return Ok(NetworkResponse::Reject("Version too old".into()));
}
if version.user_agent.len() > config.network_limits.max_user_agent_length {
return Ok(NetworkResponse::Reject(format!(
"User agent too long (max {} bytes)",
config.network_limits.max_user_agent_length
)));
}
peer_state.version = version.version;
peer_state.services = version.services;
peer_state.user_agent = version.user_agent.clone();
peer_state.start_height = version.start_height;
#[cfg(feature = "bip324")]
{
use crate::service_flags::{has_flag, standard};
let peer_supports_v2 = has_flag(version.services, standard::NODE_V2_TRANSPORT);
let we_support_v2 = config.service_flags.node_v2_transport;
if peer_supports_v2 && we_support_v2 {
let handshake = crate::v2_transport::V2Handshake::new_responder();
peer_state.v2_handshake = Some(std::sync::Arc::new(handshake));
}
}
Ok(NetworkResponse::SendMessage(Box::new(
NetworkMessage::VerAck,
)))
}
fn process_verack_message(peer_state: &mut PeerState) -> Result<NetworkResponse> {
peer_state.handshake_complete = true;
Ok(NetworkResponse::Ok)
}
fn process_addr_message(
addr: &AddrMessage,
peer_state: &mut PeerState,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if addr.addresses.len() > config.network_limits.max_addr_addresses {
return Ok(NetworkResponse::Reject(format!(
"Too many addresses (max {})",
config.network_limits.max_addr_addresses
)));
}
peer_state.known_addresses.extend(addr.addresses.clone());
Ok(NetworkResponse::Ok)
}
fn process_addrv2_message(
addrv2: &AddrV2Message,
peer_state: &mut PeerState,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if addrv2.addresses.len() > config.network_limits.max_addr_addresses {
return Ok(NetworkResponse::Reject(format!(
"Too many addresses (max {})",
config.network_limits.max_addr_addresses
)));
}
for addr_v2 in &addrv2.addresses {
if let Some(legacy_addr) = addr_v2.to_legacy() {
peer_state.known_addresses.push(legacy_addr);
}
}
Ok(NetworkResponse::Ok)
}
fn process_inv_message(
inv: &InvMessage,
chain_access: Option<&dyn ChainStateAccess>,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if inv.inventory.len() > config.network_limits.max_inv_items {
return Ok(NetworkResponse::Reject(format!(
"Too many inventory items (max {})",
config.network_limits.max_inv_items
)));
}
if let Some(chain) = chain_access {
let mut needed_items = Vec::with_capacity(inv.inventory.len());
for item in &inv.inventory {
if !chain.has_object(&item.hash) {
needed_items.push(item.clone());
}
}
if !needed_items.is_empty() {
return Ok(NetworkResponse::SendMessage(Box::new(
NetworkMessage::GetData(GetDataMessage {
inventory: needed_items,
}),
)));
}
}
Ok(NetworkResponse::Ok)
}
fn process_getdata_message(
getdata: &GetDataMessage,
chain_access: Option<&dyn ChainStateAccess>,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if getdata.inventory.len() > config.network_limits.max_inv_items {
return Ok(NetworkResponse::Reject(format!(
"Too many getdata items (max {})",
config.network_limits.max_inv_items
)));
}
if let Some(chain) = chain_access {
let mut responses = Vec::with_capacity(getdata.inventory.len());
for item in &getdata.inventory {
if let Some(obj) = chain.get_object(&item.hash) {
match item.inv_type {
1 => {
if let Some(tx) = obj.as_transaction() {
responses.push(NetworkMessage::Tx(Arc::clone(tx)));
}
}
2 => {
if let Some(block) = obj.as_block() {
responses.push(NetworkMessage::Block(Arc::clone(block)));
}
}
_ => {
}
}
}
}
if !responses.is_empty() {
return Ok(NetworkResponse::SendMessages(responses));
}
}
Ok(NetworkResponse::Ok)
}
fn process_getheaders_message(
getheaders: &GetHeadersMessage,
chain_access: Option<&dyn ChainStateAccess>,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if getheaders.block_locator_hashes.len() > config.validation.max_locator_hashes {
return Ok(NetworkResponse::Reject(format!(
"Too many locator hashes (max {})",
config.validation.max_locator_hashes
)));
}
if let Some(chain) = chain_access {
let headers =
chain.get_headers_for_locator(&getheaders.block_locator_hashes, &getheaders.hash_stop);
return Ok(NetworkResponse::SendMessage(Box::new(
NetworkMessage::Headers(HeadersMessage { headers }),
)));
}
Ok(NetworkResponse::Reject("Chain access not available".into()))
}
fn process_headers_message(
headers: &HeadersMessage,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if headers.headers.len() > config.network_limits.max_headers {
return Ok(NetworkResponse::Reject(format!(
"Too many headers (max {})",
config.network_limits.max_headers
)));
}
Ok(NetworkResponse::Ok)
}
fn process_block_message(
engine: &BitcoinProtocolEngine,
block: &Block,
utxo_set: Option<&UtxoSet>,
height: Option<u64>,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if block.transactions.len() > config.validation.max_txs_per_block {
return Err(crate::error::ProtocolError::MessageTooLarge {
size: block.transactions.len(),
max: config.validation.max_txs_per_block,
});
}
if let (Some(utxos), Some(h)) = (utxo_set, height) {
let context = ProtocolValidationContext::new(engine.get_protocol_version(), h)?;
let result = engine.validate_block_with_protocol(block, utxos, h, &context)?;
match result {
ValidationResult::Valid => Ok(NetworkResponse::Ok),
ValidationResult::Invalid(reason) => {
Ok(NetworkResponse::Reject(format!("Invalid block: {reason}")))
}
}
} else {
Err(crate::error::ProtocolError::Configuration(
"Missing validation context (utxo_set and height required)".into(),
))
}
}
fn process_tx_message(
engine: &BitcoinProtocolEngine,
tx: &Transaction,
height: Option<u64>,
) -> Result<NetworkResponse> {
let context =
ProtocolValidationContext::new(engine.get_protocol_version(), height.unwrap_or(0))?;
let result = engine.validate_transaction_with_protocol(tx, &context)?;
match result {
ValidationResult::Valid => Ok(NetworkResponse::Ok),
ValidationResult::Invalid(reason) => Ok(NetworkResponse::Reject(format!(
"Invalid transaction: {reason}"
))),
}
}
fn process_ping_message(
ping: &PingMessage,
_peer_state: &mut PeerState,
) -> Result<NetworkResponse> {
let pong = NetworkMessage::Pong(PongMessage { nonce: ping.nonce });
Ok(NetworkResponse::SendMessage(Box::new(pong)))
}
fn process_pong_message(pong: &PongMessage, peer_state: &mut PeerState) -> Result<NetworkResponse> {
if peer_state.ping_nonce == Some(pong.nonce) {
peer_state.ping_nonce = None;
peer_state.last_pong = Some(std::time::SystemTime::now());
}
Ok(NetworkResponse::Ok)
}
fn process_mempool_message(chain_access: Option<&dyn ChainStateAccess>) -> Result<NetworkResponse> {
if let Some(chain) = chain_access {
let mempool_txs = chain.get_mempool_transactions();
let mut responses = Vec::with_capacity(mempool_txs.len());
for tx in mempool_txs {
responses.push(NetworkMessage::Tx(Arc::new(tx)));
}
if !responses.is_empty() {
return Ok(NetworkResponse::SendMessages(responses));
}
}
Ok(NetworkResponse::Ok)
}
fn process_feefilter_message(
feefilter: &FeeFilterMessage,
peer_state: &mut PeerState,
) -> Result<NetworkResponse> {
peer_state.min_fee_rate = Some(feefilter.feerate);
Ok(NetworkResponse::Ok)
}
fn process_getblocks_message(
getblocks: &GetBlocksMessage,
chain_access: Option<&dyn ChainStateAccess>,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if getblocks.block_locator_hashes.len() > config.validation.max_locator_hashes {
return Ok(NetworkResponse::Reject(format!(
"Too many locator hashes (max {})",
config.validation.max_locator_hashes
)));
}
if let Some(chain) = chain_access {
let mut inventory = Vec::with_capacity(getblocks.block_locator_hashes.len());
for hash in &getblocks.block_locator_hashes {
if chain.has_object(hash) {
inventory.push(InventoryVector {
inv_type: 2, hash: *hash,
});
}
}
if !inventory.is_empty() {
return Ok(NetworkResponse::SendMessage(Box::new(NetworkMessage::Inv(
InvMessage { inventory },
))));
}
}
Ok(NetworkResponse::Ok)
}
fn process_getaddr_message(
peer_state: &mut PeerState,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if !peer_state.known_addresses.is_empty() {
let max_addrs = config
.network_limits
.max_addr_addresses
.min(peer_state.known_addresses.len());
let mut addresses = Vec::with_capacity(max_addrs);
addresses.extend(peer_state.known_addresses.iter().take(max_addrs).cloned());
return Ok(NetworkResponse::SendMessage(Box::new(
NetworkMessage::Addr(AddrMessage { addresses }),
)));
}
Ok(NetworkResponse::Ok)
}
fn process_notfound_message(
notfound: &NotFoundMessage,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if notfound.inventory.len() > config.network_limits.max_inv_items {
return Ok(NetworkResponse::Reject(format!(
"Too many notfound items (max {})",
config.network_limits.max_inv_items
)));
}
Ok(NetworkResponse::Ok)
}
fn process_reject_message(
reject: &RejectMessage,
_config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if reject.message.len() > 12 {
return Ok(NetworkResponse::Reject(
"Invalid reject message name".into(),
));
}
if reject.reason.len() > 111 {
return Ok(NetworkResponse::Reject("Reject reason too long".into()));
}
Ok(NetworkResponse::Ok)
}
fn process_sendheaders_message(_peer_state: &mut PeerState) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
fn process_sendcmpct_message(
sendcmpct: &SendCmpctMessage,
_peer_state: &mut PeerState,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
let valid_versions = [1, 2];
if !valid_versions.contains(&sendcmpct.version) {
return Ok(NetworkResponse::Reject(
"Invalid compact block version".into(),
));
}
if !config.compact_blocks.enabled {
return Ok(NetworkResponse::Reject("Compact blocks not enabled".into()));
}
let _ = (sendcmpct.version, sendcmpct.prefer_cmpct);
Ok(NetworkResponse::Ok)
}
fn process_cmpctblock_message(_cmpctblock: &CmpctBlockMessage) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
fn process_getblocktxn_message(
getblocktxn: &GetBlockTxnMessage,
chain_access: Option<&dyn ChainStateAccess>,
config: &ProtocolConfig,
) -> Result<NetworkResponse> {
if getblocktxn.indices.len() > config.compact_blocks.max_blocktxn_indices {
return Ok(NetworkResponse::Reject(format!(
"Too many transaction indices (max {})",
config.compact_blocks.max_blocktxn_indices
)));
}
if let Some(chain) = chain_access {
let mut transactions = Vec::new();
for &index in &getblocktxn.indices {
if let Some(obj) = chain.get_object(&getblocktxn.block_hash) {
if let Some(block) = obj.as_block() {
if (index as usize) < block.transactions.len() {
transactions.push(block.transactions[index as usize].clone());
}
}
}
}
if !transactions.is_empty() {
return Ok(NetworkResponse::SendMessage(Box::new(
NetworkMessage::BlockTxn(BlockTxnMessage {
block_hash: getblocktxn.block_hash,
transactions,
witnesses: None, }),
)));
}
}
Ok(NetworkResponse::Ok)
}
fn process_blocktxn_message(_blocktxn: &BlockTxnMessage) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
#[cfg(feature = "utxo-commitments")]
fn process_getutxoset_message(_getutxoset: &commons::GetUTXOSetMessage) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
#[cfg(feature = "utxo-commitments")]
fn process_utxoset_message(_utxoset: &commons::UTXOSetMessage) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
#[cfg(feature = "utxo-commitments")]
fn process_getfilteredblock_message(
_getfiltered: &commons::GetFilteredBlockMessage,
) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
#[cfg(feature = "utxo-commitments")]
fn process_filteredblock_message(
_filtered: &commons::FilteredBlockMessage,
) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
fn process_getbanlist_message(_getbanlist: &commons::GetBanListMessage) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}
fn process_banlist_message(_banlist: &commons::BanListMessage) -> Result<NetworkResponse> {
Ok(NetworkResponse::Ok)
}