Exemplo n.º 1
0
    def validate_spend_bundle(
        self,
        spend_bundle: SpendBundle,
        now: CoinTimestamp,
    ) -> int:
        # this should use blockchain consensus code

        announcements: List[Announcement] = []
        conditions_dicts = []
        for coin_solution in spend_bundle.coin_solutions:
            err, conditions_dict, cost = conditions_dict_for_solution(
                coin_solution.puzzle_reveal, coin_solution.solution
            )
            if conditions_dict is None:
                raise BadSpendBundleError(f"clvm validation failure {err}")
            conditions_dicts.append(conditions_dict)
            announcements.extend(created_announcements_for_conditions_dict(conditions_dict, coin_solution.coin.name()))

        for coin_solution, conditions_dict in zip(spend_bundle.coin_solutions, conditions_dicts):
            prev_transaction_block_height = now.height
            timestamp = now.seconds
            coin_record = self._db[coin_solution.coin.name()]
            err = blockchain_check_conditions_dict(
                coin_record, announcements, conditions_dict, uint32(prev_transaction_block_height), uint64(timestamp)
            )
        if err is not None:
            raise BadSpendBundleError(f"condition validation failure {err}")

        return 0
Exemplo n.º 2
0
async def sign_coin_solutions(
    coin_solutions: List[CoinSolution],
    secret_key_for_public_key_f: Callable[[bytes], Optional[PrivateKey]],
) -> SpendBundle:
    signatures = []
    pk_list = []
    msg_list = []
    for coin_solution in coin_solutions:
        # Get AGG_SIG conditions
        err, conditions_dict, cost = conditions_dict_for_solution(coin_solution.puzzle_reveal, coin_solution.solution)
        if err or conditions_dict is None:
            error_msg = f"Sign transaction failed, con:{conditions_dict}, error: {err}"
            raise ValueError(error_msg)

        # Create signature
        for _, msg in pkm_pairs_for_conditions_dict(conditions_dict, bytes(coin_solution.coin.name())):
            pk_list.append(_)
            msg_list.append(msg)
            secret_key = secret_key_for_public_key_f(_)
            if secret_key is None:
                e_msg = f"no secret key for {_}"
                raise ValueError(e_msg)
            assert bytes(secret_key.get_g1()) == bytes(_)
            signature = AugSchemeMPL.sign(secret_key, msg)
            assert AugSchemeMPL.verify(_, msg, signature)
            signatures.append(signature)

    # Aggregate signatures
    aggsig = AugSchemeMPL.aggregate(signatures)
    assert AugSchemeMPL.aggregate_verify(pk_list, msg_list, aggsig)
    return SpendBundle(coin_solutions, aggsig)
Exemplo n.º 3
0
    async def sign(self, spend_bundle: SpendBundle) -> SpendBundle:
        sigs: List[G2Element] = []
        for spend in spend_bundle.coin_spends:
            matched, puzzle_args = match_cat_puzzle(
                spend.puzzle_reveal.to_program())
            if matched:
                _, _, inner_puzzle = puzzle_args
                puzzle_hash = inner_puzzle.get_tree_hash()
                pubkey, private = await self.wallet_state_manager.get_keys(
                    puzzle_hash)
                synthetic_secret_key = calculate_synthetic_secret_key(
                    private, DEFAULT_HIDDEN_PUZZLE_HASH)
                error, conditions, cost = conditions_dict_for_solution(
                    spend.puzzle_reveal.to_program(),
                    spend.solution.to_program(),
                    self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM,
                )
                if conditions is not None:
                    synthetic_pk = synthetic_secret_key.get_g1()
                    for pk, msg in pkm_pairs_for_conditions_dict(
                            conditions, spend.coin.name(),
                            self.wallet_state_manager.constants.
                            AGG_SIG_ME_ADDITIONAL_DATA):
                        try:
                            assert bytes(synthetic_pk) == pk
                            sigs.append(
                                AugSchemeMPL.sign(synthetic_secret_key, msg))
                        except AssertionError:
                            raise ValueError(
                                "This spend bundle cannot be signed by the CAT wallet"
                            )

        agg_sig = AugSchemeMPL.aggregate(sigs)
        return SpendBundle.aggregate([spend_bundle, SpendBundle([], agg_sig)])
