Jeremy Rubin's Blog

Here you'll find an assorted mix of content from yours truly. I post about a lot of things, but primarily Bitcoin.

categories: Bitcoin, Shenzhen Journey.

Review of Smart Contract Concepts for Bitcoin

Day 7: Rubin's Bitcoin Advent Calendar

Welcome to day 7 of my Bitcoin Advent Calendar. You can see the previous post here or subscribe at judica.org/join to get the new posts in your inbox

In this post we’ll review a concepts for thinking about different types of smart contract capabilities and the implications of their availability.

Recursive v.s. Non Recursive

Recursive is pretty much just a fancy way of saying “loops”. This is sometimes also called “Turing Complete”. That’s an even fancier way of saying loops. For example, imagine a bitcoin contract with the following logic:

When Alice requests moving 1 coin to Bob by broadcasting a transaction with the request, Alice has 24 hours to completely cancel the transfer by broadcasting another transaction.

This is a looping contract because after cancelling Alice can immediately re-request the transfer. An example of non-looping but similar logic would be:

When Alice requests moving 1 coin to Bob, Alice has 24 hours to cancel the transfer by sending the coins to Alice’s backup key.

Here, the contract terminates after one canceled request by moving the coin elsewhere. It’s possible to emulate recursive behavior a limited amount by “unrolling” a loop. For example:

When Alice requests moving 1 coin to Bob, Alice has 24 hours to cancel the transfer by sending the coins to (when Alice requests moving 1 coin to Bob, Alice has 24 hours to cancel the transfer by sending the coins to Alice’s backup key).

Here we substituted the backup key with a copy of the original logic. Now Alice can make 2 cancellable requests before sending the money to the backup. This looks recursive, and it can be expressed by a recursive meta-program. Meta program is just a fancy term for a program that makes programs. But when we put the contract into writing (e.g., an address on the chain), it has to be unrolled for the specific number of iterations we want possible.

Unrolling is a very useful technique, and can be used in a broad variety of circumstances. For example, imagine we unroll a contract a million times and specify that transactions can only happen every 10 blocks. That covers like 200 years of contract execution. However, unrolling has it’s limits. When choices (action A or B) are introduced, unrolling can be less effective since you have and exponential blowup (that means unrolling even like 32 steps might be too many). However, there are some tricks that can be employed by a clever and careful programmer to reduce this complexity through, for example, memoization.

Fully Enumerated v.s. Open Ended

Suppose I have a contract which is supposed to strike an American option1 and transfer a token. It might look like this:

If Alice is paid 1 BTC by December 25th, 2021 Midnight, then transfer 100 tokens to Bob’s Control.

A fully enumerated contract would be expressed as:

If Alice is paid 1 BTC by December 25th, 2021 Midnight, then transfer 100 tokens to Bob’s Address B.

Whereas an Open Ended contract would be expressed as:

If Alice is paid 1 BTC by December 25th, 2021 Midnight, then transfer 100 tokens to the address Bob requested with the payment.

The key difference being that in the fully enumerated case we must know the exact specifics of the contract and how it will execute, and in the open ended contract case there are bits and pieces we can dynamically specify.

There are ways that a fully enumerated contract can emulate dynamic choice. For example:

If Alice is paid 1 BTC by December 25th, 2021 Midnight, then transfer 100 tokens to one of Bob’s Address B1, B2, or B3 at Bob’s discretion.

Now Bob can pick from one of three destinations in the future. However, these options must have been known in advance (a priori). With an open ended contract, the address could be generated after the fact (post hoc).

This is a separate concept from recursive or non recursive. A contract that loops could loop through a set of fully enumerated states until reaching some terminal predetermined “exit” state (e.g., a plain address). The option contract described above is non-recursive, but can be open ended.

Client v.s. Consensus Validation

