diff --git a/crates/chia-consensus/src/error.rs b/crates/chia-consensus/src/error.rs index 9cfa35bc5..f36df9763 100644 --- a/crates/chia-consensus/src/error.rs +++ b/crates/chia-consensus/src/error.rs @@ -41,6 +41,9 @@ pub enum Error { #[error("coin mismatch")] CoinMismatch, + #[error("expected lineage proof, found eve proof")] + ExpectedLineageProof, + #[error("{0}")] Custom(String), } diff --git a/crates/chia-consensus/src/fast_forward.rs b/crates/chia-consensus/src/fast_forward.rs index eaa049f18..0a081098c 100644 --- a/crates/chia-consensus/src/fast_forward.rs +++ b/crates/chia-consensus/src/fast_forward.rs @@ -1,43 +1,15 @@ use crate::error::{Error, Result}; use chia_protocol::Bytes32; use chia_protocol::Coin; -use chia_wallet::singleton::SINGLETON_TOP_LAYER_PUZZLE_HASH; +use chia_wallet::singleton::{ + SingletonArgs, SingletonSolution, SingletonStruct, SINGLETON_TOP_LAYER_PUZZLE_HASH, +}; +use chia_wallet::Proof; use clvm_traits::{FromClvm, ToClvm}; use clvm_utils::CurriedProgram; use clvm_utils::{tree_hash, tree_hash_atom, tree_hash_pair}; use clvmr::allocator::{Allocator, NodePtr}; -#[derive(FromClvm, ToClvm, Debug)] -#[clvm(tuple)] -pub struct SingletonStruct { - pub mod_hash: Bytes32, - pub launcher_id: Bytes32, - pub launcher_puzzle_hash: Bytes32, -} - -#[derive(FromClvm, ToClvm, Debug)] -#[clvm(curry)] -pub struct SingletonArgs { - pub singleton_struct: SingletonStruct, - pub inner_puzzle: I, -} - -#[derive(FromClvm, ToClvm, Debug)] -#[clvm(list)] -pub struct LineageProof { - pub parent_parent_coin_id: Bytes32, - pub parent_inner_puzzle_hash: Bytes32, - pub parent_amount: u64, -} - -#[derive(FromClvm, ToClvm, Debug)] -#[clvm(list)] -pub struct SingletonSolution { - pub lineage_proof: LineageProof, - pub amount: u64, - pub inner_solution: I, -} - // TODO: replace this with a generic function to compute the hash of curried // puzzles const OP_QUOTE: u8 = 1; @@ -107,6 +79,11 @@ pub fn fast_forward_singleton( let singleton = CurriedProgram::>::from_clvm(a, puzzle)?; let mut new_solution = SingletonSolution::::from_clvm(a, solution)?; + let lineage_proof = match &mut new_solution.lineage_proof { + Proof::Lineage(proof) => proof, + _ => return Err(Error::ExpectedLineageProof), + }; + // this is the tree hash of the singleton top layer puzzle // the tree hash of singleton_top_layer_v1_1.clsp if singleton.args.singleton_struct.mod_hash.as_ref() != SINGLETON_TOP_LAYER_PUZZLE_HASH { @@ -129,16 +106,16 @@ pub fn fast_forward_singleton( // given the parent's parent, the parent's inner puzzle and parent's amount, // we can compute the hash of the curried inner puzzle for our parent coin let parent_puzzle_hash = curry_and_treehash( - &new_solution.lineage_proof.parent_inner_puzzle_hash, + &lineage_proof.parent_inner_puzzle_hash, &singleton.args.singleton_struct, ); // now that we know the parent coin's puzzle hash, we have all the pieces to // compute the coin being spent (before the fast-forward). let parent_coin = Coin { - parent_coin_info: new_solution.lineage_proof.parent_parent_coin_id, + parent_coin_info: lineage_proof.parent_parent_coin_id, puzzle_hash: parent_puzzle_hash, - amount: new_solution.lineage_proof.parent_amount, + amount: lineage_proof.parent_amount, }; if parent_coin.coin_id() != coin.parent_coin_info { @@ -146,7 +123,7 @@ pub fn fast_forward_singleton( } let inner_puzzle_hash = tree_hash(a, singleton.args.inner_puzzle); - if inner_puzzle_hash != *new_solution.lineage_proof.parent_inner_puzzle_hash { + if inner_puzzle_hash != *lineage_proof.parent_inner_puzzle_hash { return Err(Error::InnerPuzzleHashMismatch); } @@ -160,8 +137,8 @@ pub fn fast_forward_singleton( } // update the solution to use the new parent coin's information - new_solution.lineage_proof.parent_parent_coin_id = new_parent.parent_coin_info; - new_solution.lineage_proof.parent_amount = new_parent.amount; + lineage_proof.parent_parent_coin_id = new_parent.parent_coin_info; + lineage_proof.parent_amount = new_parent.amount; new_solution.amount = new_coin.amount; let expected_new_parent = new_parent.coin_id(); @@ -362,7 +339,9 @@ mod tests { 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") + let solution = SingletonSolution::from_clvm(a, new_solution).expect("parse solution"); + assert!(matches!(solution.lineage_proof, Proof::Lineage(_))); + solution } fn serialize_solution(a: &mut Allocator, solution: &SingletonSolution) -> Vec { @@ -392,8 +371,12 @@ mod tests { |a, _coin, _new_coin, _new_parent, _puzzle, solution| { let mut new_solution = parse_solution(a, solution); + let Proof::Lineage(lineage_proof) = &mut new_solution.lineage_proof else { + unreachable!(); + }; + // corrupt the lineage proof - new_solution.lineage_proof.parent_parent_coin_id = Bytes32::from(hex!( + lineage_proof.parent_parent_coin_id = Bytes32::from(hex!( "fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe" )); @@ -409,8 +392,12 @@ mod tests { |a, _coin, _new_coin, _new_parent, _puzzle, solution| { let mut new_solution = parse_solution(a, solution); + let Proof::Lineage(lineage_proof) = &mut new_solution.lineage_proof else { + unreachable!(); + }; + // corrupt the lineage proof - new_solution.lineage_proof.parent_amount = 11; + lineage_proof.parent_amount = 11; *solution = serialize_solution(a, &new_solution); }, @@ -424,8 +411,12 @@ mod tests { |a, _coin, _new_coin, _new_parent, _puzzle, solution| { let mut new_solution = parse_solution(a, solution); + let Proof::Lineage(lineage_proof) = &mut new_solution.lineage_proof else { + unreachable!(); + }; + // corrupt the lineage proof - new_solution.lineage_proof.parent_inner_puzzle_hash = Bytes32::from(hex!( + lineage_proof.parent_inner_puzzle_hash = Bytes32::from(hex!( "fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe" )); @@ -442,25 +433,29 @@ mod tests { let mut new_solution = parse_solution(a, solution); let singleton = parse_singleton(a, puzzle); + let Proof::Lineage(lineage_proof) = &mut new_solution.lineage_proof else { + unreachable!(); + }; + // corrupt the lineage proof - new_solution.lineage_proof.parent_inner_puzzle_hash = Bytes32::from(hex!( + lineage_proof.parent_inner_puzzle_hash = Bytes32::from(hex!( "fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe" )); // adjust the coins puzzle hashes to match let parent_puzzle_hash = curry_and_treehash( - &new_solution.lineage_proof.parent_inner_puzzle_hash, + &lineage_proof.parent_inner_puzzle_hash, &singleton.args.singleton_struct, ); - *solution = serialize_solution(a, &new_solution); - *new_parent = Coin { - parent_coin_info: new_solution.lineage_proof.parent_parent_coin_id, + parent_coin_info: lineage_proof.parent_parent_coin_id, puzzle_hash: parent_puzzle_hash, - amount: new_solution.lineage_proof.parent_amount, + amount: lineage_proof.parent_amount, }; + *solution = serialize_solution(a, &new_solution); + new_coin.puzzle_hash = parent_puzzle_hash; coin.parent_coin_info = new_parent.coin_id(); diff --git a/crates/chia-tools/src/bin/run-spend.rs b/crates/chia-tools/src/bin/run-spend.rs index 54afdc05e..4666edb2c 100644 --- a/crates/chia-tools/src/bin/run-spend.rs +++ b/crates/chia-tools/src/bin/run-spend.rs @@ -1,5 +1,6 @@ use chia_consensus::gen::conditions::Condition; use chia_traits::Streamable; +use chia_wallet::Proof; use clap::Parser; use clvm_traits::{FromClvm, ToNodePtr}; use clvm_utils::tree_hash; @@ -10,7 +11,6 @@ use chia_wallet::cat::{CatArgs, CatSolution, CAT_PUZZLE_HASH}; use chia_wallet::did::{DidArgs, DidSolution, DID_INNER_PUZZLE_HASH}; use chia_wallet::singleton::{SingletonArgs, SingletonSolution, SINGLETON_TOP_LAYER_PUZZLE_HASH}; use chia_wallet::standard::{StandardArgs, StandardSolution, STANDARD_PUZZLE_HASH}; -use chia_wallet::Proof; /// Run a puzzle given a solution and print the resulting conditions #[derive(Parser, Debug)] @@ -238,7 +238,7 @@ fn print_puzzle_info(a: &Allocator, puzzle: NodePtr, solution: NodePtr) { return; }; println!(" solution"); - match sol.proof { + match sol.lineage_proof { Proof::Lineage(lp) => { println!(" lineage-proof: {lp:?}"); } diff --git a/crates/chia-wallet/src/proof.rs b/crates/chia-wallet/src/proof.rs index 993e28354..7d41f8c17 100644 --- a/crates/chia-wallet/src/proof.rs +++ b/crates/chia-wallet/src/proof.rs @@ -13,9 +13,9 @@ pub enum Proof { #[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))] #[clvm(list)] pub struct LineageProof { - pub parent_coin_info: Bytes32, - pub inner_puzzle_hash: Bytes32, - pub amount: u64, + pub parent_parent_coin_id: Bytes32, + pub parent_inner_puzzle_hash: Bytes32, + pub parent_amount: u64, } #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] diff --git a/crates/chia-wallet/src/puzzles/singleton.rs b/crates/chia-wallet/src/puzzles/singleton.rs index b44bfd13a..8f5d9bdd2 100644 --- a/crates/chia-wallet/src/puzzles/singleton.rs +++ b/crates/chia-wallet/src/puzzles/singleton.rs @@ -22,7 +22,7 @@ pub struct SingletonStruct { #[derive(Debug, Clone, PartialEq, Eq, ToClvm, FromClvm)] #[clvm(list)] pub struct SingletonSolution { - pub proof: Proof, + pub lineage_proof: Proof, pub amount: u64, pub inner_solution: I, }