Drop byteorder dependency

Taking an external dependency just to convert ints to byte arrays
is somewhat of a waste, especially when Rust isn't very aggressive
about doing cross-crate LTO.

Note that the latest LLVM pattern-matches this, and while I haven't
tested it, that should mean this means no loss of optimization.
This commit is contained in:
Matt Corallo 2019-10-17 17:40:45 -04:00
parent f1f7718b6c
commit acb43af981
11 changed files with 175 additions and 56 deletions

View File

@ -22,7 +22,6 @@ use-serde = ["hex", "serde", "bitcoin_hashes/serde", "secp256k1/serde"]
[dependencies]
bech32 = "0.7.1"
byteorder = "1.2"
bitcoin_hashes = "0.7"
bitcoinconsensus = { version = "0.17", optional = true }
serde = { version = "1", optional = true }

View File

@ -165,15 +165,13 @@ impl BlockHeader {
/// Checks that the proof-of-work for the block is valid.
pub fn validate_pow(&self, required_target: &Uint256) -> Result<(), util::Error> {
use byteorder::{ByteOrder, LittleEndian};
let target = &self.target();
if target != required_target {
return Err(BlockBadTarget);
}
let data: [u8; 32] = self.bitcoin_hash().into_inner();
let mut ret = [0u64; 4];
LittleEndian::read_u64_into(&data, &mut ret);
util::endian::bytes_to_u64_slice_le(&data, &mut ret);
let hash = &Uint256(ret);
if hash <= target { Ok(()) } else { Err(BlockBadProofOfWork) }
}

View File

@ -23,13 +23,13 @@
//! This module provides the structures and functions needed to support transactions.
//!
use byteorder::{LittleEndian, WriteBytesExt};
use std::default::Default;
use std::{fmt, io};
use hashes::{self, sha256d, Hash};
use hashes::hex::FromHex;
use util::endian;
use util::hash::BitcoinHash;
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
use blockdata::script::Script;
@ -358,7 +358,7 @@ impl Transaction {
};
// hash the result
let mut raw_vec = serialize(&tx);
raw_vec.write_u32::<LittleEndian>(sighash_u32).unwrap();
raw_vec.extend_from_slice(&endian::u32_to_array_le(sighash_u32));
sha256d::Hash::hash(&raw_vec)
}

View File

