bitcoin/p2p/
message_blockdata.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Bitcoin blockdata network messages.
4//!
5//! This module describes network messages which are used for passing
6//! Bitcoin data (blocks and transactions) around.
7//!
8
9use 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/// An inventory item.
19#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
20pub enum Inventory {
21    /// Error --- these inventories can be ignored
22    Error,
23    /// Transaction
24    Transaction(Txid),
25    /// Block
26    Block(BlockHash),
27    /// Compact Block
28    CompactBlock(BlockHash),
29    /// Witness Transaction by Wtxid
30    WTx(Wtxid),
31    /// Witness Transaction
32    WitnessTransaction(Txid),
33    /// Witness Block
34    WitnessBlock(BlockHash),
35    /// Unknown inventory type
36    Unknown {
37        /// The inventory item type.
38        inv_type: u32,
39        /// The hash of the inventory item
40        hash: [u8; 32],
41    },
42}
43
44impl Inventory {
45    /// Return the item value represented as a SHA256-d hash.
46    ///
47    /// Returns [None] only for [Inventory::Error].
48    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// Some simple messages
101
102/// The `getblocks` message
103#[derive(PartialEq, Eq, Clone, Debug)]
104pub struct GetBlocksMessage {
105    /// The protocol version
106    pub version: u32,
107    /// Locator hashes --- ordered newest to oldest. The remote peer will
108    /// reply with its longest known chain, starting from a locator hash
109    /// if possible and block 1 otherwise.
110    pub locator_hashes: Vec<BlockHash>,
111    /// References the block to stop at, or zero to just fetch the maximum 500 blocks
112    pub stop_hash: BlockHash,
113}
114
115/// The `getheaders` message
116#[derive(PartialEq, Eq, Clone, Debug)]
117pub struct GetHeadersMessage {
118    /// The protocol version
119    pub version: u32,
120    /// Locator hashes --- ordered newest to oldest. The remote peer will
121    /// reply with its longest known chain, starting from a locator hash
122    /// if possible and block 1 otherwise.
123    pub locator_hashes: Vec<BlockHash>,
124    /// References the header to stop at, or zero to just fetch the maximum 2000 headers
125    pub stop_hash: BlockHash,
126}
127
128impl GetBlocksMessage {
129    /// Construct a new `getblocks` message
130    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    /// Construct a new `getheaders` message
139    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}