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..61f04124 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -18,7 +18,6 @@ use std::io::Cursor; use std::{error, fmt, io, mem}; -use bitcoin::consensus::encode as btcenc; use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak}; use crate::hashes::{sha256, Hash}; @@ -38,7 +37,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 +61,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 +88,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 +96,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) } } @@ -210,42 +210,11 @@ 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 +244,128 @@ 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) + } + } + } +} +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)), + } + } +} +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 { + Ok(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, mut w: W) -> Result { + Ok(bitcoin::consensus::encode::Encodable::consensus_encode( + &self, &mut w, + )?) + } +} +impl Decodable for bitcoin::ScriptBuf { + fn consensus_decode(mut d: D) -> Result { + Ok(bitcoin::consensus::encode::Decodable::consensus_decode( + &mut d, + )?) + } +} + +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 +373,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 +384,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 +407,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/lib.rs b/src/lib.rs index e784896f..65904986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,6 @@ 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::block::ExtData as BlockExtData; -pub use ::bitcoin::consensus::encode::VarInt; pub use crate::fast_merkle_root::fast_merkle_root; pub use crate::hash_types::*; pub use crate::issuance::{AssetId, ContractHash}; 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..f276be80 100644 --- a/src/pset/raw.rs +++ b/src/pset/raw.rs @@ -21,10 +21,9 @@ use std::{fmt, io}; use super::Error; use crate::encode::{ - self, deserialize, serialize, Decodable, Encodable, ReadExt, WriteExt, MAX_VEC_SIZE, + self, deserialize, serialize, Decodable, Encodable, VarInt, WriteExt, 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( @@ -198,7 +197,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")) } }