Signatures
In the previous lesson we created our first coin and spent it. This was fun and a good exercise, but had some security problems. Specifically, we used a hashed password. This is very limited because once you spend that coin, your provided solution is revealed and your password can no longer be used securely for anything in the future.
Additionally, when you submit your spend to the network, a full node can view the solution (including the password), allowing a bad player to spend the coin themselves.
The solution to this is to use signatures. But, before we introduce signatures, let's go over a quick introduction of public and private keys.
Intro to Keys
When you create a Chia wallet you are given 24 words that you should keep secure and private. These words are used to generate a master private key. A master private key can be used to derive multiple child keys, all accessed and controlled from your 24 words (in other words, 24 words = full access to your wallet).
With each private key there is an associated public key, which can be shared without revealing your private key.
You can retrieve your key info using:
chia keys show
This will give you your master public key
, while keeping your seed phrase and private key hidden. Although rarely needed or recommended, you can see this info with the --show-mnemonic-seed
flag after chia keys show
.
Having a public key that derives from a private key allows us to do interesting things. Specifically, we can sign a message using our private key which can be verified as authentic. Let's read about that more.
Intro to Signatures
If you wanted to send a message to someone but they wish to have the ability to verify the sender, you can use signatures.
A digital signature allows you to use a private key to sign a message. This message can be verified by a recipient using your public key to verify the message comes from you.
To see this in action, let's sign a message with the master public key. Each key is labeled with a keypath from chia keys sign
, it resembles m/12381/8444/0/0
, or just m
for your master public key.
chia keys sign --message "hello" --hd_path m
This will then allow you to choose your wallet by fingerprint:
Choose key:
1) 1660000549
Or, you can pass that in as an additional flag to chia keys sign
:
chia keys sign --message "hello" --hd_path m --fingerprint 1660000549
Response:
Public key: b8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d
Signature: 91c3d0504c2c5e02091f92cf0c3f79f2d7350656b0dc554dfc94f7e256b53d415e1a15108e329004ff1c5e91e24b445d18e52b2777e9a01a7a12d7f69a9df30c6fe3c196bdc5aa8072ea23d0edb6422253bb02d560bce721a459e6cf9e847aed
The Public key
in this response should match the key shown in chia keys show
. The second part of this response is the signature.
This signature will be passed along with the message hello
to whoever we want to see it. The signature can be used to verify the message against your public key without exposing your private key. To verify a message we need:
- The signature,
- the sender's public key,
- the message
chia keys verify --message hello --signature 91c3d0504c2c5e02091f92cf0c3f79f2d7350656b0dc554dfc94f7e256b53d415e1a15108e329004ff1c5e91e24b445d18e52b2777e9a01a7a12d7f69a9df30c6fe3c196bdc5aa8072ea23d0edb6422253bb02d560bce721a459e6cf9e847aed --public_key b8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d
Response:
True
In order for this concept to work, the public key must be known to be the public key of the original sender. When this is the case we can verify that the message came from the original source and that the message has not been altered.
If we change either of these two pieces of information, the signature is no longer valid. The signature only works with the message hello
and that specific public key.
Now, let's learn about the use of signatures in Chialisp.
Verifying signatures in Chialisp
One of the available conditions is AGG_SIG_ME
, which takes a public key and a message to sign.
The general syntax for this is (50 public_key message)
Take a look at this example:
(mod (PUBLIC_KEY conditions)
(include condition_codes.clib)
(include sha256tree.clib)
(c
(list AGG_SIG_ME PUBLIC_KEY (sha256tree conditions))
conditions
)
)
We will be introducing a few new things here. The first new thing introduced is c
, which will combine the new AGG_SIG_ME
condition with the conditions passed in as a solution.
The other new idea is an include, which allows us to use the names of conditions instead of the numbers.
To get these include files, issue the command:
cdv clsp retrieve sha256tree condition_codes
Let's now understand the basic signature requirement. This code expects the public key to be curried, with our AGG_SIG_ME
condition being set up like so:
(list AGG_SIG_ME PUBLIC_KEY (sha256tree conditions))
The message
in this case is Sha256tree conditions
which takes the tree hash of our passed in conditions. We put anything in the message we do not want changed by the farmer. We will be using sha256tree
on the value because you cannot use a list. sha256tree
gets the tree hash of the conditions.
By having this as the message of AGG_SIG_ME
we will be able to prove that the conditions have not been modified (as we are verifying against the public key, similar to chia keys verify
).
Practice
Let's go through creating this coin and spending it. As we go through this we will need to keep track of a lot of information. It may be helpful to open a text file to keep track.
First, let's get our public key:
chia keys show
The public key response may look like this:
Master public key (m): b8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d
Now, curry in your public key prefixed with 0x (important). I'll use mine in this example, so be sure to update with the appropriate value!
cdv clsp curry signature.clsp -a 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d
Response (reminder, yours will be different):
(a (q 2 (q 4 (c 4 (c 5 (c (a 6 (c 2 (c 11 ()))) ()))) 11) (c (q 50 2 (i (l 5) (q 11 (q . 2) (a 6 (c 2 (c 9 ()))) (a 6 (c 2 (c 13 ())))) (q 11 (q . 1) 5)) 1) 1)) (c (q . 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d) 1))
Now, use your compiled code to get the serialized version of the code (the puzzle reveal) and the puzzle hash:
opc "(a (q 2 (q 4 (c 4 (c 5 (c (a 6 (c 2 (c 11 ()))) ()))) 11) (c (q 50 2 (i (l 5) (q 11 (q . 2) (a 6 (c 2 (c 9 ()))) (a 6 (c 2 (c 13 ())))) (q 11 (q . 1) 5)) 1) 1)) (c (q . 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d) 1))"
Response:
ff02ffff01ff02ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff0bff80808080ff80808080ff0b80ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b0b8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954dff018080
That is the puzzle reveal, now for the puzzle hash:
opc -H "(a (q 2 (q 4 (c 4 (c 5 (c (a 6 (c 2 (c 11 ()))) ()))) 11) (c (q 50 2 (i (l 5) (q 11 (q . 2) (a 6 (c 2 (c 9 ()))) (a 6 (c 2 (c 13 ())))) (q 11 (q . 1) 5)) 1) 1)) (c (q . 0xb8f7dd239557ff8c49d338f89ac1a258a863fa52cd0a502e3aaae4b6738ba39ac8d982215aa3fa16bc5f8cb7e44b954d) 1))"
Response:
aa0dc6276e519a604dd0a750b8efb53c5d65b55f189cc0ca29d498d45b69a216
Now, using this puzzle hash we can encode an address:
cdv encode --prefix txch aa0dc6276e519a604dd0a750b8efb53c5d65b55f189cc0ca29d498d45b69a216
Response:
txch14gxuvfmw2xdxqnws5agt3ma483wktd2lrzwvpj3f6jvdgkmf5gtq8g3aw3
Great! Now you have all of the information to create your coin!
Create a Coin
To create a coin we send a certain amount of chia to the address for this Chialisp. The amount
is up to you, the value used determines the value of this locked-up coin.
chia wallet send --amount 0.01 --fee 0.00005 --address txch14gxuvfmw2xdxqnws5agt3ma483wktd2lrzwvpj3f6jvdgkmf5gtq8g3aw3