Exemplo n.º 4
0
def get_output_amount_for_puzzle_and_solution(puzzle: Program, solution: Program) -> int:
    error, conditions, cost = conditions_dict_for_solution(puzzle, solution, INFINITE_COST)
    total = 0
    if conditions:
        for _ in conditions.get(ConditionOpcode.CREATE_COIN, []):
            total += Program.to(_.vars[1]).as_int()
    return total
Exemplo n.º 5
0
def additions_for_solution(coin_name: bytes32, puzzle_reveal: Program, solution: Program, max_cost: int) -> List[Coin]:
    """
    Checks the conditions created by CoinSolution and returns the list of all coins created
    """
    err, dic, cost = conditions_dict_for_solution(puzzle_reveal, solution, max_cost)
    if err or dic is None:
        return []
    return created_outputs_for_conditions_dict(dic, coin_name)
Exemplo n.º 6
0
def announcement_names_for_solution(coin_name: bytes, puzzle_reveal: Program,
                                    solution: Program) -> List[bytes32]:
    """
    Checks the conditions created by CoinSolution and returns the list of announcements
    """
    err, dic, cost = conditions_dict_for_solution(puzzle_reveal, solution)
    if err or dic is None:
        return []
    return created_announcement_names_for_conditions_dict(dic, coin_name)
Exemplo n.º 7
0
async def sign_coin_spends(
    coin_spends: List[CoinSpend],
    secret_key_for_public_key_f:
    Any,  # Potentially awaitable function from G1Element => Optional[PrivateKey]
    additional_data: bytes,
    max_cost: int,
) -> SpendBundle:
    """
    Sign_coin_spends runs the puzzle code with the given argument and searches the
    result for an AGG_SIG_ME condition, which it attempts to sign by requesting a
    matching PrivateKey corresponding with the given G1Element (public key) specified
    in the resulting condition output.

    It's important to note that as mentioned in the documentation about the standard
    spend that the public key presented to the secret_key_for_public_key_f function
    provided to sign_coin_spends must be prepared to do the key derivations required
    by the coin types it's allowed to spend (at least the derivation of the standard
    spend as done by calculate_synthetic_secret_key with DEFAULT_PUZZLE_HASH).

    If a coin performed a different key derivation, the pk presented to this function
    would be similarly alien, and would need to be tried against the first stage
    derived keys (those returned by master_sk_to_wallet_sk from the ['sk'] member of
    wallet rpc's get_private_key method).
    """
    signatures: List[blspy.G2Element] = []
    pk_list: List[blspy.G1Element] = []
    msg_list: List[bytes] = []
    for coin_spend in coin_spends:
        # Get AGG_SIG conditions
        err, conditions_dict, cost = conditions_dict_for_solution(
            coin_spend.puzzle_reveal, coin_spend.solution, max_cost)
        if err or conditions_dict is None:
            error_msg = f"Sign transaction failed, con:{conditions_dict}, error: {err}"
            raise ValueError(error_msg)

        # Create signature
        for pk_bytes, msg in pkm_pairs_for_conditions_dict(
                conditions_dict, coin_spend.coin.name(), additional_data):
            pk = blspy.G1Element.from_bytes(pk_bytes)
            pk_list.append(pk)
            msg_list.append(msg)
            if inspect.iscoroutinefunction(secret_key_for_public_key_f):
                secret_key = await secret_key_for_public_key_f(pk)
            else:
                secret_key = secret_key_for_public_key_f(pk)
            if secret_key is None:
                e_msg = f"no secret key for {pk}"
                raise ValueError(e_msg)
            assert bytes(secret_key.get_g1()) == bytes(pk)
            signature = AugSchemeMPL.sign(secret_key, msg)
            assert AugSchemeMPL.verify(pk, msg, signature)
            signatures.append(signature)

    # Aggregate signatures
    aggsig = AugSchemeMPL.aggregate(signatures)
    assert AugSchemeMPL.aggregate_verify(pk_list, msg_list, aggsig)
    return SpendBundle(coin_spends, aggsig)
