Example #1
0
def get_discrepancies_for_spend_bundle(
    trade_offer: SpendBundle,
) -> Tuple[bool, Optional[Dict], Optional[Exception]]:
    try:
        cc_discrepancies: Dict[str, int] = dict()
        for coinsol in trade_offer.coin_spends:
            puzzle: Program = Program.from_bytes(bytes(coinsol.puzzle_reveal))
            solution: Program = Program.from_bytes(bytes(coinsol.solution))
            # work out the deficits between coin amount and expected output for each
            r = cc_utils.uncurry_cc(puzzle)
            if r:
                # Calculate output amounts
                mod_hash, genesis_checker, inner_puzzle = r
                innersol = solution.first()

                total = get_output_amount_for_puzzle_and_solution(inner_puzzle, innersol)
                colour = bytes(genesis_checker).hex()
                if colour in cc_discrepancies:
                    cc_discrepancies[colour] += coinsol.coin.amount - total
                else:
                    cc_discrepancies[colour] = coinsol.coin.amount - total
            else:
                coin_amount = coinsol.coin.amount
                out_amount = get_output_amount_for_puzzle_and_solution(puzzle, solution)
                diff = coin_amount - out_amount
                if "chia" in cc_discrepancies:
                    cc_discrepancies["chia"] = cc_discrepancies["chia"] + diff
                else:
                    cc_discrepancies["chia"] = diff

        return True, cc_discrepancies, None
    except Exception as e:
        return False, None, e
def print_conditions(spend_bundle: SpendBundle):
    print("\nConditions:")
    for coin_solution in spend_bundle.coin_solutions:
        result = Program.from_bytes(bytes(coin_solution.puzzle_reveal)).run(
            Program.from_bytes(bytes(coin_solution.solution)))
        error, result_human = parse_sexp_to_conditions(result)
        assert error is None
        assert result_human is not None
        for cvp in result_human:
            print(
                f"{ConditionOpcode(cvp.opcode).name}: {[var.hex() for var in cvp.vars]}"
            )
    print("")
Example #3
0
def compress_coin_solution(coin_solution: CoinSolution):
    compressed_puzzle = compress_cse_puzzle(coin_solution.puzzle_reveal)
    return [
        [coin_solution.coin.parent_coin_info, coin_solution.coin.amount],
        [compressed_puzzle,
         Program.from_bytes(bytes(coin_solution.solution))],
    ]
def solution_to_pool_state(full_spend: CoinSpend) -> Optional[PoolState]:
    full_solution_ser: SerializedProgram = full_spend.solution
    full_solution: Program = Program.from_bytes(bytes(full_solution_ser))

    if full_spend.coin.puzzle_hash == SINGLETON_LAUNCHER_HASH:
        # Launcher spend
        extra_data: Program = full_solution.rest().rest().first()
        return pool_state_from_extra_data(extra_data)

    # Not launcher spend
    inner_solution: Program = full_solution.rest().rest().first()

    # Spend which is not absorb, and is not the launcher
    num_args = len(inner_solution.as_python())
    assert num_args in (2, 3)

    if num_args == 2:
        # pool member
        if inner_solution.rest().first().as_int() != 0:
            return None

        # This is referred to as p1 in the chialisp code
        # spend_type is absorbing money if p1 is a cons box, spend_type is escape if p1 is an atom
        # TODO: The comment above, and in the CLVM, seems wrong
        extra_data = inner_solution.first()
        if isinstance(extra_data.as_python(), bytes):
            # Absorbing
            return None
        return pool_state_from_extra_data(extra_data)
    else:
        # pool waitingroom
        if inner_solution.first().as_int() == 0:
            return None
        extra_data = inner_solution.rest().first()
        return pool_state_from_extra_data(extra_data)
