UNPKG

@covenance/dlc

Version:

Crypto and Bitcoin functions for Covenance DLC implementation

131 lines (114 loc) 4.61 kB
import { Point, utils, CURVE } from './secp256k1'; import { PrivKey, PubKey, Signature, AdaptorSignature, Sighash } from './types'; import { sha256, bytesToHex } from '../utils'; import { mod } from './general'; /** * Counterparty function to create an adaptor signature for a specific event outcome * @param counterpartyPrivKey - Counterparty's private key * @param oracleSigPoint - Oracle's signature point for the event outcome * @param cetSighash - Sighash of the event outcome CET * @returns Counterparty's adaptor signature */ export async function createAdaptorSig( counterpartyPrivKey: PrivKey, oracleSigPoint: Point, cetSighash: Sighash, tag: Uint8Array = Uint8Array.from(Buffer.from('7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c', 'hex')) // SHA256('BIP0340/challenge') ): Promise<AdaptorSignature> { let r_cp: Uint8Array; let r_cpBigInt: bigint; let R_cp: Point; let R_prime_cp: Point; // Generate nonce UNTIL (R_cp + S_i).y is even do { // Generate random nonce r_cp = utils.randomPrivateKey(); r_cpBigInt = BigInt('0x' + bytesToHex(r_cp)); // Compute R_cp = r_cp * G R_cp = Point.fromPrivateKey(r_cpBigInt); // Compute R'_cp = R_cp + S_i R_prime_cp = R_cp.add(oracleSigPoint); } while ((R_prime_cp.y & 1n) === 1n); // Fail if r = 0 if (r_cpBigInt === 0n) { throw new Error('Generated nonce is zero'); } // Get counterparty's public key const P_cp = Point.fromPrivateKey(counterpartyPrivKey); // Compute H(tag||tag||R'_cp||P_cp||cet_i) const hashInput = new Uint8Array([ ...tag, ...tag, ...R_prime_cp.toRawBytes(true).slice(1), ...P_cp.toRawBytes(true).slice(1), ...cetSighash ]); const hash = await sha256(hashInput); const e = mod(BigInt('0x' + bytesToHex(hash)), CURVE.n); // Compute s'_cp = r_cp + H(R'_cp||P_cp||cet_i)x_cp const x_cp = BigInt('0x' + bytesToHex(counterpartyPrivKey)); const s_prime_cp = mod(r_cpBigInt + e * x_cp, CURVE.n); return { R_prime: R_prime_cp, s_prime: s_prime_cp}; } /** * Counterparty function to adapt (finalize) an adaptor signature * @param adaptorSig - The adaptor signature to finalize * @param s - The oracle's scalar value (s) from their signature or repayment secret * @returns The final signature */ export function adaptSig( adaptorSig: AdaptorSignature, s: bigint ): Signature { // Compute s_cp = s'_cp + s const s_cp = adaptorSig.s_prime + s; return { R: adaptorSig.R_prime, s: mod(s_cp, CURVE.n) }; } /** * Function to verify an adaptor signature * @param adaptorSig - The adaptor signature to verify * @param counterpartyPubKey - The counterparty's public key * @param cetSighash - The sighash of the event outcome CET * @param oracleSigPoint - The oracle's signature point for the event outcome * @returns Boolean indicating whether the adaptor signature is valid */ export async function verifyAdaptorSig( adaptorSig: AdaptorSignature, counterpartyPubKey: PubKey, cetSighash: Sighash, oracleSigPoint: Point, tag: Uint8Array = Uint8Array.from(Buffer.from('7bb52d7a9fef58323eb1bf7a407db382d2f3f2d81bb1224f49fe518f6d48d37c', 'hex')) // SHA256('BIP0340/challenge') ): Promise<boolean> { // Compute H(tag||tag||R'_cp||P_cp||cet_i) const hashInput = new Uint8Array([ ...tag, ...tag, ...adaptorSig.R_prime.toRawBytes(true).slice(1), ...counterpartyPubKey.toRawBytes(true).slice(1), ...cetSighash ]); const hash = await sha256(hashInput); const e = mod(BigInt('0x' + bytesToHex(hash)), CURVE.n); // Compute s'_cp * G const sG = Point.BASE.multiply(adaptorSig.s_prime); // Compute R_cp = R'_cp - S_i const R_cp = adaptorSig.R_prime.subtract(oracleSigPoint); // Compute R_cp + e * P_cp const rightSide = R_cp.add(counterpartyPubKey.multiply(e)); // Verify s'_cp * G = R_cp + e * P_cp return sG.equals(rightSide); } /** * Function to generate a private key which produces a public key with even y. * This is to avoid doing BIP-340 secret-key parity adjustment (flip if public key has odd Y) each time an adaptor signature is created. * @returns A private key which produces a public key with even y. */ export function generateEvenYPrivateKey(): PrivKey { let privKey = BigInt('0x' + bytesToHex(utils.randomPrivateKey())); let pubKey = Point.fromPrivateKey(privKey); if ((pubKey.y & 1n) === 1n) { privKey = CURVE.n - privKey; // negate secret pubKey = Point.fromPrivateKey(privKey); // now pubKey.y is even } return Buffer.from(privKey.toString(16).padStart(64, "0"), "hex"); }