Signature Schemes

This is an extremely important subject. Care must be taken to clearly understand all the concepts presented here. When generating a Signature for a Transaction, within limitations, the developer get’s to choose which parts of the Transaction they would like to have covered by the Signature. This flexibility introduces some novel potential use cases for Bitcoin, but also exposes one to the risk of the potential for lost funds. More about the use cases later.

In Bitcoin we generally refer to a Signed Transaction. The Signature itself though, is embedded inside the Input data structure of the spending Transaction. Specifically it is embedded inside the ScriptSig field of the Input [of the spending Transaction] for which we are generating a Signature. If a spending Transaction contains more than one Input, then we generate signatures for each of the Inputs, one at a time.

The method by which the Signature is calculated is referred to as the Signature Hash Algorithm. Generally though, developers just refer to this as Sighash or the Sighash algorithm.

Let’s look at a brief code snippet that shows the use of the SIGHASH Flag:


  var unlockBuilder = P2PKHUnlockBuilder(privateKey.publicKey);
  var transaction = new Transaction()
      .spendFromOutput(utxo, Transaction.NLOCKTIME_MAX_VALUE, scriptBuilder: unlockBuilder) //implicitly creates correct Inputs in spending transaction
      .spendTo(recipientAddress, BigInt.from(50000000), scriptBuilder: P2PKHLockBuilder(recipientAddress)) //spend half a bitcoin == 50 million satoshis (creates Outputs in spending transaction)
      .sendChangeTo(changeAddress, scriptBuilder: P2PKHLockBuilder(changeAddress)) // spend change to myself
      .withFeePerKb(1000); //how much we are willing to pay the miners

  //Sign the Transaction Input (sighashType controls which Sighash algoritm is applied)
  transaction.signInput(0, privateKey, sighashType: SighashType.SIGHASH_ALL | SighashType.SIGHASH_FORKID);
  • The index (0) of the Input for which we are generating a Signature (remember that the Signature ends up embedded in the Input Script)
  • A private key
  • An integer, which in this case is the bitwise OR of two flags;
    • SIGHASH_ALL
    • SIGHASH_FORKID

Please note that unlike the other Sighash flags, the SIGHASH_FORKID flag does not affect which part of the transaction is signed. It does however affect the Signature that is created for ScriptSig in the spending Transaction. It does so by appending a 4-byte flag to the Signature Hash, thereby creating a spending Transaction that won’t be recognised by the Bitcoin Core (BTC) network

The SIGHASH_FORKID flag is a requirement for all Transactions broadcast on the Bitcoin SV (BSV) and Bitcoin Cash (BCH) networks.

For more details see this article.

  • SIGHASH_ALL
  • SIGHASH_SINGLE
  • SIGHASH_NONE
  • SIGHASH_ALL | ANYONECANPAY
  • SIGHASH_SINGLE | ANYONECANPAY
  • SIGHASH_NONE | ANYONECANPAY

It is unfortunately not self-evident from the naming of these flags what their meanings are. We will explore each of them in more detail in the following Sighash section.

For the purposes of using ECDSA Signatures within Bitcoin, it must be noted that the Signatures embedded within the Input Script (ScriptSig), have been slightly altered as follows:

  • the Sighash flag is encoded as a single byte and then appended to the DER-Encoded Signature
  • during Script execution, the OP_CHECKSIG command unpacks the Sighash flag from the Signature, and then runs the relevant SIGHASH algorithm to produce the hash which will be used alongside a provided PublicKey to perform an ECDSA Signature verification.

One needs three things for ECDSA signature verification using OP_CHECKSIG inside Script

  • A DER encoded Signature with Sighash flag appended (provided on the stack)
  • A Public Key (provided on the stack)
  • A Message (a hash value derived by applying the appropriate Sighash algorithm over a Transaction)

Sighash

Within the following sections we will be using graphic examples to illustrate various concepts related to the Sighash algorithm. We will be using two transactions, each composed of two inputs, and two outputs. The following graphic is a map of sorts; to help the reader have points of reference that will hopefully aid in navigating the nuances of Sighash.

Figure 1: Field-level detail of a Transaction with two inputs and two outputs

Figure 1: Field-level detail of a Transaction with two inputs and two outputs

Fields that are always signed

Having now spoken about Bitcoin as Triple Entry Accounting System. Part of what makes this possible, is the fact that there always exists a minimum level of Signature coverage that binds the spending transaction to the transaction being spent from. Therefore we should take note that:

  • The Signature always covers the Output Script (ScriptPubKey) of the Transaction Output we are spending from
  • The Signature always covers the following fields in the spending Transaction
    • Version
    • Transaction ID of the Transaction we are spending from
    • Index of the Transaction Output in the Transaction we are spending from
    • Locktime

With respect to the Signing of the Output Script (ScriptPubKey) of the Transaction Output we are spending from, there exists an important caveat:

Within Bitcoin Script we have an OP_CODE called OP_CODESEPARATOR. The Sighash algorithm within Bitcoin obeys the following rules w.r.t to the OP_CODESEPARATOR.

  • Only the portion the Script following the last occurence of an OP_CODESEPARATOR is signed.
  • In the case where no OP_CODESEPARATOR is found within the Script, the entire Script is signed.

