From cb4f521fe32675a0238453a7c850083405f2952a Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 21 Nov 2024 12:29:09 -0500 Subject: [PATCH 1/2] Fix memo parsing for offers --- Cargo.lock | 1 + crates/chia-puzzles/Cargo.toml | 1 + crates/chia-puzzles/src/puzzles/offer.rs | 113 ++++++++++++++++++++++- 3 files changed, 110 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a36be4add..d6fd614e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -421,6 +421,7 @@ dependencies = [ name = "chia-puzzles" version = "0.16.0" dependencies = [ + "anyhow", "arbitrary", "chia-bls 0.16.0", "chia-protocol", diff --git a/crates/chia-puzzles/Cargo.toml b/crates/chia-puzzles/Cargo.toml index a415afecb..cbf60f863 100644 --- a/crates/chia-puzzles/Cargo.toml +++ b/crates/chia-puzzles/Cargo.toml @@ -27,6 +27,7 @@ arbitrary = { workspace = true, features = ["derive"], optional = true } [dev-dependencies] hex = { workspace = true } +anyhow = { workspace = true } [lib] crate-type = ["rlib"] diff --git a/crates/chia-puzzles/src/puzzles/offer.rs b/crates/chia-puzzles/src/puzzles/offer.rs index 0fa29128e..366d623d4 100644 --- a/crates/chia-puzzles/src/puzzles/offer.rs +++ b/crates/chia-puzzles/src/puzzles/offer.rs @@ -25,20 +25,29 @@ pub struct NotarizedPayment { pub struct Payment { pub puzzle_hash: Bytes32, pub amount: u64, - #[clvm(default)] - pub memos: Vec, + #[clvm(rest)] + pub memos: Option, } +#[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] +#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] +#[clvm(list)] +pub struct Memos(pub Vec); + impl Payment { pub fn new(puzzle_hash: Bytes32, amount: u64) -> Self { - Self::with_memos(puzzle_hash, amount, Vec::new()) + Self { + puzzle_hash, + amount, + memos: None, + } } - pub fn with_memos(puzzle_hash: Bytes32, amount: u64, memos: Vec) -> Self { + pub fn with_memos(puzzle_hash: Bytes32, amount: u64, memos: Memos) -> Self { Self { puzzle_hash, amount, - memos, + memos: Some(memos), } } } @@ -96,6 +105,9 @@ pub const SETTLEMENT_PAYMENTS_PUZZLE_HASH_V1: TreeHash = TreeHash::new(hex!( #[cfg(test)] mod tests { + use clvm_utils::tree_hash; + use clvmr::{serde::node_from_bytes, Allocator}; + use super::*; use crate::assert_puzzle_hash; @@ -105,4 +117,95 @@ mod tests { assert_puzzle_hash!(SETTLEMENT_PAYMENTS_PUZZLE => SETTLEMENT_PAYMENTS_PUZZLE_HASH); assert_puzzle_hash!(SETTLEMENT_PAYMENTS_PUZZLE_V1 => SETTLEMENT_PAYMENTS_PUZZLE_HASH_V1); } + + #[test] + fn test_empty_memos() -> anyhow::Result<()> { + let mut allocator = Allocator::new(); + + /* + ((0xd951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2 + (0x2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b 0x04a817c800 ()) + )) + */ + let expected_payment = node_from_bytes( + &mut allocator, + &hex!( + " + ffffa0d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce13 + 77cad2ffffa02a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae972 + 67eee629113bff8504a817c800ff80808080 + " + ), + )?; + + let nonce = Bytes32::from(hex!( + "d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2" + )); + let puzzle_hash = Bytes32::from(hex!( + "2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b" + )); + let amount = 20_000_000_000; + let memos = Memos(Vec::new()); + + let payment = Payment::with_memos(puzzle_hash, amount, memos); + let notarized_payment = SettlementPaymentsSolution { + notarized_payments: vec![NotarizedPayment { + nonce, + payments: vec![payment], + }], + } + .to_clvm(&mut allocator)?; + + assert_eq!( + tree_hash(&allocator, notarized_payment), + tree_hash(&allocator, expected_payment) + ); + + Ok(()) + } + + #[test] + fn test_missing_memos() -> anyhow::Result<()> { + let mut allocator = Allocator::new(); + + /* + ((0xd951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2 + (0x2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b 0x04a817c800) + )) + */ + let expected_payment = node_from_bytes( + &mut allocator, + &hex!( + " + ffffa0d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce13 + 77cad2ffffa02a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae972 + 67eee629113bff8504a817c800808080 + " + ), + )?; + + let nonce = Bytes32::from(hex!( + "d951714bbcd0d0af317b3ef432472b57e7c48d3036b4491539c186ce1377cad2" + )); + let puzzle_hash = Bytes32::from(hex!( + "2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b" + )); + let amount = 20_000_000_000; + + let payment = Payment::new(puzzle_hash, amount); + let notarized_payment = SettlementPaymentsSolution { + notarized_payments: vec![NotarizedPayment { + nonce, + payments: vec![payment], + }], + } + .to_clvm(&mut allocator)?; + + assert_eq!( + tree_hash(&allocator, notarized_payment), + tree_hash(&allocator, expected_payment) + ); + + Ok(()) + } } From 64e841f130cfa8cf2324ab8b75b0374a6e27f840 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Thu, 21 Nov 2024 12:47:15 -0500 Subject: [PATCH 2/2] Improve API and add comment --- crates/chia-puzzles/src/puzzles/offer.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/chia-puzzles/src/puzzles/offer.rs b/crates/chia-puzzles/src/puzzles/offer.rs index 366d623d4..ea0be5892 100644 --- a/crates/chia-puzzles/src/puzzles/offer.rs +++ b/crates/chia-puzzles/src/puzzles/offer.rs @@ -25,6 +25,8 @@ pub struct NotarizedPayment { pub struct Payment { pub puzzle_hash: Bytes32, pub amount: u64, + /// The memos should usually be set to [`None`] instead of an empty list. + /// This is for compatibility with the way the reference wallet encodes offers. #[clvm(rest)] pub memos: Option, } @@ -43,11 +45,11 @@ impl Payment { } } - pub fn with_memos(puzzle_hash: Bytes32, amount: u64, memos: Memos) -> Self { + pub fn with_memos(puzzle_hash: Bytes32, amount: u64, memos: Vec) -> Self { Self { puzzle_hash, amount, - memos: Some(memos), + memos: Some(Memos(memos)), } } } @@ -145,7 +147,7 @@ mod tests { "2a5cbc6f5076e0517bdb1e4664b3c26e64d27178b65aaa1ae97267eee629113b" )); let amount = 20_000_000_000; - let memos = Memos(Vec::new()); + let memos = Vec::new(); let payment = Payment::with_memos(puzzle_hash, amount, memos); let notarized_payment = SettlementPaymentsSolution {