When you have a Bitcoin in an output, anyone who has run, say, Bitcoin Core can tell that it is valid by seeing it in the UTXO set. But what happens if you want to issue a token on top of Bitcoin with some set of unique rules? Bitcoin does not know anything about these tokens, and so it would be possible to make an invalid transaction (e.g., spending more value than you have). In order to ensure the token is valid and not corrupt, one must trace every prior transaction back to some “axiomatic” genesis transaction(s) minting the token. These traces can be cached, but by default Bitcoin software will not enforce that only valid transfers be made. We say that the token is Client validated while the Bitcoin is Consensus validated.

Is one worse than the other? Not necessarily. While permitting invalid transactions in the chain seems bad, as long as the invalid transactions can be uniformly excluded by all who care about the token it is not much worse than the work you do to run a Bitcoin full node anyways. There does seem to be some value in the Bitcoin network preventing you from making invalid transactions, but the network doesn’t stop you from making bad transactions (e.g., you could send money to the wrong place).

Client side validation can check all sorts of properties, not just tokens. For example, you could write a contract for an on-chain governed company and check transactions for valid state transitions amending the rules.

The main drawback to client side validation comes when you want your contract to interoperate with Bitcoin values. While client side validation can burn tokens that are transferred invalidly, imagine an exchange contract that swaps Bitcoin for Token. If the exchange contract sends more Bitcoin than it should, the clients can tell that it was an invalid transaction but the Bitcoin is still gone. Thus Client validated contracts are best left to things that don’t hold Bitcoin. The exception to this rule is if the Client validated contracts admit a custodian, a special monitor or set of monitors that handle the contracts Bitcoin balances in e.g. a multisig. The monitors can client-side validate the contracts and sign off on any balance transfers. The drawback to this approach is trust, but in certain applications that we’ll see later the monitor could be all of the participants themselves, which makes the application of the rules trustless.

Validation v.s. Computation

Validation and Computation are two sides of the same coin. A simple example to demonstrate:

Computation Sort the numbers [4,5,1] None [1,4,5]
Validation Check that [4,5,1] is sorted by indexes A A = [2,0,1] True

Validation is a computation, but hopefully it’s easier to perform the validation computation than the computation itself.

In a Bitcoin transaction we are always validating that the transaction was approved. A transaction in Bitcoin makes a clear precondition (the coins spent) and postcondition (the coins sent). Even in cases where we have to do a lot of computation to check the authorization, we still know the proposed outcome.

Compare to an Ethereum transaction: We pass some input to a function, and the EVM verifies that our input was authorized (e.g., send 1 Eth to contract X with message “hello”). Then, the side effects of that action are computed dynamically by the EVM. For certain contracts, we might be able to predict what the side effect will be (e.g., a 1:1 token exchange like Eth to Wrapped Eth), but for other contracts (e.g., a floating exchange rate for Eth to Wrapped BTC) we will get an outcome that can’t be fully predicted. It is possible for contracts to choose to engineer themselves in a way to create more predictability, however in Ethereum this does not result in an Invalid transaction, it results in a valid transaction (that e.g. still costs gas) that has a result which is not taken. For example, a transaction which says “Buy 1 WBTC for 15 ETH” might fail to acquire WBTC since the price is now 16ETH, but the transaction would be valid that you tried to make the trade and failed. This is because Ethereum’s base layer is computational in nature with little validation: validation must be built on top.

Sidenote: Malleability

For certain Bitcoin “covenant” transactions the validation/computation line can be thin. Transactions must always be transactions in a block, but it’s possible that in the future miners could receive “details” of a transaction and be responsible for generating the appropriate transaction themselves. For example, Blockstream released details on a noninteractive feebumping transaction, whereby a miner can dynamically compute a transaction that pays them more fees the longer it takes to confirm.

In the case of malleability like this, it’s not as simple as saying “don’t do it”, because miners have an incentive to extract the value if it is available.

Dynamic State

Contracts can have different types of state. State is just a fancy term for information available to execution.