Exemplo n.º 8
0
 async def get_sigs(self, innerpuz: Program, innersol: Program, coin_name: bytes32) -> List[G2Element]:
     puzzle_hash = innerpuz.get_tree_hash()
     pubkey, private = await self.wallet_state_manager.get_keys(puzzle_hash)
     synthetic_secret_key = calculate_synthetic_secret_key(private, DEFAULT_HIDDEN_PUZZLE_HASH)
     sigs: List[G2Element] = []
     error, conditions, cost = conditions_dict_for_solution(innerpuz, innersol)
     if conditions is not None:
         for _, msg in pkm_pairs_for_conditions_dict(
             conditions, coin_name, self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA
         ):
             signature = AugSchemeMPL.sign(synthetic_secret_key, msg)
             sigs.append(signature)
     return sigs
Exemplo n.º 9
0
def fee_for_solution(puzzle_reveal: SerializedProgram,
                     solution: SerializedProgram, max_cost: int) -> int:
    err, dic, cost = conditions_dict_for_solution(puzzle_reveal, solution,
                                                  max_cost)
    if err or dic is None:
        return 0

    total = 0
    for cvp in dic.get(ConditionOpcode.RESERVE_FEE, []):
        amount_bin = cvp.vars[0]
        amount = int_from_bytes(amount_bin)
        total += amount
    return total
Exemplo n.º 10
0
def sign_tx(pks: List[str], spend_bundle: SpendBundle):
    # This field is the ADDITIONAL_DATA found in the constants
    additional_data: bytes = bytes.fromhex(
        "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb")
    puzzle_hash_to_sk: Dict[bytes32, PrivateKey] = {}

    for p in pks:
        child_sk: PrivateKey = PrivateKey.from_bytes(bytes.fromhex(p))
        # master_private_key = PrivateKey.from_bytes(
        #     bytes.fromhex(p))
        # child_sk = master_sk_to_wallet_sk(master_private_key, 242)
        child_pk: G1Element = child_sk.get_g1()
        puzzle = puzzle_for_pk(child_pk)
        puzzle_hash = puzzle.get_tree_hash()
        puzzle_hash_to_sk[puzzle_hash] = child_sk

    aggregate_signature: G2Element = G2Element()
    for coin_solution in spend_bundle.coin_solutions:
        if coin_solution.coin.puzzle_hash not in puzzle_hash_to_sk:
            return
        sk: PrivateKey = puzzle_hash_to_sk[coin_solution.coin.puzzle_hash]
        synthetic_secret_key: PrivateKey = calculate_synthetic_secret_key(
            sk, DEFAULT_HIDDEN_PUZZLE_HASH)

        err, conditions_dict, cost = conditions_dict_for_solution(
            coin_solution.puzzle_reveal, coin_solution.solution, 11000000000)

        if err or conditions_dict is None:
            print(
                f"Sign transaction failed, con:{conditions_dict}, error: {err}"
            )
            return

        pk_msgs = pkm_pairs_for_conditions_dict(
            conditions_dict, bytes(coin_solution.coin.name()), additional_data)
        assert len(pk_msgs) == 1
        _, msg = pk_msgs[0]
        signature = AugSchemeMPL.sign(synthetic_secret_key, msg)

        aggregate_signature = AugSchemeMPL.aggregate(
            [aggregate_signature, signature])

    new_spend_bundle = SpendBundle(spend_bundle.coin_solutions,
                                   aggregate_signature)
    # print(json.dumps(new_spend_bundle.to_json_dict()))
    return json.dumps(new_spend_bundle.to_json_dict())
