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