Bitcoin contracts

comment

Bitcoin is designed to support a wide variety of transaction types. Although currently the majority of transactions are standard payments to address it is possible to build more complex protocols.

Block chain can be used to build agreements between people that do not require trust or central authorities. Properties exchanged via the block chain may even be physical things like cars.

Bitcoin achieves high flexibility due to three features:

  1. scripts — unlocking funds in transactions is done using a simple scripting language,
  2. signature hash flags — indicate which parts of the transaction are signed,
  3. sequence numbers and lock time — mark transactions as not valid until specified time.

This implementation uses several ES6 features not yet enabled by default in Chrome (version 36). It is necessary to enable Experimental JavaScript at chrome://flags/#enable-javascript-harmony.

Firefox (version 31) supports generators as well as Promises.

Stack visualization uses position: sticky CSS property that can be enabled by Experimental Web Platform Features at chrome://flags/#enable-experimental-web-platform-features in Chrome or layout.css.sticky.enabled toggle at about:config in Firefox.

Standard transaction

Most of Bitcoin transactions are sending funds to an address. Addresses are encoded public key hashes with a checksum.

Sending bitcoins to an address internally creates a transaction containing a script with embedded public key hash. This script checks if the spending transaction is signed by a correct key.

The address below is a version 1 bitcoin address:

1DTjvhLV6S72NQrSDrCX1GTCb9B3D5pmCB

Decoding it using the base58 encoding yields a byte array:

This array can be split into three parts. First byte is a version tag (), last 4 bytes are used as a checksum () and the rest is a public key hash:

The checksum is verified by using the SHA-256 function on the version and the public key hash twice and taking first four bytes of the digest:

The address above is .

The extracted public key hash is used to construct the output script. The script will unlock funds only when the next transaction provides two parameters — the public key that hashes to the given hash and a signature created using the private key that matches that public key.

082eae...in #0out #08d212a...in #0out #0

Transaction of the left (082eae) transfers ownership of bitcoins to the address 1DTjvhLV6S72NQrSDrCX1GTCb9B3D5pmCB by setting up a short script in one of its outputs.

Output #0’s script contains the following five operators:

  1. OP_DUP — duplicates an item on the stack,
  2. OP_HASH160 — hashes top-most item on the stack twice — once with SHA-256 and then with RIPEMD-160,
  3. 88b028348642ad1bbaa8fcc054273070eda045fe — pushes this byte vector onto the stack,
  4. OP_EQUALVERIFY — takes two top-most items on the stack and checks if they are equal, if not the entire script execution fails,
  5. OP_CHECKSIG — verifies if the current transaction is signed by the correct key (the private key that corresponds to the public key that was hashed earlier).

The output script can be also represented in a simple programming language as a list of parameters that will be provided by spending the transaction’s input script (assume statements) and list of assertions that should be met to move funds (verify statements).

assume signature, pubKey

verify hash160(pubKey) == '88b028348642ad1bbaa8fcc054273070eda045fe'
verify checkSig(signature, pubKey)

Compiling to the Bitcoin Script results in these opcodes:

Executing script

When a different transaction wants to claim the coins sent to this output it has to provide a second part of the script — the input script. The input script is also called scriptSig as it usually contains signatures.

These two scripts — the input script and the output script — are executed in that order and if the execution completes successfully and leaves a truthy item on the top of the stack then the transaction is considered valid.

The input script (two opcodes) from the spending transaction is concatenated with the output script from the first one:

Stack:

// input script
3045022100de14671eaebfe7eaa085e100f40b4a452fdd3a55975a9007062de361dfc4ccb302207a00ee3ec2341a36effa37d0b7b196202348f0840d6b46fa111d040827d1f39b01
0381fa985809e1a23fc261cb4c02ffdc968a1d2548f40428af1931c3e706f5c7d4

// output script
OP_DUP
OP_HASH160
88b028348642ad1bbaa8fcc054273070eda045fe
OP_EQUALVERIFY
OP_CHECKSIG

Raw transactions

Raw bytes of the transaction on the left:

010000000192572bd6cb7e01247c7f876d63e79e754d7c1e246eda3f2ccf5305b97dc67e73000000006a47304402203a21c63594018be1f0d475ad4cd8e3f6a9465c18ff14ee3da489be8d32690819022019bf6f328c128dcab3cea878cbf2b87efaf4922a0915f8603bef9126eb57ea53012102744bd7d38356ab4563a91b3a2a8aae190feec76cf2e329f488bf429c7b351752ffffffff01b0360000000000001976a91488b028348642ad1bbaa8fcc054273070eda045fe88ac00000000

