Repository: LNP-BP/client_side_validation (language: rust)

Issue:
Encode miniscript structures in a binary way in strict_encoding
https://github.com/LNP-BP/rust-lnpbp/blob/95e66c5297fa0222b9a29c2e9b4fe82edeae2e49/strict_encoding/src/miniscript.rs#L37


Discussion / hints:

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index e0576934..86978f63 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -134,7 +134,7 @@ version = "0.5.0-rc.1"
 dependencies = [
  "commit_verify",
  "single_use_seals",
- "strict_encoding 1.4.0",
+ "strict_encoding 1.5.0-beta.1",
 ]
 
 [[package]]
@@ -675,7 +675,7 @@ dependencies = [
 
 [[package]]
 name = "strict_encoding"
-version = "1.4.0"
+version = "1.5.0-beta.1"
 dependencies = [
  "amplify",
  "bitcoin",
@@ -707,7 +707,7 @@ dependencies = [
  "amplify_derive",
  "proc-macro2",
  "quote",
- "strict_encoding 1.4.0",
+ "strict_encoding 1.5.0-beta.1",
  "syn",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index a4155d71..af86ab77 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,7 @@ name = "client_side_validation"
 path = "src/lib.rs"
 
 [dependencies]
-strict_encoding = { version = "1", path = "./strict_encoding" }
+strict_encoding = { version = "1.5.0-beta.1", path = "./strict_encoding" }
 commit_verify = { version = "0.5.0-rc.1", path = "./commit_verify" }
 single_use_seals = { version = "0.5.0-rc.1", path = "./single_use_seals" }
 
diff --git a/codecov.yml b/codecov.yml
index 74164a7a..8247d95d 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -10,7 +10,10 @@ coverage:
       default:
         target: 75%
         threshold: 1%
+        branches:
+          - master
     patch:
       default:
-        target: 90%
-        threshold: 1%
\ No newline at end of file
+        target: 80%
+        threshold: 1%
+        only_pulls: true
diff --git a/strict_encoding/Cargo.toml b/strict_encoding/Cargo.toml
index 1f4b99dc..6e3e4f64 100644
--- a/strict_encoding/Cargo.toml
+++ b/strict_encoding/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "strict_encoding"
-version = "1.4.0"
+version = "1.5.0-beta.1"
 license = "Apache-2.0"
 authors = ["Dr. Maxim Orlovsky <orlovsky@pandoracore.com>"]
 description = "Strict encoding: deterministic binary serialization for networking & client-side validation"
diff --git a/strict_encoding/src/bitcoin.rs b/strict_encoding/src/bitcoin.rs
index 7a64f005..f2ade74f 100644
--- a/strict_encoding/src/bitcoin.rs
+++ b/strict_encoding/src/bitcoin.rs
@@ -242,7 +242,7 @@ impl StrictDecode for bitcoin::PublicKey {
                 })?)
             }
             invalid_flag => Err(Error::DataIntegrityError(format!(
-                "Invalid public key encoding flag {}; must be either 0x02, 0x03 or 0x4",
+                "Invalid public key encoding flag {:#04x}; must be either 0x02, 0x03 or 0x04",
                 invalid_flag
             ))),
         }
diff --git a/strict_encoding/src/lib.rs b/strict_encoding/src/lib.rs
index ebdb0e25..735d0037 100644
--- a/strict_encoding/src/lib.rs
+++ b/strict_encoding/src/lib.rs
@@ -67,13 +67,13 @@ pub mod test_helpers;
 
 mod amplify_types;
 mod bitcoin;
-mod byte_str;
 mod collections;
 #[cfg(feature = "crypto")]
 mod crypto;
 #[cfg(feature = "miniscript")]
 mod miniscript;
 pub mod net;
+mod pointers;
 mod primitives;
 pub mod strategies;
 
diff --git a/strict_encoding/src/miniscript.rs b/strict_encoding/src/miniscript.rs
index 59e39e74..44c54fd3 100644
--- a/strict_encoding/src/miniscript.rs
+++ b/strict_encoding/src/miniscript.rs
@@ -12,98 +12,692 @@
 // You should have received a copy of the Apache 2.0 License along with this
 // software. If not, see <https://opensource.org/licenses/Apache-2.0>.
 
-use std::fmt::Display;
 use std::io;
-use std::str::FromStr;
 
-use miniscript::descriptor::DescriptorSinglePub;
-use miniscript::{policy, Descriptor, Miniscript, MiniscriptKey};
+use bitcoin::consensus::ReadExt;
+use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
+use miniscript::descriptor::{
+    Bare, DescriptorSinglePub, DescriptorXKey, InnerXKey, Pkh, Sh, ShInner,
+    Wildcard, Wpkh, Wsh, WshInner,
+};
+use miniscript::policy::concrete::Policy;
+use miniscript::{
+    Descriptor, DescriptorPublicKey, Miniscript, MiniscriptKey, Terminal,
+};
 
 use crate::{Error, StrictDecode, StrictEncode};
 
