UNPKG

sensible-sdk

Version:

Sensible-SDK

197 lines (196 loc) 7.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TxComposer = exports.sighashType = void 0; const bsv = require("../bsv"); const utils_1 = require("../common/utils"); const scryptlib_1 = require("../scryptlib"); const Signature = bsv.crypto.Signature; exports.sighashType = Signature.SIGHASH_ALL | Signature.SIGHASH_FORKID; const P2PKH_UNLOCK_SIZE = 1 + 1 + 71 + 1 + 33; const P2PKH_DUST_AMOUNT = 1; class TxComposer { constructor(tx) { this.sigHashList = []; this.changeOutputIndex = -1; this.tx = tx || new bsv.Transaction(); } toObject() { let composer = { tx: this.tx.toObject(), sigHashList: this.sigHashList, changeOutputIndex: this.changeOutputIndex, }; return composer; } static fromObject(composerObj) { let txObj = composerObj.tx; let tx = new bsv.Transaction(); txObj.inputs.forEach((v) => { tx.addInput(new bsv.Transaction.Input(v)); }); txObj.outputs.forEach((v) => { tx.addOutput(new bsv.Transaction.Output(v)); }); tx.nLockTime = txObj.nLockTime; tx.version = txObj.version; let txComposer = new TxComposer(tx); txComposer.sigHashList = composerObj.sigHashList; txComposer.changeOutputIndex = composerObj.changeOutputIndex; return txComposer; } getRawHex() { return this.tx.serialize(true); } getTx() { return this.tx; } getTxId() { return this.tx.id; } getInput(inputIndex) { return this.tx.inputs[inputIndex]; } getOutput(outputIndex) { return this.tx.outputs[outputIndex]; } appendP2PKHInput(utxo) { this.tx.addInput(new bsv.Transaction.Input.PublicKeyHash({ output: new bsv.Transaction.Output({ script: bsv.Script.buildPublicKeyHashOut(utxo.address), satoshis: utxo.satoshis, }), prevTxId: utxo.txId, outputIndex: utxo.outputIndex, script: bsv.Script.empty(), })); const inputIndex = this.tx.inputs.length - 1; return inputIndex; } appendInput(input) { this.tx.addInput(new bsv.Transaction.Input({ output: new bsv.Transaction.Output({ script: input.lockingScript, satoshis: input.satoshis, }), prevTxId: input.txId, outputIndex: input.outputIndex, script: bsv.Script.empty(), })); const inputIndex = this.tx.inputs.length - 1; return inputIndex; } appendP2PKHOutput(output) { this.tx.addOutput(new bsv.Transaction.Output({ script: new bsv.Script(output.address), satoshis: output.satoshis, })); const outputIndex = this.tx.outputs.length - 1; return outputIndex; } appendOutput(output) { this.tx.addOutput(new bsv.Transaction.Output({ script: output.lockingScript, satoshis: output.satoshis, })); const outputIndex = this.tx.outputs.length - 1; return outputIndex; } appendOpReturnOutput(opreturnData) { this.tx.addOutput(new bsv.Transaction.Output({ script: bsv.Script.buildSafeDataOut(opreturnData), satoshis: 0, })); const outputIndex = this.tx.outputs.length - 1; return outputIndex; } clearChangeOutput() { if (this.changeOutputIndex != -1) { this.tx.outputs.splice(this.changeOutputIndex, 1); this.changeOutputIndex = 0; } } appendChangeOutput(changeAddress, feeb = 0.05, extraSize = 0) { //Calculate the fee and determine whether to change //If there is change, it will be output in the last item const unlockSize = this.tx.inputs.filter((v) => v.output.script.isPublicKeyHashOut()) .length * P2PKH_UNLOCK_SIZE; let fee = Math.ceil((this.tx.toBuffer().length + unlockSize + extraSize + bsv.Transaction.CHANGE_OUTPUT_MAX_SIZE) * feeb); let changeAmount = this.getUnspentValue() - fee; if (changeAmount >= P2PKH_DUST_AMOUNT) { this.changeOutputIndex = this.appendP2PKHOutput({ address: changeAddress, satoshis: changeAmount, }); } else { this.changeOutputIndex = -1; } return this.changeOutputIndex; } unlockP2PKHInput(privateKey, inputIndex, sigtype = exports.sighashType) { const tx = this.tx; const sig = new bsv.Transaction.Signature({ publicKey: privateKey.publicKey, prevTxId: tx.inputs[inputIndex].prevTxId, outputIndex: tx.inputs[inputIndex].outputIndex, inputIndex, signature: bsv.Transaction.Sighash.sign(tx, privateKey, sigtype, inputIndex, tx.inputs[inputIndex].output.script, tx.inputs[inputIndex].output.satoshisBN), sigtype, }); tx.inputs[inputIndex].setScript(bsv.Script.buildPublicKeyHashIn(sig.publicKey, sig.signature.toDER(), sig.sigtype)); } getTxFormatSig(privateKey, inputIndex, sigtype = exports.sighashType) { let sig = (0, scryptlib_1.signTx)(this.tx, privateKey, this.getInput(inputIndex).output.script.toASM(), this.getInput(inputIndex).output.satoshis, inputIndex, sigtype); return sig; } getInputPreimage(inputIndex, sigtype = exports.sighashType) { return (0, scryptlib_1.getPreimage)(this.tx, this.getInput(inputIndex).output.script.toASM(), this.getInput(inputIndex).output.satoshis, inputIndex, sigtype); } getUnspentValue() { const inputAmount = this.tx.inputs.reduce((pre, cur) => cur.output.satoshis + pre, 0); const outputAmount = this.tx.outputs.reduce((pre, cur) => cur.satoshis + pre, 0); let unspentAmount = inputAmount - outputAmount; return unspentAmount; } getFeeRate() { let unspent = this.getUnspentValue(); let txSize = this.tx.toBuffer().length; return unspent / txSize; } getSigHashLit() { this.sigHashList.forEach((v) => { v.sighash = (0, scryptlib_1.toHex)(bsv.Transaction.Sighash.sighash(this.tx, v.sighashType, v.inputIndex, this.getInput(v.inputIndex).output.script, this.getInput(v.inputIndex).output.satoshisBN)); }); return this.sigHashList; } addSigHashInfo({ inputIndex, address, sighashType, contractType, }) { this.sigHashList.push({ inputIndex, address, sighash: "", sighashType, contractType, }); } getPrevoutsHash() { let prevouts = Buffer.alloc(0); this.tx.inputs.forEach((input) => { const indexBuf = Buffer.alloc(4, 0); indexBuf.writeUInt32LE(input.outputIndex); prevouts = Buffer.concat([ prevouts, Buffer.from(input.prevTxId).reverse(), indexBuf, ]); }); return bsv.crypto.Hash.sha256sha256(prevouts).toString("hex"); } dumpTx(network) { (0, utils_1.dumpTx)(this.tx, network); } } exports.TxComposer = TxComposer;