Example #5
0
 async def test_clvm_strict_mode(self, rust_checker: bool):
     block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR.program))
     disassembly = binutils.disassemble(block)
     # this is a valid generator program except the first clvm
     # if-condition, that depends on executing an unknown operator
     # ("0xfe"). In strict mode, this should fail, but in non-strict
     # mode, the unknown operator should be treated as if it returns ().
     program = SerializedProgram.from_bytes(
         binutils.assemble(
             f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin())
     generator = BlockGenerator(program, [])
     npc_result: NPCResult = get_name_puzzle_conditions(
         generator,
         test_constants.MAX_BLOCK_COST_CLVM,
         cost_per_byte=test_constants.COST_PER_BYTE,
         safe_mode=True,
         rust_checker=rust_checker,
     )
     assert npc_result.error is not None
     npc_result = get_name_puzzle_conditions(
         generator,
         test_constants.MAX_BLOCK_COST_CLVM,
         cost_per_byte=test_constants.COST_PER_BYTE,
         safe_mode=False,
         rust_checker=rust_checker,
     )
     assert npc_result.error is None
Example #6
0
 def test_deserialization_simple_list(self):
     # ("hello" "friend")
     b = hexstr_to_bytes("ff8568656c6c6fff86667269656e6480")
     cost, output = DESERIALIZE_MOD.run_with_cost([b])
     print(cost, output)
     prog = Program.to(output)
     assert prog == Program.from_bytes(b)
    async def test_clvm_max_cost(self):

        block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR.program))
        disassembly = binutils.disassemble(block)
        # this is a valid generator program except the first clvm
        # if-condition, that depends on executing an unknown operator
        # ("0xfe"). In strict mode, this should fail, but in non-strict
        # mode, the unknown operator should be treated as if it returns ().
        # the CLVM program has a cost of 391969
        program = SerializedProgram.from_bytes(
            binutils.assemble(
                f"(i (softfork (q . 10000000)) (q . ()) {disassembly})").
            as_bin())

        # ensure we fail if the program exceeds the cost
        generator = BlockGenerator(program, [])
        npc_result: NPCResult = get_name_puzzle_conditions(
            generator, 10000000, False)

        assert npc_result.error is not None
        assert npc_result.clvm_cost == 0

        # raise the max cost to make sure this passes
        # ensure we pass if the program does not exceeds the cost
        npc_result: NPCResult = get_name_puzzle_conditions(
            generator, 20000000, False)

        assert npc_result.error is None
        assert npc_result.clvm_cost > 10000000
    def test_shatrees_match(self):
        """Checks to see that all .sha256tree files match their .hex files"""
        for prog_path in wallet_program_files:
            # load the .hex file as a program
            hex_filename = path_with_ext(prog_path, ".hex")
            clvm_hex = hex_filename.read_text()  # .decode("utf8")
            clvm_blob = bytes.fromhex(clvm_hex)
            s = SerializedProgram.from_bytes(clvm_blob)
            p = Program.from_bytes(clvm_blob)

            # load the checked-in shatree
            existing_sha = path_with_ext(
                prog_path, ".hex.sha256tree").read_text().strip()

            self.assertEqual(
                s.get_tree_hash().hex(),
                existing_sha,
                msg=
                f"Checked-in shatree hash file does not match shatree hash of loaded SerializedProgram: {prog_path}",  # noqa
            )
            self.assertEqual(
                p.get_tree_hash().hex(),
                existing_sha,
                msg=
                f"Checked-in shatree hash file does not match shatree hash of loaded Program: {prog_path}",
            )
Example #9
0
def parse_program(program: str, include=[]):
    if '(' in program:
        prog = Program.to(assemble(program))
    elif '.' not in program:
        prog = Program.from_bytes(bytes.fromhex(program))
    else:
        with open(program, "r") as file:
            filestring = file.read()
            if '(' in filestring:
                if re.compile('\(mod\s').search(filestring):
                    prog = Program.to(
                        compile_clvm_text(filestring, append_include(include)))
                else:
                    prog = Program.to(assemble(filestring))
            else:
                prog = Program.from_bytes(bytes.fromhex(filestring))
    return prog
Example #10
0
def make_generator_args(
        generator_ref_list: List[SerializedProgram]) -> SerializedProgram:
    """
    `make_generator_args`: The format and contents of these arguments affect consensus.
    """
    gen_ref_list = [Program.from_bytes(bytes(g)) for g in generator_ref_list]
    return SerializedProgram.from_bytes(
        bytes(Program.to([DESERIALIZE_MOD, gen_ref_list])))
    def test_make_generator_args(self):
        generator_ref_list = [gen1]
        gen_args = create_generator_args(generator_ref_list)
        gen_args_as_program = Program.from_bytes(bytes(gen_args))

        # First Argument to the block generator is the first template generator
        arg2 = gen_args_as_program.first()
        print(arg2)
        assert arg2 == bytes(gen1)
Example #12
0
 def test_deserialization_large_numbers(self):
     # '(99999999999999999999999999999999999999999999999999999999999999999 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -99999999999999999999999999999999999999999999999999999999999999999999999999999)'  # noqa
     b = hexstr_to_bytes(
         "ff9c00f316271c7fc3908a8bef464e3945ef7a253609ffffffffffffffffffb00fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1ff22ea0179500526edb610f148ec0c614155678491902d6000000000000000000180"  # noqa
     )  # noqa
     cost, output = DESERIALIZE_MOD.run_with_cost([b])
     print(cost, output)
     prog = Program.to(output)
     assert prog == Program.from_bytes(b)
Example #13
0
 def test_deserialization_password_coin(self):
     # (i (= (sha256 2) (q 0x2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824)) (c (q 51) (c 5 (c (q 100) (q ())))) (q "wrong password"))  # noqa
     b = hexstr_to_bytes(
         "ff04ffff0affff0bff0280ffff01ffa02cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b98248080ffff05ffff01ff3380ffff05ff05ffff05ffff01ff6480ffff01ff8080808080ffff01ff8e77726f6e672070617373776f72648080"  # noqa
     )  # noqa
     cost, output = DESERIALIZE_MOD.run_with_cost([b])
     print(cost, output)
     prog = Program.to(output)
     assert prog == Program.from_bytes(b)
