-
-
Save trepca/d6a0d7f761de7459643422eb73c435e6 to your computer and use it in GitHub Desktop.
import time | |
from chia.clvm.spend_sim import SimClient, SpendSim | |
from chia.types.blockchain_format.program import Program | |
from chia.types.coin_spend import make_spend | |
from chia.types.condition_opcodes import ConditionOpcode | |
from chia.types.mempool_inclusion_status import MempoolInclusionStatus | |
from chia.util.hash import std_hash | |
from chia.util.ints import uint64 | |
from chia_rs import CoinSpend, G2Element, SpendBundle | |
from clvm_tools import clvmc | |
async def main(): | |
sim: SpendSim | |
async with SpendSim.managed() as sim: | |
# set simulators time to current time | |
sim.pass_time(uint64(time.time())) | |
# rpc client to simulator "node" | |
client: SimClient = SimClient(sim) | |
# this puzzle passes any conditions it receives without any validation | |
anyone_can_spend_puzzle = Program.to(1) | |
# get a puzzle hash for it | |
acs_ph = anyone_can_spend_puzzle.get_tree_hash() | |
# farm some coins to acs_ph | |
await sim.farm_block(acs_ph) | |
# compile password clsp from https://chialisp.com/chialisp-first-smart-coin/ | |
password_clsp = ''' | |
;;; This puzzle locks coins with a password. | |
;;; It should not be used for production purposes. | |
;;; Use a password that has no meaning to you, preferably random. | |
(mod ( | |
PASSWORD_HASH ; This is the sha256 hash of the password. | |
password ; This is the original password used in the password hash. | |
conditions ; An arbitrary list of conditions to output. | |
) | |
; If the hash of the password matches, | |
(if (= (sha256 password) PASSWORD_HASH) | |
; Output the conditions list. | |
conditions | |
; Otherwise, throw an error. | |
(x) | |
) | |
) | |
''' | |
password_puzzle = Program.to(clvmc.compile_clvm_text(password_clsp, [])) | |
secret_password_puzzle = password_puzzle.curry(std_hash(b"SUPER SECRET PASSWORD")) | |
# get a puzzle hash for it | |
secret_password_ph = secret_password_puzzle.get_tree_hash() | |
# now we'll spend our coin to turn it to password locked coin | |
# first fetch our unspent coin from blockchain | |
our_coin_records = await client.get_coin_records_by_puzzle_hash(acs_ph, include_spent_coins=False) | |
our_coin = our_coin_records[0].coin | |
original_amount = sum([coin.coin.amount for coin in our_coin_records]) | |
solution = Program.to([ | |
# we're going to lock 10_000_000 mojos with our password | |
[ConditionOpcode.CREATE_COIN, secret_password_ph, 10_000_000], | |
# whatever is left will be returned to us | |
[ConditionOpcode.CREATE_COIN, acs_ph, our_coin.amount - 10_000_000] | |
]) | |
coin_spend = make_spend(our_coin, anyone_can_spend_puzzle, solution) | |
# now we can spend our coin by pushing it to the simulator | |
# first need to create a spend bundle or more traditionally called a transaction | |
bundle = SpendBundle([coin_spend], | |
# G2Element() means empty signature, we don't need signatures since our coin doesn't | |
# require them | |
G2Element()) | |
# push the bundle to the simulator | |
await client.push_tx(bundle) | |
# now we farm another block to save our spends to blockchain | |
await sim.farm_block() | |
# let check that we spent 10_000_000 mojos to our password locked coin | |
our_coin_records = await client.get_coin_records_by_puzzle_hash(acs_ph, include_spent_coins=False) | |
assert sum([cr.coin.amount for cr in our_coin_records]) == original_amount - 10_000_000 | |
# let's now try to spend our password locked coin | |
# first fetch our unspent coin from blockchain | |
password_protected_coins = await client.get_coin_records_by_puzzle_hash(secret_password_ph, | |
include_spent_coins=False) | |
# we should have only one coin | |
password_protected_coin = password_protected_coins[0].coin | |
# we'll try to spend it with wrong password | |
wrong_solution = Program.to(["WRONG PASSWORD", [ConditionOpcode.CREATE_COIN, acs_ph, 10_000_000]]) | |
wrong_coin_spend: CoinSpend = make_spend(password_protected_coin, secret_password_puzzle, wrong_solution) | |
# we can run this spend locally to see if it's valid | |
puzzle = wrong_coin_spend.puzzle_reveal.to_program() | |
# run the puzzle with the solution | |
try: | |
puzzle.run(wrong_coin_spend.solution) | |
assert False, "This should not happen" | |
except ValueError: | |
pass | |
# now we'll try to spend it with the correct password | |
correct_solution = Program.to( | |
[b"SUPER SECRET PASSWORD", | |
# we're creating a new coin back to us (our puzzle hash) | |
[[ConditionOpcode.CREATE_COIN, acs_ph, 10_000_000]]]) | |
correct_coin_spend: CoinSpend = make_spend(password_protected_coin, secret_password_puzzle, correct_solution) | |
bundle = SpendBundle([correct_coin_spend], G2Element()) | |
status, err = await client.push_tx(bundle) | |
# this should go through | |
assert status == MempoolInclusionStatus.SUCCESS | |
await sim.farm_block() | |
# check if there are any password locked coins left | |
password_protected_coins = await client.get_coin_records_by_puzzle_hash(secret_password_ph, | |
include_spent_coins=False) | |
assert len(password_protected_coins) == 0 | |
# let's check that we spent our password locked coin and that we got 10_000_000 mojos back | |
our_coin_records = await client.get_coin_records_by_puzzle_hash(acs_ph, include_spent_coins=False) | |
our_balance = sum([coin.coin.amount for coin in our_coin_records]) | |
assert our_balance == original_amount, f"Expected {original_amount}, got {our_balance}" | |
print("All good.") | |
if __name__ == "__main__": | |
import asyncio | |
asyncio.run(main()) |
Thanks for sharing, I tried it to see how the simulator work, but I had this error:
TypeError: argument 'coin_spends': 'CoinSpend' object cannot be converted to 'CoinSpend'
I solved by importing CoinSpend, SpendBundle from chia.types instead of chia_rs. (is it the rust implementation right?)
Any idea why this is happening?
Thanks for sharing, I tried it to see how the simulator work, but I had this error:
TypeError: argument 'coin_spends': 'CoinSpend' object cannot be converted to 'CoinSpend'
I solved by importing CoinSpend, SpendBundle from chia.types instead of chia_rs. (is it the rust implementation right?) Any idea why this is happening?
on which line do you get that error? which chia-blockchain version ara you using?
Yap sorry for the imprecision.
Version 2.2.1
File "/compostaggio/chiaBox/stdTx/sim_spendCoin.py", line 79, in main
bundle = SpendBundle([coin_spend],
^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: argument 'coin_spends': 'CoinSpend' object cannot be converted to 'CoinSpend'
Trying again I discovered that I needed only to repalce the chia_rc SpendBundle to make it working.
So my import are:
from chia_rs import CoinSpend, G2Element
from chia.types.spend_bundle import SpendBundle
You will need to install
chia-blockchain
in your environment to run this.