Transaction Signing
Transaction signatures are central to how transactions are generally secured, preventing people other than the intended recipient of funds from spending them. Signatures are created using asymmetric cryptography and involve generating a hash of the transaction and performing a signature operation using the sender's private key. Anyone with the corresponding public key can then verify the validity of the signature. As described in Standard Scripts, the OP_CHECKSIG and related operations are used to validate signatures included in the unlocking script of a future transaction input.
However, there are a number of issues with signing a transaction that must be addressed:
- Transactions are identified by their idem or id.
- The signatures are not part of the idem.
- The signatures are created from the idem.
Points (1) and (2) mean that if the signature is changed, the transaction's idem will not change but its id will. In addition, because signatures relate only to a single input to a transaction (i.e. spending an unspent transaction output or UTXO) the may be multiple signatures in a transaction potentially created by different private keys, or even different people.
As a consequence of these factors, signatures have more parameters than may be immediately obvious, and the details of how signatures are generated can be, and have been, changed in a number of ways. These parameters are encoded in the Hash Type.
Signature Hash Type
The Nexa signature hash type is very different than Bitcoin or Bitcoin Cash.
Preimage Format
The preimage for a signature within a single input is a serialization of the following transaction components. Three of the components are aggregate hashes over a set of inputs, and one is an aggregate hash over a set of outputs; which inputs and outputs are covered is determined by the signature hash type. The assembled preimage is then hashed with a double SHA-256, and it is this hash that is signed.
- The transaction version
- A hash of the covered inputs' previous outputs
- A hash of the covered inputs' amounts
- A hash of the covered inputs' sequence numbers
- The script code being signed
- A hash of the covered transaction outputs
- The transaction lock time
- The signature hash type
The following table specifies, in detail, the preimage format for a signature within a single input.
| Field | Length | Format | Description |
|---|---|---|---|
| transaction version | 1 byte | unsigned integer | The value of the transaction's version field. |
| previous outputs hash | 32 bytes | hash | A double SHA-256 hash of the covered inputs' previous outputs. See Previous Outputs Hash. |
| input amounts hash | 32 bytes | hash | A double SHA-256 hash of the covered inputs' amounts. See Input Amounts Hash. |
| sequence numbers hash | 32 bytes | hash | A double SHA-256 hash of the covered inputs' sequence numbers. See Sequence Numbers Hash. |
| modified locking script length | variable | variable length integer | The number of bytes for modified_locking_script. |
| modified locking script | modified_locking_script_length bytes |
bytes | The script being signed. See Modified Locking Script. |
| transaction outputs hash | 32 bytes | hash | A double SHA-256 hash of the covered outputs. See Transaction Outputs Hash. |
| transaction lock time | 4 bytes | unsigned integer(LE) | The lock time of the transaction. |
| signature hash type | variable | Hash Type | The signature hash type, serialized as a length-prefixed byte string. For the default (all inputs, all outputs) it is the empty string, encoded as the single length byte 0x00. |
Note that, unlike Bitcoin and Bitcoin Cash, the transaction version is a single byte, a dedicated hash of the input amounts is included, and the individual previous output, value, and sequence number of the input being signed are not serialized separately -- they are covered by the aggregate hashes above.
Previous Outputs Hash
The double-SHA256-hash of the following data is used.
For each covered transaction input, append the following information:
| Field | Length | Format | Description |
|---|---|---|---|
| input type | 1 byte | unsigned integer | The type of the input. |
| outpoint | 32 bytes | hash | The outpoint of the output being spent. |
Input Amounts Hash
The double-SHA256-hash of the following data is used.
For each covered transaction input, append the following information:
| Field | Length | Format | Description |
|---|---|---|---|
| amount | 8 bytes | signed integer(LE) | The value, in the smallest unit, of the output being spent by this input. |
Sequence Numbers Hash
The double-SHA256-hash of the following data is used.
For each covered transaction input, append the following information:
| Field | Length | Format | Description |
|---|---|---|---|
| sequence number | 4 bytes | unsigned integer(LE) | The sequence number field of the transaction input. |
Modified Locking Script
The modified locking script is the script being evaluated by the signature-checking operation, taken from the most recent OP_CODESEPARATOR (if any) to the end of the script, with the signature being checked removed.
For a script template spend this is the template script itself.
For example, the standard pay-to-public-key-template uses the two-byte template script OP_FROMALTSTACK OP_CHECKSIGVERIFY (0x6c 0xad), so the modified locking script is those two bytes (length-prefixed as 0x02 0x6c 0xad).
Transaction Outputs Hash
The double-SHA256-hash of the following data is used.
For each covered transaction output, append the following information:
| Field | Length | Format | Description |
|---|---|---|---|
| output type | 1 byte | unsigned integer | The type of the output. |
| value | 8 bytes | signed integer(LE) | The number of units to be transferred. |
| locking script length | variable | variable length integer | The size of the locking script in bytes. |
| locking script | locking_script_length bytes |
bytes | The contents of the locking script. |
Signature Format
Depending on the signature algorithm used, the representation of the signature itself can vary. Nexa supports Schnorr signatures for the CHECKSIG/CHECKDATASIG[VERIFY] operations and for the CHECKMULTISIG[VERIFY] operations.
The specific format of the signature depends on the operation to be executed and the algorithm being used to generate the signature.
ECDSA Signature Format
ECDSA signatures follow a strict DER encoding format, followed by the above hash type. They are distinguished from Schnorr signatures by length, despite having a variable-length format (see Schorr signature format).
| Field | Length | Format | Description |
|---|---|---|---|
| magic number | 1 byte | byte | The magic number value: 0x30. |
| signature length | 1 byte | unsigned integer | The number of bytes to follow in the signature. |
| r | variable | DER-encoded integer | The ECDSA "r" value. |
| s | variable | DER-encoded integer | The ECDSA "s" value. |
| hash type | 1 byte | LSB of hash type | Indicates the parameters used to generate the pre-image for this signature. |
DER-Encoded Integer
| Field | Length | Format | Description |
|---|---|---|---|
| integer header-byte | 1 byte | byte | The integer value indicator: 0x02. |
| value length | 1 byte | unsigned integer | The number of bytes used to encode the integer value. |
| value | value_length bytes |
unsigned integer(BE) | The integer value being encoded. This must be the smallest viable representation of the value being encoded. That is, the highest order byte may only be 0x00 if it is necessary to ensure the rest of the value is not interpreted as negative, in which case it is required.For example, the value may start with 0x0080 or 0x7F but not 0x80 or 0x007F. |
Schnorr Signature Format
Schnorr signatures have a less variable format, though the hash type field is removed for OP_CHECKDATASIG. This allows them to be easily distinguished from ECDSA signatures on length alone. In fact, ECDSA signatures that happen to be the length of a Schnorr signature in the same context (though they should be extremely rare, with probability 2-49) should be re-generated to avoid being errantly treated as an invalid Schnorr signature.
| Field | Length | Format | Description |
|---|---|---|---|
| r | 32 bytes | unsigned integer(BE) | The Schorr "r" value. |
| s | 32 bytes | unsigned integer(BE) | The Schorr "s" value. |
| hash type | 0-1 bytes | LSB of hash type | Indicates the parameters used to generate the pre-image for this signature. Not included for OP_CHECKDATASIG signatures. |
Bitcoin Core Signatures
Bitcoin Core signatures work very similarly to modern Bitcoin Cash signatures. The primary difference is its different preimage format, as described in the following section.
Preimage Format
Bitcoin Core preimages are generated using the following steps:
- If
SIGHASH_SINGLEis used without a corresponding output, due to a bug, the entire preimage used for that signature becomes0x0100000000000000000000000000000000000000000000000000000000000000, and none of the following steps need performed. - Take the Modified Locking Script
- Replace the current input's scripts with this modified script
- Set all other input scripts to empty byte arrays
- Handle the Hash Type logic as in Bitcoin Cash, but
SIGHASH_FORKIDshould NOT be set.
Signature Format
Bitcoin Core signatures follow the ECDSA signature format described above.