Example #14
0
    def test_make_generator_args(self):
        generator_ref_list = [gen1]
        gen_args = create_generator_args(generator_ref_list)
        gen_args_as_program = Program.from_bytes(bytes(gen_args))

        d = gen_args_as_program.first()

        # First argument: clvm deserializer

        b = bytes.fromhex(
            "ff8568656c6c6fff86667269656e6480")  # ("hello" "friend")
        cost, output = d.run_with_cost([b])
        # print(cost, output)
        out = Program.to(output)
        assert out == Program.from_bytes(b)

        # Second Argument to the block generator is the first template generator
        arg2 = gen_args_as_program.rest().first()
        print(arg2)
        assert arg2 == bytes(gen1)
    def test_make_generator_args(self):
        generator_ref_list = [gen1]
        gen_args = make_generator_args(generator_ref_list)
        gen_args_as_program = Program.from_bytes(bytes(gen_args))

        d = gen_args_as_program.first()

        # First arguemnt: clvm deserializer

        b = hexstr_to_bytes(
            "ff8568656c6c6fff86667269656e6480")  # ("hello" "friend")
        cost, output = d.run_with_cost([b])
        # print(cost, output)
        out = Program.to(output)
        assert out == Program.from_bytes(b)

        # Second Argument
        arg2 = gen_args_as_program.rest().first().first()
        print(arg2)
        assert bytes(arg2) == bytes(gen1)
Example #16
0
def get_inner_puzzle_from_puzzle(full_puzzle: Program) -> Optional[Program]:
    p = Program.from_bytes(bytes(full_puzzle))
    r = p.uncurry()
    if r is None:
        return None
    _, args = r

    _, inner_puzzle = list(args.as_iter())
    if not is_pool_singleton_inner_puzzle(inner_puzzle):
        return None
    return inner_puzzle
def get_delayed_puz_info_from_launcher_spend(coinsol: CoinSolution) -> Tuple[uint64, bytes32]:
    extra_data = Program.from_bytes(bytes(coinsol.solution)).rest().rest().first()
    # Extra data is (pool_state delayed_puz_info)
    # Delayed puz info is (seconds delayed_puzzle_hash)
    seconds: Optional[uint64] = None
    delayed_puzzle_hash: Optional[bytes32] = None
    for key, value in extra_data.as_python():
        if key == b"t":
            seconds = int_from_bytes(value)
        if key == b"h":
            delayed_puzzle_hash = bytes32(value)
    assert seconds is not None
    assert delayed_puzzle_hash is not None
    return seconds, delayed_puzzle_hash
