Nexa: Instant Transactions and Transaction Finality

What if a customer purchases something small, say a cup of coffee, with Nexa?

Can the merchant let the customer leave the store with the coffee before the transaction has been confirmed (commonly called 0-conf)?

This question is all about transaction finality.

“Transaction finality” refers to when the transaction is irreversibly committed to the blockchain or another database, such as what is used by your bank.

First, a warning: Transaction finality has actually been proven to be unsolvable in anonymous, decentralized permissionless systems, via a few famous (in computer science anyway) papers. Search for “the FLP impossibility result”, and “the CAP theorem” for more information.

So, what about all those blockchains that claim rapid finality?

Probably due to incompetence, intentional lying, not actually being a decentralized, anonymous, permissionless blockchain, or a combination of all three.

So why do we claim that a merchant can allow a customer to make small purchases with 0-conf transactions? Our claim is based on 2 technologies which detect and punish double spends rather than solving transaction finality.

Doublespend Proofs

The first is called “doublespend proofs”, which was first conceived by Tom Zander and then funded by us (Bitcoin Unlimited) to convene a small conference and pay for the software development.

A “doublespend” is when a customer uses the same money to buy something from 2 different merchants. Or, the customer sends the same money to both a merchant and then back to themselves. If this is confusing, think of it as writing multiple $10 checks when you only have $10 left in your bank account, or writing 1 check and then dashing to the bank and withdrawing.

In this case, only one of the two transactions will be added to the blockchain (only 1 check will be honored), so in the first case one merchant is not paid, and in the second case, the merchant may not be paid depending on which transaction is chosen (which is theoretically random, but tends to be the first transaction “seen” by network participants).

With “doublespend proofs”, all the honest nodes in the network are watching for a double spend. Doublespends are easy to identify, because they spend the same UTXO (blockchain ledger) entries. However, in traditional Bitcoin, transactions that are doublespends are dropped (ignored). The reason they are dropped is so that miners won’t be given both transactions and get to choose the one with the biggest fee (which of course is invariably the cheating one). However, this means that a merchant may not ever see the double spend since it does not propagate through the network.

In Nexa, if a doublespend is detected, a proof of that doublespend is extracted out of both transactions. This proof does NOT contain either of the doublespend transactions so announcing the proof does not spread the “bad” transaction around. I don’t want to go into the technical details about this, but basically, the proof says “look, this person just signed 2 messages sending this money to 2 different places! I’m not going to tell you what those messages are but I’m going to give you enough info to prove that I’m not lying”.

This doublespend proof is a high priority message that is broadcast to all full nodes and interested light clients. So the coffee selling merchant will get a notification in seconds if a doublespend is attempted.

Presumably the point-of-sale device then pops up a big red warning box (there’s almost no “accidental” way a doublespend can happen), and so the merchant then just politely asks the customer to step aside while the transaction confirms. With Nexa’s 2 minute average block time, this is inconvenient but not in the same category as making someone wait for 10 minutes!

Ok, this works fine UNLESS the doublespend is hidden. But if it’s hidden, how can it be mined? It can’t unless the doublespender is ALSO a miner. If the doublespender is a miner, they can hide the doublespend and it will succeed at whatever fraction of the hash power that miner has.

So the argument goes “ok if you have a million dollar crypto mine, maybe you could reliably doublespend coffee…”

But there are very few people who could do that, AND you are tangling your entire million dollar crypto mine into criminal activity. This is a very important concept. If the CEO of that crypto mine steals coffee just by running out of the store without paying, the fact that they own a crypto mine is irrelevant. But if the crypto mine itself is used to facilitate criminal activity, the entire operation typically is forfeit (of course, this depends on your jurisdiction and the details of the case, but this is why you see the government auctioning off drug dealers’ sports cars).

So what major miner is going to risk their entire operation for free coffee?

But this is why anyone accepting (say) 100,000 USD in crypto should wait for several confirmations. Maybe a miner would risk the mine for that kind of money. Doublespend proofs are currently deployed in the Nexa network.

Doublespend Forfeits

The second technology is something called double-spend forfeits:

Solving the 0-conf problem using forfeits

by /u/awemany

Overview

The problem of ensuring good security for unconfirmed transactions in Bitcoin is repeatedly discussed in the community. Right now these so-called 0-conf transactions are usually expected to be of low risk in the case of small amounts and face-to-face interaction of merchant and customer.

