Personal note, Many things happened in past two months the required my full attention. I hope to resume a steady flow of posts in coming days.
In the last post we’ve talked about one the biggest bitcoin misconception – The idea that transaction actually moves coins from one wallet to another. The truth is that transactions are nothing more that statements. These statements always points to a previous statement (that in turn point to an even older statement and so on), and usually these statement also specify an amount of coins that the current owner is wishing to transfer. The statement also contains a riddle, or an equation that needs to be proofed, and mostly, the key to proof this equation will require the use of the private_key that is associated with the recipient bitcoin address.
Pay attention, even though Bob will be required to use his own private_key to proof that he indeed can solve this problem, the private_key still won’t be available to any one.
Now let’s look for a second at this transaction message. We’ve already learned how to create a bitcoin message (see this section about Version message and this one about headers). We just need to make sure that all of the fields are filled in accordance the protocol rules. Just like filling a form. You can find a complete list of the fields that needs to be filled in the bitcoin developers documentation.
Most of the fields are quite straight foreword. I might still create another post in the future with detail instructions on how to fill all the fields, but this isn’t really the topic of this post. This post deals with one of bitcoin more fascinating aspects – The riddle that Alice place in her statement. The riddle that only Bob can solve -The script.
(You just can’t wait to create your own transaction? you’re more than welcome to watch my videos on creating bitcoin transaction)
Scripts, what is it?
Scripts is a computer language. In more detail, it’s a set of predefined words that are agreed upon. Every node that follows the rules specified in the bitcoin protocol will know how to read, interpret and implement these words. Because bitcoin messages are basically nothing more then a string on bytes, these words are not written in plain English, rather are translated to
OP_CODEs. That way, we can send our message as a string of bytes, and the receiving node will know that these bytes represent some instructions. (Important note, The receiving node will only treat this bytes as instructions only if they appear inside one of the script field.)
Here’re selected few:
|OP_1ADD||139||0x8b||in||out||1 is added to the input.|
|OP_1SUB||140||0x8c||in||out||1 is subtracted from the input.|
|N/A||1-75||0x01-0x4b||(special)||data||The next opcode bytes is data to be pushed onto the stack|
|OP_MIN||163||0xa3||a b||out||Returns the smaller of a and b.|
|OP_SHA256||168||0xa8||in||hash||The input is hashed using SHA-256|
|OP_EQUAL||135||0x87||x1 x2||True / false||Returns 1 if the inputs are exactly equal, 0 otherwise.|
The original list included around 200 of these words, but currently most nodes will only support few dozes of these words. Using these few words we can create many “riddles” or state many conditions to claim the coins in our transaction message.
For example I can add the following string of bytes as my script.
0x01 0x8b 0x87 0x02 0x87 <1> <OP_1ADD> <2> <OP_EQUAL>
- It will take the number 1.
- Use the OP_CODE
OP_1ADDto add 1 to it -> The output of this OP_CODE will be 2.
- Use the OP_CODE
OP_EQUALto make sure if the result is equal to 2. -> The output of this OP_CODE will be True.
A word of caution though, most nodes not only refuse to accept most of these OP_CODEs, they will even refuse to accept most non-standard scripts, mainly because they want users to use standard transactions. Many nodes will not only refuse to accept a transaction with a non standard script, they’ll also refuse to transmit these transactions to other nodes.
You might’ve already noticed that this script language can only be written as a list of operations. Unlike other high level languages (such as python for example) Scripts can only be used in a predefined order. This type of structure is called stack, because we’re stacking variables and data on top of each other. But not only we’re stacking them, using the stack structure also means that they’ll be processed in accordance to the order in which they were stacked.
In our previous example, the integer 1 was the first item in our stack. Then came the operation
OP_1ADD which took that item as its input, processed this item by adding 1 to it, and than giving the output 2. Now the number 2 is stacked BELLOW the integer 2.
<1> <OP_1ADD> <2> <OP_EQUAL> <2> <2> <OP_EQUAL>
The node recognize the OP_CODE <0x02> as the integer 2, so it moves on to the next item in our stack – the OP_CODE
OP_EQUAL. This operation input is the two items that are directly bellow it and compere the two. If both are equal, it will return True.
This example code can’t be used with a standard bitcoin transaction, it’s only meant to give you a general feel on how scripts works.
You can find an example of a real transaction over here:
Give it a try with bitpy
One of bitpy newest feature is the ability to create stacks and see them in action in real time. Mind you, only few OP_CODES are currently implemented, but it might still give you a feel on how stacks works.
Simple stack architecture with python
Stack architecture can easily be implemented using arrays. After all, it’s nothing than an array of objects (variables, operations, results etc’).
In our bitpy project, under
Utils/OpCodes/Codes.py I’ve created a
stack class. In its most basic form, this class will only create and empty array upon initialization, followed by 2 methods only.
class Stack(): def __init__(self): self.items =  def push(self, item): self.items.append(item) def pop(self): elm = self.items.pop() return elm
- push(item) append new item to the array
- pop(item) remove the topmost item in my array.
This should be enough to create a very basic stack class. Still, I’ve added few more methods.
class Stack(): def __init__(self): self.items =  def isEmpty(self): return self.items ==  def push(self, item): self.items.append(item) def pop(self): elm = self.items.pop() return elm def size(self): return len(self.items) def printStack(self): display = "" for items in self.items: items = str(items) if len(items) > 5: display += " " + "<"+ items[:5] + "..." + ">" else: display += " " + "<" + items + ">" return display def clear(self): self.items.clear()
isEmpty method will check if our stack array is empty.
size method will give us the size of the array.
printStack will provide us with a visual representation of our array. Pay attention that I’ve limited the size of each item to only 5 characters so that items such as hashed messages, bitcoin addresses, keys etc’ won’t take the all screen.
clear method will remove all items from our array.
Using this methods we can easily start implementing more advanced OP_CODE to our stack array.
def OP_DUP(self): elm = self.pop() self.items.append(elm) self.items.append(elm) def OP_HASH160(self): #saved as string! self.push(Utils.keyUtils.keys.generate_hashed_public_key_string(self.pop())) def OP_EQUAL(self): elm1 = self.pop() elm2 = self.pop() if elm1 == elm2: self.push(1) else: self.push(0) def OP_VERIFY(self): top = self.pop() if top == 1: self.push(1) else: self.push(0) def OP_RETURN(self, input): self.push(input)