Example #18
0
 async def test_clvm_strict_mode(self):
     block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR))
     disassembly = binutils.disassemble(block)
     # this is a valid generator program except the first clvm
     # if-condition, that depends on executing an unknown operator
     # ("0xfe"). In strict mode, this should fail, but in non-strict
     # mode, the unknown operator should be treated as if it returns ().
     program = SerializedProgram.from_bytes(
         binutils.assemble(
             f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin())
     error, npc_list, cost = get_name_puzzle_conditions(program, True)
     assert error is not None
     error, npc_list, cost = get_name_puzzle_conditions(program, False)
     assert error is None
def lineage_proof_for_coin_spend(coin_spend: CoinSpend) -> Program:
    """Take a coin solution, return a lineage proof for their child to use in spends"""
    coin = coin_spend.coin
    parent_name = coin.parent_coin_info
    amount = coin.amount

    inner_puzzle_hash = None
    if coin.puzzle_hash == LAUNCHER_PUZZLE_HASH:
        return Program.to([parent_name, amount])

    full_puzzle = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
    _, args = full_puzzle.uncurry()
    _, __, ___, inner_puzzle = list(args.as_iter())
    inner_puzzle_hash = inner_puzzle.get_tree_hash()

    return Program.to([parent_name, inner_puzzle_hash, amount])
Example #20
0
def spendable_cc_list_from_coin_spend(coin_spend: CoinSpend,
                                      hash_to_puzzle_f) -> List[SpendableCC]:
    """
    Given a `CoinSpend`, extract out a list of `SpendableCC` objects.

    Since `SpendableCC` needs to track the inner puzzles and a `Coin` only includes
    puzzle hash, we also need a `hash_to_puzzle_f` function that turns puzzle hashes into
    the corresponding puzzles. This is generally either a `dict` or some kind of DB
    (if it's large or persistent).
    """

    spendable_cc_list = []

    coin = coin_spend.coin
    puzzle = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
    r = uncurry_cc(puzzle)
    if r:
        mod_hash, genesis_coin_checker, inner_puzzle = r
        lineage_proof = lineage_proof_for_cc_parent(
            coin, inner_puzzle.get_tree_hash())
    else:
        lineage_proof = lineage_proof_for_coin(coin)

    for new_coin in coin_spend.additions():
        puzzle = hash_to_puzzle_f(new_coin.puzzle_hash)
        if puzzle is None:
            # we don't recognize this puzzle hash, skip it
            continue
        r = uncurry_cc(puzzle)
        if r is None:
            # this isn't a cc puzzle
            continue

        mod_hash, genesis_coin_checker, inner_puzzle = r

        genesis_coin_id = genesis_coin_id_for_genesis_coin_checker(
            genesis_coin_checker)

        # TODO: address hint error and remove ignore
        #       error: Argument 2 to "SpendableCC" has incompatible type "Optional[bytes32]"; expected "bytes32"
        #       [arg-type]
        cc_spend_info = SpendableCC(new_coin, genesis_coin_id, inner_puzzle,
                                    lineage_proof)  # type: ignore[arg-type]
        spendable_cc_list.append(cc_spend_info)

    return spendable_cc_list
Example #21
0
def lineage_proof_for_coinsol(coin_spend: CoinSpend) -> LineageProof:
    parent_name: bytes32 = coin_spend.coin.parent_coin_info

    inner_puzzle_hash: Optional[bytes32] = None
    if coin_spend.coin.puzzle_hash != SINGLETON_LAUNCHER_HASH:
        full_puzzle = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
        r = full_puzzle.uncurry()
        if r is not None:
            _, args = r
            _, inner_puzzle = list(args.as_iter())
            inner_puzzle_hash = inner_puzzle.get_tree_hash()

    amount: uint64 = coin_spend.coin.amount

    return LineageProof(
        parent_name,
        inner_puzzle_hash,
        amount,
    )
Example #22
0
def spendable_cc_list_from_coin_solution(
        coin_solution: CoinSolution, hash_to_puzzle_f) -> List[SpendableCC]:
    """
    Given a `CoinSolution`, extract out a list of `SpendableCC` objects.

    Since `SpendableCC` needs to track the inner puzzles and a `Coin` only includes
    puzzle hash, we also need a `hash_to_puzzle_f` function that turns puzzle hashes into
    the corresponding puzzles. This is generally either a `dict` or some kind of DB
    (if it's large or persistent).
    """

    spendable_cc_list = []

    coin = coin_solution.coin
    puzzle = Program.from_bytes(bytes(coin_solution.puzzle_reveal))
    r = uncurry_cc(puzzle)
    if r:
        mod_hash, genesis_coin_checker, inner_puzzle = r
        lineage_proof = lineage_proof_for_cc_parent(
            coin, inner_puzzle.get_tree_hash())
    else:
        lineage_proof = lineage_proof_for_coin(coin)

    for new_coin in coin_solution.additions():
        puzzle = hash_to_puzzle_f(new_coin.puzzle_hash)
        if puzzle is None:
            # we don't recognize this puzzle hash, skip it
            continue
        r = uncurry_cc(puzzle)
        if r is None:
            # this isn't a cc puzzle
            continue

        mod_hash, genesis_coin_checker, inner_puzzle = r

        genesis_coin_id = genesis_coin_id_for_genesis_coin_checker(
            genesis_coin_checker)

        cc_spend_info = SpendableCC(new_coin, genesis_coin_id, inner_puzzle,
                                    lineage_proof)
        spendable_cc_list.append(cc_spend_info)

    return spendable_cc_list
def find_interesting_singletons(
        puzzle_db: PuzzleDB,
        removals: List[CoinSpend]) -> List[SingletonWallet]:
    singletons = []
    for coin_spend in removals:
        if coin_spend.coin.puzzle_hash == LAUNCHER_PUZZLE_HASH:
            r = Program.from_bytes(bytes(coin_spend.solution))
            key_value_list = r.rest().rest().first()

            eve_coin = coin_spend.additions()[0]

            lineage_proof = lineage_proof_for_coin_spend(coin_spend)
            launcher_id = coin_spend.coin.name()
            singleton = SingletonWallet(
                launcher_id,
                coin_spend.coin.puzzle_hash,
                key_value_list,
                eve_coin,
                lineage_proof,
            )
            singletons.append(singleton)
    return singletons
Example #24
0
    async def create_wallet_for_cc(
        wallet_state_manager: Any,
        wallet: Wallet,
        genesis_checker_hex: str,
    ) -> CCWallet:
        self = CCWallet()
        self.cost_of_single_tx = None
        self.base_puzzle_program = None
        self.base_inner_puzzle_hash = None
        self.standard_wallet = wallet
        self.log = logging.getLogger(__name__)

        self.wallet_state_manager = wallet_state_manager

        self.cc_info = CCInfo(
            Program.from_bytes(bytes.fromhex(genesis_checker_hex)), [])
        info_as_string = bytes(self.cc_info).hex()
        self.wallet_info = await wallet_state_manager.user_store.create_wallet(
            "CC Wallet", WalletType.COLOURED_COIN, info_as_string)
        if self.wallet_info is None:
            raise Exception("wallet_info is None")

        await self.wallet_state_manager.add_new_wallet(self, self.id())
        return self
Example #25
0
    async def load_backup(self, filename: str):
        try:
            f = open(filename, "r")
            details = f.readline().split(":")
            f.close()
            origin = Coin(bytes.fromhex(details[0]), bytes.fromhex(details[1]),
                          uint64(int(details[2])))
            backup_ids = []
            for d in details[3].split(","):
                backup_ids.append(bytes.fromhex(d))
            num_of_backup_ids_needed = uint64(int(details[5]))
            if num_of_backup_ids_needed > len(backup_ids):
                raise Exception
            innerpuz = Program.from_bytes(bytes.fromhex(details[4]))
            did_info = DIDInfo(
                origin,
                backup_ids,
                num_of_backup_ids_needed,
                self.did_info.parent_info,
                innerpuz,
                None,
                None,
                None,
            )
            await self.save_info(did_info, False)
            await self.wallet_state_manager.update_wallet_puzzle_hashes(
                self.wallet_info.id)

            full_puz = did_wallet_puzzles.create_fullpuz(
                innerpuz, origin.puzzle_hash)
            full_puzzle_hash = full_puz.get_tree_hash()
            (
                sub_height,
                header_hash,
            ) = await self.wallet_state_manager.search_blockrecords_for_puzzlehash(
                full_puzzle_hash)
            assert sub_height is not None
            assert header_hash is not None
            full_nodes = self.wallet_state_manager.server.connection_by_type[
                NodeType.FULL_NODE]
            additions: Union[RespondAdditions, RejectAdditionsRequest,
                             None] = None
            for id, node in full_nodes.items():
                request = wallet_protocol.RequestAdditions(
                    sub_height, header_hash, None)
                additions = await node.request_additions(request)
                if additions is not None:
                    break
                if isinstance(additions, RejectAdditionsRequest):
                    continue

            assert additions is not None
            assert isinstance(additions, RespondAdditions)
            # All additions in this block here:
            new_puzhash = (await self.get_new_puzzle()).get_tree_hash()
            new_pubkey = bytes(
                (await self.wallet_state_manager.get_unused_derivation_record(
                    self.wallet_info.id)).pubkey)

            all_parents: bytes32 = set()
            for puzzle_list_coin in additions.coins:
                puzzle_hash, coins = puzzle_list_coin
                for coin in coins:
                    all_parents.add(coin.parent_coin_info)
            for puzzle_list_coin in additions.coins:
                puzzle_hash, coins = puzzle_list_coin
                if puzzle_hash == full_puzzle_hash:
                    # our coin
                    for coin in coins:
                        future_parent = CCParent(
                            coin.parent_coin_info,
                            innerpuz.get_tree_hash(),
                            coin.amount,
                        )
                        await self.add_parent(coin.name(), future_parent,
                                              False)
                        if coin.name() in all_parents:
                            continue
                        did_info = DIDInfo(
                            origin,
                            backup_ids,
                            num_of_backup_ids_needed,
                            self.did_info.parent_info,
                            innerpuz,
                            coin,
                            new_puzhash,
                            new_pubkey,
                        )
                        await self.save_info(did_info, False)

            return None
        except Exception as e:
            raise e
Example #26
0
                                 package_or_requirement="chia.wallet.puzzles")