These face-to-face interactions usually mean that there exist extra-blockchain means of enforcing good manners and discouraging theft (merchant and customer knowing each other, justice system, …). However, there is no reason to refrain from trying to make this better through technical means.

In principle, transactions that are not yet included in blocks can be replaced by a loose duo of a scammer and a miner willing to help the scammer out, which creates a certain risk for a merchant to accept an unconfirmed Bitcoin transaction.

Apart from preconsensus approaches discussed elsewhere, which will cut down the time window in which a scammer might be successfully double-spending, there is also the option to use payment channels to have secure and fast transactions.

However, this necessitates set up of a payment channel before the transaction takes place, which creates a burden for the customer as well as the merchant. This is the approach taken by the Core variant (BTC) of Bitcoin with their Lightning Network implementation.

A third alternative, proposed here rather for the Cash variant (BCH) of Bitcoin (henceforth simply called Bitcoin) is to use the usual zero-confirmation payment approach, but add a special feature to the transaction that will act as a forfeit, should the scammer attempt a double spend of the money that he sent to the merchant.

In about two months from now, the OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY opcodes are planned to activate for Bitcoin. Using these opcodes, it is possible to implement such a forfeit in an automated way. This scheme is hereby named Zero Confirmation Forfeits, or ZCF for short.

Prior art

There is an old idea by Peter Todd for a merchant to go forward with a scorched earth policy in case the customer turns out to be a scammer and double-spends.

The idea is described (and criticized) in an older post here on Medium by Mike Hearn here: Replace by fee. A counter argument | by Mike Hearn | Mike’s blog.

Then, there is a paper on a scheme that is overall quite close to the one described here: https://eprint.iacr.org/2017/394.pdf

In this paper, the authors describe a way to use specially prepared outputs with specially prepared signatures so that double-spending a transaction will reveal the private key.

In contrast, the scheme that is discussed herein, using the to-be-activated CHECKDATASIG/-VERIFY opcodes can also be implemented in a backwards-compatible manner and does not need the creation of any special outputs before paying a merchant, unlike the scheme by Solà et al.

All that is needed is upgraded software and customers and merchants can use this scheme right away, without further preparation. This seems highly desirable from a user interface perspective.

Current status of the proposed scheme

This post is merely exploring a proof-of-concept of this scheme. Much work is necessary to make this a real world feature of Bitcoin!

Brief overview of the new opcodes

OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY, which are tweaked variants of Andrew Stone’s OP_DATASIGVERIFY opcode, allow to check the validity of a Bitcoin ECDSA signature in the Bitcoin Script Forth-like predicate language. OP_CHECKDATASIG will return true (1) on the stack when the triple

[signature] [message] [public key]

turns out to be validly signed data resp. false (0) for an mismatching signature.

The [message] part is hashed once with SHA256 before it is used for checking the signature, a fact which will become important later in this document. Similarly, OP_CHECKDATASIGVERIFY works like OP_CHECKDATASIG, but will not leave a result on the stack but rather abort the script as failing, should the signature turn out not to be valid. A very similar (or even functionally identical) opcode reportedly exists in Blockstream’s Elements project as well.

Creating a Zero Conf Forfeit transaction

The key insight for this scheme is that double-spending a transaction means double-spending one of its inputs. And double-spending one of its inputs means creating two distinct signatures that are valid for the same public key.

A merchant who requires a zero-confirmation transaction with forfeit (a ZCF transaction) will therefore require from the customer a transaction that has the following structure:

  • Inputs: [P2PKH inputs 1] ... [P2PKH input I]
  • Outputs: [any-type-output 1] ... [any-type-output O] [Forfeit Output]

With the [Forfeit Output] being the key requirement, though it is also important that all of the inputs are of the P2PKH type and also from distinct addresses (otherwise, the customer will lose his forfeit by default). Only then should a transaction deemed to be of the Zero Conf Forfeit (ZCF) type.

The forfeit output is a P2SH output that pays to a specially prepared forfeit script. This forfeit script will allow to spend the output using either a scriptSig that contains just

[signature] [public key] [P2SH script]

or by supplying two distinct messages and signatures for the same public key (the same for the two messages, not the same as above), like this:

