use chik_protocol::Bytes32;
use chik_puzzle_types::{
did::{DidArgs, DidSolution},
singleton::SingletonStruct,
};
use chik_puzzles::{DID_INNERPUZ_HASH, SINGLETON_LAUNCHER_HASH, SINGLETON_TOP_LAYER_V1_1_HASH};
use klvm_traits::{FromKlvm, ToKlvm};
use klvm_utils::{ToTreeHash, TreeHash};
use klvmr::{Allocator, NodePtr};
use crate::{DriverError, Layer, Puzzle, SpendContext};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DidLayer<M, I> {
pub launcher_id: Bytes32,
pub recovery_list_hash: Option<Bytes32>,
pub num_verifications_required: u64,
pub metadata: M,
pub inner_puzzle: I,
}
impl<M, I> DidLayer<M, I> {
pub fn new(
launcher_id: Bytes32,
recovery_list_hash: Option<Bytes32>,
num_verifications_required: u64,
metadata: M,
inner_puzzle: I,
) -> Self {
Self {
launcher_id,
recovery_list_hash,
num_verifications_required,
metadata,
inner_puzzle,
}
}
pub fn with_metadata<N>(self, metadata: N) -> DidLayer<N, I> {
DidLayer {
launcher_id: self.launcher_id,
recovery_list_hash: self.recovery_list_hash,
num_verifications_required: self.num_verifications_required,
metadata,
inner_puzzle: self.inner_puzzle,
}
}
}
impl<M, I> Layer for DidLayer<M, I>
where
I: Layer,
M: ToKlvm<Allocator> + FromKlvm<Allocator>,
{
type Solution = DidSolution<I::Solution>;
fn parse_puzzle(allocator: &Allocator, puzzle: Puzzle) -> Result<Option<Self>, DriverError> {
let Some(puzzle) = puzzle.as_curried() else {
return Ok(None);
};
if puzzle.mod_hash != DID_INNERPUZ_HASH.into() {
return Ok(None);
}
let args = DidArgs::<NodePtr, M>::from_klvm(allocator, puzzle.args)?;
if args.singleton_struct.mod_hash != SINGLETON_TOP_LAYER_V1_1_HASH.into()
|| args.singleton_struct.launcher_puzzle_hash != SINGLETON_LAUNCHER_HASH.into()
{
return Err(DriverError::InvalidSingletonStruct);
}
let Some(inner_puzzle) =
I::parse_puzzle(allocator, Puzzle::parse(allocator, args.inner_puzzle))?
else {
return Ok(None);
};
Ok(Some(Self {
launcher_id: args.singleton_struct.launcher_id,
recovery_list_hash: args.recovery_list_hash,
num_verifications_required: args.num_verifications_required,
metadata: args.metadata,
inner_puzzle,
}))
}
fn parse_solution(
allocator: &Allocator,
solution: NodePtr,
) -> Result<Self::Solution, DriverError> {
match DidSolution::<NodePtr>::from_klvm(allocator, solution)? {
DidSolution::Spend(inner_solution) => {
let inner_solution = I::parse_solution(allocator, inner_solution)?;
Ok(DidSolution::Spend(inner_solution))
}
DidSolution::Recover(recovery) => Ok(DidSolution::Recover(recovery)),
}
}
fn construct_puzzle(&self, ctx: &mut SpendContext) -> Result<NodePtr, DriverError> {
let inner_puzzle = self.inner_puzzle.construct_puzzle(ctx)?;
ctx.curry(DidArgs::new(
inner_puzzle,
self.recovery_list_hash,
self.num_verifications_required,
SingletonStruct::new(self.launcher_id),
&self.metadata,
))
}
fn construct_solution(
&self,
ctx: &mut SpendContext,
solution: Self::Solution,
) -> Result<NodePtr, DriverError> {
match solution {
DidSolution::Spend(inner_solution) => {
let inner_solution = self.inner_puzzle.construct_solution(ctx, inner_solution)?;
Ok(ctx.alloc(&DidSolution::Spend(inner_solution))?)
}
DidSolution::Recover(recovery) => {
Ok(ctx.alloc(&DidSolution::<NodePtr>::Recover(recovery))?)
}
}
}
}
impl<M, I> ToTreeHash for DidLayer<M, I>
where
M: ToTreeHash,
I: ToTreeHash,
{
fn tree_hash(&self) -> TreeHash {
let inner_puzzle_hash = self.inner_puzzle.tree_hash();
let metadata_hash = self.metadata.tree_hash();
DidArgs::curry_tree_hash(
inner_puzzle_hash,
self.recovery_list_hash,
self.num_verifications_required,
SingletonStruct::new(self.launcher_id),
metadata_hash,
)
}
}