DESERIALIZE_MOD = load_clvm("chialisp_deserialisation.clvm",
                            package_or_requirement="chia.wallet.puzzles")

DECOMPRESS_PUZZLE = load_clvm("decompress_puzzle.clvm",
                              package_or_requirement="chia.wallet.puzzles")
DECOMPRESS_CSE = load_clvm("decompress_coin_solution_entry.clvm",
                           package_or_requirement="chia.wallet.puzzles")

DECOMPRESS_CSE_WITH_PREFIX = load_clvm(
    "decompress_coin_solution_entry_with_prefix.clvm",
    package_or_requirement="chia.wallet.puzzles")
DECOMPRESS_BLOCK = load_clvm("block_program_zero.clvm",
                             package_or_requirement="chia.wallet.puzzles")

Nil = Program.from_bytes(b"\x80")

original_generator = hexstr_to_bytes(
    "ff01ffffffa00000000000000000000000000000000000000000000000000000000000000000ff830186a080ffffff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3ff018080ffff80ffff01ffff33ffa06b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9ff830186a08080ff8080808080"
)  # noqa


class TestCompression(TestCase):
    def test_spend_bundle_suitable(self):
        sb: SpendBundle = make_spend_bundle(1)
        assert bundle_suitable_for_compression(sb)

    def test_compress_spend_bundle(self):
        pass

    def test_compressed_block_results(self):
