UNPKG

@atomicport/bitcoin

Version:

Support Cross-Chain-Swap with HTLC on any blockchains

182 lines (181 loc) 6.76 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = __importDefault(require("axios")); const mempool_js_1 = __importDefault(require("@mempool/mempool.js")); const varuint_bitcoin_1 = __importDefault(require("varuint-bitcoin")); const bitcoinjs_lib_1 = require("bitcoinjs-lib"); const crypto_1 = require("crypto"); const ecpair_1 = __importDefault(require("ecpair")); const ecc = __importStar(require("tiny-secp256k1")); /** * bitcoin 系のコインのインターフェース */ class Bitcoin { mempool; network; baseUrl; constructor(network) { this.network = network; const networkStr = network === bitcoinjs_lib_1.networks.bitcoin ? 'bitcoin' : 'testnet'; this.mempool = (0, mempool_js_1.default)({ hostname: 'mempool.space', network: networkStr }).bitcoin; this.baseUrl = `https://mempool.space/${networkStr}`; } createHashPair() { const s = (0, crypto_1.randomBytes)(32); const p1 = (0, crypto_1.createHash)('sha256').update(s).digest(); const p2 = (0, crypto_1.createHash)('sha256').update(p1).digest(); return { proof: s.toString('hex'), secret: p2.toString('hex'), }; } async getCurrentBlockHeight() { return await this.mempool.blocks.getBlocksTipHeight(); } async postTransaction(txhex) { const endpoint = `${this.baseUrl}/api/tx`; return new Promise((resolve, reject) => { axios_1.default .post(endpoint, txhex) .then((res) => { resolve(res.data); }) .catch((error) => { reject(error); }); }); } async getInputData(txid, contractAddress) { const txInfo = await this.mempool.transactions.getTx({ txid }); let value = 0; let index = 0; for (let i = 0; i < txInfo.vout.length; i++) { if (txInfo.vout[i].scriptpubkey_address == contractAddress) { value = txInfo.vout[i].value; index = i; } } return { value, index }; } async getUtxos(address) { const utxosData = await this.mempool.addresses.getAddressTxsUtxo({ address }); const utxos = []; for (let i = 0; i < utxosData.length; i++) { const hash = utxosData[i].txid; const index = utxosData[i].vout; const value = utxosData[i].value; utxos.push({ hash, index, value, }); } return utxos; } buildAndSignTx(sender, address, recipient, sendingSat, feeSat, utxos) { const psbt = new bitcoinjs_lib_1.Psbt({ network: this.network }); let total = 0; const pubKeyHash = bitcoinjs_lib_1.crypto.hash160(sender.publicKey).toString('hex'); for (let len = utxos.length, i = 0; i < len; i++) { psbt.addInput({ hash: utxos[i].hash, index: utxos[i].index, witnessUtxo: { script: Buffer.from('0014' + pubKeyHash, 'hex'), value: utxos[i].value, }, }); total += utxos[i].value; } psbt.addOutput({ address: recipient, value: sendingSat, }); const changeSat = total - sendingSat - feeSat; if (changeSat < 0) { throw new Error(`Balance is insufficient. Balance (UTXO Total): ${total} satoshi`); } psbt.addOutput({ address: address, value: changeSat, }); for (let len = utxos.length, i = 0; i < len; i++) { psbt.signInput(i, sender); psbt.validateSignaturesOfInput(i, (pubkey, msghash, signature) => { return (0, ecpair_1.default)(ecc).fromPublicKey(pubkey).verify(msghash, signature); }); } psbt.finalizeAllInputs(); return psbt.extractTransaction().toHex(); } witnessStackToScriptWitness(witness) { let buffer = Buffer.allocUnsafe(0); function writeSlice(slice) { buffer = Buffer.concat([buffer, Buffer.from(slice)]); } function writeVarInt(i) { const currentLen = buffer.length; const varintLen = varuint_bitcoin_1.default.encodingLength(i); buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]); varuint_bitcoin_1.default.encode(i, buffer, currentLen); } function writeVarSlice(slice) { writeVarInt(slice.length); writeSlice(slice); } function writeVector(vector) { writeVarInt(vector.length); vector.forEach(writeVarSlice); } writeVector(witness); return buffer; } /** * Generate HTLC Contract Script for Bitcoin */ generateSwapWitnessScript(receiverPublicKey, userRefundPublicKey, paymentHash, timelock) { return bitcoinjs_lib_1.script.fromASM(` OP_HASH256 ${paymentHash} OP_EQUAL OP_IF ${receiverPublicKey.toString('hex')} OP_ELSE ${bitcoinjs_lib_1.script.number.encode(timelock).toString('hex')} OP_CHECKLOCKTIMEVERIFY OP_DROP ${userRefundPublicKey.toString('hex')} OP_ENDIF OP_CHECKSIG ` .trim() .replace(/\s+/g, ' ')); } } exports.default = Bitcoin;