Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a less strict solution representation #674

Merged
merged 2 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/chia-puzzles/src/puzzles/cat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl GenesisByCoinIdTailArgs {

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct CatSolution<I> {
pub inner_puzzle_solution: I,
pub lineage_proof: Option<LineageProof>,
Expand Down
4 changes: 2 additions & 2 deletions crates/chia-puzzles/src/puzzles/did.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl DidArgs<TreeHash, TreeHash> {

#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
#[repr(u8)]
pub enum DidSolution<I> {
Recover(#[clvm(rest)] Box<DidRecoverySolution>) = 0,
Expand All @@ -68,7 +68,7 @@ pub enum DidSolution<I> {

#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct DidRecoverySolution {
pub amount: u64,
pub new_inner_puzzle_hash: Bytes32,
Expand Down
4 changes: 2 additions & 2 deletions crates/chia-puzzles/src/puzzles/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl NftStateLayerArgs<TreeHash, TreeHash> {

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct NftStateLayerSolution<I> {
pub inner_solution: I,
}
Expand Down Expand Up @@ -121,7 +121,7 @@ impl NftOwnershipLayerArgs<TreeHash, TreeHash> {

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct NftOwnershipLayerSolution<I> {
pub inner_solution: I,
}
Expand Down
4 changes: 2 additions & 2 deletions crates/chia-puzzles/src/puzzles/singleton.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl SingletonStruct {

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct SingletonSolution<I> {
pub lineage_proof: Proof,
pub amount: u64,
Expand All @@ -66,7 +66,7 @@ pub struct SingletonSolution<I> {

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct LauncherSolution<T> {
pub singleton_puzzle_hash: Bytes32,
pub amount: u64,
Expand Down
2 changes: 1 addition & 1 deletion crates/chia-puzzles/src/puzzles/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl StandardArgs {

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[clvm(list)]
#[clvm(solution)]
pub struct StandardSolution<P, S> {
pub original_public_key: Option<PublicKey>,
pub delegated_puzzle: P,
Expand Down
6 changes: 4 additions & 2 deletions crates/clvm-derive/src/from_clvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fn field_parser_fn_body(
let decode_next = match repr {
Repr::Atom | Repr::Transparent => unreachable!(),
// Decode `(A . B)` pairs for lists.
Repr::List => quote!(decode_pair),
Repr::List | Repr::Solution => quote!(decode_pair),
// Decode `(c (q . A) B)` pairs for curried arguments.
Repr::Curry => quote!(decode_curried_arg),
};
Expand Down Expand Up @@ -150,6 +150,8 @@ fn field_parser_fn_body(
fn check_rest_value(crate_name: &Ident, repr: Repr) -> TokenStream {
match repr {
Repr::Atom | Repr::Transparent => unreachable!(),
// We don't need to check the terminator of a solution.
Repr::Solution => quote! {},
Repr::List => {
// If the last field is not `rest`, we need to check that the `node` is nil.
// If it's not nil, it's not a proper list, and we should return an error.
Expand Down Expand Up @@ -286,7 +288,7 @@ fn impl_for_enum(
let decode_next = match enum_info.default_repr {
Repr::Atom | Repr::Transparent => unreachable!(),
// Decode `(A . B)` pairs for lists.
Repr::List => quote!(decode_pair),
Repr::List | Repr::Solution => quote!(decode_pair),
// Decode `(c (q . A) B)` pairs for curried arguments.
Repr::Curry => quote!(decode_curried_arg),
};
Expand Down
6 changes: 5 additions & 1 deletion crates/clvm-derive/src/parser/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use syn::{
pub enum Repr {
/// Represents `(A . (B . (C . ())))`.
List,
/// The same as `list`, but the terminator doesn't have to be `()`.
Solution,
/// Represents `(c (q . A) (c (q . B) (c (q . C) 1)))`.
Curry,
/// Represents the first field `A` on its own, with no other fields allowed.
Expand All @@ -22,7 +24,7 @@ pub enum Repr {
impl Repr {
pub fn expect(repr: Option<Repr>) -> Repr {
repr.expect(
"missing either `list`, `curry`, `transparent`, or `atom` in `clvm` attribute options",
"missing either `list`, `curry`, `solution`, `transparent`, or `atom` in `clvm` attribute options",
)
}
}
Expand All @@ -31,6 +33,7 @@ impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::List => "list",
Self::Solution => "solution",
Self::Curry => "curry",
Self::Transparent => "transparent",
Self::Atom => "atom",
Expand Down Expand Up @@ -75,6 +78,7 @@ impl Parse for ClvmOption {

match ident.to_string().as_str() {
"list" => Ok(Self::Repr(Repr::List)),
"solution" => Ok(Self::Repr(Repr::Solution)),
"curry" => Ok(Self::Repr(Repr::Curry)),
"transparent" => Ok(Self::Repr(Repr::Transparent)),
"atom" => Ok(Self::Repr(Repr::Atom)),
Expand Down
8 changes: 5 additions & 3 deletions crates/clvm-derive/src/to_clvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ fn encode_fields(
let encode_next = match repr {
Repr::Atom | Repr::Transparent => unreachable!(),
// Encode `(A . B)` pairs for lists.
Repr::List => quote!(encode_pair),
Repr::List | Repr::Solution => quote!(encode_pair),
// Encode `(c (q . A) B)` pairs for curried arguments.
Repr::Curry => quote!(encode_curried_arg),
};

let initial_value = match repr {
Repr::Atom | Repr::Transparent => unreachable!(),
Repr::List => quote!(encoder.encode_atom(#crate_name::Atom::Borrowed(&[]))?),
Repr::List | Repr::Solution => {
quote!(encoder.encode_atom(#crate_name::Atom::Borrowed(&[]))?)
}
Repr::Curry => quote!(encoder.encode_atom(#crate_name::Atom::Borrowed(&[1]))?),
};

Expand Down Expand Up @@ -243,7 +245,7 @@ fn impl_for_enum(
let encode_next = match enum_info.default_repr {
Repr::Atom | Repr::Transparent => unreachable!(),
// Encode `(A . B)` pairs for lists.
Repr::List => quote!(encode_pair),
Repr::List | Repr::Solution => quote!(encode_pair),
// Encode `(c (q . A) B)` pairs for curried arguments.
Repr::Curry => quote!(encode_curried_arg),
};
Expand Down
6 changes: 6 additions & 0 deletions crates/clvm-traits/docs/derive_macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ let ptr = value.to_clvm(a).unwrap();
assert_eq!(Tiers::from_clvm(a, ptr).unwrap(), value);
```

### Solution

The solution representation is the same as list, except it does not check the nil terminator when parsing.
This allows it to be lenient to additional parameters that are in the CLVM object, since they don't affect anything.
If you want your solution to be parsed strictly, you can use list instead.

### Curry

This represents the argument part of a curried CLVM program.
Expand Down
49 changes: 49 additions & 0 deletions crates/clvm-traits/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,55 @@ mod derive_tests {
check(&Struct { a: 52, b: -32 }, "ff3481e0");
}

#[test]
fn test_solution_struct() {
#[derive(Debug, ToClvm, FromClvm, PartialEq)]
#[clvm(solution)]
struct Struct {
a: u64,
b: i32,
}

// Includes the nil terminator.
check(&Struct { a: 52, b: -32 }, "ff34ff81e080");

// Allows additional parameters.
let mut allocator = Allocator::new();
let ptr = clvm_list!(100, 200, 300, 400)
.to_clvm(&mut allocator)
.unwrap();
let value = Struct::from_clvm(&allocator, ptr).unwrap();
assert_eq!(value, Struct { a: 100, b: 200 });

// Doesn't allow differing types for the actual solution parameters.
let mut allocator = Allocator::new();
let ptr = clvm_list!([1, 2, 3], 200, 300)
.to_clvm(&mut allocator)
.unwrap();
Struct::from_clvm(&allocator, ptr).unwrap_err();
}

#[test]
fn test_solution_struct_with_rest() {
#[derive(Debug, ToClvm, FromClvm, PartialEq)]
#[clvm(solution)]
struct Struct {
a: u64,
#[clvm(rest)]
b: i32,
}

// Does not include the nil terminator.
check(&Struct { a: 52, b: -32 }, "ff3481e0");

// Does not allow additional parameters, since it consumes the rest.
let mut allocator = Allocator::new();
let ptr = clvm_list!(100, 200, 300, 400)
.to_clvm(&mut allocator)
.unwrap();
Struct::from_clvm(&allocator, ptr).unwrap_err();
}

#[test]
fn test_curry_struct() {
#[derive(Debug, ToClvm, FromClvm, PartialEq)]
Expand Down
Loading