Global state is information that is observable from anywhere. An example of this in Bitcoin is the UTXO Set: any transaction could spend any coin, and can “pull it into scope” by naming it’s Outpoint (whether or not the transaction is valid is another question). Another example of global state is the current block height, used for validating things like lock times. In Ethereum, there is a much expanded concept of Global state whereby contracts persist and allow read/write access from other contracts, and unlike Bitcoin’s UTXO set, observing a piece of information doesn’t destroy it like spending a coin does.

Local State is information observable only within your own context. For example, a contract might hold balances for 3 different people, but the current values of those split balances is not something queryable by outside parties. This also includes implicit state, such as “the contract is currently pending an Action from Alice” that are not explicitly coded.

Lastly, certain things are not State. An example of this is an authorizing signature, which is ephemeral data that is used in the transaction execution but does not have relevance for the continued execution of the contract and is not particularly observable (which signature we use shouldn’t matter).

General v.s. Specific

A General contract primitive is something that can be used across many different types of contract. A Specific contract implements well defined logic. In Bitcoin and Ethereum, the focus is on General contract primitives that can be used many ways. In some other ecosystems (e.g. NXT, Stellar), contract primitives have much more specific functionality.

General/Specific ends up being more of a spectrum than a binary. Certain contract primitives might be very specific but find general use, similarly some general primitives might be more general than others.

For example, the Lightning Network on Bitcoin has pursued a path of using general purpose extensions to Bitcoin so as not to “special case” payment channels. But is that worth it? Might Payment Channels be cheaper, easier to use, etc if we just designed built-in channels from the get-go? Perhaps yes, but then it might be harder to embed other things or incorporate new innovations into Lightning if it had to fit a single mold.

This isn’t an exhaustive list of topics by any means, but it should be a good primer for thinking about upgrade proposals that people discuss in Bitcoin. You’ll find out more about that in… tomorrow’s post!.

1. An American option is the right to either purchase or compell a counterparty to buy an asset until a deadline.

What's Smart about Smart Contracts: Bitcoin Maxi Edition

Day 6: Rubin's Bitcoin Advent Calendar

Welcome to day 6 of my Bitcoin Advent Calendar. You can see the previous post here or subscribe at judica.org/join to get the new posts in your inbox

Now that we’ve established the four pillars of Privacy, Decentralization, Self Custody, and Scalability, let’s get into smart contracts. But first…

DOES BITCOIN HAVE SMART CONTRACTS?

There is a lot of fuss around if bitcoin has or doesn’t have smart contracts, and this is usually people talking past one another. Bitcoin does have enough functionality to create certain smart contracts. But Bitcoin does not “have Smart Contracts” in the same way that, say, Ethereum “has” Smart Contracts. Sure, one can argue that because Ethereum is weaker in terms of its fulfillment of the four pillars, it doesn’t really have smart contracts either. But almost undeniably there is something happening in the Ethereum Ecosystem that isn’t happening for Bitcoin – yet.

Often, Bitcoin Boosters will say that the types of things happening on Ethereum aren’t desirable at all and are just scams. Many of these Boosters then go on to promote projects of similar dubious nature… but that’s off topic for this post! While there are many projects that frankly suck, there are also many projects on Ethereum that are relevant to the interests of Bitcoiners! Examples of projects that advance Ethereum’s realization of these 4 pillars that would be difficult to build on Bitcoin include: Gnosis Safe for Custody; Tornado Cash for Privacy; Optimistic/Zero Knowledge Rollups for scalability; SmartPool for on-chain mining pool coordination. It’s claimed that any time something of value proves out in the other ecosystems, Bitcoin can easily just incorporate the highlights.