[signature1] [message1] [signature2] [message2] [public key] [P2SH script]

The first one is the regular spending case. In this case, the well-behaved customer simply spends his forfeit output like any other P2PKH output and everyone is happy.

In the second case, a miner has seen an attempted double-spend by the customer and uses the signed data from the double-spending as well as the regular (paying to the merchant) transaction to spend the forfeit output to himself. Important to note here is that CHECKDATASIG/-VERIFY allows to check the signatures of Bitcoin transactions.

An example of creating a ZCF-enabled transaction

Assume that 08454094ee44d4fcb7f92c90d57a10d57ad4dc4d7ccafe08a582fff42b0e08ba:0 is an output that has received 9999.9 testBCH to address bchreg:qrcj0e448csqtyc8rp5p6fzluk3tytetnu6eyusazv, like this:

$ ./bitcoin-cli -regtest decoderawtransaction 0200000001c093ebf9c7a22bafbc831b1dcbc075657c82636459c9cd1339926fe6a51959d8000000006b483045022100c7b546f4c2c3bf57f756fc175416a496abda199839ab6588b3eae8bff3f6d0d8022048ba06d2258e4997619ac7644347fe0e1faa78ea8500783e8f37a1633fc4721a412103cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1feffffff0280790cd4e80000001976a914f127e6b53e2005930718681d245fe5a2b22f2b9f88acd8849800000000001976a914f288e5578c011f98eb4c759e0b0ea9169c1d335e88ac93010000
{
  "txid": "08454094ee44d4fcb7f92c90d57a10d57ad4dc4d7ccafe08a582fff42b0e08ba",
  "hash": "08454094ee44d4fcb7f92c90d57a10d57ad4dc4d7ccafe08a582fff42b0e08ba",
  "size": 226,
  "version": 2,
  "locktime": 403,
  "vin": [
    {
      "txid": "d85919a5e66f923913cdc9596463827c6575c0cb1d1b83bcaf2ba2c7f9eb93c0",
      "vout": 0,
      "scriptSig": {
        "asm": "3045022100c7b546f4c2c3bf57f756fc175416a496abda199839ab6588b3eae8bff3f6d0d8022048ba06d2258e4997619ac7644347fe0e1faa78ea8500783e8f37a1633fc4721a[ALL|FORKID] 03cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1",
        "hex": "483045022100c7b546f4c2c3bf57f756fc175416a496abda199839ab6588b3eae8bff3f6d0d8022048ba06d2258e4997619ac7644347fe0e1faa78ea8500783e8f37a1633fc4721a412103cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1"
      },
      "sequence": 4294967294
    }
  ],
  "vout": [
    {
      "value": 9999.90000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 f127e6b53e2005930718681d245fe5a2b22f2b9f OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914f127e6b53e2005930718681d245fe5a2b22f2b9f88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "bchreg:qrcj0e448csqtyc8rp5p6fzluk3tytetnu6eyusazv"
        ]
      }
    },
    {
      "value": 0.09995480,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 f288e5578c011f98eb4c759e0b0ea9169c1d335e OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914f288e5578c011f98eb4c759e0b0ea9169c1d335e88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "bchreg:qreg3e2h3sq3lx8tf36euzcw4ytfc8fntc3a3jpsej"
        ]
      }
    }
  ]
}

The customer now creates the following ZCF-enabled transaction to pay the merchant 4400.0 testBCH at address bchreg:qp7a5v7t3ejacvznnpf8n6w5k3z9jtjanug7u447fj and put up a forfeit of 5599.8 testBCH, that he can spend forward (should he not double-spend) from his address bchreg:qz938xjjwnxgtcknd48e0y32zkh96lmg4utd5jc66s:

$ ./bitcoin-cli -regtest decoderawtransaction 0100000001ba080e2bf4ff82a508feca7c4ddcd47ad5107ad5902cf9b7fcd444ee94404508000000006a4730440220665817cdf25beb8505fee4ecc8239e63d973183f5741d9bee68f883df1c8c04402204e5a08daee4a65c68eaf90e7f60ed0b436de16f49b5aca48724c70d9f32a41cb412103cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1000000000200300b72660000001976a9147dda33cb8e65dc3053985279e9d4b444592e5d9f88ac00b368618200000017a914fcc909008e148ac6dc5258d8884fb167da4904c48700000000
{
  "txid": "b25d2fff5b0e3f106b6fa31d7ed6a7a1da72f923128316352397aae0da1eec79",
  "hash": "b25d2fff5b0e3f106b6fa31d7ed6a7a1da72f923128316352397aae0da1eec79",
  "size": 223,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "08454094ee44d4fcb7f92c90d57a10d57ad4dc4d7ccafe08a582fff42b0e08ba",
      "vout": 0,
      "scriptSig": {
        "asm": "30440220665817cdf25beb8505fee4ecc8239e63d973183f5741d9bee68f883df1c8c04402204e5a08daee4a65c68eaf90e7f60ed0b436de16f49b5aca48724c70d9f32a41cb[ALL|FORKID] 03cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1",
        "hex": "4730440220665817cdf25beb8505fee4ecc8239e63d973183f5741d9bee68f883df1c8c04402204e5a08daee4a65c68eaf90e7f60ed0b436de16f49b5aca48724c70d9f32a41cb412103cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1"
      },
      "sequence": 0
    }
  ],
  "vout": [
    {
      "value": 4400.00000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 7dda33cb8e65dc3053985279e9d4b444592e5d9f OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9147dda33cb8e65dc3053985279e9d4b444592e5d9f88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "bchreg:qp7a5v7t3ejacvznnpf8n6w5k3z9jtjanug7u447fj"
        ]
      }
    },
    {
      "value": 5599.80000000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_HASH160 fcc909008e148ac6dc5258d8884fb167da4904c4 OP_EQUAL",
        "hex": "a914fcc909008e148ac6dc5258d8884fb167da4904c487",
        "reqSigs": 1,
        "type": "scripthash",
        "addresses": [
          "bchreg:pr7vjzgq3c2g43ku2fvd3zz0k9na5jgycslg3ae66v"
        ]
      }
    }
  ]
}

The first output is the regular payment to the merchant. The second one is the P2SH forfeit output. The script that creates the above P2SH output is:

76a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868,

which decodes to:

OP_DUP OP_HASH160 8b139a5274cc85e2d36d4f97922a15ae5d7f68af OP_EQUAL
OP_IF
    OP_CHECKSIG
OP_ELSE
    OP_DUP OP_HASH160 f127e6b53e2005930718681d245fe5a2b22f2b9f OP_EQUAL
    OP_IF
        OP_OVER 4 OP_PICK OP_EQUAL OP_NOT OP_VERIFY
        OP_DUP OP_TOALTSTACK OP_CHECKDATASIGVERIFY
        OP_FROMALTSTACK OP_CHECKDATASIG
    OP_ELSE
        OP_RETURN
    OP_ENDIF
OP_ENDIF

The above idea is implemented here with a couple of nested OP_IF and OP_ELSE statements. Let’s go into it in detail:

OP_DUP OP_HASH160 8b139a5274cc85e2d36d4f97922a15ae5d7f68af OP_EQUAL
OP_IF
    OP_CHECKSIG

This part looks superficially similar to the scriptPubKey of a P2PKH output and indeed it is. Given [sig] [public key] on the stack, this will hash the public key, compare it to the given hash / address, check for equality, and if it is equal, do a regular CHECKSIG operation. Note that the 1 that is put on the stack by the OP_EQUAL will be consumed by the OP_IF. A successful OP_CHECKSIG will then lead to a succesful script execution with a 1 on the stack from this signature checking. This is the code path that allows the customer to regularly spend his forfeit output forward, in case he decides to not double-spend any of the inputs. Next, the else path that starts with this:

OP_DUP OP_HASH160 f127e6b53e2005930718681d245fe5a2b22f2b9f OP_EQUAL
    OP_IF

Again, this looks superficially similar to a standard output. Here, the public key of a stack that contains [signature1] [message1] [signature2] [message2] [public-key] is checked not for being a valid key for spending the output, but is rather checked to be one of the input public keys. This is to test for double spending any input. Again the OP_EQUAL and OP_IF combination will leave the stack as it was upon entry into the script. As noted above, the signatures and messages are expected to be extracts of Bitcoin transactions, more specifically the transaction to the merchant with the forfeit output itself, plus the attempted double-spend by the customer turned scammer. So, to continue in this code path:

OP_OVER 4 OP_PICK OP_EQUAL OP_NOT OP_VERIFY

