diff --git a/src/block.rs b/src/block.rs index 4e8cf743..b064a339 100644 --- a/src/block.rs +++ b/src/block.rs @@ -23,8 +23,8 @@ use std::io; use crate::dynafed; use crate::hashes::{Hash, sha256}; use crate::Transaction; -use crate::encode::{self, Encodable, Decodable, serialize}; -use crate::{BlockHash, Script, TxMerkleNode, VarInt}; +use crate::encode::{self, serialize, Decodable, Encodable, VarInt}; +use crate::{BlockHash, Script, TxMerkleNode}; /// Data related to block signatures #[derive(Clone, Debug, Eq, Hash, PartialEq)] diff --git a/src/encode.rs b/src/encode.rs index b161abf6..95ade56c 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -18,7 +18,7 @@ use std::io::Cursor; use std::{error, fmt, io, mem}; -use bitcoin::consensus::encode as btcenc; +use bitcoin::ScriptBuf; use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak}; use crate::hashes::{sha256, Hash}; @@ -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 @@ -38,7 +35,7 @@ pub enum Error { /// And I/O error Io(io::Error), /// A Bitcoin encoding error. - Bitcoin(btcenc::Error), + Bitcoin(bitcoin::consensus::encode::Error), /// Tried to allocate an oversized vector OversizedVectorAllocation { /// The capacity requested @@ -62,6 +59,8 @@ pub enum Error { HexError(crate::hex::Error), /// Got a time-based locktime when expecting a height-based one, or vice-versa BadLockTime(crate::LockTime), + /// VarInt was encoded in a non-minimal way. + NonMinimalVarInt, } impl fmt::Display for Error { @@ -87,6 +86,7 @@ impl fmt::Display for Error { Error::PsetError(ref e) => write!(f, "Pset Error: {}", e), Error::HexError(ref e) => write!(f, "Hex error {}", e), Error::BadLockTime(ref lt) => write!(f, "Invalid locktime {}", lt), + Error::NonMinimalVarInt => write!(f, "non-minimal varint"), } } } @@ -94,16 +94,14 @@ impl fmt::Display for Error { impl error::Error for Error { fn cause(&self) -> Option<&dyn error::Error> { match *self { - Error::Bitcoin(ref e) => Some(e), Error::Secp256k1zkp(ref e) => Some(e), _ => None, } } } - #[doc(hidden)] -impl From for Error { - fn from(e: btcenc::Error) -> Error { +impl From for Error { + fn from(e: bitcoin::consensus::encode::Error) -> Error { Error::Bitcoin(e) } } @@ -206,46 +204,15 @@ 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 { - let vi_len = bitcoin::VarInt(data.len() as u64).consensus_encode(&mut s)?; + let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?; s.emit_slice(data)?; Ok(vi_len + data.len()) } -/// Implement Elements encodable traits for Bitcoin encodable types. -macro_rules! impl_upstream { - ($type: ty) => { - impl Encodable for $type { - fn consensus_encode(&self, mut e: W) -> Result { - Ok(btcenc::Encodable::consensus_encode(self, &mut e)?) - } - } - - impl Decodable for $type { - fn consensus_decode(mut d: D) -> Result { - Ok(btcenc::Decodable::consensus_decode(&mut d)?) - } - } - }; -} -impl_upstream!(u8); -impl_upstream!(u32); -impl_upstream!(u64); -impl_upstream!([u8; 4]); -impl_upstream!([u8; 32]); -impl_upstream!(Box<[u8]>); -impl_upstream!([u8; 33]); -impl_upstream!(Vec); -impl_upstream!(Vec>); -impl_upstream!(btcenc::VarInt); -impl_upstream!(bitcoin::Transaction); -impl_upstream!(bitcoin::BlockHash); -impl_upstream!(bitcoin::ScriptBuf); -impl_upstream!(crate::hashes::sha256d::Hash); - // Specific locktime types (which appear in PSET/PSBT2 but not in rust-bitcoin PSBT) impl Encodable for crate::locktime::Height { fn consensus_encode(&self, s: S) -> Result { @@ -275,6 +242,77 @@ impl Decodable for crate::locktime::Time { } } +/// A variable sized integer. +pub struct VarInt(pub u64); +impl Encodable for VarInt { + 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 { + Ok(VarInt(d.read_varint()?)) + } +} +impl VarInt { + /// returns the byte size used if this var int is serialized + pub fn size(&self) -> usize { + match self.0 { + 0..=0xFC => 1, + 0xFD..=0xFFFF => 3, + 0x10000..=0xFFFFFFFF => 5, + _ => 9, + } + } +} + +// Primitive types +macro_rules! impl_int { + ($ty:ident, $meth_dec:ident, $meth_enc:ident) => { + impl Encodable for $ty { + 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 { + crate::ReadExt::$meth_dec(&mut r) + } + } + }; +} + +impl_int!(u8, read_u8, emit_u8); +impl_int!(u16, read_u16, emit_u16); +impl_int!(u32, read_u32, emit_u32); +impl_int!(u64, read_u64, emit_u64); + +impl Encodable for bitcoin::ScriptBuf { + fn consensus_encode(&self, w: W) -> Result { + consensus_encode_with_size(self.as_script().as_bytes(), w) + } +} +impl Decodable for bitcoin::ScriptBuf { + fn consensus_decode(d: D) -> Result { + let bytes = Vec::::consensus_decode(d)?; + Ok(ScriptBuf::from_bytes(bytes)) + } +} + +impl Encodable for bitcoin::hashes::sha256d::Hash { + fn consensus_encode(&self, mut w: W) -> Result { + self.as_byte_array().consensus_encode(&mut w) + } +} +impl Decodable for bitcoin::hashes::sha256d::Hash { + fn consensus_decode(d: D) -> Result { + Ok(Self::from_byte_array( + <::Bytes>::consensus_decode(d)?, + )) + } +} + // Vectors macro_rules! impl_vec { ($type: ty) => { @@ -282,7 +320,7 @@ macro_rules! impl_vec { #[inline] fn consensus_encode(&self, mut s: S) -> Result { let mut len = 0; - len += btcenc::VarInt(self.len() as u64).consensus_encode(&mut s)?; + len += VarInt(self.len() as u64).consensus_encode(&mut s)?; for c in self.iter() { len += c.consensus_encode(&mut s)?; } @@ -293,7 +331,7 @@ macro_rules! impl_vec { impl Decodable for Vec<$type> { #[inline] fn consensus_decode(mut d: D) -> Result { - let len = btcenc::VarInt::consensus_decode(&mut d)?.0; + let len = VarInt::consensus_decode(&mut d)?.0; let byte_size = (len as usize) .checked_mul(mem::size_of::<$type>()) .ok_or(self::Error::ParseFailed("Invalid length"))?; @@ -316,6 +354,60 @@ impl_vec!(TxIn); impl_vec!(TxOut); impl_vec!(Transaction); impl_vec!(TapLeafHash); +impl_vec!(Vec); // Vec> + +macro_rules! impl_array { + ( $size:literal ) => { + impl Encodable for [u8; $size] { + #[inline] + fn consensus_encode( + &self, + mut w: W, + ) -> core::result::Result { + w.emit_slice(&self[..])?; + Ok($size) + } + } + + impl Decodable for [u8; $size] { + #[inline] + fn consensus_decode(mut r: R) -> core::result::Result { + let mut ret = [0; $size]; + r.read_slice(&mut ret)?; + Ok(ret) + } + } + }; +} +impl_array!(4); +impl_array!(32); +impl_array!(33); + +impl Encodable for Box<[u8]> { + fn consensus_encode(&self, mut w: W) -> Result { + consensus_encode_with_size(&self[..], &mut w) + } +} +impl Decodable for Box<[u8]> { + fn consensus_decode(d: D) -> Result { + let v = Vec::::consensus_decode(d)?; + Ok(v.into()) + } +} + +impl Encodable for Vec { + fn consensus_encode(&self, mut w: W) -> Result { + consensus_encode_with_size(&self[..], &mut w) + } +} +impl Decodable for Vec { + 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)?; + Ok(v) + } +} macro_rules! impl_box_option { ($type: ty) => { 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 e784896f..445e1bc4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,54 +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 ::bitcoin::consensus::encode::VarInt; +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/map/global.rs b/src/pset/map/global.rs index 0496c6d8..bb6ddc64 100644 --- a/src/pset/map/global.rs +++ b/src/pset/map/global.rs @@ -20,11 +20,11 @@ use std::{ io::{self, Cursor, Read}, }; -use crate::encode; +use crate::encode::{self, VarInt}; use crate::encode::Decodable; use crate::endian::u32_to_array_le; use crate::pset::{self, map::Map, raw, Error}; -use crate::{LockTime, VarInt}; +use crate::LockTime; use bitcoin::bip32::{ChildNumber, DerivationPath, Xpub, Fingerprint, KeySource}; use secp256k1_zkp::Tweak; diff --git a/src/pset/raw.rs b/src/pset/raw.rs index 48988226..e29a145c 100644 --- a/src/pset/raw.rs +++ b/src/pset/raw.rs @@ -20,11 +20,8 @@ use std::{fmt, io}; use super::Error; -use crate::encode::{ - self, deserialize, serialize, Decodable, Encodable, ReadExt, WriteExt, MAX_VEC_SIZE, -}; +use crate::encode::{self, deserialize, serialize, Decodable, Encodable, VarInt, MAX_VEC_SIZE}; use crate::hex; -use crate::VarInt; /// A PSET key in its raw byte form. #[derive(Debug, PartialEq, Hash, Eq, Clone, Ord, PartialOrd)] #[cfg_attr( @@ -182,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)?; @@ -198,7 +195,7 @@ where let prefix = Vec::::consensus_decode(&mut d)?; let mut key = vec![]; - let subtype = Subtype::from(d.read_u8()?); + let subtype = Subtype::from(u8::consensus_decode(&mut d)?); d.read_to_end(&mut key)?; Ok(ProprietaryKey { diff --git a/src/pset/serialize.rs b/src/pset/serialize.rs index 0530c85a..87062d55 100644 --- a/src/pset/serialize.rs +++ b/src/pset/serialize.rs @@ -21,18 +21,20 @@ use std::convert::TryFrom; use std::io; use crate::confidential::{self, AssetBlindingFactor}; -use crate::encode::{self, deserialize, deserialize_partial, serialize, Decodable, Encodable}; +use crate::encode::{ + self, deserialize, deserialize_partial, serialize, Decodable, Encodable, VarInt, +}; use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; -use crate::{AssetId, BlockHash, Script, Transaction, TxOut, Txid}; use crate::hex::ToHex; +use crate::{AssetId, BlockHash, Script, Transaction, TxOut, Txid}; +use bitcoin; use bitcoin::bip32::{ChildNumber, Fingerprint, KeySource}; -use bitcoin::{self, VarInt}; -use bitcoin::{PublicKey, key::XOnlyPublicKey}; +use bitcoin::{key::XOnlyPublicKey, PublicKey}; use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak}; use super::map::{PsbtSighashType, TapTree}; use crate::schnorr; -use crate::taproot::{ControlBlock, LeafVersion, TapNodeHash, TapLeafHash}; +use crate::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapNodeHash}; use crate::sighash::SchnorrSighashType; use crate::taproot::TaprootBuilder; @@ -66,7 +68,6 @@ impl_pset_de_serialize!(crate::Sequence); impl_pset_de_serialize!(crate::locktime::Height); impl_pset_de_serialize!(crate::locktime::Time); impl_pset_de_serialize!([u8; 32]); -impl_pset_de_serialize!(VarInt); impl_pset_de_serialize!(Vec>); // scriptWitness impl_pset_hash_de_serialize!(Txid); impl_pset_hash_de_serialize!(ripemd160::Hash); @@ -78,11 +79,34 @@ impl_pset_hash_de_serialize!(TapLeafHash); impl_pset_hash_de_serialize!(TapNodeHash); // required for pegin bitcoin::Transactions -impl_pset_de_serialize!(bitcoin::Transaction); +impl Deserialize for bitcoin::Transaction { + fn deserialize(bytes: &[u8]) -> Result { + Ok(bitcoin::consensus::encode::deserialize(bytes)?) + } +} +impl Serialize for bitcoin::Transaction { + fn serialize(&self) -> Vec { + bitcoin::consensus::encode::serialize(self) + } +} // taproot impl_pset_de_serialize!(Vec); +impl Serialize for VarInt { + fn serialize(&self) -> Vec { + let mut v = vec![]; + self.consensus_encode(&mut v).expect("vec don't errors"); + v + } +} + +impl Deserialize for VarInt { + fn deserialize(bytes: &[u8]) -> Result { + VarInt::consensus_decode(bytes) + } +} + impl Serialize for Tweak { fn serialize(&self) -> Vec { encode::serialize(self.as_ref()) @@ -105,7 +129,8 @@ impl Serialize for AssetBlindingFactor { impl Deserialize for AssetBlindingFactor { fn deserialize(bytes: &[u8]) -> Result { let x = deserialize::<[u8; 32]>(bytes)?; - AssetBlindingFactor::from_slice(&x).map_err(|_| encode::Error::ParseFailed("invalid AssetBlindingFactor")) + AssetBlindingFactor::from_slice(&x) + .map_err(|_| encode::Error::ParseFailed("invalid AssetBlindingFactor")) } }