My position is somewhat unique on this matter: Smart Contracts aren’t something you build on top of a layer with good decentralization, privacy, scalability, and self custody – Smart Contracts are a central part of what makes achieving those pillars possible! In other words, we need a Smart Contract ecosystem that enables broad innovation in order to make our four pillars robust. This is not the same as saying we need the Ethereum VM, but we do need something to be different than the status quo today to empower builders to create new tools on top of Bitcoin. This differs from a traditional Bitcoiner perspective which is more along the lines of once we improve our (insert generic property here); then we can consider figuring out how to add more smart contracts.

That bar sucks it’s too crowded

Another reason sometimes given for not wanting smart contracts is that they’re too expensive and won’t scale. While this is a valid concern, the story around fees is somewhat interesting. You may have seen people complain about high fees on other platforms and say therefore it sucks and should die. It’s a bit like saying a crowded bar is no good. Obviously, if people are at the bar it is good. That your enjoyment is less is solely because you’re antisocial. On other platforms, there are users paying exorbiant fees to do transactions… but would they be doing them if they weren’t getting commensurate value? Let’s have a look at some data from cryptofees1.

Name 1 Day Fees 7 Day Avg. Fees
Ethereum $62,620,320.03$55,285,528.00
Uniswap $11,315,687.79$10,507,247.12
Binance Smart Chain $7,240,187.13$7,525,565.73
Balancer $1,572,934.46$445,368.69
Aave $1,465,761.04$1,354,792.55
SushiSwap $1,379,856.87$1,664,071.03
Bitcoin $1,160,676.57$730,368.10

Clearly a lot of folks are willigng to pay for Ethereum and projects on top of it. Bitcoin is ultimately a business, and it relies on its customers paying fees to incentivize the production of blocks. More fees, more incentive to provide security for Bitcoin. It’s a little problematic, therefore, when users are getting more utility from (by virtue of how much they are spending) other chains than Bitcoin2.

Although we need to be careful to not hurt Bitcoin’s essential properties, it’s clear that smart contracts provide massive leverage for incentivizing users to do transactions to pay for block production, without which Bitcoin falls apart.

Capitalist or Communist?

What’s this got to do with Bitcoin?

If Bitcoin is held in accounts at regulated entities like exchanges a similar act to 6102 could make redeeming actual bitcoin impossible for users of those services. Suppose those users are forced to receive in place of their exchange Bitcoin a Bitcoin Note that is backed by bitcoin. And then one day, the amount of Bitcoin per Bitcoin Note can be reduced – or worse, completely unlinked.

If this happens, all is lost. Bitcoin is fundamentally about a monetary standard for the world where no self-important rulers can manipulate the currency. Self custody is a requirement for Bitcoin to avoid these sorts of takeovers.

OK, OK, I’ll keep my coins off exchange…

Just fixing your behavior isn’t enough, to keep Bitcoin functional you need most users to follow suit. Yet many users today do choose to keep some or all of their Bitcoin on centralized services (yours truly included!).

This is for two main reasons:

1. Game Theory: we can’t do anything about this. As long as not too many other people are using an exchange for custody, it doesn’t matter if you are since a regulatory takeover won’t be too effective. So selfishly, you may as well benefit from the ease of keeping your coin on a service (assuming it is easier) rather than taking responsibility for your own assets. Likewise if no-one else is self custodying there’s not much advantage for you to be either.
2. Software freakin’ sucks for self custody! We can fix this. Although the quality of wallets has improved dramatically from Bitcoin’s early days, it’s still incredibly difficult to do well. Further, self-custody solutions don’t have solid options for handling many common needs such as inheritance, spending limits, and more.

If we work on strengthening the fully self-sovereign self-custody options that Bitcoin users have at their disposal, we can help more Bitcoin users to choose to keep their funds themselves and achieve “herd immunity” against future executive order 6102s. If we self custody in great number, we don’t permit them the immediate victory over most users. You can’t arrest everyone, not easily at least.

in short…

STRONK

1. A troy ounce of gold is about a 1 inch by 1 inch square that is 0.1 inches high.