This checks for the messages being not equal and will fail the script otherwise. First, the message2 is picked and put onto the stack once more with OP_OVER, and then 4 OP_PICK picks the message1. They are compared for inequality then with OP_EQUAL OP_NOT OP_VERIFY, with terminal failure if they are equal. Then,

        OP_DUP OP_TOALTSTACK OP_CHECKDATASIGVERIFY
        OP_FROMALTSTACK OP_CHECKDATASIG

Checks the two message, pubkey, signature combinations using CHECKDATASIG resp. CHECKDATASIGVERIFY. The pubkey is needed for both checks and is saved for convenience onto the altstack with OP_DUP OP_TOALTSTACK. The triple (signature2, message2, public key) is first checked with OP_CHECKDATASIGVERIFY and then the triple (signature1, message1, public key) is checked with OP_FROMALTSTACK OP_CHECKDATASIG, leaving the final 1 for success on the stack.

What follows is

OP_ELSE
        OP_RETURN
    OP_ENDIF
OP_ENDIF

which is the else path ending in an invalidating OP_RETURN in case other data is supplied to the script. In the following, an example of each valid code path and spending situation is now presented.

Example of regular spend of the forfeit output

In the first case of a regular spend, the supplied pubkey is hashed to yield address 8b139.. (qz938xjj..) and a regular OP_CHECKSIG is executed. Such a regular spending transaction could for example be the following, which just spends the above output forward to the same address:

$ ./bitcoin-cli -regtest decoderawtransaction 010000000179ec1edae0aa97233516831223f972daa1a7d67e1da36f6b103f0e5bff2f5db201000000af4830450221009800712a9042fc2bcf7cce2be6d907f064ddf61b55183d4988d9cd1128fb422a0220070cf7db4e1cea03e3cf0d902bfff44071f93e7a1446b8724fb7845c1d1d6e0641210320db2c52e5d23f2730770ca136c717359c73c1d1b9a4bf288bf3606bf116353f4376a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868000000000118af6861820000001976a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af88ac00000000
{
  "txid": "7f6c4b6e21d80b6e0ce36cefe85c010603332d9d981da7231a667fed2b9053f7",
  "hash": "7f6c4b6e21d80b6e0ce36cefe85c010603332d9d981da7231a667fed2b9053f7",
  "size": 260,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "b25d2fff5b0e3f106b6fa31d7ed6a7a1da72f923128316352397aae0da1eec79",
      "vout": 1,
      "scriptSig": {
        "asm": "30450221009800712a9042fc2bcf7cce2be6d907f064ddf61b55183d4988d9cd1128fb422a0220070cf7db4e1cea03e3cf0d902bfff44071f93e7a1446b8724fb7845c1d1d6e06[ALL|FORKID] 0320db2c52e5d23f2730770ca136c717359c73c1d1b9a4bf288bf3606bf116353f 76a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868",
        "hex": "4830450221009800712a9042fc2bcf7cce2be6d907f064ddf61b55183d4988d9cd1128fb422a0220070cf7db4e1cea03e3cf0d902bfff44071f93e7a1446b8724fb7845c1d1d6e0641210320db2c52e5d23f2730770ca136c717359c73c1d1b9a4bf288bf3606bf116353f4376a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868"
      },
      "sequence": 0
    }
  ],
  "vout": [
    {
      "value": 5599.79999000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 8b139a5274cc85e2d36d4f97922a15ae5d7f68af OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "bchreg:qz938xjjwnxgtcknd48e0y32zkh96lmg4utd5jc66s"
        ]
      }
    }
  ]
}}

Note the scriptSig that pushes a signature and pubkey just like for a regular P2PKH output, plus the P2SH script itself, as needed for P2SH outputs.

Example of forfeit spend to the miner in case of an input double spend

Now, if the customer is a scammer and double-spent the transaction, two different signature and message combinations will exist for the public pair. Spending the forfeit might then look like this:

