This is the second article in a series You will dive deep into individual contract proposals that have reached maturity worthy of a detailed breakdown.
The Checksigfromstack (CSFS) proposed by Jeremy Rubin of Brandon Black and Bip 348 is not a contract. As mentioned in the introductory articles in this series, some of the proposals I cover are not contracts, but rather interacting with or interrelated with them in some way. CSFS is the first example.
CSFS is a very simple opcode, but before we look at how it works, let’s take a look at the basics of how Bitcoin script actually works.
Scripting is a stack-based language. This means that data is “stacked” each other on top of the stack, and works by removing items from the top of the stack and acting based on what the opcode does.
A script has two parts when it is finally executed and verified. The “witness” provided to unlock the script, and the scripts contained in the output are used. The witness/unlock script is “added” to the left of the lock script, with each element being added (or manipulated) from left to right, one at a time to the stack. Take a look at this example (“|” marks the boundary between the witness and script):
1 2 | on_add 3 on_equal
The script in this example adds the value “1” to the stack and adds the value “2” above it. OP_ADD takes the top two elements of the stack, adds them together, and brings the result back to the stack (so that’s what you’re in the stack is “3”). Then another “3” is added to the stack. The last item, OP_Equal, takes the top two items of the stack and returns “1” to the stack (1 and 0 represent True or false as numbers).
The script must have the last item at the top of the stack terminated with true. Otherwise, the script (and the transaction that runs it) will fail and the consensus is considered invalid.
This is a basic example of a Pay-to-Pubkey-Hash (P2PKH) script. In other words, legacy addresses starting with “1”.
First, the signature and public key are added to the stack. Then the DUP is called to take the top stack item, duplicate it and return it to the top of the stack. Hash160 takes a top stack item (a copy of the public key), hash it, then returns it to the top of the stack. A hash of the public key from the script is placed on top of the stack. eqerverify is the same function as equals, grabs two top stack items and returns either 1 or 0 based on the result. The only difference is that it is also performed to verify after equalverify is equal. This will fail the transaction if the top stack item is not 1 and will also delete the top stack item. Finally, Checksig is run. This implicitly validates the signature against the hash of the transaction being verified, assuming that the top two stack items are signatures and PubKeys. If enabled, place 1 on the stack.
How CSFS works
Checksig is one of the most used opcodes in Bitcoin. All transactions use this opcode at some point in this script, with few exceptions. Signature verification is the fundamental component of the Bitcoin protocol. The problem is that there is little flexibility in terms of messages checking signatures. Checksig only checks signatures for transactions that are being verified. There is some flexibility. This means you have some freedom to decide which part of the transaction the signature applies to, but that’s it.
Rather than being limited to verifying signatures on the transaction itself, CSFS aims to change this by allowing signatures to be verified against any message that is pushed directly to the stack. The opcode follows a very basic operating structure.
Signatures and messages are dropped on top of the stack and published on top of it, and eventually CSFS grabs the top three items from the stack, assuming they are top to bottom public key, message and signatures, and confirms the signatures for the message. If the signature is valid, 1 is placed on the stack.
that’s it. A simple variant of Checksig that allows users to specify arbitrary messages rather than just spending transactions.
What does CSFS help?
So, what exactly is this good for? What is the use of checking signatures against any message on the stack rather than against spending transactions?
First, in combination with CTV, it can provide the same functionality as what lightning developers wanted from the start. This was originally proposed as a new Sighash flag for signatures (a field that determines which part of the transaction the signature applies to). This was necessary because it covers the transaction ID of the transaction that created the output that used the transaction signature. This means that the signature is valid only for transaction spending just output.
This is a desirable behavior for lightning, as it can eliminate channel penalties. For each past lightning state, a penalty key and transaction are required to prevent the channel’s counterparty from trying to claim funds that are not owned. They try and you can charge all their money. A great feature is that in order to “attach” current state transactions to previous ones, by properly distributing the funds rather than confiscating them, allowing them to stop attempts at theft.
This can be achieved using basic scripts that employ CTV hashing and signatures that are checked using CSFS. This allows the transaction hash signed by that CSFS key to use the output created by this script.
Another useful feature is delegating control of UTXO. Other variables, such as new public keys, can be passed to the script in the same way that a CTV hash signed by a CSFS key can be used effectively for UTXO using the script designed for this purpose. You can build a script that allows CSFS keys to sign off Any A public key that is verified using CSFS and can be used for normal checking verification. This allows you to delegate the ability to use UTXO to others without moving in a chain.
Finally, in conjunction with CAT, CSF can be used to construct a much more complex introspection function. However, as seen later in the series, only CAT can do so, so CSFS doesn’t really need to emulate any of this more advanced behavior.
Close thoughts
CSFS is a very basic opcode, offering simple useful features with its own rights, and even the simplest contract opcodes are very well organized to create very useful features. The above example on floating signatures specifically refers to Lightning networks, but floating signatures are generally useful primitives that apply to protocols built into Bitcoin using pre-signed transactions.
In addition to floating signatures, script delegation is a very useful primitive and generalizes well beyond delegating control over UTXO to a new public key. The same basic functionality as “sideloading” variables after facts to script validation flow can be applied to anything, not just public keys. Time lock values, hash lock pre-image, etc. Scripts that hardcode the variables to be validated are now dynamically added after facts.
On top of that, CSFS is a very mature proposal. Since 2016, there are implementations that exist in liquid networks and elements (using liquids in the code base). Plus, Bitcoin Cash has its version since 2018.
CSFS is a very mature proposal and as long as I’ve been in this field, it goes back conceptually as long as I have multiple mature implementations and very clear use cases where I can apply them.
Discover more from Earlybirds Invest
Subscribe to get the latest posts sent to your email.