UNPKG

@covenance/dlc

Version:

Crypto and Bitcoin functions for Covenance DLC implementation

207 lines (175 loc) 7.32 kB
import { expect } from 'chai'; import { Transaction, Address, PrivateKey, Script, Networks } from '../../src/btc'; import { Point, utils } from '../../src/crypto/secp256k1'; import { signCetWithAdaptorSig, verifyCetAdaptorSig, verifyCetSignature, sigToTaprootBuf } from '../../src/cet/signature'; import { createCet } from '../../src/cet/transactions'; import { adaptSig } from '../../src/crypto/counterparty'; import { commitToEvent, attestEventOutcome } from '../../src'; describe('CET Signature', () => { let borrowerKey: PrivateKey; let lenderKey: PrivateKey; let borrowerAddress: Address; let lenderAddress: Address; let dlcUtxo: any; let cet: Transaction; let oraclePrivKey: Uint8Array; let oraclePubKey: Point; let eventOutcomeHashes: Uint8Array[]; before(async () => { // Generate test keys oraclePrivKey = utils.randomPrivateKey(); oraclePubKey = Point.fromPrivateKey(oraclePrivKey); borrowerKey = new PrivateKey(); lenderKey = new PrivateKey(); borrowerAddress = new Address(borrowerKey.toPublicKey(), Networks.testnet, 'taproot'); lenderAddress = new Address(lenderKey.toPublicKey(), Networks.testnet, 'taproot'); // Generate test event outcome hashes eventOutcomeHashes = [ new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]) ]; // Create mock DLC UTXO dlcUtxo = { txId: 'a'.repeat(64), outputIndex: 0, satoshis: 100000, // 0.001 BTC script: new Script('') }; cet = createCet(dlcUtxo, 60000, 40000, borrowerAddress, lenderAddress); }); describe('signCetWithAdaptorSig and verifyCetAdaptorSig', () => { it('should create and verify a valid adaptor signature', async () => { const { signaturePoints, nonce } = await commitToEvent(eventOutcomeHashes, oraclePubKey); const outcomeIndex = 0; const counterpartyPrivKey = utils.randomPrivateKey(); const counterpartyPubKey = Point.fromPrivateKey(counterpartyPrivKey); const adaptorSig = await signCetWithAdaptorSig( counterpartyPrivKey, signaturePoints[outcomeIndex], cet, 0, Buffer.from('') ); expect(adaptorSig).to.be.instanceOf(Object); expect(adaptorSig).to.have.property('R_prime'); expect(adaptorSig).to.have.property('s_prime'); expect(adaptorSig.R_prime).to.be.instanceOf(Point); expect(typeof adaptorSig.s_prime).to.equal('bigint'); const isAdaptorSigValid = await verifyCetAdaptorSig( adaptorSig, counterpartyPubKey, signaturePoints[outcomeIndex], cet, 0, Buffer.from('') ); expect(isAdaptorSigValid).to.be.true; const oracleSig = await attestEventOutcome( oraclePrivKey, nonce, eventOutcomeHashes[outcomeIndex] ); const completedSig = adaptSig(adaptorSig, oracleSig.s); const isValid = await verifyCetSignature(completedSig, counterpartyPubKey, cet, 0, Buffer.from('')); expect(isValid).to.be.true; }); it('should detect invalid completed signatures', async () => { const { signaturePoints, nonce } = await commitToEvent(eventOutcomeHashes, oraclePubKey); const outcomeIndex = 0; const counterpartyPrivKey = utils.randomPrivateKey(); const counterpartyPubKey = Point.fromPrivateKey(counterpartyPrivKey); const adaptorSig = await signCetWithAdaptorSig( counterpartyPrivKey, signaturePoints[outcomeIndex], cet, 0, Buffer.from('') ); const isAdaptorSigValid = await verifyCetAdaptorSig( adaptorSig, counterpartyPubKey, signaturePoints[outcomeIndex], cet, 0, Buffer.from('') ); expect(isAdaptorSigValid).to.be.true; const oracleSig = await attestEventOutcome( oraclePrivKey, nonce, eventOutcomeHashes[1] // Different outcome ); const completedSig = adaptSig(adaptorSig, oracleSig.s); const isValid = await verifyCetSignature(completedSig, counterpartyPubKey, cet, 0, Buffer.from('')); expect(isValid).to.be.false; }); it('should detect signatures for wrong messages', async () => { const { signaturePoints, nonce } = await commitToEvent(eventOutcomeHashes, oraclePubKey); const outcomeIndex = 0; const counterpartyPrivKey = utils.randomPrivateKey(); const counterpartyPubKey = Point.fromPrivateKey(counterpartyPrivKey); const adaptorSig = await signCetWithAdaptorSig( counterpartyPrivKey, signaturePoints[outcomeIndex], cet, 0, Buffer.from('') ); const isAdaptorSigValid = await verifyCetAdaptorSig( adaptorSig, counterpartyPubKey, signaturePoints[outcomeIndex], cet, 0, Buffer.from('') ); expect(isAdaptorSigValid).to.be.true; const oracleSig = await attestEventOutcome( oraclePrivKey, nonce, eventOutcomeHashes[outcomeIndex] ); const completedSig = adaptSig(adaptorSig, oracleSig.s); const differentCet = createCet(dlcUtxo, 70000, 30000, borrowerAddress, lenderAddress); const isValid = await verifyCetSignature(completedSig, counterpartyPubKey, differentCet, 0, Buffer.from('')); expect(isValid).to.be.false; }); }); describe('sigToTaprootBuf', () => { it('should correctly serialize a Taproot signature with default sighash', () => { // Test vector from BIP341 test cases const R = new Point( BigInt('0x8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983'), BigInt('0x78fd9f664e274c9c2a2744197da522fdf1e3aba999b318e2587be098d90d4533') ); const signature = { R, s: BigInt('0x8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983') }; const serialized = sigToTaprootBuf(signature); expect(serialized.length).to.equal(64); expect(Buffer.from(serialized.slice(0, 32)).toString('hex')) .to.equal('8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983'); expect(Buffer.from(serialized.slice(32, 64)).toString('hex')) .to.equal('8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983'); }); it('should correctly serialize a Taproot signature with custom sighash', () => { const R = new Point( BigInt('0x8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983'), BigInt('0x78fd9f664e274c9c2a2744197da522fdf1e3aba999b318e2587be098d90d4533') ); const signature = { R, s: BigInt('0x8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983') }; const sighash = 0x01; // SIGHASH_ALL const serialized = sigToTaprootBuf(signature, sighash); expect(serialized.length).to.equal(65); expect(Buffer.from(serialized.slice(0, 32)).toString('hex')) .to.equal('8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983'); expect(Buffer.from(serialized.slice(32, 64)).toString('hex')) .to.equal('8608a76e87a5be42162284e8d7efc6cf71470351b36e07914fd0cfcb7beae983'); expect(serialized[64]).to.equal(0x01); }); }); });