Skip to content

Commit

Permalink
add bls_with_taproot puzzle and member class
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-o-how committed Nov 20, 2024
1 parent 4652380 commit f3d542f
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 1 deletion.
109 changes: 109 additions & 0 deletions chia/_tests/clvm/test_member_puzzles.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
)
from chia.wallet.puzzles.custody.member_puzzles.member_puzzles import (
BLSMember,
BLSWithTaprootMember,
FixedPuzzleMember,
PasskeyMember,
PasskeyPuzzleAssertMember,
Expand Down Expand Up @@ -119,6 +120,114 @@ async def test_bls_member(cost_logger: CostLogger) -> None:
await sim.rewind(block_height)


@pytest.mark.anyio
async def test_bls_with_taproot_member(cost_logger: CostLogger) -> None:
async with sim_and_client() as (sim, client):
delegated_puzzle = Program.to(1)
delegated_puzzle_hash = delegated_puzzle.get_tree_hash()
sk = AugSchemeMPL.key_gen(bytes.fromhex(str(0) * 64))

taproot_puzzle = Program.to([1, [60, "bah"]]) # CREATE_COIN_ANNOUNCEMENT "bah"

bls_with_taproot_member = BLSWithTaprootMember(sk.public_key(), taproot_puzzle)
bls_puzzle = PuzzleWithRestrictions(0, [], bls_with_taproot_member)
memo = PuzzleHint(
bls_puzzle.puzzle.puzzle_hash(0),
bls_puzzle.puzzle.memo(0),
)

assert bls_puzzle.memo() == Program.to(
(
bls_puzzle.spec_namespace,
[
bls_puzzle.nonce,
[],
0,
memo.to_program(),
],
)
)

# Farm and find coin
await sim.farm_block(bls_puzzle.puzzle_hash())
coin = (await client.get_coin_records_by_puzzle_hashes([bls_puzzle.puzzle_hash()], include_spent_coins=False))[
0
].coin
block_height = sim.block_height

# Create an announcements to be asserted in the delegated puzzle
announcement = CreateCoinAnnouncement(msg=b"foo", coin_id=coin.name())

# test non-taproot spend
sb = WalletSpendBundle(
[
make_spend(
coin,
bls_puzzle.puzzle_reveal(),
bls_puzzle.solve(
[],
[],
bls_with_taproot_member.solve(),
DelegatedPuzzleAndSolution(
delegated_puzzle,
Program.to(
[
announcement.to_program(),
announcement.corresponding_assertion().to_program(),
]
),
),
),
)
],
bls_with_taproot_member.sign_with_synthetic_secret_key(
sk, delegated_puzzle_hash + coin.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA
),
)
result = await client.push_tx(
cost_logger.add_cost(
"BLSMember spendbundle",
sb,
)
)
assert result == (MempoolInclusionStatus.SUCCESS, None)
await sim.farm_block()
await sim.rewind(block_height)

# test taproot spend
sb = WalletSpendBundle(
[
make_spend(
coin,
bls_puzzle.puzzle_reveal(),
bls_puzzle.solve(
[],
[],
bls_with_taproot_member.solve(True),
DelegatedPuzzleAndSolution(
delegated_puzzle,
Program.to(
[
announcement.to_program(),
announcement.corresponding_assertion().to_program(),
]
),
),
),
)
],
G2Element(), # no signature required in our test hidden_puzzle
)
result = await client.push_tx(
cost_logger.add_cost(
"BLSMember spendbundle",
sb,
)
)
assert result == (MempoolInclusionStatus.SUCCESS, None)
breakpoint()


@pytest.mark.anyio
@pytest.mark.parametrize(
"with_restrictions",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
; this puzzle follows the Managed Inner Puzzle Spec MIPS01 as a Member Puzzle
; this code offers a secure approval of a delegated puzzle passed in as a Truth to be run elsewhere

; Delegated_Puzzle_Hash is added to the solution in the above layer
; original_public_key, hidden_puzzle and solution are only for use in the taproot case
; set original_public_key to 0 for non-taproot case
(mod (SYNTHETIC_PUBLIC_KEY Delegated_Puzzle_Hash original_public_key hidden_puzzle solution)
(include condition_codes.clib)

(defmacro assert items
(if (r items)
(list if (f items) (c assert (r items)) (q . (x)))
(f items)
)
)

(defun sha256tree1
(TREE)
(if (l TREE)
(sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE)))
(sha256 1 TREE)
)
)

; "is_hidden_puzzle_correct" returns true iff the hidden puzzle is correctly encoded

(defun-inline is_hidden_puzzle_correct (SYNTHETIC_PUBLIC_KEY original_public_key hidden_puzzle)
(=
SYNTHETIC_PUBLIC_KEY
(point_add
original_public_key
(pubkey_for_exp (sha256 original_public_key (sha256tree1 hidden_puzzle)))
)
)
)

; "possibly_prepend_aggsig" is the main entry point

(if original_public_key
(assert
(is_hidden_puzzle_correct SYNTHETIC_PUBLIC_KEY original_public_key hidden_puzzle)
(a hidden_puzzle (c Delegated_Puzzle_Hash solution))
)
(list (list AGG_SIG_ME SYNTHETIC_PUBLIC_KEY Delegated_Puzzle_Hash))
)
)

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ff02ffff01ff02ffff03ff17ffff01ff02ffff03ffff09ff05ffff1dff17ffff1effff0bff17ffff02ff06ffff04ff02ffff04ff2fff8080808080808080ffff01ff02ff2fffff04ff0bff5f8080ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ff0bff80808080ff808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080
37 changes: 36 additions & 1 deletion chia/wallet/puzzles/custody/member_puzzles/member_puzzles.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass
from typing import Any