Exemplo n.º 11
0
    def validate_spend_bundle(
        self,
        spend_bundle: SpendBundle,
        now: CoinTimestamp,
        max_cost: int,
    ) -> int:
        # this should use blockchain consensus code

        coin_announcements: Set[bytes32] = set()
        puzzle_announcements: Set[bytes32] = set()

        conditions_dicts = []
        for coin_solution in spend_bundle.coin_solutions:
            err, conditions_dict, cost = conditions_dict_for_solution(
                coin_solution.puzzle_reveal, coin_solution.solution, max_cost)
            if conditions_dict is None:
                raise BadSpendBundleError(f"clvm validation failure {err}")
            conditions_dicts.append(conditions_dict)
            coin_announcements.update(
                coin_announcement_names_for_conditions_dict(
                    conditions_dict, coin_solution.coin.name()))
            puzzle_announcements.update(
                puzzle_announcement_names_for_conditions_dict(
                    conditions_dict, coin_solution.coin.puzzle_hash))

        for coin_solution, conditions_dict in zip(spend_bundle.coin_solutions,
                                                  conditions_dicts):
            prev_transaction_block_height = now.height
            timestamp = now.seconds
            coin_record = self._db[coin_solution.coin.name()]
            err = mempool_check_conditions_dict(
                coin_record,
                coin_announcements,
                puzzle_announcements,
                conditions_dict,
                uint32(prev_transaction_block_height),
                uint64(timestamp),
            )
        if err is not None:
            raise BadSpendBundleError(f"condition validation failure {err}")

        return 0
Exemplo n.º 12
0
async def sign_coin_solutions(
    coin_solutions: List[CoinSolution],
    secret_key_for_public_key_f:
    Any,  # Potentially awaitable function from G1Element => Optional[PrivateKey]
    additional_data: bytes,
    max_cost: int,
) -> SpendBundle:
    signatures: List[blspy.G2Element] = []
    pk_list: List[blspy.G1Element] = []
    msg_list: List[bytes] = []
    for coin_solution in coin_solutions:
        # Get AGG_SIG conditions
        err, conditions_dict, cost = conditions_dict_for_solution(
            coin_solution.puzzle_reveal, coin_solution.solution, max_cost)
        if err or conditions_dict is None:
            error_msg = f"Sign transaction failed, con:{conditions_dict}, error: {err}"
            raise ValueError(error_msg)

        # Create signature
        for pk, msg in pkm_pairs_for_conditions_dict(
                conditions_dict, bytes(coin_solution.coin.name()),
                additional_data):
            pk_list.append(pk)
            msg_list.append(msg)
            if inspect.iscoroutinefunction(secret_key_for_public_key_f):
                secret_key = await secret_key_for_public_key_f(pk)
            else:
                secret_key = secret_key_for_public_key_f(pk)
            if secret_key is None:
                e_msg = f"no secret key for {pk}"
                raise ValueError(e_msg)
            assert bytes(secret_key.get_g1()) == bytes(pk)
            signature = AugSchemeMPL.sign(secret_key, msg)
            assert AugSchemeMPL.verify(pk, msg, signature)
            signatures.append(signature)

    # Aggregate signatures
    aggsig = AugSchemeMPL.aggregate(signatures)
    assert AugSchemeMPL.aggregate_verify(pk_list, msg_list, aggsig)
    return SpendBundle(coin_solutions, aggsig)