Example #27
0
 def test_serialization(self):
     s0 = SerializedProgram.from_bytes(b"\x00")
     p0 = Program.from_bytes(b"\x00")
     print(s0, p0)
    async def respond_to_offer(
            self, file_path: Path
    ) -> Tuple[bool, Optional[TradeRecord], Optional[str]]:
        has_wallets = await self.maybe_create_wallets_for_offer(file_path)
        if not has_wallets:
            return False, None, "Unknown Error"
        trade_offer = None
        try:
            trade_offer_hex = file_path.read_text()
            trade_offer = TradeRecord.from_bytes(
                hexstr_to_bytes(trade_offer_hex))
        except Exception as e:
            return False, None, f"Error: {e}"
        if trade_offer is not None:
            offer_spend_bundle: SpendBundle = trade_offer.spend_bundle

        coinsols: List[CoinSolution] = []  # [] of CoinSolutions
        cc_coinsol_outamounts: Dict[bytes32, List[Tuple[CoinSolution,
                                                        int]]] = dict()
        aggsig = offer_spend_bundle.aggregated_signature
        cc_discrepancies: Dict[bytes32, int] = dict()
        chia_discrepancy = None
        wallets: Dict[bytes32, Any] = dict()  # colour to wallet dict

        for coinsol in offer_spend_bundle.coin_solutions:
            puzzle: Program = Program.from_bytes(bytes(coinsol.puzzle_reveal))
            solution: Program = Program.from_bytes(bytes(coinsol.solution))

            # work out the deficits between coin amount and expected output for each
            r = cc_utils.uncurry_cc(puzzle)
            if r:
                # Calculate output amounts
                mod_hash, genesis_checker, inner_puzzle = r
                colour = bytes(genesis_checker).hex()
                if colour not in wallets:
                    wallets[
                        colour] = await self.wallet_state_manager.get_wallet_for_colour(
                            colour)
                unspent = await self.wallet_state_manager.get_spendable_coins_for_wallet(
                    wallets[colour].id())
                if coinsol.coin in [record.coin for record in unspent]:
                    return False, None, "can't respond to own offer"

                innersol = solution.first()

                total = get_output_amount_for_puzzle_and_solution(
                    inner_puzzle, innersol)
                if colour in cc_discrepancies:
                    cc_discrepancies[colour] += coinsol.coin.amount - total
                else:
                    cc_discrepancies[colour] = coinsol.coin.amount - total
                # Store coinsol and output amount for later
                if colour in cc_coinsol_outamounts:
                    cc_coinsol_outamounts[colour].append((coinsol, total))
                else:
                    cc_coinsol_outamounts[colour] = [(coinsol, total)]

            else:
                # standard chia coin
                unspent = await self.wallet_state_manager.get_spendable_coins_for_wallet(
                    1)
                if coinsol.coin in [record.coin for record in unspent]:
                    return False, None, "can't respond to own offer"
                if chia_discrepancy is None:
                    chia_discrepancy = get_output_discrepancy_for_puzzle_and_solution(
                        coinsol.coin, puzzle, solution)
                else:
                    chia_discrepancy += get_output_discrepancy_for_puzzle_and_solution(
                        coinsol.coin, puzzle, solution)
                coinsols.append(coinsol)

        chia_spend_bundle: Optional[SpendBundle] = None
        if chia_discrepancy is not None:
            chia_spend_bundle = await self.wallet_state_manager.main_wallet.create_spend_bundle_relative_chia(
                chia_discrepancy, [])
            if chia_spend_bundle is not None:
                for coinsol in coinsols:
                    chia_spend_bundle.coin_solutions.append(coinsol)

        zero_spend_list: List[SpendBundle] = []
        spend_bundle = None
        # create coloured coin
        self.log.info(cc_discrepancies)
        for colour in cc_discrepancies.keys():
            if cc_discrepancies[colour] < 0:
                my_cc_spends = await wallets[colour].select_coins(
                    abs(cc_discrepancies[colour]))
            else:
                if chia_spend_bundle is None:
                    to_exclude: List = []
                else:
                    to_exclude = chia_spend_bundle.removals()
                my_cc_spends = await wallets[colour].select_coins(0)
                if my_cc_spends is None or my_cc_spends == set():
                    zero_spend_bundle: SpendBundle = await wallets[
                        colour].generate_zero_val_coin(False, to_exclude)
                    if zero_spend_bundle is None:
                        return (
                            False,
                            None,
                            "Unable to generate zero value coin. Confirm that you have chia available",
                        )
                    zero_spend_list.append(zero_spend_bundle)

                    additions = zero_spend_bundle.additions()
                    removals = zero_spend_bundle.removals()
                    my_cc_spends = set()
                    for add in additions:
                        if add not in removals and add.amount == 0:
                            my_cc_spends.add(add)

            if my_cc_spends == set() or my_cc_spends is None:
                return False, None, "insufficient funds"

            # Create SpendableCC list and innersol_list with both my coins and the offered coins
            # Firstly get the output coin
            my_output_coin = my_cc_spends.pop()
            spendable_cc_list = []
            innersol_list = []
            genesis_id = genesis_coin_id_for_genesis_coin_checker(
                Program.from_bytes(bytes.fromhex(colour)))
            # Make the rest of the coins assert the output coin is consumed
            for coloured_coin in my_cc_spends:
                inner_solution = self.wallet_state_manager.main_wallet.make_solution(
                    consumed=[my_output_coin.name()])
                inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash(
                    coloured_coin.puzzle_hash)
                assert inner_puzzle is not None

                sigs = await wallets[colour].get_sigs(inner_puzzle,
                                                      inner_solution,
                                                      coloured_coin.name())
                sigs.append(aggsig)
                aggsig = AugSchemeMPL.aggregate(sigs)

                lineage_proof = await wallets[
                    colour].get_lineage_proof_for_coin(coloured_coin)
                spendable_cc_list.append(
                    SpendableCC(coloured_coin, genesis_id, inner_puzzle,
                                lineage_proof))
                innersol_list.append(inner_solution)

            # Create SpendableCC for each of the coloured coins received
            for cc_coinsol_out in cc_coinsol_outamounts[colour]:
                cc_coinsol = cc_coinsol_out[0]
                puzzle = Program.from_bytes(bytes(cc_coinsol.puzzle_reveal))
                solution = Program.from_bytes(bytes(cc_coinsol.solution))

                r = uncurry_cc(puzzle)
                if r:
                    mod_hash, genesis_coin_checker, inner_puzzle = r
                    inner_solution = solution.first()
                    lineage_proof = solution.rest().rest().first()
                    spendable_cc_list.append(
                        SpendableCC(cc_coinsol.coin, genesis_id, inner_puzzle,
                                    lineage_proof))
                    innersol_list.append(inner_solution)

            # Finish the output coin SpendableCC with new information
            newinnerpuzhash = await wallets[colour].get_new_inner_hash()
            outputamount = sum([
                c.amount for c in my_cc_spends
            ]) + cc_discrepancies[colour] + my_output_coin.amount
            inner_solution = self.wallet_state_manager.main_wallet.make_solution(
                primaries=[{
                    "puzzlehash": newinnerpuzhash,
                    "amount": outputamount
                }])
            inner_puzzle = await self.get_inner_puzzle_for_puzzle_hash(
                my_output_coin.puzzle_hash)
            assert inner_puzzle is not None

            lineage_proof = await wallets[colour].get_lineage_proof_for_coin(
                my_output_coin)
            spendable_cc_list.append(
                SpendableCC(my_output_coin, genesis_id, inner_puzzle,
                            lineage_proof))
            innersol_list.append(inner_solution)

            sigs = await wallets[colour].get_sigs(inner_puzzle, inner_solution,
                                                  my_output_coin.name())
            sigs.append(aggsig)
            aggsig = AugSchemeMPL.aggregate(sigs)
            if spend_bundle is None:
                spend_bundle = spend_bundle_for_spendable_ccs(
                    CC_MOD,
                    Program.from_bytes(bytes.fromhex(colour)),
                    spendable_cc_list,
                    innersol_list,
                    [aggsig],
                )
            else:
                new_spend_bundle = spend_bundle_for_spendable_ccs(
                    CC_MOD,
                    Program.from_bytes(bytes.fromhex(colour)),
                    spendable_cc_list,
                    innersol_list,
                    [aggsig],
                )
                spend_bundle = SpendBundle.aggregate(
                    [spend_bundle, new_spend_bundle])
            # reset sigs and aggsig so that they aren't included next time around
            sigs = []
            aggsig = AugSchemeMPL.aggregate(sigs)
        my_tx_records = []
        if zero_spend_list is not None and spend_bundle is not None:
            zero_spend_list.append(spend_bundle)
            spend_bundle = SpendBundle.aggregate(zero_spend_list)

        if spend_bundle is None:
            return False, None, "spend_bundle missing"

        # Add transaction history for this trade
        now = uint64(int(time.time()))
        if chia_spend_bundle is not None:
            spend_bundle = SpendBundle.aggregate(
                [spend_bundle, chia_spend_bundle])
            # debug_spend_bundle(spend_bundle)
            if chia_discrepancy < 0:
                tx_record = TransactionRecord(
                    confirmed_at_height=uint32(0),
                    created_at_time=now,
                    to_puzzle_hash=token_bytes(),
                    amount=uint64(abs(chia_discrepancy)),
                    fee_amount=uint64(0),
                    confirmed=False,
                    sent=uint32(10),
                    spend_bundle=chia_spend_bundle,
                    additions=chia_spend_bundle.additions(),
                    removals=chia_spend_bundle.removals(),
                    wallet_id=uint32(1),
                    sent_to=[],
                    trade_id=std_hash(spend_bundle.name() + bytes(now)),
                    type=uint32(TransactionType.OUTGOING_TRADE.value),
                    name=chia_spend_bundle.name(),
                )
            else:
                tx_record = TransactionRecord(
                    confirmed_at_height=uint32(0),
                    created_at_time=uint64(int(time.time())),
                    to_puzzle_hash=token_bytes(),
                    amount=uint64(abs(chia_discrepancy)),
                    fee_amount=uint64(0),
                    confirmed=False,
                    sent=uint32(10),
                    spend_bundle=chia_spend_bundle,
                    additions=chia_spend_bundle.additions(),
                    removals=chia_spend_bundle.removals(),
                    wallet_id=uint32(1),
                    sent_to=[],
                    trade_id=std_hash(spend_bundle.name() + bytes(now)),
                    type=uint32(TransactionType.INCOMING_TRADE.value),
                    name=chia_spend_bundle.name(),
                )
            my_tx_records.append(tx_record)

        for colour, amount in cc_discrepancies.items():
            wallet = wallets[colour]
            if chia_discrepancy > 0:
                tx_record = TransactionRecord(
                    confirmed_at_height=uint32(0),
                    created_at_time=uint64(int(time.time())),
                    to_puzzle_hash=token_bytes(),
                    amount=uint64(abs(amount)),
                    fee_amount=uint64(0),
                    confirmed=False,
                    sent=uint32(10),
                    spend_bundle=spend_bundle,
                    additions=spend_bundle.additions(),
                    removals=spend_bundle.removals(),
                    wallet_id=wallet.id(),
                    sent_to=[],
                    trade_id=std_hash(spend_bundle.name() + bytes(now)),
                    type=uint32(TransactionType.OUTGOING_TRADE.value),
                    name=spend_bundle.name(),
                )
            else:
                tx_record = TransactionRecord(
                    confirmed_at_height=uint32(0),
                    created_at_time=uint64(int(time.time())),
                    to_puzzle_hash=token_bytes(),
                    amount=uint64(abs(amount)),
                    fee_amount=uint64(0),
                    confirmed=False,
                    sent=uint32(10),
                    spend_bundle=spend_bundle,
                    additions=spend_bundle.additions(),
                    removals=spend_bundle.removals(),
                    wallet_id=wallet.id(),
                    sent_to=[],
                    trade_id=std_hash(spend_bundle.name() + bytes(now)),
                    type=uint32(TransactionType.INCOMING_TRADE.value),
                    name=token_bytes(),
                )
            my_tx_records.append(tx_record)

        tx_record = TransactionRecord(
            confirmed_at_height=uint32(0),
            created_at_time=uint64(int(time.time())),
            to_puzzle_hash=token_bytes(),
            amount=uint64(0),
            fee_amount=uint64(0),
            confirmed=False,
            sent=uint32(0),
            spend_bundle=spend_bundle,
            additions=spend_bundle.additions(),
            removals=spend_bundle.removals(),
            wallet_id=uint32(0),
            sent_to=[],
            trade_id=std_hash(spend_bundle.name() + bytes(now)),
            type=uint32(TransactionType.OUTGOING_TRADE.value),
            name=spend_bundle.name(),
        )

        now = uint64(int(time.time()))
        trade_record: TradeRecord = TradeRecord(
            confirmed_at_index=uint32(0),
            accepted_at_time=now,
            created_at_time=now,
            my_offer=False,
            sent=uint32(0),
            spend_bundle=offer_spend_bundle,
            tx_spend_bundle=spend_bundle,
            additions=spend_bundle.additions(),
            removals=spend_bundle.removals(),
            trade_id=std_hash(spend_bundle.name() + bytes(now)),
            status=uint32(TradeStatus.PENDING_CONFIRM.value),
            sent_to=[],
        )

        await self.save_trade(trade_record)
        await self.wallet_state_manager.add_pending_transaction(tx_record)
        for tx in my_tx_records:
            await self.wallet_state_manager.add_transaction(tx)

        return True, trade_record, None
