diff --git a/chia-bls/src/public_key.rs b/chia-bls/src/public_key.rs index 33a376ad1..e4d403a98 100644 --- a/chia-bls/src/public_key.rs +++ b/chia-bls/src/public_key.rs @@ -2,8 +2,7 @@ use crate::secret_key::is_all_zero; use crate::{DerivableKey, Error, Result}; use blst::*; use chia_traits::{read_bytes, Streamable}; -use clvm_traits::{FromClvm, ToClvm}; -use clvmr::allocator::{Allocator, NodePtr, SExp}; +use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; use sha2::{digest::FixedOutput, Digest, Sha256}; use std::fmt; use std::hash::{Hash, Hasher}; @@ -385,25 +384,27 @@ impl DerivableKey for PublicKey { } } -impl FromClvm for PublicKey { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { - let blob = match a.sexp(ptr) { - SExp::Atom => a.atom(ptr), - _ => { - return Err(clvm_traits::Error::ExpectedAtom(ptr)); - } - }; - Self::from_bytes( - blob.try_into() - .map_err(|_error| clvm_traits::Error::Custom("invalid size".to_string()))?, - ) - .map_err(|error| clvm_traits::Error::Custom(error.to_string())) +impl FromClvm for PublicKey { + fn from_clvm( + decoder: &impl ClvmDecoder, + node: N, + ) -> std::result::Result { + let bytes = decoder.decode_atom(&node)?; + let error = Err(FromClvmError::WrongAtomLength { + expected: 48, + found: bytes.len(), + }); + let bytes = bytes.try_into().or(error)?; + Self::from_bytes(bytes).map_err(|error| FromClvmError::Custom(error.to_string())) } } -impl ToClvm for PublicKey { - fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { - Ok(a.new_atom(&self.to_bytes())?) +impl ToClvm for PublicKey { + fn to_clvm( + &self, + encoder: &mut impl ClvmEncoder, + ) -> std::result::Result { + encoder.encode_atom(&self.to_bytes()) } } @@ -434,6 +435,7 @@ pub fn hash_to_g1_with_dst(msg: &[u8], dst: &[u8]) -> PublicKey { mod tests { use super::*; use crate::SecretKey; + use clvmr::Allocator; use hex::FromHex; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; @@ -622,7 +624,7 @@ mod tests { let ptr = a.new_pair(a.one(), a.one()).expect("new_pair"); assert_eq!( PublicKey::from_clvm(&a, ptr).unwrap_err(), - clvm_traits::Error::ExpectedAtom(ptr) + FromClvmError::ExpectedAtom ); } diff --git a/chia-bls/src/signature.rs b/chia-bls/src/signature.rs index 4e3c77574..4c0a840ff 100644 --- a/chia-bls/src/signature.rs +++ b/chia-bls/src/signature.rs @@ -1,8 +1,7 @@ use crate::{Error, GTElement, PublicKey, Result, SecretKey}; use blst::*; use chia_traits::{read_bytes, Streamable}; -use clvm_traits::{FromClvm, ToClvm}; -use clvmr::allocator::{Allocator, NodePtr, SExp}; +use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; use sha2::{Digest, Sha256}; use std::borrow::Borrow; use std::convert::AsRef; @@ -248,25 +247,27 @@ impl FromJsonDict for Signature { } } -impl FromClvm for Signature { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { - let blob = match a.sexp(ptr) { - SExp::Atom => a.atom(ptr), - _ => { - return Err(clvm_traits::Error::ExpectedAtom(ptr)); - } - }; - Self::from_bytes( - blob.try_into() - .map_err(|_error| clvm_traits::Error::Custom("invalid size".to_string()))?, - ) - .map_err(|error| clvm_traits::Error::Custom(error.to_string())) +impl FromClvm for Signature { + fn from_clvm( + decoder: &impl ClvmDecoder, + node: N, + ) -> std::result::Result { + let bytes = decoder.decode_atom(&node)?; + let error = Err(FromClvmError::WrongAtomLength { + expected: 96, + found: bytes.len(), + }); + let bytes = bytes.try_into().or(error)?; + Self::from_bytes(bytes).map_err(|error| FromClvmError::Custom(error.to_string())) } } -impl ToClvm for Signature { - fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { - Ok(a.new_atom(&self.to_bytes())?) +impl ToClvm for Signature { + fn to_clvm( + &self, + encoder: &mut impl ClvmEncoder, + ) -> std::result::Result { + encoder.encode_atom(&self.to_bytes()) } } @@ -529,6 +530,7 @@ pub fn sign>(sk: &SecretKey, msg: Msg) -> Signature { #[cfg(test)] mod tests { use super::*; + use clvmr::Allocator; use hex::FromHex; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; @@ -1077,7 +1079,7 @@ mod tests { let ptr = a.new_pair(a.one(), a.one()).expect("new_pair"); assert_eq!( Signature::from_clvm(&a, ptr).unwrap_err(), - clvm_traits::Error::ExpectedAtom(ptr) + FromClvmError::ExpectedAtom ); } diff --git a/chia-protocol/src/bytes.rs b/chia-protocol/src/bytes.rs index 01f6d57bb..c8f15110f 100644 --- a/chia-protocol/src/bytes.rs +++ b/chia-protocol/src/bytes.rs @@ -1,8 +1,6 @@ use chia_traits::chia_error; use chia_traits::{read_bytes, Streamable}; -use clvm_traits::{FromClvm, ToClvm}; -use clvmr::allocator::{NodePtr, SExp}; -use clvmr::Allocator; +use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; use core::fmt::Formatter; use sha2::{Digest, Sha256}; use std::convert::AsRef; @@ -95,19 +93,16 @@ impl FromJsonDict for Bytes { } } -impl ToClvm for Bytes { - fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { - Ok(a.new_atom(self.0.as_slice())?) +impl ToClvm for Bytes { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + encoder.encode_atom(self.0.as_slice()) } } -impl FromClvm for Bytes { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { - if let SExp::Atom = a.sexp(ptr) { - Ok(Self(a.atom(ptr).to_vec())) - } else { - Err(clvm_traits::Error::ExpectedAtom(ptr)) - } +impl FromClvm for Bytes { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let bytes = decoder.decode_atom(&node)?; + Ok(Self(bytes.to_vec())) } } @@ -201,26 +196,22 @@ impl Streamable for BytesImpl { } } -impl ToClvm for BytesImpl { - fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { - Ok(a.new_atom(self.0.as_slice())?) +impl ToClvm for BytesImpl { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + encoder.encode_atom(self.0.as_slice()) } } -impl FromClvm for BytesImpl { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { - let blob = match a.sexp(ptr) { - SExp::Atom => { - if a.atom_len(ptr) != N { - return Err(clvm_traits::Error::Custom("invalid size".to_string())); - } - a.atom(ptr) - } - _ => { - return Err(clvm_traits::Error::ExpectedAtom(ptr)); - } - }; - Ok(Self::from(blob)) +impl FromClvm for BytesImpl { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let bytes = decoder.decode_atom(&node)?; + if bytes.len() != LEN { + return Err(FromClvmError::WrongAtomLength { + expected: LEN, + found: bytes.len(), + }); + } + Ok(Self::from(bytes)) } } @@ -425,7 +416,10 @@ impl<'py> FromPyObject<'py> for Bytes { mod tests { use super::*; - use clvmr::serde::{node_from_bytes, node_to_bytes}; + use clvmr::{ + serde::{node_from_bytes, node_to_bytes}, + Allocator, + }; use rstest::rstest; #[rstest] @@ -690,15 +684,12 @@ mod tests { let bytes = hex::decode("f07522495060c066f66f32acc2a77e3a3e737aca8baea4d1a64ea4cdc13da9").unwrap(); let ptr = a.new_atom(&bytes).unwrap(); - assert_eq!( - Bytes32::from_clvm(a, ptr).unwrap_err(), - clvm_traits::Error::Custom("invalid size".to_string()) - ); + assert!(Bytes32::from_clvm(a, ptr).is_err()); let ptr = a.new_pair(a.one(), a.one()).unwrap(); assert_eq!( Bytes32::from_clvm(a, ptr).unwrap_err(), - clvm_traits::Error::ExpectedAtom(ptr) + FromClvmError::ExpectedAtom ); } } diff --git a/chia-protocol/src/coin.rs b/chia-protocol/src/coin.rs index e66551172..2e72c4219 100644 --- a/chia-protocol/src/coin.rs +++ b/chia-protocol/src/coin.rs @@ -1,9 +1,10 @@ use crate::streamable_struct; use crate::{bytes::Bytes32, BytesImpl}; use chia_streamable_macro::Streamable; -use clvm_traits::{clvm_list, destructure_list, match_list, FromClvm, ToClvm}; -use clvmr::allocator::NodePtr; -use clvmr::Allocator; +use clvm_traits::{ + clvm_list, destructure_list, match_list, ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, + ToClvm, ToClvmError, +}; use sha2::{Digest, Sha256}; use std::convert::TryInto; @@ -53,16 +54,16 @@ impl Coin { } } -impl ToClvm for Coin { - fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { - clvm_list!(self.parent_coin_info, self.puzzle_hash, self.amount).to_clvm(a) +impl ToClvm for Coin { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + clvm_list!(self.parent_coin_info, self.puzzle_hash, self.amount).to_clvm(encoder) } } -impl FromClvm for Coin { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { +impl FromClvm for Coin { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { let destructure_list!(parent_coin_info, puzzle_hash, amount) = - , BytesImpl<32>, u64)>::from_clvm(a, ptr)?; + , BytesImpl<32>, u64)>::from_clvm(decoder, node)?; Ok(Coin { parent_coin_info, puzzle_hash, @@ -74,7 +75,10 @@ impl FromClvm for Coin { #[cfg(test)] mod tests { use super::*; - use clvmr::serde::{node_from_bytes, node_to_bytes}; + use clvmr::{ + serde::{node_from_bytes, node_to_bytes}, + Allocator, + }; use rstest::rstest; #[rstest] diff --git a/chia-protocol/src/program.rs b/chia-protocol/src/program.rs index 10bff1f1b..6805c2557 100644 --- a/chia-protocol/src/program.rs +++ b/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::{FromClvm, ToClvm}; +use clvm_traits::{FromClvmError, FromNodePtr, ToClvmError, ToNodePtr}; use clvmr::allocator::NodePtr; use clvmr::serde::{node_from_bytes, node_to_bytes, serialized_length_from_bytes}; use clvmr::Allocator; @@ -105,20 +105,19 @@ impl FromJsonDict for Program { } } -impl FromClvm for Program { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> clvm_traits::Result { +impl FromNodePtr for Program { + fn from_node_ptr(a: &Allocator, node: NodePtr) -> std::result::Result { Ok(Self( - node_to_bytes(a, ptr) - .map_err(|error| clvm_traits::Error::Custom(error.to_string()))? + node_to_bytes(a, node) + .map_err(|error| FromClvmError::Custom(error.to_string()))? .into(), )) } } -impl ToClvm for Program { - fn to_clvm(&self, a: &mut Allocator) -> clvm_traits::Result { - node_from_bytes(a, self.0.as_ref()) - .map_err(|error| clvm_traits::Error::Custom(error.to_string())) +impl ToNodePtr for Program { + fn to_node_ptr(&self, a: &mut Allocator) -> std::result::Result { + node_from_bytes(a, self.0.as_ref()).map_err(|error| ToClvmError::Custom(error.to_string())) } } @@ -139,9 +138,9 @@ mod tests { let expected_bytes = hex::decode(expected).unwrap(); let ptr = node_from_bytes(a, &expected_bytes).unwrap(); - let program = Program::from_clvm(a, ptr).unwrap(); + let program = Program::from_node_ptr(a, ptr).unwrap(); - let round_trip = program.to_clvm(a).unwrap(); + let round_trip = program.to_node_ptr(a).unwrap(); assert_eq!(expected, hex::encode(node_to_bytes(a, round_trip).unwrap())); } } diff --git a/chia-tools/src/bin/fast-forward-spend.rs b/chia-tools/src/bin/fast-forward-spend.rs index 3484bbeed..d9bb3ad83 100644 --- a/chia-tools/src/bin/fast-forward-spend.rs +++ b/chia-tools/src/bin/fast-forward-spend.rs @@ -6,7 +6,7 @@ use chia::fast_forward::fast_forward_singleton; use chia_protocol::bytes::Bytes32; use chia_protocol::{coin::Coin, coin_spend::CoinSpend, program::Program}; use chia_traits::streamable::Streamable; -use clvm_traits::{FromClvm, ToClvm}; +use clvm_traits::{FromNodePtr, ToNodePtr}; use clvm_utils::tree_hash; use clvmr::allocator::Allocator; @@ -39,8 +39,8 @@ fn main() { .into(); let mut a = Allocator::new_limited(500000000, 62500000, 62500000); - 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 = 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_hash = Bytes32::from(tree_hash(&a, puzzle)); let new_parent_coin = Coin { @@ -68,7 +68,7 @@ fn main() { let new_spend = CoinSpend { coin: new_parent_coin, puzzle_reveal: spend.puzzle_reveal, - solution: Program::from_clvm(&a, new_solution).expect("new solution"), + solution: Program::from_node_ptr(&a, new_solution).expect("new solution"), }; let mut bytes = Vec::::new(); new_spend.stream(&mut bytes).expect("stream CoinSpend"); diff --git a/chia-tools/src/bin/gen-corpus.rs b/chia-tools/src/bin/gen-corpus.rs index 906dbf0e8..657187d1d 100644 --- a/chia-tools/src/bin/gen-corpus.rs +++ b/chia-tools/src/bin/gen-corpus.rs @@ -11,7 +11,7 @@ use chia_traits::streamable::Streamable; use chia_protocol::bytes::Bytes32; use chia_protocol::{coin::Coin, coin_spend::CoinSpend, program::Program}; use chia_wallet::singleton::SINGLETON_TOP_LAYER_PUZZLE_HASH; -use clvm_traits::FromClvm; +use clvm_traits::{FromClvm, FromNodePtr}; use clvm_utils::{tree_hash, CurriedProgram}; use clvmr::allocator::NodePtr; use clvmr::Allocator; @@ -74,10 +74,11 @@ fn main() { max_cost, |a, parent_coin_info, amount, puzzle, solution| { let puzzle_hash = Bytes32::from(tree_hash(a, puzzle)); - let mod_hash = match CurriedProgram::::from_clvm(a, puzzle) { - Ok(uncurried) => Bytes32::from(tree_hash(a, uncurried.program)), - _ => puzzle_hash, - }; + let mod_hash = + match CurriedProgram::::from_clvm(a, puzzle) { + Ok(uncurried) => Bytes32::from(tree_hash(a, uncurried.program)), + _ => puzzle_hash, + }; let run_puzzle = seen_puzzles.lock().unwrap().insert(mod_hash); let fast_forward = (mod_hash == SINGLETON_TOP_LAYER_PUZZLE_HASH) @@ -88,8 +89,9 @@ fn main() { } use std::fs::write; - let puzzle_reveal = Program::from_clvm(a, puzzle).expect("puzzle reveal"); - let solution = Program::from_clvm(a, solution).expect("solution"); + let puzzle_reveal = + Program::from_node_ptr(a, puzzle).expect("puzzle reveal"); + let solution = Program::from_node_ptr(a, solution).expect("solution"); let coin = Coin { parent_coin_info, puzzle_hash, diff --git a/chia-tools/src/bin/run-spend.rs b/chia-tools/src/bin/run-spend.rs index a386380c0..80f039c43 100644 --- a/chia-tools/src/bin/run-spend.rs +++ b/chia-tools/src/bin/run-spend.rs @@ -2,6 +2,7 @@ use chia::gen::conditions::Condition; use chia_protocol::Bytes32; use chia_traits::Streamable; use clap::Parser; +use clvm_traits::ToNodePtr; use clvm_traits::{FromClvm, ToClvm}; use clvm_utils::tree_hash; use clvm_utils::CurriedProgram; @@ -131,9 +132,9 @@ pub struct SingletonStruct { #[derive(FromClvm, ToClvm, Debug)] #[clvm(curry)] -pub struct SingletonArgs { +pub struct SingletonArgs { pub singleton_struct: SingletonStruct, - pub inner_puzzle: NodePtr, + pub inner_puzzle: I, } #[derive(FromClvm, ToClvm, Debug)] @@ -153,24 +154,24 @@ pub struct EveProof { #[derive(FromClvm, ToClvm, Debug)] #[clvm(list)] -pub struct SingletonSolution { +pub struct SingletonSolution { pub lineage_proof: LineageProof, pub amount: u64, - pub inner_solution: NodePtr, + pub inner_solution: I, } #[derive(FromClvm, ToClvm, Debug)] #[clvm(list)] -pub struct EveSingletonSolution { +pub struct EveSingletonSolution { pub lineage_proof: EveProof, pub amount: u64, - pub inner_solution: NodePtr, + pub inner_solution: I, } fn print_puzzle_info(a: &Allocator, puzzle: NodePtr, solution: NodePtr) { println!("Puzzle: {}", hex::encode(tree_hash(a, puzzle))); // exit if this puzzle is not curried - let Ok(uncurried) = CurriedProgram::::from_clvm(a, puzzle) else { + let Ok(uncurried) = CurriedProgram::::from_clvm(a, puzzle) else { println!(" puzzle has no curried parameters"); return; }; @@ -178,7 +179,9 @@ fn print_puzzle_info(a: &Allocator, puzzle: NodePtr, solution: NodePtr) { match tree_hash(a, uncurried.program) { SINGLETON_MOD_HASH => { println!("singleton_top_layer_1_1.clsp"); - let Ok(uncurried) = CurriedProgram::::from_clvm(a, puzzle) else { + let Ok(uncurried) = + CurriedProgram::>::from_clvm(a, puzzle) + else { println!("failed to uncurry singleton"); return; }; @@ -239,11 +242,11 @@ fn main() { let puzzle = spend .puzzle_reveal - .to_clvm(&mut a) + .to_node_ptr(&mut a) .expect("deserialize puzzle"); let solution = spend .solution - .to_clvm(&mut a) + .to_node_ptr(&mut a) .expect("deserialize solution"); println!("Spending {:?}", &spend.coin); diff --git a/chia-wallet/fuzz/fuzz_targets/roundtrip.rs b/chia-wallet/fuzz/fuzz_targets/roundtrip.rs index a56f3b0cd..11de18dad 100644 --- a/chia-wallet/fuzz/fuzz_targets/roundtrip.rs +++ b/chia-wallet/fuzz/fuzz_targets/roundtrip.rs @@ -1,27 +1,35 @@ #![no_main] -use std::fmt; -use chia_wallet::{nft::NftMetadata, Proof}; -use clvm_traits::{FromClvm, ToClvm}; -use clvmr::Allocator; -use libfuzzer_sys::{ - arbitrary::{Arbitrary, Unstructured}, - fuzz_target, -}; +use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let mut u = Unstructured::new(data); - roundtrip::(&mut u); - roundtrip::(&mut u); -}); + #[cfg(fuzzing)] + { + use std::fmt; + + use chia_wallet::{nft::NftMetadata, Proof}; + use clvm_traits::{FromClvm, ToClvm}; + use clvmr::{allocator::NodePtr, Allocator}; + use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured}; + + let mut u = Unstructured::new(data); + roundtrip::(&mut u); + roundtrip::(&mut u); -fn roundtrip<'a, T>(u: &mut Unstructured<'a>) -where - T: Arbitrary<'a> + ToClvm + FromClvm + PartialEq + fmt::Debug, -{ - let obj = T::arbitrary(u).unwrap(); - let mut a = Allocator::new(); - let ptr = obj.to_clvm(&mut a).unwrap(); - let obj2 = T::from_clvm(&a, ptr).unwrap(); - assert_eq!(obj, obj2); -} + fn roundtrip<'a, T>(u: &mut Unstructured<'a>) + where + T: Arbitrary<'a> + ToClvm + FromClvm + PartialEq + fmt::Debug, + { + let obj = T::arbitrary(u).unwrap(); + let mut a = Allocator::new(); + let ptr = obj.to_clvm(&mut a).unwrap(); + let obj2 = T::from_clvm(&a, ptr).unwrap(); + assert_eq!(obj, obj2); + } + } + + #[cfg(not(fuzzing))] + { + let _ = data; + } +}); diff --git a/chia-wallet/src/proof.rs b/chia-wallet/src/proof.rs index aadc97cf8..cf169afac 100644 --- a/chia-wallet/src/proof.rs +++ b/chia-wallet/src/proof.rs @@ -1,6 +1,5 @@ use arbitrary::{Arbitrary, Unstructured}; -use clvm_traits::{FromClvm, Result, ToClvm}; -use clvmr::{allocator::NodePtr, Allocator}; +use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Proof { @@ -8,19 +7,19 @@ pub enum Proof { Eve(EveProof), } -impl FromClvm for Proof { - fn from_clvm(a: &Allocator, node: NodePtr) -> Result { - LineageProof::from_clvm(a, node) +impl FromClvm for Proof { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + LineageProof::from_clvm(decoder, decoder.clone_node(&node)) .map(Self::Lineage) - .or_else(|_| EveProof::from_clvm(a, node).map(Self::Eve)) + .or_else(|_| EveProof::from_clvm(decoder, node).map(Self::Eve)) } } -impl ToClvm for Proof { - fn to_clvm(&self, a: &mut Allocator) -> Result { +impl ToClvm for Proof { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { match self { - Self::Lineage(lineage_proof) => lineage_proof.to_clvm(a), - Self::Eve(eve_proof) => eve_proof.to_clvm(a), + Self::Lineage(lineage_proof) => lineage_proof.to_clvm(encoder), + Self::Eve(eve_proof) => eve_proof.to_clvm(encoder), } } } diff --git a/chia-wallet/src/puzzles/cat.rs b/chia-wallet/src/puzzles/cat.rs index 9be53ce9d..43da15d6a 100644 --- a/chia-wallet/src/puzzles/cat.rs +++ b/chia-wallet/src/puzzles/cat.rs @@ -1,17 +1,16 @@ use chia_bls::PublicKey; -use chia_protocol::Coin; +use chia_protocol::{Bytes32, Coin}; use clvm_traits::{FromClvm, ToClvm}; -use clvmr::allocator::NodePtr; use hex_literal::hex; use crate::LineageProof; #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] -pub struct CatArgs { - pub mod_hash: [u8; 32], - pub tail_program_hash: [u8; 32], - pub inner_puzzle: NodePtr, +pub struct CatArgs { + pub mod_hash: Bytes32, + pub tail_program_hash: Bytes32, + pub inner_puzzle: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] @@ -22,10 +21,10 @@ pub struct EverythingWithSignatureTailArgs { #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] -pub struct CatSolution { - pub inner_puzzle_solution: NodePtr, +pub struct CatSolution { + pub inner_puzzle_solution: I, pub lineage_proof: Option, - pub prev_coin_id: [u8; 32], + pub prev_coin_id: Bytes32, pub this_coin_info: Coin, pub next_coin_proof: CoinProof, pub prev_subtotal: i64, @@ -35,8 +34,8 @@ pub struct CatSolution { #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] pub struct CoinProof { - pub parent_coin_info: [u8; 32], - pub inner_puzzle_hash: [u8; 32], + pub parent_coin_info: Bytes32, + pub inner_puzzle_hash: Bytes32, pub amount: u64, } diff --git a/chia-wallet/src/puzzles/did.rs b/chia-wallet/src/puzzles/did.rs index dc260f64f..ca382b241 100644 --- a/chia-wallet/src/puzzles/did.rs +++ b/chia-wallet/src/puzzles/did.rs @@ -1,41 +1,52 @@ -use clvm_traits::{clvm_list, match_list, match_tuple, Error, FromClvm, Result, ToClvm}; -use clvmr::{allocator::NodePtr, Allocator}; +use chia_protocol::Bytes32; +use clvm_traits::{ + clvm_list, match_list, match_tuple, ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, Raw, + ToClvm, ToClvmError, +}; use hex_literal::hex; use crate::singleton::SingletonStruct; #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] -pub struct DidArgs { - pub inner_puzzle: NodePtr, - pub recovery_did_list_hash: [u8; 32], +pub struct DidArgs { + pub inner_puzzle: I, + pub recovery_did_list_hash: Bytes32, pub num_verifications_required: u64, pub singleton_struct: SingletonStruct, - pub metadata: NodePtr, + pub metadata: M, } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum DidSolution { - InnerSpend(NodePtr), +pub enum DidSolution { + InnerSpend(I), } -impl FromClvm for DidSolution { - fn from_clvm(a: &Allocator, node: NodePtr) -> Result { - let (mode, args) = ::from_clvm(a, node)?; - +impl FromClvm for DidSolution +where + I: FromClvm, +{ + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let (mode, args) = )>::from_clvm(decoder, node)?; match mode { 1 => Ok(Self::InnerSpend( - ::from_clvm(a, args)?.0, + ::from_clvm(decoder, args.0)?.0, )), - _ => Err(Error::Custom(format!("unexpected did spend mode {}", mode))), + _ => Err(FromClvmError::Custom(format!( + "unexpected did spend mode {}", + mode + ))), } } } -impl ToClvm for DidSolution { - fn to_clvm(&self, a: &mut Allocator) -> Result { +impl ToClvm for DidSolution +where + I: ToClvm, +{ + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { match self { - Self::InnerSpend(solution) => clvm_list!(1, solution).to_clvm(a), + Self::InnerSpend(solution) => clvm_list!(1, solution).to_clvm(encoder), } } } @@ -87,6 +98,8 @@ pub static DID_INNER_PUZZLE_HASH: [u8; 32] = hex!( #[cfg(test)] mod tests { + use clvmr::Allocator; + use super::*; use crate::assert_puzzle_hash; diff --git a/chia-wallet/src/puzzles/nft.rs b/chia-wallet/src/puzzles/nft.rs index 4a9bf68b5..4f600255e 100644 --- a/chia-wallet/src/puzzles/nft.rs +++ b/chia-wallet/src/puzzles/nft.rs @@ -1,6 +1,7 @@ +#[cfg(fuzzing)] use arbitrary::Arbitrary; -use clvm_traits::{FromClvm, Result, ToClvm}; -use clvmr::{allocator::NodePtr, Allocator}; +use chia_protocol::Bytes32; +use clvm_traits::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, Raw, ToClvm, ToClvmError}; use hex_literal::hex; use crate::singleton::SingletonStruct; @@ -8,59 +9,60 @@ use crate::singleton::SingletonStruct; #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] pub struct NftIntermediateLauncherArgs { - pub launcher_puzzle_hash: [u8; 32], + pub launcher_puzzle_hash: Bytes32, pub mint_number: usize, pub mint_total: usize, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] -pub struct NftStateLayerArgs { - pub mod_hash: [u8; 32], - pub metadata: NodePtr, - pub metadata_updater_puzzle_hash: [u8; 32], - pub inner_puzzle: NodePtr, +pub struct NftStateLayerArgs { + pub mod_hash: Bytes32, + pub metadata: M, + pub metadata_updater_puzzle_hash: Bytes32, + pub inner_puzzle: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] -pub struct NftStateLayerSolution { - pub inner_solution: NodePtr, +pub struct NftStateLayerSolution { + pub inner_solution: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] -pub struct NftOwnershipLayerArgs { - pub mod_hash: [u8; 32], - pub current_owner: Option<[u8; 32]>, - pub transfer_program: NodePtr, - pub inner_puzzle: NodePtr, +pub struct NftOwnershipLayerArgs { + pub mod_hash: Bytes32, + pub current_owner: Option, + pub transfer_program: P, + pub inner_puzzle: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] -pub struct NftOwnershipLayerSolution { - pub inner_solution: NodePtr, +pub struct NftOwnershipLayerSolution { + pub inner_solution: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] pub struct NftRoyaltyTransferPuzzleArgs { pub singleton_struct: SingletonStruct, - pub royalty_puzzle_hash: [u8; 32], + pub royalty_puzzle_hash: Bytes32, pub trade_price_percentage: u16, } -#[derive(Debug, Clone, PartialEq, Eq, Arbitrary)] +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(fuzzing, derive(Arbitrary))] pub struct NftMetadata { pub edition_number: u64, pub edition_total: u64, pub data_uris: Vec, - pub data_hash: Option<[u8; 32]>, + pub data_hash: Option, pub metadata_uris: Vec, - pub metadata_hash: Option<[u8; 32]>, + pub metadata_hash: Option, pub license_uris: Vec, - pub license_hash: Option<[u8; 32]>, + pub license_hash: Option, } impl Default for NftMetadata { @@ -78,21 +80,21 @@ impl Default for NftMetadata { } } -impl FromClvm for NftMetadata { - fn from_clvm(a: &Allocator, node: NodePtr) -> Result { - let items: Vec<(String, NodePtr)> = FromClvm::from_clvm(a, node)?; +impl FromClvm for NftMetadata { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let items: Vec<(String, Raw)> = FromClvm::from_clvm(decoder, node)?; let mut metadata = Self::default(); for (key, value_ptr) in items { match key.as_str() { - "sn" => metadata.edition_number = FromClvm::from_clvm(a, value_ptr)?, - "st" => metadata.edition_total = FromClvm::from_clvm(a, value_ptr)?, - "u" => metadata.data_uris = FromClvm::from_clvm(a, value_ptr)?, - "h" => metadata.data_hash = FromClvm::from_clvm(a, value_ptr)?, - "mu" => metadata.metadata_uris = FromClvm::from_clvm(a, value_ptr)?, - "mh" => metadata.metadata_hash = FromClvm::from_clvm(a, value_ptr)?, - "lu" => metadata.license_uris = FromClvm::from_clvm(a, value_ptr)?, - "lh" => metadata.license_hash = FromClvm::from_clvm(a, value_ptr)?, + "sn" => metadata.edition_number = FromClvm::from_clvm(decoder, value_ptr.0)?, + "st" => metadata.edition_total = FromClvm::from_clvm(decoder, value_ptr.0)?, + "u" => metadata.data_uris = FromClvm::from_clvm(decoder, value_ptr.0)?, + "h" => metadata.data_hash = FromClvm::from_clvm(decoder, value_ptr.0)?, + "mu" => metadata.metadata_uris = FromClvm::from_clvm(decoder, value_ptr.0)?, + "mh" => metadata.metadata_hash = FromClvm::from_clvm(decoder, value_ptr.0)?, + "lu" => metadata.license_uris = FromClvm::from_clvm(decoder, value_ptr.0)?, + "lh" => metadata.license_hash = FromClvm::from_clvm(decoder, value_ptr.0)?, _ => (), } } @@ -101,40 +103,40 @@ impl FromClvm for NftMetadata { } } -impl ToClvm for NftMetadata { - fn to_clvm(&self, a: &mut Allocator) -> Result { - let mut items: Vec<(&str, NodePtr)> = Vec::new(); +impl ToClvm for NftMetadata { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + let mut items: Vec<(&str, Raw)> = Vec::new(); if !self.data_uris.is_empty() { - items.push(("u", self.data_uris.to_clvm(a)?)); + items.push(("u", Raw(self.data_uris.to_clvm(encoder)?))); } if let Some(hash) = self.data_hash { - items.push(("h", hash.to_clvm(a)?)); + items.push(("h", Raw(hash.to_clvm(encoder)?))); } if !self.metadata_uris.is_empty() { - items.push(("mu", self.metadata_uris.to_clvm(a)?)); + items.push(("mu", Raw(self.metadata_uris.to_clvm(encoder)?))); } if let Some(hash) = self.metadata_hash { - items.push(("mh", hash.to_clvm(a)?)); + items.push(("mh", Raw(hash.to_clvm(encoder)?))); } if !self.license_uris.is_empty() { - items.push(("lu", self.license_uris.to_clvm(a)?)); + items.push(("lu", Raw(self.license_uris.to_clvm(encoder)?))); } if let Some(hash) = self.license_hash { - items.push(("lh", hash.to_clvm(a)?)); + items.push(("lh", Raw(hash.to_clvm(encoder)?))); } items.extend(vec![ - ("sn", self.edition_number.to_clvm(a)?), - ("st", self.edition_total.to_clvm(a)?), + ("sn", Raw(self.edition_number.to_clvm(encoder)?)), + ("st", Raw(self.edition_total.to_clvm(encoder)?)), ]); - items.to_clvm(a) + items.to_clvm(encoder) } } diff --git a/chia-wallet/src/puzzles/singleton.rs b/chia-wallet/src/puzzles/singleton.rs index 58bdb046d..263230e8d 100644 --- a/chia-wallet/src/puzzles/singleton.rs +++ b/chia-wallet/src/puzzles/singleton.rs @@ -1,46 +1,46 @@ +use chia_protocol::Bytes32; use clvm_traits::{FromClvm, ToClvm}; -use clvmr::allocator::NodePtr; use hex_literal::hex; use crate::Proof; #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(curry)] -pub struct SingletonArgs { +pub struct SingletonArgs { pub singleton_struct: SingletonStruct, - pub inner_puzzle: NodePtr, + pub inner_puzzle: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(tuple)] pub struct SingletonStruct { - pub mod_hash: [u8; 32], - pub launcher_id: [u8; 32], - pub launcher_puzzle_hash: [u8; 32], + pub mod_hash: Bytes32, + pub launcher_id: Bytes32, + pub launcher_puzzle_hash: Bytes32, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] -pub struct SingletonSolution { +pub struct SingletonSolution { pub proof: Proof, pub amount: u64, - pub inner_solution: NodePtr, + pub inner_solution: I, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] -pub struct LauncherSolution { - pub singleton_puzzle_hash: [u8; 32], +pub struct LauncherSolution { + pub singleton_puzzle_hash: Bytes32, pub amount: u64, - pub key_value_list: NodePtr, + pub key_value_list: T, } impl SingletonStruct { - pub fn new(launcher_id: [u8; 32]) -> Self { + pub fn new(launcher_id: Bytes32) -> Self { Self { - mod_hash: SINGLETON_TOP_LAYER_PUZZLE_HASH, + mod_hash: SINGLETON_TOP_LAYER_PUZZLE_HASH.into(), launcher_id, - launcher_puzzle_hash: SINGLETON_LAUNCHER_PUZZLE_HASH, + launcher_puzzle_hash: SINGLETON_LAUNCHER_PUZZLE_HASH.into(), } } } diff --git a/chia-wallet/src/puzzles/standard.rs b/chia-wallet/src/puzzles/standard.rs index 90e6c2a54..ebbdc8ea6 100644 --- a/chia-wallet/src/puzzles/standard.rs +++ b/chia-wallet/src/puzzles/standard.rs @@ -1,7 +1,6 @@ use chia_bls::PublicKey; use clvm_traits::{FromClvm, ToClvm}; use clvm_utils::{curry_tree_hash, tree_hash_atom}; -use clvmr::allocator::NodePtr; use hex_literal::hex; #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] @@ -12,10 +11,10 @@ pub struct StandardArgs { #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] -pub struct StandardSolution { +pub struct StandardSolution { pub original_public_key: Option, - pub delegated_puzzle: NodePtr, - pub solution: NodePtr, + pub delegated_puzzle: P, + pub solution: S, } pub fn standard_puzzle_hash(synthetic_key: &PublicKey) -> [u8; 32] { diff --git a/clvm-derive/src/from_clvm.rs b/clvm-derive/src/from_clvm.rs index 4d295b398..9af018ae8 100644 --- a/clvm-derive/src/from_clvm.rs +++ b/clvm-derive/src/from_clvm.rs @@ -1,6 +1,8 @@ -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; -use syn::{parse_quote, spanned::Spanned, Data, DeriveInput, Fields, Type}; +use syn::{ + parse_quote, spanned::Spanned, Data, DeriveInput, Fields, GenericParam, Type, TypeParam, +}; use crate::helpers::{add_trait_bounds, parse_args, Repr}; @@ -57,14 +59,29 @@ pub fn from_clvm(mut ast: DeriveInput) -> TokenStream { ), }; - add_trait_bounds(&mut ast.generics, parse_quote!(#crate_name::FromClvm)); - let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + let node_name = Ident::new("Node", Span::mixed_site()); + + add_trait_bounds( + &mut ast.generics, + parse_quote!(#crate_name::FromClvm<#node_name>), + ); + + let generics_clone = ast.generics.clone(); + let (_, ty_generics, where_clause) = generics_clone.split_for_impl(); + + ast.generics + .params + .push(GenericParam::Type(TypeParam::from(node_name.clone()))); + let (impl_generics, _, _) = ast.generics.split_for_impl(); quote! { #[automatically_derived] - impl #impl_generics #crate_name::FromClvm for #struct_name #ty_generics #where_clause { - fn from_clvm(a: &clvmr::Allocator, node: clvmr::allocator::NodePtr) -> #crate_name::Result { - let #destructure_macro!( #( #field_names, )* ) = <#match_macro!( #( #field_types ),* ) as #crate_name::FromClvm>::from_clvm(a, node)?; + impl #impl_generics #crate_name::FromClvm<#node_name> for #struct_name #ty_generics #where_clause { + fn from_clvm( + decoder: &impl #crate_name::ClvmDecoder, + node: #node_name, + ) -> ::std::result::Result { + let #destructure_macro!( #( #field_names, )* ) = <#match_macro!( #( #field_types ),* ) as #crate_name::FromClvm<#node_name>>::from_clvm(decoder, node)?; Ok(#initializer) } } diff --git a/clvm-derive/src/to_clvm.rs b/clvm-derive/src/to_clvm.rs index 32c228ae0..a6b41afa6 100644 --- a/clvm-derive/src/to_clvm.rs +++ b/clvm-derive/src/to_clvm.rs @@ -1,6 +1,6 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; -use syn::{parse_quote, Data, DeriveInput, Fields, Index}; +use syn::{parse_quote, Data, DeriveInput, Fields, GenericParam, Index, TypeParam}; use crate::helpers::{add_trait_bounds, parse_args, Repr}; @@ -47,15 +47,30 @@ pub fn to_clvm(mut ast: DeriveInput) -> TokenStream { Repr::Curry => quote!( #crate_name::clvm_curried_args ), }; - add_trait_bounds(&mut ast.generics, parse_quote!(#crate_name::ToClvm)); - let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + let node_name = Ident::new("Node", Span::mixed_site()); + + add_trait_bounds( + &mut ast.generics, + parse_quote!(#crate_name::ToClvm<#node_name>), + ); + + let generics_clone = ast.generics.clone(); + let (_, ty_generics, where_clause) = generics_clone.split_for_impl(); + + ast.generics + .params + .push(GenericParam::Type(TypeParam::from(node_name.clone()))); + let (impl_generics, _, _) = ast.generics.split_for_impl(); quote! { #[automatically_derived] - impl #impl_generics #crate_name::ToClvm for #struct_name #ty_generics #where_clause { - fn to_clvm(&self, a: &mut clvmr::Allocator) -> #crate_name::Result { + impl #impl_generics #crate_name::ToClvm<#node_name> for #struct_name #ty_generics #where_clause { + fn to_clvm( + &self, + encoder: &mut impl #crate_name::ClvmEncoder + ) -> ::std::result::Result<#node_name, #crate_name::ToClvmError> { let value = #list_macro!( #( &self.#field_names ),* ); - #crate_name::ToClvm::to_clvm(&value, a) + #crate_name::ToClvm::to_clvm(&value, encoder) } } } diff --git a/clvm-traits/src/clvm_decoder.rs b/clvm-traits/src/clvm_decoder.rs new file mode 100644 index 000000000..75c63d037 --- /dev/null +++ b/clvm-traits/src/clvm_decoder.rs @@ -0,0 +1,40 @@ +use clvmr::{ + allocator::{NodePtr, SExp}, + Allocator, +}; + +use crate::FromClvmError; + +pub trait ClvmDecoder { + type Node: Clone; + + fn decode_atom(&self, node: &Self::Node) -> Result<&[u8], FromClvmError>; + fn decode_pair(&self, node: &Self::Node) -> Result<(Self::Node, Self::Node), FromClvmError>; + + /// This is a helper function that just calls `clone` on the node. + /// It's required only because the compiler can't infer that `N` is `Clone`, + /// since there's no `Clone` bound on the `FromClvm` trait. + fn clone_node(&self, node: &Self::Node) -> Self::Node { + node.clone() + } +} + +impl ClvmDecoder for Allocator { + type Node = NodePtr; + + fn decode_atom(&self, node: &Self::Node) -> Result<&[u8], FromClvmError> { + if let SExp::Atom = self.sexp(*node) { + Ok(self.atom(*node)) + } else { + Err(FromClvmError::ExpectedAtom) + } + } + + fn decode_pair(&self, node: &Self::Node) -> Result<(Self::Node, Self::Node), FromClvmError> { + if let SExp::Pair(first, rest) = self.sexp(*node) { + Ok((first, rest)) + } else { + Err(FromClvmError::ExpectedPair) + } + } +} diff --git a/clvm-traits/src/clvm_encoder.rs b/clvm-traits/src/clvm_encoder.rs new file mode 100644 index 000000000..f65812cb8 --- /dev/null +++ b/clvm-traits/src/clvm_encoder.rs @@ -0,0 +1,37 @@ +use clvmr::{allocator::NodePtr, Allocator}; + +use crate::ToClvmError; + +pub trait ClvmEncoder { + type Node: Clone; + + fn encode_atom(&mut self, bytes: &[u8]) -> Result; + fn encode_pair( + &mut self, + first: Self::Node, + rest: Self::Node, + ) -> Result; + + /// This is a helper function that just calls `clone` on the node. + /// It's required only because the compiler can't infer that `N` is `Clone`, + /// since there's no `Clone` bound on the `ToClvm` trait. + fn clone_node(&self, node: &Self::Node) -> Self::Node { + node.clone() + } +} + +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_pair( + &mut self, + first: Self::Node, + rest: Self::Node, + ) -> Result { + self.new_pair(first, rest).or(Err(ToClvmError::OutOfMemory)) + } +} diff --git a/clvm-traits/src/error.rs b/clvm-traits/src/error.rs index 1068bd07b..5bbace4bf 100644 --- a/clvm-traits/src/error.rs +++ b/clvm-traits/src/error.rs @@ -1,34 +1,30 @@ -use clvmr::{allocator::NodePtr, reduction::EvalErr}; +use std::string::FromUtf8Error; + use thiserror::Error; -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum Error { - #[error("allocator error {0:?}")] - Allocator(EvalErr), +#[derive(Error, Debug, Clone, PartialEq, Eq)] +pub enum ToClvmError { + #[error("out of memory")] + OutOfMemory, - #[error("expected atom")] - ExpectedAtom(NodePtr), + #[error("{0}")] + Custom(String), +} - #[error("expected cons")] - ExpectedCons(NodePtr), +#[derive(Error, Debug, Clone, PartialEq, Eq)] +pub enum FromClvmError { + #[error("{0}")] + InvalidUtf8(#[from] FromUtf8Error), - #[error("expected nil")] - ExpectedNil(NodePtr), + #[error("expected atom of length {expected}, but found length {found}")] + WrongAtomLength { expected: usize, found: usize }, - #[error("expected one")] - ExpectedOne(NodePtr), + #[error("expected atom")] + ExpectedAtom, - #[error("validation failed")] - Validation(NodePtr), + #[error("expected pair")] + ExpectedPair, #[error("{0}")] Custom(String), } - -pub type Result = std::result::Result; - -impl From for Error { - fn from(value: EvalErr) -> Self { - Self::Allocator(value) - } -} diff --git a/clvm-traits/src/from_clvm.rs b/clvm-traits/src/from_clvm.rs index 39f47ad1b..800d2d931 100644 --- a/clvm-traits/src/from_clvm.rs +++ b/clvm-traits/src/from_clvm.rs @@ -1,47 +1,70 @@ -use std::array::TryFromSliceError; +use clvmr::{allocator::NodePtr, Allocator}; +use num_bigint::{BigInt, Sign}; -use clvmr::{ - allocator::{NodePtr, SExp}, - op_utils::nullp, - Allocator, -}; -use num_bigint::Sign; +use crate::{ClvmDecoder, FromClvmError}; -use crate::{Error, Result}; +pub trait FromClvm: Sized { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result; +} + +pub trait FromNodePtr { + fn from_node_ptr(a: &Allocator, node: NodePtr) -> Result + where + Self: Sized; +} -pub trait FromClvm: Sized { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result; +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(_a: &Allocator, ptr: NodePtr) -> Result { - Ok(ptr) +impl FromClvm for NodePtr { + fn from_clvm( + _decoder: &impl ClvmDecoder, + node: NodePtr, + ) -> Result { + Ok(node) } } macro_rules! clvm_primitive { ($primitive:ty) => { - impl FromClvm for $primitive { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result { - if let SExp::Atom = a.sexp(ptr) { - let (sign, mut vec) = a.number(ptr).to_bytes_be(); - if vec.len() < std::mem::size_of::<$primitive>() { - let mut zeros = vec![0; std::mem::size_of::<$primitive>() - vec.len()]; - zeros.extend(vec); - vec = zeros; - } - let value = - <$primitive>::from_be_bytes(vec.as_slice().try_into().map_err( - |error: TryFromSliceError| Error::Custom(error.to_string()), - )?); - Ok(if sign == Sign::Minus { - value.wrapping_neg() - } else { - value - }) - } else { - Err(Error::ExpectedAtom(ptr)) + impl FromClvm for $primitive { + fn from_clvm( + decoder: &impl ClvmDecoder, + 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); + 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 value = <$primitive>::from_be_bytes(vec.as_slice().try_into().or(Err( + FromClvmError::WrongAtomLength { + expected: LEN, + found: bytes.len(), + }, + ))?); + + Ok(if sign == Sign::Minus { + value.wrapping_neg() + } else { + value + }) } } }; @@ -60,113 +83,116 @@ clvm_primitive!(i128); clvm_primitive!(usize); clvm_primitive!(isize); -impl FromClvm for (A, B) +impl FromClvm for (A, B) where - A: FromClvm, - B: FromClvm, + A: FromClvm, + B: FromClvm, { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result { - match a.sexp(ptr) { - SExp::Pair(first, rest) => Ok((A::from_clvm(a, first)?, B::from_clvm(a, rest)?)), - SExp::Atom => Err(Error::ExpectedCons(ptr)), - } + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let (first, rest) = decoder.decode_pair(&node)?; + let first = A::from_clvm(decoder, first)?; + let rest = B::from_clvm(decoder, rest)?; + Ok((first, rest)) } } -impl FromClvm for () { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result { - if nullp(a, ptr) { +impl FromClvm for () { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let bytes = decoder.decode_atom(&node)?; + if bytes.is_empty() { Ok(()) } else { - Err(Error::ExpectedNil(ptr)) + Err(FromClvmError::WrongAtomLength { + expected: 0, + found: bytes.len(), + }) } } } -impl FromClvm for [T; N] +impl FromClvm for [T; LEN] where - T: FromClvm, + T: FromClvm, { - fn from_clvm(a: &Allocator, mut ptr: NodePtr) -> Result { - let mut items = Vec::with_capacity(N); + fn from_clvm(decoder: &impl ClvmDecoder, mut node: N) -> Result { + let mut items = Vec::with_capacity(LEN); loop { - match a.sexp(ptr) { - SExp::Atom => { - if nullp(a, ptr) { - return match items.try_into() { - Ok(value) => Ok(value), - Err(_) => Err(Error::ExpectedCons(ptr)), - }; - } else { - return Err(Error::ExpectedNil(ptr)); - } + if let Ok((first, rest)) = decoder.decode_pair(&node) { + if items.len() >= LEN { + return Err(FromClvmError::ExpectedAtom); + } else { + items.push(T::from_clvm(decoder, first)?); + node = rest; } - SExp::Pair(first, rest) => { - if items.len() >= N { - return Err(Error::ExpectedAtom(ptr)); - } else { - items.push(T::from_clvm(a, first)?); - ptr = rest; - } + } else { + let bytes = decoder.decode_atom(&node)?; + if bytes.is_empty() { + return items.try_into().or(Err(FromClvmError::ExpectedPair)); + } else { + return Err(FromClvmError::WrongAtomLength { + expected: 0, + found: bytes.len(), + }); } } } } } -impl FromClvm for Vec +impl FromClvm for Vec where - T: FromClvm, + T: FromClvm, { - fn from_clvm(a: &Allocator, mut ptr: NodePtr) -> Result { + fn from_clvm(decoder: &impl ClvmDecoder, mut node: N) -> Result { let mut items = Vec::new(); loop { - match a.sexp(ptr) { - SExp::Atom => { - if nullp(a, ptr) { - return Ok(items); - } else { - return Err(Error::ExpectedNil(ptr)); - } - } - SExp::Pair(first, rest) => { - items.push(T::from_clvm(a, first)?); - ptr = rest; + if let Ok((first, rest)) = decoder.decode_pair(&node) { + items.push(T::from_clvm(decoder, first)?); + node = rest; + } else { + let bytes = decoder.decode_atom(&node)?; + if bytes.is_empty() { + return Ok(items); + } else { + return Err(FromClvmError::WrongAtomLength { + expected: 0, + found: bytes.len(), + }); } } } } } -impl FromClvm for Option { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result { - if nullp(a, ptr) { +impl FromClvm for Option +where + T: FromClvm, +{ + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + if let Ok(&[]) = decoder.decode_atom(&node) { Ok(None) } else { - Ok(Some(T::from_clvm(a, ptr)?)) + Ok(Some(T::from_clvm(decoder, node)?)) } } } -impl FromClvm for String { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result { - if let SExp::Atom = a.sexp(ptr) { - Self::from_utf8(a.atom(ptr).to_vec()).map_err(|error| Error::Custom(error.to_string())) - } else { - Err(Error::ExpectedAtom(ptr)) - } +impl FromClvm for String { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + let bytes = decoder.decode_atom(&node)?; + Ok(Self::from_utf8(bytes.to_vec())?) } } #[cfg(test)] mod tests { - use clvmr::serde::node_from_bytes; + use clvmr::{allocator::NodePtr, serde::node_from_bytes, Allocator}; use super::*; - fn decode(a: &mut Allocator, hex: &str) -> Result + 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/clvm-traits/src/lib.rs b/clvm-traits/src/lib.rs index bb860438a..b25183db2 100644 --- a/clvm-traits/src/lib.rs +++ b/clvm-traits/src/lib.rs @@ -40,16 +40,22 @@ assert_eq!(Point::from_clvm(a, ptr).unwrap(), point); #[cfg(feature = "derive")] pub use clvm_derive::*; +mod clvm_decoder; +mod clvm_encoder; mod error; mod from_clvm; mod macros; mod match_byte; mod to_clvm; +mod wrappers; +pub use clvm_decoder::*; +pub use clvm_encoder::*; pub use error::*; pub use from_clvm::*; pub use match_byte::*; pub use to_clvm::*; +pub use wrappers::*; #[cfg(test)] #[cfg(feature = "derive")] @@ -58,13 +64,13 @@ mod tests { use std::fmt; - use clvmr::{serde::node_to_bytes, Allocator}; + use clvmr::{allocator::NodePtr, serde::node_to_bytes, Allocator}; use super::*; fn check(value: T, expected: &str) where - T: fmt::Debug + PartialEq + ToClvm + FromClvm, + T: fmt::Debug + PartialEq + ToClvm + FromClvm, { let a = &mut Allocator::new(); diff --git a/clvm-traits/src/match_byte.rs b/clvm-traits/src/match_byte.rs index a94fa7478..5a6b22687 100644 --- a/clvm-traits/src/match_byte.rs +++ b/clvm-traits/src/match_byte.rs @@ -1,36 +1,30 @@ -use clvmr::{ - allocator::{NodePtr, SExp}, - Allocator, -}; +use num_bigint::BigInt; -use crate::{Error, FromClvm, Result, ToClvm}; +use crate::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; #[derive(Debug, Copy, Clone)] pub struct MatchByte; -impl ToClvm for MatchByte { - fn to_clvm(&self, a: &mut Allocator) -> Result { - match BYTE { - 0 => Ok(a.null()), - 1 => Ok(a.one()), - _ => Ok(a.new_number(BYTE.into())?), +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) } } -impl FromClvm for MatchByte { - fn from_clvm(a: &Allocator, node: NodePtr) -> Result { - if let SExp::Atom = a.sexp(node) { - match a.atom(node) { - [] if BYTE == 0 => Ok(Self), - [byte] if *byte == BYTE && BYTE > 0 => Ok(Self), - _ => Err(Error::Custom(format!( - "expected an atom with a value of {}", - BYTE - ))), - } - } else { - Err(Error::ExpectedAtom(node)) +impl FromClvm for MatchByte { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { + match decoder.decode_atom(&node)? { + [] if BYTE == 0 => Ok(Self), + [byte] if *byte == BYTE && BYTE > 0 => Ok(Self), + _ => Err(FromClvmError::Custom(format!( + "expected an atom with a single byte value of {}", + BYTE + ))), } } } diff --git a/clvm-traits/src/to_clvm.rs b/clvm-traits/src/to_clvm.rs index ef27b4ce0..e5dac9a20 100644 --- a/clvm-traits/src/to_clvm.rs +++ b/clvm-traits/src/to_clvm.rs @@ -1,22 +1,50 @@ use clvmr::{allocator::NodePtr, Allocator}; +use num_bigint::BigInt; -use crate::{Error, Result}; +use crate::{ClvmEncoder, ToClvmError}; -pub trait ToClvm { - fn to_clvm(&self, a: &mut Allocator) -> Result; +pub trait ToClvm { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result; } -impl ToClvm for NodePtr { - fn to_clvm(&self, _a: &mut Allocator) -> Result { +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 impl ClvmEncoder, + ) -> Result { Ok(*self) } } +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, a: &mut Allocator) -> Result { - a.new_number((*self).into()).map_err(Error::Allocator) + 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())) } } }; @@ -35,99 +63,99 @@ clvm_primitive!(i128); clvm_primitive!(usize); clvm_primitive!(isize); -impl ToClvm for &T +impl ToClvm for &T where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { - T::to_clvm(*self, a) + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + T::to_clvm(*self, encoder) } } -impl ToClvm for (A, B) +impl ToClvm for (A, B) where - A: ToClvm, - B: ToClvm, + A: ToClvm, + B: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { - let first = self.0.to_clvm(a)?; - let rest = self.1.to_clvm(a)?; - Ok(a.new_pair(first, rest)?) + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> 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, a: &mut Allocator) -> Result { - Ok(a.null()) +impl ToClvm for () { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + encoder.encode_atom(&[]) } } -impl ToClvm for &[T] +impl ToClvm for &[T] where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { - let mut result = a.null(); + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + let mut result = encoder.encode_atom(&[])?; for item in self.iter().rev() { - let value = item.to_clvm(a)?; - result = a.new_pair(value, result)?; + let value = item.to_clvm(encoder)?; + result = encoder.encode_pair(value, result)?; } Ok(result) } } -impl ToClvm for [T; N] +impl ToClvm for [T; LEN] where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { - self.as_slice().to_clvm(a) + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + self.as_slice().to_clvm(encoder) } } -impl ToClvm for Vec +impl ToClvm for Vec where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { - self.as_slice().to_clvm(a) + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + self.as_slice().to_clvm(encoder) } } -impl ToClvm for Option +impl ToClvm for Option where - T: ToClvm, + T: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { match self { - Some(value) => value.to_clvm(a), - None => Ok(a.null()), + Some(value) => value.to_clvm(encoder), + None => encoder.encode_atom(&[]), } } } -impl ToClvm for &str { - fn to_clvm(&self, a: &mut Allocator) -> Result { - Ok(a.new_atom(self.as_bytes())?) +impl ToClvm for &str { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + encoder.encode_atom(self.as_bytes()) } } -impl ToClvm for String { - fn to_clvm(&self, a: &mut Allocator) -> Result { - self.as_str().to_clvm(a) +impl ToClvm for String { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + self.as_str().to_clvm(encoder) } } #[cfg(test)] mod tests { - use clvmr::serde::node_to_bytes; + use clvmr::{serde::node_to_bytes, Allocator}; use hex::ToHex; use super::*; - fn encode(a: &mut Allocator, value: T) -> Result + fn encode(a: &mut Allocator, value: T) -> Result where - T: ToClvm, + T: ToClvm, { let actual = value.to_clvm(a).unwrap(); let actual_bytes = node_to_bytes(a, actual).unwrap(); diff --git a/clvm-traits/src/wrappers.rs b/clvm-traits/src/wrappers.rs new file mode 100644 index 000000000..d91cb0ff7 --- /dev/null +++ b/clvm-traits/src/wrappers.rs @@ -0,0 +1,19 @@ +use crate::{ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, ToClvm, ToClvmError}; + +/// A wrapper for an intermediate CLVM value. This is required to +/// implement `ToClvm` and `FromClvm` for `N`, since the compiler +/// cannot guarantee that the generic `N` type doesn't already +/// implement these traits. +pub struct Raw(pub N); + +impl FromClvm for Raw { + fn from_clvm(_decoder: &impl ClvmDecoder, node: N) -> Result { + Ok(Self(node)) + } +} + +impl ToClvm for Raw { + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + Ok(encoder.clone_node(&self.0)) + } +} diff --git a/clvm-utils/fuzz/fuzz_targets/curry.rs b/clvm-utils/fuzz/fuzz_targets/curry.rs index ae7171a76..d8b198830 100644 --- a/clvm-utils/fuzz/fuzz_targets/curry.rs +++ b/clvm-utils/fuzz/fuzz_targets/curry.rs @@ -9,7 +9,7 @@ use fuzzing_utils::{make_tree, BitCursor}; fuzz_target!(|data: &[u8]| { let mut a = Allocator::new(); let input = make_tree(&mut a, &mut BitCursor::new(data), true); - if let Ok(curry) = CurriedProgram::::from_clvm(&a, input) { + if let Ok(curry) = CurriedProgram::::from_clvm(&a, input) { curry.to_clvm(&mut a).unwrap(); } }); diff --git a/clvm-utils/src/curried_program.rs b/clvm-utils/src/curried_program.rs index 9ea164e5c..44e52aefd 100644 --- a/clvm-utils/src/curried_program.rs +++ b/clvm-utils/src/curried_program.rs @@ -1,33 +1,33 @@ use clvm_traits::{ - clvm_list, clvm_quote, destructure_list, destructure_quote, match_list, match_quote, FromClvm, - MatchByte, Result, ToClvm, + clvm_list, clvm_quote, destructure_list, destructure_quote, match_list, match_quote, + ClvmDecoder, ClvmEncoder, FromClvm, FromClvmError, MatchByte, ToClvm, ToClvmError, }; -use clvmr::{allocator::NodePtr, Allocator}; #[derive(Debug, Clone)] -pub struct CurriedProgram { - pub program: NodePtr, - pub args: T, +pub struct CurriedProgram { + pub program: P, + pub args: A, } -impl FromClvm for CurriedProgram +impl FromClvm for CurriedProgram where - T: FromClvm, + P: FromClvm, + A: FromClvm, { - fn from_clvm(a: &Allocator, ptr: NodePtr) -> Result { + fn from_clvm(decoder: &impl ClvmDecoder, node: N) -> Result { let destructure_list!(_, destructure_quote!(program), args) = - , match_quote!(NodePtr), T)>::from_clvm(a, ptr)?; - + , match_quote!(P), A)>::from_clvm(decoder, node)?; Ok(Self { program, args }) } } -impl ToClvm for CurriedProgram +impl ToClvm for CurriedProgram where - T: ToClvm, + P: ToClvm, + A: ToClvm, { - fn to_clvm(&self, a: &mut Allocator) -> Result { - clvm_list!(2, clvm_quote!(self.program), self.args.to_clvm(a)?).to_clvm(a) + fn to_clvm(&self, encoder: &mut impl ClvmEncoder) -> Result { + clvm_list!(2, clvm_quote!(&self.program), &self.args).to_clvm(encoder) } } @@ -36,29 +36,28 @@ mod tests { use std::fmt::Debug; use clvm_traits::clvm_curried_args; - use clvmr::serde::node_to_bytes; + use clvmr::{allocator::NodePtr, serde::node_to_bytes, Allocator}; use super::*; - fn check(program: T, args: A, expected: &str) + fn check(program: P, args: A, expected: &str) where - T: Debug + ToClvm + PartialEq + FromClvm, - A: Debug + Clone + PartialEq + ToClvm + FromClvm, + P: Debug + PartialEq + ToClvm + FromClvm, + A: Debug + PartialEq + ToClvm + FromClvm, { let a = &mut Allocator::new(); let curry = CurriedProgram { program: program.to_clvm(a).unwrap(), - args: args.clone(), + args: &args, } .to_clvm(a) .unwrap(); let actual = node_to_bytes(a, curry).unwrap(); assert_eq!(hex::encode(actual), expected); - let curried = CurriedProgram::::from_clvm(a, curry).unwrap(); - let round_program = T::from_clvm(a, curried.program).unwrap(); - assert_eq!(round_program, program); + let curried = CurriedProgram::::from_clvm(a, curry).unwrap(); + assert_eq!(curried.program, program); assert_eq!(curried.args, args); } diff --git a/fuzz/fuzz_targets/fast-forward.rs b/fuzz/fuzz_targets/fast-forward.rs index 47efac326..28cd6f2dc 100644 --- a/fuzz/fuzz_targets/fast-forward.rs +++ b/fuzz/fuzz_targets/fast-forward.rs @@ -7,7 +7,7 @@ use chia_protocol::Bytes32; use chia_protocol::Coin; use chia_protocol::CoinSpend; use chia_traits::streamable::Streamable; -use clvm_traits::ToClvm; +use clvm_traits::ToNodePtr; use clvm_utils::tree_hash; use clvmr::serde::node_to_bytes; use clvmr::Allocator; @@ -23,10 +23,10 @@ fuzz_target!(|data: &[u8]| { hex!("abababababababababababababababababababababababababababababababab"); let mut a = Allocator::new_limited(500000000, 62500000, 62500000); - let Ok(puzzle) = spend.puzzle_reveal.to_clvm(&mut a) else { + let Ok(puzzle) = spend.puzzle_reveal.to_node_ptr(&mut a) else { return; }; - let Ok(solution) = spend.solution.to_clvm(&mut a) else { + let Ok(solution) = spend.solution.to_node_ptr(&mut a) else { return; }; let puzzle_hash = Bytes32::from(tree_hash(&a, puzzle)); diff --git a/src/error.rs b/src/error.rs index 807d44d24..9cfa35bc5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,5 @@ use crate::gen::validation_error::ValidationErr; +use clvm_traits::{FromClvmError, ToClvmError}; use clvmr::reduction::EvalErr; use thiserror::Error; @@ -7,8 +8,11 @@ use pyo3::PyErr; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum Error { - #[error("CLVM {0}")] - Clvm(#[from] clvm_traits::Error), + #[error("To CLVM {0}")] + ToClvm(#[from] ToClvmError), + + #[error("From CLVM {0}")] + FromClvm(#[from] FromClvmError), #[error("Eval {0}")] Eval(#[from] EvalErr), diff --git a/src/fast_forward.rs b/src/fast_forward.rs index 49d20e7ed..5a5212a64 100644 --- a/src/fast_forward.rs +++ b/src/fast_forward.rs @@ -17,9 +17,9 @@ pub struct SingletonStruct { #[derive(FromClvm, ToClvm, Debug)] #[clvm(curry)] -pub struct SingletonArgs { +pub struct SingletonArgs { pub singleton_struct: SingletonStruct, - pub inner_puzzle: NodePtr, + pub inner_puzzle: I, } #[derive(FromClvm, ToClvm, Debug)] @@ -32,10 +32,10 @@ pub struct LineageProof { #[derive(FromClvm, ToClvm, Debug)] #[clvm(list)] -pub struct SingletonSolution { +pub struct SingletonSolution { pub lineage_proof: LineageProof, pub amount: u64, - pub inner_solution: NodePtr, + pub inner_solution: I, } // TODO: replace this with a generic function to compute the hash of curried @@ -110,8 +110,8 @@ pub fn fast_forward_singleton( return Err(Error::PuzzleHashMismatch); } - let singleton = CurriedProgram::::from_clvm(a, puzzle)?; - let mut new_solution = SingletonSolution::from_clvm(a, solution)?; + let singleton = CurriedProgram::>::from_clvm(a, puzzle)?; + let mut new_solution = SingletonSolution::::from_clvm(a, solution)?; // this is the tree hash of the singleton top layer puzzle // the tree hash of singleton_top_layer_v1_1.clsp @@ -186,6 +186,7 @@ mod tests { use crate::gen::run_puzzle::run_puzzle; use chia_protocol::CoinSpend; use chia_traits::streamable::Streamable; + use clvm_traits::ToNodePtr; use clvmr::serde::{node_from_bytes, node_to_bytes}; use hex_literal::hex; use rstest::rstest; @@ -213,8 +214,8 @@ mod tests { let new_parents_parent = hex::decode(new_parents_parent).unwrap(); let mut a = Allocator::new_limited(500000000, 62500000, 62500000); - 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 = 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_hash = Bytes32::from(tree_hash(&a, puzzle)); let new_parent_coin = Coin { @@ -279,7 +280,7 @@ mod tests { &hex!("abababababababababababababababababababababababababababababababab"); let mut a = Allocator::new_limited(500000000, 62500000, 62500000); - let puzzle = spend.puzzle_reveal.to_clvm(&mut a).expect("to_clvm"); + let puzzle = spend.puzzle_reveal.to_node_ptr(&mut a).expect("to_clvm"); let puzzle_hash = Bytes32::from(tree_hash(&a, puzzle)); let mut new_parent_coin = Coin { @@ -371,24 +372,27 @@ mod tests { ); } - fn parse_solution(a: &mut Allocator, solution: &[u8]) -> SingletonSolution { + fn parse_solution(a: &mut Allocator, solution: &[u8]) -> SingletonSolution { let new_solution = node_from_bytes(a, solution).expect("parse solution"); SingletonSolution::from_clvm(a, new_solution).expect("parse solution") } - fn serialize_solution(a: &mut Allocator, solution: &SingletonSolution) -> Vec { + fn serialize_solution(a: &mut Allocator, solution: &SingletonSolution) -> Vec { let new_solution = solution.to_clvm(a).expect("to_clvm"); node_to_bytes(a, new_solution).expect("serialize solution") } - fn parse_singleton(a: &mut Allocator, puzzle: &[u8]) -> CurriedProgram { + fn parse_singleton( + a: &mut Allocator, + puzzle: &[u8], + ) -> CurriedProgram> { let puzzle = node_from_bytes(a, puzzle).expect("parse puzzle"); - CurriedProgram::::from_clvm(a, puzzle).expect("uncurry") + CurriedProgram::>::from_clvm(a, puzzle).expect("uncurry") } fn serialize_singleton( a: &mut Allocator, - singleton: &CurriedProgram, + singleton: &CurriedProgram>, ) -> Vec { let puzzle = singleton.to_clvm(a).expect("to_clvm"); node_to_bytes(a, puzzle).expect("serialize puzzle")