Exemplo n.º 13
0
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}")
Exemplo n.º 14
0
def spend_bundle_for_spendable_ccs(
    mod_code: Program,
    genesis_coin_checker: Program,
    spendable_cc_list: List[SpendableCC],
    inner_solutions: List[Program],
    sigs: Optional[List[G2Element]] = [],
) -> SpendBundle:
    """
    Given a list of `SpendableCC` objects and inner solutions for those objects, create a `SpendBundle`
    that spends all those coins. Note that it the signature is not calculated it, so the caller is responsible
    for fixing it.
    """

    N = len(spendable_cc_list)

    if len(inner_solutions) != N:
        raise ValueError(
            "spendable_cc_list and inner_solutions are different lengths")

    input_coins = [_.coin for _ in spendable_cc_list]

    # figure out what the output amounts are by running the inner puzzles & solutions
    output_amounts = []
    for cc_spend_info, inner_solution in zip(spendable_cc_list,
                                             inner_solutions):
        error, conditions, cost = conditions_dict_for_solution(
            cc_spend_info.inner_puzzle, inner_solution, INFINITE_COST)
        total = 0
        if conditions:
            for _ in conditions.get(ConditionOpcode.CREATE_COIN, []):
                total += Program.to(_.vars[1]).as_int()
        output_amounts.append(total)

    coin_spends = []

    deltas = [input_coins[_].amount - output_amounts[_] for _ in range(N)]
    subtotals = subtotals_for_deltas(deltas)

    if sum(deltas) != 0:
        raise ValueError("input and output amounts don't match")

    bundles = [bundle_for_spendable_cc_list(_) for _ in spendable_cc_list]

    for index in range(N):
        cc_spend_info = spendable_cc_list[index]

        puzzle_reveal = cc_puzzle_for_inner_puzzle(mod_code,
                                                   genesis_coin_checker,
                                                   cc_spend_info.inner_puzzle)

        prev_index = (index - 1) % N
        next_index = (index + 1) % N
        prev_bundle = bundles[prev_index]
        my_bundle = bundles[index]
        next_bundle = bundles[next_index]

        solution = [
            inner_solutions[index],
            prev_bundle,
            my_bundle,
            next_bundle,
            subtotals[index],
        ]
        coin_spend = CoinSpend(input_coins[index], puzzle_reveal,
                               Program.to(solution))
        coin_spends.append(coin_spend)

    if sigs is None or sigs == []:
        return SpendBundle(coin_spends, NULL_SIGNATURE)
    else:
        return SpendBundle(coin_spends, AugSchemeMPL.aggregate(sigs))
Exemplo n.º 15
0
def unsigned_spend_bundle_for_spendable_cats(
        mod_code: Program,
        spendable_cat_list: List[SpendableCAT]) -> SpendBundle:
    """
    Given a list of `SpendableCAT` objects, create a `SpendBundle` that spends all those coins.
    Note that no signing is done here, so it falls on the caller to sign the resultant bundle.
    """

    N = len(spendable_cat_list)

    # figure out what the deltas are by running the inner puzzles & solutions
    deltas = []
    for spend_info in spendable_cat_list:
        error, conditions, cost = conditions_dict_for_solution(
            spend_info.inner_puzzle, spend_info.inner_solution, INFINITE_COST)
        total = spend_info.extra_delta * -1
        if conditions:
            for _ in conditions.get(ConditionOpcode.CREATE_COIN, []):
                if _.vars[1] != b"\x8f":  # -113 in bytes
                    total += Program.to(_.vars[1]).as_int()
        deltas.append(spend_info.coin.amount - total)

    if sum(deltas) != 0:
        raise ValueError("input and output amounts don't match")

    subtotals = subtotals_for_deltas(deltas)

    infos_for_next = []
    infos_for_me = []
    ids = []
    for _ in spendable_cat_list:
        infos_for_next.append(next_info_for_spendable_cat(_))
        infos_for_me.append(Program.to(_.coin.as_list()))
        ids.append(_.coin.name())

    coin_spends = []
    for index in range(N):
        spend_info = spendable_cat_list[index]

        puzzle_reveal = construct_cat_puzzle(
            mod_code, spend_info.limitations_program_hash,
            spend_info.inner_puzzle)

        prev_index = (index - 1) % N
        next_index = (index + 1) % N
        prev_id = ids[prev_index]
        my_info = infos_for_me[index]
        next_info = infos_for_next[next_index]

        solution = [
            spend_info.inner_solution,
            spend_info.lineage_proof.to_program(),
            prev_id,
            my_info,
            next_info,
            subtotals[index],
            spend_info.extra_delta,
        ]
        coin_spend = CoinSpend(spend_info.coin, puzzle_reveal,
                               Program.to(solution))
        coin_spends.append(coin_spend)

    return SpendBundle(coin_spends, NULL_SIGNATURE)
