chia-blockchain/chia/wallet/puzzles/test_cc.py

245 lines
8.1 KiB
Python

# this is used to iterate on `cc.clvm` to ensure that it's producing the sort
# of output that we expect
from typing import Dict, List, Optional, Tuple
from blspy import G2Element
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.condition_opcodes import ConditionOpcode
from chia.types.spend_bundle import CoinSolution, SpendBundle
from chia.util.ints import uint64
from chia.wallet.cc_wallet.cc_utils import (
CC_MOD,
cc_puzzle_for_inner_puzzle,
cc_puzzle_hash_for_inner_puzzle_hash,
spend_bundle_for_spendable_ccs,
spendable_cc_list_from_coin_solution,
)
from chia.wallet.cc_wallet.debug_spend_bundle import debug_spend_bundle
from chia.wallet.puzzles.genesis_by_coin_id_with_0 import create_genesis_or_zero_coin_checker
from chia.wallet.puzzles.genesis_by_puzzle_hash_with_0 import create_genesis_puzzle_or_zero_coin_checker
CONDITIONS = dict((k, bytes(v)[0]) for k, v in ConditionOpcode.__members__.items()) # pylint: disable=E1101
NULL_SIGNATURE = G2Element()
ANYONE_CAN_SPEND_PUZZLE = Program.to(1) # simply return the conditions
PUZZLE_TABLE: Dict[bytes32, Program] = dict((_.get_tree_hash(), _) for _ in [ANYONE_CAN_SPEND_PUZZLE])
def hash_to_puzzle_f(puzzle_hash: bytes32) -> Optional[Program]:
return PUZZLE_TABLE.get(puzzle_hash)
def add_puzzles_to_puzzle_preimage_db(puzzles: List[Program]) -> None:
for _ in puzzles:
PUZZLE_TABLE[_.get_tree_hash()] = _
def int_as_bytes32(v: int) -> bytes32:
return v.to_bytes(32, byteorder="big")
def generate_farmed_coin(
block_index: int,
puzzle_hash: bytes32,
amount: int,
) -> Coin:
"""
Generate a (fake) coin which can be used as a starting point for a chain
of coin tests.
"""
return Coin(int_as_bytes32(block_index), puzzle_hash, uint64(amount))
def issue_cc_from_farmed_coin(
mod_code: Program,
coin_checker_for_farmed_coin,
block_id: int,
inner_puzzle_hash: bytes32,
amount: int,
) -> Tuple[Program, SpendBundle]:
"""
This is an example of how to issue a cc.
"""
# get a farmed coin
farmed_puzzle = ANYONE_CAN_SPEND_PUZZLE
farmed_puzzle_hash = farmed_puzzle.get_tree_hash()
# mint a cc
farmed_coin = generate_farmed_coin(block_id, farmed_puzzle_hash, amount=uint64(amount))
genesis_coin_checker = coin_checker_for_farmed_coin(farmed_coin)
minted_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash(mod_code, genesis_coin_checker, inner_puzzle_hash)
output_conditions = [[ConditionOpcode.CREATE_COIN, minted_cc_puzzle_hash, farmed_coin.amount]]
# for this very simple puzzle, the solution is simply the output conditions
# this is just a coincidence... for more complicated puzzles, you'll likely have to do some real work
solution = Program.to(output_conditions)
coin_solution = CoinSolution(farmed_coin, farmed_puzzle, solution)
spend_bundle = SpendBundle([coin_solution], NULL_SIGNATURE)
return genesis_coin_checker, spend_bundle
def solution_for_pay_to_any(puzzle_hash_amount_pairs: List[Tuple[bytes32, int]]) -> Program:
output_conditions = [
[ConditionOpcode.CREATE_COIN, puzzle_hash, amount] for puzzle_hash, amount in puzzle_hash_amount_pairs
]
return Program.to(output_conditions)
def test_spend_through_n(mod_code, coin_checker_for_farmed_coin, n):
"""
Test to spend ccs from a farmed coin to a cc genesis coin, then to N outputs,
then joining back down to two outputs.
"""
################################
# spend from a farmed coin to a cc genesis coin
# get a farmed coin
eve_inner_puzzle = ANYONE_CAN_SPEND_PUZZLE
eve_inner_puzzle_hash = eve_inner_puzzle.get_tree_hash()
# generate output values [0x100, 0x200, ...]
output_values = [0x100 + 0x100 * _ for _ in range(n)]
total_minted = sum(output_values)
genesis_coin_checker, spend_bundle = issue_cc_from_farmed_coin(
mod_code, coin_checker_for_farmed_coin, 1, eve_inner_puzzle_hash, total_minted
)
# hack the wrapped puzzles into the PUZZLE_TABLE DB
puzzles_for_db = [cc_puzzle_for_inner_puzzle(mod_code, genesis_coin_checker, eve_inner_puzzle)]
add_puzzles_to_puzzle_preimage_db(puzzles_for_db)
debug_spend_bundle(spend_bundle)
################################
# collect up the spendable coins
spendable_cc_list = []
for coin_solution in spend_bundle.coin_solutions:
spendable_cc_list.extend(spendable_cc_list_from_coin_solution(coin_solution, hash_to_puzzle_f))
# now spend the genesis coin cc to N outputs
output_conditions = solution_for_pay_to_any([(eve_inner_puzzle_hash, _) for _ in output_values])
inner_puzzle_solution = Program.to(output_conditions)
spend_bundle = spend_bundle_for_spendable_ccs(
mod_code,
genesis_coin_checker,
spendable_cc_list,
[inner_puzzle_solution],
)
debug_spend_bundle(spend_bundle)
################################
# collect up the spendable coins
spendable_cc_list = []
for coin_solution in spend_bundle.coin_solutions:
spendable_cc_list.extend(spendable_cc_list_from_coin_solution(coin_solution, hash_to_puzzle_f))
# now spend N inputs to two outputs
output_amounts = ([0] * (n - 2)) + [0x1, total_minted - 1]
inner_solutions = [
solution_for_pay_to_any([(eve_inner_puzzle_hash, amount)] if amount else []) for amount in output_amounts
]
spend_bundle = spend_bundle_for_spendable_ccs(
mod_code,
genesis_coin_checker,
spendable_cc_list,
inner_solutions,
)
debug_spend_bundle(spend_bundle)
def test_spend_zero_coin(mod_code: Program, coin_checker_for_farmed_coin):
"""
Test to spend ccs from a farmed coin to a cc genesis coin, then to N outputs,
then joining back down to two outputs.
"""
eve_inner_puzzle = ANYONE_CAN_SPEND_PUZZLE
eve_inner_puzzle_hash = eve_inner_puzzle.get_tree_hash()
total_minted = 0x111
genesis_coin_checker, spend_bundle = issue_cc_from_farmed_coin(
mod_code, coin_checker_for_farmed_coin, 1, eve_inner_puzzle_hash, total_minted
)
puzzles_for_db = [cc_puzzle_for_inner_puzzle(mod_code, genesis_coin_checker, eve_inner_puzzle)]
add_puzzles_to_puzzle_preimage_db(puzzles_for_db)
eve_cc_list = []
for _ in spend_bundle.coin_solutions:
eve_cc_list.extend(spendable_cc_list_from_coin_solution(_, hash_to_puzzle_f))
assert len(eve_cc_list) == 1
eve_cc_spendable = eve_cc_list[0]
# farm regular chia
farmed_coin = generate_farmed_coin(2, eve_inner_puzzle_hash, amount=500)
# create a zero cc from this farmed coin
wrapped_cc_puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash(mod_code, genesis_coin_checker, eve_inner_puzzle_hash)
solution = solution_for_pay_to_any([(wrapped_cc_puzzle_hash, 0)])
coin_solution = CoinSolution(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, solution)
spendable_cc_list = spendable_cc_list_from_coin_solution(coin_solution, hash_to_puzzle_f)
assert len(spendable_cc_list) == 1
zero_cc_spendable = spendable_cc_list[0]
# we have our zero coin
# now try to spend it
spendable_cc_list = [eve_cc_spendable, zero_cc_spendable]
inner_solutions = [
solution_for_pay_to_any([]),
solution_for_pay_to_any([(wrapped_cc_puzzle_hash, eve_cc_spendable.coin.amount)]),
]
spend_bundle = spend_bundle_for_spendable_ccs(mod_code, genesis_coin_checker, spendable_cc_list, inner_solutions)
debug_spend_bundle(spend_bundle)
def main():
mod_code = CC_MOD
def coin_checker_for_farmed_coin_by_coin_id(coin: Coin):
return create_genesis_or_zero_coin_checker(coin.name())
test_spend_through_n(mod_code, coin_checker_for_farmed_coin_by_coin_id, 12)
test_spend_zero_coin(mod_code, coin_checker_for_farmed_coin_by_coin_id)
def coin_checker_for_farmed_coin_by_puzzle_hash(coin: Coin):
return create_genesis_puzzle_or_zero_coin_checker(coin.puzzle_hash)
test_spend_through_n(mod_code, coin_checker_for_farmed_coin_by_puzzle_hash, 10)
if __name__ == "__main__":
main()