Skip to content

Commit

Permalink
Tysm Yak
Browse files Browse the repository at this point in the history
  • Loading branch information
Rigidity committed Jan 12, 2025
1 parent e889136 commit e066edb
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 73 deletions.
4 changes: 2 additions & 2 deletions crates/chia-sdk-types/src/puzzles/vault/restrictions.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
mod force_1_of_2_restricted_variable;
mod force_assert_coin_announcement;
mod force_coin_message;
mod recovery;
mod timelock;

pub use force_1_of_2_restricted_variable::*;
pub use force_assert_coin_announcement::*;
pub use force_coin_message::*;
pub use recovery::*;
pub use timelock::*;

use clvm_traits::{FromClvm, ToClvm};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::RESTRICTIONS_PUZZLE_HASH;

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[clvm(curry)]
pub struct Force1of2RestrictedVariable {
pub struct Recovery {
pub delegated_puzzle_feeder_mod_hash: Bytes32,
pub one_of_n_mod_hash: Bytes32,
pub left_side_subtree_hash_hash: Bytes32,
Expand All @@ -20,7 +20,7 @@ pub struct Force1of2RestrictedVariable {
pub delegated_puzzle_validator_list_hash: Bytes32,
}

impl Force1of2RestrictedVariable {
impl Recovery {
pub fn new(
left_side_subtree_hash: Bytes32,
nonce: usize,
Expand All @@ -40,51 +40,53 @@ impl Force1of2RestrictedVariable {
}
}

impl Mod for Force1of2RestrictedVariable {
const MOD_REVEAL: &[u8] = &FORCE_1_OF_2_RESTRICTED_VARIABLE_PUZZLE;
const MOD_HASH: TreeHash = FORCE_1_OF_2_RESTRICTED_VARIABLE_PUZZLE_HASH;
impl Mod for Recovery {
const MOD_REVEAL: &[u8] = &RECOVERY_PUZZLE;
const MOD_HASH: TreeHash = RECOVERY_PUZZLE_HASH;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ToClvm, FromClvm)]
#[clvm(solution)]
pub struct Force1of2RestrictedVariableSolution {
pub struct RecoverySolution {
pub new_right_side_member_hash: Bytes32,
}

impl Force1of2RestrictedVariableSolution {
impl RecoverySolution {
pub fn new(new_right_side_member_hash: Bytes32) -> Self {
Self {
new_right_side_member_hash,
}
}
}

pub const FORCE_1_OF_2_RESTRICTED_VARIABLE_PUZZLE: [u8; 650] = hex!(
pub const RECOVERY_PUZZLE: [u8; 706] = hex!(
"
ff02ffff01ff02ffff03ffff02ff12ffff04ff02ffff04ff8205ffffff04ffff
02ff16ffff04ff02ffff04ff2fffff04ffff0bff18ff5f80ffff04ffff02ff16
ffff04ff02ffff04ff05ffff04ffff02ff16ffff04ff02ffff04ff0bffff04ff
ff0bff18ffff0bff14ff17ffff0bff18ffff02ff16ffff04ff02ffff04ff2fff
ff04ffff0bff18ff5f80ffff04ffff02ff16ffff04ff02ffff04ff81bfffff04
ff02ffff01ff02ffff03ffff02ff3affff04ff02ffff04ff8205ffffff04ffff
02ff2effff04ff02ffff04ff2fffff04ffff0bff2cff5f80ffff04ffff02ff2e
ffff04ff02ffff04ff05ffff04ffff02ff2effff04ff02ffff04ff0bffff04ff
ff0bff2cffff0bff12ff17ffff0bff2cffff02ff2effff04ff02ffff04ff2fff
ff04ffff0bff2cff5f80ffff04ffff02ff2effff04ff02ffff04ff81bfffff04
ff82017fffff04ff8202ffffff04ff820bffff80808080808080ff8080808080
80808080ff8080808080ff8080808080ff808080808080ff8080808080ffff01
8205ffffff01ff088080ff0180ffff04ffff01ffffff3301ff02ff02ffff03ff
05ffff01ff0bff7affff02ff1effff04ff02ffff04ff09ffff04ffff02ff1cff
ff04ff02ffff04ff0dff80808080ff808080808080ffff016a80ff0180ffffff
02ffff03ff05ffff01ff02ffff03ffff02ffff03ffff09ff11ff1080ffff01ff
02ffff03ffff20ffff09ff29ff0b8080ffff01ff0101ff8080ff0180ff8080ff
0180ffff01ff0880ffff01ff02ff12ffff04ff02ffff04ff0dffff04ff0bff80
8080808080ff0180ffff01ff010180ff0180ffffa04bf5122f344554c53bde2e
bb8cd2b7e3d1600ad631c385a5d7cce23c7785459aa09dcf97a184f32623d11a
73124ceb99a5709b083721e878a16d78f596718ba7b2ffa102a12871fee210fb
8619291eaea194581cbd2531e4b23759d225f6806923f63222a102a8d5dd63fb
a471ebcb1f3e8f7c1e1879b7152a6e7298a91ce119a63400ade7c5ffff0bff5a
ffff02ff1effff04ff02ffff04ff05ffff04ffff02ff1cffff04ff02ffff04ff
07ff80808080ff808080808080ff0bff14ffff0bff14ff6aff0580ffff0bff14
ff0bff4a8080ff018080
8205ffffff01ff088080ff0180ffff04ffff01ffffff333cff3eff0142ffff02
ffff02ffff03ff05ffff01ff0bff76ffff02ff3effff04ff02ffff04ff09ffff
04ffff02ff2affff04ff02ffff04ff0dff80808080ff808080808080ffff0166
80ff0180ff02ffff03ff05ffff01ff02ffff03ffff20ffff21ffff09ff11ff3c
80ffff09ff11ff1480ffff09ff11ff18808080ffff01ff02ffff03ffff02ffff
03ffff09ff11ff1080ffff01ff02ffff03ffff20ffff09ff29ff0b8080ffff01
ff0101ff8080ff0180ff8080ff0180ffff01ff0880ffff01ff02ff3affff04ff
02ffff04ff0dffff04ff0bff808080808080ff0180ffff01ff088080ff0180ff
ff01ff010180ff0180ffffffa04bf5122f344554c53bde2ebb8cd2b7e3d1600a
d631c385a5d7cce23c7785459aa09dcf97a184f32623d11a73124ceb99a5709b
083721e878a16d78f596718ba7b2ffa102a12871fee210fb8619291eaea19458
1cbd2531e4b23759d225f6806923f63222a102a8d5dd63fba471ebcb1f3e8f7c
1e1879b7152a6e7298a91ce119a63400ade7c5ffff0bff56ffff02ff3effff04
ff02ffff04ff05ffff04ffff02ff2affff04ff02ffff04ff07ff80808080ff80
8080808080ff0bff12ffff0bff12ff66ff0580ffff0bff12ff0bff468080ff01
8080
"
);

pub const FORCE_1_OF_2_RESTRICTED_VARIABLE_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
"4f7bc8f30deb6dad75a1e29ceacb67fd0fe0eda79173e45295ff2cfbb8de53c6"
pub const RECOVERY_PUZZLE_HASH: TreeHash = TreeHash::new(hex!(
"c570d1c054e7907c00aa2e9a004298af040baf8ac9f2aa1fbdc7840f6e134848"
));
151 changes: 123 additions & 28 deletions napi/__test__/vaults.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import test from "ava";
import {
childVault,
ClvmAllocator,
force1Of2RestrictedVariable,
customMemberHash,
k1MemberHash,
K1SecretKey,
K1Signature,
MemberConfig,
mOfNHash,
recoveryRestriction,
sha256,
Simulator,
Spend,
timelockRestriction,
toCoinId,
Vault,
VaultSpend,
Expand Down Expand Up @@ -347,54 +349,147 @@ test("single signer recovery vault", (t) => {
const sim = new Simulator();
const clvm = new ClvmAllocator();

const k1 = sim.k1Pair(1);
const recovery = sim.k1Pair(2);
const custodyKey = sim.k1Pair(1);
const recoveryKey = sim.k1Pair(2);

// Initial vault
const config: MemberConfig = {
topLevel: false,
nonce: 0,
restrictions: [],
};

const recoveryPathHash = k1MemberHash(config, recovery.publicKey, false);
const memberHash = k1MemberHash(config, k1.publicKey, false);
const memberHash = k1MemberHash(config, custodyKey.publicKey, false);

const topLevelConfig: MemberConfig = {
topLevel: true,
nonce: 0,
restrictions: [
force1Of2RestrictedVariable(
recoveryPathHash,
0,
clvm.nil().treeHash(),
clvm.nil().treeHash()
),
],
};
const timelock = timelockRestriction(1n);
const recovery = recoveryRestriction(
memberHash,
0,
clvm.alloc([timelock.puzzleHash]).treeHash(),
clvm.nil().treeHash()
);
const initialRecoveryHash = k1MemberHash(
{
...config,
restrictions: [recovery],
},
recoveryKey.publicKey,
false
);

const vault = mintVault(
let vault = mintVault(
sim,
clvm,
mOfNHash(topLevelConfig, 1, [recoveryPathHash, memberHash])
mOfNHash({ ...config, topLevel: true }, 1, [
memberHash,
initialRecoveryHash,
])
);

const delegatedSpend = clvm.delegatedSpendForConditions([
let delegatedSpend = clvm.delegatedSpendForConditions([
clvm.createCoin(vault.custodyHash, vault.coin.amount, null),
]);

const signature = signK1(clvm, k1.secretKey, vault, delegatedSpend, false);
let vaultSpend = new VaultSpend(delegatedSpend, vault.coin);
vaultSpend.spendMOfN({ ...config, topLevel: true }, 1, [
memberHash,
initialRecoveryHash,
]);
vaultSpend.spendK1(
clvm,
config,
custodyKey.publicKey,
signK1(clvm, custodyKey.secretKey, vault, delegatedSpend, false),
false
);
clvm.spendVault(vault, vaultSpend);

const vaultSpend = new VaultSpend(delegatedSpend, vault.coin);
vaultSpend.spendForce1Of2RestrictedVariable(
sim.spend(clvm.coinSpends(), []);

// Initiate recovery
const oldCustodyHash = vault.custodyHash;
const recoveryDelegatedSpend: Spend = {
puzzle: clvm.nil(),
solution: clvm.nil(),
};

const recoveryFinishMemberSpend = clvm.delegatedSpendForConditions([
clvm.createCoin(oldCustodyHash, vault.coin.amount, null),
clvm.assertSecondsRelative(1n),
]);
const recoveryFinishMemberHash = customMemberHash(
{ ...config, restrictions: [timelock] },
recoveryFinishMemberSpend.puzzle.treeHash()
);

const custodyHash = mOfNHash({ ...config, topLevel: true }, 1, [
memberHash,
recoveryFinishMemberHash,
]);

delegatedSpend = clvm.delegatedSpendForConditions([
clvm.createCoin(custodyHash, vault.coin.amount, null),
]);

vault = childVault(vault, vault.custodyHash);
vaultSpend = new VaultSpend(delegatedSpend, vault.coin);
vaultSpend.spendRecoveryRestriction(
clvm,
recoveryPathHash,
memberHash,
0,
clvm.alloc([timelock.puzzleHash]).treeHash(),
clvm.nil().treeHash(),
clvm.nil().treeHash(),
memberHash
recoveryFinishMemberHash
);
vaultSpend.spendMOfN({ ...config, topLevel: true }, 1, [
memberHash,
initialRecoveryHash,
]);
vaultSpend.spendK1(
clvm,
{ ...config, restrictions: [recovery] },
recoveryKey.publicKey,
signK1(clvm, recoveryKey.secretKey, vault, delegatedSpend, false),
false
);
clvm.spendVault(vault, vaultSpend);

sim.spend(clvm.coinSpends(), []);

// Finish recovery
vault = childVault(vault, custodyHash);
vaultSpend = new VaultSpend(recoveryDelegatedSpend, vault.coin);
vaultSpend.spendMOfN({ ...config, topLevel: true }, 1, [
memberHash,
recoveryFinishMemberHash,
]);
vaultSpend.spendCustomMember(
clvm,
{ ...config, restrictions: [timelock] },
recoveryFinishMemberSpend
);
vaultSpend.spendTimelockRestriction(clvm, 1n);
clvm.spendVault(vault, vaultSpend);

sim.spend(clvm.coinSpends(), []);

// Make sure the vault is spendable after recovery
vault = childVault(vault, oldCustodyHash);
delegatedSpend = clvm.delegatedSpendForConditions([
clvm.createCoin(vault.custodyHash, vault.coin.amount, null),
]);
vaultSpend = new VaultSpend(delegatedSpend, vault.coin);
vaultSpend.spendMOfN({ ...config, topLevel: true }, 1, [
memberHash,
initialRecoveryHash,
]);
vaultSpend.spendK1(
clvm,
config,
custodyKey.publicKey,
signK1(clvm, custodyKey.secretKey, vault, delegatedSpend, false),
false
);
vaultSpend.spendMOfN(topLevelConfig, 1, [recoveryPathHash, memberHash]);
vaultSpend.spendK1(clvm, config, k1.publicKey, signature, false);
clvm.spendVault(vault, vaultSpend);

sim.spend(clvm.coinSpends(), []);
Expand Down
10 changes: 8 additions & 2 deletions napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,10 @@ export declare function mOfNHash(config: MemberConfig, required: number, items:
export declare function k1MemberHash(config: MemberConfig, publicKey: K1PublicKey, fastForward: boolean): Uint8Array
export declare function r1MemberHash(config: MemberConfig, publicKey: R1PublicKey, fastForward: boolean): Uint8Array
export declare function singletonMemberHash(config: MemberConfig, launcherId: Uint8Array): Uint8Array
export declare function force1Of2RestrictedVariable(leftSideSubtreeHash: Uint8Array, nonce: number, memberValidatorListHash: Uint8Array, delegatedPuzzleValidatorListHash: Uint8Array): Restriction
export declare function fixedMemberHash(config: MemberConfig, fixedPuzzleHash: Uint8Array): Uint8Array
export declare function customMemberHash(config: MemberConfig, innerHash: Uint8Array): Uint8Array
export declare function recoveryRestriction(leftSideSubtreeHash: Uint8Array, nonce: number, memberValidatorListHash: Uint8Array, delegatedPuzzleValidatorListHash: Uint8Array): Restriction
export declare function timelockRestriction(timelock: bigint): Restriction
export declare class SecretKey {
static fromSeed(seed: Uint8Array): SecretKey
static fromBytes(bytes: Uint8Array): SecretKey
Expand Down Expand Up @@ -451,7 +454,10 @@ export declare class VaultSpend {
spendK1(clvm: ClvmAllocator, config: MemberConfig, publicKey: K1PublicKey, signature: K1Signature, fastForward: boolean): void
spendR1(clvm: ClvmAllocator, config: MemberConfig, publicKey: R1PublicKey, signature: R1Signature, fastForward: boolean): void
spendSingleton(clvm: ClvmAllocator, config: MemberConfig, launcherId: Uint8Array, singletonInnerPuzzleHash: Uint8Array, singletonAmount: bigint): void
spendForce1Of2RestrictedVariable(clvm: ClvmAllocator, leftSideSubtreeHash: Uint8Array, nonce: number, memberValidatorListHash: Uint8Array, delegatedPuzzleValidatorListHash: Uint8Array, newRightSideMemberHash: Uint8Array): void
spendFixedPuzzle(clvm: ClvmAllocator, config: MemberConfig, fixedPuzzleHash: Uint8Array): void
spendCustomMember(clvm: ClvmAllocator, config: MemberConfig, spend: Spend): void
spendRecoveryRestriction(clvm: ClvmAllocator, leftSideSubtreeHash: Uint8Array, nonce: number, memberValidatorListHash: Uint8Array, delegatedPuzzleValidatorListHash: Uint8Array, newRightSideMemberHash: Uint8Array): void
spendTimelockRestriction(clvm: ClvmAllocator, timelock: bigint): void
}

/* auto-generated by `pnpm run update-declarations` */
Expand Down
7 changes: 5 additions & 2 deletions napi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { SecretKey, PublicKey, Signature, mnemonicFromEntropy, mnemonicToEntropy, verifyMnemonic, randomBytes, generateMnemonic, mnemonicToSeed, ClvmAllocator, curryTreeHash, intToSignedBytes, signedBytesToInt, toCoinId, Tls, Peer, Program, K1SecretKey, K1PublicKey, K1Signature, R1SecretKey, R1PublicKey, R1Signature, Simulator, compareBytes, sha256, treeHashAtom, treeHashPair, fromHexRaw, fromHex, toHex, childVault, VaultSpend, mOfNHash, k1MemberHash, r1MemberHash, singletonMemberHash, force1Of2RestrictedVariable } = nativeBinding
const { SecretKey, PublicKey, Signature, mnemonicFromEntropy, mnemonicToEntropy, verifyMnemonic, randomBytes, generateMnemonic, mnemonicToSeed, ClvmAllocator, curryTreeHash, intToSignedBytes, signedBytesToInt, toCoinId, Tls, Peer, Program, K1SecretKey, K1PublicKey, K1Signature, R1SecretKey, R1PublicKey, R1Signature, Simulator, compareBytes, sha256, treeHashAtom, treeHashPair, fromHexRaw, fromHex, toHex, childVault, VaultSpend, mOfNHash, k1MemberHash, r1MemberHash, singletonMemberHash, fixedMemberHash, customMemberHash, recoveryRestriction, timelockRestriction } = nativeBinding

module.exports.SecretKey = SecretKey
module.exports.PublicKey = PublicKey
Expand Down Expand Up @@ -349,4 +349,7 @@ module.exports.mOfNHash = mOfNHash
module.exports.k1MemberHash = k1MemberHash
module.exports.r1MemberHash = r1MemberHash
module.exports.singletonMemberHash = singletonMemberHash
module.exports.force1Of2RestrictedVariable = force1Of2RestrictedVariable
module.exports.fixedMemberHash = fixedMemberHash
module.exports.customMemberHash = customMemberHash
module.exports.recoveryRestriction = recoveryRestriction
module.exports.timelockRestriction = timelockRestriction
Loading

0 comments on commit e066edb

Please sign in to comment.