def debug_spend_bundle(spend_bundle: SpendBundle) -> None:
    """
    Print a lot of useful information about a `SpendBundle` that might help with debugging
    its clvm.
    """

    pks = []
    msgs = []

    created_announcements: List[List[bytes]] = []
    asserted_annoucements = []

    print("=" * 80)
    for coin_solution in spend_bundle.coin_solutions:
        coin = coin_solution.coin
        puzzle_reveal = coin_solution.puzzle_reveal
        solution = coin_solution.solution
        coin_name = coin.name()

        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, m in pkm_pairs_for_conditions_dict(conditions, coin_name,
                                                       bytes([3] * 32)):
                pks.append(pk)
                msgs.append(m)
            print()
            r = puzzle_reveal.run(solution)
            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_announcements.extend(
                    [coin_name] + _.vars for _ in conditions.get(
                        ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, []))
                asserted_annoucements.extend([
                    _.vars[0].hex() for _ in conditions.get(
                        ConditionOpcode.ASSERT_COIN_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_announcement_pairs = [(_, std_hash(b"".join(_)).hex())
                                  for _ in created_announcements]
    if created_announcements:
        print("created announcements")
        for announcement, hashed in sorted(created_announcement_pairs,
                                           key=lambda _: _[-1]):
            as_hex = [f"0x{_.hex()}" for _ in announcement]
            print(f"  {as_hex} =>\n      {hashed}")

    eor_announcements = sorted(
        set(_[-1]
            for _ in created_announcement_pairs) ^ set(asserted_annoucements))

    print()
    print()
    print(f"zero_coin_set = {sorted(zero_coin_set)}")
    print()
    print(
        f"created announcements = {sorted([_[-1] for _ in created_announcement_pairs])}"
    )
    print()
    print(f"asserted announcements = {sorted(asserted_annoucements)}")
    print()
    print(f"symdiff of announcements = {sorted(eor_announcements)}")
    print()
    print()
    print("=" * 80)
    print()
    validates = AugSchemeMPL.aggregate_verify(
        pks, msgs, spend_bundle.aggregated_signature)
    print(f"aggregated signature check pass: {validates}")
Exemplo n.º 17
0
def sign_tx(intermediate_sk: PrivateKey, spend_bundle: SpendBundle,
            use_hardened_keys: bool):
    """
    Takes in an unsigned transaction (called a spend bundle in chia), and a 24 word mnemonic (master sk)
    and generates the aggregate BLS signature for the transaction.
    """

    # This field is the ADDITIONAL_DATA found in the constants
    additional_data: bytes = bytes.fromhex(
        "ccd5bb71183532bff220ba46c268991a3ff07eb358e8255a65c30a2dce0e5fbb")
    puzzle_hash_to_sk: Dict[bytes32, PrivateKey] = {}

    if use_hardened_keys:
        # Change this loop to scan more keys if you have more
        for i in range(5000):
            child_sk: PrivateKey = AugSchemeMPL.derive_child_sk(
                intermediate_sk, i)
            child_pk: G1Element = child_sk.get_g1()
            puzzle = puzzle_for_pk(child_pk)
            puzzle_hash = puzzle.get_tree_hash()
            puzzle_hash_to_sk[puzzle_hash] = child_sk
    else:
        # Change this loop to scan more keys if you have more
        for i in range(5000):
            child_sk: PrivateKey = AugSchemeMPL.derive_child_sk_unhardened(
                intermediate_sk, i)
            child_pk: G1Element = child_sk.get_g1()
            puzzle = puzzle_for_pk(child_pk)
            puzzle_hash = puzzle.get_tree_hash()
            puzzle_hash_to_sk[puzzle_hash] = child_sk

    aggregate_signature: G2Element = G2Element()
    for coin_solution in spend_bundle.coin_solutions:
        if coin_solution.coin.puzzle_hash not in puzzle_hash_to_sk:
            print(
                f"Puzzle hash {coin_solution.coin.puzzle_hash} not found for this key."
            )
            return
        sk: PrivateKey = puzzle_hash_to_sk[coin_solution.coin.puzzle_hash]
        synthetic_secret_key: PrivateKey = calculate_synthetic_secret_key(
            sk, DEFAULT_HIDDEN_PUZZLE_HASH)

        err, conditions_dict, cost = conditions_dict_for_solution(
            coin_solution.puzzle_reveal, coin_solution.solution, 11000000000)

        if err or conditions_dict is None:
            print(
                f"Sign transaction failed, con:{conditions_dict}, error: {err}"
            )
            return

        pk_msgs = pkm_pairs_for_conditions_dict(
            conditions_dict, bytes(coin_solution.coin.name()), additional_data)
        assert len(pk_msgs) == 1
        _, msg = pk_msgs[0]
        signature = AugSchemeMPL.sign(synthetic_secret_key, msg)

        aggregate_signature = AugSchemeMPL.aggregate(
            [aggregate_signature, signature])

    new_spend_bundle = SpendBundle(spend_bundle.coin_solutions,
                                   aggregate_signature)
    print("")
    print("Signed spend bundle JSON:\n")
    print(json.dumps(new_spend_bundle.to_json_dict()))
Exemplo n.º 18
0
    def validate_spend_bundle(
        self,
        spend_bundle: SpendBundle,
        now: CoinTimestamp,
        max_cost: int,
    ) -> int:
        # this should use blockchain consensus code

        coin_announcements: Set[bytes32] = set()
        puzzle_announcements: Set[bytes32] = set()

        conditions_dicts = []
        for coin_solution in spend_bundle.coin_solutions:
            assert isinstance(coin_solution.coin, Coin)
            err, conditions_dict, cost = conditions_dict_for_solution(
                coin_solution.puzzle_reveal, coin_solution.solution, max_cost)
            if conditions_dict is None:
                raise BadSpendBundleError(f"clvm validation failure {err}")
            conditions_dicts.append(conditions_dict)
            coin_announcements.update(
                coin_announcement_names_for_conditions_dict(
                    conditions_dict,
                    coin_solution.coin,
                ))
            puzzle_announcements.update(
                puzzle_announcement_names_for_conditions_dict(
                    conditions_dict,
                    coin_solution.coin,
                ))

        ephemeral_db = dict(self._db)
        for coin in spend_bundle.additions():
            name = coin.name()
            ephemeral_db[name] = CoinRecord(
                coin,
                uint32(now.height),
                uint32(0),
                False,
                False,
                uint64(now.seconds),
            )

        for coin_solution, conditions_dict in zip(spend_bundle.coin_solutions,
                                                  conditions_dicts):  # noqa
            prev_transaction_block_height = now.height
            timestamp = now.seconds
            coin_record = ephemeral_db.get(coin_solution.coin.name())
            if coin_record is None:
                raise BadSpendBundleError(
                    f"coin not found for id 0x{coin_solution.coin.name().hex()}"
                )  # noqa
            err = mempool_check_conditions_dict(
                coin_record,
                coin_announcements,
                puzzle_announcements,
                conditions_dict,
                uint32(prev_transaction_block_height),
                uint64(timestamp),
            )
        if err is not None:
            raise BadSpendBundleError(f"condition validation failure {err}")

        return 0