UNPKG

@atomicport/bitcoin

Version:

Support Cross-Chain-Swap with HTLC on any blockchains

155 lines (154 loc) 7.13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BitcoinHtlc = void 0; const bip65_1 = __importDefault(require("bip65")); const Bitcoin_1 = __importDefault(require("./Bitcoin")); const bitcoinjs_lib_1 = require("bitcoinjs-lib"); /** * HTLC operations on the Bitcoin. */ class BitcoinHtlc extends Bitcoin_1.default { constructor(network) { super(network); } /** * Issue HTLC and obtain the key at the time of issue */ async lock(sender, receiver, secret, amount, options) { // set option paramater const fee = options?.fee || 1800; const lockHeight = options?.lockHeight || 2; const blockHeight = await this.getCurrentBlockHeight(); const timelock = bip65_1.default.encode({ blocks: blockHeight + lockHeight }); // generate contract const witnessScript = this.generateSwapWitnessScript(receiver.publicKey, sender.publicKey, secret, timelock); const p2wsh = bitcoinjs_lib_1.payments.p2wsh({ redeem: { output: witnessScript, network: this.network }, network: this.network, }); // get addresses const senderAddress = bitcoinjs_lib_1.payments.p2wpkh({ pubkey: sender.publicKey, network: this.network }).address; if (senderAddress == undefined || p2wsh.address == undefined) { throw new Error('senderAddress or contractAddress is undefined'); } // get balance const utxos = await this.getUtxos(senderAddress); if (!utxos || utxos.length <= 0) { throw new Error(`There was no UTXO currently available at the specified address ${senderAddress}.`); } // create transaction & announce const txHex = this.buildAndSignTx(sender, senderAddress, p2wsh.address, amount, fee, utxos); const hash = await this.postTransaction(txHex); return { hash, contractAddress: p2wsh.address, witnessScript: witnessScript.toString('hex'), }; } async withdraw(hash, contractAddress, witnessScript, receiver, proof, option) { // set option paramater const fee = option?.fee || 1800; const witnessUtxoValue = await this.getInputData(hash, contractAddress); const p2wpkh = bitcoinjs_lib_1.payments.p2wpkh({ pubkey: receiver.publicKey, network: this.network }); if (p2wpkh.address === undefined) throw new Error(`recieverAddress is undefined`); // transaction process const transaction = new bitcoinjs_lib_1.Psbt({ network: this.network }) .addInput({ hash, index: witnessUtxoValue.index, sequence: 0xfffffffe, witnessScript: Buffer.from(witnessScript, 'hex'), witnessUtxo: { script: Buffer.from('0020' + bitcoinjs_lib_1.crypto.sha256(Buffer.from(witnessScript, 'hex')).toString('hex'), 'hex'), value: witnessUtxoValue.value, }, }) .addOutput({ address: p2wpkh.address, value: witnessUtxoValue.value - fee, }) .signInput(0, receiver) .finalizeInput(0, (inputIndex, input, tapLeafHashToFinalize) => { const decompiled = bitcoinjs_lib_1.script.decompile(tapLeafHashToFinalize); if (!decompiled || decompiled[0] !== bitcoinjs_lib_1.opcodes.OP_HASH256) { throw new Error(`Can not finalize input #${inputIndex}`); } const witnessStackClaimBranch = bitcoinjs_lib_1.payments.p2wsh({ redeem: { input: bitcoinjs_lib_1.script.compile([input.partialSig[0].signature, Buffer.from(proof, 'hex')]), output: Buffer.from(witnessScript, 'hex'), }, }); return { finalScriptSig: undefined, finalScriptWitness: this.witnessStackToScriptWitness(witnessStackClaimBranch.witness), }; }) .extractTransaction(); console.log(`transaction id: ${transaction.getId()}`); await new Promise((ok) => { setTimeout(() => { ok(''); }, 10000); }); return await this.postTransaction(transaction.toHex()); } /** * Called by the sender if there was no withdraw AND the time lock has * expired. This will refund the contract amount. * @returns transaction hash */ async refund(hash, contractAddress, witnessScript, sender, option) { // set option paramater const fee = option?.fee || 1800; const decompiled = bitcoinjs_lib_1.script.decompile(Buffer.from(witnessScript, 'hex')); const witnessUtxoValue = await this.getInputData(hash, contractAddress); const p2wpkh = bitcoinjs_lib_1.payments.p2wpkh({ pubkey: sender.publicKey, network: this.network }); if (decompiled == null || decompiled[6] == null) throw new Error("script hasn't lock time"); if (p2wpkh.address === undefined) throw new Error(`recieverAddress is undefined`); const timelock = bip65_1.default.encode({ blocks: bitcoinjs_lib_1.script.number.decode(decompiled[6]) }); // transaction process const transaction = new bitcoinjs_lib_1.Psbt({ network: this.network }) .setLocktime(timelock) .addInput({ hash, index: witnessUtxoValue.index, sequence: 0xfffffffe, witnessScript: Buffer.from(witnessScript, 'hex'), witnessUtxo: { script: Buffer.from('0020' + bitcoinjs_lib_1.crypto.sha256(Buffer.from(witnessScript, 'hex')).toString('hex'), 'hex'), value: witnessUtxoValue.value, }, }) .addOutput({ address: p2wpkh.address, value: witnessUtxoValue.value - fee, }) .signInput(0, sender) .finalizeInput(0, (inputIndex, input, tapLeafHashToFinalize) => { const decompiled = bitcoinjs_lib_1.script.decompile(tapLeafHashToFinalize); if (!decompiled || decompiled[0] !== bitcoinjs_lib_1.opcodes.OP_HASH256) { throw new Error(`Can not finalize input #${inputIndex}`); } const witnessStackRefundBranch = bitcoinjs_lib_1.payments.p2wsh({ redeem: { input: bitcoinjs_lib_1.script.compile([input.partialSig[0].signature, Buffer.from('', 'hex')]), output: Buffer.from(witnessScript, 'hex'), }, }); return { finalScriptSig: undefined, finalScriptWitness: this.witnessStackToScriptWitness(witnessStackRefundBranch.witness), }; }) .extractTransaction(); return await this.postTransaction(transaction.toHex()); } } exports.BitcoinHtlc = BitcoinHtlc;