Parsed into a JavaScript object reveal the transaction’s structure — version field that is currently always 1, lockTime that allows creating transactions that will become valid in the future and a list of inputs and outputs.

// click Parse

Each input references the output in a different transaction (with an exception of inputs in generation transactions), has a sequence number that allows updating that input and a script.

Scripts in standard transaction’s inputs push a signature and a public key byte arrays onto the stack.

Outputs contain only the value in satoshi that is assigned to this output and the output script that will decide whether a different transaction’s input can claim this output’s coins.

Raw transaction on the right:

0100000001c21ae5cfc3a73eba85ad9c7d06304840b1e3f0d147feea41f62ab3392dae2e08000000006b483045022100de14671eaebfe7eaa085e100f40b4a452fdd3a55975a9007062de361dfc4ccb302207a00ee3ec2341a36effa37d0b7b196202348f0840d6b46fa111d040827d1f39b01210381fa985809e1a23fc261cb4c02ffdc968a1d2548f40428af1931c3e706f5c7d4ffffffff01c8320000000000001976a914f4f1d83d4ce7b5a3d2dfb2384af09f6d95c8279388ac00000000

Parsed into a JavaScript object:

// click Parse

Escrow

Standard transactions require exactly one key pair to unlock. Using the OP_CHECKMULTISIG operator instead of the OP_CHECKSIG in the output script allows creating transactions that need several keys to unlock funds.

A classic example is an escrow where a trusted third party (broker) is used between the merchant and the client to secure funds. A transaction is set up that requires two keys out of three (merchant, client and a trusted party) to move coins. If a good is delivered then the merchant and the client agree to move funds to the merchant’s account. If the merchant fails to send the good, the client and the trusted party move funds back to the client. And if the merchant can prove to the broker that the good was indeed sent then that third party and the merchant send coins to the merchant’s account.

Another example of using the OP_CHECKMULTISIG requires both husband and wife agreeing to paying for something or using multiple keys to protect coins in a cold storage.

4bfd9c...in #0out #0out #1436a9a...in #0out #0

Left transaction’s output #1 contains a script that uses OP_CHECKMULTISIG:

  1. OP_2 — pushes digit 2 on the stack (number of required signatures),
  2. 041ce544058996033a34adb07be380e63956c588… — pushes first public key,
  3. 04db5efff14362653c0fc2e5437ac964dd3e0931… — pushes second public key,
  4. 043b9264a9afb2c9dbc3602cf25ab9a5f5ee1f99… — pushes third public key,
  5. OP_3 — pushes digit 3 on the stack (number of provided public keys),
  6. OP_CHECKMULTISIG — takes a number of public keys, then these public keys, then a number of signatures and these signatures and performs the signature verification, if all signatures are valid pushes true on the stack, false otherwise.

The output script represented in a simple programming language:

assume unused, signature1, signature2

let pubKeyA = '041ce544058996033a34adb07be380e63956c588dd036d20824447d88700ec91f45a98894bbbdab68ac304b5e68f77ea2f614516d0ace35f76e3b376b9917d6c84'
let pubKeyB = '04db5efff14362653c0fc2e5437ac964dd3e093110c8fbcd5d9fd135ec3c98dc926d26b344def4397c3d99ebbd56e35c53cdd501e2c7a9a0cc5c2b04e7d0a38751'
let pubKeyC = '043b9264a9afb2c9dbc3602cf25ab9a5f5ee1f991e6edfb9c2982a9d31cd7e41c2c9c2a8ad2a8da0c8943b54192c9fea120bf5cce390b459269698efaadb42d649'

verify checkMultiSig(2, pubKeyA, pubKeyB, pubKeyC, 3)

Compiling to the Bitcoin Script results in these opcodes:

Executing script

The input script (three opcodes) from the spending transaction is concatenated with the output script from the first one:

Stack:

