@covenance/dlc
Version:
Crypto and Bitcoin functions for Covenance DLC implementation
153 lines (138 loc) • 4.69 kB
text/typescript
import { Point, utils } from './crypto/secp256k1';
import { Signature, SignatureHex, AdaptorSignature, AdaptorSignatureHex } from './crypto/types';
/**
* Converts a hex string to a Uint8Array
* @param hex - The hex string to convert
* @returns The Uint8Array representation of the hex string
*/
export function hexToBytes(hex: string): Uint8Array {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
}
return bytes;
}
/**
* Converts a Uint8Array to a hex string
* @param bytes - The Uint8Array to convert
* @returns The hex string representation of the Uint8Array
*/
export function bytesToHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
/**
* Converts a Point to a hex string
* @param point - The Point to convert
* @returns The hex string representation of the Point
*/
export function pointToHex(point: Point): string {
return bytesToHex(point.toRawBytes(true));
}
/**
* Converts a hex string to a Point
* @param hex - The hex string to convert
* @returns The Point representation of the hex string
*/
export function hexToPoint(hex: string): Point {
return Point.fromHex(hex);
}
/**
* Converts a Signature to a hex string representation
* @param sig - The Signature to convert
* @returns The hex string representation of the Signature
*/
export function signatureToHex(sig: Signature): SignatureHex {
return {
R: pointToHex(sig.R),
s: sig.s.toString(16)
};
}
/**
* Converts a hex string representation to a Signature
* @param hex - The hex string representation to convert
* @returns The Signature representation of the hex string
*/
export function hexToSignature(hex: SignatureHex): Signature {
return {
R: hexToPoint(hex.R),
s: BigInt('0x' + hex.s)
};
}
/**
* Converts an AdaptorSignature to a hex string representation
* @param sig - The AdaptorSignature to convert
* @returns The hex string representation of the AdaptorSignature
*/
export function adaptorSignatureToHex(sig: AdaptorSignature): AdaptorSignatureHex {
return {
R_prime: pointToHex(sig.R_prime),
s_prime: sig.s_prime.toString(16)
};
}
/**
* Converts a hex string representation to an AdaptorSignature
* @param hex - The hex string representation to convert
* @returns The AdaptorSignature representation of the hex string
*/
export function hexToAdaptorSignature(hex: AdaptorSignatureHex): AdaptorSignature {
return {
R_prime: hexToPoint(hex.R_prime),
s_prime: BigInt('0x' + hex.s_prime)
};
}
/**
* Computes the hash of a message using SHA256
* @param message - The message to hash
* @returns The hash of the message
*/
export async function sha256(message: Uint8Array): Promise<Uint8Array> {
return utils.sha256(message);
}
/**
* Computes the hash of a message using SHA256 and returns it as a hex string
* @param message - The message to hash
* @returns The hex string representation of the hash
*/
export async function sha256Hex(message: string): Promise<string> {
const hash = await sha256(hexToBytes(message));
return bytesToHex(hash);
}
/**
* Copies a bigint to a Uint8Array as big-endian
* @param dst - The Uint8Array to copy to
* @param offset - The offset to copy to
* @param n - The bigint to copy
*/
export function be32(dst: Uint8Array, offset: number, n: bigint): void {
for (let i = 31; i >= 0; i--) {
dst[offset + i] = Number(n & 0xffn);
n >>= 8n;
}
if (n) throw new RangeError('integer > 256 bits');
}
/**
* Convert seconds → BIP68 buffer.
* Uses 512-second granularity, sets the type flag (bit 22).
* Throws if out of range (> 0xffff * 512 seconds).
* @param seconds - The number of seconds to convert
* @returns The BIP68 buffer
*/
export function csvTimeDelay(seconds: number): Buffer {
if (!Number.isFinite(seconds) || seconds < 0) throw new RangeError("seconds ≥ 0");
const units = Math.ceil(seconds / 512);
if (units > 0xffff) throw new RangeError("max 0xffff * 512s ≈ 388.5 days");
const TYPE_FLAG = 1 << 22;
const value = TYPE_FLAG | units;
const data = encodeScriptNum(value); // minimal ScriptNum
return data;
}
export function encodeScriptNum(n: number): Buffer {
if (n === 0) return Buffer.alloc(0); // minimal zero
const bytes: number[] = [];
let v = n >>> 0; // n ≤ 0x0040ffff fits 32-bit
while (v) { bytes.push(v & 0xff); v >>>= 8; } // little-endian magnitude
if (bytes[bytes.length - 1] & 0x80) bytes.push(0x00);// keep positive (no sign bit set)
return Buffer.from(bytes);
}