from chia_rs import G1Element
from chia_rs import AugSchemeMPL, G1Element, G2Element
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
Expand All @@ -16,14 +16,24 @@
from chia.util.hash import std_hash
from chia.wallet.puzzles.custody.custody_architecture import Puzzle
from chia.wallet.puzzles.load_clvm import load_clvm_maybe_recompile
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
calculate_synthetic_public_key,
calculate_synthetic_secret_key,
)
from chia.wallet.singleton import SINGLETON_LAUNCHER_PUZZLE_HASH, SINGLETON_TOP_LAYER_MOD_HASH

BLS_MEMBER_MOD = load_clvm_maybe_recompile(
"bls_member.clsp", package_or_requirement="chia.wallet.puzzles.custody.member_puzzles"
)

BLS_WITH_TAPROOT_MEMBER_MOD = load_clvm_maybe_recompile(
"bls_with_taproot_member.clsp", package_or_requirement="chia.wallet.puzzles.custody.member_puzzles"
)

PASSKEY_MEMBER_MOD = load_clvm_maybe_recompile(
"passkey_member.clsp", package_or_requirement="chia.wallet.puzzles.custody.member_puzzles"
)

PASSKEY_PUZZLE_ASSERT_MEMBER_MOD = load_clvm_maybe_recompile(
"passkey_member_puzzle_assert.clsp", package_or_requirement="chia.wallet.puzzles.custody.member_puzzles"
)
Expand Down Expand Up @@ -67,6 +77,31 @@ def puzzle_hash(self, nonce: int) -> bytes32:
return self.puzzle(nonce).get_tree_hash()


@dataclass(frozen=True)
class BLSWithTaprootMember(Puzzle):
public_key: G1Element
hidden_puzzle: Program # must be specified manually due to frozen class

def memo(self, nonce: int) -> Program:
return Program.to(0)

def puzzle(self, nonce: int) -> Program:
synthetic_public_key = calculate_synthetic_public_key(self.public_key, self.hidden_puzzle.get_tree_hash())
return BLS_WITH_TAPROOT_MEMBER_MOD.curry(bytes(synthetic_public_key))

def puzzle_hash(self, nonce: int) -> bytes32:
return self.puzzle(nonce).get_tree_hash()

def sign_with_synthetic_secret_key(self, original_secret_key, message) -> G2Element:
synthetic_sk = calculate_synthetic_secret_key(original_secret_key, self.hidden_puzzle.get_tree_hash())
return AugSchemeMPL.sign(synthetic_sk, message)

def solve(self, use_hidden_puzzle: bool = False, hidden_puzzle_solution=Program.to(0)) -> Program:
if use_hidden_puzzle:
return Program.to([self.public_key, self.hidden_puzzle, hidden_puzzle_solution])
return Program.to([0])


@dataclass(frozen=True)
class PasskeyMember(Puzzle):
secp_pk: bytes
Expand Down
1 change: 1 addition & 0 deletions chia/wallet/puzzles/deployed_puzzle_hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"augmented_condition": "d303eafa617bedf0bc05850dd014e10fbddf622187dc07891a2aacba9d8a93f6",
"block_program_zero": "f0a38c8efe58895ae527c65c37f700a4238504691b83990e5dd91bd8b3c30eae",
"bls_member": "21a3ae8b3ce64d41ca98d6d8df8f465c9e1bfb19ab40284a5da8479ba7fade78",
"bls_with_member_mod": "b3118b102fa68e96d90cadd72ad97957a846498aa33daa5b6781f01379315aa8",
"cat_v2": "37bef360ee858133b69d595a906dc45d01af50379dad515eb9518abb7c1d2a7a",
"chialisp_deserialisation": "94ec19077f9a34e0b11ad2456af0170f4cc03f11230ca42e3f82e6e644ac4f5d",
"conditions_banned": "e7e26a3fc3a8a35e3e9944e8107bf330e107e884afdb8c5003559b3882b6566b",
Expand Down

0 comments on commit f3d542f

Please sign in to comment.