bitcoin/network/
message_blockdata.rs

1// Rust Bitcoin Library
2// Written in 2014 by
3//     Andrew Poelstra <apoelstra@wpsoftware.net>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! Blockdata network messages
16//!
17//! This module describes network messages which are used for passing
18//! Bitcoin data (blocks and transactions) around.
19//!
20
21use std::io;
22
23use hashes::sha256d;
24
25use network::constants;
26use consensus::encode::{self, Decodable, Encodable};
27use hash_types::{BlockHash, Txid, Wtxid};
28
29/// An inventory item.
30#[derive(PartialEq, Eq, Clone, Debug, Copy, Hash, PartialOrd, Ord)]
31pub enum Inventory {
32    /// Error --- these inventories can be ignored
33    Error,
34    /// Transaction
35    Transaction(Txid),
36    /// Block
37    Block(BlockHash),
38    /// Witness Transaction by Wtxid
39    WTx(Wtxid),
40    /// Witness Transaction
41    WitnessTransaction(Txid),
42    /// Witness Block
43    WitnessBlock(BlockHash),
44    /// Unknown inventory type
45    Unknown {
46        /// The inventory item type.
47        inv_type: u32,
48        /// The hash of the inventory item
49        hash: [u8; 32],
50    }
51}
52
53impl Encodable for Inventory {
54    #[inline]
55    fn consensus_encode<S: io::Write>(
56        &self,
57        mut s: S,
58    ) -> Result<usize, io::Error> {
59        macro_rules! encode_inv {
60            ($code:expr, $item:expr) => {
61                u32::consensus_encode(&$code, &mut s)? +
62                $item.consensus_encode(&mut s)?
63            }
64        }
65        Ok(match *self {
66            Inventory::Error => encode_inv!(0, sha256d::Hash::default()),
67            Inventory::Transaction(ref t) => encode_inv!(1, t),
68            Inventory::Block(ref b) => encode_inv!(2, b),
69            Inventory::WTx(w) => encode_inv!(5, w),
70            Inventory::WitnessTransaction(ref t) => encode_inv!(0x40000001, t),
71            Inventory::WitnessBlock(ref b) => encode_inv!(0x40000002, b),
72            Inventory::Unknown { inv_type: t, hash: ref d } => encode_inv!(t, d),
73        })
74    }
75}
76
77impl Decodable for Inventory {
78    #[inline]
79    fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
80        let inv_type: u32 = Decodable::consensus_decode(&mut d)?;
81        Ok(match inv_type {
82            0 => Inventory::Error,
83            1 => Inventory::Transaction(Decodable::consensus_decode(&mut d)?),
84            2 => Inventory::Block(Decodable::consensus_decode(&mut d)?),
85            5 => Inventory::WTx(Decodable::consensus_decode(&mut d)?),
86            0x40000001 => Inventory::WitnessTransaction(Decodable::consensus_decode(&mut d)?),
87            0x40000002 => Inventory::WitnessBlock(Decodable::consensus_decode(&mut d)?),
88            tp => Inventory::Unknown {
89                inv_type: tp,
90                hash: Decodable::consensus_decode(&mut d)?,
91            }
92        })
93    }
94}
95
96// Some simple messages
97
98/// The `getblocks` message
99#[derive(PartialEq, Eq, Clone, Debug)]
100pub struct GetBlocksMessage {
101    /// The protocol version
102    pub version: u32,
103    /// Locator hashes --- ordered newest to oldest. The remote peer will
104    /// reply with its longest known chain, starting from a locator hash
105    /// if possible and block 1 otherwise.
106    pub locator_hashes: Vec<BlockHash>,
107    /// References the block to stop at, or zero to just fetch the maximum 500 blocks
108    pub stop_hash: BlockHash,
109}
110
111/// The `getheaders` message
112#[derive(PartialEq, Eq, Clone, Debug)]
113pub struct GetHeadersMessage {
114    /// The protocol version
115    pub version: u32,
116    /// Locator hashes --- ordered newest to oldest. The remote peer will
117    /// reply with its longest known chain, starting from a locator hash
118    /// if possible and block 1 otherwise.
119    pub locator_hashes: Vec<BlockHash>,
120    /// References the header to stop at, or zero to just fetch the maximum 2000 headers
121    pub stop_hash: BlockHash
122}
123
124impl GetBlocksMessage {
125    /// Construct a new `getblocks` message
126    pub fn new(locator_hashes: Vec<BlockHash>, stop_hash: BlockHash) -> GetBlocksMessage {
127        GetBlocksMessage {
128            version: constants::PROTOCOL_VERSION,
129            locator_hashes: locator_hashes,
130            stop_hash: stop_hash
131        }
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 {
141            version: constants::PROTOCOL_VERSION,
142            locator_hashes: locator_hashes,
143            stop_hash: stop_hash
144        }
145    }
146}
147
148impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
149
150#[cfg(test)]
151mod tests {
152    use super::{GetHeadersMessage, GetBlocksMessage};
153
154    use hashes::hex::FromHex;
155
156    use consensus::encode::{deserialize, serialize};
157    use std::default::Default;
158
159    #[test]
160    fn getblocks_message_test() {
161        let from_sat = Vec::from_hex("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
162        let genhash = Vec::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap();
163
164        let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
165        assert!(decode.is_ok());
166        let real_decode = decode.unwrap();
167        assert_eq!(real_decode.version, 70002);
168        assert_eq!(real_decode.locator_hashes.len(), 1);
169        assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
170        assert_eq!(real_decode.stop_hash, Default::default());
171
172        assert_eq!(serialize(&real_decode), from_sat);
173    }
174
175    #[test]
176    fn getheaders_message_test() {
177        let from_sat = Vec::from_hex("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
178        let genhash = Vec::from_hex("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap();
179
180        let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
181        assert!(decode.is_ok());
182        let real_decode = decode.unwrap();
183        assert_eq!(real_decode.version, 70002);
184        assert_eq!(real_decode.locator_hashes.len(), 1);
185        assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
186        assert_eq!(real_decode.stop_hash, Default::default());
187
188        assert_eq!(serialize(&real_decode), from_sat);
189    }
190}
191