@ -35,12 +35,12 @@ use std::error;
use std::fmt;
use std::io;
use std::io::{Cursor, Read, Write};
use byteorder::{LittleEndian, WriteBytesExt, ReadBytesExt};
use hashes::hex::ToHex;
use hashes::{sha256d, Hash as HashTrait};
use secp256k1;
use util::endian;
use util::base58;
use util::psbt;
@ -276,39 +276,42 @@ macro_rules! encoder_fn {
($name:ident, $val_type:ty, $writefn:ident) => {
#[inline]
fn $name(&mut self, v: $val_type) -> Result<(), Error> {
WriteBytesExt::$writefn::<LittleEndian>(self, v).map_err(Error::Io)
self.write_all(&endian::$writefn(v)).map_err(Error::Io)
}
}
}
macro_rules! decoder_fn {
($name:ident, $val_type:ty, $readfn:ident) => {
($name:ident, $val_type:ty, $readfn:ident, $byte_len: expr) => {
#[inline]
fn $name(&mut self) -> Result<$val_type, Error> {
ReadBytesExt::$readfn::<LittleEndian>(self).map_err(Error::Io)
assert_eq!(::std::mem::size_of::<$val_type>(), $byte_len); // size_of isn't a constfn in 1.22
let mut val = [0; $byte_len];
self.read_exact(&mut val[..]).map_err(Error::Io)?;
Ok(endian::$readfn(&val))
}
}
}
impl<W: Write> WriteExt for W {
encoder_fn!(emit_u64, u64, write_u64);
encoder_fn!(emit_u32, u32, write_u32);
encoder_fn!(emit_u16, u16, write_u16);
encoder_fn!(emit_i64, i64, write_i64);
encoder_fn!(emit_i32, i32, write_i32);
encoder_fn!(emit_i16, i16, write_i16);
encoder_fn!(emit_u64, u64, u64_to_array_le);
encoder_fn!(emit_u32, u32, u32_to_array_le);
encoder_fn!(emit_u16, u16, u16_to_array_le);
encoder_fn!(emit_i64, i64, i64_to_array_le);
encoder_fn!(emit_i32, i32, i32_to_array_le);
encoder_fn!(emit_i16, i16, i16_to_array_le);
#[inline]
fn emit_i8(&mut self, v: i8) -> Result<(), Error> {
self.write_i8(v).map_err(Error::Io)
self.write_all(&[v as u8]).map_err(Error::Io)
}
#[inline]
fn emit_u8(&mut self, v: u8) -> Result<(), Error> {
self.write_u8(v).map_err(Error::Io)
self.write_all(&[v]).map_err(Error::Io)
}
#[inline]
fn emit_bool(&mut self, v: bool) -> Result<(), Error> {
self.write_i8(if v {1} else {0}).map_err(Error::Io)
self.write_all(&[if v {1} else {0}]).map_err(Error::Io)
}
#[inline]
fn emit_slice(&mut self, v: &[u8]) -> Result<(), Error> {
@ -317,20 +320,24 @@ impl<W: Write> WriteExt for W {
}
impl<R: Read> ReadExt for R {
decoder_fn!(read_u64, u64, read_u64);
decoder_fn!(read_u32, u32, read_u32);
decoder_fn!(read_u16, u16, read_u16);
decoder_fn!(read_i64, i64, read_i64);
decoder_fn!(read_i32, i32, read_i32);
decoder_fn!(read_i16, i16, read_i16);
decoder_fn!(read_u64, u64, slice_to_u64_le, 8);
decoder_fn!(read_u32, u32, slice_to_u32_le, 4);
decoder_fn!(read_u16, u16, slice_to_u16_le, 2);
decoder_fn!(read_i64, i64, slice_to_i64_le, 8);
decoder_fn!(read_i32, i32, slice_to_i32_le, 4);
decoder_fn!(read_i16, i16, slice_to_i16_le, 2);
#[inline]
fn read_u8(&mut self) -> Result<u8, Error> {
ReadBytesExt::read_u8(self).map_err(Error::Io)
let mut slice = [0u8; 1];
self.read_exact(&mut slice)?;
Ok(slice[0])
}
#[inline]
fn read_i8(&mut self) -> Result<i8, Error> {
ReadBytesExt::read_i8(self).map_err(Error::Io)
let mut slice = [0u8; 1];
self.read_exact(&mut slice)?;
Ok(slice[0] as i8)
}
#[inline]
fn read_bool(&mut self) -> Result<bool, Error> {

View File

@ -52,7 +52,6 @@ pub extern crate secp256k1;
pub extern crate bech32;
#[cfg(any(test, feature = "serde"))] extern crate hex;
extern crate byteorder;
#[cfg(feature = "serde")] extern crate serde;
#[cfg(all(test, feature = "serde"))] #[macro_use] extern crate serde_derive; // for 1.22.0 compat
#[cfg(all(test, feature = "serde"))] extern crate serde_json;

View File

@ -23,7 +23,6 @@ use network::constants;
use consensus::{Encodable, Decodable, ReadExt};
use consensus::encode;
use std::io;
use byteorder::WriteBytesExt;
use network::message_network::RejectReason::{MALFORMED, INVALID, OBSOLETE, DUPLICATE, NONSTANDARD, DUST, CHECKPOINT, FEE};
use hashes::sha256d;
@ -107,7 +106,7 @@ pub enum RejectReason {
impl Encodable for RejectReason {
fn consensus_encode<W: io::Write>(&self, mut e: W) -> Result<usize, encode::Error> {
e.write_u8(*self as u8)?;
e.write_all(&[*self as u8])?;
Ok(1)
}
}

View File

@ -16,10 +16,10 @@
use std::{error, fmt, str, slice, iter};
use byteorder::{ByteOrder, LittleEndian};
use hashes::{sha256d, Hash};
use util::endian;
/// An error that might occur during base58 decoding
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error {
@ -162,8 +162,8 @@ pub fn from_check(data: &str) -> Result<Vec<u8>, Error> {
return Err(Error::TooShort(ret.len()));
}
let ck_start = ret.len() - 4;
let expected = LittleEndian::read_u32(&sha256d::Hash::hash(&ret[..ck_start])[..4]);
let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]);
let expected = endian::slice_to_u32_le(&sha256d::Hash::hash(&ret[..ck_start])[..4]);
let actual = endian::slice_to_u32_le(&ret[ck_start..(ck_start + 4)]);
if expected != actual {
return Err(Error::BadChecksum(expected, actual));
}

View File

@ -51,13 +51,13 @@ use std::fmt::{Display, Formatter};
use std::io::Cursor;
use hashes::{Hash, sha256d, siphash24};
use byteorder::{ByteOrder, LittleEndian};
use blockdata::block::Block;
use blockdata::script::Script;
use blockdata::transaction::OutPoint;
use consensus::{Decodable, Encodable};
use consensus::encode::VarInt;
use util::endian;
use util::hash::BitcoinHash;
/// Golomb encoding parameter as in BIP-158, see also https://gist.github.com/sipa/576d5f09c3b86c3b1b75598d799fc845
@ -155,8 +155,8 @@ impl<'a> BlockFilterWriter<'a> {
/// Create a block filter writer
pub fn new(writer: &'a mut io::Write, block: &'a Block) -> BlockFilterWriter<'a> {
let block_hash_as_int = block.bitcoin_hash().into_inner();
let k0 = LittleEndian::read_u64(&block_hash_as_int[0..8]);
let k1 = LittleEndian::read_u64(&block_hash_as_int[8..16]);
let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]);
let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]);
let writer = GCSFilterWriter::new(writer, k0, k1, M, P);
BlockFilterWriter { block, writer }
}
@ -208,8 +208,8 @@ impl BlockFilterReader {
/// Create a block filter reader
pub fn new(block_hash: &sha256d::Hash) -> BlockFilterReader {
let block_hash_as_int = block_hash.into_inner();
let k0 = LittleEndian::read_u64(&block_hash_as_int[0..8]);
let k1 = LittleEndian::read_u64(&block_hash_as_int[8..16]);
let k0 = endian::slice_to_u64_le(&block_hash_as_int[0..8]);
let k1 = endian::slice_to_u64_le(&block_hash_as_int[8..16]);
BlockFilterReader { reader: GCSFilterReader::new(k0, k1, M, P) }
}

View File

@ -17,17 +17,15 @@
//! at https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
use std::default::Default;
use std::io::Cursor;
use std::{error, fmt};
use std::str::FromStr;
#[cfg(feature = "serde")] use serde;
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
use hashes::{hex, hash160, sha512, Hash, HashEngine, Hmac, HmacEngine};
use secp256k1::{self, Secp256k1};
use network::constants::Network;
use util::base58;
use util::{base58, endian};
use util::key::{PublicKey, PrivateKey};
/// A chain code
@ -448,7 +446,6 @@ impl ExtendedPrivKey {
/// Private->Private child key derivation
pub fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
let mut be_n = [0; 4];
match i {
ChildNumber::Normal {..} => {
// Non-hardened key: compute public data and use that
@ -460,9 +457,8 @@ impl ExtendedPrivKey {
hmac_engine.input(&self.private_key[..]);
}
}
BigEndian::write_u32(&mut be_n, u32::from(i));
hmac_engine.input(&be_n);
hmac_engine.input(&endian::u32_to_array_be(u32::from(i)));
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
let mut sk = PrivateKey {
compressed: true,
@ -529,9 +525,7 @@ impl ExtendedPubKey {
ChildNumber::Normal { index: n } => {
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
hmac_engine.input(&self.public_key.key.serialize()[..]);
let mut be_n = [0; 4];
BigEndian::write_u32(&mut be_n, n);
hmac_engine.input(&be_n);
hmac_engine.input(&endian::u32_to_array_be(n));
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
@ -588,9 +582,7 @@ impl fmt::Display for ExtendedPrivKey {
}[..]);
ret[4] = self.depth as u8;
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
BigEndian::write_u32(&mut ret[9..13], u32::from(self.child_number));
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
ret[13..45].copy_from_slice(&self.chain_code[..]);
ret[45] = 0;
ret[46..78].copy_from_slice(&self.private_key[..]);
@ -608,7 +600,7 @@ impl FromStr for ExtendedPrivKey {
return Err(base58::Error::InvalidLength(data.len()));
}
let cn_int: u32 = Cursor::new(&data[9..13]).read_u32::<BigEndian>().unwrap();
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
let child_number: ChildNumber = ChildNumber::from(cn_int);
let network = if &data[0..4] == [0x04u8, 0x88, 0xAD, 0xE4] {
@ -647,9 +639,7 @@ impl fmt::Display for ExtendedPubKey {
}[..]);
ret[4] = self.depth as u8;
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
BigEndian::write_u32(&mut ret[9..13], u32::from(self.child_number));
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
ret[13..45].copy_from_slice(&self.chain_code[..]);
ret[45..78].copy_from_slice(&self.public_key.key.serialize()[..]);
fmt.write_str(&base58::check_encode_slice(&ret[..]))
@ -666,7 +656,7 @@ impl FromStr for ExtendedPubKey {
return Err(base58::Error::InvalidLength(data.len()));
}
let cn_int: u32 = Cursor::new(&data[9..13]).read_u32::<BigEndian>().unwrap();
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
let child_number: ChildNumber = ChildNumber::from(cn_int);
Ok(ExtendedPubKey {

125
src/util/endian.rs Normal file
View File

@ -0,0 +1,125 @@
macro_rules! define_slice_to_be {
($name: ident, $type: ty) => {
#[inline]
pub fn $name(slice: &[u8]) -> $type {
assert_eq!(slice.len(), ::std::mem::size_of::<$type>());
let mut res = 0;
for i in 0..::std::mem::size_of::<$type>() {
res |= (slice[i] as $type) << (::std::mem::size_of::<$type>() - i - 1)*8;
}
res
}
}
}
macro_rules! define_slice_to_le {
($name: ident, $type: ty) => {
#[inline]
pub fn $name(slice: &[u8]) -> $type {
assert_eq!(slice.len(), ::std::mem::size_of::<$type>());
let mut res = 0;
for i in 0..::std::mem::size_of::<$type>() {
res |= (slice[i] as $type) << i*8;
}
res
}
}
}
macro_rules! define_be_to_array {
($name: ident, $type: ty, $byte_len: expr) => {
#[inline]
pub fn $name(val: $type) -> [u8; $byte_len] {
assert_eq!(::std::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22
let mut res = [0; $byte_len];
for i in 0..$byte_len {
res[i] = ((val >> ($byte_len - i - 1)*8) & 0xff) as u8;
}
res
}
}
}
macro_rules! define_le_to_array {
($name: ident, $type: ty, $byte_len: expr) => {
#[inline]
pub fn $name(val: $type) -> [u8; $byte_len] {
assert_eq!(::std::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22
let mut res = [0; $byte_len];
for i in 0..$byte_len {
res[i] = ((val >> i*8) & 0xff) as u8;
}
res
}
}
}
define_slice_to_be!(slice_to_u32_be, u32);
define_be_to_array!(u32_to_array_be, u32, 4);
define_slice_to_le!(slice_to_u16_le, u16);
define_slice_to_le!(slice_to_u32_le, u32);
define_slice_to_le!(slice_to_u64_le, u64);
define_le_to_array!(u16_to_array_le, u16, 2);
define_le_to_array!(u32_to_array_le, u32, 4);
define_le_to_array!(u64_to_array_le, u64, 8);
#[inline]
pub fn i16_to_array_le(val: i16) -> [u8; 2] {
u16_to_array_le(val as u16)
}
#[inline]
pub fn slice_to_i16_le(slice: &[u8]) -> i16 {
slice_to_u16_le(slice) as i16
}
#[inline]
pub fn slice_to_i32_le(slice: &[u8]) -> i32 {
slice_to_u32_le(slice) as i32
}
#[inline]
pub fn i32_to_array_le(val: i32) -> [u8; 4] {
u32_to_array_le(val as u32)
}
#[inline]
pub fn slice_to_i64_le(slice: &[u8]) -> i64 {
slice_to_u64_le(slice) as i64
}
#[inline]
pub fn i64_to_array_le(val: i64) -> [u8; 8] {
u64_to_array_le(val as u64)
}
macro_rules! define_chunk_slice_to_int {
($name: ident, $type: ty, $converter: ident) => {
#[inline]
pub fn $name(inp: &[u8], outp: &mut [$type]) {
assert_eq!(inp.len(), outp.len() * ::std::mem::size_of::<$type>());
for (outp_val, data_bytes) in outp.iter_mut().zip(inp.chunks(::std::mem::size_of::<$type>())) {
*outp_val = $converter(data_bytes);
}
}
}
}
define_chunk_slice_to_int!(bytes_to_u64_slice_le, u64, slice_to_u64_le);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn endianness_test() {
assert_eq!(slice_to_u32_be(&[0xde, 0xad, 0xbe, 0xef]), 0xdeadbeef);
assert_eq!(u32_to_array_be(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]);
assert_eq!(slice_to_u16_le(&[0xad, 0xde]), 0xdead);
assert_eq!(slice_to_u32_le(&[0xef, 0xbe, 0xad, 0xde]), 0xdeadbeef);
assert_eq!(slice_to_u64_le(&[0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]), 0x1badcafedeadbeef);
assert_eq!(u16_to_array_le(0xdead), [0xad, 0xde]);
assert_eq!(u32_to_array_le(0xdeadbeef), [0xef, 0xbe, 0xad, 0xde]);
assert_eq!(u64_to_array_le(0x1badcafedeadbeef), [0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]);
}
#[test]
fn endian_chunk_test() {
let inp = [0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b, 0xfe, 0xca, 0xad, 0x1b, 0xce, 0xfa, 0x01, 0x02];
let mut out = [0; 2];
bytes_to_u64_slice_le(&inp, &mut out);
assert_eq!(out, [0x1badcafedeadbeef, 0x0201face1badcafe]);
}
}

View File

@ -30,6 +30,8 @@ pub mod psbt;
pub mod uint;
pub mod bip158;
pub(crate) mod endian;
use std::{error, fmt};
use network;