Inner Puzzles
Puzzles all have something in common, they output a list of conditions. This tells the blockchain what you want to do with the coin. We did this exact thing in an earlier lesson:
(mod (PUBLIC_KEY conditions)
(include condition_codes.clib)
(include sha256tree.clib)
(c
(list AGG_SIG_ME PUBLIC_KEY (sha256tree conditions))
conditions
)
)
Here, we are outputting a custom conditions
which is passed in as a solution.
Inner Puzzles
We can create a coin within a coin where the inner coin returns a list of conditions to the outer coin. To do this, we will use (a INNER_PUZZLE inner_solution)
. The operator a
means apply
and is how you execute some code. The inner puzzle will be executed with the inner solution.
What the inner puzzle and inner solution is exactly is up to the coin creator. Here is a more complex example for an outer coin:
(mod (PUBLIC_KEY INNER_PUZZLE inner_solution)
(include condition_codes.clib)
(include sha256tree.clib)
; Assert the signature matches and append the conditions.
(defun calculate_output (PUBLIC_KEY inner_solution conditions)
(c
(list AGG_SIG_ME PUBLIC_KEY (sha256tree inner_solution))
conditions
)
)
; Pass the output of the inner puzzle to `calculate_output`.
(calculate_output PUBLIC_KEY inner_solution (a INNER_PUZZLE inner_solution))
)
This will first run (a INNER_PUZZLE inner_solution)
, passing the result to our custom function calculate_output
. This function will require a signature of the outputted conditions from the inner coin.
You can think of the outer coin as additional layer to control the inner coin. Almost like a template for your coins.
Inner Puzzle Creation
Let's create an inner puzzle. This will use a new condition code ASSERT_HEIGHT_RELATIVE, which will make sure a certain number of blocks have passed since coin creation before the coin can be spent.
(mod (REQUIRED_BLOCKS conditions)
(include condition_codes.clib)
(c
(list ASSERT_HEIGHT_RELATIVE REQUIRED_BLOCKS)
conditions
)
)
We will also need to get the appropriate include files:
cdv clsp retrieve sha256tree condition_codes
The Steps
Many of these values used (such as my public key) are specific to me. I left them in to clearly see command usage, but you'll want to substitute where appropriate.
First, let's get our master public key.
chia keys show
Response:
Fingerprint: 1660000549
Master public key (m): b8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d
Farmer public key (m/12381/8444/0/0): b4ef65cc62af8cd6d25e72444e1886938cfec436933cfc5e32c14367f846f6728f74a133c30bbab84fd4d0afec0966a0
Pool public key (m/12381/8444/1/0): b6cb156bad3580795ae9377e717a43925ed7622f18ff6ab8c3471f1fdd18ad9906b8e19c22f25fa29fccbf4af3f4acab
First wallet address: txch1kwa0ach5f3djns83cpvsywnwlx4mjqnzyja3vg0jwlsp3cfwes2qlfdf7c
You will specifically want the master public key prefixed with 0x
. For me, this value is:
0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d
Now, let's curry the inner puzzle with some value (in # of blocks)
cdv clsp curry inner-puzzle.clsp -a 20
Response:
(a (q 2 (q 4 (c 2 (c 5 ())) 11) (c (q . 82) 1)) (c (q . 20) 1))
Once we've created the bytecode for the inner puzzle, we will curry that result in to the outer puzzle, after our public key.
cdv clsp curry outer-puzzle.clsp -a 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d -a '(a (q 2 (q 4 (c 2 (c 5 ())) 11) (c (q . 82) 1)) (c (q . 20) 1))'
Response:
(a (q 2 (q 2 10 (c 2 (c 5 (c 23 (c (a 11 23) ()))))) (c (q 50 (c (c 4 (c 5 (c (a 14 (c 2 (c 11 ()))) ()))) 23) 2 (i (l 5) (q 11 (q . 2) (a 14 (c 2 (c 9 ()))) (a 14 (c 2 (c 13 ())))) (q 11 (q . 1) 5)) 1) 1)) (c (q . 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d) (c (q 2 (q 2 (q 4 (c 2 (c 5 ())) 11) (c (q . 82) 1)) (c (q . 20) 1)) 1)))
Now that we curried in our public key, the bytecode will be different from key to key. Make sure you use your own key and prefix with 0x!
Create the Coin
Now that we have curried the inner puzzle in to the outer puzzle, we will create a coin for our final outer puzzle bytecode.
Get Puzzle Hash
opc -H "<Outer Puzzle>"
opc -H "(a (q 2 (q 2 10 (c 2 (c 5 (c 23 (c (a 11 23) ()))))) (c (q 50 (c (c 4 (c 5 (c (a 14 (c 2 (c 11 ()))) ()))) 23) 2 (i (l 5) (q 11 (q . 2) (a 14 (c 2 (c 9 ()))) (a 14 (c 2 (c 13 ())))) (q 11 (q . 1) 5)) 1) 1)) (c (q . 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d) (c (q 2 (q 2 (q 4 (c 2 (c 5 ())) 11) (c (q . 82) 1)) (c (q . 20) 1)) 1)))"
Response:
e4fd576c99d4cb789a21b3173d18e916c37634720c9ecd9d25f615d24bd1e3c5
Get Puzzle Reveal
While we are at it, let's also get our serialized puzzle for the puzzle_reveal
.
opc "<Outer Puzzle>"
opc "(a (q 2 (q 2 10 (c 2 (c 5 (c 23 (c (a 11 23) ()))))) (c (q 50 (c (c 4 (c 5 (c (a 14 (c 2 (c 11 ()))) ()))) 23) 2 (i (l 5) (q 11 (q . 2) (a 14 (c 2 (c 9 ()))) (a 14 (c 2 (c 13 ())))) (q 11 (q . 1) 5)) 1) 1)) (c (q . 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d) (c (q 2 (q 2 (q 4 (c 2 (c 5 ())) 11) (c (q . 82) 1)) (c (q . 20) 1)) 1)))"
Response:
ff02ffff01ff02ffff01ff02ff0affff04ff02ffff04ff05ffff04ff17ffff04ffff02ff0bff1780ff808080808080ffff04ffff01ff32ffff04ffff04ff04ffff04ff05ffff04ffff02ff0effff04ff02ffff04ff0bff80808080ff80808080ff1780ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff0effff04ff02ffff04ff09ff80808080ffff02ff0effff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b0b8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954dffff04ffff01ff02ffff01ff02ffff01ff04ffff04ff02ffff04ff05ff808080ff0b80ffff04ffff0152ff018080ffff04ffff0114ff018080ff01808080
Get Address
Now that we have our puzzle hash, we can encode this as an address.
cdv encode -p txch e4fd576c99d4cb789a21b3173d18e916c37634720c9ecd9d25f615d24bd1e3c5
Response:
txch1un74wmye6n9h3x3pkvtn6x8fzmphvdrjpj0vm8f97c2ayj73u0zsjqanlc
We will use this address as the receive address for our new coin.
Create Coin
chia wallet send --amount 0.01 --fee 0.00005 --address txch1un74wmye6n9h3x3pkvtn6x8fzmphvdrjpj0vm8f97c2ayj73u0zsjqanlc
Response:
Submitting transaction...
Transaction submitted to nodes: [{'peer_id': '67095d445d879556da95feeee70174c66b131d4f29bd447df5fbc56789a01f24', 'inclusion_status': 'SUCCESS', 'error_msg': None}]
Run 'chia wallet get_transaction -f 1660000549 -tx 0xcfc2382e19b8c02a251cc4092951aeda45d643d32fe9bbe675761a228a393b1a' to get status
Let's get the status.
chia wallet get_transaction -f 1660000549 -tx 0xcfc2382e19b8c02a251cc4092951aeda45d643d32fe9bbe675761a228a393b1a
Eventually, this will say confirmed:
Transaction cfc2382e19b8c02a251cc4092951aeda45d643d32fe9bbe675761a228a393b1a
Status: Confirmed
Amount sent: 0.01 TXCH
To address: txch1un74wmye6n9h3x3pkvtn6x8fzmphvdrjpj0vm8f97c2ayj73u0zsjqanlc
Created at: 2022-11-01 22:34:50
In the meantime, we can start crafting our spendbundle.json
:
{
"coin_spends": [
{
"coin": {
"amount": 10000000000,
"parent_coin_info": "",
"puzzle_hash": ""
},
"puzzle_reveal": "",
"solution": ""
}
],
"aggregated_signature": ""
}
Next up, let's get the coin record and coin ID.
Retreive Coin Info
First, you can grab the coin by puzzlehash. If anyone has ever used the same CLVM for a coin, there will be multiple coins returned (this is unlikely since our puzzle included our own public key):
cdv rpc coinrecords --by puzzle_hash e4fd576c99d4cb789a21b3173d18e916c37634720c9ecd9d25f615d24bd1e3c5
Response:
[
{
"coin": {
"amount": 10000000000,
"parent_coin_info": "0xdb8e67eb6c0d91206329eb4fe53c403b3b0be29fdd99baa67e574c2318ade1f7",
"puzzle_hash": "0xe4fd576c99d4cb789a21b3173d18e916c37634720c9ecd9d25f615d24bd1e3c5"
},
"coinbase": false,
"confirmed_block_index": 333655,
"spent_block_index": 0,
"timestamp": 1667356550
}
]
This does not give the coin ID, but does give everything needed to calculate the coin ID.
A coin ID is the hash of the parent coin ID, puzzle hash of the coin, and the amount.
We can retrieve the coin ID with this template:
cdv inspect -id coins --parent-id "<Parent Coin Id>" --puzzle-hash "<Puzzle Hash>" --amount "<Amount>"