UNPKG

tweakroot

Version:

tweak a taproot pubkey using elliptic curve operations

125 lines (108 loc) 3.72 kB
/** * Simplified key2pub implementation for testing tweakroot * * This implementation uses the noble-secp256k1 library for proper * elliptic curve cryptography operations. */ import * as secp from '@noble/secp256k1'; /** * Convert a private key to an x-only Taproot public key * * @param {string} privateKeyHex - 64-character hex string private key * @returns {string} 64-character hex string x-only public key */ export function privateKeyToPublicKey (privateKeyHex) { try { // Remove 0x prefix if present const privateKey = privateKeyHex.startsWith('0x') ? privateKeyHex.slice(2) : privateKeyHex; // Validate the private key if (!isValidPrivateKey(privateKey)) { throw new Error('Invalid private key format'); } // Convert the hex private key to a Uint8Array const privateKeyBytes = hexToBytes(privateKey); // Get the full public key point (x and y coordinates) const publicKeyPoint = secp.getPublicKey(privateKeyBytes, true); // For Taproot, we only need the x-coordinate (first 32 bytes excluding the prefix byte) // The noble-secp256k1 library returns compressed public keys (33 bytes) where the first byte is a prefix // We extract just the x-coordinate (32 bytes) const xOnlyPubKey = bytesToHex(publicKeyPoint.slice(1, 33)); return xOnlyPubKey; } catch (error) { throw new Error(`Error converting private key to public key: ${error.message}`); } } /** * Validate that a string is a valid private key (64-character hex string) * * @param {string} privateKey - The potential private key to validate * @returns {boolean} Whether the string is a valid private key */ export function isValidPrivateKey (privateKey) { if (!privateKey || typeof privateKey !== 'string') { return false; } // Must be 64 hex characters if (!/^[0-9a-fA-F]{64}$/.test(privateKey)) { return false; } // Must be in the valid range for secp256k1 const MAX_VALUE = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140'; return privateKey.toUpperCase() < MAX_VALUE && privateKey !== '0'.repeat(64); } /** * Validate that a string is a valid public key (64-character hex string) * * @param {string} publicKey - The potential public key to validate * @returns {boolean} Whether the string is a valid public key */ export function isValidPublicKey (publicKey) { if (!publicKey || typeof publicKey !== 'string') { return false; } // Must be 64 hex characters return /^[0-9a-fA-F]{64}$/.test(publicKey); } /** * Increment a private key by 1 * * @param {string} privateKeyHex - The private key to increment * @returns {string} The incremented private key */ export function incrementPrivateKey (privateKeyHex) { // Convert hex to byte array const privateKeyBytes = hexToBytes(privateKeyHex); // Add 1 with carry let carry = 1; for (let i = privateKeyBytes.length - 1; i >= 0; i--) { const value = privateKeyBytes[i] + carry; privateKeyBytes[i] = value & 0xFF; carry = value >> 8; if (carry === 0) break; } return bytesToHex(privateKeyBytes); } /** * Convert a hex string to Uint8Array bytes * * @param {string} hex - The hex string * @returns {Uint8Array} The resulting bytes */ function hexToBytes (hex) { const bytes = new Uint8Array(hex.length / 2); for (let i = 0; i < bytes.length; i++) { bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16); } return bytes; } /** * Convert bytes to a hex string * * @param {Uint8Array} bytes - The bytes to convert * @returns {string} The resulting hex string */ function bytesToHex (bytes) { return Array.from(bytes) .map(b => b.toString(16).padStart(2, '0')) .join(''); }