Merge rust-bitcoin/rust-bitcoin#1288: New type network magic
6e5e8d80a6
add error implementations for `ParseMagicError` and `UnknownMagic` (Noah)a79c69894a
new type network magic (Noah) Pull request description: #1266 Added a new type `Magic` for network magic. ACKs for top commit: Kixunil: ACK6e5e8d80a6
apoelstra: ACK6e5e8d80a6
Tree-SHA512: a67703e497af0e317f03d5cd4efca9ed6878c252a68aefa56592bfcac2946e8f62f1ae746de8a20344d846ec5623997acffe8a04b81b4faa25e579bebcd95f90
This commit is contained in:
commit
1286bcebb5
|
@ -26,14 +26,19 @@
|
|||
//! assert_eq!(&bytes[..], &[0xF9, 0xBE, 0xB4, 0xD9]);
|
||||
//! ```
|
||||
|
||||
use core::{fmt, ops, convert::From};
|
||||
use core::{fmt, ops, convert::TryFrom, borrow::Borrow, borrow::BorrowMut};
|
||||
use core::str::FromStr;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::io;
|
||||
use crate::prelude::{String, ToOwned};
|
||||
use crate::consensus::encode::{self, Encodable, Decodable};
|
||||
use crate::internal_macros::debug_from_display;
|
||||
use crate::hashes::hex::{FromHex, Error};
|
||||
use crate::error::impl_std_error;
|
||||
use bitcoin_internals::write_err;
|
||||
|
||||
/// Version of the protocol as appearing in network message headers
|
||||
/// This constant is used to signal to other peers which features you support.
|
||||
|
@ -75,20 +80,14 @@ impl Network {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use bitcoin::network::constants::Network;
|
||||
/// use bitcoin::network::constants::{Network, Magic};
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// assert_eq!(Some(Network::Bitcoin), Network::from_magic(0xD9B4BEF9));
|
||||
/// assert_eq!(None, Network::from_magic(0xFFFFFFFF));
|
||||
/// assert_eq!(Ok(Network::Bitcoin), Network::try_from(Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9])));
|
||||
/// assert_eq!(None, Network::from_magic(Magic::from_bytes([0xFF, 0xFF, 0xFF, 0xFF])));
|
||||
/// ```
|
||||
pub fn from_magic(magic: u32) -> Option<Network> {
|
||||
// Note: any new entries here must be added to `magic` below
|
||||
match magic {
|
||||
0xD9B4BEF9 => Some(Network::Bitcoin),
|
||||
0x0709110B => Some(Network::Testnet),
|
||||
0x40CF030A => Some(Network::Signet),
|
||||
0xDAB5BFFA => Some(Network::Regtest),
|
||||
_ => None
|
||||
}
|
||||
pub fn from_magic(magic: Magic) -> Option<Network> {
|
||||
Network::try_from(magic).ok()
|
||||
}
|
||||
|
||||
/// Return the network magic bytes, which should be encoded little-endian
|
||||
|
@ -97,33 +96,13 @@ impl Network {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use bitcoin::network::constants::Network;
|
||||
/// use bitcoin::network::constants::{Network, Magic};
|
||||
///
|
||||
/// let network = Network::Bitcoin;
|
||||
/// assert_eq!(network.magic(), 0xD9B4BEF9);
|
||||
/// assert_eq!(network.magic(), Magic::from_bytes([0xF9, 0xBE, 0xB4, 0xD9]));
|
||||
/// ```
|
||||
pub fn magic(self) -> u32 {
|
||||
// Note: any new entries here must be added to `from_magic` above
|
||||
match self {
|
||||
Network::Bitcoin => 0xD9B4BEF9,
|
||||
Network::Testnet => 0x0709110B,
|
||||
Network::Signet => 0x40CF030A,
|
||||
Network::Regtest => 0xDAB5BFFA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Network {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
use Network::*;
|
||||
|
||||
let s = match *self {
|
||||
Bitcoin => "bitcoin",
|
||||
Testnet => "testnet",
|
||||
Signet => "signet",
|
||||
Regtest => "regtest",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
pub fn magic(self) -> Magic {
|
||||
Magic::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,6 +129,180 @@ impl FromStr for Network {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Network {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
use Network::*;
|
||||
|
||||
let s = match *self {
|
||||
Bitcoin => "bitcoin",
|
||||
Testnet => "testnet",
|
||||
Signet => "signet",
|
||||
Regtest => "regtest",
|
||||
};
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Network magic bytes to identify the cryptocurrency network the message was intended for.
|
||||
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
|
||||
pub struct Magic([u8; 4]);
|
||||
|
||||
impl Magic {
|
||||
/// Bitcoin mainnet network magic bytes.
|
||||
pub const BITCOIN: Self = Self([0xF9, 0xBE, 0xB4, 0xD9]);
|
||||
/// Bitcoin testnet network magic bytes.
|
||||
pub const TESTNET: Self = Self([0x0B, 0x11, 0x09, 0x07]);
|
||||
/// Bitcoin signet network magic bytes.
|
||||
pub const SIGNET: Self = Self([0x0A, 0x03, 0xCF, 0x40]);
|
||||
/// Bitcoin regtest network magic bytes.
|
||||
pub const REGTEST: Self = Self([0xFA, 0xBF, 0xB5, 0xDA]);
|
||||
|
||||
/// Create network magic from bytes.
|
||||
pub fn from_bytes(bytes: [u8; 4]) -> Magic {
|
||||
Magic(bytes)
|
||||
}
|
||||
|
||||
/// Get network magic bytes.
|
||||
pub fn to_bytes(self) -> [u8; 4] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// An error in parsing magic bytes.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct ParseMagicError {
|
||||
/// The error that occurred when parsing the string.
|
||||
error: Error,
|
||||
/// The byte string that failed to parse.
|
||||
magic: String
|
||||
}
|
||||
|
||||
impl FromStr for Magic {
|
||||
type Err = ParseMagicError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Magic, Self::Err> {
|
||||
match <[u8; 4]>::from_hex(s) {
|
||||
Ok(magic) => Ok(Magic::from_bytes(magic)),
|
||||
Err(e) => Err(ParseMagicError { error: e, magic: s.to_owned() })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Network> for Magic {
|
||||
fn from(network: Network) -> Magic {
|
||||
match network {
|
||||
// Note: new network entries must explicitly be matched in `try_from` below.
|
||||
Network::Bitcoin => Magic::BITCOIN,
|
||||
Network::Testnet => Magic::TESTNET,
|
||||
Network::Signet => Magic::SIGNET,
|
||||
Network::Regtest => Magic::REGTEST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error in parsing magic from string.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UnknownMagic(Magic);
|
||||
|
||||
impl TryFrom<Magic> for Network {
|
||||
type Error = UnknownMagic;
|
||||
|
||||
fn try_from(magic: Magic) -> Result<Self, Self::Error> {
|
||||
match magic {
|
||||
// Note: any new network entries must be matched against here.
|
||||
Magic::BITCOIN => Ok(Network::Bitcoin),
|
||||
Magic::TESTNET => Ok(Network::Testnet),
|
||||
Magic::SIGNET => Ok(Network::Signet),
|
||||
Magic::REGTEST => Ok(Network::Regtest),
|
||||
_ => Err(UnknownMagic(magic))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Magic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
bitcoin_internals::fmt_hex_exact!(f, 4, &self.0, bitcoin_internals::hex::Case::Lower)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
debug_from_display!(Magic);
|
||||
|
||||
impl fmt::LowerHex for Magic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
bitcoin_internals::fmt_hex_exact!(f, 4, &self.0, bitcoin_internals::hex::Case::Lower)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::UpperHex for Magic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
bitcoin_internals::fmt_hex_exact!(f, 4, &self.0, bitcoin_internals::hex::Case::Upper)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Magic {
|
||||
fn consensus_encode<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<usize, io::Error> {
|
||||
self.0.consensus_encode(writer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for Magic {
|
||||
fn consensus_decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, encode::Error> {
|
||||
Ok(Magic(Decodable::consensus_decode(reader)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Magic {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8; 4]> for Magic {
|
||||
fn as_ref(&self) -> &[u8; 4] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8]> for Magic {
|
||||
fn borrow(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<[u8; 4]> for Magic {
|
||||
fn borrow(&self) -> &[u8; 4] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowMut<[u8]> for Magic {
|
||||
fn borrow_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowMut<[u8; 4]> for Magic {
|
||||
fn borrow_mut(&mut self) -> &mut [u8; 4] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseMagicError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write_err!(f, "failed to parse {} as network magic", self.magic; self.error)
|
||||
}
|
||||
}
|
||||
impl_std_error!(ParseMagicError, error);
|
||||
|
||||
impl fmt::Display for UnknownMagic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
write!(f, "unknown network magic {}", self.0)
|
||||
}
|
||||
}
|
||||
impl_std_error!(UnknownMagic);
|
||||
|
||||
/// Flags to indicate which network services a node supports.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ServiceFlags(u64);
|
||||
|
@ -320,8 +473,10 @@ impl Decodable for ServiceFlags {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Network, ServiceFlags};
|
||||
use super::{Network, ServiceFlags, Magic};
|
||||
use crate::consensus::encode::{deserialize, serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn serialize_test() {
|
||||
|
@ -408,4 +563,20 @@ mod tests {
|
|||
assert_eq!(back, network);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn magic_from_str() {
|
||||
let known_network_magic_strs = [
|
||||
("f9beb4d9", Network::Bitcoin),
|
||||
("0b110907", Network::Testnet),
|
||||
("fabfb5da", Network::Regtest),
|
||||
("0a03cf40", Network::Signet),
|
||||
];
|
||||
|
||||
for (magic_str, network) in &known_network_magic_strs {
|
||||
let magic: Magic = Magic::from_str(magic_str).unwrap();
|
||||
assert_eq!(Network::try_from(magic).unwrap(), *network);
|
||||
assert_eq!(&magic.to_string(), magic_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ use crate::network::{message_network, message_bloom};
|
|||
use crate::network::message_blockdata;
|
||||
use crate::network::message_filter;
|
||||
use crate::network::message_compact_blocks;
|
||||
use crate::network::constants::Magic;
|
||||
use crate::consensus::encode::{CheckedData, Decodable, Encodable, VarInt};
|
||||
use crate::consensus::{encode, serialize};
|
||||
use crate::util::merkleblock::MerkleBlock;
|
||||
|
@ -149,7 +150,7 @@ crate::error::impl_std_error!(CommandStringError);
|
|||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct RawNetworkMessage {
|
||||
/// Magic bytes to identify the network these messages are meant for
|
||||
pub magic: u32,
|
||||
pub magic: Magic,
|
||||
/// The actual message data
|
||||
pub payload: NetworkMessage
|
||||
}
|
||||
|
@ -470,7 +471,7 @@ impl Decodable for RawNetworkMessage {
|
|||
mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
use super::{RawNetworkMessage, NetworkMessage, CommandString};
|
||||
use crate::network::constants::ServiceFlags;
|
||||
use crate::network::constants::{ServiceFlags, Magic, Network};
|
||||
use crate::consensus::encode::{deserialize, deserialize_partial, serialize};
|
||||
use crate::hashes::hex::FromHex;
|
||||
use crate::hashes::sha256d::Hash;
|
||||
|
@ -545,7 +546,7 @@ mod test {
|
|||
];
|
||||
|
||||
for msg in msgs {
|
||||
let raw_msg = RawNetworkMessage {magic: 57, payload: msg};
|
||||
let raw_msg = RawNetworkMessage {magic: Magic::from_bytes([57, 0, 0, 0]), payload: msg};
|
||||
assert_eq!(deserialize::<RawNetworkMessage>(&serialize(&raw_msg)).unwrap(), raw_msg);
|
||||
}
|
||||
|
||||
|
@ -573,7 +574,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn serialize_verack_test() {
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::Verack }),
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::Verack }),
|
||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x61,
|
||||
0x63, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]);
|
||||
|
@ -581,7 +582,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn serialize_ping_test() {
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::Ping(100) }),
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::Ping(100) }),
|
||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x70, 0x69, 0x6e, 0x67,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x08, 0x00, 0x00, 0x00, 0x24, 0x67, 0xf1, 0x1d,
|
||||
|
@ -591,7 +592,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn serialize_mempool_test() {
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::MemPool }),
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::MemPool }),
|
||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x6d, 0x65, 0x6d, 0x70,
|
||||
0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]);
|
||||
|
@ -599,7 +600,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn serialize_getaddr_test() {
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::GetAddr }),
|
||||
assert_eq!(serialize(&RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::GetAddr }),
|
||||
vec![0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61,
|
||||
0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]);
|
||||
|
@ -611,7 +612,7 @@ mod test {
|
|||
&[0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61,
|
||||
0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]);
|
||||
let preimage = RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::GetAddr };
|
||||
let preimage = RawNetworkMessage { magic: Magic::from(Network::Bitcoin), payload: NetworkMessage::GetAddr };
|
||||
assert!(msg.is_ok());
|
||||
let msg : RawNetworkMessage = msg.unwrap();
|
||||
assert_eq!(preimage.magic, msg.magic);
|
||||
|
@ -640,7 +641,7 @@ mod test {
|
|||
|
||||
assert!(msg.is_ok());
|
||||
let msg = msg.unwrap();
|
||||
assert_eq!(msg.magic, 0xd9b4bef9);
|
||||
assert_eq!(msg.magic, Magic::from(Network::Bitcoin));
|
||||
if let NetworkMessage::Version(version_msg) = msg.payload {
|
||||
assert_eq!(version_msg.version, 70015);
|
||||
assert_eq!(version_msg.services, ServiceFlags::NETWORK | ServiceFlags::BLOOM | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED);
|
||||
|
@ -677,7 +678,7 @@ mod test {
|
|||
|
||||
let (msg, consumed) = msg.unwrap();
|
||||
assert_eq!(consumed, data.to_vec().len() - 2);
|
||||
assert_eq!(msg.magic, 0xd9b4bef9);
|
||||
assert_eq!(msg.magic, Magic::from(Network::Bitcoin));
|
||||
if let NetworkMessage::Version(version_msg) = msg.payload {
|
||||
assert_eq!(version_msg.version, 70015);
|
||||
assert_eq!(version_msg.services, ServiceFlags::NETWORK | ServiceFlags::BLOOM | ServiceFlags::WITNESS | ServiceFlags::NETWORK_LIMITED);
|
||||
|
|
Loading…
Reference in New Issue