-impl StrictEncode for DescriptorSinglePub {
-    #[inline]
+const MS_FALSE: u8 = 0;
+const MS_TRUE: u8 = 1;
+
+const MS_KEY: u8 = 0x02;
+const MS_KEY_HASH: u8 = 0x03;
+const MS_THRESH: u8 = 0x20;
+const MS_MULTI: u8 = 0x21;
+
+const MS_AFTER: u8 = 0x09;
+const MS_OLDER: u8 = 0x08;
+const MS_SHA256: u8 = 0x04;
+const MS_HASH256: u8 = 0x05;
+const MS_RIPEMD160: u8 = 0x06;
+const MS_HASH160: u8 = 0x07;
+
+const MS_AND_V: u8 = 0x22;
+const MS_AND_B: u8 = 0x23;
+const MS_AND_OR: u8 = 0x24;
+const MS_OR_B: u8 = 0x28;
+const MS_OR_D: u8 = 0x29;
+const MS_OR_C: u8 = 0x2a;
+const MS_OR_I: u8 = 0x2b;
+
+const MS_ALT: u8 = 0x10;
+const MS_SWAP: u8 = 0x11;
+const MS_CHECK: u8 = 0x12;
+const MS_DUP_IF: u8 = 0x13;
+const MS_VERIFY: u8 = 0x18;
+const MS_NON_ZERO: u8 = 0x19;
+const MS_ZERO_NE: u8 = 0x1a;
+
+/// We need this shit because of rust compiler limitations.
+///
+/// The two macros below, as well as custom private functions `decode_policy`,
+/// `encode_miniscript` and `decode_miniscript` were introduced because rust
+/// compiler dies on recursion overflow each time when a generic type calls
+/// itself in a recursive mode passing one of its arguments by a mutable
+/// reference to itself. Thus, we had to split the implementation logic in such
+/// a way that we do not pass a mutable reference to a variable, and just re-use
+/// the reference instead. This contradicts rust API guidelines, but in fact it
+/// is a rust compiler who contradicts them, we just do not have other choice.
+macro_rules! strict_encode_ms {
+    ($encoder:ident; $tag:ident, $depth:expr, $($ms:expr),+) => { {
+        let mut len = 0usize;
+        $encoder.write_all(&$tag.strict_serialize()?)?;
+        len += 1;
+        $( len += encode_miniscript($ms, $encoder, $depth)?; )+
+        len
+    } };
+}
+
+macro_rules! strict_encode_seq {
+    ( $encoder:ident; $($item:expr),+ ) => {
+        {
+            let mut len = 0usize;
+            $(
+                let data = $item.strict_serialize()?;
+                $encoder.write_all(&data)?;
+                len += data.len();
+            )+
+            len
+        }
+    };
+}
+
+impl<Pk> StrictEncode for Policy<Pk>
+where
+    Pk: MiniscriptKey + StrictEncode,
+    <Pk as MiniscriptKey>::Hash: StrictEncode,
+{
     fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
-        Ok(strict_encode_list!(e; self.key, self.origin))
+        Ok(match self {
+            Policy::Unsatisfiable => MS_FALSE.strict_encode(e)?,
+            Policy::Trivial => MS_TRUE.strict_encode(e)?,
+            Policy::Key(pk) => strict_encode_seq!(e; MS_KEY, pk),
+            Policy::After(tl) => strict_encode_seq!(e; MS_AFTER, tl),
+            Policy::Older(tl) => strict_encode_seq!(e; MS_OLDER, tl),
+            Policy::Sha256(hash) => strict_encode_seq!(e; MS_SHA256, hash),
+            Policy::Hash256(hash) => strict_encode_seq!(e; MS_HASH256, hash),
+            Policy::Ripemd160(hash) => {
+                strict_encode_seq!(e; MS_RIPEMD160, hash)
+            }
+            Policy::Hash160(hash) => strict_encode_seq!(e; MS_HASH160, hash),
+            Policy::And(ast) => strict_encode_seq!(e; MS_AND_B, ast),
+            Policy::Or(ast) => strict_encode_seq!(e; MS_OR_B, ast),
+            Policy::Threshold(thresh, vec) => {
+                strict_encode_seq!(e; MS_THRESH, thresh, vec)
+            }
+        })
     }
 }
 
-impl StrictDecode for DescriptorSinglePub {
-    #[inline]
+fn decode_policy<Pk>(d: &mut impl io::Read) -> Result<Policy<Pk>, Error>
+where
+    Pk: MiniscriptKey + StrictDecode,
+    <Pk as MiniscriptKey>::Hash: StrictDecode,
+{
+    let byte = d.read_u8()?;
+    Ok(match byte {
+        MS_TRUE => Policy::Trivial,
+        MS_FALSE => Policy::Unsatisfiable,
+        MS_KEY => Policy::Key(Pk::strict_decode(d)?),
+        MS_AFTER => Policy::After(u32::strict_decode(d)?),
+        MS_OLDER => Policy::Older(u32::strict_decode(d)?),
+        MS_SHA256 => Policy::Sha256(sha256::Hash::strict_decode(d)?),
+        MS_HASH256 => Policy::Hash256(sha256d::Hash::strict_decode(d)?),
+        MS_RIPEMD160 => Policy::Ripemd160(ripemd160::Hash::strict_decode(d)?),
+        MS_HASH160 => Policy::Hash160(hash160::Hash::strict_decode(d)?),
+        MS_AND_B => {
+            let len = d.read_u16()?;
+            let mut vec = Vec::with_capacity(len as usize);
+            for _ in 0..len {
+                vec.push(decode_policy(d)?);
+            }
+            Policy::And(vec)
+        }
+        MS_OR_B => {
+            let len = d.read_u16()?;
+            let mut vec = Vec::with_capacity(len as usize);
+            for _ in 0..len {
+                vec.push((d.read_u16()? as usize, decode_policy(d)?));
+            }
+            Policy::Or(vec)
+        }
+        MS_THRESH => {
+            let thresh = d.read_u16()? as usize;
+            let len = d.read_u16()?;
+            let mut vec = Vec::with_capacity(len as usize);
+            for _ in 0..len {
+                vec.push(decode_policy(d)?);
+            }
+            Policy::Threshold(thresh, vec)
+        }
+
+        MS_KEY_HASH | MS_ALT | MS_SWAP | MS_CHECK | MS_DUP_IF | MS_VERIFY
+        | MS_NON_ZERO | MS_ZERO_NE | MS_AND_V | MS_AND_OR | MS_OR_D
+        | MS_OR_C | MS_OR_I | MS_MULTI => {
+            return Err(Error::DataIntegrityError(format!(
+                "byte {:#04X} is a valid miniscript instruction, but does  \
+                     not belong to a set of concrete policy instructions. Try \
+                     to decode data using different miniscript type",
+                byte
+            )))
+        }
+
+        wrong => {
+            return Err(Error::DataIntegrityError(format!(
+                "byte {:#04X} does not correspond to any of miniscript \
+                     concrete policy instructions",
+                wrong
+            )))
+        }
+    })
+}
+
+impl<Pk> StrictDecode for Policy<Pk>
+where
+    Pk: MiniscriptKey + StrictDecode,
+    <Pk as MiniscriptKey>::Hash: StrictDecode,
+{
     fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
-        Ok(strict_decode_self!(d; key, origin; crate))
+        decode_policy(&mut d)
     }
 }
 
-// TODO #4: Encode miniscript structures in a binary way
-
-impl<Pk> StrictEncode for policy::Concrete<Pk>
+// We need this shit because of rust compiler generic limitations
+fn encode_miniscript<Pk, Ctx>(
+    ms: &Miniscript<Pk, Ctx>,
+    e: &mut impl io::Write,
+    mut depth: u8,
+) -> Result<usize, Error>
 where