// input script
OP_FALSE
30450220523b47504644354214e847d3a91337516835591e25b5eaeefb8c349823c5ff92022100e4fc44e18a252c853495923f0f88668f1aa539f5c69418dfb392d77cff84049c01
3046022100ab63540868657b7674a77c1fc691f6842d8d15f825241d7ad12fb1ff87e63de3022100dec537d994abccebac546bc260f0dbbed6135c7d470f35409a21d21746380fe601

// output script
OP_2
041ce544058996033a34adb07be380e63956c588dd036d20824447d88700ec91f45a98894bbbdab68ac304b5e68f77ea2f614516d0ace35f76e3b376b9917d6c84
04db5efff14362653c0fc2e5437ac964dd3e093110c8fbcd5d9fd135ec3c98dc926d26b344def4397c3d99ebbd56e35c53cdd501e2c7a9a0cc5c2b04e7d0a38751
043b9264a9afb2c9dbc3602cf25ab9a5f5ee1f991e6edfb9c2982a9d31cd7e41c2c9c2a8ad2a8da0c8943b54192c9fea120bf5cce390b459269698efaadb42d649
OP_3
OP_CHECKMULTISIG

One extra argument (OP_FALSE) is needed in the input script due to a bug in OP_CHECKMULTISIG.

Raw transactions

The first transaction is a lot longer than the standard one because output #1 contains full public keys and not just their hashes.

Raw bytes of the transaction on the left:

0100000001c532b97ded19882906d4f8cfb2f5df927010e57cc17816f44a76d28a5b22d650000000008c493046022100fb0c6b6ab08420425e054e4bbc7d2f022229619ad94dcfe6a5091c43b11b8dff02210097ea263ca74fd561470e1e7da37708faa19661963fc388644c83dbb7914aca8601410413a73711ae2ff667b1b4bf60c5cdf1f63f972cbc7c5fbadc50dc5510dc9d25930c2536b6432c8ab8d8ddadb55a96e5ad957fd2f835bc9b6949a86efa3a65d3acffffffff0290d00300000000001976a9148dc294df289c81d6eb61f72475061ee4da2ee5f988ac7082030000000000c95241041ce544058996033a34adb07be380e63956c588dd036d20824447d88700ec91f45a98894bbbdab68ac304b5e68f77ea2f614516d0ace35f76e3b376b9917d6c844104db5efff14362653c0fc2e5437ac964dd3e093110c8fbcd5d9fd135ec3c98dc926d26b344def4397c3d99ebbd56e35c53cdd501e2c7a9a0cc5c2b04e7d0a3875141043b9264a9afb2c9dbc3602cf25ab9a5f5ee1f991e6edfb9c2982a9d31cd7e41c2c9c2a8ad2a8da0c8943b54192c9fea120bf5cce390b459269698efaadb42d64953ae00000000

Transaction object:

// click Parse

Raw bytes of the transaction on the right:

0100000001c68db8fc6652dc9d7c7a58e48c118c42086f670dd8f87987625cc910ef9cfd4b0100000094004830450220523b47504644354214e847d3a91337516835591e25b5eaeefb8c349823c5ff92022100e4fc44e18a252c853495923f0f88668f1aa539f5c69418dfb392d77cff84049c01493046022100ab63540868657b7674a77c1fc691f6842d8d15f825241d7ad12fb1ff87e63de3022100dec537d994abccebac546bc260f0dbbed6135c7d470f35409a21d21746380fe601ffffffff01605b0300000000001976a9149bfdf3e906ce07448dac85ce385ab6bdad7e7b8188ac00000000

Transaction object:

// click Parse

Kickstarter

Each signature used in input scripts contains a flags field indicating which parts of the transaction are signed. Using this field it is possible to implement crowdfunding campaign (or any other assurance contract).

An entrepreneur announces that they will complete a project only when a specified amount of bitcoins (for example 150 μBTC) is collected. Backers who want to pledge money create transactions sending bitcoins (for example 50 μBTC) to this address, setting the output value to the target amount (150 μBTC) and sign it with SIGHASH_ANYONECANPAY flag.

These pledge transactions are not broadcast but are sent directly to the entrepreneur. As the output value (150 μBTC) is higher than the input value (50 μBTC) it is impossible for the entrepreneur to retrieve bitcoins from these transactions directly.

The SIGHASH_ANYONECANPAY flag allows merging transaction inputs. When enough funds have been gathered (the sum of inputs is higher than target value) the entrepreneur creates one final transaction from all pledges and broadcasts it collecting all the funds.

