use std::io;
use bitcoincash::{
consensus::{encode, Decodable},
network::message::CommandString,
VarInt,
};
use crate::chaindef::{Block, NetworkMessage};
use anyhow::Result;
use bitcoincash::consensus::ReadExt;
use tokio::io::AsyncReadExt;
pub struct P2PNetworkMessage {
pub magic: u32,
pub cmd: CommandString,
pub raw: Vec<u8>,
}
impl P2PNetworkMessage {
pub fn parse(&self) -> Result<NetworkMessage> {
let mut raw: &[u8] = &self.raw;
let payload = match self.cmd.as_ref() {
"version" => NetworkMessage::Version(Decodable::consensus_decode(&mut raw)?),
"verack" => NetworkMessage::Verack,
"inv" => NetworkMessage::Inv(Decodable::consensus_decode(&mut raw)?),
"block" => NetworkMessage::Block(Decodable::consensus_decode(&mut raw)?),
"headers" => {
let len = VarInt::consensus_decode(&mut raw)?.0;
let mut headers = Vec::with_capacity(len as usize);
for _ in 0..len {
headers.push(Block::consensus_decode(&mut raw)?.header);
}
NetworkMessage::Headers(headers)
}
"ping" => NetworkMessage::Ping(Decodable::consensus_decode(&mut raw)?),
"pong" => NetworkMessage::Pong(Decodable::consensus_decode(&mut raw)?),
"reject" => NetworkMessage::Reject(Decodable::consensus_decode(&mut raw)?),
_ => NetworkMessage::Unknown {
command: self.cmd.clone(),
payload: raw.to_vec(),
},
};
Ok(payload)
}
pub async fn async_consensus_decode<D: tokio::io::AsyncRead + Unpin>(
mut d: D,
) -> Result<Self, encode::Error> {
let magic: u32 = d.read_u32_le().await?;
let cmd: CommandString = Self::read_and_decode(&mut d, 12).await?;
let len: u32 = d.read_u32_le().await?;
let _checksum: u32 = d.read_u32_le().await?;
let mut raw: Vec<u8> = vec![0u8; len as usize];
if len > 0 {
d.read_exact(&mut raw).await.map_err(encode::Error::Io)?;
}
Ok(Self { magic, cmd, raw })
}
async fn read_and_decode<T: Decodable, D: tokio::io::AsyncRead + Unpin>(
d: &mut D,
len: usize,
) -> Result<T, encode::Error> {
let mut buf = vec![0u8; len];
d.read_exact(&mut buf).await.map_err(encode::Error::Io)?;
let mut cursor = std::io::Cursor::new(buf);
T::consensus_decode(&mut cursor)
}
}
impl Decodable for P2PNetworkMessage {
fn consensus_decode<D: io::Read + ?Sized>(d: &mut D) -> Result<Self, encode::Error> {
let magic = Decodable::consensus_decode(d)?;
let cmd = Decodable::consensus_decode(d)?;
let len = u32::consensus_decode(d)?;
let _checksum = <[u8; 4]>::consensus_decode(d)?; let mut raw = vec![0u8; len as usize];
d.read_slice(&mut raw)?;
Ok(Self { magic, cmd, raw })
}
}