UNPKG

bitcoin-tx-lib

Version:

A Typescript library for building and signing Bitcoin transactions

162 lines 9.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Transaction = void 0; const txbase_1 = require("./base/txbase"); const opcodes_1 = require("./constants/opcodes"); const utils_1 = require("./utils"); const txutils_1 = require("./utils/txutils"); class Transaction extends txbase_1.BaseTransaction { constructor(pairkey, options) { var _a, _b; super(pairkey); this.version = (_a = options === null || options === void 0 ? void 0 : options.version) !== null && _a !== void 0 ? _a : 2; this.locktime = (_b = options === null || options === void 0 ? void 0 : options.locktime) !== null && _b !== void 0 ? _b : 0; } build(format = "raw") { let witnessData = ""; let hexTransaction = String((0, utils_1.numberToHexLE)(this.version, 32, "hex")); // version if (this.isSegwit() && format != "txid") // Marker and Flag for SegWit transactions hexTransaction += (0, utils_1.bytesToHex)(new Uint8Array([0x00, 0x01])); //"00" + "01"; hexTransaction += String((0, utils_1.numberToVarTnt)(this.inputs.length, "hex")); // number of inputs this.inputs.forEach(input => { var _a; hexTransaction += (0, utils_1.reverseEndian)(input.txid); // txid hexTransaction += String((0, utils_1.numberToHexLE)(input.vout, 32, "hex")); // index output (vout) if (this.isSegwitInput(input)) { witnessData += String(this.generateWitness(input, "hex")); hexTransaction += "00"; // script sig in witness area // P2WPKH } else { let scriptSig = String(this.generateScriptSig(input, "hex")); let scriptSigLength = String((0, utils_1.numberToHexLE)((0, utils_1.getBytesCount)(scriptSig), 8, "hex")); hexTransaction += scriptSigLength.concat(scriptSig); witnessData += "00"; // no witness, only scriptSig } // 0xfffffffd Replace By Fee (RBF) enabled BIP 125 hexTransaction += (_a = input.sequence) !== null && _a !== void 0 ? _a : (0, utils_1.reverseEndian)("fffffffd"); // 0xfffffffd }); hexTransaction += String((0, utils_1.numberToVarTnt)(this.outputs.length, "hex")); // number of outputs hexTransaction += this.outputsRaw(); // amount+scriptpubkey if (this.isSegwit() && format != "txid") hexTransaction += witnessData; hexTransaction += String((0, utils_1.numberToHexLE)(this.locktime, 32, "hex")); // locktime return hexTransaction; } getTxid() { let hexTransaction = this.build("txid"); let hash = String((0, utils_1.hash256)(hexTransaction)); return String((0, utils_1.reverseEndian)(hash)); } generateScriptSig(inputSig, resultType) { let hexTransaction = String((0, utils_1.numberToHexLE)(this.version, 32, "hex")); // version hexTransaction += String((0, utils_1.numberToVarTnt)(this.inputs.length, "hex")); // number of inputs this.inputs.forEach(input => { var _a; hexTransaction += String((0, utils_1.reverseEndian)(input.txid)); // txid hexTransaction += String((0, utils_1.numberToHexLE)(input.vout, 32, "hex")); // index output (vout) if (input.txid === inputSig.txid) { let scriptLength = (0, utils_1.hexToBytes)(input.scriptPubKey).length; hexTransaction += String((0, utils_1.numberToVarTnt)(scriptLength, "hex")); hexTransaction += input.scriptPubKey; } else hexTransaction += "00"; // length 0x00 to sign // 0xfffffffd Replace By Fee (RBF) enabled BIP 125 hexTransaction += (_a = input.sequence) !== null && _a !== void 0 ? _a : (0, utils_1.reverseEndian)("fffffffd"); }); hexTransaction += String((0, utils_1.numberToVarTnt)(this.outputs.length, "hex")); // number of outputs hexTransaction += this.outputsRaw(); hexTransaction += String((0, utils_1.numberToHexLE)(this.locktime, 32, "hex")); // locktime hexTransaction += String((0, utils_1.numberToHexLE)(opcodes_1.OP_CODES.SIGHASH_ALL, 32, "hex")); let sigHash = String((0, utils_1.hash256)(hexTransaction)); // hash256 -> sha256(sha256(content)) let signature = String(this.pairKey.signDER(sigHash)); signature += String((0, utils_1.numberToHexLE)(opcodes_1.OP_CODES.SIGHASH_ALL, 8, "hex")); let signatureLength = String((0, utils_1.numberToHex)((0, utils_1.getBytesCount)(signature), 8, "hex")); let publicKey = this.pairKey.getPublicKeyCompressed("hex"); let publicKeyLength = String((0, utils_1.numberToHex)((0, utils_1.getBytesCount)(publicKey), 8, "hex")); let scriptSig = signatureLength.concat(signature, publicKeyLength, publicKey); if (resultType == "hex") return scriptSig; return (0, utils_1.hexToBytes)(scriptSig); } generateWitness(input, resultType = "hex") { var _a; let hexTransaction = String((0, utils_1.numberToHexLE)(this.version, 32, "hex")); // version // hashPrevouts let prevouts = this.inputs.map(input => { let vout = String((0, utils_1.numberToHexLE)(input.vout, 32, "hex")); // index output (vout) let txid = String((0, utils_1.reverseEndian)(input.txid)); // txid return txid.concat(vout); }).join(""); let hashPrevouts = (0, utils_1.hash256)(prevouts); hexTransaction += hashPrevouts; // hashSequence let sequence = this.inputs.map(input => { var _a; return (_a = input.sequence) !== null && _a !== void 0 ? _a : (0, utils_1.reverseEndian)("fffffffd"); }).join(""); let hashSequence = (0, utils_1.hash256)(sequence); hexTransaction += hashSequence; // out point hexTransaction += String((0, utils_1.reverseEndian)(input.txid)); hexTransaction += String((0, utils_1.numberToHexLE)(input.vout, 32, "hex")); // script code let scriptCode = (0, txutils_1.scriptPubkeyToScriptCode)(input.scriptPubKey); hexTransaction += scriptCode; // amount hexTransaction += String((0, utils_1.numberToHexLE)(input.value, 64, "hex")); // sequence // 0xfffffffd Replace By Fee (RBF) enabled BIP 125 hexTransaction += (_a = input.sequence) !== null && _a !== void 0 ? _a : (0, utils_1.reverseEndian)("fffffffd"); // hashOutputs let hashOutputs = (0, utils_1.hash256)(this.outputsRaw()); hexTransaction += hashOutputs; hexTransaction += String((0, utils_1.numberToHexLE)(this.locktime, 32, "hex")); // locktime hexTransaction += String((0, utils_1.numberToHexLE)(opcodes_1.OP_CODES.SIGHASH_ALL, 32, "hex")); // sighash let sigHash = String((0, utils_1.hash256)(hexTransaction)); // hash256 -> sha256(sha256(content)) let signature = String(this.pairKey.signDER(sigHash)); signature += String((0, utils_1.numberToHex)(opcodes_1.OP_CODES.SIGHASH_ALL, 8, "hex")); let signatureLength = String((0, utils_1.numberToVarTnt)((0, utils_1.getBytesCount)(signature), "hex")); let publicKey = this.pairKey.getPublicKeyCompressed("hex"); let publicKeyLength = String((0, utils_1.numberToVarTnt)((0, utils_1.getBytesCount)(publicKey), "hex")); let itemCount = String((0, utils_1.numberToHex)(2, 8, "hex")); // 2 items(signature & pubkey) 0x02 let scriptSig = itemCount.concat(signatureLength, signature, publicKeyLength, publicKey); if (resultType == "hex") return scriptSig; return (0, utils_1.hexToBytes)(scriptSig); } isSegwit() { return this.inputs.some(this.isSegwitInput); } isSegwitInput(input) { const bytes = (0, utils_1.hexToBytes)(input.scriptPubKey); return ((bytes.length === 22 && bytes[0] == 0x00 && bytes[1] == 0x14) || // P2WPKH (bytes.length === 34 && bytes[0] == 0x00 && bytes[1] == 0x20)); // P2WSH } // docs https://learnmeabitcoin.com/technical/transaction/size/ weight() { // witness marker and flag * 1 let witnessMK = 0; // 2 bytes of marker and flag 0x00+0x01 = 2 bytes * 1 if (this.isSegwit()) witnessMK = 2; let hexTransaction = this.build(); let witnessInputs = this.inputs.filter(this.isSegwitInput); // witness size * 1 let witnessSize = witnessInputs.reduce((sum, input) => { let witness = String(this.generateWitness(input)); return sum + (0, utils_1.getBytesCount)(witness); }, 0); // discount the size of the witness fields and multiply by 4 let transactionSize = (0, utils_1.getBytesCount)(hexTransaction); transactionSize = (transactionSize - (witnessSize + witnessMK)) * 4; transactionSize = transactionSize + (witnessSize + witnessMK); // * 1 return Math.ceil(transactionSize); } // docs https://learnmeabitcoin.com/technical/transaction/size/ vBytes() { return Math.ceil(this.weight() / 4); } clear() { this.inputs = []; this.outputs = []; } } exports.Transaction = Transaction; //# sourceMappingURL=transaction.js.map