diff --git a/src/encode.rs b/src/encode.rs index fab99983..95ade56c 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -27,9 +27,6 @@ use crate::transaction::{Transaction, TxIn, TxOut}; pub use bitcoin::{self, consensus::encode::MAX_VEC_SIZE}; -// Use the ReadExt/WriteExt traits as is from upstream -pub use bitcoin::consensus::encode::{ReadExt, WriteExt}; - use crate::taproot::TapLeafHash; /// Encoding error @@ -207,7 +204,7 @@ impl Decodable for sha256::Midstate { } } -pub(crate) fn consensus_encode_with_size( +pub(crate) fn consensus_encode_with_size( data: &[u8], mut s: S, ) -> Result { @@ -245,64 +242,16 @@ impl Decodable for crate::locktime::Time { } } -// TODO reuse bitcoin's `WriteExt::emit_varint`, `ReadExt::read_varint` when available - /// A variable sized integer. pub struct VarInt(pub u64); impl Encodable for VarInt { - fn consensus_encode(&self, mut e: W) -> Result { - match self.0 { - i @ 0..=0xFC => { - e.emit_u8(i as u8)?; - Ok(1) - } - i @ 0xFD..=0xFFFF => { - e.emit_u8(0xFD)?; - e.emit_u16(i as u16)?; - Ok(3) - } - i @ 0x10000..=0xFFFFFFFF => { - e.emit_u8(0xFE)?; - e.emit_u32(i as u32)?; - Ok(5) - } - i => { - e.emit_u8(0xFF)?; - e.emit_u64(i)?; - Ok(9) - } - } + fn consensus_encode(&self, mut e: W) -> Result { + Ok(e.emit_varint(self.0)?) } } impl Decodable for VarInt { - fn consensus_decode(mut d: D) -> Result { - match d.read_u8()? { - 0xFF => { - let x = d.read_u64()?; - if x < 0x100000000 { - Err(Error::NonMinimalVarInt) - } else { - Ok(VarInt(x)) - } - } - 0xFE => { - let x = d.read_u32()?; - if x < 0x10000 { - Err(Error::NonMinimalVarInt) - } else { - Ok(VarInt(x as u64)) - } - } - 0xFD => { - let x = d.read_u16()?; - if x < 0xFD { - Err(Error::NonMinimalVarInt) - } else { - Ok(VarInt(x as u64)) - } - } - n => Ok(VarInt(n as u64)), - } + fn consensus_decode(mut d: D) -> Result { + Ok(VarInt(d.read_varint()?)) } } impl VarInt { @@ -321,14 +270,14 @@ impl VarInt { macro_rules! impl_int { ($ty:ident, $meth_dec:ident, $meth_enc:ident) => { impl Encodable for $ty { - fn consensus_encode(&self, mut w: W) -> Result { + fn consensus_encode(&self, mut w: W) -> Result { w.$meth_enc(*self)?; Ok(mem::size_of::<$ty>()) } } impl Decodable for $ty { - fn consensus_decode(mut r: R) -> Result { - Ok(ReadExt::$meth_dec(&mut r)?) + fn consensus_decode(mut r: R) -> Result { + crate::ReadExt::$meth_dec(&mut r) } } }; @@ -411,7 +360,7 @@ macro_rules! impl_array { ( $size:literal ) => { impl Encodable for [u8; $size] { #[inline] - fn consensus_encode( + fn consensus_encode( &self, mut w: W, ) -> core::result::Result { @@ -422,7 +371,7 @@ macro_rules! impl_array { impl Decodable for [u8; $size] { #[inline] - fn consensus_decode(mut r: R) -> core::result::Result { + fn consensus_decode(mut r: R) -> core::result::Result { let mut ret = [0; $size]; r.read_slice(&mut ret)?; Ok(ret) @@ -452,7 +401,7 @@ impl Encodable for Vec { } } impl Decodable for Vec { - fn consensus_decode(mut d: D) -> Result { + fn consensus_decode(mut d: D) -> Result { let s = VarInt::consensus_decode(&mut d)?.0 as usize; let mut v = vec![0; s]; d.read_slice(&mut v)?; diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 00000000..b7c1c543 --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,194 @@ +use std::io; + +use crate::encode; + +/// Extensions of `Write` to encode data as per Bitcoin consensus. +pub trait WriteExt: io::Write { + /// Outputs a 64-bit unsigned integer. + fn emit_u64(&mut self, v: u64) -> Result<(), io::Error>; + /// Outputs a 32-bit unsigned integer. + fn emit_u32(&mut self, v: u32) -> Result<(), io::Error>; + /// Outputs a 16-bit unsigned integer. + fn emit_u16(&mut self, v: u16) -> Result<(), io::Error>; + /// Outputs an 8-bit unsigned integer. + fn emit_u8(&mut self, v: u8) -> Result<(), io::Error>; + + /// Outputs a 64-bit signed integer. + fn emit_i64(&mut self, v: i64) -> Result<(), io::Error>; + /// Outputs a 32-bit signed integer. + fn emit_i32(&mut self, v: i32) -> Result<(), io::Error>; + /// Outputs a 16-bit signed integer. + fn emit_i16(&mut self, v: i16) -> Result<(), io::Error>; + /// Outputs an 8-bit signed integer. + fn emit_i8(&mut self, v: i8) -> Result<(), io::Error>; + + /// Outputs a variable sized integer. + fn emit_varint(&mut self, v: u64) -> Result; + + /// Outputs a boolean. + fn emit_bool(&mut self, v: bool) -> Result<(), io::Error>; + + /// Outputs a byte slice. + fn emit_slice(&mut self, v: &[u8]) -> Result; +} + +/// Extensions of `Read` to decode data as per Bitcoin consensus. +pub trait ReadExt: io::Read { + /// Reads a 64-bit unsigned integer. + fn read_u64(&mut self) -> Result; + /// Reads a 32-bit unsigned integer. + fn read_u32(&mut self) -> Result; + /// Reads a 16-bit unsigned integer. + fn read_u16(&mut self) -> Result; + /// Reads an 8-bit unsigned integer. + fn read_u8(&mut self) -> Result; + + /// Reads a 64-bit signed integer. + fn read_i64(&mut self) -> Result; + /// Reads a 32-bit signed integer. + fn read_i32(&mut self) -> Result; + /// Reads a 16-bit signed integer. + fn read_i16(&mut self) -> Result; + /// Reads an 8-bit signed integer. + fn read_i8(&mut self) -> Result; + + /// Reads a variable sized integer. + fn read_varint(&mut self) -> Result; + + /// Reads a boolean. + fn read_bool(&mut self) -> Result; + + /// Reads a byte slice. + fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), encode::Error>; +} + +macro_rules! encoder_fn { + ($name:ident, $val_type:ty) => { + #[inline] + fn $name(&mut self, v: $val_type) -> core::result::Result<(), io::Error> { + self.write_all(&v.to_le_bytes()) + } + }; +} + +macro_rules! decoder_fn { + ($name:ident, $val_type:ty, $byte_len: expr) => { + #[inline] + fn $name(&mut self) -> core::result::Result<$val_type, encode::Error> { + let mut val = [0; $byte_len]; + self.read_exact(&mut val[..]).map_err(encode::Error::Io)?; + Ok(<$val_type>::from_le_bytes(val)) + } + }; +} + +impl WriteExt for W { + encoder_fn!(emit_u64, u64); + encoder_fn!(emit_u32, u32); + encoder_fn!(emit_u16, u16); + encoder_fn!(emit_i64, i64); + encoder_fn!(emit_i32, i32); + encoder_fn!(emit_i16, i16); + + #[inline] + fn emit_i8(&mut self, v: i8) -> Result<(), io::Error> { + self.write_all(&[v as u8]) + } + #[inline] + fn emit_u8(&mut self, v: u8) -> Result<(), io::Error> { + self.write_all(&[v]) + } + #[inline] + fn emit_bool(&mut self, v: bool) -> Result<(), io::Error> { + self.write_all(&[v as u8]) + } + #[inline] + fn emit_slice(&mut self, v: &[u8]) -> Result { + self.write_all(v)?; + Ok(v.len()) + } + #[inline] + fn emit_varint(&mut self, v: u64) -> Result { + match v { + i @ 0..=0xFC => { + self.emit_u8(i as u8)?; + Ok(1) + } + i @ 0xFD..=0xFFFF => { + self.emit_u8(0xFD)?; + self.emit_u16(i as u16)?; + Ok(3) + } + i @ 0x10000..=0xFFFFFFFF => { + self.emit_u8(0xFE)?; + self.emit_u32(i as u32)?; + Ok(5) + } + i => { + self.emit_u8(0xFF)?; + self.emit_u64(i)?; + Ok(9) + } + } + } +} + +impl ReadExt for R { + decoder_fn!(read_u64, u64, 8); + decoder_fn!(read_u32, u32, 4); + decoder_fn!(read_u16, u16, 2); + decoder_fn!(read_i64, i64, 8); + decoder_fn!(read_i32, i32, 4); + decoder_fn!(read_i16, i16, 2); + + #[inline] + fn read_u8(&mut self) -> Result { + let mut slice = [0u8; 1]; + self.read_exact(&mut slice)?; + Ok(slice[0]) + } + #[inline] + fn read_i8(&mut self) -> Result { + let mut slice = [0u8; 1]; + self.read_exact(&mut slice)?; + Ok(slice[0] as i8) + } + #[inline] + fn read_bool(&mut self) -> Result { + ReadExt::read_i8(self).map(|bit| bit != 0) + } + #[inline] + fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), encode::Error> { + self.read_exact(slice).map_err(encode::Error::Io) + } + #[inline] + fn read_varint(&mut self) -> Result { + match self.read_u8()? { + 0xFF => { + let x = self.read_u64()?; + if x < 0x100000000 { + Err(encode::Error::NonMinimalVarInt) + } else { + Ok(x) + } + } + 0xFE => { + let x = self.read_u32()?; + if x < 0x10000 { + Err(encode::Error::NonMinimalVarInt) + } else { + Ok(x as u64) + } + } + 0xFD => { + let x = self.read_u16()?; + if x < 0xFD { + Err(encode::Error::NonMinimalVarInt) + } else { + Ok(x as u64) + } + } + n => Ok(n as u64), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 65904986..445e1bc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,53 +31,68 @@ pub extern crate bitcoin; /// Re-export of secp256k1-zkp crate pub extern crate secp256k1_zkp; /// Re-export of serde crate -#[cfg(feature = "serde")] #[macro_use] pub extern crate actual_serde as serde; -#[cfg(all(test, feature = "serde"))] extern crate serde_test; +#[cfg(feature = "serde")] +#[macro_use] +pub extern crate actual_serde as serde; +#[cfg(all(test, feature = "serde"))] +extern crate serde_test; -#[cfg(test)] extern crate rand; -#[cfg(test)] extern crate bincode; -#[cfg(any(test, feature = "serde_json"))] extern crate serde_json; +#[cfg(test)] +extern crate bincode; +#[cfg(test)] +extern crate rand; +#[cfg(any(test, feature = "serde_json"))] +extern crate serde_json; -#[macro_use] mod internal_macros; +#[macro_use] +mod internal_macros; pub mod address; pub mod blech32; +mod blind; mod block; pub mod confidential; pub mod dynafed; pub mod encode; mod error; +mod ext; mod fast_merkle_root; pub mod hash_types; pub mod hex; -pub mod locktime; pub mod issuance; +pub mod locktime; pub mod opcodes; -pub mod script; -mod transaction; -mod blind; mod parse; -pub mod sighash; pub mod pset; -pub mod taproot; pub mod schnorr; +pub mod script; #[cfg(feature = "serde")] mod serde_utils; +pub mod sighash; +pub mod taproot; +mod transaction; // consider making upstream public mod endian; // re-export bitcoin deps which we re-use pub use bitcoin::bech32; pub use bitcoin::hashes; // export everything at the top level so it can be used as `elements::Transaction` etc. -pub use crate::address::{Address, AddressParams, AddressError}; -pub use crate::transaction::{OutPoint, PeginData, PegoutData, EcdsaSighashType, TxIn, TxOut, TxInWitness, TxOutWitness, Transaction, AssetIssuance}; -pub use crate::transaction::Sequence; -pub use crate::blind::{ConfidentialTxOutError, TxOutSecrets, SurjectionInput, TxOutError, VerificationError, BlindError, UnblindError, BlindValueProofs, BlindAssetProofs, RangeProofMessage}; -pub use crate::block::{BlockHeader, Block}; +pub use crate::address::{Address, AddressError, AddressParams}; +pub use crate::blind::{ + BlindAssetProofs, BlindError, BlindValueProofs, ConfidentialTxOutError, RangeProofMessage, + SurjectionInput, TxOutError, TxOutSecrets, UnblindError, VerificationError, +}; pub use crate::block::ExtData as BlockExtData; +pub use crate::block::{Block, BlockHeader}; +pub use crate::ext::{ReadExt, WriteExt}; pub use crate::fast_merkle_root::fast_merkle_root; pub use crate::hash_types::*; pub use crate::issuance::{AssetId, ContractHash}; pub use crate::locktime::LockTime; +pub use crate::schnorr::{SchnorrSig, SchnorrSigError}; pub use crate::script::Script; pub use crate::sighash::SchnorrSighashType; -pub use crate::schnorr::{SchnorrSig, SchnorrSigError}; +pub use crate::transaction::Sequence; +pub use crate::transaction::{ + AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PegoutData, Transaction, TxIn, + TxInWitness, TxOut, TxOutWitness, +}; diff --git a/src/pset/raw.rs b/src/pset/raw.rs index f276be80..e29a145c 100644 --- a/src/pset/raw.rs +++ b/src/pset/raw.rs @@ -20,9 +20,7 @@ use std::{fmt, io}; use super::Error; -use crate::encode::{ - self, deserialize, serialize, Decodable, Encodable, VarInt, WriteExt, MAX_VEC_SIZE, -}; +use crate::encode::{self, deserialize, serialize, Decodable, Encodable, VarInt, MAX_VEC_SIZE}; use crate::hex; /// A PSET key in its raw byte form. #[derive(Debug, PartialEq, Hash, Eq, Clone, Ord, PartialOrd)] @@ -181,7 +179,7 @@ impl Encodable for ProprietaryKey where Subtype: Copy + From + Into, { - fn consensus_encode(&self, mut e: W) -> Result { + fn consensus_encode(&self, mut e: W) -> Result { let mut len = self.prefix.consensus_encode(&mut e)? + 1; e.emit_u8(self.subtype.into())?; len += e.write(&self.key)?;