Introduction
Bitcoin is sometimes referred to as programmable money. Due to its digital nature, users enjoy significant flexibility in determining how to use their funds. When discussing Bitcoin, we often mention wallets and tokens, but we can also think of wallets as keys, tokens as checks, and the blockchain as rows of locked safes. Each safe has a small slot that allows anyone to deposit checks or view their value, but only the key holder can unlock the safe.
When the key holder wants to transfer funds to someone else, they unlock the safe, issue a new check referencing the old check (which will then be destroyed), and lock it back into a safe that the recipient can open. To spend this money, the new recipient must repeat this process.
In this article, we will delve into scripts, a programming language interpreted by nodes in the Bitcoin network. Scripts manage the locking and unlocking mechanisms of the aforementioned safes.
How Does Bitcoin Work?
Continuing with this analogy, we can say that each transaction consists of two parts—keys (used for unlocking) and locks. You need to use a key to open the safe containing the check to be sent, then add the new check to another safe that uses a different lock. If you want to spend the money in the new safe, you will need another key.
This process is actually quite simple. The types of locks in the system may vary. Some safes require multiple keys, while others may require you to prove you know a particular password. Our keys are known as scriptSig, while the locks are called scriptPubKey. If we take a closer look at these components, they are essentially composed of data bits and code blocks, which together form a small program.
When you make a transaction, you are broadcasting this combination to the network. Every node that receives the transaction checks this program to determine if the transaction is valid. If the transaction is invalid, it will be rejected, and you will not be able to use the locked funds.
The checks (tokens) you hold are referred to as unspent transaction outputs (UTXOs). As long as you can provide a key that matches the lock, anyone can access these funds. Specifically, the key is the scriptSig, while the lock is the scriptPubKey. If the UTXOs are in your wallet, they may have a condition that only the person who can prove ownership of the public key can unlock these funds. To unlock the funds, you need to provide a scriptSig containing a digital signature, using the private key corresponding to the public key specified in the scriptPubKey. Everything will gradually become clearer.
Understanding the Bitcoin Stack
Scripts are a stack-based language, meaning that when reading a set of instructions, we arrange them into a vertical stack. For example, a list of A, B, and C forms a stack where A is at the bottom and C is at the top. When an instruction requires us to perform an operation, we start processing one or more elements from the top of the stack.
Elements A, B, and C are added and "popped" from the stack.
Distinguishing Data from Instructions
We can differentiate between data (such as signatures, hashes, and public keys) and instructions (or opcodes). Instructions are responsible for manipulating and processing the data. Here is a very simple script example:
Script Example
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
The red parts represent data, while the blue parts represent opcodes. We read from left to right, so we first place the string <xyz> onto the stack. Next comes the <md5 hasher> opcode. This opcode does not exist in Bitcoin, but we assume it removes the top element of the stack (i.e., <xyz>), hashes it using the MD5 algorithm, and then adds the output back to the stack, which in this case is d16fb36f0911f878998c136191af705e.
How convenient! The next element to be added is <d16fb36f0911f878998c136191af705e>, resulting in two identical elements on the stack. Finally, <check if equal> will pop both elements and check if they are equal. If they are equal, it adds <1> to the stack; if not, it adds <0>.
At the end of the instruction list, our script can fail in two ways: if there are zero remaining elements or if certain conditions are not met, causing an operator to fail. In this example, we did not encounter such an operator, so we end up with a non-zero element (<1>), indicating that our script is valid. These rules also apply to actual Bitcoin transactions.
The above example is purely fictional. Now, let's look at some real examples.
Pay to Public Key (P2PK)
The mechanism of Pay to Public Key (P2PK) is very straightforward. It is designed to lock funds to a specific public key. If you wish to receive funds this way, you need to provide your public key to the sender instead of your Bitcoin address.
The first transaction between Satoshi Nakamoto and Hal Finney in 2009 was a P2PK transaction. This structure was widely used in the early days of Bitcoin, but it has largely been replaced by Pay to Public Key Hash (P2PKH) today.
The locking script for a P2PK transaction follows the format <public key> OP_CHECKSIG. It's that simple. You may have already realized that OP_CHECKSIG checks the signature against the provided public key. Thus, our scriptSig will be a simple <signature>. Remember, the scriptSig is the key used to unlock.
The signature is added to the stack, followed by the public key. OP_CHECKSIG will pop both the signature and the public key to verify if the signature matches the public key. If they match, <1> is added to the stack; otherwise, <0> is added.
It doesn't get any simpler than this. The signature is added to the stack, followed by the public key. OP_CHECKSIG simultaneously pops the signature and the public key and verifies the signature against the public key. If they match, <1> is added to the stack; otherwise, <0> is added. For reasons we will elaborate on in the next section, P2PK has become less commonly used.
Pay to Public Key Hash (P2PKH)
Pay to Public Key Hash (P2PKH) is the most common type of transaction today. Unless you have intentionally downloaded outdated software, your wallet is likely to default to using this type of transaction.
The scriptPubKey for P2PKH is as follows:
OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG
Before introducing the scriptSig, let's analyze the roles of the new opcodes:
OP_DUP
OP_DUP will pop the first element from the stack, duplicate this element, and then add both copies back to the stack. This is typically done to operate on the copy without affecting the original element.
OP_HASH160
OP_HASH160 will pop the first element from the stack and perform two rounds of hashing. The first round uses the SHA-256 algorithm, followed by the RIPEMD-160 algorithm applied to the output of the SHA-256 hash. The final result is added back to the stack.
OP_EQUALVERIFY
OP_EQUALVERIFY is a combination of two opcodes: OP_EQUAL and OP_VERIFY. OP_EQUAL pops the top two elements from the stack and checks if they are the same. If they are equal, it adds <1> to the stack; if they are not, it adds <0>. Then, OP_VERIFY pops the top element and checks if it is true (i.e., non-zero). If it is not, the transaction fails. In summary, if the top two elements do not match, OP_EQUALVERIFY results in a transaction failure.
In this case, the scriptSig looks like this:
<signature> <public key>
You need to provide the signature and the corresponding public key to unlock the P2PKH output.
We have simply added an extra step to verify that the public key matches the hash in the script.
As shown above, you can understand the more detailed process through a GIF. Compared to the P2PK script, it is not much different, just with an additional step to validate the public key against the hash.
It is important to note that in the locking script for P2PKH, the public key is not visible—we can only see its hash. If we look at unspent P2PKH outputs in a blockchain explorer, we cannot determine the public key. It is only revealed when the recipient decides to transfer the funds.
This design has several advantages. First, the public key hash is easier to share than the complete public key, which is why Satoshi Nakamoto introduced public key hashes in 2009. The public key hash is what we now know as a Bitcoin address. Second, the public key hash provides an additional layer of security against quantum computing. Since the public key is only exposed after funds are spent, it becomes more difficult for others to derive the private key. They would need to reverse two rounds of hashing (RIPEMD-160 and SHA-256) to obtain the private key.
Pay to Script Hash (P2SH)
Pay to Script Hash (P2SH) is an interesting development in Bitcoin. It allows the sender to lock funds to the hash of a script without knowing the specific contents of the script. For example, consider the following SHA-256 hash:
e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1
The sender does not need to know the inputs of the hash to lock the funds. However, anyone wanting to spend these funds must provide the script used for that hash and satisfy the conditions in the script.
The above hash was created using the following script:
<multiply by 2> <4> <check if equal>
If you want to spend the tokens bound to this scriptPubKey, you not only need to provide these commands but also a scriptSig that makes the entire script evaluate to true. In this case, you only need <2> to achieve <4> as a result through <multiply by 2>.
In practice, the scriptPubKey for a P2SH output is:
OP_HASH160 <redeemScript hash> OP_EQUAL
There are no new opcodes here, but we do introduce <redeemScript hash> as a new element. As the name suggests, this is the hash of the script that we need to provide to redeem the funds (referred to as the redeemScript). The contents of the scriptSig will vary based on the redeemScript but will typically be some combination of a signature and an additional public key, followed by the (mandatory) redeemScript:
<signature> <public key> <redeemScript>
Our computation operates slightly differently from the current stack execution, divided into two parts. The first part simply checks whether you have provided the correct hash.
At the end of this mini-program, the top element is non-zero, indicating it is valid.
It is important to note that we do not perform any operations on the elements preceding the redeemScript, as they are not yet needed at this point. We have reached the end of this mini-program, and the top element is non-zero, indicating it is valid.
But we are not finished yet. Network nodes recognize this structure as P2SH, so they actually keep the elements of the scriptSig waiting in another stack. This is where the signature and public key come into play.
Up to this point, we have treated redeemScript as a single element, but now it will be interpreted as instructions, which can be anything. Taking the P2PKH locking script as an example, we must provide a <signature> and <public key> that match the <public key hash> in the redeemScript.
Once your redeemScript is expanded, you will find that our situation is identical to that of a conventional P2PKH transaction. After that, you simply run this program as usual.
P2SH and Its Applications
We have demonstrated the so-called P2SH (Pay to Script Hash) script, but in a practical environment, you are unlikely to encounter this type of script. While you can create a P2SH script, doing so does not offer any advantages and will occupy more block space, increasing costs.
P2SH is typically used in cases such as multi-signature or SegWit-compatible transactions. Multi-signature transactions can be quite large because they require multiple keys. Before implementing pay to script hash, the sender must list all potential public keys in the locking script. However, for P2SH, regardless of how complex the spending conditions are, the hash of the redeemScript remains a fixed size. Therefore, the cost ultimately transfers to the user wishing to unlock that locking script.
SegWit compatibility is another application example of P2SH (we will discuss the differences in transaction structures in detail in the next section). SegWit is a soft fork that results in changes to block and transaction formats. Since it is an optional upgrade, not all wallet software can recognize these changes.
If a client encapsulates the SegWit script hash in P2SH, this is not an issue. As with all such transactions, nodes do not need to know what the redeemScript is that is used for unlocking.
SegWit Transactions (P2WPKH and P2WSH)
For a more comprehensive introduction to SegWit, please refer to the "Segregated Witness Beginner's Guide." To understand the transaction format in SegWit, you only need to know that we no longer have just scriptSig and scriptPubKey. Now, we also have a new area called "witness." The data previously stored in scriptSig has been moved to the witness, so scriptSig is now empty.
If you have seen addresses starting with "bc1," those addresses are what we refer to as native SegWit addresses (which are different from SegWit-compatible addresses that start with "3").
Pay to Witness Public Key Hash (P2WPKH)
Pay to Witness Public Key Hash (P2WPKH) is the SegWit version of P2PKH. Our witness content looks as follows:
<signature> <public key>
You will notice that this is the same as the scriptSig in P2PKH. Here, the scriptSig is empty. Meanwhile, the scriptPubKey is similar to:
<OP_0> <public key hash>
This looks a bit strange, right? Let's compare the positions of the signature, public key, and their hash opcodes.
We do not display additional opcodes here because the receiving nodes will know how to process it based on the length of the <public key hash>. They will calculate the length and understand that it must be handled in the same way as a traditional P2PKH transaction.
Unupgraded nodes may not know how to interpret the transaction in this manner, but that is not important. Under the old rules, there was no witness, so they would see an empty scriptSig and some data. They would evaluate this data and mark it as valid—in their view, anyone could spend these outputs. Thus, SegWit is considered a backward-compatible soft fork.
Pay to Public Key Hash (P2PKH)
Pay to Public Key Hash (P2PKH) is the new P2SH. If you have understood this far, you may already have a grasp of how it works, but we will go through it anyway. Our witness content is what would typically be in the scriptSig. For example, in packaging a P2PKH transaction into P2WSH, it might look like this:
<signature> <public key>
The next scriptPubKey is:
<OP_0> <public key hash>
The same rules apply here. SegWit nodes will read the length of the script hash and determine that it is a P2WSH output, evaluating it similarly to P2SH. Meanwhile, old nodes will simply view it as an output that anyone can spend.
Conclusion
After delving deeply into Bitcoin, you will gradually understand why it possesses such great potential. Transactions can consist of various different components. By flexibly utilizing these building blocks, users can set a variety of conditions concerning how and when their funds can be used.
Risk Warning
While the cryptocurrency market offers significant growth potential and innovation opportunities, it also carries a high level of market risk and price volatility. The value of crypto assets can fluctuate dramatically in a short period, potentially leading to substantial financial losses for investors. Additionally, the cryptocurrency market faces multiple risk factors, including technical risks, legal and regulatory uncertainties, cybersecurity threats, and market manipulation. We strongly advise users to conduct thorough research and due diligence before making any investment decisions and to consult professional financial advisors. All investment decisions are made at the user’s own risk. Thank you for your trust and support of Venkate!
Building The Future of Crypto Exchange
Where Meet a Confluence of Inspiration and Innovation
Venkate Exchange is an innovative cryptocurrency trading platform, drawing its name and inspiration from Venkateswara—a deity symbolizing wealth and prosperity in Indian mythology.
Comments
0 comments
Article is closed for comments.