-    Pk: MiniscriptKey + FromStr,
+    Pk: MiniscriptKey,
+    Pk: MiniscriptKey + StrictEncode,
+    <Pk as MiniscriptKey>::Hash: StrictEncode,
+    Ctx: miniscript::ScriptContext,
 {
-    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
-        self.to_string().strict_encode(e)
+    if depth > 64 {
+        return Err(Error::ExceedMaxItems(65));
     }
+    depth += 1;
+    Ok(match &ms.node {
+        Terminal::False => MS_FALSE.strict_encode(e)?,
+        Terminal::True => MS_TRUE.strict_encode(e)?,
+        Terminal::PkK(pk) => strict_encode_seq!(e; MS_KEY, pk),
+        Terminal::PkH(hash) => strict_encode_seq!(e; MS_KEY_HASH, hash),
+        Terminal::After(tl) => strict_encode_seq!(e; MS_AFTER, tl),
+        Terminal::Older(tl) => strict_encode_seq!(e; MS_OLDER, tl),
+        Terminal::Sha256(hash) => strict_encode_seq!(e; MS_SHA256, hash),
+        Terminal::Hash256(hash) => strict_encode_seq!(e; MS_HASH256, hash),
+        Terminal::Ripemd160(hash) => {
+            strict_encode_seq!(e; MS_RIPEMD160, hash)
+        }
+        Terminal::Hash160(hash) => strict_encode_seq!(e; MS_HASH160, hash),
+
+        Terminal::Alt(ms) => strict_encode_ms!(e; MS_ALT, depth, ms),
+        Terminal::Swap(ms) => strict_encode_ms!(e; MS_SWAP, depth, ms),
+        Terminal::Check(ms) => strict_encode_ms!(e; MS_CHECK, depth, ms),
+        Terminal::DupIf(ms) => strict_encode_ms!(e; MS_DUP_IF, depth, ms),
+        Terminal::Verify(ms) => strict_encode_ms!(e; MS_VERIFY, depth, ms),
+        Terminal::NonZero(ms) => strict_encode_ms!(e; MS_NON_ZERO, depth, ms),
+        Terminal::ZeroNotEqual(ms) => {
+            strict_encode_ms!(e; MS_ZERO_NE, depth, ms)
+        }
+
+        Terminal::AndV(ms1, ms2) => {
+            strict_encode_ms!(e; MS_AND_V, depth, ms1, ms2)
+        }
+        Terminal::AndB(ms1, ms2) => {
+            strict_encode_ms!(e; MS_AND_B, depth, ms1, ms2)
+        }
+        Terminal::AndOr(ms1, ms2, ms3) => {
+            strict_encode_ms!(e; MS_AND_OR, depth, ms1, ms2, ms3)
+        }
+        Terminal::OrB(ms1, ms2) => {
+            strict_encode_ms!(e; MS_OR_B, depth, ms1, ms2)
+        }
+        Terminal::OrD(ms1, ms2) => {
+            strict_encode_ms!(e; MS_OR_D, depth, ms1, ms2)
+        }
+        Terminal::OrC(ms1, ms2) => {
+            strict_encode_ms!(e; MS_OR_C, depth, ms1, ms2)
+        }
+        Terminal::OrI(ms1, ms2) => {
+            strict_encode_ms!(e; MS_OR_I, depth, ms1, ms2)
+        }
+        Terminal::Multi(thresh, vec) => {
+            strict_encode_seq!(e; MS_MULTI, thresh, vec)
+        }
+        Terminal::Thresh(thresh, vec) => {
+            let mut len = 0usize;
+            len += strict_encode_seq!(e; MS_THRESH, thresh, vec.len());
+            for ms in vec {
+                len += encode_miniscript(ms, e, depth)?;
+            }
+            len
+        }
+    })
 }
 
-impl<Pk> StrictDecode for policy::Concrete<Pk>
+impl<Pk, Ctx> StrictEncode for Miniscript<Pk, Ctx>
 where
-    Pk: MiniscriptKey + FromStr,
-    <Pk as FromStr>::Err: Display,
-    <Pk as MiniscriptKey>::Hash: FromStr,
-    <<Pk as MiniscriptKey>::Hash as FromStr>::Err: Display,
+    Pk: MiniscriptKey,
+    Pk: MiniscriptKey + StrictEncode,
+    <Pk as MiniscriptKey>::Hash: StrictEncode,
+    Ctx: miniscript::ScriptContext,
 {
-    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
-        String::strict_decode(d)?.parse().map_err(|_| {
-            Error::DataIntegrityError(s!("Unparsable miniscript policy string"))
-        })
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        encode_miniscript(self, &mut e, 1)
     }
 }
 
-impl<Pk, Ctx> StrictEncode for Miniscript<Pk, Ctx>
+fn decode_miniscript<Pk, Ctx>(
+    d: &mut impl io::Read,
+    mut depth: u8,
+) -> Result<Miniscript<Pk, Ctx>, Error>
 where
