Overview
The Signature class handles ECDSA (secp256k1) signatures for the Hive blockchain. It provides methods for creating signatures, verifying them, recovering public keys, and converting between different signature formats.
Signatures include recovery information that allows the original public key to be recovered from the signature and message, which is essential for Hive’s signature verification system.
Constructor
const signature = new Signature(data, recovery, compressed?)
Raw signature data (64 bytes: 32 bytes r-value + 32 bytes s-value)
Recovery byte (0-3) used to recover the public key
Whether the signature uses compressed format (default: true)
Example
import { Signature } from 'hive-tx'
// Create from raw components
const data = new Uint8Array(64) // 64-byte signature data
const recovery = 0 // Recovery parameter (0-3)
const signature = new Signature(data, recovery, true)
Static Methods
from
Creates a Signature from a hex string.
const signature = Signature.from(hexString)
130-character hex string containing recovery byte and signature data
Example
import { Signature } from 'hive-tx'
// From hex string (130 characters: 2 for recovery + 128 for signature)
const sigHex = '1f8a7b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f'
const signature = Signature.from(sigHex)
console.log('Recovery:', signature.recovery)
console.log('Data length:', signature.data.length) // 64
Instance Methods
customToString
Returns the signature as a 130-character hex string.
const hexString = signature.customToString()
130-character hex string representation of the signature
Example
import { PrivateKey, Transaction } from 'hive-tx'
// Create and sign a transaction
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const tx = new Transaction()
await tx.addOperation('vote', {
voter: 'alice',
author: 'bob',
permlink: 'my-post',
weight: 10000
})
const { digest } = tx.digest()
const signature = privateKey.sign(digest)
// Convert to hex string
const sigHex = signature.customToString()
console.log('Signature hex:', sigHex)
console.log('Length:', sigHex.length) // 130
toBuffer
Converts the signature to a 65-byte buffer format (recovery byte + signature data).
const buffer = signature.toBuffer()
65-byte buffer containing recovery byte + 64-byte signature data
Example
import { PrivateKey } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const message = sha256('Hello, Hive!')
const signature = privateKey.sign(message)
// Get buffer representation
const buffer = signature.toBuffer()
console.log('Buffer length:', buffer.length) // 65
console.log('Recovery byte:', buffer[0])
console.log('Signature data:', buffer.subarray(1))
getPublicKey
Recovers the public key from this signature and message hash. This is one of the most powerful features of ECDSA signatures on Hive.
const publicKey = signature.getPublicKey(message)
message
Uint8Array | string
required
32-byte message hash as Uint8Array or 64-character hex string (must be a valid SHA256 hash)
Recovered public key that created this signature
Example
import { PrivateKey, Transaction } from 'hive-tx'
// Sign a transaction
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const expectedPublicKey = privateKey.createPublic()
const tx = new Transaction()
await tx.addOperation('transfer', {
from: 'alice',
to: 'bob',
amount: '1.000 HIVE',
memo: ''
})
const { digest } = tx.digest()
const signature = privateKey.sign(digest)
// Recover public key from signature
const recoveredPublicKey = signature.getPublicKey(digest)
// Verify they match
console.log('Expected:', expectedPublicKey.toString())
console.log('Recovered:', recoveredPublicKey.toString())
console.log('Match:', expectedPublicKey.toString() === recoveredPublicKey.toString())
// Output: true
Creating Signatures
Signatures are typically created using a PrivateKey:
import { PrivateKey } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'
const privateKey = PrivateKey.fromLogin('alice', 'password', 'posting')
// Sign a message
const message = sha256('My message to sign')
const signature = privateKey.sign(message)
console.log('Signature:', signature.customToString())
console.log('Recovery:', signature.recovery)
Verifying Signatures
There are two ways to verify signatures:
Method 1: Using PublicKey.verify
import { PrivateKey } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const publicKey = privateKey.createPublic()
const message = sha256('Message to verify')
const signature = privateKey.sign(message)
// Verify using PublicKey
const isValid = publicKey.verify(message, signature)
console.log('Signature valid:', isValid) // true
// Verify with wrong message
const wrongMessage = sha256('Different message')
const isInvalid = publicKey.verify(wrongMessage, signature)
console.log('Wrong message:', isInvalid) // false
Method 2: Using Signature.getPublicKey
import { PrivateKey } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const expectedPublicKey = privateKey.createPublic()
const message = sha256('Message to verify')
const signature = privateKey.sign(message)
// Recover public key and compare
const recoveredPublicKey = signature.getPublicKey(message)
const isValid = recoveredPublicKey.toString() === expectedPublicKey.toString()
console.log('Signature valid:', isValid) // true
Transaction Signing Example
import { Transaction, PrivateKey, Signature } from 'hive-tx'
async function signAndVerifyTransaction() {
// Create transaction
const tx = new Transaction()
await tx.addOperation('vote', {
voter: 'alice',
author: 'bob',
permlink: 'awesome-post',
weight: 10000
})
// Sign with private key
const privateKey = PrivateKey.fromLogin('alice', 'password', 'posting')
const signedTx = tx.sign(privateKey)
// Get the signature from the transaction
const signatureHex = signedTx.signatures[0]
const signature = Signature.from(signatureHex)
// Verify signature
const { digest } = tx.digest()
const publicKey = privateKey.createPublic()
const isValid = publicKey.verify(digest, signature)
console.log('Transaction signed:', signatureHex)
console.log('Signature valid:', isValid)
// Broadcast
const result = await tx.broadcast()
console.log('Transaction ID:', result.tx_id)
}
signAndVerifyTransaction()
External Signature Application
You can sign externally (e.g., with hardware wallets) and apply the signature:
import { Transaction, PrivateKey, Signature } from 'hive-tx'
async function externalSigning() {
// Create transaction
const tx = new Transaction()
await tx.addOperation('transfer', {
from: 'alice',
to: 'bob',
amount: '5.000 HIVE',
memo: ''
})
// Get digest for external signing
const { digest, txId } = tx.digest()
console.log('Transaction ID:', txId)
console.log('Digest to sign:', Buffer.from(digest).toString('hex'))
// Sign externally (simulated here with actual key)
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const signature = privateKey.sign(digest)
const signatureHex = signature.customToString()
console.log('External signature:', signatureHex)
// Apply signature to transaction
tx.addSignature(signatureHex)
// Verify before broadcasting
const publicKey = privateKey.createPublic()
const sig = Signature.from(signatureHex)
const isValid = publicKey.verify(digest, sig)
console.log('Signature valid:', isValid)
if (isValid) {
const result = await tx.broadcast()
console.log('Broadcast successful:', result.tx_id)
}
}
externalSigning()
Multi-Signature Transactions
import { Transaction, PrivateKey } from 'hive-tx'
async function multiSigTransaction() {
const tx = new Transaction()
await tx.addOperation('transfer', {
from: 'multisig-account',
to: 'recipient',
amount: '100.000 HIVE',
memo: 'Multi-sig payment'
})
// Get digest once
const { digest } = tx.digest()
// Sign with first key
const key1 = PrivateKey.fromString(process.env.SIGNER1_KEY!)
const sig1 = key1.sign(digest)
tx.addSignature(sig1.customToString())
// Sign with second key
const key2 = PrivateKey.fromString(process.env.SIGNER2_KEY!)
const sig2 = key2.sign(digest)
tx.addSignature(sig2.customToString())
// Sign with third key
const key3 = PrivateKey.fromString(process.env.SIGNER3_KEY!)
const sig3 = key3.sign(digest)
tx.addSignature(sig3.customToString())
console.log('Signatures:', tx.transaction?.signatures.length)
// Verify all signatures
for (let i = 0; i < tx.transaction!.signatures.length; i++) {
const sig = Signature.from(tx.transaction!.signatures[i])
const recoveredKey = sig.getPublicKey(digest)
console.log(`Signer ${i + 1} public key:`, recoveredKey.toString())
}
// Broadcast
const result = await tx.broadcast()
console.log('Multi-sig transaction broadcast:', result.tx_id)
}
multiSigTransaction()
Hive signatures use the following format:
[RECOVERY_BYTE][R_VALUE][S_VALUE]
- Recovery byte: 1 byte (values 31-34 for compressed, 27-30 for uncompressed)
- R value: 32 bytes (signature component)
- S value: 32 bytes (signature component)
- Total: 65 bytes → 130 hex characters
import { PrivateKey } from 'hive-tx'
import { sha256 } from '@noble/hashes/sha2'
const privateKey = PrivateKey.fromLogin('alice', 'password', 'active')
const message = sha256('Example message')
const signature = privateKey.sign(message)
const hex = signature.customToString()
console.log('Full signature:', hex)
console.log('Length:', hex.length) // 130
const buffer = signature.toBuffer()
console.log('Recovery byte:', buffer[0].toString(16)) // Usually 1f-22
console.log('R value:', Buffer.from(buffer.subarray(1, 33)).toString('hex'))
console.log('S value:', Buffer.from(buffer.subarray(33, 65)).toString('hex'))
Properties
64-byte signature data (r-value + s-value)
Recovery parameter (0-3) used to recover the public key
Error Handling
import { Signature } from 'hive-tx'
try {
// Invalid hex string
const sig1 = Signature.from('invalid')
} catch (error) {
console.error('Invalid signature hex:', error.message)
}
try {
// Wrong message length
const sig = Signature.from('1f' + '00'.repeat(64))
const invalidMessage = new Uint8Array(16) // Should be 32 bytes
sig.getPublicKey(invalidMessage)
} catch (error) {
console.error('Invalid message:', error.message)
// Output: Expected a valid sha256 hash as message
}
See Also