diff --git a/chronik/bitcoinsuite-core/src/script/script.rs b/chronik/bitcoinsuite-core/src/script/script.rs --- a/chronik/bitcoinsuite-core/src/script/script.rs +++ b/chronik/bitcoinsuite-core/src/script/script.rs @@ -5,6 +5,7 @@ use bytes::Bytes; use crate::{ + error::DataError, hash::{Hashed, ShaRmd160}, script::{opcode::*, PubKey, ScriptMut, ScriptOpIter, UncompressedPubKey}, ser::{BitcoinSer, BitcoinSerializer}, @@ -221,15 +222,23 @@ fn ser_to(&self, bytes: &mut S) { self.0.ser_to(bytes) } + + fn deser(data: &mut Bytes) -> Result { + Ok(Script(Bytes::deser(data)?)) + } } #[cfg(test)] mod tests { + use bytes::Bytes; + use crate::{script::Script, ser::BitcoinSer}; fn verify_ser(a: Script, b: &[u8]) { assert_eq!(a.ser().as_ref(), b); assert_eq!(a.ser_len(), b.len()); + let mut bytes = Bytes::copy_from_slice(b); + assert_eq!(a, Script::deser(&mut bytes).unwrap()); } #[test] diff --git a/chronik/bitcoinsuite-core/src/ser.rs b/chronik/bitcoinsuite-core/src/ser.rs --- a/chronik/bitcoinsuite-core/src/ser.rs +++ b/chronik/bitcoinsuite-core/src/ser.rs @@ -6,6 +6,11 @@ use bytes::{Bytes, BytesMut}; +use crate::{ + bytes::{read_array, read_bytes}, + error::DataError, +}; + /// Serializer for implementors of [`BitcoinSer`]. pub trait BitcoinSerializer { /// Serialize the given slice of data. @@ -13,7 +18,7 @@ } /// Trait for serializing data using the serialization Bitcoin is using. -pub trait BitcoinSer { +pub trait BitcoinSer: Sized { /// Serialize to the given serializer fn ser_to(&self, bytes: &mut S); @@ -31,6 +36,9 @@ self.ser_to(&mut len); len } + + /// Deserialize the given bytes to `Self` + fn deser(data: &mut Bytes) -> Result; } impl BitcoinSerializer for BytesMut { @@ -51,12 +59,21 @@ write_compact_size(bytes, self.len() as u64); bytes.put(self.as_ref()); } + + fn deser(data: &mut Bytes) -> Result { + let size = read_compact_size(data)?; + read_bytes(data, size as usize) + } } impl BitcoinSer for [u8; N] { fn ser_to(&self, bytes: &mut S) { bytes.put(self.as_ref()); } + + fn deser(data: &mut Bytes) -> Result { + read_array::(data) + } } impl BitcoinSer for Vec { @@ -66,12 +83,25 @@ part.ser_to(bytes); } } + + fn deser(data: &mut Bytes) -> Result { + let size = read_compact_size(data)? as usize; + let mut entries = Vec::with_capacity(size.min(0x10000)); + for _ in 0..size { + entries.push(T::deser(data)?); + } + Ok(entries) + } } impl BitcoinSer for bool { fn ser_to(&self, bytes: &mut S) { bytes.put(&[*self as u8]); } + + fn deser(data: &mut Bytes) -> Result { + Ok(read_array::<1>(data)?[0] != 0) + } } macro_rules! integer_impls { @@ -81,6 +111,11 @@ fn ser_to(&self, bytes: &mut S) { bytes.put(&self.to_le_bytes()) } + + fn deser(data: &mut Bytes) -> Result { + let value = $T::from_le_bytes(read_array::<$SIZE>(data)?); + Ok(value) + } } )+ } @@ -108,15 +143,29 @@ } } +fn read_compact_size(bytes: &mut Bytes) -> Result { + let first_byte = read_array::<1>(bytes)?[0]; + match first_byte { + 0..=0xfc => Ok(first_byte as u64), + 0xfd => Ok(u16::from_le_bytes(read_array::<2>(bytes)?) as u64), + 0xfe => Ok(u32::from_le_bytes(read_array::<4>(bytes)?) as u64), + 0xff => Ok(u64::from_le_bytes(read_array::<8>(bytes)?)), + } +} + #[cfg(test)] mod tests { + use std::fmt::Debug; + use bytes::Bytes; use crate::ser::BitcoinSer; - fn verify_ser(a: T, b: &[u8]) { + fn verify_ser(a: T, b: &[u8]) { assert_eq!(a.ser().as_ref(), b); assert_eq!(a.ser_len(), b.len()); + let mut bytes = Bytes::copy_from_slice(b); + assert_eq!(a, T::deser(&mut bytes).unwrap()); } #[test] diff --git a/chronik/bitcoinsuite-core/src/tx/tx.rs b/chronik/bitcoinsuite-core/src/tx/tx.rs --- a/chronik/bitcoinsuite-core/src/tx/tx.rs +++ b/chronik/bitcoinsuite-core/src/tx/tx.rs @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. use crate::{ + error::DataError, script::Script, ser::{BitcoinSer, BitcoinSerializer}, tx::TxId, @@ -145,12 +146,26 @@ self.outputs.ser_to(bytes); self.locktime.ser_to(bytes); } + + fn deser(data: &mut bytes::Bytes) -> Result { + Ok(TxMut { + version: BitcoinSer::deser(data)?, + inputs: BitcoinSer::deser(data)?, + outputs: BitcoinSer::deser(data)?, + locktime: BitcoinSer::deser(data)?, + }) + } } impl BitcoinSer for Tx { fn ser_to(&self, bytes: &mut S) { TxMut::ser_to(self, bytes) } + + fn deser(data: &mut bytes::Bytes) -> Result { + let tx = TxMut::deser(data)?; + Ok(Tx::with_txid(TxId::from_tx(&tx), tx)) + } } impl BitcoinSer for OutPoint { @@ -158,6 +173,13 @@ self.txid.ser_to(bytes); self.out_idx.ser_to(bytes); } + + fn deser(data: &mut bytes::Bytes) -> Result { + Ok(OutPoint { + txid: BitcoinSer::deser(data)?, + out_idx: BitcoinSer::deser(data)?, + }) + } } impl BitcoinSer for TxInput { @@ -166,6 +188,15 @@ self.script.ser_to(bytes); self.sequence.ser_to(bytes); } + + fn deser(data: &mut bytes::Bytes) -> Result { + Ok(TxInput { + prev_out: BitcoinSer::deser(data)?, + script: BitcoinSer::deser(data)?, + sequence: BitcoinSer::deser(data)?, + coin: None, + }) + } } impl BitcoinSer for TxOutput { @@ -173,10 +204,19 @@ self.value.ser_to(bytes); self.script.ser_to(bytes); } + + fn deser(data: &mut bytes::Bytes) -> Result { + Ok(TxOutput { + value: BitcoinSer::deser(data)?, + script: BitcoinSer::deser(data)?, + }) + } } #[cfg(test)] mod tests { + use bytes::Bytes; + use crate::{ script::Script, ser::BitcoinSer, @@ -186,9 +226,14 @@ fn verify_ser(tx: TxMut, ser: &[u8]) { assert_eq!(tx.ser().as_ref(), ser); assert_eq!(tx.ser_len(), ser.len()); - let tx = Tx::with_txid(TxId::from([12; 32]), tx); + let mut bytes = Bytes::copy_from_slice(ser); + assert_eq!(tx, TxMut::deser(&mut bytes).unwrap()); + + let tx = Tx::with_txid(TxId::from_tx(&tx), tx); assert_eq!(tx.ser().as_ref(), ser); assert_eq!(tx.ser_len(), ser.len()); + let mut bytes = Bytes::copy_from_slice(ser); + assert_eq!(tx, Tx::deser(&mut bytes).unwrap()); } #[test] diff --git a/chronik/bitcoinsuite-core/src/tx/txid.rs b/chronik/bitcoinsuite-core/src/tx/txid.rs --- a/chronik/bitcoinsuite-core/src/tx/txid.rs +++ b/chronik/bitcoinsuite-core/src/tx/txid.rs @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. use crate::{ + bytes::read_array, error::DataError, hash::{Hashed, Sha256d}, ser::{BitcoinSer, BitcoinSerializer}, @@ -129,6 +130,10 @@ fn ser_to(&self, bytes: &mut S) { bytes.put(self.as_bytes()) } + + fn deser(data: &mut bytes::Bytes) -> Result { + Ok(TxId(Sha256d(read_array(data)?))) + } } #[cfg(test)]