-    Pk: MiniscriptKey + FromStr,
+    Pk: MiniscriptKey,
+    Pk: MiniscriptKey + StrictDecode,
+    <Pk as MiniscriptKey>::Hash: StrictDecode,
     Ctx: miniscript::ScriptContext,
 {
-    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
-        self.to_string().strict_encode(e)
+    if depth > 64 {
+        return Err(Error::ExceedMaxItems(65));
     }
+    depth += 1;
+    let term = match d.read_u8()? {
+        MS_TRUE => Terminal::True,
+        MS_FALSE => Terminal::False,
+        MS_KEY => Terminal::PkK(Pk::strict_decode(d)?),
+        MS_KEY_HASH => Terminal::PkH(Pk::Hash::strict_decode(d)?),
+
+        MS_AFTER => Terminal::After(u32::strict_decode(d)?),
+        MS_OLDER => Terminal::Older(u32::strict_decode(d)?),
+        MS_SHA256 => Terminal::Sha256(sha256::Hash::strict_decode(d)?),
+        MS_HASH256 => {
+            Terminal::Hash256(sha256d::Hash::strict_decode(d)?)
+        }
+        MS_RIPEMD160 => {
+            Terminal::Ripemd160(ripemd160::Hash::strict_decode(d)?)
+        }
+        MS_HASH160 => {
+            Terminal::Hash160(hash160::Hash::strict_decode(d)?)
+        }
+
+        MS_ALT => Terminal::Alt(decode_miniscript(d, depth)?.into()),
+        MS_SWAP => Terminal::Swap(decode_miniscript(d, depth)?.into()),
+        MS_CHECK => Terminal::Check(decode_miniscript(d, depth)?.into()),
+        MS_DUP_IF => Terminal::DupIf(decode_miniscript(d, depth)?.into()),
+        MS_VERIFY => Terminal::Verify(decode_miniscript(d, depth)?.into()),
+        MS_NON_ZERO => {
+            Terminal::NonZero(decode_miniscript(d, depth)?.into())
+        }
+        MS_ZERO_NE => {
+            Terminal::ZeroNotEqual(decode_miniscript(d, depth)?.into())
+        }
+
+        MS_AND_V => Terminal::AndV(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_AND_B => Terminal::AndB(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_AND_OR => Terminal::AndOr(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_OR_B => Terminal::OrB(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_OR_D => Terminal::OrD(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_OR_C => Terminal::OrC(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_OR_I => Terminal::OrI(
+            decode_miniscript(d, depth)?.into(),
+            decode_miniscript(d, depth)?.into(),
+        ),
+        MS_MULTI => Terminal::Multi(
+            d.read_u16()? as usize,
+            Vec::strict_decode(d)?,
+        ),
+        MS_THRESH => {
+            let thresh = d.read_u16()? as usize;
+            let len = d.read_u16()?;
+            let mut vec = Vec::with_capacity(len as usize);
+            for _ in 0..len {
+                vec.push(decode_miniscript(d, depth)?.into());
+            }
+            Terminal::Thresh(thresh, vec)
+        },
+
+        wrong => {
+            return Err(Error::DataIntegrityError(format!(
+                "byte {:#04X} does not correspond to any of miniscript instructions",
+                wrong
+            )))
+        }
+    };
+    Miniscript::from_ast(term).map_err(|err| {
+        Error::DataIntegrityError(format!(
+            "miniscript does not pass check: {}",
+            err
+        ))
+    })
 }
 
 impl<Pk, Ctx> StrictDecode for Miniscript<Pk, Ctx>
 where
-    Pk: MiniscriptKey + FromStr,
-    <Pk as FromStr>::Err: Display,
-    <Pk as MiniscriptKey>::Hash: FromStr,
-    <<Pk as MiniscriptKey>::Hash as FromStr>::Err: Display,
+    Pk: MiniscriptKey + StrictDecode,
+    <Pk as MiniscriptKey>::Hash: StrictDecode,
     Ctx: miniscript::ScriptContext,
 {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        decode_miniscript(&mut d, 1)
+    }
+}
+
+const DESCRIPTOR_BARE: u8 = 0x00;
+const DESCRIPTOR_PKH: u8 = 0x01;
+const DESCRIPTOR_SH: u8 = 0x02;
+const DESCRIPTOR_SH_SORTED_MULTI: u8 = 0x03;
+const DESCRIPTOR_SH_WPKH: u8 = 0x08;
+const DESCRIPTOR_SH_WSH: u8 = 0x09;
+const DESCRIPTOR_SH_WSH_SORTED_MULTI: u8 = 0x0a;
+const DESCRIPTOR_WPKH: u8 = 0x10;
+const DESCRIPTOR_WSH: u8 = 0x11;
+const DESCRIPTOR_WSH_SORTED_MULTI: u8 = 0x12;
+// Taproot: 0x2_
+
+impl StrictEncode for DescriptorPublicKey {
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        Ok(match self {
+            DescriptorPublicKey::SinglePub(pk) => {
+                strict_encode_list!(e; 0x01u8, pk)
+            }
+            DescriptorPublicKey::XPub(xpub) => {
+                strict_encode_list!(e; 0x02u8, xpub)
+            }
+        })
+    }
+}
+
+impl StrictDecode for DescriptorPublicKey {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        Ok(match u8::strict_decode(&mut d)? {
+            0x01 => DescriptorPublicKey::SinglePub(
+                DescriptorSinglePub::strict_decode(&mut d)?,
+            ),
+            0x02 => DescriptorPublicKey::XPub(DescriptorXKey::strict_decode(
+                &mut d,
+            )?),
+            wrong => {
+                return Err(Error::DataIntegrityError(format!(
+                    "unknown descriptor key tag `{:#04X}",
+                    wrong
+                )))
+            }
+        })
+    }
+}
+
+impl StrictEncode for DescriptorSinglePub {
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        Ok(strict_encode_list!(e; self.origin, self.key))
+    }
+}
+
+impl StrictDecode for DescriptorSinglePub {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        Ok(strict_decode_self!(d; origin, key; crate))
+    }
+}
+
+impl<Pk> StrictEncode for DescriptorXKey<Pk>
+where
+    Pk: InnerXKey + StrictEncode,
+{
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        Ok(
+            strict_encode_list!(e; self.origin, self.derivation_path, self.xkey, self.wildcard),
+        )
+    }
+}
+
+impl<Pk> StrictDecode for DescriptorXKey<Pk>
+where
+    Pk: InnerXKey + StrictDecode,
+{
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        Ok(
+            strict_decode_self!(d; origin, derivation_path, xkey, wildcard; crate),
+        )
+    }
+}
+
+impl StrictEncode for Wildcard {
+    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
+        match self {
+            Wildcard::None => 0u8,
+            Wildcard::Unhardened => 1u8,
+            Wildcard::Hardened => 2u8,
+        }
+        .strict_encode(e)
+    }
+}
+
+impl StrictDecode for Wildcard {
     fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
-        String::strict_decode(d)?.parse().map_err(|_| {
-            Error::DataIntegrityError(s!("Can't parse miniscript string"))
+        Ok(match u8::strict_decode(d)? {
+            0 => Wildcard::None,
+            1 => Wildcard::Unhardened,
+            2 => Wildcard::Hardened,
+            wrong => {
+                return Err(Error::DataIntegrityError(format!(
+                    "unknown descriptor xpub wildcard type `{:#04X}`",
+                    wrong
+                )))
+            }
         })
     }
 }
 
 impl<Pk> StrictEncode for Descriptor<Pk>
 where
-    Pk: MiniscriptKey + FromStr,
+    Pk: MiniscriptKey + StrictEncode,
+    <Pk as MiniscriptKey>::Hash: StrictEncode,
 {
-    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
-        self.to_string().strict_encode(e)
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        Ok(match self {
+            Descriptor::Bare(bare) => {
+                strict_encode_list!(e; DESCRIPTOR_BARE, bare.as_inner())
+            }
+            Descriptor::Pkh(pkh) => {
+                strict_encode_list!(e; DESCRIPTOR_PKH, pkh.as_inner())
+            }
+            Descriptor::Wpkh(wpkh) => {
+                strict_encode_list!(e; DESCRIPTOR_WPKH, wpkh.as_inner())
+            }
+            Descriptor::Sh(sh) => match sh.as_inner() {
+                ShInner::Wsh(wsh) => match wsh.as_inner() {
+                    WshInner::SortedMulti(multi) => {
+                        strict_encode_list!(e; DESCRIPTOR_SH_WSH_SORTED_MULTI, multi.k, multi.pks)
+                    }
+                    WshInner::Ms(ms) => {
+                        strict_encode_list!(e; DESCRIPTOR_SH_WSH, ms)
+                    }
+                },
+                ShInner::Wpkh(wpkh) => {
+                    strict_encode_list!(e; DESCRIPTOR_SH_WPKH, wpkh.as_inner())
+                }
+                ShInner::SortedMulti(multi) => {
+                    strict_encode_list!(e; DESCRIPTOR_SH_SORTED_MULTI, multi.k, multi.pks)
+                }
+                ShInner::Ms(ms) => strict_encode_list!(e; DESCRIPTOR_SH, ms),
+            },
+            Descriptor::Wsh(wsh) => match wsh.as_inner() {
+                WshInner::SortedMulti(multi) => {
+                    strict_encode_list!(e; DESCRIPTOR_WSH_SORTED_MULTI, multi.k, multi.pks)
+                }
+                WshInner::Ms(ms) => strict_encode_list!(e; DESCRIPTOR_WSH, ms),
+            },
+        })
     }
 }
 
 impl<Pk> StrictDecode for Descriptor<Pk>
 where
