Solved : The Singleton Token Model

June 6, 2022
Arthur C. Clarke - The Sands of Mars

UPDATE: - Article updated to account for nSequence flaw. See section on Security Concerns.

TLDR; The previously discussed implementation of the Singleton Token Model had a flaw which made it impractical for day-to-day use. I have modified the token model construction to retain it’s key properties, while making it practical for everyday use. The solution combines client-side ECDSA verification, SPV and the non-final transaction pool of a mining node into an efficient protocol for miner-validated tokens that can be authenticated client-side without an expensive retracing of the token history. We retain the properties of miner-validation, client-side authentication and non-permissioned, trustless P2P exchange.

Caution: This is a thought-experiment at the moment. I have not written any code to validate the method I detail here. Please read this with a critical eye, and provide feedback as necessary.

Making a Mini Ledger

By way of demonstrating one of the key aspects of the token protocol, we will now proceed to describe the construction of a “mini-ledger”. Our ledger will have the following properties:

  • It will be immutable. I.e. it behaves as an append-only log where new rows may be appended only.
  • It will be tamper-evident. It will not be possible to remove or alter one of the rows in the ledger without detection.

Step 1 - Linking rows in the ledger

Figure 1: Binding the rows with ECDSA

Figure 1: Binding the rows with ECDSA

We link rows in the ledger by signing over three-out-of-four fields and storing the signature in the fourth field. In Figure 1 we generate a signature over the highlighted portions of rows (1) and (2), and store the signature in the second column or row (2).

We continue to do this iteratively for each row. E.g. row (2)’s signature is generated by signing the fields in row (2) along with the field in the first column of row (3). We then store row (2)’s signature in the second column of row (3)

For each of the rows we can have a different signatory. That would mean e.g. that signatory #1 has their signature in row(2), column(3) etc.

By doing this, we are indelibly linking the rows together, and one can’t remove any of the rows without ruining the signatures.

Step 2 - Bitcoin Transaction Signatures

Figure 2: Signing Bitcoin Transaction Inputs

Figure 2: Signing Bitcoin Transaction Inputs

Recall now, that in order to spend a Bitcoin UTXO, we usually generate a signature for (input #1) in Figure 2 using a SIGHASH algorithm of our choosing.

Step 3 - The Complete Mini Ledger

Figure 3: The completed mini ledger

Figure 3: The completed mini ledger

Combining the concepts from (Step 1) and (Step 2), we get an immutable “mini ledger”. To firm up our mini-ledger scheme we will assume that each row in the ledger represents a transfer of ownership, and that the ledger can be appended to only. Removal or modification of any rows in the middle, or at the end will break the signature chain and invalidate the ledger.

We use an ECDSA signature scheme, and require that the signatures for the “mini ledger” be generated with the same key that signed the spending condition for the input in the preceding row.

Therefore, the signature in row 2, must be generated using the private key that is used to create the signature for in_1 (input #1), signature in row 3 is generated with private key used for in_2, etc.

NOTE that we can accomplish this ledger construction by using a Signature scheme of (SIGHASH_ANYONECANPAY | SIGHASH_SINGLE), which ensures that we only sign row-by-row for the UTXO spend, and allows for additional rows to be added to our ledger.

The Singleton Revisited

Figure 4: The Singleton Revisited

Figure 4: The Singleton Revisited

Our intention with our redesigned Singleton Token is now to :

  • Keep the ownership transaction(mini-ledger) perpetually off-chain (until redemption)
  • Use the non-final Transaction Pool as a means to validate the token’s spending conditions (inputs&outputs)
  • Maintain off-chain ledger integrity by using a series of linked ECDSA signatures.
  • Allow the transaction to expire from non-final Transaction Pool without affecting the token’s integrity or security.

Constructing Row #1 (Issuance)

  • The issuer creates an input (input #1), that spends from the issuance transaction.
  • The signature in input #1 is generated using (SIGHASH_SINGLE | ANYONECANPAY), so that the contents of output #1 (part of token identity) can’t be altered.

Constructing Row #2 (Transfer to Owner 1)

  • Owner 1 passes the issuer a signed Input Script (SIGHASH_NONE | ANYONECANPAY), which spends from an on-chain UTXO, with nSequence(input #2) set 10s (a small amount of time in the future)
  • The issuer places Owner 1’s input script in (input #2)
  • The issuer generates an ECDSA signature over hash( (input #1 || output #1 || input #2) ), using the same private key as was used for input #1
  • The issuer places the signature in output #2
  • The issuer passes the mini-ledger Tx to Owner 1
  • Owner 1 performs a client-side ECDSA verification over the signature-chain, verifying the integrity of the ledger.
  • Owner 1 adds a third, temporary (input #3) with nSequence active
  • Owner 1 broadcasts the non-final Mini Ledger to the non-final Transaction Pool to wait out the expiry of nSequence(input #2) (this step prevents having the issuer rug funds from Owner 1’s UTXO )

Constructing Row #3 (Transfer to Owner 2)

The transfer proceeds exactly as the first transfer has done.

  • Owner 1 removes their temporary input #3
  • Owner 2 passes Owner 1 a signed Input Script (SIGHASH_NONE | ANYONECANPAY), which spends from an on-chain UTXO, with nSequence(input #3) set 10s (a small amount of time in the future)
  • Owner 1 places Owner 2’s input script in (input #3)
  • Owner 1 generates an ECDSA signature over hash( (input #2 || output #2 || input #3) ), using the same private key as was used for input #2
  • Owner 1 places the signature in output #3
  • Owner 1 passes the mini-ledger Tx to Owner 2
  • Owner 2 performs a client-side ECDSA verification over the signature-chain, verifying the integrity of the ledger.
  • Owner 2 adds a third, temporary (input #4) with nSequence active
  • Owner 2 broadcasts the non-final Mini Ledger to the non-final Transaction Pool to wait out the expiry of nSequence(input #3) (this step prevents having Owner 1 rug funds from Owner 2’s UTXO )

Security Concerns

The token transfer process represents a potential scenario where loss-of-funds could occur. Above we propose a simple timelock scheme alongside miner-validation to secure funds. This could be enhanced further by layering in a 2/2 multisig into the exchange, but that’s beyond the scope of this post.

It is important to have a secure protocol that leverages the non-Final Transaction Pool to append a new entry to the mini-ledger. The non-Final Transaction Pool is used as intended; a temporary space to allow for miner-validation of a transaction’s inputs and outputs.

Notice that combining the two signature schemes allows:

  • the use of the non-Final transaction pool for miner-validation of the ownership transfer
  • the use of a chain of ECDSA signatures to secure the mini-ledger against tampering

IMPORTANT: There exists the potential for an earlier holder of the token to broadcast the transaction and force the token to be settled on-chain. This is because we cannot assert an nSequence lock with how we are chaining signatures. One way to fix this is for the Issuer to use an OP_PUSH_TX covenant that enforces an active nSequence. That way, the Issuer’s initial UTXO will never pass miner validation.

Alternately, the new recipient of a token can require that the previous ownership of a token must include at least one nSequence covenant enforced by a UTXO.

Conclusion

Our modified Singleton Token scheme combines several aspects of Bitcoin to arrive at a solution. With each token transfer our off-chain mini-ledger grows by a small amount. The size of growth depends on whether the token protocol calls for additional meta-data in the outputs. At a minimum, each new Input + Output should add around 200 bytes of data to the ledger (estimated).

The modest growth of the mini-ledger along with the computational expense of performing client-side ECDSA verification over a number of iterations must be kept in mind when considering if this protocol is a good fit for your project.