diff --git a/chia/_tests/wallet/test_conditions.py b/chia/_tests/wallet/test_conditions.py index 8292a5b31b4c..57f36ed49edd 100644 --- a/chia/_tests/wallet/test_conditions.py +++ b/chia/_tests/wallet/test_conditions.py @@ -48,8 +48,10 @@ CreateCoin, CreateCoinAnnouncement, CreatePuzzleAnnouncement, + ReceiveMessage, Remark, ReserveFee, + SendMessage, Softfork, Timelock, UnknownCondition, @@ -151,6 +153,18 @@ def test_completeness() -> None: [AMT, ["81f6", "a0" + HASH_HEX]], ), ConditionSerializations(ConditionOpcode.REMARK, Program.to([]), ["rest"], ["80"]), + ConditionSerializations( + ConditionOpcode.SEND_MESSAGE, + Program.to([0x3F, b"foobar", Program.to(HASH)]), + ["mode", "msg", "args"], + ["63", "0x" + b"foobar".hex(), "a0" + HASH_HEX], + ), + ConditionSerializations( + ConditionOpcode.RECEIVE_MESSAGE, + Program.to([0x3F, b"foobar", Program.to(HASH)]), + ["mode", "msg", "args"], + ["63", "0x" + b"foobar".hex(), "a0" + HASH_HEX], + ), ], ) def test_condition_serialization(serializations: ConditionSerializations, abstractions: bool) -> None: @@ -307,6 +321,8 @@ def test_timelock_parsing(timelock_info: TimelockInfo) -> None: CreateAnnouncement, AssertAnnouncement, Timelock, + SendMessage, + ReceiveMessage, ], ) @pytest.mark.parametrize( @@ -359,6 +375,8 @@ def test_invalid_condition( CreateAnnouncement, AssertAnnouncement, Timelock, + SendMessage, + ReceiveMessage, ] ], prg: bytes, diff --git a/chia/wallet/conditions.py b/chia/wallet/conditions.py index 228b42085a32..1a79384aa7e0 100644 --- a/chia/wallet/conditions.py +++ b/chia/wallet/conditions.py @@ -11,7 +11,7 @@ from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.condition_opcodes import ConditionOpcode from chia.util.hash import std_hash -from chia.util.ints import uint32, uint64 +from chia.util.ints import uint8, uint32, uint64 from chia.util.streamable import Streamable, streamable _T_Condition = TypeVar("_T_Condition", bound="Condition") @@ -404,6 +404,48 @@ def from_program(cls, program: Program, puzzle_hash: Optional[bytes32] = None) - ) +@final +@streamable +@dataclass(frozen=True) +class SendMessage(Condition): + mode: uint8 + msg: bytes + args: Program + + def to_program(self) -> Program: + condition: Program = Program.to([ConditionOpcode.SEND_MESSAGE, self.mode, self.msg, self.args]) + return condition + + @classmethod + def from_program(cls, program: Program) -> SendMessage: + return cls( + uint8(program.at("rf").as_int()), + program.at("rrf").as_atom(), + program.at("rrrf"), + ) + + +@final +@streamable +@dataclass(frozen=True) +class ReceiveMessage(Condition): + mode: uint8 + msg: bytes + args: Program + + def to_program(self) -> Program: + condition: Program = Program.to([ConditionOpcode.RECEIVE_MESSAGE, self.mode, self.msg, self.args]) + return condition + + @classmethod + def from_program(cls, program: Program) -> ReceiveMessage: + return cls( + uint8(program.at("rf").as_int()), + program.at("rrf").as_atom(), + program.at("rrrf"), + ) + + @final @streamable @dataclass(frozen=True) @@ -1063,6 +1105,8 @@ def from_program(cls, program: Program) -> Timelock: ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.value: AssertCoinAnnouncement, ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT.value: CreatePuzzleAnnouncement, ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT.value: AssertPuzzleAnnouncement, + ConditionOpcode.SEND_MESSAGE.value: SendMessage, + ConditionOpcode.RECEIVE_MESSAGE.value: ReceiveMessage, ConditionOpcode.ASSERT_CONCURRENT_SPEND.value: AssertConcurrentSpend, ConditionOpcode.ASSERT_CONCURRENT_PUZZLE.value: AssertConcurrentPuzzle, ConditionOpcode.ASSERT_MY_COIN_ID.value: AssertMyCoinID, @@ -1101,6 +1145,8 @@ def from_program(cls, program: Program) -> Timelock: ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.value: AssertAnnouncement, ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT.value: CreateAnnouncement, ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT.value: AssertAnnouncement, + ConditionOpcode.SEND_MESSAGE.value: SendMessage, + ConditionOpcode.RECEIVE_MESSAGE.value: ReceiveMessage, ConditionOpcode.ASSERT_CONCURRENT_SPEND.value: AssertConcurrentSpend, ConditionOpcode.ASSERT_CONCURRENT_PUZZLE.value: AssertConcurrentPuzzle, ConditionOpcode.ASSERT_MY_COIN_ID.value: AssertMyCoinID, @@ -1156,6 +1202,7 @@ def conditions_from_json_dicts(conditions: Iterable[Dict[str, Any]]) -> List[Con raise ValueError(f"Invalid condition opcode {opcode_specified}") try: + print(condition["args"]) final_condition_list.append(CONDITION_DRIVERS[opcode].from_json_dict(condition["args"])) except Exception: condition["opcode"] = Program.to(opcode) diff --git a/chia/wallet/puzzles/condition_codes.clib b/chia/wallet/puzzles/condition_codes.clib index 19f4d3164b6e..a6a5d22b5883 100644 --- a/chia/wallet/puzzles/condition_codes.clib +++ b/chia/wallet/puzzles/condition_codes.clib @@ -34,6 +34,10 @@ ; puzzle-hash (defconstant ASSERT_CONCURRENT_PUZZLE 65) + ; mask message ... + (defconstant SEND_MESSAGE 66) + (defconstant RECEIVE_MESSAGE 67) + ; the conditions below let coins inquire about themselves (defconstant ASSERT_MY_COIN_ID 70)