Why Lua?

One of the more eyebrow-raising aspects of Operate’s design is that Ops - the on-chain functions that power-up your Bitcoin transactions - are written using the Lua scripting language.

A few people have asked me, “why Lua?”, but this is a question that demands more than a 280 character tweet. So grab a cuppa and make yourself comfy whilst I explain how I settled on Lua and why I think it’s a good choice.

# Before Operate

At the beginning of 2019 I was working on an experimental project called Functional Bitcoin. This work was really just a learning exercise, and Operate is what Functional Bitcoin would have been if I knew then what I knew now.

Functional Bitcoin used JavaScript for its on-chain functions. A logical choice because JavaScript is the most popular scripting language in the world, has the most vibrant developer ecosystem with endless resources and open source libraries, and offers the broadest reach in terms of potential users.

In addition, one of my goals is to get this technology working in the browser. Given all of this, JavaScript would seem like an obvious choice.

# Sandboxing

The fundamental design of Operate and Functional Bitcoin are the same. Anyone is able to publish code on-chain, and any other party may end up loading and running that untrusted code. So in theory, attackers may publish malicious code in functions, and try to get other parties to run that code.

The solution to this problem is to sandbox the environment in which functions are run. If the functions can’t access the host’s file system, kill processes, make changes the the host’s environment, or access any sensitive data, there's little damage an attacker can cause. In the case of browsers, we need to prevent the functions from accessing the current origin’s cookies and local storage.

In Functional Bitcoin I made use of the vm2 module, which is a very solid sandbox solution for Node.js, but not one that is usable in browser environments. I looked in to some convoluted solutions involving iFrames and web workers, but I’m not aware of any truly battle-hardened browser sandbox solutions.

Eventually I came to the conclusion that, perhaps ironically, JavaScript wasn’t going to be the solution if I wanted to get this stuff working in the browser.

# Enter Lua


When I started work on Operate, I spent some time researching possible alternatives to JavaScript. My primary concern was the sandboxing issue, but I also wanted to find a scripting language that could be embedded and used in different languages and platforms - so Operate would not necessarily be tied to one platform. And I wanted something lightweight, nimble and easy to learn.

It didn't take long for me to realise that Lua not only ticked all those boxes, it was pretty much the stand-out candidate. If you want to let users run in-process, Turing-complete scripts from untrusted sources, Lua is the best language for the job.

Lua was always designed to be embedded. Something like Operate is exactly the use case Lua excels at. Effective sandboxing is easily achieved, and due to Lua's simple lightweight design, it is a very easy language to learn.

There are a few other languages occupying a similar space to Lua, but I considered them all a bit too obscure. Lua is actually quite widely used in some surprising places - for example in nginx and Redis, in embedded hardware, and particularly in games and the game-modding scene - so has a reasonable ecosystem around it.

I then discovered there were solid implementations of Lua available to both Elixir/Erlang and JavaScript and the decision was made. Operate would use Lua.

# Lua vs JavaScript

Both languages are actually very similar. They are both prototype-based languages with similar(ish) variable scoping rules. There are some subtle differences between the two, but fundamentally switching from one to the other is pretty easy. I suspect anyone competent in JavaScript can wrap their head around Lua in one afternoon of playing with code.

Lua vs JavaScript

One difference that is relevant to Operate is how Lua handles binary data. Rather than using Buffers and converting to and from strings, in Lua strings are just sequences of individual bytes that can contain any arbitrary binary data. I prefer this - it means we can just read any data from Bitcoin transactions, and whether it is a UTF-8 string or some other binary data, we can use it in the same way.

Lua is a small language. It has few concepts to learn, just one data type (the all-flexible "table" - similar to a JavaScript object but any value can be a key) and a minimal but useful standard library.

Some will undoubtedly find Lua a more restrictive environment than JavaScript, and it certainly has a much smaller ecosystem of resources and open source libraries. But when it comes to Operate this isn't such a problem. Whilst there is no limit to how large and complex your Ops become, I envisage most Ops will be small, single-purpose functions designed to do one thing and do it well.

# To the browser and beyond

At the start of this article I stated that it is a goal to see Operate in the browser. Then I went on to abandon JavaScript and create the agent software in Elixir running Ops written in Lua. How does this get in the browser?

JavaScript has an excellent implementation of Lua that can be used in the browser. Very soon you will see an Operate agent written in JavaScript. Any programming language that has an implementation of a Lua VM can in theory have the Operate agent ported to it. Soon, Bitcoin transactions made for Operate will be able to be executed in a safe sandboxed environment, in the browser, Node.js, Elixir, and more platforms, and always return the same consistent result.

That is why Lua.