Skip to main content

Signature Templates

When a contract function has a sig parameter, it needs a cryptographic signature from a private key for the spending transaction. In place of a signature, a SignatureTemplate can be passed, which will generate the correct signature when the transaction is built.

tip

SignatureTemplate can be used with a Contract as function argument to generate a signature automatically, or can be used in the TransactionBuilder to create an Unlocker for a P2PKH UTXO.

SignatureTemplate

Constructor

new SignatureTemplate(
signer: Keypair | Uint8Array | string,
hashtype?: HashType,
signatureAlgorithm?: SignatureAlgorithm
)

In place of a signature, a SignatureTemplate can be passed, which will generate the correct signature using the signer parameter. This signer can be any representation of a private key, including WIF strings, BCHJS' ECPair, bitcore-lib-cash' PrivateKey, or binary private keys represented as Uint8Array. This ensures that SignatureTemplate can be used with any BCH library.

Example

const aliceWif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
const aliceSignatureTemplate = new SignatureTemplate(aliceWif)

const transferDetails = await new TransactionBuilder({ provider })
.addInput(selectedContractUtxo, contract.unlock.transfer(aliceSignatureTemplate))
.addOutput({
to: 'bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx',
amount: 10000n
})
.send();

The hashtype and signatureAlgorithm options are covered under 'Advanced Usage'.

SignatureTemplate Methods

unlockP2PKH()

Importantly the SignatureTemplate can also be used to generate the Unlocker for a P2PKH UTXO in the following way:

signatureTemplate.unlockP2PKH(): Unlocker

Example

import { aliceTemplate, aliceAddress, transactionBuilder } from './somewhere.js';

const aliceUtxos = await provider.getUtxos(aliceAddress);
transactionBuilder.addInput(aliceUtxos[0], aliceTemplate.unlockP2PKH());

getPublicKey()

The SignatureTemplate also has a helper method to get the matching PublicKey in the following way:

signatureTemplate.getPublicKey(): Uint8Array

Example

import { aliceTemplate } from './somewhere.js';

const alicePublicKey = aliceTemplate.getPublicKey()

signMessageHash()

The SignatureTemplate also has a helper method to sign a message hash, which can be used to sign non-transaction messages. This is useful for generating datasig signatures for smart contract use cases.

signatureTemplate.signMessageHash(message: Uint8Array): Uint8Array

Example

import { aliceTemplate } from './somewhere.js';
import { sha256 } from '@cashscript/utils';
import { hexToBin } from '@bitauth/libauth';

const signature = aliceTemplate.signMessageHash(sha256(hexToBin('0000000000000000000000')));

Advanced Usage

HashType

The hashtype (also called the sighash flag) determines which parts of the spending transaction the signature commits to. When the transaction is signed, these parts are serialized into a signing serialization (or sighash preimage), which is hashed and signed.

The default hashtype is HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS because this is the most secure option for smart contract use cases: it commits to every input and output of the transaction, so the signed transaction cannot be altered in any way after signing.

export enum HashType {
SIGHASH_ALL = 0x01,
SIGHASH_NONE = 0x02,
SIGHASH_SINGLE = 0x03,
SIGHASH_UTXOS = 0x20,
SIGHASH_ANYONECANPAY = 0x80,
}

SIGHASH_ALL, SIGHASH_NONE and SIGHASH_SINGLE choose which outputs are signed, while SIGHASH_UTXOS and SIGHASH_ANYONECANPAY are modifiers that can be OR'd on top to change what else is committed to.

FlagValueCommits toTypical use
SIGHASH_ALL0x01all inputs and all outputsthe default — sign the exact transaction
SIGHASH_NONE0x02all inputs, but no outputslet the outputs be decided after signing
SIGHASH_SINGLE0x03all inputs, and only the one output at the same index as the signed inputpair a single input to a single output
SIGHASH_UTXOS0x20(modifier) additionally commits to the full contents of the UTXOs being spentrecommended for all contracts — see below
SIGHASH_ANYONECANPAY0x80(modifier) only the current input, allowing other inputs to be addedcrowdfunding-style transactions where anyone can add an input

The default SIGHASH_ALL | SIGHASH_UTXOS is the right choice for almost all smart contract use cases: SIGHASH_UTXOS commits to the full value and token contents of every UTXO being spent, which protects introspection-based covenants against an attacker substituting a spent UTXO for a different one of equal amount. The other flags weaken these commitments. SIGHASH_NONE and SIGHASH_SINGLE leave outputs unsigned, making the transaction malleable, and SIGHASH_ANYONECANPAY only signs the current input. Only use them when you specifically need that flexibility.

For a full technical breakdown of the signing serialization and every valid flag combination, see the Bitcoin Cash transaction signing reference.

Example

const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';

const signatureTemplate = new SignatureTemplate(
wif, HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS
);

const configuredHashType = signatureTemplate.getHashType()

The hashtype byte

The hashtype is appended as a single byte to the end of every signature, so a validator or contract can tell how a signature was produced by inspecting its last byte. On Bitcoin Cash the SIGHASH_FORKID bit (0x40) is always set. SignatureTemplate OR's it in automatically, so the byte appended to the signature is your hashtype combined with 0x40. For example the default SIGHASH_ALL | SIGHASH_UTXOS (0x21) is appended as 0x61. This is the value to compare against when inspecting a signature's last byte inside a contract.

SignatureAlgorithm

The signatureAlgorithm parameter determines the cryptographic algorithm used for signing. By default, the modern and compact Schnorr algorithm is used.

export enum SignatureAlgorithm {
ECDSA = 0x00,
SCHNORR = 0x01,
}

Example

const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';

const hashType = HashType.SIGHASH_ALL | HashType.SIGHASH_UTXOS
const signatureAlgorithm = SignatureAlgorithm.SCHNORR
const signatureTemplate = new SignatureTemplate(wif, hashType,signatureAlgorithm);

const configuredSignatureAlgorithm = signatureTemplate.getSignatureAlgorithm()