Creating pledges

Backers create pledge transactions (on the right) that take a small amount of bitcoins (50 μBTC) and set output to target value (150 μBTC). All pledges have one and the same output containing amount and receiving address of the entrepreneur.

878d07...in #0out #0443de4...in #0out #086626c...in #0out #0f392df...in #0out #0c21484...in #0out #0e18f50...in #0out #083e1bb...in #0out #033e4d7...in #0out #0

Raw transaction

Raw bytes of the transaction on the left:

01000000011f5e7131923054920104e5080983572c6e29366d0c7f95548398e7a1c80dfa23500000006b4830450221009ce31d9d621c4ef2a753cb238d8bbb4b02edaf17cea98d95945011b84448bd39022010c699f51a8d1399748ce57b3db3ccd7a076872fd564492df96b1c7ff5ad57e5012103a72a9fc1615f45b461534c0a035ea4ea228f86c11f52dbfa6997f1483dbcc21bffffffff0188130000000000001976a914da8df9cc99e719562b52c209ebea7e28b8b3c60b88ac00000000

Transaction object:

// click Parse

Raw transaction — pledge

Raw bytes of the transaction on the right:

0100000001b6c88c761235e7ccc6ee59d53348537c94a4fe1adaf04c92ce767b4f4c078d87000000006a4730440220207ab184b288275c6466075d724a9f632487b6996489d49c8148cf30f9d5fc1a0220518b62471a25f8da86d0184de5b0496d4c2b272115c84fa58ea32e568b53053d8121032153889b1813175f337f24944ff632a6b9a78b63e09ff8c6a569f5d5d429cf97ffffffff01983a0000000000001976a91454000657e2b8ebed5b1a1565b17aec63583ddc6688ac00000000

Transaction object:

// click Parse

Collecting funds

When the entrepreneur receives enough funds (the sum of inputs is higher than 150 μBTC) they create the final transaction that takes all inputs from pledges and one output (the same output that is used in all pledges). Because the inputs were signed by backers with the SIGHASH_ANYONECANPAY flag the signatures are still valid. The final transaction is broadcast and the entrepreneur collects all the pledges.

878d07...in #0out #0443de4...in #0out #086626c...in #0out #0f392df...in #0out #0737ec6...in #0in #1in #2in #3out #0

Raw final transaction

Raw bytes of the final transaction on the right:

0100000004b6c88c761235e7ccc6ee59d53348537c94a4fe1adaf04c92ce767b4f4c078d87000000006a4730440220207ab184b288275c6466075d724a9f632487b6996489d49c8148cf30f9d5fc1a0220518b62471a25f8da86d0184de5b0496d4c2b272115c84fa58ea32e568b53053d8121032153889b1813175f337f24944ff632a6b9a78b63e09ff8c6a569f5d5d429cf97ffffffff064a3e45ad865f6ee493e0d1dc48d54f8f275ddf5f9f271cae1d294d70e43d44000000006b483045022059f303e73e34d3e56bc6a91ee02248da2ff12ffcdf8b676bc743f886c90ef165022100a4d7e00e4d325fe7e7406757bc9647e2f4f620ceb30ce759e22ecb88fc67f8b081210341607135b7e955eacd6d14018348911c5fc7b930af359ee9ced887f175ba89c4fffffffff0f231fc2ac7fdc5a62836127a747b795931f65a9a4b7379448521617b6c6286000000006a473044022001e345ebcc3e8c0673a77dd9e5fa2bf3d0fa295c7eb867edd4e9269b5bd4e8d002200970be9d82380a4cb7b7778fe078992b65199a5f77e07f953119f44ced6e7a5a8121033a76c02081da7453bc040f781959cca920f19cc2344731510bd81cde3469febfffffffff84639d836b4c7558f815a53fbcaed239f5383dec4e4d6a61c79cd0ff8adf92f3000000006b48304502200a513923f5e71f9f84377a605d38ecb7978d3ea013a58e60bce77ac1c8b94bc5022100d1b9547e610ce4022877dc95ab7cbd9538679a7ecd04f7da300270871b53256e81210341607135b7e955eacd6d14018348911c5fc7b930af359ee9ced887f175ba89c4ffffffff01983a0000000000001976a91454000657e2b8ebed5b1a1565b17aec63583ddc6688ac00000000

Transaction object:

// click Parse