$ ./bitcoin-cli -regtest decoderawtransaction 010000000179ec1edae0aa97233516831223f972daa1a7d67e1da36f6b103f0e5bff2f5db201000000fd3701473045022100cf7e027d3423845c8db9ea81654c739bb009454728cee307cda2963f672c7cc2022012a2ed81a9544ce8eaf47cfbb466a6267684127fd06cc423c4e2a3c702173d212093f66ecca97e91ab69cf8e62838e92992d0a25a4432d6e75da9ba7b90a82d6bf4630440220665817cdf25beb8505fee4ecc8239e63d973183f5741d9bee68f883df1c8c04402204e5a08daee4a65c68eaf90e7f60ed0b436de16f49b5aca48724c70d9f32a41cb20a51b7e2846c3dbbe54fd830461db15f3d18f63d1f596baafb2a8c8b4ef90e6582103cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c14376a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868000000000100b36861820000001976a914b0db3d9021a1066ed5be4c2954162a6f4e757c8c88ac00000000
{
  "txid": "4d351c6b0eb615bdd8b826aa2af4bf9f3f1b8d2f7aa95cd2f4c0c19ecd9cec76",
  "hash": "4d351c6b0eb615bdd8b826aa2af4bf9f3f1b8d2f7aa95cd2f4c0c19ecd9cec76",
  "size": 398,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "b25d2fff5b0e3f106b6fa31d7ed6a7a1da72f923128316352397aae0da1eec79",
      "vout": 1,
      "scriptSig": {
        "asm": "3045022100cf7e027d3423845c8db9ea81654c739bb009454728cee307cda2963f672c7cc2022012a2ed81a9544ce8eaf47cfbb466a6267684127fd06cc423c4e2a3c702173d21 93f66ecca97e91ab69cf8e62838e92992d0a25a4432d6e75da9ba7b90a82d6bf 30440220665817cdf25beb8505fee4ecc8239e63d973183f5741d9bee68f883df1c8c04402204e5a08daee4a65c68eaf90e7f60ed0b436de16f49b5aca48724c70d9f32a41cb a51b7e2846c3dbbe54fd830461db15f3d18f63d1f596baafb2a8c8b4ef90e658 03cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c1 76a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868",
        "hex": "473045022100cf7e027d3423845c8db9ea81654c739bb009454728cee307cda2963f672c7cc2022012a2ed81a9544ce8eaf47cfbb466a6267684127fd06cc423c4e2a3c702173d212093f66ecca97e91ab69cf8e62838e92992d0a25a4432d6e75da9ba7b90a82d6bf4630440220665817cdf25beb8505fee4ecc8239e63d973183f5741d9bee68f883df1c8c04402204e5a08daee4a65c68eaf90e7f60ed0b436de16f49b5aca48724c70d9f32a41cb20a51b7e2846c3dbbe54fd830461db15f3d18f63d1f596baafb2a8c8b4ef90e6582103cdb375ff72e4e42898d80193c4026a91d8acea44a42ddf1c30b72d759e7d40c14376a9148b139a5274cc85e2d36d4f97922a15ae5d7f68af8763ac6776a914f127e6b53e2005930718681d245fe5a2b22f2b9f8763785479879169766bbb6cba676a6868"
      },
      "sequence": 0
    }
  ],
  "vout": [
    {
      "value": 5599.80000000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 b0db3d9021a1066ed5be4c2954162a6f4e757c8c OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914b0db3d9021a1066ed5be4c2954162a6f4e757c8c88ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "bchreg:qzcdk0vsyxssvmk4hexzj4qk9fh5uatu3s29fz5p5t"
        ]
      }
    }
  ]
}

The scriptSig here is a lot more verbose, containing the two signatures, the two messages (hashes) as well as the public key (that hashes to f127...) and finally the P2SH script.

In this example, the miner would spend the forfeit to a miner-owned address. This could be simplified, of course, to be a transaction without any outputs, therefore spending everything as fees to himself. For testing this, it was a lot simpler to make this a regular-looking transaction, however.

Any other input should end up in the OP_RETURN and thus fail to spend the output.

To be noted here is that CHECKDATASIG will hash the input given to it once. Very luckily, any transaction input of the P2PKH type is hashed twice before its signature is checked in bitcoind (for the required SIGHASH_FORKID signature type). This allows to supply the single hashing intermediate result from the SignatureHash(..) function in bitcoind to this script to enable the forfeit logic.

In case of more inputs, the above script would be adapted with more input public key hashes in an OP_OR clause or similar.

Status, outlook and proposed changes

