1use hashes::{sha256d, Hash as _};
10use io::{Read, Write};
11
12use crate::blockdata::block::BlockHash;
13use crate::blockdata::transaction::{Txid, Wtxid};
14use crate::consensus::encode::{self, Decodable, Encodable};
15use crate::internal_macros::impl_consensus_encoding;
16use crate::p2p;
17
18#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
20pub enum Inventory {
21 Error,
23 Transaction(Txid),
25 Block(BlockHash),
27 CompactBlock(BlockHash),
29 WTx(Wtxid),
31 WitnessTransaction(Txid),
33 WitnessBlock(BlockHash),
35 Unknown {
37 inv_type: u32,
39 hash: [u8; 32],
41 },
42}
43
44impl Inventory {
45 pub fn network_hash(&self) -> Option<[u8; 32]> {
49 match self {
50 Inventory::Error => None,
51 Inventory::Transaction(t) => Some(t.to_byte_array()),
52 Inventory::Block(b) => Some(b.to_byte_array()),
53 Inventory::CompactBlock(b) => Some(b.to_byte_array()),
54 Inventory::WTx(t) => Some(t.to_byte_array()),
55 Inventory::WitnessTransaction(t) => Some(t.to_byte_array()),
56 Inventory::WitnessBlock(b) => Some(b.to_byte_array()),
57 Inventory::Unknown { hash, .. } => Some(*hash),
58 }
59 }
60}
61
62impl Encodable for Inventory {
63 #[inline]
64 fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
65 macro_rules! encode_inv {
66 ($code:expr, $item:expr) => {
67 u32::consensus_encode(&$code, w)? + $item.consensus_encode(w)?
68 };
69 }
70 Ok(match *self {
71 Inventory::Error => encode_inv!(0, sha256d::Hash::all_zeros()),
72 Inventory::Transaction(ref t) => encode_inv!(1, t),
73 Inventory::Block(ref b) => encode_inv!(2, b),
74 Inventory::CompactBlock(ref b) => encode_inv!(4, b),
75 Inventory::WTx(w) => encode_inv!(5, w),
76 Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t),
77 Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b),
78 Inventory::Unknown { inv_type: t, hash: ref d } => encode_inv!(t, d),
79 })
80 }
81}
82
83impl Decodable for Inventory {
84 #[inline]
85 fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
86 let inv_type: u32 = Decodable::consensus_decode(r)?;
87 Ok(match inv_type {
88 0 => Inventory::Error,
89 1 => Inventory::Transaction(Decodable::consensus_decode(r)?),
90 2 => Inventory::Block(Decodable::consensus_decode(r)?),
91 4 => Inventory::CompactBlock(Decodable::consensus_decode(r)?),
92 5 => Inventory::WTx(Decodable::consensus_decode(r)?),
93 0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(r)?),
94 0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(r)?),
95 tp => Inventory::Unknown { inv_type: tp, hash: Decodable::consensus_decode(r)? },
96 })
97 }
98}
99
100#[derive(PartialEq, Eq, Clone, Debug)]
104pub struct GetBlocksMessage {
105 pub version: u32,
107 pub locator_hashes: Vec<BlockHash>,
111 pub stop_hash: BlockHash,
113}
114
115#[derive(PartialEq, Eq, Clone, Debug)]
117pub struct GetHeadersMessage {
118 pub version: u32,
120 pub locator_hashes: Vec<BlockHash>,
124 pub stop_hash: BlockHash,
126}
127
128impl GetBlocksMessage {
129 pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
131 GetBlocksMessage { version: p2p::PROTOCOL_VERSION, locator_hashes, stop_hash }
132 }
133}
134
135impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
136
137impl GetHeadersMessage {
138 pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetHeadersMessage {
140 GetHeadersMessage { version: p2p::PROTOCOL_VERSION, locator_hashes, stop_hash }
141 }
142}
143
144impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
145
146#[cfg(test)]
147mod tests {
148 use hashes::Hash;
149 use hex::test_hex_unwrap as hex;
150
151 use super::{GetBlocksMessage, GetHeadersMessage};
152 use crate::consensus::encode::{deserialize, serialize};
153
154 #[test]
155 fn getblocks_message_test() {
156 let from_sat = hex!("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000");
157 let genhash = hex!("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
158
159 let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
160 assert!(decode.is_ok());
161 let real_decode = decode.unwrap();
162 assert_eq!(real_decode.version, 70002);
163 assert_eq!(real_decode.locator_hashes.len(), 1);
164 assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
165 assert_eq!(real_decode.stop_hash, Hash::all_zeros());
166
167 assert_eq!(serialize(&real_decode), from_sat);
168 }
169
170 #[test]
171 fn getheaders_message_test() {
172 let from_sat = hex!("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000");
173 let genhash = hex!("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
174
175 let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
176 assert!(decode.is_ok());
177 let real_decode = decode.unwrap();
178 assert_eq!(real_decode.version, 70002);
179 assert_eq!(real_decode.locator_hashes.len(), 1);
180 assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
181 assert_eq!(real_decode.stop_hash, Hash::all_zeros());
182
183 assert_eq!(serialize(&real_decode), from_sat);
184 }
185}