This roughly corresponds to bitcoin's taproot.
"""
import hashlib
from typing import Union

from blspy import G1Element, PrivateKey
from clvm.casts import int_from_bytes

from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32

from .load_clvm import load_clvm
from .p2_conditions import puzzle_for_conditions

DEFAULT_HIDDEN_PUZZLE = Program.from_bytes(bytes.fromhex("ff0980"))

DEFAULT_HIDDEN_PUZZLE_HASH = DEFAULT_HIDDEN_PUZZLE.get_tree_hash(
)  # this puzzle `(x)` always fails

MOD = load_clvm("p2_delegated_puzzle_or_hidden_puzzle.clvm")

SYNTHETIC_MOD = load_clvm("calculate_synthetic_public_key.clvm")

PublicKeyProgram = Union[bytes, Program]

GROUP_ORDER = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001


def calculate_synthetic_offset(public_key: G1Element,
                               hidden_puzzle_hash: bytes32) -> int:
def debug_spend_bundle(
    spend_bundle,
    agg_sig_additional_data=DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA
) -> None:
    """
    Print a lot of useful information about a `SpendBundle` that might help with debugging
    its clvm.
    """

    pks = []
    msgs = []

    created_coin_announcements: List[List[bytes]] = []
    asserted_coin_announcements = []
    created_puzzle_announcements: List[List[bytes]] = []
    asserted_puzzle_announcements = []

    print("=" * 80)
    for coin_spend in spend_bundle.coin_spends:
        coin = coin_spend.coin
        puzzle_reveal = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
        solution = Program.from_bytes(bytes(coin_spend.solution))
        coin_name = coin.name()

        if puzzle_reveal.get_tree_hash() != coin_spend.coin.puzzle_hash:
            print("*** BAD PUZZLE REVEAL")
            print(
                f"{puzzle_reveal.get_tree_hash().hex()} vs {coin_spend.coin.puzzle_hash.hex()}"
            )
            print("*" * 80)
            continue

        print(f"consuming coin {dump_coin(coin)}")
        print(f"  with id {coin_name}")
        print()
        print(
            f"\nbrun -y main.sym '{bu_disassemble(puzzle_reveal)}' '{bu_disassemble(solution)}'"
        )
        error, conditions, cost = conditions_dict_for_solution(
            puzzle_reveal, solution, INFINITE_COST)
        if error:
            print(f"*** error {error}")
        elif conditions is not None:
            for pk_bytes, m in pkm_pairs_for_conditions_dict(
                    conditions, coin_name, agg_sig_additional_data):
                pks.append(G1Element.from_bytes(pk_bytes))
                msgs.append(m)
            print()
            cost, r = puzzle_reveal.run_with_cost(INFINITE_COST,
                                                  solution)  # type: ignore
            print(disassemble(r))
            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) == 1:
                            as_prog = Program.to([c.opcode, c.vars[0]])
                        if len(c.vars) == 2:
                            as_prog = Program.to(
                                [c.opcode, c.vars[0], c.vars[1]])
                        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, [])
                ])
                print()
            else:
                print("(no output conditions generated)")
        print()
        print("-------")

    created = set(spend_bundle.additions())
    spent = set(spend_bundle.removals())

    zero_coin_set = 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")
    for coin in sorted(spent, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => spent coin id {coin.name()}")
    print()
    print("created coins")
    for coin in sorted(created, key=lambda _: _.name()):
        print(f"  {dump_coin(coin)}")
        print(f"      => created coin id {coin.name()}")

    if ephemeral:
        print()
        print("ephemeral coins")
        for coin in sorted(ephemeral, key=lambda _: _.name()):
            print(f"  {dump_coin(coin)}")
            print(f"      => created coin id {coin.name()}")

    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(
        set(_[-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(
        set(_[-1] for _ in created_puzzle_announcement_pairs)
        ^ set(asserted_puzzle_announcements))

    print()
    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()
    print()
    print("=" * 80)
    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"  msg_data: {[msg.hex()[:-128] for msg in msgs]}")
    print(f"  coin_ids: {[msg.hex()[-128:-64] for msg in msgs]}")
    print(f"  add_data: {[msg.hex()[-64:] for msg in msgs]}")
    print(f"signature: {spend_bundle.aggregated_signature}")