💾 Archived View for 0x735.com › chialisp.gmi captured on 2021-11-30 at 14:23:00. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
import pytest import json import asyncio from pathlib import Path from typing import Dict, List, Optional from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.blockchain_format.program import Program from chia.types.condition_opcodes import ConditionOpcode from chia.util.ints import uint64 from chia.util.hash import std_hash from clvm.casts import int_to_bytes import cdv.clibs as std_lib from cdv.util.load_clvm import load_clvm from chia.util.ints import uint64 from chia.types.spend_bundle import SpendBundle from mysim import CoinWrapper, SmartCoinWrapper from mysim import setup as setup_sim from chia.consensus.default_constants import DEFAULT_CONSTANTS from blspy import AugSchemeMPL, G1Element, G2Element, PrivateKey from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import ( # standard_transaction puzzle_for_pk, calculate_synthetic_secret_key, DEFAULT_HIDDEN_PUZZLE_HASH, ) from chia.types.coin_spend import CoinSpend from clvm_tools.clvmc import compile_clvm from chia.types.blockchain_format.program import Program, SerializedProgram
async def create_sim(): network, oracle, alice, bob = await setup_sim() await network.farm_block() await network.farm_block(farmer=oracle) await network.farm_block(farmer=alice) await network.farm_block(farmer=bob) return network, oracle, alice, bob async def destroy_sim(network): await network.close() def load_program(clsp_filename): clsp_path = Path("clsp") full_path = clsp_path/clsp_filename hex_filename = f"{clsp_filename}.hex" output = clsp_path/hex_filename compile_clvm(full_path, output, search_paths=[Path("include")]) with open(output, "r") as f: clvm_hex = f.read() clvm_blob = bytes.fromhex(clvm_hex) return Program.from_bytes(clvm_blob) async def lookup_puzhash(network, puzhash): info = await network.sim_client.get_coin_records_by_puzzle_hash(puzhash) return info
This is a test to send xch from alice to bob using the standard transaction
In this puzzle program, the solution is ignored. The reveal of the puzzle returns a fixed list of conditions. This roughly corresponds to OP_SECURETHEBAG in bitcoin.
(mod (conditions) (qq (q . (unquote conditions))) )
Setup Simulator:
network, oracle, alice, bob = asyncio.run(create_sim())
# Load the clvm P2_CONDITIONS_MOD = load_clvm('p2_conditions.clvm', 'clsp') # define the conditions # What is the required format here? Should it be a list of lists? conditions = [[51, 0x0fedbeef, 21]] # run (compile) the clvm to get the puzzle p2_conditions_puzzle = P2_CONDITIONS_MOD.run([conditions]) # create solution # How does program.to interpret the list given as input? p2_conditions_solution = Program.to([p2_conditions_puzzle, 0])
Because the coins are in wallets that require signatures to be accessed, in order to use this puzzle we have to treat it as a delegated puzzle.
The idea of the delegated puzzle is that it gets signed during creation so only the right signature can unlock it.
In this puzzle program, the solution must be a signed delegated puzzle, along with its (unsigned) solution. The delegated puzzle is executed, passing in the solution. This obviously could be done recursively, arbitrarily deep (as long as the maximum cost is not exceeded). If you want to specify the conditions directly (thus terminating the potential recursion), you can use p2_conditions. This roughly corresponds to bitcoin's graftroot.
(mod (PUBKEY delegated_puzzle delegated_puzzle_solution) (include condition_codes.clib) ;; hash a tree ;; This is used to calculate a puzzle hash given a puzzle program. (defun sha256tree1 (TREE) (if (l TREE) (sha256 2 (sha256tree1 (f TREE)) (sha256tree1 (r TREE))) (sha256 1 TREE) ) ) (c (list AGG_SIG_ME PUBKEY (sha256tree1 delegated_puzzle)) (a delegated_puzzle delegated_puzzle_solution)) )
# Define some values public_key = alice.pk() # Load the p2_delegated_puzzle clvm file. P2_DELEGATED_PUZZLE_MOD = load_clvm('p2_delegated_puzzle.clvm', 'clsp') p2_del_puz_puzzle = P2_DELEGATED_PUZZLE_MOD.curry(public_key) # Create the delegated puzzle. This is just the p2_conditions puzzle P2_CONDITIONS_MOD = load_clvm('p2_conditions.clvm', 'clsp')
# First we just capture the balance of alice and bob's wallets. alice_start_bal = alice.balance() bob_start_bal = bob.balance() amount = 100 puzzle = bob.puzzle found_coin = asyncio.run(alice.choose_coin(amount)) c1 = [ConditionOpcode.CREATE_COIN, bob.puzzle_hash, amount] c2 = [ConditionOpcode.CREATE_COIN, alice.puzzle_hash, found_coin.amount - amount] conditions = [c1, c2] delegated_puzzle_solution = Program.to((1, conditions)) solution = Program.to([[], delegated_puzzle_solution, []]) # What's going on with solution here # The chialisp program p2_delegated_puzzle takes 3 arguments: # - public_key # - delegated_puzzle # - delegated_puzzle_solution # The delegated puzzle_solution is the program that returns all the conditions, so in terms # of the chialisp program, it is the delegated puzzle. It seems to me that the naming here # is quite confusing... in the python above we are calling it a delegated puzzle solution, when # in fact it is a puzzle that has solution (). signature = AugSchemeMPL.sign( calculate_synthetic_secret_key(alice.sk_, DEFAULT_HIDDEN_PUZZLE_HASH), ( delegated_puzzle_solution.get_tree_hash() + found_coin.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA ), ) spend_bundle = SpendBundle( [ CoinSpend( found_coin.as_coin(), # Coin to spend alice.puzzle, # Puzzle used for found_coin solution, # The solution to the puzzle locking found_coin ) ], signature, ) res = asyncio.run(network.push_tx(spend_bundle)) # Assuming this is successful we should see alice's balance decrease by 100 and bobs # balance increase by 100: print("Alice:") print("Open:\t{}".format(alice_start_bal)) print("Final:\t{}\n".format(alice.balance())) print("Bob:") print("Open:\t{}".format(bob_start_bal)) print("Final:\t{}\n".format(bob.balance()))
The custom puzzle is just a program that returns (51 bob.puzzle_hash, amount). So it doesn't need a solution.
(mod (PUZZLE_HASH AMOUNT) (include condition_codes.clib) (list (list CREATE_COIN PUZZLE_HASH AMOUNT) ) )
amount = 94 CUSTOM_PUZ_MOD = load_clvm('alice_custom_puzzle.clsp', 'clsp') # When currying, just enter the values in the order they occur, # and do not put them in a list!! custom_puzzle = CUSTOM_PUZ_MOD.curry(bob.puzzle_hash, amount) found_coin = asyncio.run(alice.choose_coin(amount)) c1 = [ConditionOpcode.CREATE_COIN, custom_puzzle.get_tree_hash(), amount] c2 = [ConditionOpcode.CREATE_COIN, alice.puzzle_hash, found_coin.amount - amount] conditions = [c1, c2] delegated_puzzle_solution = Program.to((1, conditions)) solution = Program.to([[], delegated_puzzle_solution, []]) signature = AugSchemeMPL.sign( calculate_synthetic_secret_key(alice.sk_, DEFAULT_HIDDEN_PUZZLE_HASH), ( delegated_puzzle_solution.get_tree_hash() + found_coin.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA ), ) spend_bundle = SpendBundle( [ CoinSpend( found_coin.as_coin(), # Coin to spend alice.puzzle, # Puzzle used for found_coin solution, # The solution to the puzzle locking found_coin ) ], signature, ) res = asyncio.run(network.push_tx(spend_bundle)) print(res) # update = asyncio.run(network.farm_block(farmer=oracle)) new_coin = asyncio.run( network.sim_client.get_coin_records_by_puzzle_hash( custom_puzzle.get_tree_hash())) for coin in new_coin: print(coin)
signature = G2Element() spend_bundle = SpendBundle( [ CoinSpend( new_coin[0].coin, custom_puzzle, Program.to(0)) ], signature) res = asyncio.run(network.push_tx(spend_bundle)) # Check coins - bob's balance should increase by amount and the new coin record # should show it is spent coins = asyncio.run(network.sim_client.get_coin_records_by_puzzle_hash(custom_puzzle.get_tree_hash())) print("Bobs final balance: {}".format(bob.balance())) for c in coins: print(coin)
Chialisp of a coin that pays to bob, but only if alice spends it.
(mod (PUBKEY MESSAGE PUZZLE_HASH AMOUNT) (include condition_codes.clib) (list (list CREATE_COIN PUZZLE_HASH AMOUNT) (list AGG_SIG_ME PUBKEY MESSAGE) ) )
amount = 5555 CUSTOM_PUZ_MOD = load_clvm('alice_signed_coin.clsp', 'clsp') custom_puzzle = CUSTOM_PUZ_MOD.curry(alice.pk(), 'hello coin', bob.puzzle_hash, amount) found_coin = asyncio.run(alice.choose_coin(amount)) c1 = [ConditionOpcode.CREATE_COIN, custom_puzzle.get_tree_hash(), amount] c2 = [ConditionOpcode.CREATE_COIN, alice.puzzle_hash, found_coin.amount - amount] conditions = [c1, c2] delegated_puzzle_solution = Program.to((1, conditions)) solution = Program.to([[], delegated_puzzle_solution, []]) signature = AugSchemeMPL.sign( calculate_synthetic_secret_key(alice.sk_, DEFAULT_HIDDEN_PUZZLE_HASH), ( delegated_puzzle_solution.get_tree_hash() + found_coin.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA ), ) spend_bundle = SpendBundle( [ CoinSpend( found_coin.as_coin(), # Coin to spend alice.puzzle, # Puzzle used for found_coin solution, # The solution to the puzzle locking found_coin ) ], signature, ) res = asyncio.run(network.push_tx(spend_bundle)) print(res) # update = asyncio.run(network.farm_block(farmer=oracle)) new_coin = asyncio.run( network.sim_client.get_coin_records_by_puzzle_hash( custom_puzzle.get_tree_hash()))
from chia.wallet.sign_coin_spends import sign_coin_spends signature = AugSchemeMPL.sign( calculate_synthetic_secret_key(alice.sk_, custom_puzzle.get_tree_hash()), ( custom_puzzle.get_tree_hash() + int_to_bytes(new_coin[-1].coin.amount) + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA ), ) # signature = G2Element() msg = custom_puzzle.get_tree_hash() + bytes(amount) signature = AugSchemeMPL.sign(alice.sk_, msg) coin_spend = CoinSpend( new_coin[-1].coin, custom_puzzle, Program.to(0)) spend_bundle = asyncio.run(sign_coin_spends([coin_spend], alice.pk_to_sk, DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM)) spend_bundle = SpendBundle([coin_spend], signature) res = asyncio.run(network.push_tx(spend_bundle)) # Check coins - bob's balance should increase by amount and the new coin record # should show it is spent coins = asyncio.run(network.sim_client.get_coin_records_by_puzzle_hash(custom_puzzle.get_tree_hash())) print("Bobs final balance: {}".format(bob.balance())) for c in coins: print(coin) print(res)
The oracle makes a coin that anyone can spend
Alice makes a coin that verifies the oracle coin
Alice spends both coins
The will output either an list, consbox, atom, or value from a given input. Program.to([100 200]) => (100 200) # list => list Program.to((100, 200)) => (100 . 200) # tuple => cons Program.to(0) => 0 # value => atom