use std::{
cmp::min,
io::{Read, Write},
};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use zebra_chain::{
block,
serialization::{
ReadZcashExt, SerializationError, TrustedPreallocate, ZcashDeserialize,
ZcashDeserializeInto, ZcashSerialize,
},
transaction::{
self,
UnminedTxId::{self, *},
WtxId,
},
};
use super::MAX_PROTOCOL_MESSAGE_LEN;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum InventoryHash {
Error,
Tx(transaction::Hash),
Block(block::Hash),
FilteredBlock(block::Hash),
Wtx(transaction::WtxId),
}
impl InventoryHash {
#[allow(dead_code)]
pub fn from_legacy_tx_id(legacy_tx_id: transaction::Hash) -> InventoryHash {
InventoryHash::Tx(legacy_tx_id)
}
pub fn block_hash(&self) -> Option<block::Hash> {
match self {
InventoryHash::Error => None,
InventoryHash::Tx(_legacy_tx_id) => None,
InventoryHash::Block(hash) => Some(*hash),
InventoryHash::FilteredBlock(_ignored_hash) => None,
InventoryHash::Wtx(_wtx_id) => None,
}
}
pub fn unmined_tx_id(&self) -> Option<UnminedTxId> {
match self {
InventoryHash::Error => None,
InventoryHash::Tx(legacy_tx_id) => Some(UnminedTxId::from_legacy_id(*legacy_tx_id)),
InventoryHash::Block(_hash) => None,
InventoryHash::FilteredBlock(_hash) => None,
InventoryHash::Wtx(wtx_id) => Some(UnminedTxId::from(wtx_id)),
}
}
fn code(&self) -> u32 {
match self {
InventoryHash::Error => 0,
InventoryHash::Tx(_tx_id) => 1,
InventoryHash::Block(_hash) => 2,
InventoryHash::FilteredBlock(_hash) => 3,
InventoryHash::Wtx(_wtx_id) => 5,
}
}
}
impl From<WtxId> for InventoryHash {
fn from(wtx_id: WtxId) -> InventoryHash {
InventoryHash::Wtx(wtx_id)
}
}
impl From<&WtxId> for InventoryHash {
fn from(wtx_id: &WtxId) -> InventoryHash {
InventoryHash::from(*wtx_id)
}
}
impl From<UnminedTxId> for InventoryHash {
fn from(tx_id: UnminedTxId) -> InventoryHash {
match tx_id {
Legacy(hash) => InventoryHash::Tx(hash),
Witnessed(wtx_id) => InventoryHash::Wtx(wtx_id),
}
}
}
impl From<&UnminedTxId> for InventoryHash {
fn from(tx_id: &UnminedTxId) -> InventoryHash {
InventoryHash::from(*tx_id)
}
}
impl From<block::Hash> for InventoryHash {
fn from(hash: block::Hash) -> InventoryHash {
InventoryHash::Block(hash)
}
}
impl ZcashSerialize for InventoryHash {
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
writer.write_u32::<LittleEndian>(self.code())?;
match self {
InventoryHash::Error => writer.write_all(&[0; 32]),
InventoryHash::Tx(tx_id) => tx_id.zcash_serialize(writer),
InventoryHash::Block(hash) => hash.zcash_serialize(writer),
InventoryHash::FilteredBlock(hash) => hash.zcash_serialize(writer),
InventoryHash::Wtx(wtx_id) => wtx_id.zcash_serialize(writer),
}
}
}
impl ZcashDeserialize for InventoryHash {
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
let code = reader.read_u32::<LittleEndian>()?;
match code {
0 => {
let _bytes = reader.read_32_bytes()?;
Ok(InventoryHash::Error)
}
1 => Ok(InventoryHash::Tx(reader.zcash_deserialize_into()?)),
2 => Ok(InventoryHash::Block(reader.zcash_deserialize_into()?)),
3 => Ok(InventoryHash::FilteredBlock(
reader.zcash_deserialize_into()?,
)),
5 => Ok(InventoryHash::Wtx(reader.zcash_deserialize_into()?)),
_ => Err(SerializationError::Parse("invalid inventory code")),
}
}
}
pub(crate) const MIN_INV_HASH_SIZE: usize = 36;
pub const MAX_INV_IN_RECEIVED_MESSAGE: u64 = 50_000;
pub const MAX_TX_INV_IN_SENT_MESSAGE: u64 = 25_000;
impl TrustedPreallocate for InventoryHash {
fn max_allocation() -> u64 {
let message_size_limit = ((MAX_PROTOCOL_MESSAGE_LEN - 1) / MIN_INV_HASH_SIZE) as u64;
min(message_size_limit, MAX_INV_IN_RECEIVED_MESSAGE)
}
}