Digital Signatures
How digital signatures work in Bitcoin and their implementation in the BSV TypeScript SDK.
What are Digital Signatures?
Digital signatures prove ownership and authorize Bitcoin transactions:
import { PrivateKey, Transaction } from '@bsv/sdk'
// Create a signature
const privateKey = PrivateKey.fromRandom()
const message = 'transaction data'
const signature = privateKey.sign(message)
// Verify the signature
const publicKey = privateKey.toPublicKey()
const isValid = publicKey.verify(message, signature)
Bitcoin Signatures
Bitcoin uses ECDSA (Elliptic Curve Digital Signature Algorithm):
- secp256k1: The elliptic curve used by Bitcoin
- SHA-256: Hash function for message digests
- DER Encoding: Standard format for signature serialization
SIGHASH Types
SIGHASH flags determine what parts of a transaction are signed:
SIGHASH_ALL (Default)
Signs all inputs and outputs:
SIGHASH_NONE
Signs all inputs but no outputs:
SIGHASH_SINGLE
Signs all inputs and one corresponding output:
SIGHASH_ANYONECANPAY
Can be combined with other flags to sign only one input:
Transaction Signing
The SDK handles transaction signing automatically:
// Manual signing (low-level)
const tx = new Transaction()
const signature = tx.sign(privateKey, inputIndex, sighashType)
// Wallet signing (recommended)
const wallet = new WalletClient()
const action = await wallet.createAction({
outputs: [/* outputs */]
})
// Wallet handles signing internally
Signature Verification
Verify signatures to ensure transaction validity:
// Verify a specific signature
const isValid = publicKey.verify(messageHash, signature)
// Verify entire transaction
const txValid = await transaction.verify(chainTracker)
DER Encoding
Signatures are encoded in DER format:
// Get DER-encoded signature
const derSignature = signature.toDER()
// Parse DER signature
const sig = Signature.fromDER(derBytes)
// Get r and s components
const r = signature.r
const s = signature.s
Security Considerations
Nonce Security
- Each signature must use a unique, random nonce
- Reusing nonces can leak private keys
- The SDK handles nonce generation securely
Signature Malleability
- Bitcoin signatures can be modified without invalidating them
- Use canonical signatures to prevent malleability
- The SDK produces canonical signatures by default
Hash Types
- Choose appropriate SIGHASH types for your use case
- SIGHASH_ALL is safest for most applications
- Other types enable advanced transaction patterns
Common Patterns
Multi-Input Signing
// Sign multiple inputs in a transaction
for (let i = 0; i < transaction.inputs.length; i++) {
const signature = privateKey.sign(transaction.getSignatureHash(i))
transaction.inputs[i].unlockingScript = createUnlockingScript(signature)
}
Conditional Signatures
// Different signatures for different conditions
const signature1 = privateKey1.sign(txHash, 'all')
const signature2 = privateKey2.sign(txHash, 'single')
Error Handling
Common signature issues:
- Invalid private key format
- Incorrect message hash
- Malformed signature data
- Verification failures
try {
const signature = privateKey.sign(message)
} catch (error) {
console.error('Signing failed:', error.message)
}
Best Practices
- Always use secure random number generation
- Verify signatures before trusting them
- Use appropriate SIGHASH types for your use case
- Store signatures in DER format for interoperability
- Never reuse nonces across signatures
Wallet Integration
Most applications use wallets for signing:
// Wallet handles signature creation
const wallet = new WalletClient()
const result = await wallet.createAction({
description: 'Payment transaction',
outputs: [/* outputs */]
})
// Signatures created automatically
Next Steps
- Understand Key Management for signature security
- Learn about Script Templates for signature usage
- Explore Transaction Structure for signature placement