From 5fc78e0082829f7157d3c6666eb5227c2f342ced Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 12 Nov 2024 08:39:48 -0800 Subject: [PATCH 1/3] Add coin announcement restriction --- chia/_tests/clvm/test_restrictions.py | 65 ++++++++++++++++++- .../restriction_puzzles/restrictions.py | 21 ++++++ .../force_assert_coin_announcement.clsp | 16 +++++ .../force_assert_coin_announcement.clsp.hex | 1 + .../puzzles/deployed_puzzle_hashes.json | 1 + 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp create mode 100644 chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp.hex diff --git a/chia/_tests/clvm/test_restrictions.py b/chia/_tests/clvm/test_restrictions.py index 87ad68a444a9..8242a78160bf 100644 --- a/chia/_tests/clvm/test_restrictions.py +++ b/chia/_tests/clvm/test_restrictions.py @@ -11,8 +11,9 @@ from chia.types.mempool_inclusion_status import MempoolInclusionStatus from chia.util.errors import Err from chia.util.ints import uint64 +from chia.wallet.conditions import CreateCoinAnnouncement from chia.wallet.puzzles.custody.custody_architecture import DelegatedPuzzleAndSolution, PuzzleWithRestrictions -from chia.wallet.puzzles.custody.restriction_puzzles.restrictions import Timelock +from chia.wallet.puzzles.custody.restriction_puzzles.restrictions import ForceCoinAnnouncement, Timelock from chia.wallet.puzzles.custody.restriction_utilities import ValidatorStackRestriction from chia.wallet.wallet_spend_bundle import WalletSpendBundle @@ -136,3 +137,65 @@ async def test_timelock_wrapper(cost_logger: CostLogger) -> None: # memo format assertion for coverage sake assert restriction.memo(0) == Program.to([None]) + + +@pytest.mark.anyio +async def test_force_coin_announcement_wrapper(cost_logger: CostLogger) -> None: + async with sim_and_client() as (sim, client): + restriction = ValidatorStackRestriction([ForceCoinAnnouncement()]) + pwr = PuzzleWithRestrictions(0, [restriction], ACSMember()) + + # Farm and find coin + await sim.farm_block(pwr.puzzle_hash()) + coin = (await client.get_coin_records_by_puzzle_hashes([pwr.puzzle_hash()], include_spent_coins=False))[0].coin + + # Attempt to just use any old dpuz + any_old_dpuz = DelegatedPuzzleAndSolution(puzzle=Program.to((1, [[1, "foo"]])), solution=Program.to(None)) + wrapped_dpuz = restriction.modify_delegated_puzzle_and_solution(any_old_dpuz, [Program.to(None)]) + not_timelocked_attempt = WalletSpendBundle( + [ + make_spend( + coin, + pwr.puzzle_reveal(), + pwr.solve( + [], [Program.to([any_old_dpuz.puzzle.get_tree_hash()])], Program.to([[1, "bar"]]), any_old_dpuz + ), + ) + ], + G2Element(), + ) + result = await client.push_tx(not_timelocked_attempt) + assert result == (MempoolInclusionStatus.FAILED, Err.GENERATOR_RUNTIME_ERROR) + + # Now actually put a coin announcement in the dpuz + announcement = CreateCoinAnnouncement(bytes32.zeros, coin_id=coin.name()) + announcement_dpuz = DelegatedPuzzleAndSolution( + puzzle=Program.to( + (1, [announcement.to_program(), announcement.corresponding_assertion().to_program(), [1, "foo"]]) + ), + solution=Program.to(None), + ) + wrapped_dpuz = restriction.modify_delegated_puzzle_and_solution(announcement_dpuz, [Program.to(None)]) + sb = cost_logger.add_cost( + "Minimal puzzle with restrictions w/ coin announcement forcing wrapper", + WalletSpendBundle( + [ + make_spend( + coin, + pwr.puzzle_reveal(), + pwr.solve( + [], + [Program.to([announcement_dpuz.puzzle.get_tree_hash()])], + Program.to([[1, "bar"]]), + wrapped_dpuz, + ), + ) + ], + G2Element(), + ), + ) + result = await client.push_tx(sb) + assert result == (MempoolInclusionStatus.SUCCESS, None) + + # memo format assertion for coverage sake + assert restriction.memo(0) == Program.to([None]) diff --git a/chia/wallet/puzzles/custody/restriction_puzzles/restrictions.py b/chia/wallet/puzzles/custody/restriction_puzzles/restrictions.py index 1c14fea64858..2edf9c70068b 100644 --- a/chia/wallet/puzzles/custody/restriction_puzzles/restrictions.py +++ b/chia/wallet/puzzles/custody/restriction_puzzles/restrictions.py @@ -10,6 +10,11 @@ TIMELOCK_WRAPPER = load_clvm_maybe_recompile( "timelock.clsp", package_or_requirement="chia.wallet.puzzles.custody.restriction_puzzles.wrappers" ) +FORCE_COIN_ANNOUNCEMENT_WRAPPER = load_clvm_maybe_recompile( + "force_assert_coin_announcement.clsp", + package_or_requirement="chia.wallet.puzzles.custody.restriction_puzzles.wrappers", +) +FORCE_COIN_ANNOUNCEMENT_WRAPPER_HASH = FORCE_COIN_ANNOUNCEMENT_WRAPPER.get_tree_hash() @dataclass(frozen=True) @@ -28,3 +33,19 @@ def puzzle(self, nonce: int) -> Program: def puzzle_hash(self, nonce: int) -> bytes32: return self.puzzle(nonce).get_tree_hash() + + +@dataclass(frozen=True) +class ForceCoinAnnouncement: + @property + def member_not_dpuz(self) -> bool: + return False + + def memo(self, nonce: int) -> Program: + return Program.to(None) + + def puzzle(self, nonce: int) -> Program: + return FORCE_COIN_ANNOUNCEMENT_WRAPPER + + def puzzle_hash(self, nonce: int) -> bytes32: + return FORCE_COIN_ANNOUNCEMENT_WRAPPER_HASH diff --git a/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp new file mode 100644 index 000000000000..ddbe7e96d576 --- /dev/null +++ b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp @@ -0,0 +1,16 @@ +(mod + ( + Conditions + ) + + (include condition_codes.clib) + + (defun check_conditions (conditions) + (if (= (f (f conditions)) ASSERT_COIN_ANNOUNCEMENT) + conditions + (c (f conditions) (check_conditions (r conditions))) + ) + ) + + (check_conditions Conditions) +) diff --git a/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp.hex b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp.hex new file mode 100644 index 000000000000..3599cc8138d8 --- /dev/null +++ b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp.hex @@ -0,0 +1 @@ +ff02ffff01ff02ff06ffff04ff02ffff04ff05ff80808080ffff04ffff01ff3dff02ffff03ffff09ff11ff0480ffff0105ffff01ff04ff09ffff02ff06ffff04ff02ffff04ff0dff808080808080ff0180ff018080 diff --git a/chia/wallet/puzzles/deployed_puzzle_hashes.json b/chia/wallet/puzzles/deployed_puzzle_hashes.json index 036a8c37ec57..07073f4aae51 100644 --- a/chia/wallet/puzzles/deployed_puzzle_hashes.json +++ b/chia/wallet/puzzles/deployed_puzzle_hashes.json @@ -33,6 +33,7 @@ "everything_with_signature": "1720d13250a7c16988eaf530331cefa9dd57a76b2c82236bec8bbbff91499b89", "exigent_metadata_layer": "d5fd32e069fda83e230ccd8f6a7c4f652231aed5c755514b3d996cbeff4182b8", "flag_proofs_checker": "fe2e3c631562fbb9be095297f762bf573705a0197164e9361ad5d50e045ba241", + "force_assert_coin_announcement": "ca0daca027e5ebd4a61fad7e32cfe1e984ad5b561c2fc08dea30accf3a191fab", "genesis_by_coin_id": "493afb89eed93ab86741b2aa61b8f5de495d33ff9b781dfc8919e602b2afa150", "genesis_by_coin_id_or_singleton": "40170305e3a71c3e7523f37fbcfc3188f9f949da0818a6331f28251e76e8c56f", "genesis_by_puzzle_hash": "de5a6e06d41518be97ff6365694f4f89475dda773dede267caa33da63b434e36", From bcfac76c8fd49732c2999fe339707cd3aed97b55 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 13 Nov 2024 07:16:45 -0800 Subject: [PATCH 2/3] Add comments per @matt-o-how --- .../wrappers/force_assert_coin_announcement.clsp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp index ddbe7e96d576..dc50dcffb38d 100644 --- a/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp +++ b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp @@ -1,3 +1,7 @@ +; This is a restriction on delegated puzzles intended the force a coin announcement +; +; The idea behind enforcing this is that it makes the current coin spend non-replayable since it will be implictly +; asserting that a specific coin ID is spent in tandem. (mod ( Conditions @@ -6,6 +10,7 @@ (include condition_codes.clib) (defun check_conditions (conditions) + ; If we run out of conditions without finding an assertion, this will raise with "path into atom" (if (= (f (f conditions)) ASSERT_COIN_ANNOUNCEMENT) conditions (c (f conditions) (check_conditions (r conditions))) From 88f0e701ac4fdb085421e354d3bfba146d9f2ea0 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 13 Nov 2024 07:20:16 -0800 Subject: [PATCH 3/3] typo --- .../wrappers/force_assert_coin_announcement.clsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp index dc50dcffb38d..6f1555b516f8 100644 --- a/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp +++ b/chia/wallet/puzzles/custody/restriction_puzzles/wrappers/force_assert_coin_announcement.clsp @@ -1,4 +1,4 @@ -; This is a restriction on delegated puzzles intended the force a coin announcement +; This is a restriction on delegated puzzles intended to force a coin announcement ; ; The idea behind enforcing this is that it makes the current coin spend non-replayable since it will be implictly ; asserting that a specific coin ID is spent in tandem.