zebra/zebra-network/src/protocol/message.rs

254 lines
8.9 KiB
Rust
Raw Normal View History

//! Definitions of network messages.
use std::net;
2019-09-13 05:29:17 -07:00
use chrono::{DateTime, Utc};
2019-09-26 19:10:39 -07:00
use zebra_chain::block::Block;
use zebra_chain::{transaction::Transaction, types::BlockHeight};
2019-09-13 05:29:17 -07:00
use crate::meta_addr::MetaAddr;
use super::inv::InventoryHash;
use super::types::*;
/// 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
2019-09-10 09:57:58 -07:00
//
// XXX not all messages are filled in yet. Messages written as { /* XXX add
// fields */ } are explicitly incomplete and we need to define a mapping between
// the serialized message data and the internal representation. Note that this
// is different from messages like GetAddr which have no data (and so have no
// fields).
2019-09-15 12:11:07 -07:00
#[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: Services,
/// The time when the version message was sent.
timestamp: DateTime<Utc>,
/// 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: (Services, net::SocketAddr),
/// The network address of the node sending this message, and its
/// advertised network services.
address_from: (Services, 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.
Implement ZcashDeserialize for Message::Version. This has a test that the serialization implementation round trips correctly, but is very much a work in progress. Issues with this code include: The deserialization logic for message headers is somewhat ugly because of a lack of a convenient idiom for reading a few bytes at a time into a fixed-size array. Perhaps this could be fixed with an extension trait, or avoided altogether by using `u32`s instead of `[u8; 4]`, even when the latter is the "morally correct type". Deserialization does an extra allocation, copying the body into a temporary buffer. This avoids two problems: 1) capping the number of bytes that can be read by the `Read`er passed into the body parser and 2) allows making two passes over the body data, one to parse it and one to compute the checksum. We could avoid making two passes over the body by computing the checksum simultaneously with the parsing. A convenient way to do this would be to define a ``` struct ChecksumReader<R: Read> { inner: R, digest: Sha256, } impl<R: Read> Read for ChecksumReader<R> { /* ... */ } ``` and implement `Read` for `ChecksumReader` to forward reads from the inner reader, copying data into the digest object as it does so. (It could also have a maximum length to enforce that we do not read past the nominal end of the body). A similar `ChecksumWriter` could be used during serialization, although because the checksum is at the beginning rather than the end of the message it does not help avoid an allocation there. It could also be generic over a `Digest` implementation, although because we need a truncated double-SHA256 we would have to write a custom `Digest` implementation, so this is probably not worthwhile unless we have other checksum types. Finally, this does very minimal testing -- just round-trip serialization on a single message. It would be good to build in support for property-based testing, probably using `proptest`; if we could define generation and shrinking strategies for every component type of every message, we could do strong randomized testing of the serialization.
2019-09-15 12:11:41 -07:00
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,
},
2019-09-09 23:59:49 -07:00
/// A `verack` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#verack)
Verack,
2019-09-09 23:59:49 -07:00
/// A `ping` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#ping)
2019-09-19 13:17:22 -07:00
Ping(
/// A nonce unique to this [`Ping`] message.
Nonce,
),
2019-09-09 23:59:49 -07:00
/// A `pong` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#pong)
Pong(
2019-09-19 13:17:22 -07:00
/// The nonce from the [`Ping`] message this was in response to.
Nonce,
),
2019-09-10 09:57:58 -07:00
/// A `reject` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#reject)
Reject {
/// Type of message rejected.
// Q: can we just reference the Type, rather than instantiate an instance of the enum type?
message: Box<Message>,
/// 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]>,
},
2019-09-10 09:57:58 -07:00
/// An `addr` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#addr)
Addr(Vec<MetaAddr>),
2019-09-10 09:57:58 -07:00
/// 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 {
/// The block itself.
block: Block,
},
2019-09-10 09:57:58 -07:00
/// A `getblocks` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getblocks)
GetBlocks {/* XXX add fields */},
/// A `headers` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#headers)
Headers {/* XXX add fields */},
/// A `getheaders` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getheaders)
GetHeaders {/* XXX add fields */},
/// An `inv` message.
///
/// [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?
Inventory(Vec<InventoryHash>),
2019-09-10 09:57:58 -07:00
/// 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.
///
2019-09-10 09:57:58 -07:00
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#getdata)
GetData(Vec<InventoryHash>),
2019-09-10 09:57:58 -07:00
/// A `notfound` message.
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#notfound)
// See note above on `Inventory`.
NotFound(Vec<InventoryHash>),
2019-09-10 09:57:58 -07:00
/// 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).
version: Version,
/// The `Transaction` type itself.
// XXX Is this ~aesthetic~?
transaction: Transaction,
},
2019-09-10 09:57:58 -07:00
/// 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 {/* XXX add fields */},
/// 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 {/* XXX add fields */},
/// 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 {/* XXX add fields */},
/// 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 {/* XXX add fields */},
/// A `merkleblock` 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
MerkleBlock {/* XXX add fields */},
}
/// Reject Reason CCodes
///
/// [Bitcoin reference](https://en.bitcoin.it/wiki/Protocol_documentation#reject)
2019-09-15 12:11:07 -07:00
#[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,
}