Within the Sighash context, this Script is generally referred to as the Subscript.

Figure 2: Fields of a Transaction which are always signed

Figure 2: Fields of a Transaction which are always signed

Fields that are never signed

In general, when one generates a Signature over a data structure, and then embed the Signature inside that data structure, the portion where one embeds the Signature is not covered by the Signature. I.e. you don’t recursively sign over the Signature.

For this reason, you will notice that when we calculate a Signature to be placed inside the Script Field (ScriptSig) of a Transaction Input. The ScriptSig for this Transaction Input is never signed.

In Figure 3 we are calculating the Signature for Input #1

Figure 3: Fields marked in RED never signed

Figure 3: Fields marked in RED never signed

SIGHASH_ALL

The SIGHASH_ALL flag causes the algorithm to sign :

  • fields from all of the Inputs in the Spending Transaction
  • fields from all of the Outputs in the Spending Transaction
  • all of the Output Scripts (ScriptPubkey) in the Spent Transaction
  • The version and locktime fields in the Spending Transaction

The popular literature sometimes uses imprecise language like “sign all the inputs”. Please see Figure 4 for details on exactly which fields are signed.

Figure 4: Signature scheme for SIGHASH_ALL

Figure 4: Signature scheme for SIGHASH_ALL

SIGHASH_SINGLE

The SIGHASH_SINGLE flag causes the algorithm to sign :

  • fields from all of the Inputs in the Spending Transaction
  • only fields from the Output in the Spending Transaction that has the same Index as the Input for which we are generating a Signature. E.g. in Figure 5 we only sign fields from the Output #1, since we are generating a Signature for Input #1.
  • all of the Output Scripts (ScriptPubkey) in the Spent Transaction
  • The version and locktime fields in the Spending Transaction

Note that the Sequence Number in the Inputs of the Spending Transaction are not signed.

Figure 5: Signature scheme for SIGHASH_SINGLE

Figure 5: Signature scheme for SIGHASH_SINGLE

SIGHASH_NONE

The SIGHASH_NONE flag causes the algorithm to sign :

  • fields from all of the Inputs in the Spending Transaction
  • none of the fields from Outputs in the Spending Transaction
  • all of the Output Scripts (ScriptPubkey) in the Spent Transaction
  • The version and locktime fields in the Spending Transaction

Note that the Sequence Number in the Inputs of the Spending Transaction are not signed.

Figure 6: Signature scheme for SIGHASH_NONE

Figure 6: Signature scheme for SIGHASH_NONE

SIGHASH_ALL | ANYONECANPAY

The ANYONECANPAY flag is not used by itself. It is always combined with one of the other Sighash flags in a Bitwise OR operation.

Combining the SIGHASH_ALL flag with the ANYONECANPAY flag causes the algorithm to sign:

  • only fields from the Input in the Spending Transaction for which we are creating a Signature
  • fields from all of the Outputs in the Spending Transaction
  • only the Output Script(ScriptPubkey) in the Spent Transaction that shares the same Index as the Input for which we are creating a Signature.
  • The version and locktime fields in the Spending Transaction

Note that the Sequence Number in the Inputs of the Spending Transaction are not signed.

Figure 7: Signature scheme for (SIGHASH_ALL | ANYONECANPAY)

Figure 7: Signature scheme for (SIGHASH_ALL | ANYONECANPAY)

SIGHASH_SINGLE | ANYONECANPAY

The ANYONECANPAY flag is not used by itself. It is always combined with one of the other Sighash flags in a Bitwise OR operation.

Combining the SIGHASH_SINGLE flag with the ANYONECANPAY flag causes the algorithm to sign:

  • only fields from the Input in the Spending Transaction for which we are creating a Signature
  • only fields from the Outputs in the Spending Transaction which share the same Index as the Input for which we are creating a Signature.
  • only the Output Script(ScriptPubkey) in the Spent Transaction that shares the same Index as the Input for which we are creating a Signature.
  • The version and locktime fields in the Spending Transaction

Note that the Sequence Number in the Inputs of the Spending Transaction are not signed.

Figure 8: Signature scheme for (SIGHASH_SINGLE | ANYONECANPAY)

Figure 8: Signature scheme for (SIGHASH_SINGLE | ANYONECANPAY)

SIGHASH_NONE | ANYONECANPAY

The ANYONECANPAY flag is not used by itself. It is always combined with one of the other Sighash flags in a Bitwise OR operation.

Combining the SIGHASH_SINGLE flag with the ANYONECANPAY flag causes the algorithm to sign:

  • only fields from the Input in the Spending Transaction for which we are creating a Signature
  • none of the fields from the Outputs in the Spending Transaction
  • only the Output Script(ScriptPubkey) in the Spent Transaction that shares the same Index as the Input for which we are creating a Signature.
  • The version and locktime fields in the Spending Transaction

Note that the Sequence Number in the Inputs of the Spending Transaction are not signed.

Figure 9: Signature scheme for (SIGHASH_NONE | ANYONECANPAY)

Figure 9: Signature scheme for (SIGHASH_NONE | ANYONECANPAY)