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 network::constants;
22use consensus::encode::{Decodable, Encodable};
23use consensus::encode::{self, Decoder, Encoder};
24use bitcoin_hashes::sha256d;
25
26#[derive(PartialEq, Eq, Clone, Debug)]
27/// The type of an inventory object
28pub enum InvType {
29    /// Error --- these inventories can be ignored
30    Error,
31    /// Transaction
32    Transaction,
33    /// Block
34    Block,
35    /// Witness Block
36    WitnessBlock,
37    /// Witness Transaction
38    WitnessTransaction
39}
40
41// Some simple messages
42
43/// The `getblocks` message
44#[derive(PartialEq, Eq, Clone, Debug)]
45pub struct GetBlocksMessage {
46    /// The protocol version
47    pub version: u32,
48    /// Locator hashes --- ordered newest to oldest. The remote peer will
49    /// reply with its longest known chain, starting from a locator hash
50    /// if possible and block 1 otherwise.
51    pub locator_hashes: Vec<sha256d::Hash>,
52    /// References the block to stop at, or zero to just fetch the maximum 500 blocks
53    pub stop_hash: sha256d::Hash,
54}
55
56/// The `getheaders` message
57#[derive(PartialEq, Eq, Clone, Debug)]
58pub struct GetHeadersMessage {
59    /// The protocol version
60    pub version: u32,
61    /// Locator hashes --- ordered newest to oldest. The remote peer will
62    /// reply with its longest known chain, starting from a locator hash
63    /// if possible and block 1 otherwise.
64    pub locator_hashes: Vec<sha256d::Hash>,
65    /// References the header to stop at, or zero to just fetch the maximum 2000 headers
66    pub stop_hash: sha256d::Hash
67}
68
69/// An inventory object --- a reference to a Bitcoin object
70#[derive(PartialEq, Eq, Clone, Debug)]
71pub struct Inventory {
72    /// The type of object that is referenced
73    pub inv_type: InvType,
74    /// The object's hash
75    pub hash: sha256d::Hash
76}
77
78impl GetBlocksMessage {
79    /// Construct a new `getblocks` message
80    pub fn new(locator_hashes: Vec<sha256d::Hash>, stop_hash: sha256d::Hash) -> GetBlocksMessage {
81        GetBlocksMessage {
82            version: constants::PROTOCOL_VERSION,
83            locator_hashes: locator_hashes.clone(),
84            stop_hash: stop_hash
85        }
86    }
87}
88
89impl_consensus_encoding!(GetBlocksMessage, version, locator_hashes, stop_hash);
90
91impl GetHeadersMessage {
92    /// Construct a new `getheaders` message
93    pub fn new(locator_hashes: Vec<sha256d::Hash>, stop_hash: sha256d::Hash) -> GetHeadersMessage {
94        GetHeadersMessage {
95            version: constants::PROTOCOL_VERSION,
96            locator_hashes: locator_hashes,
97            stop_hash: stop_hash
98        }
99    }
100}
101
102impl_consensus_encoding!(GetHeadersMessage, version, locator_hashes, stop_hash);
103
104impl<S: Encoder> Encodable<S> for Inventory {
105    #[inline]
106    fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> {
107        match self.inv_type {
108            InvType::Error => 0u32, 
109            InvType::Transaction => 1,
110            InvType::Block => 2,
111            InvType::WitnessBlock => 0x40000002,
112            InvType::WitnessTransaction => 0x40000001
113        }.consensus_encode(s)?;
114        self.hash.consensus_encode(s)
115    }
116}
117
118impl<D: Decoder> Decodable<D> for Inventory {
119    #[inline]
120    fn consensus_decode(d: &mut D) -> Result<Inventory, encode::Error> {
121        let int_type: u32 = Decodable::consensus_decode(d)?;
122        Ok(Inventory {
123            inv_type: match int_type {
124                0 => InvType::Error,
125                1 => InvType::Transaction,
126                2 => InvType::Block,
127                // TODO do not fail here
128                _ => { panic!("bad inventory type field") }
129            },
130            hash: Decodable::consensus_decode(d)?
131        })
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::{GetHeadersMessage, GetBlocksMessage};
138
139    use hex::decode as hex_decode;
140
141    use consensus::encode::{deserialize, serialize};
142    use std::default::Default;
143
144    #[test]
145    fn getblocks_message_test() {
146        let from_sat = hex_decode("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
147        let genhash = hex_decode("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap();
148
149        let decode: Result<GetBlocksMessage, _> = deserialize(&from_sat);
150        assert!(decode.is_ok());
151        let real_decode = decode.unwrap();
152        assert_eq!(real_decode.version, 70002);
153        assert_eq!(real_decode.locator_hashes.len(), 1);
154        assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
155        assert_eq!(real_decode.stop_hash, Default::default());
156
157        assert_eq!(serialize(&real_decode), from_sat);
158    }
159
160    #[test]
161    fn getheaders_message_test() {
162        let from_sat = hex_decode("72110100014a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
163        let genhash = hex_decode("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b").unwrap();
164
165        let decode: Result<GetHeadersMessage, _> = deserialize(&from_sat);
166        assert!(decode.is_ok());
167        let real_decode = decode.unwrap();
168        assert_eq!(real_decode.version, 70002);
169        assert_eq!(real_decode.locator_hashes.len(), 1);
170        assert_eq!(serialize(&real_decode.locator_hashes[0]), genhash);
171        assert_eq!(real_decode.stop_hash, Default::default());
172
173        assert_eq!(serialize(&real_decode), from_sat);
174    }
175}
176