-    Pk: MiniscriptKey + FromStr,
-    <Pk as FromStr>::Err: Display,
-    <Pk as MiniscriptKey>::Hash: FromStr,
-    <<Pk as MiniscriptKey>::Hash as FromStr>::Err: Display,
+    Pk: MiniscriptKey + StrictDecode,
+    <Pk as MiniscriptKey>::Hash: StrictDecode,
 {
-    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
-        String::strict_decode(d)?.parse().map_err(|_| {
-            Error::DataIntegrityError(s!("Can't parse descriptor string"))
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        impl From<miniscript::Error> for Error {
+            fn from(err: miniscript::Error) -> Self {
+                Error::DataIntegrityError(format!(": {}", err))
+            }
+        }
+
+        Ok(match u8::strict_decode(&mut d)? {
+            DESCRIPTOR_BARE => {
+                Descriptor::Bare(Bare::new(Miniscript::strict_decode(&mut d)?)?)
+            }
+            DESCRIPTOR_PKH => {
+                Descriptor::Pkh(Pkh::new(Pk::strict_decode(&mut d)?))
+            }
+            DESCRIPTOR_SH => {
+                Descriptor::Sh(Sh::new(Miniscript::strict_decode(&mut d)?)?)
+            }
+            DESCRIPTOR_SH_SORTED_MULTI => Descriptor::Sh(Sh::new_sortedmulti(
+                usize::strict_decode(&mut d)?,
+                Vec::strict_decode(&mut d)?,
+            )?),
+            DESCRIPTOR_SH_WPKH => {
+                Descriptor::Sh(Sh::new_wpkh(Pk::strict_decode(&mut d)?)?)
+            }
+            DESCRIPTOR_SH_WSH => {
+                Descriptor::Sh(Sh::new_wsh(Miniscript::strict_decode(&mut d)?)?)
+            }
+            DESCRIPTOR_SH_WSH_SORTED_MULTI => {
+                Descriptor::Sh(Sh::new_wsh_sortedmulti(
+                    usize::strict_decode(&mut d)?,
+                    Vec::strict_decode(&mut d)?,
+                )?)
+            }
+            DESCRIPTOR_WPKH => {
+                Descriptor::Wpkh(Wpkh::new(Pk::strict_decode(&mut d)?)?)
+            }
+            DESCRIPTOR_WSH => {
+                Descriptor::Wsh(Wsh::new(Miniscript::strict_decode(&mut d)?)?)
+            }
+            DESCRIPTOR_WSH_SORTED_MULTI => {
+                Descriptor::Wsh(Wsh::new_sortedmulti(
+                    usize::strict_decode(&mut d)?,
+                    Vec::strict_decode(&mut d)?,
+                )?)
+            }
+            wrong => {
+                return Err(Error::DataIntegrityError(format!(
+                    "unknown miniscript descriptor type: #{:#04X}",
+                    wrong
+                )))
+            }
         })
     }
 }
+
+#[cfg(test)]
+mod test {
+    use crate::test_helpers::*;
+    // use crate::StrictEncode;
+    use miniscript::{policy, Descriptor, Miniscript, Segwitv0};
+    use std::str::FromStr;
+
+    #[test]
+    fn test_policy() {
+        const SET: [&str; 12] = [
+            "and(pk(A),or(and(after(9),pk(B)),and(after(1000000000),pk(C))))",
+            "pk(A)",
+            "after(9)",
+            "older(1)",
+            "sha256(1111111111111111111111111111111111111111111111111111111111111111)",
+            "and(pk(A),pk(B))",
+            "or(pk(A),pk(B))",
+            "thresh(2,pk(A),pk(B),pk(C))",
+            "thresh(2,after(9),after(9),pk(A))",
+            "and(pk(A),or(after(9),after(9)))",
+            "or(1@and(pk(A),pk(B)),127@pk(C))",
+            "and(and(and(or(127@thresh(2,pk(A),pk(B),thresh(2,or(127@pk(A),1@pk(B)),after(100),or(and(pk(C),after(200)),and(pk(D),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925))),pk(E))),1@pk(F)),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925)),or(127@pk(G),1@after(300))),or(127@after(400),pk(H)))",
+        ];
+
+        for s in &SET {
+            let policy = policy::Concrete::<String>::from_str(s).unwrap();
+            test_object_encoding_roundtrip(&policy).unwrap();
+        }
+    }
+
+    #[test]
+    fn test_miniscript() {
+        const SET: [&str; 28] = [
+            "lltvln:after(1231488000)",
+            "uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))",
+            "or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))",
+            "j:and_v(vdv:after(1567547623),older(2016))",
+            "t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))",
+            "t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))",
+            "or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))",
+            "or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))",
+            "and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))",
+            "j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))",
+            "and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))",
+            "j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))",
+            "and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))",
+            "thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))",
+            "and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))",
+            "or_d(d:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))",
+            "c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))",
+            "c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))",
+            "and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))",
+            "andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))",
+            "or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))",
+            "thresh(2,c:pk_h(5dedfbf9ea599dd4e3ca6a80b333c472fd0b3f69),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))",
+            "and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))",
+            "and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))",
+            "c:or_i(and_v(v:older(16),pk_h(9fc5dbe5efdce10374a4dd4053c93af540211718)),pk_h(2fbd32c8dd59ee7c17e66cb6ebea7e9846c3040f))",
+            "or_d(c:pk_h(c42e7ef92fdb603af844d064faad95db9bcdfd3d),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))",
+            "c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(9fc5dbe5efdce10374a4dd4053c93af540211718),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(dd100be7d9aea5721158ebde6d6a1fd8fff93bb1)))",
+            "c:or_i(andor(c:pk_h(fcd35ddacad9f2d5be5e464639441c6065e6955d),pk_h(9652d86bedf43ad264362e6e6eba6eb764508127),pk_h(06afd46bcdfd22ef94ac122aa11f241244a37ecc)),pk_k(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))"
+        ];
+
+        for s in &SET {
+            let ms =
+                Miniscript::<bitcoin::PublicKey, Segwitv0>::from_str_insane(s)
+                    .unwrap();
+            test_object_encoding_roundtrip(&ms).unwrap();
+        }
+    }
+
+    #[test]
+    fn test_descriptor() {
+        const SET: [&str; 16] = [
+            "pk(020000000000000000000000000000000000000000000000000000000000000002)",
+            "multi(1,020000000000000000000000000000000000000000000000000000000000000002)",
+            "pkh(020000000000000000000000000000000000000000000000000000000000000002)",
+            "wsh(c:pk_k(020000000000000000000000000000000000000000000000000000000000000002))",
+            "sh(wsh(c:pk_k(020000000000000000000000000000000000000000000000000000000000000002)))",
+            "wsh(after(1000))",
+            "wsh(older(1000))",
+            "wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357)",
+            "sh(wpkh(03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873))",
+            "wsh(multi(2,03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd,03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626))",
+            "sh(sortedmulti(1,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352))#uetvewm2",
+            "wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH))#7etm7zk7",
+            "sh(wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*)))#u60cee0u",
+            "wpkh(tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/44'/0'/0'/0/*)",
+            "wpkh([2cbe2a6d/44'/0'/0']tpubDCvNhURocXGZsLNqWcqD3syHTqPXrMSTwi8feKVwAcpi29oYKsDD3Vex7x2TDneKMVN23RbLprfxB69v94iYqdaYHsVz3kPR37NQXeqouVz/0/*)#nhdxg96s",
+            "sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy",
+        ];
+
+        let secp = bitcoin::secp256k1::Secp256k1::new();
+
+        for s in &SET {
+            let (descr, _) = Descriptor::parse_descriptor(&secp, s).unwrap();
+            test_object_encoding_roundtrip(&descr).unwrap();
+        }
+    }
+}
diff --git a/strict_encoding/src/byte_str.rs b/strict_encoding/src/pointers.rs
similarity index 61%
rename from strict_encoding/src/byte_str.rs
rename to strict_encoding/src/pointers.rs
index 372a0b29..483a7ae7 100644
--- a/strict_encoding/src/byte_str.rs
+++ b/strict_encoding/src/pointers.rs
@@ -12,8 +12,11 @@
 // You should have received a copy of the Apache 2.0 License along with this
 // software. If not, see <https://opensource.org/licenses/Apache-2.0>.
 
+use std::cell::RefCell;
 use std::io;
 use std::ops::Deref;
+use std::rc::Rc;
+use std::sync::Arc;
 
 use crate::{Error, StrictDecode, StrictEncode};
 
@@ -28,6 +31,38 @@ impl StrictEncode for &[u8] {
     }
 }
 
