From af3aca64ed47277a1a1f4f7f89cd7e7903d0a04a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 1 Jul 2024 23:45:29 -0400 Subject: [PATCH 1/8] Use Atom for encoding --- crates/chia-protocol/src/bytes.rs | 5 ++-- crates/clvm-derive/src/to_clvm.rs | 4 +-- crates/clvm-traits/src/clvm_encoder.rs | 32 ++++++++++++++++++++--- crates/clvm-traits/src/lib.rs | 2 ++ crates/clvm-traits/src/match_byte.rs | 7 +---- crates/clvm-traits/src/to_clvm.rs | 26 ++++++------------ crates/clvm-utils/src/hash_encoder.rs | 5 ++-- crates/clvm-utils/src/lib.rs | 2 ++ crates/clvm-utils/src/prettier_printer.rs | 1 + 9 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 crates/clvm-utils/src/prettier_printer.rs diff --git a/crates/chia-protocol/src/bytes.rs b/crates/chia-protocol/src/bytes.rs index d9dd02a37..cb9f7f9a2 100644 --- a/crates/chia-protocol/src/bytes.rs +++ b/crates/chia-protocol/src/bytes.rs @@ -1,6 +1,7 @@ use chia_traits::{chia_error, read_bytes, Streamable}; use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; use clvm_utils::TreeHash; +use clvmr::Atom; use sha2::{Digest, Sha256}; use std::array::TryFromSliceError; use std::fmt; @@ -111,7 +112,7 @@ impl FromJsonDict for Bytes { impl ToClvm for Bytes { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - encoder.encode_atom(self.0.as_slice()) + encoder.encode_atom(Atom::Borrowed(self.0.as_slice())) } } @@ -258,7 +259,7 @@ impl FromJsonDict for BytesImpl { impl ToClvm for BytesImpl { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - encoder.encode_atom(self.0.as_slice()) + encoder.encode_atom(Atom::Borrowed(self.0.as_slice())) } } diff --git a/crates/clvm-derive/src/to_clvm.rs b/crates/clvm-derive/src/to_clvm.rs index 13360f880..2d76d6cd1 100644 --- a/crates/clvm-derive/src/to_clvm.rs +++ b/crates/clvm-derive/src/to_clvm.rs @@ -52,8 +52,8 @@ fn encode_fields( let initial_value = match repr { Repr::Atom | Repr::Transparent => unreachable!(), - Repr::List => quote!(encoder.encode_atom(&[])?), - Repr::Curry => quote!(encoder.encode_atom(&[1])?), + Repr::List => quote!(encoder.encode_atom(#crate_name::Atom::Borrowed(&[]))?), + Repr::Curry => quote!(encoder.encode_atom(#crate_name::Atom::Borrowed(&[1]))?), }; // We're going to build the return value in reverse order, so we need to start with the terminator. diff --git a/crates/clvm-traits/src/clvm_encoder.rs b/crates/clvm-traits/src/clvm_encoder.rs index 7870ba29e..38d25bb1b 100644 --- a/crates/clvm-traits/src/clvm_encoder.rs +++ b/crates/clvm-traits/src/clvm_encoder.rs @@ -1,17 +1,33 @@ -use clvmr::{Allocator, NodePtr}; +use clvmr::{Allocator, Atom, NodePtr}; +use num_bigint::BigInt; use crate::{clvm_list, clvm_quote, ToClvm, ToClvmError}; pub trait ClvmEncoder: Sized { type Node: Clone + ToClvm; - fn encode_atom(&mut self, bytes: &[u8]) -> Result; + fn encode_atom(&mut self, atom: Atom<'_>) -> Result; fn encode_pair( &mut self, first: Self::Node, rest: Self::Node, ) -> Result; + fn encode_bigint(&mut self, number: BigInt) -> Result { + let bytes = number.to_signed_bytes_be(); + let mut slice = bytes.as_slice(); + + // Remove leading zeros. + while !slice.is_empty() && slice[0] == 0 { + if slice.len() > 1 && (slice[1] & 0x80 == 0x80) { + break; + } + slice = &slice[1..]; + } + + self.encode_atom(Atom::Borrowed(slice)) + } + fn encode_curried_arg( &mut self, first: Self::Node, @@ -32,8 +48,12 @@ pub trait ClvmEncoder: Sized { impl ClvmEncoder for Allocator { type Node = NodePtr; - fn encode_atom(&mut self, bytes: &[u8]) -> Result { - self.new_atom(bytes).or(Err(ToClvmError::OutOfMemory)) + fn encode_atom(&mut self, atom: Atom<'_>) -> Result { + match atom { + Atom::Borrowed(bytes) => self.new_atom(bytes), + Atom::U32(bytes, _len) => self.new_small_number(u32::from_be_bytes(bytes)), + } + .or(Err(ToClvmError::OutOfMemory)) } fn encode_pair( @@ -43,6 +63,10 @@ impl ClvmEncoder for Allocator { ) -> Result { self.new_pair(first, rest).or(Err(ToClvmError::OutOfMemory)) } + + fn encode_bigint(&mut self, number: BigInt) -> Result { + self.new_number(number).or(Err(ToClvmError::OutOfMemory)) + } } pub trait ToNodePtr { diff --git a/crates/clvm-traits/src/lib.rs b/crates/clvm-traits/src/lib.rs index 100b19143..c38df5744 100644 --- a/crates/clvm-traits/src/lib.rs +++ b/crates/clvm-traits/src/lib.rs @@ -26,6 +26,8 @@ pub use match_byte::*; pub use to_clvm::*; pub use wrappers::*; +pub use clvmr::Atom; + #[cfg(test)] #[cfg(feature = "derive")] mod derive_tests { diff --git a/crates/clvm-traits/src/match_byte.rs b/crates/clvm-traits/src/match_byte.rs index 34f4a7343..f0da5f5c2 100644 --- a/crates/clvm-traits/src/match_byte.rs +++ b/crates/clvm-traits/src/match_byte.rs @@ -7,12 +7,7 @@ pub struct MatchByte; impl ToClvm for MatchByte { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - if BYTE == 0 { - return encoder.encode_atom(&[]); - } - let number = BigInt::from(BYTE); - let bytes = number.to_signed_bytes_be(); - encoder.encode_atom(&bytes) + encoder.encode_bigint(BigInt::from(BYTE)) } } diff --git a/crates/clvm-traits/src/to_clvm.rs b/crates/clvm-traits/src/to_clvm.rs index 0d7281b45..b68044767 100644 --- a/crates/clvm-traits/src/to_clvm.rs +++ b/crates/clvm-traits/src/to_clvm.rs @@ -1,5 +1,6 @@ use std::{rc::Rc, sync::Arc}; +use clvmr::Atom; use num_bigint::BigInt; use crate::{ClvmEncoder, ToClvmError}; @@ -8,22 +9,11 @@ pub trait ToClvm { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result; } -pub fn simplify_int_bytes(mut slice: &[u8]) -> &[u8] { - while (!slice.is_empty()) && (slice[0] == 0) { - if slice.len() > 1 && (slice[1] & 0x80 == 0x80) { - break; - } - slice = &slice[1..]; - } - slice -} - macro_rules! clvm_primitive { ($primitive:ty) => { impl ToClvm for $primitive { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - let number = BigInt::from(*self); - encoder.encode_atom(simplify_int_bytes(&number.to_signed_bytes_be())) + encoder.encode_bigint(BigInt::from(*self)) } } }; @@ -98,7 +88,7 @@ where impl ToClvm for () { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - encoder.encode_atom(&[]) + encoder.encode_atom(Atom::Borrowed(&[])) } } @@ -107,7 +97,7 @@ where T: ToClvm, { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - let mut result = encoder.encode_atom(&[])?; + let mut result = encoder.encode_atom(Atom::Borrowed(&[]))?; for item in self.iter().rev() { let value = item.to_clvm(encoder)?; result = encoder.encode_pair(value, result)?; @@ -141,14 +131,14 @@ where fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { match self { Some(value) => value.to_clvm(encoder), - None => encoder.encode_atom(&[]), + None => encoder.encode_atom(Atom::Borrowed(&[])), } } } impl ToClvm for &str { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - encoder.encode_atom(self.as_bytes()) + encoder.encode_atom(Atom::Borrowed(self.as_bytes())) } } @@ -161,14 +151,14 @@ impl ToClvm for String { #[cfg(feature = "chia-bls")] impl ToClvm for chia_bls::PublicKey { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - encoder.encode_atom(&self.to_bytes()) + encoder.encode_atom(Atom::Borrowed(&self.to_bytes())) } } #[cfg(feature = "chia-bls")] impl ToClvm for chia_bls::Signature { fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { - encoder.encode_atom(&self.to_bytes()) + encoder.encode_atom(Atom::Borrowed(&self.to_bytes())) } } diff --git a/crates/clvm-utils/src/hash_encoder.rs b/crates/clvm-utils/src/hash_encoder.rs index ef9cd3c4f..edfb4e8ca 100644 --- a/crates/clvm-utils/src/hash_encoder.rs +++ b/crates/clvm-utils/src/hash_encoder.rs @@ -1,6 +1,7 @@ #![allow(clippy::items_after_statements)] use clvm_traits::{ClvmEncoder, ToClvm, ToClvmError}; +use clvmr::Atom; use crate::{tree_hash_atom, tree_hash_pair, TreeHash}; @@ -23,8 +24,8 @@ pub struct TreeHasher; impl ClvmEncoder for TreeHasher { type Node = TreeHash; - fn encode_atom(&mut self, bytes: &[u8]) -> Result { - Ok(tree_hash_atom(bytes)) + fn encode_atom(&mut self, bytes: Atom<'_>) -> Result { + Ok(tree_hash_atom(bytes.as_ref())) } fn encode_pair( diff --git a/crates/clvm-utils/src/lib.rs b/crates/clvm-utils/src/lib.rs index 53c8688e4..09a54a70e 100644 --- a/crates/clvm-utils/src/lib.rs +++ b/crates/clvm-utils/src/lib.rs @@ -27,9 +27,11 @@ mod curried_program; mod curry_tree_hash; mod hash_encoder; +mod prettier_printer; mod tree_hash; pub use curried_program::*; pub use curry_tree_hash::*; pub use hash_encoder::*; +pub use prettier_printer::*; pub use tree_hash::*; diff --git a/crates/clvm-utils/src/prettier_printer.rs b/crates/clvm-utils/src/prettier_printer.rs new file mode 100644 index 000000000..3fcf08777 --- /dev/null +++ b/crates/clvm-utils/src/prettier_printer.rs @@ -0,0 +1 @@ +pub struct PrettierPrinter {} From e17e0d11ad5cac8c34a9eb3c53d9cdfd6e93da09 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 2 Jul 2024 00:15:46 -0400 Subject: [PATCH 2/8] Use the encoder/decoder instead of node in trait --- crates/chia-protocol/src/bytes.rs | 16 ++-- crates/chia-protocol/src/coin.rs | 8 +- crates/chia-puzzles/src/puzzles/nft.rs | 8 +- crates/clvm-derive/src/from_clvm.rs | 54 +++++++++----- crates/clvm-derive/src/to_clvm.rs | 60 +++++++++++---- crates/clvm-traits/src/clvm_decoder.rs | 11 +-- crates/clvm-traits/src/clvm_encoder.rs | 11 +-- crates/clvm-traits/src/from_clvm.rs | 82 ++++++++++---------- crates/clvm-traits/src/lib.rs | 8 +- crates/clvm-traits/src/match_byte.rs | 8 +- crates/clvm-traits/src/to_clvm.rs | 95 ++++++++++++------------ crates/clvm-traits/src/wrappers.rs | 8 +- crates/clvm-utils/src/curried_program.rs | 22 +++--- crates/clvm-utils/src/hash_encoder.rs | 9 +-- 14 files changed, 221 insertions(+), 179 deletions(-) diff --git a/crates/chia-protocol/src/bytes.rs b/crates/chia-protocol/src/bytes.rs index cb9f7f9a2..24e1cae00 100644 --- a/crates/chia-protocol/src/bytes.rs +++ b/crates/chia-protocol/src/bytes.rs @@ -110,14 +110,14 @@ impl FromJsonDict for Bytes { } } -impl ToClvm for Bytes { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for Bytes { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_atom(Atom::Borrowed(self.0.as_slice())) } } -impl FromClvm for Bytes { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for Bytes { + fn from_clvm(decoder: &D, node: N) -> Result { let bytes = decoder.decode_atom(&node)?; Ok(Self(bytes.as_ref().to_vec())) } @@ -257,14 +257,14 @@ impl FromJsonDict for BytesImpl { } } -impl ToClvm for BytesImpl { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl, const LEN: usize> ToClvm for BytesImpl { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_atom(Atom::Borrowed(self.0.as_slice())) } } -impl FromClvm for BytesImpl { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl, const LEN: usize> FromClvm for BytesImpl { + fn from_clvm(decoder: &D, node: N) -> Result { let bytes = decoder.decode_atom(&node)?; if bytes.as_ref().len() != LEN { return Err(FromClvmError::WrongAtomLength { diff --git a/crates/chia-protocol/src/coin.rs b/crates/chia-protocol/src/coin.rs index 42e6a4074..54735023d 100644 --- a/crates/chia-protocol/src/coin.rs +++ b/crates/chia-protocol/src/coin.rs @@ -55,14 +55,14 @@ impl Coin { } } -impl ToClvm for Coin { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for Coin { + fn to_clvm(&self, encoder: &mut E) -> Result { clvm_list!(self.parent_coin_info, self.puzzle_hash, self.amount).to_clvm(encoder) } } -impl FromClvm for Coin { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for Coin { + fn from_clvm(decoder: &D, node: N) -> Result { let destructure_list!(parent_coin_info, puzzle_hash, amount) = , BytesImpl<32>, u64)>::from_clvm(decoder, node)?; Ok(Coin { diff --git a/crates/chia-puzzles/src/puzzles/nft.rs b/crates/chia-puzzles/src/puzzles/nft.rs index 529fb5bd9..64db1db14 100644 --- a/crates/chia-puzzles/src/puzzles/nft.rs +++ b/crates/chia-puzzles/src/puzzles/nft.rs @@ -195,8 +195,8 @@ impl Default for NftMetadata { } } -impl FromClvm for NftMetadata { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for NftMetadata { + fn from_clvm(decoder: &D, node: N) -> Result { let items: Vec<(String, Raw)> = FromClvm::from_clvm(decoder, node)?; let mut metadata = Self::default(); @@ -218,8 +218,8 @@ impl FromClvm for NftMetadata { } } -impl ToClvm for NftMetadata { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for NftMetadata { + fn to_clvm(&self, encoder: &mut E) -> Result { let mut items: Vec<(&str, Raw)> = Vec::new(); if !self.data_uris.is_empty() { diff --git a/crates/clvm-derive/src/from_clvm.rs b/crates/clvm-derive/src/from_clvm.rs index a2fe2d7a2..eaca4b197 100644 --- a/crates/clvm-derive/src/from_clvm.rs +++ b/crates/clvm-derive/src/from_clvm.rs @@ -11,10 +11,13 @@ use crate::{ pub fn from_clvm(ast: DeriveInput) -> TokenStream { let parsed = parse("FromClvm", &ast); let node_name = Ident::new("Node", Span::mixed_site()); + let decoder_name = Ident::new("D", Span::mixed_site()); match parsed { - ParsedInfo::Struct(struct_info) => impl_for_struct(ast, struct_info, &node_name), - ParsedInfo::Enum(enum_info) => impl_for_enum(ast, &enum_info, &node_name), + ParsedInfo::Struct(struct_info) => { + impl_for_struct(ast, struct_info, &node_name, &decoder_name) + } + ParsedInfo::Enum(enum_info) => impl_for_enum(ast, &enum_info, &node_name, &decoder_name), } } @@ -26,7 +29,7 @@ struct ParsedFields { fn field_parser_fn_body( crate_name: &Ident, - node_name: &Ident, + decoder_name: &Ident, fields: &[FieldInfo], repr: Repr, ) -> ParsedFields { @@ -96,7 +99,7 @@ fn field_parser_fn_body( // This handles the actual decoding of the field's value. let mut decoded_value = quote! { - <#ty as #crate_name::FromClvm<#node_name>>::from_clvm(decoder, #ident) + <#ty as #crate_name::FromClvm<#decoder_name>>::from_clvm(decoder, #ident) }; if let Some(default) = &field.optional_with_default { @@ -183,7 +186,12 @@ fn check_rest_value(crate_name: &Ident, repr: Repr) -> TokenStream { } } -fn impl_for_struct(ast: DeriveInput, struct_info: StructInfo, node_name: &Ident) -> TokenStream { +fn impl_for_struct( + ast: DeriveInput, + struct_info: StructInfo, + node_name: &Ident, + decoder_name: &Ident, +) -> TokenStream { let crate_name = crate_name(struct_info.crate_name); let ParsedFields { @@ -192,7 +200,7 @@ fn impl_for_struct(ast: DeriveInput, struct_info: StructInfo, node_name: &Ident) mut body, } = field_parser_fn_body( &crate_name, - node_name, + decoder_name, &struct_info.fields, struct_info.repr, ); @@ -216,16 +224,21 @@ fn impl_for_struct(ast: DeriveInput, struct_info: StructInfo, node_name: &Ident) } } - trait_impl(ast, &crate_name, node_name, &body) + trait_impl(ast, &crate_name, node_name, decoder_name, &body) } -fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> TokenStream { +fn impl_for_enum( + ast: DeriveInput, + enum_info: &EnumInfo, + node_name: &Ident, + decoder_name: &Ident, +) -> TokenStream { let crate_name = crate_name(enum_info.crate_name.clone()); let mut body = TokenStream::new(); if enum_info.is_untagged { - let variant_parsers = enum_variant_parsers(&crate_name, node_name, enum_info); + let variant_parsers = enum_variant_parsers(&crate_name, node_name, decoder_name, enum_info); // If the enum is untagged, we need to try each variant parser until one succeeds. for parser in variant_parsers { @@ -252,7 +265,7 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T if enum_info.default_repr == Repr::Atom { // If the enum is represented as an atom, we can simply decode the discriminant and match against it. body.extend(quote! { - let discriminant = <#discriminant_type as #crate_name::FromClvm<#node_name>>::from_clvm( + let discriminant = <#discriminant_type as #crate_name::FromClvm<#decoder_name>>::from_clvm( decoder, node, )?; @@ -267,7 +280,8 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T } }); } else { - let variant_parsers = enum_variant_parsers(&crate_name, node_name, enum_info); + let variant_parsers = + enum_variant_parsers(&crate_name, node_name, decoder_name, enum_info); let decode_next = match enum_info.default_repr { Repr::Atom | Repr::Transparent => unreachable!(), @@ -282,7 +296,7 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T body.extend(quote! { let (discriminant_node, node) = decoder.#decode_next(&node)?; - let discriminant = <#discriminant_type as #crate_name::FromClvm<#node_name>>::from_clvm( + let discriminant = <#discriminant_type as #crate_name::FromClvm<#decoder_name>>::from_clvm( decoder, discriminant_node, )?; @@ -299,12 +313,13 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T } } - trait_impl(ast, &crate_name, node_name, &body) + trait_impl(ast, &crate_name, node_name, decoder_name, &body) } fn enum_variant_parsers( crate_name: &Ident, node_name: &Ident, + decoder_name: &Ident, enum_info: &EnumInfo, ) -> Vec { let mut variant_parsers = Vec::new(); @@ -317,7 +332,7 @@ fn enum_variant_parsers( decoded_names, decoded_values, mut body, - } = field_parser_fn_body(crate_name, node_name, &variant.fields, repr); + } = field_parser_fn_body(crate_name, decoder_name, &variant.fields, repr); match variant.kind { VariantKind::Unit => { @@ -355,6 +370,7 @@ fn trait_impl( mut ast: DeriveInput, crate_name: &Ident, node_name: &Ident, + decoder_name: &Ident, body: &TokenStream, ) -> TokenStream { let type_name = ast.ident; @@ -363,7 +379,7 @@ fn trait_impl( // This isn't always perfect, but it's how derive macros work. add_trait_bounds( &mut ast.generics, - &parse_quote!(#crate_name::FromClvm<#node_name>), + &parse_quote!(#crate_name::FromClvm<#decoder_name>), ); let generics_clone = ast.generics.clone(); @@ -374,15 +390,19 @@ fn trait_impl( .params .push(GenericParam::Type(node_name.clone().into())); + ast.generics.params.push(GenericParam::Type( + parse_quote!(#decoder_name: #crate_name::ClvmDecoder), + )); + let (impl_generics, _, _) = ast.generics.split_for_impl(); // Generate the final trait implementation. quote! { #[automatically_derived] - impl #impl_generics #crate_name::FromClvm<#node_name> + impl #impl_generics #crate_name::FromClvm<#decoder_name> for #type_name #ty_generics #where_clause { fn from_clvm( - decoder: &impl #crate_name::ClvmDecoder, + decoder: &#decoder_name, mut node: #node_name, ) -> ::std::result::Result { #body diff --git a/crates/clvm-derive/src/to_clvm.rs b/crates/clvm-derive/src/to_clvm.rs index 2d76d6cd1..2dcb4890d 100644 --- a/crates/clvm-derive/src/to_clvm.rs +++ b/crates/clvm-derive/src/to_clvm.rs @@ -11,16 +11,19 @@ use crate::{ pub fn to_clvm(ast: DeriveInput) -> TokenStream { let parsed = parse("ToClvm", &ast); let node_name = Ident::new("Node", Span::mixed_site()); + let encoder_name = Ident::new("E", Span::mixed_site()); match parsed { - ParsedInfo::Struct(struct_info) => impl_for_struct(ast, struct_info, &node_name), - ParsedInfo::Enum(enum_info) => impl_for_enum(ast, &enum_info, &node_name), + ParsedInfo::Struct(struct_info) => { + impl_for_struct(ast, struct_info, &node_name, &encoder_name) + } + ParsedInfo::Enum(enum_info) => impl_for_enum(ast, &enum_info, &node_name, &encoder_name), } } fn encode_fields( crate_name: &Ident, - node_name: &Ident, + encoder_name: &Ident, fields: &[FieldInfo], repr: Repr, ) -> TokenStream { @@ -69,7 +72,7 @@ fn encode_fields( // Encode the field value. if_body.extend(quote! { - let value_node = <#ty as #crate_name::ToClvm<#node_name>>::to_clvm(&#value_name, encoder)?; + let value_node = <#ty as #crate_name::ToClvm<#encoder_name>>::to_clvm(&#value_name, encoder)?; }); if field.rest { @@ -105,7 +108,12 @@ fn encode_fields( body } -fn impl_for_struct(ast: DeriveInput, struct_info: StructInfo, node_name: &Ident) -> TokenStream { +fn impl_for_struct( + ast: DeriveInput, + struct_info: StructInfo, + node_name: &Ident, + encoder_name: &Ident, +) -> TokenStream { let crate_name = crate_name(struct_info.crate_name); let mut body = TokenStream::new(); @@ -138,7 +146,7 @@ fn impl_for_struct(ast: DeriveInput, struct_info: StructInfo, node_name: &Ident) body.extend(encode_fields( &crate_name, - node_name, + encoder_name, &struct_info.fields, struct_info.repr, )); @@ -147,10 +155,15 @@ fn impl_for_struct(ast: DeriveInput, struct_info: StructInfo, node_name: &Ident) Ok(node) }); - trait_impl(ast, &crate_name, node_name, &body) + trait_impl(ast, &crate_name, node_name, encoder_name, &body) } -fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> TokenStream { +fn impl_for_enum( + ast: DeriveInput, + enum_info: &EnumInfo, + node_name: &Ident, + encoder_name: &Ident, +) -> TokenStream { let crate_name = crate_name(enum_info.crate_name.clone()); let mut variant_destructures = Vec::new(); @@ -187,7 +200,12 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T for variant in &enum_info.variants { let repr = variant.repr.unwrap_or(enum_info.default_repr); - variant_bodies.push(encode_fields(&crate_name, node_name, &variant.fields, repr)); + variant_bodies.push(encode_fields( + &crate_name, + encoder_name, + &variant.fields, + repr, + )); } // Encode the variant's fields directly. @@ -214,7 +232,7 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T match self { #( Self::#variant_names => { - <#discriminant_type as #crate_name::ToClvm<#node_name>>::to_clvm( + <#discriminant_type as #crate_name::ToClvm<#encoder_name>>::to_clvm( &#discriminant_names, encoder, ) @@ -234,7 +252,12 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T for variant in &enum_info.variants { let repr = variant.repr.unwrap_or(enum_info.default_repr); - variant_bodies.push(encode_fields(&crate_name, node_name, &variant.fields, repr)); + variant_bodies.push(encode_fields( + &crate_name, + encoder_name, + &variant.fields, + repr, + )); } // Encode the discriminant followed by the variant's fields. @@ -245,7 +268,7 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T #( #variant_destructures => { #variant_bodies - let discriminant_node = <#discriminant_type as #crate_name::ToClvm<#node_name>>::to_clvm( + let discriminant_node = <#discriminant_type as #crate_name::ToClvm<#encoder_name>>::to_clvm( &#discriminant_names, encoder, )?; @@ -257,13 +280,14 @@ fn impl_for_enum(ast: DeriveInput, enum_info: &EnumInfo, node_name: &Ident) -> T } }; - trait_impl(ast, &crate_name, node_name, &body) + trait_impl(ast, &crate_name, node_name, encoder_name, &body) } fn trait_impl( mut ast: DeriveInput, crate_name: &Ident, node_name: &Ident, + encoder_name: &Ident, body: &TokenStream, ) -> TokenStream { let type_name = ast.ident; @@ -272,7 +296,7 @@ fn trait_impl( // This isn't always perfect, but it's how derive macros work. add_trait_bounds( &mut ast.generics, - &parse_quote!(#crate_name::ToClvm<#node_name>), + &parse_quote!(#crate_name::ToClvm<#encoder_name>), ); let generics_clone = ast.generics.clone(); @@ -283,16 +307,20 @@ fn trait_impl( .params .push(GenericParam::Type(node_name.clone().into())); + ast.generics.params.push(GenericParam::Type( + parse_quote!(#encoder_name: #crate_name::ClvmEncoder), + )); + let (impl_generics, _, _) = ast.generics.split_for_impl(); // Generate the final trait implementation. quote! { #[automatically_derived] - impl #impl_generics #crate_name::ToClvm<#node_name> + impl #impl_generics #crate_name::ToClvm<#encoder_name> for #type_name #ty_generics #where_clause { fn to_clvm( &self, - encoder: &mut impl #crate_name::ClvmEncoder + encoder: &mut #encoder_name ) -> ::std::result::Result<#node_name, #crate_name::ToClvmError> { #body } diff --git a/crates/clvm-traits/src/clvm_decoder.rs b/crates/clvm-traits/src/clvm_decoder.rs index 5b095ad93..cf7261590 100644 --- a/crates/clvm-traits/src/clvm_decoder.rs +++ b/crates/clvm-traits/src/clvm_decoder.rs @@ -6,7 +6,7 @@ use crate::{ }; pub trait ClvmDecoder: Sized { - type Node: Clone + FromClvm; + type Node: Clone + FromClvm; fn decode_atom(&self, node: &Self::Node) -> Result, FromClvmError>; fn decode_pair(&self, node: &Self::Node) -> Result<(Self::Node, Self::Node), FromClvmError>; @@ -59,7 +59,7 @@ pub trait FromNodePtr { impl FromNodePtr for T where - T: FromClvm, + T: FromClvm, { fn from_node_ptr(a: &Allocator, node: NodePtr) -> Result where @@ -69,11 +69,8 @@ where } } -impl FromClvm for NodePtr { - fn from_clvm( - _decoder: &impl ClvmDecoder, - node: NodePtr, - ) -> Result { +impl FromClvm for NodePtr { + fn from_clvm(_decoder: &Allocator, node: NodePtr) -> Result { Ok(node) } } diff --git a/crates/clvm-traits/src/clvm_encoder.rs b/crates/clvm-traits/src/clvm_encoder.rs index 38d25bb1b..c4c89424e 100644 --- a/crates/clvm-traits/src/clvm_encoder.rs +++ b/crates/clvm-traits/src/clvm_encoder.rs @@ -4,7 +4,7 @@ use num_bigint::BigInt; use crate::{clvm_list, clvm_quote, ToClvm, ToClvmError}; pub trait ClvmEncoder: Sized { - type Node: Clone + ToClvm; + type Node: Clone + ToClvm; fn encode_atom(&mut self, atom: Atom<'_>) -> Result; fn encode_pair( @@ -75,18 +75,15 @@ pub trait ToNodePtr { impl ToNodePtr for T where - T: ToClvm, + T: ToClvm, { fn to_node_ptr(&self, a: &mut Allocator) -> Result { self.to_clvm(a) } } -impl ToClvm for NodePtr { - fn to_clvm( - &self, - _encoder: &mut impl ClvmEncoder, - ) -> Result { +impl ToClvm for NodePtr { + fn to_clvm(&self, _encoder: &mut Allocator) -> Result { Ok(*self) } } diff --git a/crates/clvm-traits/src/from_clvm.rs b/crates/clvm-traits/src/from_clvm.rs index 320f36146..d5a8d2a3e 100644 --- a/crates/clvm-traits/src/from_clvm.rs +++ b/crates/clvm-traits/src/from_clvm.rs @@ -4,17 +4,17 @@ use num_bigint::{BigInt, Sign}; use crate::{ClvmDecoder, FromClvmError}; -pub trait FromClvm: Sized { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result; +pub trait FromClvm: Sized +where + D: ClvmDecoder, +{ + fn from_clvm(decoder: &D, node: D::Node) -> Result; } macro_rules! clvm_primitive { ($primitive:ty) => { - impl FromClvm for $primitive { - fn from_clvm( - decoder: &impl ClvmDecoder, - node: N, - ) -> Result { + impl> FromClvm for $primitive { + fn from_clvm(decoder: &D, node: N) -> Result { const LEN: usize = std::mem::size_of::<$primitive>(); let bytes = decoder.decode_atom(&node)?; @@ -57,8 +57,8 @@ clvm_primitive!(i128); clvm_primitive!(usize); clvm_primitive!(isize); -impl FromClvm for bool { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for bool { + fn from_clvm(decoder: &D, node: N) -> Result { let atom = decoder.decode_atom(&node)?; match atom.as_ref() { [] => Ok(false), @@ -70,39 +70,39 @@ impl FromClvm for bool { } } -impl FromClvm for Box +impl, T> FromClvm for Box where - T: FromClvm, + T: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + fn from_clvm(decoder: &D, node: N) -> Result { T::from_clvm(decoder, node).map(Box::new) } } -impl FromClvm for Rc +impl, T> FromClvm for Rc where - T: FromClvm, + T: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + fn from_clvm(decoder: &D, node: N) -> Result { T::from_clvm(decoder, node).map(Rc::new) } } -impl FromClvm for Arc +impl, T> FromClvm for Arc where - T: FromClvm, + T: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + fn from_clvm(decoder: &D, node: N) -> Result { T::from_clvm(decoder, node).map(Arc::new) } } -impl FromClvm for (A, B) +impl, A, B> FromClvm for (A, B) where - A: FromClvm, - B: FromClvm, + A: FromClvm, + B: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + fn from_clvm(decoder: &D, node: N) -> Result { let (first, rest) = decoder.decode_pair(&node)?; let first = A::from_clvm(decoder, first)?; let rest = B::from_clvm(decoder, rest)?; @@ -110,8 +110,8 @@ where } } -impl FromClvm for () { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for () { + fn from_clvm(decoder: &D, node: N) -> Result { let bytes = decoder.decode_atom(&node)?; if bytes.as_ref().is_empty() { Ok(()) @@ -124,11 +124,11 @@ impl FromClvm for () { } } -impl FromClvm for [T; LEN] +impl, T, const LEN: usize> FromClvm for [T; LEN] where - T: FromClvm, + T: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, mut node: N) -> Result { + fn from_clvm(decoder: &D, mut node: N) -> Result { let mut items = Vec::with_capacity(LEN); loop { if let Ok((first, rest)) = decoder.decode_pair(&node) { @@ -153,11 +153,11 @@ where } } -impl FromClvm for Vec +impl, T> FromClvm for Vec where - T: FromClvm, + T: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, mut node: N) -> Result { + fn from_clvm(decoder: &D, mut node: N) -> Result { let mut items = Vec::new(); loop { if let Ok((first, rest)) = decoder.decode_pair(&node) { @@ -178,11 +178,11 @@ where } } -impl FromClvm for Option +impl, T> FromClvm for Option where - T: FromClvm, + T: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + fn from_clvm(decoder: &D, node: N) -> Result { if let Ok(atom) = decoder.decode_atom(&node) { if atom.as_ref().is_empty() { return Ok(None); @@ -192,16 +192,16 @@ where } } -impl FromClvm for String { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for String { + fn from_clvm(decoder: &D, node: N) -> Result { let bytes = decoder.decode_atom(&node)?; Ok(Self::from_utf8(bytes.as_ref().to_vec())?) } } #[cfg(feature = "chia-bls")] -impl FromClvm for chia_bls::PublicKey { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for chia_bls::PublicKey { + fn from_clvm(decoder: &D, node: N) -> Result { let bytes = decoder.decode_atom(&node)?; let error = Err(FromClvmError::WrongAtomLength { expected: 48, @@ -213,8 +213,8 @@ impl FromClvm for chia_bls::PublicKey { } #[cfg(feature = "chia-bls")] -impl FromClvm for chia_bls::Signature { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for chia_bls::Signature { + fn from_clvm(decoder: &D, node: N) -> Result { let bytes = decoder.decode_atom(&node)?; let error = Err(FromClvmError::WrongAtomLength { expected: 96, @@ -227,13 +227,13 @@ impl FromClvm for chia_bls::Signature { #[cfg(test)] mod tests { - use clvmr::{serde::node_from_bytes, Allocator, NodePtr}; + use clvmr::{serde::node_from_bytes, Allocator}; use super::*; fn decode(a: &mut Allocator, hex: &str) -> Result where - T: FromClvm, + T: FromClvm, { let bytes = hex::decode(hex).unwrap(); let actual = node_from_bytes(a, &bytes).unwrap(); diff --git a/crates/clvm-traits/src/lib.rs b/crates/clvm-traits/src/lib.rs index c38df5744..408cd0267 100644 --- a/crates/clvm-traits/src/lib.rs +++ b/crates/clvm-traits/src/lib.rs @@ -37,11 +37,11 @@ mod derive_tests { use std::fmt::Debug; - use clvmr::{serde::node_to_bytes, Allocator, NodePtr}; + use clvmr::{serde::node_to_bytes, Allocator}; fn check(value: &T, expected: &str) where - T: Debug + PartialEq + ToClvm + FromClvm, + T: Debug + PartialEq + ToClvm + FromClvm, { let a = &mut Allocator::new(); @@ -56,8 +56,8 @@ mod derive_tests { fn coerce_into(value: A) -> B where - A: ToClvm, - B: FromClvm, + A: ToClvm, + B: FromClvm, { let a = &mut Allocator::new(); let ptr = value.to_clvm(a).unwrap(); diff --git a/crates/clvm-traits/src/match_byte.rs b/crates/clvm-traits/src/match_byte.rs index f0da5f5c2..a6816aa5f 100644 --- a/crates/clvm-traits/src/match_byte.rs +++ b/crates/clvm-traits/src/match_byte.rs @@ -5,14 +5,14 @@ use crate::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmErr #[derive(Debug, Copy, Clone)] pub struct MatchByte; -impl ToClvm for MatchByte { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl, const BYTE: u8> ToClvm for MatchByte { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_bigint(BigInt::from(BYTE)) } } -impl FromClvm for MatchByte { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { +impl, const BYTE: u8> FromClvm for MatchByte { + fn from_clvm(decoder: &D, node: N) -> Result { match decoder.decode_atom(&node)?.as_ref() { [] if BYTE == 0 => Ok(Self), [byte] if *byte == BYTE && BYTE > 0 => Ok(Self), diff --git a/crates/clvm-traits/src/to_clvm.rs b/crates/clvm-traits/src/to_clvm.rs index b68044767..963d20f2c 100644 --- a/crates/clvm-traits/src/to_clvm.rs +++ b/crates/clvm-traits/src/to_clvm.rs @@ -5,14 +5,17 @@ use num_bigint::BigInt; use crate::{ClvmEncoder, ToClvmError}; -pub trait ToClvm { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result; +pub trait ToClvm +where + E: ClvmEncoder, +{ + fn to_clvm(&self, encoder: &mut E) -> Result; } macro_rules! clvm_primitive { ($primitive:ty) => { - impl ToClvm for $primitive { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + impl> ToClvm for $primitive { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_bigint(BigInt::from(*self)) } } @@ -32,71 +35,71 @@ clvm_primitive!(i128); clvm_primitive!(usize); clvm_primitive!(isize); -impl ToClvm for bool { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for bool { + fn to_clvm(&self, encoder: &mut E) -> Result { i32::from(*self).to_clvm(encoder) } } -impl ToClvm for &T +impl, T> ToClvm for &T where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { T::to_clvm(*self, encoder) } } -impl ToClvm for Box +impl, T> ToClvm for Box where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { T::to_clvm(self, encoder) } } -impl ToClvm for Rc +impl, T> ToClvm for Rc where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { T::to_clvm(self, encoder) } } -impl ToClvm for Arc +impl, T> ToClvm for Arc where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { T::to_clvm(self, encoder) } } -impl ToClvm for (A, B) +impl, A, B> ToClvm for (A, B) where - A: ToClvm, - B: ToClvm, + A: ToClvm, + B: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { let first = self.0.to_clvm(encoder)?; let rest = self.1.to_clvm(encoder)?; encoder.encode_pair(first, rest) } } -impl ToClvm for () { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for () { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_atom(Atom::Borrowed(&[])) } } -impl ToClvm for &[T] +impl, T> ToClvm for &[T] where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { let mut result = encoder.encode_atom(Atom::Borrowed(&[]))?; for item in self.iter().rev() { let value = item.to_clvm(encoder)?; @@ -106,29 +109,29 @@ where } } -impl ToClvm for [T; LEN] +impl, T, const LEN: usize> ToClvm for [T; LEN] where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { self.as_slice().to_clvm(encoder) } } -impl ToClvm for Vec +impl, T> ToClvm for Vec where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { self.as_slice().to_clvm(encoder) } } -impl ToClvm for Option +impl, T> ToClvm for Option where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { match self { Some(value) => value.to_clvm(encoder), None => encoder.encode_atom(Atom::Borrowed(&[])), @@ -136,42 +139,42 @@ where } } -impl ToClvm for &str { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for &str { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_atom(Atom::Borrowed(self.as_bytes())) } } -impl ToClvm for String { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for String { + fn to_clvm(&self, encoder: &mut E) -> Result { self.as_str().to_clvm(encoder) } } #[cfg(feature = "chia-bls")] -impl ToClvm for chia_bls::PublicKey { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for chia_bls::PublicKey { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_atom(Atom::Borrowed(&self.to_bytes())) } } #[cfg(feature = "chia-bls")] -impl ToClvm for chia_bls::Signature { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for chia_bls::Signature { + fn to_clvm(&self, encoder: &mut E) -> Result { encoder.encode_atom(Atom::Borrowed(&self.to_bytes())) } } #[cfg(test)] mod tests { - use clvmr::{serde::node_to_bytes, Allocator, NodePtr}; + use clvmr::{serde::node_to_bytes, Allocator}; use hex::ToHex; use super::*; fn encode(a: &mut Allocator, value: T) -> Result where - T: ToClvm, + T: ToClvm, { let actual = value.to_clvm(a)?; let actual_bytes = node_to_bytes(a, actual).unwrap(); diff --git a/crates/clvm-traits/src/wrappers.rs b/crates/clvm-traits/src/wrappers.rs index d91cb0ff7..c7c1de193 100644 --- a/crates/clvm-traits/src/wrappers.rs +++ b/crates/clvm-traits/src/wrappers.rs @@ -6,14 +6,14 @@ use crate::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmErr /// implement these traits. pub struct Raw(pub N); -impl FromClvm for Raw { - fn from_clvm(_decoder: &impl ClvmDecoder, node: N) -> Result { +impl> FromClvm for Raw { + fn from_clvm(_decoder: &D, node: N) -> Result { Ok(Self(node)) } } -impl ToClvm for Raw { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { +impl> ToClvm for Raw { + fn to_clvm(&self, encoder: &mut E) -> Result { Ok(encoder.clone_node(&self.0)) } } diff --git a/crates/clvm-utils/src/curried_program.rs b/crates/clvm-utils/src/curried_program.rs index 43b5f180b..14afcf214 100644 --- a/crates/clvm-utils/src/curried_program.rs +++ b/crates/clvm-utils/src/curried_program.rs @@ -9,24 +9,24 @@ pub struct CurriedProgram { pub args: A, } -impl FromClvm for CurriedProgram +impl, P, A> FromClvm for CurriedProgram where - P: FromClvm, - A: FromClvm, + P: FromClvm, + A: FromClvm, { - fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + fn from_clvm(decoder: &D, node: N) -> Result { let destructure_list!(_, destructure_quote!(program), args) = , match_quote!(P), A)>::from_clvm(decoder, node)?; Ok(Self { program, args }) } } -impl ToClvm for CurriedProgram +impl, P, A> ToClvm for CurriedProgram where - P: ToClvm, - A: ToClvm, + P: ToClvm, + A: ToClvm, { - fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + fn to_clvm(&self, encoder: &mut E) -> Result { clvm_list!(2, clvm_quote!(&self.program), &self.args).to_clvm(encoder) } } @@ -36,14 +36,14 @@ mod tests { use std::fmt::Debug; use clvm_traits::clvm_curried_args; - use clvmr::{serde::node_to_bytes, Allocator, NodePtr}; + use clvmr::{serde::node_to_bytes, Allocator}; use super::*; fn check(program: &P, args: &A, expected: &str) where - P: Debug + PartialEq + ToClvm + FromClvm, - A: Debug + PartialEq + ToClvm + FromClvm, + P: Debug + PartialEq + ToClvm + FromClvm, + A: Debug + PartialEq + ToClvm + FromClvm, { let a = &mut Allocator::new(); diff --git a/crates/clvm-utils/src/hash_encoder.rs b/crates/clvm-utils/src/hash_encoder.rs index edfb4e8ca..be9960545 100644 --- a/crates/clvm-utils/src/hash_encoder.rs +++ b/crates/clvm-utils/src/hash_encoder.rs @@ -11,7 +11,7 @@ pub trait ToTreeHash { impl ToTreeHash for T where - T: ToClvm, + T: ToClvm, { fn tree_hash(&self) -> TreeHash { self.to_clvm(&mut TreeHasher).unwrap() @@ -37,11 +37,8 @@ impl ClvmEncoder for TreeHasher { } } -impl ToClvm for TreeHash { - fn to_clvm( - &self, - _encoder: &mut impl ClvmEncoder, - ) -> Result { +impl ToClvm for TreeHash { + fn to_clvm(&self, _encoder: &mut TreeHasher) -> Result { Ok(*self) } } From 25117aef6c89b102bee812e4b859e9b7a2dbcb0e Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 2 Jul 2024 00:16:26 -0400 Subject: [PATCH 3/8] Fix fuzzer --- crates/chia-puzzles/fuzz/fuzz_targets/roundtrip.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/chia-puzzles/fuzz/fuzz_targets/roundtrip.rs b/crates/chia-puzzles/fuzz/fuzz_targets/roundtrip.rs index d863df15f..0f44f8f72 100644 --- a/crates/chia-puzzles/fuzz/fuzz_targets/roundtrip.rs +++ b/crates/chia-puzzles/fuzz/fuzz_targets/roundtrip.rs @@ -4,7 +4,7 @@ use std::fmt; use chia_puzzles::{nft::NftMetadata, Proof}; use clvm_traits::{FromClvm, ToClvm}; -use clvmr::{allocator::NodePtr, Allocator}; +use clvmr::Allocator; use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; use libfuzzer_sys::fuzz_target; @@ -16,7 +16,7 @@ fuzz_target!(|data: &[u8]| { fn roundtrip<'a, T>(u: &mut Unstructured<'a>) where - T: Arbitrary<'a> + ToClvm + FromClvm + PartialEq + fmt::Debug, + T: Arbitrary<'a> + ToClvm + FromClvm + PartialEq + fmt::Debug, { let obj = T::arbitrary(u).unwrap(); let mut a = Allocator::new(); From 66d691f4dce90199de42250f331894c65df0b745 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 2 Jul 2024 00:21:07 -0400 Subject: [PATCH 4/8] Remove ToNodePtr and FromNodePtr --- .../fuzz/fuzz_targets/fast-forward.rs | 6 ++-- crates/chia-consensus/src/fast_forward.rs | 8 ++--- crates/chia-protocol/src/program.rs | 33 ++++++++++--------- crates/chia-puzzles/src/puzzles/cat.rs | 8 ++--- crates/chia-puzzles/src/puzzles/standard.rs | 4 +-- .../chia-tools/src/bin/fast-forward-spend.rs | 8 ++--- crates/chia-tools/src/bin/gen-corpus.rs | 7 ++-- crates/chia-tools/src/bin/run-spend.rs | 6 ++-- crates/clvm-traits/src/clvm_decoder.rs | 18 ---------- crates/clvm-traits/src/clvm_encoder.rs | 13 -------- 10 files changed, 40 insertions(+), 71 deletions(-) diff --git a/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs b/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs index bbd34ccf7..d23ddae24 100644 --- a/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs +++ b/crates/chia-consensus/fuzz/fuzz_targets/fast-forward.rs @@ -8,7 +8,7 @@ use chia_protocol::Bytes32; use chia_protocol::Coin; use chia_protocol::CoinSpend; use chia_traits::streamable::Streamable; -use clvm_traits::ToNodePtr; +use clvm_traits::ToClvm; use clvm_utils::tree_hash; use clvmr::serde::node_to_bytes; use clvmr::{Allocator, NodePtr}; @@ -24,10 +24,10 @@ fuzz_target!(|data: &[u8]| { hex!("abababababababababababababababababababababababababababababababab"); let mut a = Allocator::new_limited(500_000_000); - let Ok(puzzle) = spend.puzzle_reveal.to_node_ptr(&mut a) else { + let Ok(puzzle) = spend.puzzle_reveal.to_clvm(&mut a) else { return; }; - let Ok(solution) = spend.solution.to_node_ptr(&mut a) else { + let Ok(solution) = spend.solution.to_clvm(&mut a) else { return; }; let puzzle_hash = Bytes32::from(tree_hash(&a, puzzle)); diff --git a/crates/chia-consensus/src/fast_forward.rs b/crates/chia-consensus/src/fast_forward.rs index 1905744e1..1c0bdb17c 100644 --- a/crates/chia-consensus/src/fast_forward.rs +++ b/crates/chia-consensus/src/fast_forward.rs @@ -160,7 +160,7 @@ mod tests { use crate::gen::run_puzzle::run_puzzle; use chia_protocol::CoinSpend; use chia_traits::streamable::Streamable; - use clvm_traits::ToNodePtr; + use clvm_traits::ToClvm; use clvmr::serde::{node_from_bytes, node_to_bytes}; use hex_literal::hex; use rstest::rstest; @@ -189,8 +189,8 @@ mod tests { let new_parents_parent = hex::decode(new_parents_parent).unwrap(); let mut a = Allocator::new_limited(500_000_000); - let puzzle = spend.puzzle_reveal.to_node_ptr(&mut a).expect("to_clvm"); - let solution = spend.solution.to_node_ptr(&mut a).expect("to_clvm"); + let puzzle = spend.puzzle_reveal.to_clvm(&mut a).expect("to_clvm"); + let solution = spend.solution.to_clvm(&mut a).expect("to_clvm"); let puzzle_hash = Bytes32::from(tree_hash(&a, puzzle)); let new_parent_coin = Coin { @@ -265,7 +265,7 @@ mod tests { &hex!("abababababababababababababababababababababababababababababababab"); let mut a = Allocator::new_limited(500_000_000); - let puzzle = spend.puzzle_reveal.to_node_ptr(&mut a).expect("to_clvm"); + let puzzle = spend.puzzle_reveal.to_clvm(&mut a).expect("to_clvm"); let puzzle_hash = Bytes32::from(tree_hash(&a, puzzle)); let mut new_parent_coin = Coin { diff --git a/crates/chia-protocol/src/program.rs b/crates/chia-protocol/src/program.rs index 0a8a3a958..d61aa4ce4 100644 --- a/crates/chia-protocol/src/program.rs +++ b/crates/chia-protocol/src/program.rs @@ -1,7 +1,7 @@ use crate::bytes::Bytes; use chia_traits::chia_error::{Error, Result}; use chia_traits::Streamable; -use clvm_traits::{FromClvmError, FromNodePtr, ToClvmError, ToNodePtr}; +use clvm_traits::{FromClvm, FromClvmError, ToClvm, ToClvmError}; use clvmr::allocator::NodePtr; use clvmr::cost::Cost; use clvmr::reduction::EvalErr; @@ -54,14 +54,14 @@ impl Program { self.0.into_inner() } - pub fn run( + pub fn run>( &self, a: &mut Allocator, flags: u32, max_cost: Cost, arg: &A, ) -> std::result::Result<(Cost, NodePtr), EvalErr> { - let arg = arg.to_node_ptr(a).map_err(|_| { + let arg = arg.to_clvm(a).map_err(|_| { EvalErr( a.nil(), "failed to convert argument to CLVM objects".to_string(), @@ -316,7 +316,7 @@ impl Program { fn py_to(args: &Bound<'_, PyAny>) -> PyResult { let mut a = Allocator::new_limited(500_000_000); let clvm = clvm_convert(&mut a, args)?; - Program::from_node_ptr(&a, clvm) + Program::from_clvm(&a, clvm) .map_err(|error| PyErr::new::(error.to_string())) } @@ -416,7 +416,7 @@ impl Program { let mut a = Allocator::new_limited(500_000_000); let prg = node_from_bytes_backrefs(&mut a, self.0.as_ref())?; - let Ok(uncurried) = CurriedProgram::::from_node_ptr(&a, prg) else { + let Ok(uncurried) = CurriedProgram::::from_clvm(&a, prg) else { let a = Rc::new(a); let prg = LazyNode::new(a.clone(), prg); let ret = a.nil(); @@ -432,11 +432,12 @@ impl Program { } // the args of curried puzzles are in the form of: // (c . ((q . ) . ( . ()))) - let (_, ((_, arg), (rest, ()))) = <( - clvm_traits::MatchByte<4>, - (clvm_traits::match_quote!(NodePtr), (NodePtr, ())), - ) as FromNodePtr>::from_node_ptr(&a, args) - .map_err(|error| PyErr::new::(error.to_string()))?; + let (_, ((_, arg), (rest, ()))) = + <( + clvm_traits::MatchByte<4>, + (clvm_traits::match_quote!(NodePtr), (NodePtr, ())), + ) as FromClvm>::from_clvm(&a, args) + .map_err(|error| PyErr::new::(error.to_string()))?; curried_args.push(arg); args = rest; } @@ -501,8 +502,8 @@ impl FromJsonDict for Program { } } -impl FromNodePtr for Program { - fn from_node_ptr(a: &Allocator, node: NodePtr) -> std::result::Result { +impl FromClvm for Program { + fn from_clvm(a: &Allocator, node: NodePtr) -> std::result::Result { Ok(Self( node_to_bytes(a, node) .map_err(|error| FromClvmError::Custom(error.to_string()))? @@ -511,8 +512,8 @@ impl FromNodePtr for Program { } } -impl ToNodePtr for Program { - fn to_node_ptr(&self, a: &mut Allocator) -> std::result::Result { +impl ToClvm for Program { + fn to_clvm(&self, a: &mut Allocator) -> std::result::Result { node_from_bytes(a, self.0.as_ref()).map_err(|error| ToClvmError::Custom(error.to_string())) } } @@ -528,9 +529,9 @@ mod tests { let expected_bytes = hex::decode(expected).unwrap(); let ptr = node_from_bytes(a, &expected_bytes).unwrap(); - let program = Program::from_node_ptr(a, ptr).unwrap(); + let program = Program::from_clvm(a, ptr).unwrap(); - let round_trip = program.to_node_ptr(a).unwrap(); + let round_trip = program.to_clvm(a).unwrap(); assert_eq!(expected, hex::encode(node_to_bytes(a, round_trip).unwrap())); } diff --git a/crates/chia-puzzles/src/puzzles/cat.rs b/crates/chia-puzzles/src/puzzles/cat.rs index 4f55b6f2a..30d2da4ba 100644 --- a/crates/chia-puzzles/src/puzzles/cat.rs +++ b/crates/chia-puzzles/src/puzzles/cat.rs @@ -260,7 +260,7 @@ pub const CAT_PUZZLE_HASH_V1: TreeHash = TreeHash::new(hex!( #[cfg(test)] mod tests { - use clvm_traits::ToNodePtr; + use clvm_traits::ToClvm; use clvm_utils::tree_hash; use clvmr::{serde::node_from_bytes, Allocator}; @@ -298,7 +298,7 @@ mod tests { }, ), } - .to_node_ptr(&mut a) + .to_clvm(&mut a) .unwrap(); let allocated_tree_hash = hex::encode(tree_hash(&a, curried_ptr)); @@ -320,7 +320,7 @@ mod tests { program: mod_ptr, args: EverythingWithSignatureTailArgs::new(public_key), } - .to_node_ptr(&mut a) + .to_clvm(&mut a) .unwrap(); let allocated_tree_hash = hex::encode(tree_hash(&a, curried_ptr)); @@ -341,7 +341,7 @@ mod tests { program: mod_ptr, args: GenesisByCoinIdTailArgs::new(genesis_coin_id), } - .to_node_ptr(&mut a) + .to_clvm(&mut a) .unwrap(); let allocated_tree_hash = hex::encode(tree_hash(&a, curried_ptr)); diff --git a/crates/chia-puzzles/src/puzzles/standard.rs b/crates/chia-puzzles/src/puzzles/standard.rs index 26af9f09c..16e2e9ebf 100644 --- a/crates/chia-puzzles/src/puzzles/standard.rs +++ b/crates/chia-puzzles/src/puzzles/standard.rs @@ -77,7 +77,7 @@ pub const DEFAULT_HIDDEN_PUZZLE_HASH: TreeHash = TreeHash::new(hex!( #[cfg(test)] mod tests { - use clvm_traits::ToNodePtr; + use clvm_traits::ToClvm; use clvm_utils::tree_hash; use clvmr::{serde::node_from_bytes, Allocator}; @@ -102,7 +102,7 @@ mod tests { program: mod_ptr, args: StandardArgs::new(synthetic_key), } - .to_node_ptr(&mut a) + .to_clvm(&mut a) .unwrap(); let allocated_tree_hash = hex::encode(tree_hash(&a, curried_ptr)); diff --git a/crates/chia-tools/src/bin/fast-forward-spend.rs b/crates/chia-tools/src/bin/fast-forward-spend.rs index f76bcbb5a..750a4cb89 100644 --- a/crates/chia-tools/src/bin/fast-forward-spend.rs +++ b/crates/chia-tools/src/bin/fast-forward-spend.rs @@ -5,7 +5,7 @@ use chia_consensus::fast_forward::fast_forward_singleton; use chia_protocol::Bytes32; use chia_protocol::{Coin, CoinSpend, Program}; use chia_traits::streamable::Streamable; -use clvm_traits::{FromNodePtr, ToNodePtr}; +use clvm_traits::{FromClvm, ToClvm}; use clvm_utils::tree_hash; use clvmr::allocator::Allocator; @@ -38,8 +38,8 @@ fn main() { .unwrap(); let mut a = Allocator::new_limited(500_000_000); - let puzzle = spend.puzzle_reveal.to_node_ptr(&mut a).expect("to_clvm"); - let solution = spend.solution.to_node_ptr(&mut a).expect("to_clvm"); + let puzzle = spend.puzzle_reveal.to_clvm(&mut a).expect("to_clvm"); + let solution = spend.solution.to_clvm(&mut a).expect("to_clvm"); let puzzle_hash = Bytes32::from(tree_hash(&a, puzzle)); let new_parent_coin = Coin { @@ -67,7 +67,7 @@ fn main() { let new_spend = CoinSpend { coin: new_parent_coin, puzzle_reveal: spend.puzzle_reveal, - solution: Program::from_node_ptr(&a, new_solution).expect("new solution"), + solution: Program::from_clvm(&a, new_solution).expect("new solution"), }; let mut bytes = Vec::::new(); new_spend.stream(&mut bytes).expect("stream CoinSpend"); diff --git a/crates/chia-tools/src/bin/gen-corpus.rs b/crates/chia-tools/src/bin/gen-corpus.rs index 7f3c53f37..4ffb58e9d 100644 --- a/crates/chia-tools/src/bin/gen-corpus.rs +++ b/crates/chia-tools/src/bin/gen-corpus.rs @@ -11,7 +11,7 @@ use chia_traits::streamable::Streamable; use chia_bls::G2Element; use chia_protocol::{Bytes32, Coin, CoinSpend, Program, SpendBundle}; use chia_puzzles::singleton::SINGLETON_TOP_LAYER_PUZZLE_HASH; -use clvm_traits::{FromClvm, FromNodePtr}; +use clvm_traits::FromClvm; use clvm_utils::{tree_hash, CurriedProgram}; use clvmr::allocator::NodePtr; use clvmr::Allocator; @@ -100,9 +100,8 @@ fn main() { if !run_puzzle && !fast_forward && !args.spend_bundles { return; } - let puzzle_reveal = - Program::from_node_ptr(a, puzzle).expect("puzzle reveal"); - let solution = Program::from_node_ptr(a, solution).expect("solution"); + let puzzle_reveal = Program::from_clvm(a, puzzle).expect("puzzle reveal"); + let solution = Program::from_clvm(a, solution).expect("solution"); let coin = Coin { parent_coin_info, puzzle_hash, diff --git a/crates/chia-tools/src/bin/run-spend.rs b/crates/chia-tools/src/bin/run-spend.rs index eb657875a..0cdf60a9f 100644 --- a/crates/chia-tools/src/bin/run-spend.rs +++ b/crates/chia-tools/src/bin/run-spend.rs @@ -2,7 +2,7 @@ use chia_consensus::gen::conditions::Condition; use chia_puzzles::Proof; use chia_traits::Streamable; use clap::Parser; -use clvm_traits::{FromClvm, ToNodePtr}; +use clvm_traits::{FromClvm, ToClvm}; use clvm_utils::tree_hash; use clvm_utils::CurriedProgram; use clvmr::{allocator::NodePtr, Allocator}; @@ -275,11 +275,11 @@ fn main() { let puzzle = spend .puzzle_reveal - .to_node_ptr(&mut a) + .to_clvm(&mut a) .expect("deserialize puzzle"); let solution = spend .solution - .to_node_ptr(&mut a) + .to_clvm(&mut a) .expect("deserialize solution"); println!("Spending {:?}", &spend.coin); diff --git a/crates/clvm-traits/src/clvm_decoder.rs b/crates/clvm-traits/src/clvm_decoder.rs index cf7261590..e66ce176a 100644 --- a/crates/clvm-traits/src/clvm_decoder.rs +++ b/crates/clvm-traits/src/clvm_decoder.rs @@ -51,24 +51,6 @@ impl ClvmDecoder for Allocator { } } -pub trait FromNodePtr { - fn from_node_ptr(a: &Allocator, node: NodePtr) -> Result - where - Self: Sized; -} - -impl FromNodePtr for T -where - T: FromClvm, -{ - fn from_node_ptr(a: &Allocator, node: NodePtr) -> Result - where - Self: Sized, - { - T::from_clvm(a, node) - } -} - impl FromClvm for NodePtr { fn from_clvm(_decoder: &Allocator, node: NodePtr) -> Result { Ok(node) diff --git a/crates/clvm-traits/src/clvm_encoder.rs b/crates/clvm-traits/src/clvm_encoder.rs index c4c89424e..c5ea14b62 100644 --- a/crates/clvm-traits/src/clvm_encoder.rs +++ b/crates/clvm-traits/src/clvm_encoder.rs @@ -69,19 +69,6 @@ impl ClvmEncoder for Allocator { } } -pub trait ToNodePtr { - fn to_node_ptr(&self, a: &mut Allocator) -> Result; -} - -impl ToNodePtr for T -where - T: ToClvm, -{ - fn to_node_ptr(&self, a: &mut Allocator) -> Result { - self.to_clvm(a) - } -} - impl ToClvm for NodePtr { fn to_clvm(&self, _encoder: &mut Allocator) -> Result { Ok(*self) From 1f3945c1d97753ba1400cc7e5e5ec46d7a73b0d9 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Tue, 2 Jul 2024 00:49:45 -0400 Subject: [PATCH 5/8] Remove unimplemented pretty printer --- crates/clvm-utils/src/lib.rs | 2 -- crates/clvm-utils/src/prettier_printer.rs | 1 - 2 files changed, 3 deletions(-) delete mode 100644 crates/clvm-utils/src/prettier_printer.rs diff --git a/crates/clvm-utils/src/lib.rs b/crates/clvm-utils/src/lib.rs index 09a54a70e..53c8688e4 100644 --- a/crates/clvm-utils/src/lib.rs +++ b/crates/clvm-utils/src/lib.rs @@ -27,11 +27,9 @@ mod curried_program; mod curry_tree_hash; mod hash_encoder; -mod prettier_printer; mod tree_hash; pub use curried_program::*; pub use curry_tree_hash::*; pub use hash_encoder::*; -pub use prettier_printer::*; pub use tree_hash::*; diff --git a/crates/clvm-utils/src/prettier_printer.rs b/crates/clvm-utils/src/prettier_printer.rs deleted file mode 100644 index 3fcf08777..000000000 --- a/crates/clvm-utils/src/prettier_printer.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct PrettierPrinter {} From d478acc97b507fe4cb4f43d56adf923bcf77b8ec Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 14 Jul 2024 23:17:27 -0400 Subject: [PATCH 6/8] Test MatchByte round trip for each byte value and fix bug --- crates/clvm-traits/src/match_byte.rs | 53 ++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/crates/clvm-traits/src/match_byte.rs b/crates/clvm-traits/src/match_byte.rs index a6816aa5f..9cd01a0a6 100644 --- a/crates/clvm-traits/src/match_byte.rs +++ b/crates/clvm-traits/src/match_byte.rs @@ -1,4 +1,4 @@ -use num_bigint::BigInt; +use clvmr::Atom; use crate::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; @@ -7,7 +7,13 @@ pub struct MatchByte; impl, const BYTE: u8> ToClvm for MatchByte { fn to_clvm(&self, encoder: &mut E) -> Result { - encoder.encode_bigint(BigInt::from(BYTE)) + encoder.encode_atom(Atom::Borrowed(if BYTE == 0 { + &[] + } else if BYTE < 0x80 { + &[BYTE] + } else { + &[0, BYTE] + })) } } @@ -15,10 +21,51 @@ impl, const BYTE: u8> FromClvm for MatchByte Result { match decoder.decode_atom(&node)?.as_ref() { [] if BYTE == 0 => Ok(Self), - [byte] if *byte == BYTE && BYTE > 0 => Ok(Self), + [byte] if *byte == BYTE && BYTE > 0 && BYTE < 0x80 => Ok(Self), + [0, byte] if *byte == BYTE && BYTE >= 0x80 => Ok(Self), _ => Err(FromClvmError::Custom(format!( "expected an atom with a single byte value of {BYTE}" ))), } } } + +#[cfg(test)] +mod tests { + use clvmr::Allocator; + + use super::*; + + fn test_byte() { + let mut allocator = Allocator::new(); + let match_byte = MatchByte::; + let encoded = match_byte.to_clvm(&mut allocator).unwrap(); + MatchByte::::from_clvm(&allocator, encoded).unwrap(); + } + + macro_rules! test_bytes { + ( $( $byte:expr ),* ) => { + $( test_byte::<$byte>(); )* + }; + } + + #[test] + fn test_bytes() { + test_bytes!( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + ); + } +} From 38ed939af68da7be55ea9d9b0f36dd21c77d66d0 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 28 Jul 2024 14:31:40 -0500 Subject: [PATCH 7/8] Implement int encoding directly --- Cargo.lock | 9 ++ crates/clvm-traits/fuzz/.gitignore | 4 + crates/clvm-traits/fuzz/Cargo.toml | 20 ++++ .../fuzz/fuzz_targets/int_encoding.rs | 40 ++++++++ crates/clvm-traits/src/clvm_decoder.rs | 14 +++ crates/clvm-traits/src/from_clvm.rs | 65 ++++++------ crates/clvm-traits/src/int_encoding.rs | 98 +++++++++++++++++++ crates/clvm-traits/src/lib.rs | 2 + crates/clvm-traits/src/to_clvm.rs | 12 ++- 9 files changed, 227 insertions(+), 37 deletions(-) create mode 100644 crates/clvm-traits/fuzz/.gitignore create mode 100644 crates/clvm-traits/fuzz/Cargo.toml create mode 100644 crates/clvm-traits/fuzz/fuzz_targets/int_encoding.rs create mode 100644 crates/clvm-traits/src/int_encoding.rs diff --git a/Cargo.lock b/Cargo.lock index 6ea048eea..394572eb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -649,6 +649,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "clvm-traits-fuzz" +version = "0.0.0" +dependencies = [ + "clvm-traits", + "clvmr", + "libfuzzer-sys", +] + [[package]] name = "clvm-utils" version = "0.10.0" diff --git a/crates/clvm-traits/fuzz/.gitignore b/crates/clvm-traits/fuzz/.gitignore new file mode 100644 index 000000000..1a45eee77 --- /dev/null +++ b/crates/clvm-traits/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/crates/clvm-traits/fuzz/Cargo.toml b/crates/clvm-traits/fuzz/Cargo.toml new file mode 100644 index 000000000..a94af67c1 --- /dev/null +++ b/crates/clvm-traits/fuzz/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "clvm-traits-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = { workspace = true } +clvmr = { workspace = true } +clvm-traits = { workspace = true } + +[[bin]] +name = "int_encoding" +path = "fuzz_targets/int_encoding.rs" +test = false +doc = false +bench = false diff --git a/crates/clvm-traits/fuzz/fuzz_targets/int_encoding.rs b/crates/clvm-traits/fuzz/fuzz_targets/int_encoding.rs new file mode 100644 index 000000000..5e8e981ef --- /dev/null +++ b/crates/clvm-traits/fuzz/fuzz_targets/int_encoding.rs @@ -0,0 +1,40 @@ +#![no_main] + +use clvm_traits::{decode_number, encode_number}; +use clvmr::Allocator; +use libfuzzer_sys::{arbitrary::Unstructured, fuzz_target}; + +fuzz_target!(|data: &[u8]| { + let mut unstructured = Unstructured::new(data); + + macro_rules! impl_num { + ( $num_type:ty, $signed:expr ) => { + let num: $num_type = unstructured.arbitrary().unwrap(); + let mut allocator = Allocator::new(); + let ptr = allocator.new_number(num.into()).unwrap(); + let atom = allocator.atom(ptr); + let expected = atom.as_ref(); + + #[allow(unused_comparisons)] + let encoded = encode_number(&num.to_be_bytes(), num < 0); + assert_eq!(expected, encoded); + + let expected = num.to_be_bytes(); + let decoded = decode_number(&encoded, $signed).unwrap(); + assert_eq!(expected, decoded); + }; + } + + impl_num!(u8, false); + impl_num!(i8, true); + impl_num!(u16, false); + impl_num!(i16, true); + impl_num!(u32, false); + impl_num!(i32, true); + impl_num!(u64, false); + impl_num!(i64, true); + impl_num!(u128, false); + impl_num!(i128, true); + impl_num!(usize, false); + impl_num!(isize, true); +}); diff --git a/crates/clvm-traits/src/clvm_decoder.rs b/crates/clvm-traits/src/clvm_decoder.rs index e66ce176a..490fe1a5a 100644 --- a/crates/clvm-traits/src/clvm_decoder.rs +++ b/crates/clvm-traits/src/clvm_decoder.rs @@ -1,4 +1,5 @@ use clvmr::{allocator::SExp, Allocator, Atom, NodePtr}; +use num_bigint::BigInt; use crate::{ destructure_list, destructure_quote, match_list, match_quote, FromClvm, FromClvmError, @@ -11,6 +12,11 @@ pub trait ClvmDecoder: Sized { fn decode_atom(&self, node: &Self::Node) -> Result, FromClvmError>; fn decode_pair(&self, node: &Self::Node) -> Result<(Self::Node, Self::Node), FromClvmError>; + fn decode_bigint(&self, node: &Self::Node) -> Result { + let atom = self.decode_atom(node)?; + Ok(BigInt::from_signed_bytes_be(atom.as_ref())) + } + fn decode_curried_arg( &self, node: &Self::Node, @@ -49,6 +55,14 @@ impl ClvmDecoder for Allocator { Err(FromClvmError::ExpectedPair) } } + + fn decode_bigint(&self, node: &Self::Node) -> Result { + if let SExp::Atom = self.sexp(*node) { + Ok(self.number(*node)) + } else { + Err(FromClvmError::ExpectedAtom) + } + } } impl FromClvm for NodePtr { diff --git a/crates/clvm-traits/src/from_clvm.rs b/crates/clvm-traits/src/from_clvm.rs index d5a8d2a3e..897ffe8e9 100644 --- a/crates/clvm-traits/src/from_clvm.rs +++ b/crates/clvm-traits/src/from_clvm.rs @@ -1,8 +1,8 @@ use std::{rc::Rc, sync::Arc}; -use num_bigint::{BigInt, Sign}; +use num_bigint::BigInt; -use crate::{ClvmDecoder, FromClvmError}; +use crate::{decode_number, ClvmDecoder, FromClvmError}; pub trait FromClvm: Sized where @@ -12,50 +12,45 @@ where } macro_rules! clvm_primitive { - ($primitive:ty) => { + ($primitive:ty, $signed:expr) => { impl> FromClvm for $primitive { fn from_clvm(decoder: &D, node: N) -> Result { const LEN: usize = std::mem::size_of::<$primitive>(); - let bytes = decoder.decode_atom(&node)?; - let number = BigInt::from_signed_bytes_be(bytes.as_ref()); - let (sign, mut vec) = number.to_bytes_be(); - - if vec.len() < std::mem::size_of::<$primitive>() { - let mut zeros = vec![0; LEN - vec.len()]; - zeros.extend(vec); - vec = zeros; - } + let atom = decoder.decode_atom(&node)?; + let slice = atom.as_ref(); - let value = <$primitive>::from_be_bytes(vec.as_slice().try_into().or(Err( - FromClvmError::WrongAtomLength { + let Some(bytes) = decode_number(slice, $signed) else { + return Err(FromClvmError::WrongAtomLength { expected: LEN, - found: bytes.as_ref().len(), - }, - ))?); - - Ok(if sign == Sign::Minus { - value.wrapping_neg() - } else { - value - }) + found: slice.len(), + }); + }; + + Ok(<$primitive>::from_be_bytes(bytes)) } } }; } -clvm_primitive!(u8); -clvm_primitive!(i8); -clvm_primitive!(u16); -clvm_primitive!(i16); -clvm_primitive!(u32); -clvm_primitive!(i32); -clvm_primitive!(u64); -clvm_primitive!(i64); -clvm_primitive!(u128); -clvm_primitive!(i128); -clvm_primitive!(usize); -clvm_primitive!(isize); +clvm_primitive!(u8, false); +clvm_primitive!(i8, true); +clvm_primitive!(u16, false); +clvm_primitive!(i16, true); +clvm_primitive!(u32, false); +clvm_primitive!(i32, true); +clvm_primitive!(u64, false); +clvm_primitive!(i64, true); +clvm_primitive!(u128, false); +clvm_primitive!(i128, true); +clvm_primitive!(usize, false); +clvm_primitive!(isize, true); + +impl> FromClvm for BigInt { + fn from_clvm(decoder: &D, node: N) -> Result { + decoder.decode_bigint(&node) + } +} impl> FromClvm for bool { fn from_clvm(decoder: &D, node: N) -> Result { diff --git a/crates/clvm-traits/src/int_encoding.rs b/crates/clvm-traits/src/int_encoding.rs new file mode 100644 index 000000000..03fb3d8aa --- /dev/null +++ b/crates/clvm-traits/src/int_encoding.rs @@ -0,0 +1,98 @@ +pub fn encode_number(slice: &[u8], negative: bool) -> Vec { + let mut start = 0; + let pad_byte = if negative { 0xFF } else { 0x00 }; + + // Skip leading pad bytes + while start < slice.len() && slice[start] == pad_byte { + start += 1; + } + + let needs_padding = if negative { + start == slice.len() || (slice[start] & 0x80) == 0 + } else { + start < slice.len() && (slice[start] & 0x80) != 0 + }; + + let mut result = Vec::with_capacity(if needs_padding { + slice.len() - start + 1 + } else { + slice.len() - start + }); + + if needs_padding { + result.push(pad_byte); + } + + result.extend_from_slice(&slice[start..]); + result +} + +pub fn decode_number(mut slice: &[u8], signed: bool) -> Option<[u8; LEN]> { + let negative = signed && !slice.is_empty() && slice[0] & 0x80 != 0; + let padding_byte = if negative { 0xFF } else { 0x00 }; + + if slice.len() > LEN && slice[0] == padding_byte { + slice = &slice[slice.len() - LEN..]; + } + + if slice.len() > LEN { + return None; + } + + assert!(slice.len() <= LEN); + + let mut result = [padding_byte; LEN]; + let start = LEN - slice.len(); + + result[start..].copy_from_slice(slice); + + Some(result) +} + +#[cfg(test)] +mod tests { + use super::*; + + use clvmr::Allocator; + + macro_rules! test_roundtrip { + ( $num:expr, $signed:expr ) => { + let mut allocator = Allocator::new(); + let ptr = allocator.new_number($num.into()).unwrap(); + let atom = allocator.atom(ptr); + let expected = atom.as_ref(); + + #[allow(unused_comparisons)] + let encoded = encode_number(&$num.to_be_bytes(), $num < 0); + assert_eq!(expected, encoded); + + let expected = $num.to_be_bytes(); + let decoded = decode_number(&encoded, $signed).unwrap(); + assert_eq!(expected, decoded); + }; + } + + #[test] + fn test_signed_encoding() { + test_roundtrip!(0i32, true); + test_roundtrip!(1i32, true); + test_roundtrip!(2i32, true); + test_roundtrip!(3i32, true); + test_roundtrip!(255i32, true); + test_roundtrip!(4716i32, true); + test_roundtrip!(-255i32, true); + test_roundtrip!(-10i32, true); + test_roundtrip!(i32::MIN, true); + test_roundtrip!(i32::MAX, true); + } + + #[test] + fn test_unsigned_encoding() { + test_roundtrip!(0u32, false); + test_roundtrip!(1u32, false); + test_roundtrip!(2u32, false); + test_roundtrip!(3u32, false); + test_roundtrip!(255u32, false); + test_roundtrip!(u32::MAX, false); + } +} diff --git a/crates/clvm-traits/src/lib.rs b/crates/clvm-traits/src/lib.rs index 408cd0267..823a3762b 100644 --- a/crates/clvm-traits/src/lib.rs +++ b/crates/clvm-traits/src/lib.rs @@ -13,6 +13,7 @@ mod clvm_decoder; mod clvm_encoder; mod error; mod from_clvm; +mod int_encoding; mod macros; mod match_byte; mod to_clvm; @@ -22,6 +23,7 @@ pub use clvm_decoder::*; pub use clvm_encoder::*; pub use error::*; pub use from_clvm::*; +pub use int_encoding::*; pub use match_byte::*; pub use to_clvm::*; pub use wrappers::*; diff --git a/crates/clvm-traits/src/to_clvm.rs b/crates/clvm-traits/src/to_clvm.rs index 963d20f2c..c2672b0f0 100644 --- a/crates/clvm-traits/src/to_clvm.rs +++ b/crates/clvm-traits/src/to_clvm.rs @@ -3,7 +3,7 @@ use std::{rc::Rc, sync::Arc}; use clvmr::Atom; use num_bigint::BigInt; -use crate::{ClvmEncoder, ToClvmError}; +use crate::{encode_number, ClvmEncoder, ToClvmError}; pub trait ToClvm where @@ -16,7 +16,9 @@ macro_rules! clvm_primitive { ($primitive:ty) => { impl> ToClvm for $primitive { fn to_clvm(&self, encoder: &mut E) -> Result { - encoder.encode_bigint(BigInt::from(*self)) + let bytes = self.to_be_bytes(); + #[allow(unused_comparisons)] + encoder.encode_atom(Atom::Borrowed(&encode_number(&bytes, *self < 0))) } } }; @@ -35,6 +37,12 @@ clvm_primitive!(i128); clvm_primitive!(usize); clvm_primitive!(isize); +impl> ToClvm for BigInt { + fn to_clvm(&self, encoder: &mut E) -> Result<::Node, ToClvmError> { + encoder.encode_bigint(self.clone()) + } +} + impl> ToClvm for bool { fn to_clvm(&self, encoder: &mut E) -> Result { i32::from(*self).to_clvm(encoder) From b3d4192e154ff66b5eda39e95d11c27595c5c0af Mon Sep 17 00:00:00 2001 From: Rigidity Date: Sun, 28 Jul 2024 14:34:52 -0500 Subject: [PATCH 8/8] Add fuzzing for clvm-traits --- .github/workflows/build-test.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 006c20dfb..091d6e147 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -344,6 +344,11 @@ jobs: cd crates/chia-puzzles cargo +nightly fuzz build cargo fuzz list | xargs -I "%" sh -c "cargo +nightly fuzz run % -- -max_total_time=20 || exit 255" + - name: cargo fuzz (clvm-traits) + run: | + cd crates/clvm-traits + cargo +nightly fuzz build + cargo fuzz list | xargs -I "%" sh -c "cargo +nightly fuzz run % -- -max_total_time=20 || exit 255" unit_tests: runs-on: ${{ matrix.os }}