Executing script

The following script validates the first input in the final transaction.

The only difference between this script and the standard transaction’s is the signature hash type (81ALL with ANYONECANPAY flag) at the end of the backer’s signature (line 2). Standard transactions are signed with ALL (01).

Stack:

// input script
30440220207ab184b288275c6466075d724a9f632487b6996489d49c8148cf30f9d5fc1a0220518b62471a25f8da86d0184de5b0496d4c2b272115c84fa58ea32e568b53053d81
032153889b1813175f337f24944ff632a6b9a78b63e09ff8c6a569f5d5d429cf97

// output script
OP_DUP
OP_HASH160
da8df9cc99e719562b52c209ebea7e28b8b3c60b
OP_EQUALVERIFY
OP_CHECKSIG

Multilottery

One example that uses both time-locked transactions and non-standard scripts is the decentralized lottery. This scheme allows multiple users to gamble bitcoins so that one of them — randomly — wins.

The protocol is composed out of two parts:

  1. Open — all users create strings of random data and publish hashes of these strings. Then they create transactions that forces them to reveal the data in the second phase or pay a fine to the rest of the players.
  2. Gamble — users create a gamble transaction that contains all those hashes and sign it. Then they reveal their secret strings and the lottery winner claims the coins.

The example below uses three players that randomly choose a number (0, 1 or 2). If sum of their numbers modulo 3 is 0 the first player wins, if it is 1 the second player wins and if it is 2 the third player wins.

To make the lottery secure against brute force attacks players use long strings of random data instead of their random numbers directly. If the drawn number is 0 the player uses a string of length 32, if it is 1 — 33 and if it is 2 — 34.

Open phase

7ae576...in #0out #0be7749...in #0out #0b78a94...in #0out #0

Users create and broadcast transaction that requires either one signature and a secret string or two signatures to unlock.

  • Owner’s signature and secret string — forces user to reveal the secret string.
  • Two signatures — used when the owner does not reveal the secret string. This transaction is given to other users and is time-locked so that the owner has enough time to reveal the secret.
assume signatureA, signatureB, secret

let secretKnown = sha256(secret) == '527ccdd755dcccf03192383624e0a7d0263815ce2ecf1f69cb0423ab7e6f0f3e'

let pubKeyA = '04d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc691'
let pubKeyB = '04c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40d'

let signedByA = checkSig(signatureA, pubKeyA)
let signedByB = checkSig(signatureB, pubKeyB)

verify (secretKnown || signedByB) && signedByA

Compiling to Bitcoin Script results in opcodes:

Open phase — Executing reveal script

The input script (three opcodes) from the spending transaction is concatenated with the output script from the first one:

Stack contents:

// input script
30440220749de3ac2a014374ca7952ebbbd5e094ed45c90f1d069d392c2c0589e3812e330220261f1d1184a4d1b4b4424d710a19dcff492404e1831a6ef8e251000b7f8de95d01
30450220363ee61c89c8369df409c186f099c60e128b758e64364100011c39df91141c3b022100ab26b4a7c2bbd3c14ef30d71d063c8a0db61a20e4dd856fbffe60ca26ea0f15e01
ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568123

// output script
OP_SHA256
527ccdd755dcccf03192383624e0a7d0263815ce2ecf1f69cb0423ab7e6f0f3e
OP_EQUAL
OP_SWAP
04d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc691
OP_CHECKSIG
OP_BOOLOR
OP_SWAP
04c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40d
OP_CHECKSIG
OP_BOOLAND

Open phase — raw transactions

Raw bytes of the transaction on the left:

01000000012c67b1b6feba79d0bfa5449fa28df67561a7f24e510efacf6942d0507e3baf33010000008c493046022100a2d5a7577529f4ce88e0a6a109b56a825c9131f0f3f692335f717b95f9e04370022100e753f58b46f9668c75441df19063b4aed358e2125b505c26681987f0505fd492014104c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40dffffffff01a086010000000000ada820527ccdd755dcccf03192383624e0a7d0263815ce2ecf1f69cb0423ab7e6f0f3e877c4104d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc691ac9b7c4104c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40dac9a00000000

Transaction object:

// click Parse

Raw bytes of the reveal transaction on the right:

010000000131aee0997b9f607efb0a69cd50ad3e74e28686184f914aa55b5a10f20a76e57a00000000b24730440220749de3ac2a014374ca7952ebbbd5e094ed45c90f1d069d392c2c0589e3812e330220261f1d1184a4d1b4b4424d710a19dcff492404e1831a6ef8e251000b7f8de95d014830450220363ee61c89c8369df409c186f099c60e128b758e64364100011c39df91141c3b022100ab26b4a7c2bbd3c14ef30d71d063c8a0db61a20e4dd856fbffe60ca26ea0f15e0120ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568123ffffffff01905f0100000000001976a914ed8600720a03985c1816b24d6b1622d0ed58861a88ac00000000

Transaction object:

// click Parse

Raw bytes of the fine (pay deposit) transaction on the right:

010000000131aee0997b9f607efb0a69cd50ad3e74e28686184f914aa55b5a10f20a76e57a0000000095483045022100a27c9532f4eb90240f598aa4c3ad43bc604fb4464688dad8113a943212d6638f022035ff6f215a4a432590d987f9c8ea839678506904a97643e8e99371b991b9438001493046022100b9d333b096a2a19bf6aa142aa429e7feb4d400b7af82bed4f5eca3ea7b7e83ec02210082d5be5da0a523c9c6e86ecf48f16cfbe3b32def49c92ce376cc87aa746760860101000000000001c8af0000000000001976a91462a2486468040e8a1a1f91d8949ac4dc838a0ed288ac4e258552

Transaction object:

// click Parse

Gamble phase

In this phase one of the users creates a transaction that contains hashes of all secrets. The output can be unlocked only when all secrets have been revealed and only by the lottery winner.

540d81...in #0in #1in #2out #0aef4cf...in #0out #0

The output script checks the sizes of revealed secrets and their hashes. Then it computes one number from these sizes and uses it to select a public key that will be used to verify the spending transaction.

assume signature, sC, sB, sA

let sizeA = size(sA)

verify within(sizeA, 32, 35)
verify sha256(sA) == '197bf68fb520e8d3419dc1d4ac1eb89e7dfd7cfe561c19abf7611d7626d9f02c'

let sizeB = size(sB)

verify within(sizeB, 32, 35)
verify sha256(sB) == 'f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226'

let sizeC = size(sC)

verify within(sizeC, 32, 35)
verify sha256(sC) == '527ccdd755dcccf03192383624e0a7d0263815ce2ecf1f69cb0423ab7e6f0f3e'

// sum of random numbers modulo 3
let mutable num = sizeB + sizeC + sizeA - 96
if (num > 2) {
    num <- num - 3
}
if (num > 2) {
    num <- num - 3
}

let keyA = '04d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc691'

let keyB = '04208a50909284aede02ad107bb1f52175b025cdf0453537b686433bcade6d3e210b6c82bcbdf8676b2161687e232f5d9afdaa4ed7b3e3bf9608d41b40ebde6ed4'

let keyC = '04c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40d'

// select one of keys using num (if num == 0 -> keyA etc.)
let key = roll(keyA, keyB, keyC, num)
verify checkSigVerify(signature, key)

Compiling to Bitcoin Script results in opcodes:

Gamble phase — Executing winner’s script

The input script (four opcodes) from the spending transaction is concatenated with the output script from the first one:

Stack contents:

// input script
304402207d9184b4e4a1fe11bcb7743db85f1f82ff00ab8c54f58b44de2ae50d8b1c35f3022019d9f60cd927a317e258d7ee3b9ceba49cd2a6bf15c195e4fc5d5f5915b83b6201
ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568123
1b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f
2c2c01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c338e

// output script
OP_SIZE
OP_TUCK
20
23
OP_WITHIN
OP_VERIFY
OP_SHA256
197bf68fb520e8d3419dc1d4ac1eb89e7dfd7cfe561c19abf7611d7626d9f02c
OP_EQUALVERIFY
OP_SWAP
OP_SIZE
OP_TUCK
20
23
OP_WITHIN
OP_VERIFY
OP_SHA256
f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226
OP_EQUALVERIFY
OP_ROT
OP_SIZE
OP_TUCK
20
23
OP_WITHIN
OP_VERIFY
OP_SHA256
527ccdd755dcccf03192383624e0a7d0263815ce2ecf1f69cb0423ab7e6f0f3e
OP_EQUALVERIFY
OP_ADD
OP_ADD
60
OP_SUB
OP_DUP
OP_2
OP_GREATERTHAN
OP_IF
OP_3
OP_SUB
OP_ENDIF
OP_DUP
OP_2
OP_GREATERTHAN
OP_IF
OP_3
OP_SUB
OP_ENDIF
04d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc691
04208a50909284aede02ad107bb1f52175b025cdf0453537b686433bcade6d3e210b6c82bcbdf8676b2161687e232f5d9afdaa4ed7b3e3bf9608d41b40ebde6ed4
04c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40d
OP_3
OP_ROLL
OP_ROLL
OP_3
OP_ROLL
OP_SWAP
OP_CHECKSIGVERIFY