+// TODO: 19 Re-implement with const generics once MSRV > 1.50
+
+impl StrictEncode for [u8; 16] {
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        e.write_all(self)?;
+        Ok(self.len())
+    }
+}
+
+impl StrictDecode for [u8; 16] {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        let mut ret = [0u8; 16];
+        d.read_exact(&mut ret)?;
+        Ok(ret)
+    }
+}
+
+impl StrictEncode for [u8; 20] {
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        e.write_all(self)?;
+        Ok(self.len())
+    }
+}
+
+impl StrictDecode for [u8; 20] {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        let mut ret = [0u8; 20];
+        d.read_exact(&mut ret)?;
+        Ok(ret)
+    }
+}
+
 impl StrictEncode for [u8; 32] {
     fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
         e.write_all(self)?;
@@ -43,6 +78,21 @@ impl StrictDecode for [u8; 32] {
     }
 }
 
+impl StrictEncode for [u8; 64] {
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        e.write_all(self)?;
+        Ok(self.len())
+    }
+}
+
+impl StrictDecode for [u8; 64] {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        let mut ret = [0u8; 64];
+        d.read_exact(&mut ret)?;
+        Ok(ret)
+    }
+}
+
 impl StrictEncode for Box<[u8]> {
     fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
         self.deref().strict_encode(e)
@@ -58,6 +108,60 @@ impl StrictDecode for Box<[u8]> {
     }
 }
 
