//! Definitions of network messages. use std::error::Error; use std::net; use chrono::{DateTime, Utc}; use zebra_chain::block::{Block, BlockHeader, BlockHeaderHash}; use zebra_chain::{transaction::Transaction, types::BlockHeight}; use super::inv::InventoryHash; use super::types::*; use crate::meta_addr::MetaAddr; /// A Bitcoin-like network message for the Zcash protocol. /// /// The Zcash network protocol is mostly inherited from Bitcoin, and a list of /// Bitcoin network messages can be found [on the Bitcoin /// wiki][btc_wiki_protocol]. /// /// That page describes the wire format of the messages, while this enum stores /// an internal representation. The internal representation is unlinked from the /// wire format, and the translation between the two happens only during /// serialization and deserialization. For instance, Bitcoin identifies messages /// by a 12-byte ascii command string; we consider this a serialization detail /// and use the enum discriminant instead. (As a side benefit, this also means /// that we have a clearly-defined validation boundary for network messages /// during serialization). /// /// [btc_wiki_protocol]: https://en.bitcoin.it/wiki/Protocol_documentation #[derive(Clone, Debug, Eq, PartialEq)] pub enum Message { /// A `version` message. /// /// Note that although this is called `version` in Bitcoin, its role is really /// analogous to a `ClientHello` message in TLS, used to begin a handshake, and /// is distinct from a simple version number. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#version) Version { /// The network version number supported by the sender. version: Version, /// The network services advertised by the sender. services: PeerServices, /// The time when the version message was sent. timestamp: DateTime, /// The network address of the node receiving this message, and its /// advertised network services. /// /// Q: how does the handshake know the remote peer's services already? address_recv: (PeerServices, net::SocketAddr), /// The network address of the node sending this message, and its /// advertised network services. address_from: (PeerServices, net::SocketAddr), /// Node random nonce, randomly generated every time a version /// packet is sent. This nonce is used to detect connections /// to self. nonce: Nonce, /// The Zcash user agent advertised by the sender. user_agent: String, /// The last block received by the emitting node. start_height: BlockHeight, /// Whether the remote peer should announce relayed /// transactions or not, see [BIP 0037](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki) relay: bool, }, /// A `verack` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#verack) Verack, /// A `ping` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#ping) Ping( /// A nonce unique to this [`Ping`] message. Nonce, ), /// A `pong` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#pong) Pong( /// The nonce from the [`Ping`] message this was in response to. Nonce, ), /// A `reject` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#reject) Reject { /// Type of message rejected. // It's unclear if this is strictly limited to message command // codes, so leaving it a String. message: String, /// RejectReason code relating to rejected message. ccode: RejectReason, /// Human-readable version of rejection reason. reason: String, /// Optional extra data provided for some errors. // Currently, all errors which provide this field fill it with // the TXID or block header hash of the object being rejected, // so the field is 32 bytes. // // Q: can we tell Rust that this field is optional? Or just // default its value to an empty array, I guess. data: Option<[u8; 32]>, }, /// An `addr` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#addr) Addr(Vec), /// A `getaddr` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getaddr) GetAddr, /// A `block` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#block) Block { /// Transaction data format version (note, this is signed). // XXX does this get folded into the Block struct? version: Version, /// The block itself. block: Box, }, /// A `getblocks` message. /// /// Requests the list of blocks starting right after the last /// known hash in `block_locator_hashes`, up to `hash_stop` or 500 /// blocks, whichever comes first. /// /// You can send in fewer known hashes down to a minimum of just /// one hash. However, the purpose of the block locator object is /// to detect a wrong branch in the caller's main chain. If the /// peer detects that you are off the main chain, it will send in /// block hashes which are earlier than your last known block. So /// if you just send in your last known hash and it is off the /// main chain, the peer starts over at block #1. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getblocks) // The locator hashes are processed by a node in the order as they // appear in the message. If a block hash is found in the node's // main chain, the list of its children is returned back via the // inv message and the remaining locators are ignored, no matter // if the requested limit was reached, or not. // // The 500 headers number is from the Bitcoin docs, we are not // certain (yet) that other implementations of Zcash obey this // restriction, or if they don't, what happens if we send them too // many results. GetBlocks { /// The protocol version. version: Version, /// Block locators, from newest back to genesis block. block_locator_hashes: Vec, /// `BlockHeaderHash` of the last desired block. /// /// Set to zero to get as many blocks as possible (500). hash_stop: BlockHeaderHash, }, /// A `headers` message. /// /// Returns block headers in response to a getheaders packet. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#headers) // Note that the block headers in this packet include a // transaction count (a var_int, so there can be more than 81 // bytes per header) as opposed to the block headers that are // hashed by miners. Headers(Vec), /// A `getheaders` message. /// /// Requests a series of block headers starting right after the /// last known hash in `block_locator_hashes`, up to `hash_stop` /// or 2000 blocks, whichever comes first. /// /// You can send in fewer known hashes down to a minimum of just /// one hash. However, the purpose of the block locator object is /// to detect a wrong branch in the caller's main chain. If the /// peer detects that you are off the main chain, it will send in /// block hashes which are earlier than your last known block. So /// if you just send in your last known hash and it is off the /// main chain, the peer starts over at block #1. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getheaders) // The 2000 headers number is from the Bitcoin docs, we are not // certain (yet) that other implementations of Zcash obey this // restriction, or if they don't, what happens if we send them too // many results. GetHeaders { /// The protocol version. version: Version, /// Block locators, from newest back to genesis block. block_locator_hashes: Vec, /// `BlockHeaderHash` of the last desired block header. /// /// Set to zero to get as many block headers as possible (2000). hash_stop: BlockHeaderHash, }, /// An `inv` message. /// /// Allows a node to advertise its knowledge of one or more /// objects. It can be received unsolicited, or in reply to /// `getblocks`. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#inv) // XXX the bitcoin reference above suggests this can be 1.8 MB in bitcoin -- maybe // larger in Zcash, since Zcash objects could be bigger (?) -- does this tilt towards // having serialization be async? Inv(Vec), /// A `getdata` message. /// /// `getdata` is used in response to `inv`, to retrieve the /// content of a specific object, and is usually sent after /// receiving an `inv` packet, after filtering known elements. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getdata) GetData(Vec), /// A `notfound` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#notfound) // See note above on `Inventory`. NotFound(Vec), /// A `tx` message. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#tx) // `flag` is not included (it's optional), and therefore // `tx_witnesses` aren't either, as they go if `flag` goes. Tx { /// Transaction data format version (note, this is signed). // XXX do we still need this with the transaction data handling? version: Version, /// The `Transaction` type itself. transaction: Box, }, /// A `mempool` message. /// /// This was defined in [BIP35], which is included in Zcash. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#mempool) /// [BIP35]: https://github.com/bitcoin/bips/blob/master/bip-0035.mediawiki Mempool, /// A `filterload` message. /// /// This was defined in [BIP37], which is included in Zcash. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#filterload.2C_filteradd.2C_filterclear.2C_merkleblock) /// [BIP37]: https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki FilterLoad { /// The filter itself is simply a bit field of arbitrary /// byte-aligned size. The maximum size is 36,000 bytes. filter: Filter, /// The number of hash functions to use in this filter. The /// maximum value allowed in this field is 50. hash_functions_count: u32, /// A random value to add to the seed value in the hash /// function used by the bloom filter. tweak: Tweak, /// A set of flags that control how matched items are added to the filter. flags: u8, }, /// A `filteradd` message. /// /// This was defined in [BIP37], which is included in Zcash. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#filterload.2C_filteradd.2C_filterclear.2C_merkleblock) /// [BIP37]: https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki FilterAdd { /// The data element to add to the current filter. // The data field must be smaller than or equal to 520 bytes // in size (the maximum size of any potentially matched // object). // // A Vec instead of [u8; 520] because of needed traits. data: Vec, }, /// A `filterclear` message. /// /// This was defined in [BIP37], which is included in Zcash. /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#filterload.2C_filteradd.2C_filterclear.2C_merkleblock) /// [BIP37]: https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki FilterClear, } impl From for Message where E: Error, { fn from(e: E) -> Self { Message::Reject { message: e.to_string(), // The generic case, impls for specific error types should // use specific varieties of `RejectReason`. ccode: RejectReason::Other, reason: e.source().unwrap().to_string(), // Allow this to be overriden but not populated by default, methinks. data: None, } } } /// Reject Reason CCodes /// /// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#reject) #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(u8)] #[allow(missing_docs)] pub enum RejectReason { Malformed = 0x01, Invalid = 0x10, Obsolete = 0x11, Duplicate = 0x12, Nonstandard = 0x40, Dust = 0x41, InsufficientFee = 0x42, Checkpoint = 0x43, Other = 0x50, }