BTC#: Stack Machine Operations

Series: BTC# – Learning to Program Bitcoin in C#

« Previous: The Stack

Next: Stack Machine Operations »

The Bitcoin stack machine operates on a stack of commands. Those commands can take two forms – elements and operations. Elements are numbers, or you can think of them as byte arrays. They could be numbers like zero and one or things like signatures and public keys. Operations are the instructions to the stack machine to do something. Typical operations remove (“pop”) an item from the stack, do something with it, and push a transformed value or a result back onto the stack.

BitcoinMechanics

I’ve created a StackMachine class that will have an Execute method, that we’ll come to later, and a collection of other static methods that perform the various stack machine operations.

Each of these methods will take the stack as an argument and return a boolean that indicates that the operation succeeded. I haven’t implemented all of these yet, but I’ve generated stubs for them all that return false.

public static bool OP_ADD(Stack<byte[]> stack) { return false; }

The stack argument uses the built-in generic C# Stack class, which gives us the Pop, Push, and Peek methods out of the box.

The type I’ve given to the generic Stack class is byte[]. In C# we need to supply a type, unlike the Python code in Programming Bitcoin, which is untyped and takes a mixture of integers and byte arrays. This means that our C# code is a little more verbose in some places, where we move between types.

With the stub methods in place we can write tests first. Because the stubs all return false, we can use the classic TDD red-green process of starting with a failing test and building up functional code from there.

For our first example, I’ll take the Duplicate operation, OP_DUP. This looks at the top element of the stack and duplicates it so that there are two identical copies on the stack.

[TestMethod]
public void OP_DUP_true()
{
    var stack = new Stack<byte[]>();
    stack.Push(BitConverter.GetBytes(12345));

    var result = StackMachine.OP_DUP(stack);
    var stackElements = stack.ToArray();

    Assert.IsTrue(result);
    Assert.AreEqual(2, stack.Count);
    CollectionAssert.AreEqual(stackElements[0], stackElements[1]);
}

The test puts something on the stack (it doesn’t matter what), calls the duplicate operation, and then checks three things: that the operation returned true, that there are now two elements on the stack, and that the byte arrays that make up each of the elements are identical. You could also add a test to make sure that if there were no elements on the stack when the duplicate operation was called, that it failed, returning false.

To get the test to pass, replace the stub with a method body that checks for an element on the stack and duplicates it.

public static bool OP_DUP(Stack<byte[]> stack)
{
    if (stack.Count < 1) return false;

    stack.Push(stack.Peek());
    return true;
}

The Peek() method returns that value of the top element on the stack but doesn’t pop it off.

The next exercise in the book is to implement the OP_HASH160 operation.

public static bool OP_HASH160(Stack<byte[]> stack)
{
    if (stack.Count < 1) return false;

    stack.Push(stack.Pop().Hash160());
    return true;
}

This is very straightforward because it delegates the work to the Hash160 implementation that I added when we were looking at Base 58 encoding.

In the next post, we’ll look at how we spend funds by executing combined locking and unlocking scripts.

« Previous: The Stack

Next: Stack Machine Operations »

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s