💾 Archived View for 0x735.com › chialisp.gmi captured on 2022-01-08 at 13:37:01. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2021-11-30)

-=-=-=-=-=-=-

ChiaLisp Lab

Imports

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

Simulator Functions

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

Standard Transactions

This is a test to send xch from alice to bob using the standard transaction

Pay to conditions

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.

pay to delegated puzzle

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')


Alice sends 100 mojo to Bob


# 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()))

Alice makes a custom puzzle that when spent, will send its balance to bob.

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)

Alice creates a coin that can only be spent by her.

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)

Alice spends a coin that depends on an oracle announcement

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

Testnet Transactions

Mainnet Transactions

Singletons & NFTs

Oracles

Notes and Snippets

p2_delegated_puzzle_or_hidden_puzzle.py