Ultimate Scripting: A Platform for Generalized Financial Contracts on Mastercoin

0.1. Introduction

Perhaps the key advantage of Mastercoin over the raw Bitcoin protocol is the potential to include much more advanced transaction types, including transactions that specify behavior based on future information well off into the future. For example, Mastercoin joins Ripple in being one of the only two major cryptocurrency networks that include the ability for users to make binding exchange offers as a type of transaction. From there, the Mastercoin Foundation intends to integrate even more complex contracts, including bets, contracts for difference and on-blockchain dice rolls. However, up until this point Mastercoin has been taking a relatively unstructured process in developing these idea, essentially treating each one as a separate "feature" with its own transaction code and rules. This document outlines an alternative way of specifying Mastercoin contracts which follows an open-ended philosophy, specifying only the basic data and arithmetic building blocks and allowing anyone to craft arbitrarily complex Mastercoin contracts to suit their own needs, including needs which we may not even anticipate.

0.2. Specification

The underlying idea behind this specification is to allow anyone to create a contract which pays out according to an arbitrary formula. The formula will be defined in a Bitcoin-like stack-based scripting language, consisting of numbers and opcodes.

The evaluation algorithm is as follows:

dataStack = [] 
opStack = script
while len(opStack) > 0:
    var op = opStack.pop()
    if typeof(op) == 'opcode': eval(dataStack,op)
    else: dataStack.push(op)
return dataStack.pop()

Where eval is defined for each opcode below. Any error (eg. division by zero) will make the script return FAIL, and result in the entire transaction being treated as invalid by the Mastercoin network. All variables will be signed 64-bit integers, and all arithmetic operations wrap around (that is, if the underlying arithmetic operation returns R, the value pushed is ((R + 2^63) % 2^64) - 2^63.

For simplicity, we define ds[-1] to be the topmost value on the dataStack, ds[-2] the second topmost, etc. The opcodes are in two categories: arithmetic operations and data operations. Arithmetic operations are:

If a block which does not yet exist is passed into any of these commands, then the script returns FAIL; similarly if BLOCK_POST is called on a time where no block exists yet or BLOCK_PRE on any time before the Genesis block.

There is also a special operation:

A Mastercoin contract transaction will have the following form:

MAKE_CONTRACT_OFFER(PRIVKEY,CURRENCY,X,Y,[FORMULA_0, FORMULA_1 ...])

Where FORMULA_k for all k is a scripts. This will take X units of CURRENCY from the address controlled by PRIVKEY and put it in escrow. Anyone else will then be able to create a transaction of the form:

ACCEPT_CONTRACT_OFFER(PRIVKEY,TXID)

And put in Y units of currency to activate the contract. Then, to complete the contract anyone will be able to run:

CLAIM_CONTRACT(TXID,FORMULA_INDEX)

Suppose FORMULA_INDEX is k. Then, the scripting engine will run FORMULA_k. If FORMULA_k fails, the transaction will be meaningless. If it succeeds, say with output N, it will then close the script and automatically give N units of CURRENCY to the contract maker, and X + Y - N to the acceptor (coercing N to inside the range [0 ... X + Y] if necessary).

0.3. Examples

Contract for difference

MAKE_CONTRACT_OFFER(
    Bobs_privkey,    
    MSC,
    1000000000,
    1000000000,
    1400000000 BLOCK_POST <MSC_USD> PRICE_FEED 40 SUB 100000000 MUL 1000000000 ADD
)

Pseudo-decompilation of the script:

1000000000 + 100000000 * ( PRICE_FEED(MSC_USD, BLOCK_POST(1400000000)) - 40)

This is a contract for difference, where Bob pays in 10 MSC, asks someone else to pay 10 MSC, and which allows either party to claim the contract to pay Bob 10 MSC plus 1 MSC for every dollar that the Mastercoin price falls below 40 by Unix time 1400000000 (May 2014). Note however a full contract for difference would be more complex, including a "force liquidation" clause for both parties:

MAKE_CONTRACT_OFFER(
    Bobs_privkey,    
    MSC,
    1000000000,
    1000000000,
    CURBLOCK <MSC_USD> PRICE 30 GE CHECK_NONZERO 2000000000,
    CURBLOCK <MSC_USD> PRICE 50 LE CHECK_NONZERO 0,
    1400000000 BLOCK_POST <MSC_USD> PRICE_FEED 40 SUB 100000000 MUL 1000000000 ADD
)

Pseudo-decompilation of the first sceipt:

CHECK_NONZERO ( 30 >= PRICE_FEED(MSC_USD, CURBLOCK) ) 2000000000

Note that it would be the responsibility of each party to make a liquidation claim. The reason why this was done was for efficiency; we do not want to have to evaluate every open contract every block.

Plain bet

Will the price be above 40 or below it at time 1400000000?

MAKE_CONTRACT_OFFER(
    Bobs_privkey,
    MSC,
    100000000,
    100000000,
    1400000000 BLOCK_POST <MSC_USD> PRICE_FEED 40 LE 200000000 MUL
)

Crop insurance

Data feeds do not just need to be just about prices. Suppose that you are a Farmer in Iowa and you want to hedge against the possibility of a drought:

MAKE_CONTRACT_OFFER(
    Bobs_privkey,
    MSC,
    100000000,
    100000000,
    1400000000 BLOCK_POST <PRECIPITATION_IN_IOWA> SPECIAL_FEED 2000000000 SUB
)

Pseudo-decompilation:

2000000000 - SPECIAL_FEED(<PRECIPITATION_IN_IOWA>, BLOCK_POST(1400000000))

Blockchain-based coin flip

MAKE_CONTRACT_OFFER(
    Bobs_privkey,
    MSC,
    100000000,
    100000000,
    ACCEPTANCE_BLOCK 2 ADD BLOCK_HASH DROP DROP DROP 2 SWAP MOD 2000000000 MUL
)

Pseudo-decompilation:

(BLOCK_HASH(ACCEPTANCE_BLOCK + 2)[0:64] % 2) * 2000000000

Fairly self-explanatory. Note that this does assume that neither party has substantial mining power (specifically, enough to be able to send out three blocks at once)

Third-party-judged insurance

MAKE_CONTRACT_OFFER(
    Bobs_privkey,
    MSC,
    5000000,
    1000000000,
    < judge's address[0:64] > < judge's address[64:128] > < judge's address[128:160] > HASINPUT CHECK_NONZERO 1005000000,
    1400000000 CURTIME GT CHECK 0
)

The judge can force the policy to pay out to Bob at any time, but if the judge does nothing it eventually expires, sending all funds to the insurer.

Possible Extensions

  1. One project that would likely be necessary is a compiler to generate script code from a more human-friendly language (at the least, S-expression syntax, ideally BEDMAS-style arithmetic).
  2. Allow a type of contract that pays out to any address, essentially swallowing up Bitcoin multisig functionality at the same time. A scripting opcode that pushes the script's output address as three integers might be necessary here. If this is integrated, then the scripting system would potentially be able to describe any transaction in existence.
  3. Allow contracts with one party, more than two parties, a coalition of parties, etc, and allow scripts to look at who donated to the script and how much. It might be possible to encode an entire distributed autonomous corporation that proportionally pays out dividends to investors into a script.
  4. Allow integers all the way up to 2^256 to allow proper cryptographic arithmetic (eg. enhance the dice algorithm with full-scale provably fair gambling scripts)
  5. Allow floating point numbers.