The whole “implementation” right now consists of a bunch of very messy, still unpublished ad-hoc scripts based on the primal100 variant of Vitalik’ Buterins pybitcointools(GitHub - primal100/pybitcointools: Simple, common-sense Bitcoin-themed Python ECC library) plus an abuse of the functional test suite in the Bitcoin Unlimited software stack. Furthermore, as Bitcoin Unlimited has not yet been updated to support the CHECKDATASIG/-VERIFY opcodes, the above example has been created and tested using the BitcoinABC implementation.

For reference, to allow playing with CHECKDATASIG, it should be started like this:

./bitcoind -regtest -debug -magneticanomalyactivationtime=0 -relaypriority=0

In short, all this needs to be cleaned up, as well as documented, as well as implemented in consumer wallets such as Copay to be of real use. Furthermore, mining software likely needs to be adapted to allow an exemption for double-spending the discussed forfeit outputs described here in. A standard needs to be written - and so forth …

If this is deemed to be an acceptable way to deal with the 0-conf problem, it might make sense to change the mining code in leading implementations to allow spending forfeits easy as a discouragement of scammers.

It should finally be noted that the nature of this change is one that “in the expected case, it will never be used”. Just having this feature around might be enough deterrent for scammers to not try scamming - and using ZCF transactions should deter potential scammers from trying.

However, it is also imaginable that this feature will - if implemented ecosystem-wide - rarely be used, as 0-conf is currently quite usable for low risk anyways. This might end up with a minor Volterra-Lotka-like dynamic with a low amount of scams happening and merchants reacting by requiring forfeits.

I propose that wallets and point-of-sale system display a transaction protected with a sufficient (TBD) forfeit with a green ‘F’ in their user interfaces.

Conclusions

The above presents a scheme to create forfeit outputs that would discourage people from double-spending unconfirmed payments to merchants. It includes an example set of regtest transactions to demonstrate the general idea.

The author is happy for any input on this, including bug reports, pointers to related work and similar. Finally, I like to thank Andrew Stone and one anonymous reviewer for having a detailed look at the draft of this post.

I also further like to thank Andrew Stone and imaginary_username, because AFAIR I developed this idea when throwing ideas back and forth in a discussion with them on the Bitcoin Unlimited slack.

(Final editing note: I planned to publish this on Medium, but it screwed up the formatting of all the code. So I am leaving it here on github then.)

Addendum

From the discussion on reddit: One worry that was mentioned by user TorusJKL and others is that there might be collusion between miner and scammer. In this case, one way to address this is to increase the forfeit, assuming the double spend is sent into the network. And if reaches only a particular miner, adding an extra time lock on the forfeit until it can be spend forward (but only in the regular spending case) would seem to further discourage this kind of collusion.

view raw ZCF.md hosted with :heart: by GitHub

Doublespend forfeits work like this: you put $20 in an envelope and say “hey, if someone catches me trying to cheat anyone else, they can take the money.”

Except that this is done in a provable, decentralized, permissionless, trustless fashion. In the case of doublespend forfeits, it’s the miners who can take the money if you cheat.

But even if you are a miner (and could therefore “win” your own forfeit), it’s a lot harder to cheat. To understand this, suppose you have 25% of the hash power. So you will get your $5 coffee (and capture your own $20 penalty, so $5 gain) 1/4 of the time. But 75% of the time you lose $20 (I’m not counting paying for the $5 coffee as a loss because presumably you wanted the coffee).

So if you do this many times, your averaged win is $1.25 and loss is $15, resulting in a $13.75 average loss per attempt.

Of course, the honest customer uses the same $20 for every payment every day so it’s kind of like just keeping an extra emergency $20 in your wallet, because you can always remove that $20 from its role in the doublespend forfeit protocol and spend it.

Note that the merchant who was cheated typically would not receive the forfeit (unless they happen to also be a miner and mine the next block). So the system isn’t perfect, but it would dramatically discourage cheating.

So depending on the amount of money put up as a forfeit, a merchant can reliably accept larger payments as zero-conf.

Doublespend forfeits are enabled on the Nexa blockchain by the CHECKDATASIG opcode that I designed. So they are possible in the Nexa and Bitcoin Cash blockchains. However, a wallet-level protocol is required to actually implement them, and as far as I know, no wallet does so yet.

Written by: Andrew Stone

7 Likes

RealG Andrew Stone, great write up :writing_hand:

4 Likes