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.
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,
sighashType?: SighashType,
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, hex strings (optionally 0x-prefixed), BCHJS' ECPair, bitcore-lib-cash' PrivateKey (any object with a toWIF() method), or binary private keys represented as Uint8Array. This ensures that SignatureTemplate can be used with any BCH library. A key that is not 32 bytes long throws.
The defaults are SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS and SignatureAlgorithm.SCHNORR.
checkMultiSig requires ECDSA signatures. For each signature argument of a checkMultiSig call, create the template
with new SignatureTemplate(key, undefined, SignatureAlgorithm.ECDSA). The Schnorr default suits checkSig.
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 sighashType 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());
publicKey
The SignatureTemplate exposes the matching compressed public key as a property:
signatureTemplate.publicKey: Uint8Array
Example
import { aliceTemplate } from './somewhere.js';
const alicePublicKey = aliceTemplate.publicKey
getPublicKey(), getHashType() and getSignatureAlgorithm() remain available but are deprecated in favour of the
publicKey, sighashType and signatureAlgorithm properties. HashType is a deprecated alias of SighashType.
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.
signMessageHash() signs the 32-byte hash it is given with the template's algorithm (a 64-byte Schnorr signature by default) and appends no sighash byte. checkDataSig(s, msg, pk) verifies a signature over sha256(msg), so pass sha256(msg), not msg itself.
signatureTemplate.signMessageHash(message: Uint8Array): Uint8Array
Example
import { aliceTemplate } from './somewhere.js';
import { sha256 } from '@solidcash/utils';
import { hexToBin } from '@bitauth/libauth';
const signature = aliceTemplate.signMessageHash(sha256(hexToBin('0000000000000000000000')));
Advanced Usage
SighashType
The default sighashType is SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS because this is the most secure
option for smart contract use cases. Every signature also carries the fork id flag, which BCH consensus requires, so
it is not part of the configured value.
The constructor throws on an unknown base type or flag, and on SIGHASH_UTXOS combined with SIGHASH_ANYONECANPAY.
It prints a warning for SIGHASH_NONE, SIGHASH_SINGLE and SIGHASH_ANYONECANPAY, which do not commit to all
inputs and outputs, so another party can change the transaction.
export enum SighashType {
SIGHASH_ALL = 0x01,
SIGHASH_NONE = 0x02,
SIGHASH_SINGLE = 0x03,
SIGHASH_UTXOS = 0x20,
SIGHASH_ANYONECANPAY = 0x80,
}
Example
import { SighashType, SignatureTemplate } from 'solidcash';
const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
const signatureTemplate = new SignatureTemplate(
wif, SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS
);
const configuredSighashType = signatureTemplate.sighashType
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
import { SighashType, SignatureAlgorithm, SignatureTemplate } from 'solidcash';
const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
const sighashType = SighashType.SIGHASH_ALL | SighashType.SIGHASH_UTXOS
const signatureAlgorithm = SignatureAlgorithm.SCHNORR
const signatureTemplate = new SignatureTemplate(wif, sighashType, signatureAlgorithm);
const configuredSignatureAlgorithm = signatureTemplate.signatureAlgorithm