+impl<T> StrictEncode for Rc<T>
+where
+    T: StrictEncode,
+{
+    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
+        self.deref().strict_encode(e)
+    }
+}
+
+impl<T> StrictDecode for Rc<T>
+where
+    T: StrictDecode,
+{
+    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
+        Ok(Rc::new(T::strict_decode(d)?))
+    }
+}
+
+impl<T> StrictEncode for RefCell<T>
+where
+    T: StrictEncode,
+{
+    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
+        self.deref().strict_encode(e)
+    }
+}
+
+impl<T> StrictDecode for RefCell<T>
+where
+    T: StrictDecode,
+{
+    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
+        Ok(RefCell::new(T::strict_decode(d)?))
+    }
+}
+
+impl<T> StrictEncode for Arc<T>
+where
+    T: StrictEncode,
+{
+    fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
+        self.deref().strict_encode(e)
+    }
+}
+
+impl<T> StrictDecode for Arc<T>
+where
+    T: StrictDecode,
+{
+    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
+        Ok(Arc::new(T::strict_decode(d)?))
+    }
+}
+
 impl StrictEncode for &str {
     fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
         self.as_bytes().strict_encode(e)
diff --git a/strict_encoding/src/primitives.rs b/strict_encoding/src/primitives.rs
index 69907a76..36e6dc90 100644
--- a/strict_encoding/src/primitives.rs
+++ b/strict_encoding/src/primitives.rs
@@ -14,12 +14,27 @@
 
 //! Taking implementation of little-endian integer encoding
 
-use bitcoin::util::uint::{Uint128, Uint256};
 use core::time::Duration;
 use std::io;
 
 use super::{strategies, Error, Strategy, StrictDecode, StrictEncode};
 
+impl StrictEncode for bool {
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        (*self as u8).strict_encode(&mut e)
+    }
+}
+
+impl StrictDecode for bool {
+    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
+        match u8::strict_decode(&mut d)? {
+            0 => Ok(false),
+            1 => Ok(true),
+            v => Err(Error::ValueOutOfRange("boolean", 0..1, v as u128)),
+        }
+    }
+}
+
 impl Strategy for u8 {
     type Strategy = strategies::BitcoinConsensus;
 }