Gamble phase — raw transactions

Raw gamble transaction on the left:

010000000397341cb62ff241b4a86063751885a072f4a6abe08e0fbe4548dc4c19fb5f1b190100000089463043021f174f4f000f1d4f4b62dde3659b2ae4563c894433319c3e4371b3a2cc0751cf022054890080a18f858a37254ad3ea913213377b459e58c467aaf1432d06a047800c014104d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc691ffffffffa5e8e5f4701195ce07d6319e2eea5bdf422019d1672eeb11f7c97a92821259ed010000008c493046022100f69891463fc2d1a714fbb8761410a0ce2df3f5dc5d24ed232ef1e14fb3476f08022100cada2c329383e11e9c6358d0215643f3ce9dbb267ec263d74569a4dc1df4ae42014104208a50909284aede02ad107bb1f52175b025cdf0453537b686433bcade6d3e210b6c82bcbdf8676b2161687e232f5d9afdaa4ed7b3e3bf9608d41b40ebde6ed4ffffffffd60c2dbd9907a8acb28f83ff0ebc6731c6f9d0917d9bd87f0d5cae8fcb273d72000000008b48304502200c3623da2de1c02910293a70428862d73c32ca8cffbe3e421b54348e8584317d022100b9b19f3de332c9464d99f23e88b0a5ce9e643e23254bbb197edd7fefc6992b56014104c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40dffffffff013057050000000000fd6301827d01200123a569a820197bf68fb520e8d3419dc1d4ac1eb89e7dfd7cfe561c19abf7611d7626d9f02c887c827d01200123a569a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226887b827d01200123a569a820527ccdd755dcccf03192383624e0a7d0263815ce2ecf1f69cb0423ab7e6f0f3e8893930160947652a0635394687652a0635394684104d4bf4642f56fc7af0d2382e2cac34fa16ed3321633f91d06128f0e5c0d17479778cc1f2cc7e4a0c6f1e72d905532e8e127a031bb9794b3ef9b68b657f51cc6914104208a50909284aede02ad107bb1f52175b025cdf0453537b686433bcade6d3e210b6c82bcbdf8676b2161687e232f5d9afdaa4ed7b3e3bf9608d41b40ebde6ed44104c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40d537a7a537a7cad00000000

Transaction object:

// click Parse

Raw spending transaction of the winner:

0100000001a48a2cf33ecd5134784418646820bd69d6c1ceff36dd5497200073d56b810d5400000000ab47304402207d9184b4e4a1fe11bcb7743db85f1f82ff00ab8c54f58b44de2ae50d8b1c35f3022019d9f60cd927a317e258d7ee3b9ceba49cd2a6bf15c195e4fc5d5f5915b83b620120ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568123201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f202c2c01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c338effffffff012030050000000000434104c9ce67ff2df2cd6be5f58345b4e311c5f10aab49d3cf3f73e8dcac1f9cd0de966e924be091e7bc854aef0d0baafa80fe5f2d6af56b1788e1e8ec8d241b41c40dac00000000

Transaction object:

// click Parse

Transactions containing non-standard scripts like these will not be relied through the Bitcoin network but if they are included in a block they will be accepted by the standard client.

Comments

John Daniels (@semanticist)
Really good explanation of how BitCoin transactions actually work, at a technical level, not a hand-waving level.
Wilson Tayar (@wilsontayar)
Awesome idea for an anonymous kickstarter.
Oleg Andreev (@oleganza)
Nice article on how Bitcoin contracts work.
Cody Stebbins ‏(@CodyStebbins)
Bitcoin is not just about decentralized currency, but contracts as well. This blog gives a great overview.
cancel

Revisions

  1. Initial version.