diff --git a/chia/_tests/wallet/test_debug_spend_bundle.py b/chia/_tests/wallet/test_debug_spend_bundle.py index 2bdc31207ca4..22b129ce85ae 100644 --- a/chia/_tests/wallet/test_debug_spend_bundle.py +++ b/chia/_tests/wallet/test_debug_spend_bundle.py @@ -12,10 +12,74 @@ from chia.types.condition_opcodes import ConditionOpcode from chia.util.hash import std_hash from chia.util.ints import uint64 +from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import ( + puzzle_for_conditions, + puzzle_for_pk, + solution_for_conditions, +) from chia.wallet.util.debug_spend_bundle import debug_spend_bundle from chia.wallet.wallet_spend_bundle import WalletSpendBundle +def test_debug_messages() -> None: + sender_sk = PrivateKey.from_bytes(bytes([1] * 32)) + sender_pk = sender_sk.get_g1() + sender_puz = puzzle_for_pk(sender_pk) + sender_coin = Coin(bytes32.zeros, sender_puz.get_tree_hash(), uint64(10000)) + receiver_sk = PrivateKey.from_bytes(bytes([2] * 32)) + receiver_pk = receiver_sk.get_g1() + receiver_puz = puzzle_for_pk(receiver_pk) + receiver_coin = Coin(bytes32.zeros, receiver_puz.get_tree_hash(), uint64(10000)) + sender_conditions = Program.to( + [ + [ConditionOpcode.SEND_MESSAGE, 0b010010, b"puzhash", receiver_coin.puzzle_hash], + [ConditionOpcode.SEND_MESSAGE, 0b111111, b"coin_id", receiver_coin.name()], + [ + ConditionOpcode.SEND_MESSAGE, + 0b101101, + b"parent_amount", + receiver_coin.parent_coin_info, + receiver_coin.amount, + ], + [ConditionOpcode.SEND_MESSAGE, 0b100101, b"missing_args", receiver_coin.parent_coin_info], + [ConditionOpcode.SEND_MESSAGE, 0b100100, b"missing", receiver_coin.parent_coin_info], + [ConditionOpcode.SEND_MESSAGE, 0b001101, b"wrong_args", receiver_coin.puzzle_hash, receiver_coin.amount], + ] + ) + receiver_conditions = Program.to( + [ + [ConditionOpcode.RECEIVE_MESSAGE, 0b010010, b"puzhash", sender_coin.puzzle_hash], + [ConditionOpcode.RECEIVE_MESSAGE, 0b111111, b"coin_id", sender_coin.name()], + [ + ConditionOpcode.RECEIVE_MESSAGE, + 0b101101, + b"parent_amount", + sender_coin.parent_coin_info, + sender_coin.amount, + ], + [ConditionOpcode.RECEIVE_MESSAGE, 0b101101, b"missing_args", sender_coin.parent_coin_info], + [ConditionOpcode.RECEIVE_MESSAGE, 0b001001, b"missing", sender_coin.amount], + [ConditionOpcode.RECEIVE_MESSAGE, 0b001101, b"wrong_args", sender_coin.amount], + ] + ) + sender_sol = solution_for_conditions(sender_conditions) + sender_delegated_puzzle = puzzle_for_conditions(sender_conditions) + sender_msg = sender_delegated_puzzle.get_tree_hash() + sender_sig = AugSchemeMPL.sign(sender_sk, sender_msg) + sender_sb = WalletSpendBundle([make_spend(sender_coin, sender_puz, sender_sol)], sender_sig) + + receiver_sol = solution_for_conditions(receiver_conditions) + receiver_delegated_puzzle = puzzle_for_conditions(receiver_conditions) + receiver_msg = receiver_delegated_puzzle.get_tree_hash() + receiver_sig = AugSchemeMPL.sign(receiver_sk, receiver_msg) + receiver_sb = WalletSpendBundle([make_spend(receiver_coin, receiver_puz, receiver_sol)], receiver_sig) + + sb = WalletSpendBundle.aggregate([sender_sb, receiver_sb]) + result = StringIO() + sys.stdout = result + debug_spend_bundle(sb) + + def test_debug_spend_bundle() -> None: sk = PrivateKey.from_bytes(bytes([1] * 32)) pk = sk.get_g1() @@ -41,6 +105,7 @@ def test_debug_spend_bundle() -> None: [ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, bytes32.zeros], [ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, std_hash(coin.puzzle_hash)], [ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, b"hey"], + [ConditionOpcode.SEND_MESSAGE, 0x17, ACS, ACS_PH], ] ) @@ -70,7 +135,4 @@ def test_debug_spend_bundle() -> None: ) ) - assert ( - result.getvalue() - == '================================================================================\nconsuming coin (0x0000000000000000000000000000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000000000000000000000000000 ())\n with id f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\n\n\nbrun -y main.sym \'(a (q 2 (q . 15) (c (q 2 (q . "hey") (c (q . "now") 1)) 1)) (c (q . "brown") (c (q . "cow") 1)))\' \'()\'\n\n--- Uncurried Args ---\n- \n - Layer 1:\n - Mod hash: 507414e217dc45d6dbb923077c48641c9d2ba8430c92df9c49660480f398b133\n - "brown"\n - "cow"\n - Layer 2:\n - Mod hash: 24255ef5d941493b9978f3aabb0ed07d084ade196d23f463ff058954cbf6e9b6\n - \n - Layer 1:\n - Mod hash: 7ee93c7e6fde43b6ac75100244fd294b8247362f22724b2ef0099bf7ab083def\n - "now"\n\n*** BAD PUZZLE REVEAL\n3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 vs 0000000000000000000000000000000000000000000000000000000000000000\n********************************************************************************\n\nconsuming coin (0x0000000000000000000000000000000000000000000000000000000000000000 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 3)\n with id f61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f\n\n\nbrun -y main.sym \'(a (q 2 (q . 15) (c (q 2 (q . "hey") (c (q . "now") 1)) 1)) (c (q . "brown") (c (q . "cow") 1)))\' \'((49 0xaa1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac1 0x0000000000000000000000000000000000000000000000000000000000000000) (q) (51 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 ()) (51 0x0000000000000000000000000000000000000000000000000000000000000000 1) (51 0x0000000000000000000000000000000000000000000000000000000000000000 2 ("memo" "memo" "memo")) (60 ()) (61 0x0000000000000000000000000000000000000000000000000000000000000000) (61 0x98442bcc931fc4c94a2a1b17dafb8959a867097bd3aa73871ca9cfc8327346b4) (60 "hey") (62 ()) (63 0x0000000000000000000000000000000000000000000000000000000000000000) (63 0xfb4c79668bee67977af996dc2f42071735a819b4221d3f5c9e8e9f43c30c2bc5) (62 "hey"))\'\n\n--- Uncurried Args ---\n- \n - Layer 1:\n - Mod hash: 507414e217dc45d6dbb923077c48641c9d2ba8430c92df9c49660480f398b133\n - "brown"\n - "cow"\n - Layer 2:\n - Mod hash: 24255ef5d941493b9978f3aabb0ed07d084ade196d23f463ff058954cbf6e9b6\n - \n - Layer 1:\n - Mod hash: 7ee93c7e6fde43b6ac75100244fd294b8247362f22724b2ef0099bf7ab083def\n - "now"\n\n((AGG_SIG_UNSAFE 0xaa1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac1 0x0000000000000000000000000000000000000000000000000000000000000000) (REMARK) (CREATE_COIN 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 ()) (CREATE_COIN 0x0000000000000000000000000000000000000000000000000000000000000000 1) (CREATE_COIN 0x0000000000000000000000000000000000000000000000000000000000000000 2 ("memo" "memo" "memo")) (CREATE_COIN_ANNOUNCEMENT ()) (ASSERT_COIN_ANNOUNCEMENT 0x0000000000000000000000000000000000000000000000000000000000000000) (ASSERT_COIN_ANNOUNCEMENT 0x98442bcc931fc4c94a2a1b17dafb8959a867097bd3aa73871ca9cfc8327346b4) (CREATE_COIN_ANNOUNCEMENT "hey") (CREATE_PUZZLE_ANNOUNCEMENT ()) (ASSERT_PUZZLE_ANNOUNCEMENT 0x0000000000000000000000000000000000000000000000000000000000000000) (ASSERT_PUZZLE_ANNOUNCEMENT 0xfb4c79668bee67977af996dc2f42071735a819b4221d3f5c9e8e9f43c30c2bc5) (CREATE_PUZZLE_ANNOUNCEMENT "hey"))\n\ngrouped conditions:\n\n (AGG_SIG_UNSAFE 0xaa1a1c26055a329817a5759d877a2795f9499b97d6056edde0eea39512f24e8bc874b4471f0501127abb1ea0d9f68ac1 0x0000000000000000000000000000000000000000000000000000000000000000)\n\n (REMARK)\n\n (CREATE_COIN 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 ())\n (CREATE_COIN 0x0000000000000000000000000000000000000000000000000000000000000000 1)\n (CREATE_COIN 0x0000000000000000000000000000000000000000000000000000000000000000 2 ("memo" "memo" "memo"))\n\n (CREATE_COIN_ANNOUNCEMENT ())\n (CREATE_COIN_ANNOUNCEMENT "hey")\n\n (ASSERT_COIN_ANNOUNCEMENT 0x0000000000000000000000000000000000000000000000000000000000000000)\n (ASSERT_COIN_ANNOUNCEMENT 0x98442bcc931fc4c94a2a1b17dafb8959a867097bd3aa73871ca9cfc8327346b4)\n\n (CREATE_PUZZLE_ANNOUNCEMENT ())\n (CREATE_PUZZLE_ANNOUNCEMENT "hey")\n\n (ASSERT_PUZZLE_ANNOUNCEMENT 0x0000000000000000000000000000000000000000000000000000000000000000)\n (ASSERT_PUZZLE_ANNOUNCEMENT 0xfb4c79668bee67977af996dc2f42071735a819b4221d3f5c9e8e9f43c30c2bc5)\n\n\n-------\nconsuming coin (0xf61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 ())\n with id a6f9255571f8a910057476920a960ae7be70034677303a944b28a47550b9a5e4\n\n\nbrun -y main.sym \'(a (q 2 (q . 15) (c (q 2 (q . "hey") (c (q . "now") 1)) 1)) (c (q . "brown") (c (q . "cow") 1)))\' \'()\'\n\n--- Uncurried Args ---\n- \n - Layer 1:\n - Mod hash: 507414e217dc45d6dbb923077c48641c9d2ba8430c92df9c49660480f398b133\n - "brown"\n - "cow"\n - Layer 2:\n - Mod hash: 24255ef5d941493b9978f3aabb0ed07d084ade196d23f463ff058954cbf6e9b6\n - \n - Layer 1:\n - Mod hash: 7ee93c7e6fde43b6ac75100244fd294b8247362f22724b2ef0099bf7ab083def\n - "now"\n\n()\n\n(no output conditions generated)\n\n-------\n\nspent coins\n (0x0000000000000000000000000000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000000000000000000000000000 ())\n => spent coin id f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b\n (0x0000000000000000000000000000000000000000000000000000000000000000 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 3)\n => spent coin id f61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f\n\ncreated coins\n (0xf61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f 0x0000000000000000000000000000000000000000000000000000000000000000 1)\n => created coin id 041ca97661e2ab59c4d85848092229b00bae000573927b6ab9af4e2becd765c5\n (0xf61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f 0x0000000000000000000000000000000000000000000000000000000000000000 2)\n => created coin id d3f6dc7e6c0a81d22ebebd5559962bab95fa7798ad4b0bcbb42da214215e5e98\n\nephemeral coins\n (0xf61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f 0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307 ())\n => created coin id a6f9255571f8a910057476920a960ae7be70034677303a944b28a47550b9a5e4\ncreated coin announcements\n [\'0xf61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f\', \'0x686579\'] =>\n 17577cbab71385b4ee009c792f5ed2d954fbd3a31fede834150184519e0ac3fe\n [\'0xf61c4154a3541d84bf6ed0f05ac8a062ab08022b48e53daf7f5603fc8eb7ed1f\', \'0x\'] =>\n 98442bcc931fc4c94a2a1b17dafb8959a867097bd3aa73871ca9cfc8327346b4\ncreated puzzle announcements\n [\'0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307\', \'0x686579\'] =>\n 73f660046d0c542a49db3410b64f12770fc23707bbf1f082d35d9af71f89edd2\n [\'0x3c4f5a82fc6548a256f5959430704623fa7ac291f45e1da47e2f91f0e6c30307\', \'0x\'] =>\n fb4c79668bee67977af996dc2f42071735a819b4221d3f5c9e8e9f43c30c2bc5\n\n\nzero_coin_set = [b\'\\xa6\\xf9%Uq\\xf8\\xa9\\x10\\x05tv\\x92\\n\\x96\\n\\xe7\\xbep\\x03Fw0:\\x94K(\\xa4uP\\xb9\\xa5\\xe4\']\n\ncreated coin announcements = [\'17577cbab71385b4ee009c792f5ed2d954fbd3a31fede834150184519e0ac3fe\', \'98442bcc931fc4c94a2a1b17dafb8959a867097bd3aa73871ca9cfc8327346b4\']\n\nasserted coin announcements = [\'0000000000000000000000000000000000000000000000000000000000000000\', \'98442bcc931fc4c94a2a1b17dafb8959a867097bd3aa73871ca9cfc8327346b4\']\n\nsymdiff of coin announcements = [\'0000000000000000000000000000000000000000000000000000000000000000\', \'17577cbab71385b4ee009c792f5ed2d954fbd3a31fede834150184519e0ac3fe\']\n\ncreated puzzle announcements = [\'73f660046d0c542a49db3410b64f12770fc23707bbf1f082d35d9af71f89edd2\', \'fb4c79668bee67977af996dc2f42071735a819b4221d3f5c9e8e9f43c30c2bc5\']\n\nasserted puzzle announcements = [\'0000000000000000000000000000000000000000000000000000000000000000\', \'fb4c79668bee67977af996dc2f42071735a819b4221d3f5c9e8e9f43c30c2bc5\']\n\nsymdiff of puzzle announcements = [\'0000000000000000000000000000000000000000000000000000000000000000\', \'73f660046d0c542a49db3410b64f12770fc23707bbf1f082d35d9af71f89edd2\']\n\n\n================================================================================\n\naggregated signature check pass: True\npks: []\nmsgs: [\'0000000000000000000000000000000000000000000000000000000000000000\']\nadd_data: ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb\nsignature: a458f8f379a4bec00f36cd38e36790210f0b7c4ee772417c0cd20513d75c7505a0532d220ab03a471e9e255e2c54472a03abd31a9feb56d551bf482e31e48747b04fe90f4e3c2af98a2de0d11f869cde360f93b8efa37ec1c28950aa52bfbacd\n' # noqa - ) + # spend = WalletSpendBundle([make_spend(coin, ACS, solution)], sig) diff --git a/chia/wallet/util/debug_spend_bundle.py b/chia/wallet/util/debug_spend_bundle.py index 2ffcf0163a10..9a3393aafbad 100644 --- a/chia/wallet/util/debug_spend_bundle.py +++ b/chia/wallet/util/debug_spend_bundle.py @@ -1,6 +1,11 @@ from __future__ import annotations +import dataclasses +import json +from pathlib import Path + from chia_rs import AugSchemeMPL +from clvm.casts import int_to_bytes from clvm.operators import KEYWORD_FROM_ATOM from clvm_tools.binutils import disassemble as bu_disassemble @@ -9,7 +14,15 @@ from chia.types.blockchain_format.program import INFINITE_COST, Program from chia.types.condition_opcodes import ConditionOpcode from chia.util.condition_tools import conditions_dict_for_solution, pkm_pairs_for_conditions_dict -from chia.util.hash import std_hash +from chia.wallet.conditions import ( + AssertCoinAnnouncement, + AssertPuzzleAnnouncement, + CreateCoinAnnouncement, + CreatePuzzleAnnouncement, + SendMessage, + UnknownCondition, + parse_conditions_non_consensus, +) from chia.wallet.uncurried_puzzle import UncurriedPuzzle CONDITIONS = {opcode.name: opcode.value[0] for opcode in ConditionOpcode} @@ -38,31 +51,48 @@ def coin_as_program(coin: Coin) -> Program: def dump_coin(coin: Coin) -> str: - return disassemble(coin_as_program(coin)) + coin_str = ( + f"Coin ID: {coin.name().hex()}\n" + f"Parent ID: {coin.parent_coin_info}\n" + f"PuzzleHash: {coin.puzzle_hash}\n" + f"Amount: {coin.amount}\n" + ) + return coin_str -def recursive_uncurry_dump(puzzle: Program, layer: int, prefix: str, uncurried_already: UncurriedPuzzle) -> None: +def recursive_uncurry_dump( + puzzle: Program, layer: int, prefix: str, uncurried_already: UncurriedPuzzle, puzzle_dict: dict[str, str] +) -> None: mod = uncurried_already.mod curried_args = uncurried_already.args if mod != puzzle: - print(f"{prefix}- Layer {layer}:") + mod_hex = mod.get_tree_hash().hex() + for key, val in puzzle_dict.items(): + if val == mod_hex: + mod_name = key + break + else: + mod_name = "Unknown Puzzle" + print(f"{prefix}- Layer {layer}: {mod_name.upper()}") print(f"{prefix} - Mod hash: {mod.get_tree_hash().hex()}") + print(f"{prefix} - Curried args:") + for arg in curried_args.as_iter(): - uncurry_dump(arg, prefix=f"{prefix} ") + uncurry_dump(arg, prefix=f"{prefix} ") mod2, curried_args2 = mod.uncurry() if mod2 != mod: - recursive_uncurry_dump(mod, layer + 1, prefix, UncurriedPuzzle(mod2, curried_args2)) + recursive_uncurry_dump(mod, layer + 1, prefix, UncurriedPuzzle(mod2, curried_args2), puzzle_dict) else: print(f"{prefix}- {bu_disassemble(puzzle)}") def uncurry_dump(puzzle: Program, prefix: str = "") -> None: + puzzle_json_path = Path("chia/wallet/puzzles/deployed_puzzle_hashes.json") + with open(puzzle_json_path) as f: + puzzle_dict = json.load(f) mod, curried_args = puzzle.uncurry() - if mod != puzzle: - print(f"{prefix}- ") - prefix = f"{prefix} " - recursive_uncurry_dump(puzzle, 1, prefix, UncurriedPuzzle(mod, curried_args)) + recursive_uncurry_dump(puzzle, 1, prefix, UncurriedPuzzle(mod, curried_args), puzzle_dict) def debug_spend_bundle(spend_bundle, agg_sig_additional_data=DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA) -> None: @@ -74,25 +104,34 @@ def debug_spend_bundle(spend_bundle, agg_sig_additional_data=DEFAULT_CONSTANTS.A pks = [] msgs = [] - created_coin_announcements: list[list[bytes]] = [] - asserted_coin_announcements = [] - created_puzzle_announcements: list[list[bytes]] = [] - asserted_puzzle_announcements = [] + created_coin_announcements: dict[bytes, list] = {} + asserted_coin_announcements: dict[bytes, list] = {} + created_puzzle_announcements: dict[bytes, list] = {} + asserted_puzzle_announcements: dict[bytes, list] = {} + sent_messages: dict[bytes, list] = {} + received_messages: dict[bytes, list] = {} + bad_messages: dict[bytes, list] = {} print("=" * 80) - for coin_spend in spend_bundle.coin_spends: + for i, coin_spend in enumerate(spend_bundle.coin_spends): coin = coin_spend.coin + coin_name = coin.name() puzzle_reveal = Program.from_bytes(bytes(coin_spend.puzzle_reveal)) solution = Program.from_bytes(bytes(coin_spend.solution)) - coin_name = coin.name() - print(f"consuming coin {dump_coin(coin)}") - print(f" with id {coin_name.hex()}") - print() - print(f"\nbrun -y main.sym '{bu_disassemble(puzzle_reveal)}' '{bu_disassemble(solution)}'") + sent_messages[coin_name] = [] + received_messages[coin_name] = [] + bad_messages[coin_name] = [] + created_coin_announcements[coin_name] = [] + asserted_coin_announcements[coin_name] = [] + created_puzzle_announcements[coin_name] = [] + asserted_puzzle_announcements[coin_name] = [] + + print(f"Spending Coin {i}:") + print(dump_coin(coin_spend.coin)) print() - print("--- Uncurried Args ---") + print("--- Puzzle Info ---") uncurry_dump(puzzle_reveal) if puzzle_reveal.get_tree_hash() != coin_spend.coin.puzzle_hash: @@ -103,130 +142,301 @@ def debug_spend_bundle(spend_bundle, agg_sig_additional_data=DEFAULT_CONSTANTS.A print() continue - conditions = conditions_dict_for_solution(puzzle_reveal, solution, INFINITE_COST) - for pk, m in pkm_pairs_for_conditions_dict(conditions, coin, agg_sig_additional_data): + print("\n\n--- Output Conditions ---\n") + conditions_iter = puzzle_reveal.run(solution).as_iter() + conditions = sorted( + parse_conditions_non_consensus(conditions_iter, abstractions=False), + key=lambda instance: instance.__class__.__name__, + ) + conditions_dict = conditions_dict_for_solution(puzzle_reveal, solution, INFINITE_COST) + for pk, m in pkm_pairs_for_conditions_dict(conditions_dict, coin, agg_sig_additional_data): pks.append(pk) msgs.append(m) - print() - _cost, r = puzzle_reveal.run_with_cost(INFINITE_COST, solution) - print(disassemble(r)) - create_coin_conditions = [con for con in r.as_iter() if con.first().as_int() == 51] - print() - if conditions and len(conditions) > 0: - print("grouped conditions:") - for condition_programs in conditions.values(): - print() - for c in condition_programs: - if len(c.vars) == 0: - as_prog = Program.to([c.opcode]) - elif len(c.vars) == 1: - as_prog = Program.to([c.opcode, c.vars[0]]) - elif len(c.vars) == 2: - if c.opcode == ConditionOpcode.CREATE_COIN: - cc = next( - cc - for cc in create_coin_conditions - if cc.at("rf").atom == c.vars[0] and cc.at("rrf").atom == c.vars[1] - ) - if cc.at("rrr").atom is None: - as_prog = Program.to([c.opcode, c.vars[0], c.vars[1], cc.at("rrrf")]) - else: - as_prog = Program.to([c.opcode, c.vars[0], c.vars[1]]) - else: - as_prog = Program.to([c.opcode, c.vars[0], c.vars[1]]) - else: - raise Exception(f"Unexpected number of vars: {len(c.vars)}") - - print(f" {disassemble(as_prog)}") - created_coin_announcements.extend( - [coin_name, *_.vars] for _ in conditions.get(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, []) - ) - asserted_coin_announcements.extend( - [_.vars[0].hex() for _ in conditions.get(ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [])] - ) - created_puzzle_announcements.extend( - [puzzle_reveal.get_tree_hash(), *_.vars] - for _ in conditions.get(ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, []) - ) - asserted_puzzle_announcements.extend( - [_.vars[0].hex() for _ in conditions.get(ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT, [])] - ) + for cond in conditions: + cond_type = KFA[cond.to_program().first().as_int()] + print(f"{cond_type}") + if cond_type in {"SEND_MESSAGE", "RECEIVE_MESSAGE"}: + if isinstance(cond, UnknownCondition): + print(f" mode: {cond.args[0].as_int():06b}") + print(f" message: {cond.args[1].as_atom().hex()}") + print(" ** Malformed args for condition **") + bad_messages[coin_name].append(cond) + else: + assert isinstance(cond, SendMessage) + print(f" mode: {cond.mode_integer:06b}") + print(f" message: {cond.msg.hex()}") + other_side = cond.receiver if cond_type == "SEND_MESSAGE" else cond.sender + assert other_side is not None + for key, val in other_side.to_json_dict().items(): + if val is not None: + print(f" {key}: {val}") + else: + for key, val in cond.to_json_dict().items(): + if val is not None: + print(f" {key}: {val}") + if isinstance(cond, SendMessage): + if cond_type == "SEND_MESSAGE": + sent_messages[coin_name].append(cond) + if cond_type == "RECEIVE_MESSAGE": + received_messages[coin_name].append(cond) + if isinstance(cond, AssertPuzzleAnnouncement): + asserted_puzzle_announcements[coin_name].append(cond) + if isinstance(cond, CreatePuzzleAnnouncement): + if cond.puzzle_hash is None: + cond = dataclasses.replace(cond, puzzle_hash=coin.puzzle_hash) + created_puzzle_announcements[coin_name].append(cond) + if isinstance(cond, AssertCoinAnnouncement): + asserted_coin_announcements[coin_name].append(cond) + if isinstance(cond, CreateCoinAnnouncement): + if cond.coin_id is None: + cond = dataclasses.replace(cond, coin_id=coin_name) + created_coin_announcements[coin_name].append(cond) print() - else: - print("(no output conditions generated)") - print() - print("-------") + print("=" * 80) + + print("Coin Summary\n") created = set(spend_bundle.additions()) spent = set(spend_bundle.removals()) - zero_coin_set = {coin.name() for coin in created if coin.amount == 0} - ephemeral = created.intersection(spent) created.difference_update(ephemeral) spent.difference_update(ephemeral) print() - print("spent coins") + print("--- SPENT COINS ---\n") for coin in sorted(spent, key=lambda _: _.name()): - print(f" {dump_coin(coin)}") - print(f" => spent coin id {coin.name().hex()}") + print(f"{dump_coin(coin)}") print() - print("created coins") + print("--- CREATED COINS ---\n") for coin in sorted(created, key=lambda _: _.name()): - print(f" {dump_coin(coin)}") - print(f" => created coin id {coin.name().hex()}") + print(f"{dump_coin(coin)}") if ephemeral: print() - print("ephemeral coins") + print("--- EPHEMERAL COINS ---\n") for coin in sorted(ephemeral, key=lambda _: _.name()): - print(f" {dump_coin(coin)}") - print(f" => created coin id {coin.name().hex()}") - - created_coin_announcement_pairs = [(_, std_hash(b"".join(_)).hex()) for _ in created_coin_announcements] - if created_coin_announcement_pairs: - print("created coin announcements") - for announcement, hashed in sorted(created_coin_announcement_pairs, key=lambda _: _[-1]): - as_hex = [f"0x{_.hex()}" for _ in announcement] - print(f" {as_hex} =>\n {hashed}") - - eor_coin_announcements = sorted({_[-1] for _ in created_coin_announcement_pairs} ^ set(asserted_coin_announcements)) - - created_puzzle_announcement_pairs = [(_, std_hash(b"".join(_)).hex()) for _ in created_puzzle_announcements] - if created_puzzle_announcements: - print("created puzzle announcements") - for announcement, hashed in sorted(created_puzzle_announcement_pairs, key=lambda _: _[-1]): - as_hex = [f"0x{_.hex()}" for _ in announcement] - print(f" {as_hex} =>\n {hashed}") - - eor_puzzle_announcements = sorted( - {_[-1] for _ in created_puzzle_announcement_pairs} ^ set(asserted_puzzle_announcements) - ) + print(f"{dump_coin(coin)}") + + print("-" * 80) + print("PUZZLE ANNOUNCEMENTS\n\n") + + print("Created Puzzle Announcements\n") + for coin_id, cpas in created_puzzle_announcements.items(): + for cpa in cpas: + assertion = cpa.corresponding_assertion().msg_calc + asserted_by = [] + for asserting_coin_id, apas in asserted_puzzle_announcements.items(): + for apa in apas: + if assertion == apa.msg: + asserted_by.append(asserting_coin_id.hex()) + print(f"CoinID: {coin_id.hex()}") + print(f"Message: {cpa.msg.hex()}") + print(f"Announces: {assertion}") + if asserted_by: + print("Asserted By Coins:") + for c_id in asserted_by: + print(f" -> {c_id}") + else: + print("** Not Asserted **") + print() + print("Asserted Puzzle Announcements\n") + for coin_id, apas in asserted_puzzle_announcements.items(): + for apa in apas: + assertion = apa.msg + created_by = [] + message = None + for creating_coin_id, cpas in created_puzzle_announcements.items(): + for cpa in cpas: + if cpa.corresponding_assertion().msg_calc == assertion: + created_by.append(creating_coin_id.hex()) + message = cpa.msg + print(f"CoinID: {coin_id.hex()}") + if message is not None: + print(f"Message: {message.hex()}") + print(f"Announces: {assertion}") + if created_by: + print("Asserted By Coins:") + for c_id in created_by: + print(f" -> {c_id}") + else: + print("** Not Asserted **") + print() + + print("-" * 80) + print("COIN ANNOUNCEMENTS\n") + + print("Created Coin Announcements\n") + for coin_id, ccas in created_coin_announcements.items(): + for cca in ccas: + assertion = cca.corresponding_assertion().msg_calc + asserted_by = [] + for asserting_coin_id, acas in asserted_coin_announcements.items(): + for aca in acas: + if assertion == aca.msg: + asserted_by.append(asserting_coin_id.hex()) + print(f"CoinID: {coin_id.hex()}") + print(f"Message: {cca.msg.hex()}") + print(f"Announces: {assertion}") + if asserted_by: + print("Asserted By Coins:") + for c_id in asserted_by: + print(f" -> {c_id}") + else: + print("** Not Asserted **") + print() + + print("Asserted Coin Announcements\n") + for coin_id, acas in asserted_coin_announcements.items(): + for aca in acas: + assertion = aca.msg + created_by = [] + message = None + for creating_coin_id, ccas in created_coin_announcements.items(): + for cca in ccas: + if cca.corresponding_assertion().msg_calc == assertion: + created_by.append(creating_coin_id.hex()) + message = cca.msg + print(f"CoinID: {coin_id.hex()}") + if message is not None: + print(f"Message: {message.hex()}") + print(f"Announces: {assertion}") + if created_by: + print("Asserted By Coins:") + for c_id in created_by: + print(f" -> {c_id}") + else: + print("** Not Asserted **") + print() + + print("-" * 80) + print("SENT MESSAGES") print() + for coin_id, messages in sent_messages.items(): + coin = next((cs.coin for cs in spend_bundle.coin_spends if cs.coin.name() == coin_id), None) + coin_list = [coin.parent_coin_info, coin.puzzle_hash, int_to_bytes(coin.amount)] + for message in messages: + mode = f"{message.mode:06b}" + print(f"SENDER: {coin_id.hex()}") + print(f" Mode: {mode}") + print(f" Message: {message.msg}") + + sender_mode = mode[:3] + receiver_mode = mode[3:] + expected_receiver_args = [] + if sender_mode == "111": + expected_receiver_args.append(Program.to(coin.name())) + else: + for i in range(3): + if int(sender_mode, 2) & (1 << (2 - i)): + expected_receiver_args.append(Program.to(coin_list[i])) + # now find a matching receiver + found = False + for receiver_id, r_messages in received_messages.items(): + for r_message in r_messages: + if r_message.mode == message.mode: + if r_message.args == expected_receiver_args: + print(f"RECEIVER: {receiver_id.hex()}") + print(f" Message: {r_message.msg}") + if r_message.msg != message.msg: + print(" ** Messages do not match **") + expected_sender_args = [] + receiver_coin = next( + (cs.coin for cs in spend_bundle.coin_spends if cs.coin.name() == receiver_id), None + ) + assert receiver_coin is not None + receiver_coin_list = [ + receiver_coin.parent_coin_info, + receiver_coin.puzzle_hash, + int_to_bytes(receiver_coin.amount), + ] + if receiver_mode == "111": + expected_sender_args.append(Program.to(receiver_id)) + else: + for i in range(3): + if int(receiver_mode, 2) & (1 << (2 - i)): + expected_sender_args.append(Program.to(receiver_coin_list[i])) + if expected_sender_args != message.args: + print(" ** Mismatched Sender Args **") + print() + found = True + if not found: + print("** RECEIVER NOT FOUND **") + print() + + print("-" * 80) + print("RECEIVED MESSAGES") print() - print(f"zero_coin_set = {sorted(zero_coin_set)}") - print() - if created_coin_announcement_pairs or asserted_coin_announcements: - print(f"created coin announcements = {sorted([_[-1] for _ in created_coin_announcement_pairs])}") - print() - print(f"asserted coin announcements = {sorted(asserted_coin_announcements)}") - print() - print(f"symdiff of coin announcements = {sorted(eor_coin_announcements)}") - print() - if created_puzzle_announcement_pairs or asserted_puzzle_announcements: - print(f"created puzzle announcements = {sorted([_[-1] for _ in created_puzzle_announcement_pairs])}") - print() - print(f"asserted puzzle announcements = {sorted(asserted_puzzle_announcements)}") - print() - print(f"symdiff of puzzle announcements = {sorted(eor_puzzle_announcements)}") - print() + for coin_id, messages in received_messages.items(): + coin = next((cs.coin for cs in spend_bundle.coin_spends if cs.coin.name() == coin_id), None) + coin_list = [coin.parent_coin_info, coin.puzzle_hash, int_to_bytes(coin.amount)] + for message in messages: + mode = f"{message.mode:06b}" + print(f"RECEIVER: {coin_id.hex()}") + print(f" Mode: {mode}") + print(f" Message: {message.msg}") + + sender_mode = mode[:3] + receiver_mode = mode[3:] + expected_sender_args = [] + if receiver_mode == "111": + expected_sender_args.append(Program.to(coin.name())) + else: + for i in range(3): + if int(receiver_mode, 2) & (1 << (2 - i)): + expected_sender_args.append(Program.to(coin_list[i])) + # now find a matching sender + found = False + for sender_id, s_messages in sent_messages.items(): + for s_message in s_messages: + if s_message.mode == message.mode: + if s_message.args == expected_sender_args: + print(f"SENDER: {sender_id.hex()}") + print(f" Message: {s_message.msg}") + if s_message.msg != message.msg: + print(" ** Messages do not match **") + expected_receiver_args = [] + sender_coin = next( + (cs.coin for cs in spend_bundle.coin_spends if cs.coin.name() == sender_id), None + ) + assert sender_coin is not None + sender_coin_list = [ + sender_coin.parent_coin_info, + sender_coin.puzzle_hash, + int_to_bytes(sender_coin.amount), + ] + if sender_mode == "111": + expected_receiver_args.append(Program.to(sender_id)) + else: + for i in range(3): + if int(sender_mode, 2) & (1 << (2 - i)): + expected_receiver_args.append(Program.to(sender_coin_list[i])) + if expected_receiver_args != message.args: + print(" ** Mismatched Sender Args **") + print() + found = True + if not found: + print("** SENDER NOT FOUND **") + print() + print() print("=" * 80) + print("SIGNATURES") print() validates = AugSchemeMPL.aggregate_verify(pks, msgs, spend_bundle.aggregated_signature) - print(f"aggregated signature check pass: {validates}") - print(f"pks: {pks}") - print(f"msgs: {[msg.hex() for msg in msgs]}") - print(f"add_data: {agg_sig_additional_data.hex()}") - print(f"signature: {spend_bundle.aggregated_signature}") + print(f"Aggregated signature check pass: {validates}") + print() + print("Public Keys:") + for pk in pks: + print(f" {pk}") + print() + print("Messages") + for msg in msgs: + print(f" {msg.hex()}") + print() + print("Additional Data:") + print(f" {agg_sig_additional_data.hex()}") + print() + print("Signature:") + print(f" {spend_bundle.aggregated_signature}") + print()