Smart Coins
Everything on the Chia blockchain is a coin. They are often referred to as smart coins because every coin has a Chialisp program associated with it. That program, known as a puzzle, decides how and when the coin can be spent, and what happens when it is. NFTs, CATs, and standard transactions are all defined using puzzles. Another example of something you can do with Chialisp is lock up funds until a certain amount of time has elapsed.
Now that you have learned how to write basic Chialisp programs, you can apply that to more complex puzzles. There's a bit more involved in creating a puzzle and using it for a coin, but we'll get into that more later.
In this lesson, we will be writing a puzzle that requires a simple password to unlock coins that use it.
If you are using PowerShell, make sure to install the PowerShell 7.3 preview version:
winget install --id Microsoft.Powershell.Preview --source winget
This version fixes nested quoting, which is required for many of the commands on this page.
While this is great for learning the fundamentals, it is an insecure way to protect funds on a blockchain. We will explore the reason and better methods later on.
Basic Example
Let's create a little program to have the user guess a password. We are going to build up to a more advanced password-protected coin, but let's start with the basics. We will hardcode the correct password to be hello
. If the user provides hello
as the solution, they got the correct password.
Write the following in a file named password.clsp
:
(mod (password)
(if (= password "hello")
"Correct!"
"Incorrect :("
)
)
Run the following command to check the password against it:
brun "$(run password.clsp)" "(hello)"
Which should produce the following output:
Correct!
However, if you provide a different solution, you'll get a different result:
brun "$(run password.clsp)" "(goodbye)"
Which should produce the following output:
Incorrect :(
This is cool, but we want to avoid hardcoded values when possible. This introduces our next concept called currying. Currying allows us to write a generalized program that can be used for many different password options. This way, we're not stuck with just a single password of hello
. Let's see how this works in the next section.
Currying Example
Our goal is to allow more than just the hardcoded password of hello
. In addition to this, we want to hide the password to the best of our ability with hashing (which we will explore in more detail later).
Replace the contents of passwords.clsp
with the following:
(mod (CORRECT_PASSWORD provided_password)
(if (= CORRECT_PASSWORD provided_password)
"Correct!"
"Incorrect :("
)
)
Values that are expected to be curried in are often written in ALL_CAPS. While we explain currying here, you can also check out the Currying Guide to learn more about what currying is and how it works.
Run the following command to curry in the password:
cdv clsp curry password.clsp -a "hello"
Which should produce the following result:
(a (q 2 (i (= 2 5) (q 1 . "Correct!") (q 1 . "Incorrect :(")) 1) (c (q . "hello") 1))
Which we can now execute with a provided solution:
brun '(a (q 2 (i (= 2 5) (q 1 . "Correct!") (q 1 . "Incorrect :(")) 1) (c (q . "hello") 1))' "(hello)"
Now that we've introduced currying, we can create a different program using a different CORRECT_PASSWORD
without modifying any of the source code.
Now, let's nest a command.
Because currying outputs CLVM, we can nest it as input to the compiler to make the process of testing this out easier.
An important thing to note here is that the nesting will not work properly if surrounded with single quotes, thus, we would use "$()"
and not '$()'
. This requires us to flip all nested quotes so that we have single quotes inside of the double quote (in this upcoming example take a look at 'goodbye'
. ).
You can try it with a new password:
brun "$(cdv clsp curry password.clsp -a 'goodbye')" "(goodbye)"
Which should output the following result:
Correct!
This time, try using the wrong password:
brun "$(cdv clsp curry password.clsp -a 'goodbye')" "(hello)"
Which should output the following result:
Incorrect :(
We're making progress, but there's still some issues with this program. The password is directly visible in the Chialisp bytecode. When we get to spending coins this information will be revealed to the world, allowing anyone to know the password. Let's see what we can do about this.
Hashing Example
Although this won't make the password more secure, it's important to understand hashing and how it can be used to keep values hidden until it is necessary to reveal them.
A hash function will take an input and return a hash value. One of the most popular hashing algorithms is sha256 which is directly supported within Chialisp. A few important notes about hash functions:
- Given a value, calculating the hash is extremely easy,
- Given a hash, calculating the original input is extremely difficult or impossible,
- Passing the same value through a hashing function multiple times will always result in the same output.
We can use these principles to our advantage by currying a hash of the expected password instead of the password value itself. This prevents us from revealing the expected password while still allowing us to check if the provided password is correct. This is done by hashing the provided password. You can think of this operation as:
(if (= (sha256 password) PASSWORD_HASH)