Merge rust-bitcoin/rust-bitcoin#968: Refactor address byte swapping

07c75304d2 Refactor address byte swapping (Tobin C. Harding)

Pull request description:

  Refactor address byte swapping

  When encoding a `network::Address` two of the fields are encoded
  big-endian instead of little-endian as is done by `consensus_encode`. In
  order to achieve this we have a helper function `addr_to_be` that swaps
  the bytes. This function is miss-named because it is not converting to a
  specific endian-ness (which implies different behaviour on machines with
  different endian-ness) but is reversing the byte order irrespective of
  the underlying architecture.

  - Remove function `addr_to_be`
  - Inline the endian-ness code when encoding an address
  - Remove TODO and use `to_be_bytes` when encoding port
  - Add a function for reading big-endian bytes `read_be_address`
  - Use `read_be_address` when decoding `Address` and `Addrv2`

  Refactor only, no logic changes. Code path is already covered by
  unit tests.

ACKs for top commit:
  apoelstra:
    ACK 07c75304d2
  Kixunil:
    ACK 07c75304d2

Tree-SHA512: 186bc86512e264a7b306f3bc2e18d1619f3cd84fc54412148cfc2663e8d6e9616ea9e2fe19eafec72d76cc11367a9b39cac2b73210d9e43eb8f453bd253b33de
This commit is contained in:
Andrew Poelstra 2022-05-24 17:40:56 +00:00
commit 324fa0f7be
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 25 additions and 20 deletions

View File

@ -67,25 +67,19 @@ impl Address {
} }
} }
fn addr_to_be(addr: [u16; 8]) -> [u16; 8] {
// consensus_encode always encodes in LE, and we want to encode in BE.
// this utility fn swap bytes before encoding so that the encoded result will be BE
let mut result = addr;
for word in &mut result {
*word = word.swap_bytes();
}
result
}
impl Encodable for Address { impl Encodable for Address {
#[inline] #[inline]
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, io::Error> { fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, io::Error> {
let len = self.services.consensus_encode(&mut s)? let mut len = self.services.consensus_encode(&mut s)?;
+ addr_to_be(self.address).consensus_encode(&mut s)?
for word in &self.address {
s.write_all(&word.to_be_bytes())?;
len += 2;
}
s.write_all(&self.port.to_be_bytes())?;
len += 2;
// consensus_encode always encodes in LE, and we want to encode in BE.
//TODO `len += io::Write::write(&mut e, &self.port.to_be_bytes())?;` when MSRV >= 1.32
+ self.port.swap_bytes().consensus_encode(s)?;
Ok(len) Ok(len)
} }
} }
@ -95,12 +89,24 @@ impl Decodable for Address {
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> { fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
Ok(Address { Ok(Address {
services: Decodable::consensus_decode(&mut d)?, services: Decodable::consensus_decode(&mut d)?,
address: addr_to_be(Decodable::consensus_decode(&mut d)?), address: read_be_address(&mut d)?,
port: u16::swap_bytes(Decodable::consensus_decode(d)?) port: u16::swap_bytes(Decodable::consensus_decode(d)?)
}) })
} }
} }
/// Read a big-endian address from reader.
fn read_be_address<R: io::Read>(mut r: R) -> Result<[u16; 8], encode::Error> {
let mut address = [0u16; 8];
let mut buf = [0u8; 2];
for word in &mut address {
io::Read::read_exact(&mut r, &mut buf)?;
*word = u16::from_be_bytes(buf)
}
Ok(address)
}
impl fmt::Debug for Address { impl fmt::Debug for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ipv6 = Ipv6Addr::from(self.address); let ipv6 = Ipv6Addr::from(self.address);
@ -180,7 +186,7 @@ impl Decodable for AddrV2 {
if len != 16 { if len != 16 {
return Err(encode::Error::ParseFailed("Invalid IPv6 address")); return Err(encode::Error::ParseFailed("Invalid IPv6 address"));
} }
let addr: [u16; 8] = addr_to_be(Decodable::consensus_decode(&mut d)?); let addr: [u16; 8] = read_be_address(&mut d)?;
if addr[0..3] == ONION { if addr[0..3] == ONION {
return Err(encode::Error::ParseFailed("OnionCat address sent with IPv6 network id")); return Err(encode::Error::ParseFailed("OnionCat address sent with IPv6 network id"));
} }
@ -214,12 +220,11 @@ impl Decodable for AddrV2 {
if len != 16 { if len != 16 {
return Err(encode::Error::ParseFailed("Invalid CJDNS address")); return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
} }
let addr: [u16; 8] = Decodable::consensus_decode(&mut d)?; let addr: [u16; 8] = read_be_address(&mut d)?;
// check the first byte for the CJDNS marker // check the first byte for the CJDNS marker
if addr[0] as u8 != 0xFC { if addr[0] != u16::from_be_bytes([0xFC, 0x00]) {
return Err(encode::Error::ParseFailed("Invalid CJDNS address")); return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
} }
let addr = addr_to_be(addr);
AddrV2::Cjdns(Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7])) AddrV2::Cjdns(Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]))
}, },
_ => { _ => {