diff --git a/Cargo.lock b/Cargo.lock index 48f3fed4..04af4bf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -561,6 +561,7 @@ dependencies = [ "lazy_static", "lightning", "lightning-net-tokio", + "lnpbp_derive", "log", "miniscript", "num-derive", diff --git a/Cargo.toml b/Cargo.toml index 22729def..f23e917b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ crate-type = ["dylib", "rlib", "staticlib"] [dependencies] amplify = { version = "~1.0.0", features = ["serde", "std"] } amplify_derive = "~1.0.0" +lnpbp_derive = { path = "derive" } lazy_static = "~1.4.0" bech32 = "~0.7.2" ed25519-dalek = { version = "~1.0.0", optional = true } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index f5d7676b..4b100617 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -98,12 +98,12 @@ fn attr_nested_one_arg( mut list: impl ExactSizeIterator, attr_name: &str, example: &str, -) -> Result> { +) -> Result> { match list.len() { 0 => proc_macro_err!(attr_name, "unexpected absence of argument", example), 1 => match list.next().expect("Core library iterator is broken") { NestedMeta::Meta(meta) => match meta { - Meta::Path(path) => Ok(path.get_ident().cloned()), + Meta::Path(path) => Ok(Some(path)), _ => proc_macro_err!(attr_name, "unexpected attribute type", example), }, NestedMeta::Lit(_) => proc_macro_err!( @@ -436,7 +436,7 @@ fn lnp_api_inner_enum(input: &DeriveInput, data: &DataEnum) -> Result TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); strict_encode_inner(derive_input) @@ -444,7 +444,7 @@ pub fn derive_strict_encode(input: TokenStream) -> TokenStream { .into() } -#[proc_macro_derive(StrictDecode, attributes(strict_decode))] +#[proc_macro_derive(StrictDecode, attributes(strict_error, strict_crate))] pub fn derive_strict_decode(input: TokenStream) -> TokenStream { let derive_input = parse_macro_input!(input as DeriveInput); strict_decode_inner(derive_input) @@ -487,6 +487,7 @@ fn strict_encode_inner_struct(input: &DeriveInput, data: &DataStruct) -> Result< let ident_name = &input.ident; let error_type_def = get_strict_error(input, data)?; + let import = get_strict_crate(input, data)?; let recurse = match data.fields { Fields::Named(ref fields) => fields @@ -527,7 +528,7 @@ fn strict_encode_inner_struct(input: &DeriveInput, data: &DataStruct) -> Result< Ok(quote! { #[allow(unused_qualifications)] - impl #impl_generics lnpbp::strict_encoding::StrictEncode for #ident_name #ty_generics #where_clause { + impl #impl_generics #import::StrictEncode for #ident_name #ty_generics #where_clause { #error_type_def #[inline] @@ -543,6 +544,7 @@ fn strict_decode_inner_struct(input: &DeriveInput, data: &DataStruct) -> Result< let ident_name = &input.ident; let error_type_def = get_strict_error(input, data)?; + let import = get_strict_crate(input, data)?; let inner = match data.fields { Fields::Named(ref fields) => { @@ -552,7 +554,7 @@ fn strict_decode_inner_struct(input: &DeriveInput, data: &DataStruct) -> Result< .map(|f| { let name = &f.ident; quote_spanned! { f.span() => - #name: lnpbp::strict_encoding::StrictDecode::strict_decode(&mut d)?, + #name: #import::StrictDecode::strict_decode(&mut d)?, } }) .collect(); @@ -568,7 +570,7 @@ fn strict_decode_inner_struct(input: &DeriveInput, data: &DataStruct) -> Result< .iter() .map(|f| { quote_spanned! { f.span() => - lnpbp::strict_encoding::StrictDecode::strict_decode(&mut d)?, + #import::StrictDecode::strict_decode(&mut d)?, } }) .collect(); @@ -586,7 +588,7 @@ fn strict_decode_inner_struct(input: &DeriveInput, data: &DataStruct) -> Result< Ok(quote! { #[allow(unused_qualifications)] - impl #impl_generics lnpbp::strict_encoding::StrictDecode for #ident_name #ty_generics #where_clause { + impl #impl_generics #import::StrictDecode for #ident_name #ty_generics #where_clause { #error_type_def #[inline] @@ -613,3 +615,20 @@ fn get_strict_error(input: &DeriveInput, data: &DataStruct) -> Result quote! {}, }) } + +fn get_strict_crate(input: &DeriveInput, data: &DataStruct) -> Result { + let name = "strict_crate"; + let example = "#[strict_crate(lnpbp_other_name)]"; + let default = quote! { ::lnpbp::strict_encoding }; + + let list = match attr_list(&input.attrs, name, example)? { + Some(x) => x, + None => return Ok(default), + }; + let strict_crate = attr_nested_one_arg(list.into_iter(), name, example)?; + + Ok(match strict_crate { + Some(ident) => quote! { #ident::strict_encoding }, + None => return Ok(default), + }) +} diff --git a/src/bp/blind.rs b/src/bp/blind.rs index d787aa21..f9cf0322 100644 --- a/src/bp/blind.rs +++ b/src/bp/blind.rs @@ -20,7 +20,10 @@ use crate::commit_verify::CommitVerify; /// Data required to generate or reveal the information about blinded /// transaction outpoint -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Default)] +#[derive( + Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, Default, StrictEncode, StrictDecode, +)] +#[strict_crate(crate)] #[display(Debug)] pub struct OutpointReveal { /// Blinding factor preventing rainbow table bruteforce attack based on diff --git a/src/bp/dbc/types.rs b/src/bp/dbc/types.rs index c4f8bffb..738a6b5c 100644 --- a/src/bp/dbc/types.rs +++ b/src/bp/dbc/types.rs @@ -32,7 +32,8 @@ pub trait Container: Sized { fn into_proof(self) -> Proof; } -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Display, StrictEncode, StrictDecode)] +#[strict_crate(crate)] #[display(Debug)] pub struct Proof { pub pubkey: secp256k1::PublicKey, @@ -94,21 +95,6 @@ pub(super) mod strict_encoding { } } - impl StrictEncode for Proof { - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.pubkey, self.script_info)) - } - } - - impl StrictDecode for Proof { - fn strict_decode(mut d: D) -> Result { - Ok(Self { - pubkey: secp256k1::PublicKey::strict_decode(&mut d)?, - script_info: ScriptInfo::strict_decode(&mut d)?, - }) - } - } - #[cfg(test)] mod test { use super::*; diff --git a/src/bp/short_id.rs b/src/bp/short_id.rs index 47fc8eb9..b6b5a998 100644 --- a/src/bp/short_id.rs +++ b/src/bp/short_id.rs @@ -339,7 +339,10 @@ impl Descriptor { } } -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Display)] +#[derive( + Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, Display, StrictEncode, StrictDecode, +)] +#[strict_crate(crate)] #[display(Debug)] pub struct ShortId(u64); diff --git a/src/bp/strict_encoding.rs b/src/bp/strict_encoding.rs index 917a8235..9110e1a4 100644 --- a/src/bp/strict_encoding.rs +++ b/src/bp/strict_encoding.rs @@ -17,7 +17,7 @@ use bitcoin::hashes::{hash160, sha256, sha256d, sha512}; use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::{secp256k1, util::bip32, BlockHash, OutPoint, Script, Txid, XpubIdentifier}; -use super::{blind::OutpointHash, blind::OutpointReveal, ShortId}; +use super::blind::OutpointHash; use crate::strict_encoding::{self, Error, StrictDecode, StrictEncode}; use bitcoin::util::bip32::KeyApplication; @@ -203,20 +203,6 @@ impl StrictDecode for KeyApplication { } } -impl StrictEncode for ShortId { - #[inline] - fn strict_encode(&self, e: E) -> Result { - self.into_u64().strict_encode(e) - } -} - -impl StrictDecode for ShortId { - #[inline] - fn strict_decode(d: D) -> Result { - Ok(Self::from(u64::strict_decode(d)?)) - } -} - impl StrictEncode for OutPoint { #[inline] fn strict_encode(&self, mut e: E) -> Result { @@ -234,24 +220,6 @@ impl StrictDecode for OutPoint { } } -impl StrictEncode for OutpointReveal { - #[inline] - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.blinding, self.txid, self.vout)) - } -} - -impl StrictDecode for OutpointReveal { - #[inline] - fn strict_decode(mut d: D) -> Result { - Ok(Self { - blinding: u64::strict_decode(&mut d)?, - txid: Txid::strict_decode(&mut d)?, - vout: u32::strict_decode(&mut d)?, - }) - } -} - impl StrictEncode for bip32::ChildNumber { #[inline] fn strict_encode(&self, mut e: E) -> Result { @@ -372,7 +340,7 @@ pub(crate) mod test { use bitcoin::{hashes::hex::FromHex, secp256k1::Message, BlockHash}; use super::*; - use crate::bp::short_id; + use crate::bp::{blind::OutpointReveal, short_id, ShortId}; use crate::strict_encoding::test::test_suite; pub(crate) fn encode_decode( diff --git a/src/lib.rs b/src/lib.rs index 2ec2dfaa..26f012ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,9 @@ pub extern crate bitcoin; pub extern crate bitcoin_hashes; pub extern crate miniscript; +#[macro_use] +extern crate lnpbp_derive; + #[macro_use] mod paradigms; #[macro_use] diff --git a/src/lnpbps/lnpbp4.rs b/src/lnpbps/lnpbp4.rs index 253c8109..3e428019 100644 --- a/src/lnpbps/lnpbp4.rs +++ b/src/lnpbps/lnpbp4.rs @@ -12,14 +12,12 @@ // If not, see . use std::collections::BTreeMap; -use std::io; use bitcoin::hashes::{sha256, Hash, HashEngine}; use bitcoin::secp256k1::rand::{thread_rng, Rng}; use bitcoin::util::uint::Uint256; use crate::commit_verify::TryCommitVerify; -use crate::strict_encoding::{self, StrictDecode, StrictEncode}; /// Source data for creation of multi-message commitments according to LNPBP-4 procedure pub type MultiMsg = BTreeMap; @@ -29,7 +27,8 @@ pub type Lnpbp4Hash = sha256::Hash; #[display(Debug)] pub struct TooManyMessagesError; -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display, StrictEncode, StrictDecode)] +#[strict_crate(crate)] #[display(Debug)] pub struct MultimsgCommitmentItem { pub protocol: Option, @@ -46,7 +45,8 @@ impl MultimsgCommitmentItem { } /// Multimessage commitment data according to LNPBP-4 specification -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display, StrictEncode, StrictDecode)] +#[strict_crate(crate)] #[display(Debug)] pub struct MultimsgCommitment { pub commitments: Vec, @@ -116,33 +116,3 @@ impl TryCommitVerify for MultimsgCommitment { }) } } - -impl StrictEncode for MultimsgCommitmentItem { - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.protocol, self.commitment)) - } -} - -impl StrictDecode for MultimsgCommitmentItem { - fn strict_decode(mut d: D) -> Result { - Ok(Self { - protocol: Option::::strict_decode(&mut d)?, - commitment: Lnpbp4Hash::strict_decode(&mut d)?, - }) - } -} - -impl StrictEncode for MultimsgCommitment { - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.commitments, self.entropy)) - } -} - -impl StrictDecode for MultimsgCommitment { - fn strict_decode(mut d: D) -> Result { - Ok(Self { - commitments: Vec::::strict_decode(&mut d)?, - entropy: Option::::strict_decode(&mut d)?, - }) - } -} diff --git a/src/paradigms/client_side_validation.rs b/src/paradigms/client_side_validation.rs index 9ebbc97c..8115afc5 100644 --- a/src/paradigms/client_side_validation.rs +++ b/src/paradigms/client_side_validation.rs @@ -229,31 +229,8 @@ hash_newtype!( ); impl_hashencode!(MerkleNode); -mod strict_encode { - use super::*; - use crate::strict_encoding::{Error, StrictDecode, StrictEncode}; - - impl StrictEncode for MerkleNode { - type Error = Error; - - #[inline] - fn strict_encode(&self, e: E) -> Result { - self.into_inner().to_vec().strict_encode(e) - } - } - - impl StrictDecode for MerkleNode { - type Error = Error; - - #[inline] - fn strict_decode(d: D) -> Result { - Ok( - Self::from_slice(&Vec::::strict_decode(d)?).map_err(|_| { - Error::DataIntegrityError("Wrong merkle node hash data size".to_string()) - })?, - ) - } - } +impl strict_encoding::Strategy for MerkleNode { + type Strategy = strict_encoding::strategies::HashFixedBytes; } /// Merklization procedure that uses tagged hashes with depth commitments diff --git a/src/rgb/contract/amount.rs b/src/rgb/contract/amount.rs index aa8dd0b6..9588ac4a 100644 --- a/src/rgb/contract/amount.rs +++ b/src/rgb/contract/amount.rs @@ -80,7 +80,8 @@ impl Ord for Revealed { } } -#[derive(Clone, Debug, Display, AsAny)] +#[derive(Clone, Debug, Display, AsAny, StrictEncode, StrictDecode)] +#[strict_crate(crate)] #[display(Debug)] pub struct Confidential { pub commitment: pedersen::Commitment, @@ -281,25 +282,6 @@ mod strict_encoding { } } - impl StrictEncode for Confidential { - type Error = Error; - - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.commitment, self.bulletproof)) - } - } - - impl StrictDecode for Confidential { - type Error = Error; - - fn strict_decode(mut d: D) -> Result { - Ok(Self { - commitment: pedersen::Commitment::strict_decode(&mut d)?, - bulletproof: pedersen::RangeProof::strict_decode(&mut d)?, - }) - } - } - impl StrictEncode for Revealed { type Error = Error; diff --git a/src/rgb/contract/nodes.rs b/src/rgb/contract/nodes.rs index 4ca233f7..f96e88c4 100644 --- a/src/rgb/contract/nodes.rs +++ b/src/rgb/contract/nodes.rs @@ -104,7 +104,8 @@ pub trait Node { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, StrictEncode, StrictDecode)] +#[strict_crate(crate)] pub struct Genesis { schema_id: SchemaId, chain: bp::Chain, @@ -113,7 +114,8 @@ pub struct Genesis { script: SimplicityScript, } -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, PartialEq, StrictEncode, StrictDecode)] +#[strict_crate(crate)] pub struct Transition { type_id: schema::TransitionType, metadata: Metadata, @@ -285,7 +287,7 @@ impl Transition { mod strict_encoding { use super::*; - use crate::strict_encoding::{strategies, Error, Strategy, StrictDecode, StrictEncode}; + use crate::strict_encoding::{strategies, Error, Strategy, StrictEncode}; use std::io; impl Strategy for NodeId { @@ -314,60 +316,6 @@ mod strict_encoding { encoder().expect("Strict encoding of genesis data must not fail") } } - - impl StrictEncode for Genesis { - type Error = Error; - - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; - self.schema_id, - self.chain, - self.metadata, - self.assignments, - self.script)) - } - } - - impl StrictDecode for Genesis { - type Error = Error; - - fn strict_decode(mut d: D) -> Result { - Ok(Self { - schema_id: SchemaId::strict_decode(&mut d)?, - chain: bp::Chain::strict_decode(&mut d)?, - metadata: Metadata::strict_decode(&mut d)?, - assignments: Assignments::strict_decode(&mut d)?, - script: SimplicityScript::strict_decode(&mut d)?, - }) - } - } - - impl StrictEncode for Transition { - type Error = Error; - - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; - self.type_id, - self.metadata, - self.ancestors, - self.assignments, - self.script)) - } - } - - impl StrictDecode for Transition { - type Error = Error; - - fn strict_decode(mut d: D) -> Result { - Ok(Self { - type_id: schema::TransitionType::strict_decode(&mut d)?, - metadata: Metadata::strict_decode(&mut d)?, - ancestors: Ancestors::strict_decode(&mut d)?, - assignments: Assignments::strict_decode(&mut d)?, - script: SimplicityScript::strict_decode(&mut d)?, - }) - } - } } #[cfg(test)] diff --git a/src/rgb/schema/state.rs b/src/rgb/schema/state.rs index b6e2cc25..7d503400 100644 --- a/src/rgb/schema/state.rs +++ b/src/rgb/schema/state.rs @@ -18,7 +18,8 @@ use num_derive::{FromPrimitive, ToPrimitive}; use super::{elliptic_curve, script, Bits, DigestAlgorithm, EllipticCurve}; -#[derive(Clone, PartialEq, Debug, Display)] +#[derive(Clone, PartialEq, Debug, Display, StrictEncode, StrictDecode)] +#[strict_crate(crate)] #[display(Debug)] pub struct StateSchema { pub format: StateFormat, @@ -152,25 +153,6 @@ mod strict_encoding { use num_derive::{FromPrimitive, ToPrimitive}; use num_traits::{Bounded, ToPrimitive}; - impl StrictEncode for StateSchema { - type Error = Error; - - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.format, self.abi)) - } - } - - impl StrictDecode for StateSchema { - type Error = Error; - - fn strict_decode(mut d: D) -> Result { - Ok(Self { - format: StateFormat::strict_decode(&mut d)?, - abi: script::AssignmentAbi::strict_decode(&mut d)?, - }) - } - } - impl_enum_strict_encoding!(StateType); impl StrictEncode for StateFormat { diff --git a/src/rgb/stash/anchor.rs b/src/rgb/stash/anchor.rs index 89668bb3..71393299 100644 --- a/src/rgb/stash/anchor.rs +++ b/src/rgb/stash/anchor.rs @@ -12,7 +12,6 @@ // If not, see . use std::collections::{BTreeMap, HashMap}; -use std::io; use amplify::Wrapper; use bitcoin::secp256k1; @@ -29,7 +28,6 @@ use crate::client_side_validation::{commit_strategy, CommitEncodeWithStrategy, C use crate::commit_verify::{CommitVerify, EmbedCommitVerify, TryCommitVerify}; use crate::lnpbp4::{MultimsgCommitment, TooManyMessagesError}; use crate::rgb::{ContractId, NodeId}; -use crate::strict_encoding::{self, StrictDecode, StrictEncode}; pub const PSBT_FEE_KEY: &[u8] = b"\x03rgb\x01"; pub const PSBT_PUBKEY_KEY: &[u8] = b"\x03rgb\x02"; @@ -66,7 +64,8 @@ tagged_hash!( doc = "Unique anchor identifier equivalent to the anchor commitment hash" ); -#[derive(Clone, Debug)] +#[derive(Clone, Debug, StrictEncode, StrictDecode)] +#[strict_crate(crate)] pub struct Anchor { pub txid: Txid, pub commitment: MultimsgCommitment, @@ -258,19 +257,3 @@ impl CommitEncodeWithStrategy for Anchor { impl ConsensusCommit for Anchor { type Commitment = AnchorId; } - -impl StrictEncode for Anchor { - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.txid, self.commitment, self.proof)) - } -} - -impl StrictDecode for Anchor { - fn strict_decode(mut d: D) -> Result { - Ok(Self { - txid: Txid::strict_decode(&mut d)?, - commitment: MultimsgCommitment::strict_decode(&mut d)?, - proof: Proof::strict_decode(&mut d)?, - }) - } -} diff --git a/src/rgb/stash/consignment.rs b/src/rgb/stash/consignment.rs index 46311524..1fce2da8 100644 --- a/src/rgb/stash/consignment.rs +++ b/src/rgb/stash/consignment.rs @@ -12,20 +12,19 @@ // If not, see . use std::collections::BTreeSet; -use std::io; use bitcoin::Txid; use crate::bp; use crate::rgb::{validation, Anchor, Genesis, Node, NodeId, Schema, Transition, Validator}; -use crate::strict_encoding::{self, StrictDecode, StrictEncode}; pub type ConsignmentEndpoints = Vec<(NodeId, bp::blind::OutpointHash)>; pub type ConsignmentData = Vec<(Anchor, Transition)>; pub const RGB_CONSIGNMENT_VERSION: u16 = 0; -#[derive(Clone, Debug, Display)] +#[derive(Clone, Debug, Display, StrictEncode, StrictDecode)] +#[strict_crate(crate)] #[display(Debug)] pub struct Consignment { version: u16, @@ -69,28 +68,12 @@ impl Consignment { } } -impl StrictEncode for Consignment { - fn strict_encode(&self, mut e: E) -> Result { - Ok(strict_encode_list!(e; self.version, self.genesis, self.endpoints, self.data)) - } -} - -impl StrictDecode for Consignment { - fn strict_decode(mut d: D) -> Result { - Ok(Self { - version: u16::strict_decode(&mut d)?, - genesis: Genesis::strict_decode(&mut d)?, - endpoints: ConsignmentEndpoints::strict_decode(&mut d)?, - data: ConsignmentData::strict_decode(&mut d)?, - }) - } -} - #[cfg(test)] pub(crate) mod test { use super::*; use crate::rgb::schema::test::schema; use crate::rgb::validation::TxResolver; + use crate::strict_encoding::StrictDecode; pub(crate) fn consignment() -> Consignment { let data: Vec = vec![ diff --git a/src/rgb/stash/disclosure.rs b/src/rgb/stash/disclosure.rs index ab6be945..14a36434 100644 --- a/src/rgb/stash/disclosure.rs +++ b/src/rgb/stash/disclosure.rs @@ -16,21 +16,6 @@ // TODO: Implement disclosures -use std::io; - -use crate::strict_encoding::{self, StrictDecode, StrictEncode}; - -#[derive(Clone, Debug)] +#[derive(Clone, Debug, StrictEncode, StrictDecode)] +#[strict_crate(crate)] pub struct Disclosure {} - -impl StrictEncode for Disclosure { - fn strict_encode(&self, _: E) -> Result { - unimplemented!() - } -} - -impl StrictDecode for Disclosure { - fn strict_decode(_: D) -> Result { - unimplemented!() - } -}