@@ -32,12 +47,6 @@ impl Strategy for u32 {
 impl Strategy for u64 {
     type Strategy = strategies::BitcoinConsensus;
 }
-impl Strategy for Uint128 {
-    type Strategy = strategies::BitcoinConsensus;
-}
-impl Strategy for Uint256 {
-    type Strategy = strategies::BitcoinConsensus;
-}
 impl Strategy for i8 {
     type Strategy = strategies::BitcoinConsensus;
 }
@@ -51,62 +60,35 @@ impl Strategy for i64 {
     type Strategy = strategies::BitcoinConsensus;
 }
 
-impl StrictEncode for bool {
-    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
-        (*self as u8).strict_encode(&mut e)
-    }
-}
-
-impl StrictDecode for bool {
-    fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
-        match u8::strict_decode(&mut d)? {
-            0 => Ok(false),
-            1 => Ok(true),
-            v => Err(Error::ValueOutOfRange("boolean", 0..1, v as u128)),
-        }
-    }
-}
-
-/*
 impl StrictEncode for u128 {
-    type Error = Error;
     #[inline]
-    fn strict_encode<E: io::Write>(
-        &self,
-        mut e: E,
-    ) -> Result<usize, Error> {
-        e.write_u128(*self)?;
-        Ok(core::mem::size_of::<u128>())
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        e.write_all(&self.to_le_bytes())?;
+        Ok(16)
     }
 }
 
 impl StrictDecode for u128 {
-    type Error = Error;
     #[inline]
-    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Self::Error> {
-        Ok(d.read_u128()?)
+    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
+        Ok(u128::from_le_bytes(<[u8; 16]>::strict_decode(d)?))
     }
 }
 
 impl StrictEncode for i128 {
-    type Error = Error;
     #[inline]
-    fn strict_encode<E: io::Write>(
-        &self,
-        mut e: E,
-    ) -> Result<usize, Error> {
-        e.write_i128(*self)?;
-        Ok(core::mem::size_of::<i128>())
+    fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
+        e.write_all(&self.to_le_bytes())?;
+        Ok(16)
     }
 }
 
 impl StrictDecode for i128 {
-    type Error = Error;
     #[inline]
-    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Self::Error> {
-        Ok(d.read_i128()?)
+    fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
+        Ok(i128::from_le_bytes(<[u8; 16]>::strict_decode(d)?))
     }
-}*/
+}
 
 impl StrictEncode for usize {
     fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
@@ -209,49 +191,148 @@ mod _chrono {
 #[cfg(test)]
 pub mod test {
     use super::*;
-    use crate::{strict_deserialize, strict_serialize};
+    use crate::strict_deserialize;
+    use crate::test_helpers::test_encoding_roundtrip;
     use chrono::{NaiveDateTime, Utc};
 
-    /// Checking that byte encoding and decoding works correctly for the most
-    /// common marginal and middle-probability cases
     #[test]
-    fn test_u8_encoding() {
-        let zero: u8 = 0;
-        let one: u8 = 1;
-        let thirteen: u8 = 13;
-        let confusing: u8 = 0xEF;
-        let nearly_full: u8 = 0xFE;
-        let full: u8 = 0xFF;
-
-        let byte_0 = &[0u8][..];
-        let byte_1 = &[1u8][..];
-        let byte_13 = &[13u8][..];
-        let byte_ef = &[0xEFu8][..];
-        let byte_fe = &[0xFEu8][..];
-        let byte_ff = &[0xFFu8][..];
-
-        assert_eq!(strict_serialize(&zero).unwrap(), byte_0);
-        assert_eq!(strict_serialize(&one).unwrap(), byte_1);
-        assert_eq!(strict_serialize(&thirteen).unwrap(), byte_13);
-        assert_eq!(strict_serialize(&confusing).unwrap(), byte_ef);
-        assert_eq!(strict_serialize(&nearly_full).unwrap(), byte_fe);
-        assert_eq!(strict_serialize(&full).unwrap(), byte_ff);
-
-        assert_eq!(u8::strict_decode(byte_0).unwrap(), zero);
-        assert_eq!(u8::strict_decode(byte_1).unwrap(), one);
-        assert_eq!(u8::strict_decode(byte_13).unwrap(), thirteen);
-        assert_eq!(u8::strict_decode(byte_ef).unwrap(), confusing);
-        assert_eq!(u8::strict_decode(byte_fe).unwrap(), nearly_full);
-        assert_eq!(u8::strict_decode(byte_ff).unwrap(), full);
+    fn test_u_encoding() {
+        test_encoding_roundtrip(&0_u8, [0]).unwrap();
+        test_encoding_roundtrip(&1_u8, [1]).unwrap();
+        test_encoding_roundtrip(&0x10_u8, [0x10]).unwrap();
+        test_encoding_roundtrip(&0xFE_u8, [0xFE]).unwrap();
+        test_encoding_roundtrip(&0xFF_u8, [0xFF]).unwrap();
+        test_encoding_roundtrip(&54_u16, [54, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_u16, [0xa6, 0x45]).unwrap();
+        test_encoding_roundtrip(&54_usize, [54, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_usize, [0xa6, 0x45]).unwrap();
+        test_encoding_roundtrip(&54_u32, [54, 0, 0, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_u32, [0xa6, 0x45, 0, 0]).unwrap();
+        test_encoding_roundtrip(&0x56fe45a6_u32, [0xa6, 0x45, 0xfe, 0x56])
+            .unwrap();
+        test_encoding_roundtrip(&54_u64, [54, 0, 0, 0, 0, 0, 0, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_u64, [0xa6, 0x45, 0, 0, 0, 0, 0, 0])
+            .unwrap();
+        test_encoding_roundtrip(
+            &0x56fe45a6_u64,
+            [0xa6, 0x45, 0xfe, 0x56, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0xcafedead56fe45a6_u64,
+            [0xa6, 0x45, 0xfe, 0x56, 0xad, 0xde, 0xfe, 0xca],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &54_u128,
+            [54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0x45a6_u128,
+            [0xa6, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0x56fe45a6_u128,
+            [0xa6, 0x45, 0xfe, 0x56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0xcafedead56fe45a6_u128,
+            [
+                0xa6, 0x45, 0xfe, 0x56, 0xad, 0xde, 0xfe, 0xca, 0, 0, 0, 0, 0,
+                0, 0, 0,
+            ],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0xbadefeed65671331cafedead56fe45a6_u128,
+            [
+                0xa6, 0x45, 0xfe, 0x56, 0xad, 0xde, 0xfe, 0xca, 0x31, 0x13,
+                0x67, 0x65, 0xed, 0xfe, 0xde, 0xba,
+            ],
+        )
+        .unwrap();
+    }
+
+    #[test]
+    fn test_i_encoding() {
+        test_encoding_roundtrip(&0_i8, [0]).unwrap();
+        test_encoding_roundtrip(&1_i8, [1]).unwrap();
+        test_encoding_roundtrip(&0x10_i8, [0x10]).unwrap();
+        test_encoding_roundtrip(&0x7E_i8, [0x7E]).unwrap();
+        test_encoding_roundtrip(&0x7F_i8, [0x7F]).unwrap();
+        test_encoding_roundtrip(&-0x7F_i8, [0x81]).unwrap();
+        test_encoding_roundtrip(&54_i16, [54, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_i16, [0xa6, 0x45]).unwrap();
+        test_encoding_roundtrip(&54_i32, [54, 0, 0, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_i32, [0xa6, 0x45, 0, 0]).unwrap();
+        test_encoding_roundtrip(&0x56fe45a6_i32, [0xa6, 0x45, 0xfe, 0x56])
+            .unwrap();
+        test_encoding_roundtrip(&54_i64, [54, 0, 0, 0, 0, 0, 0, 0]).unwrap();
+        test_encoding_roundtrip(&0x45a6_i64, [0xa6, 0x45, 0, 0, 0, 0, 0, 0])
+            .unwrap();
+        test_encoding_roundtrip(
+            &0x56fe45a6_i64,
+            [0xa6, 0x45, 0xfe, 0x56, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0x7afedead56fe45a6_i64,
+            [0xa6, 0x45, 0xfe, 0x56, 0xad, 0xde, 0xfe, 0x7a],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &54_i128,
+            [54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0x45a6_i128,
+            [0xa6, 0x45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0x56fe45a6_i128,
+            [0xa6, 0x45, 0xfe, 0x56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0xcafedead56fe45a6_i128,
+            [
+                0xa6, 0x45, 0xfe, 0x56, 0xad, 0xde, 0xfe, 0xca, 0, 0, 0, 0, 0,
+                0, 0, 0,
+            ],
+        )
+        .unwrap();
+        test_encoding_roundtrip(
+            &0x1adefeed65671331cafedead56fe45a6_i128,
+            [
+                0xa6, 0x45, 0xfe, 0x56, 0xad, 0xde, 0xfe, 0xca, 0x31, 0x13,
+                0x67, 0x65, 0xed, 0xfe, 0xde, 0x1a,
+            ],
+        )
+        .unwrap();
+    }
+
+    #[test]
+    #[should_panic(expected = "ExceedMaxItems(131071)")]
+    fn test_usize_encode_fail() {
+        0x01FFFF_usize.strict_serialize().unwrap();
+    }
+
+    #[test]
+    #[should_panic(expected = "DataNotEntirelyConsumed")]
+    fn test_usize_decode_fail() {
+        let _: usize = strict_deserialize([0xFF, 0xFF, 0xFF, 0x54]).unwrap();
     }
 
     #[test]
     fn test_bool_encoding() {
-        assert_eq!(strict_serialize(&true), Ok(vec![0x01]));
-        assert_eq!(strict_serialize(&false), Ok(vec![0x00]));
+        test_encoding_roundtrip(&true, [0x01]).unwrap();
+        test_encoding_roundtrip(&false, [0x00]).unwrap();
 
-        assert_eq!(strict_deserialize(vec![0x01]), Ok(true));
-        assert_eq!(strict_deserialize(vec![0x00]), Ok(false));
         assert_eq!(
             bool::strict_decode(&[0x20][..]),
             Err(Error::ValueOutOfRange("boolean", 0..1, 0x20))
@@ -260,16 +341,12 @@ pub mod test {
 
     #[test]
     fn test_float_encoding() {
-        let f_32 = 5.7692_f32;
-        let f_32_ser = &[73, 157, 184, 64][..];
-        let f_64 = 54546457.76965676_f64;
-        let f_64_ser = &[206, 65, 40, 206, 128, 2, 138, 65][..];
-
-        assert_eq!(strict_serialize(&f_32).unwrap(), f_32_ser.to_vec());
-        assert_eq!(strict_serialize(&f_64).unwrap(), f_64_ser.to_vec());
-
-        assert_eq!(f32::strict_deserialize(f_32_ser), Ok(f_32));
-        assert_eq!(f64::strict_deserialize(f_64_ser), Ok(f_64));
+        test_encoding_roundtrip(&5.7692_f32, [73, 157, 184, 64]).unwrap();
+        test_encoding_roundtrip(
+            &54546457.76965676_f64,
+            [206, 65, 40, 206, 128, 2, 138, 65],
+        )
+        .unwrap();
     }
 
     #[test]


Test patch: