UNPKG

@node-dlc/bitcoin

Version:
238 lines 9.46 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TxBuilder = void 0; const bufio_1 = require("@node-dlc/bufio"); const crypto_1 = require("@node-dlc/crypto"); const LockTime_1 = require("./LockTime"); const OutPoint_1 = require("./OutPoint"); const Script_1 = require("./Script"); const Tx_1 = require("./Tx"); const TxIn_1 = require("./TxIn"); const TxOut_1 = require("./TxOut"); const Value_1 = require("./Value"); class TxBuilder { constructor() { this._inputs = []; this._outputs = []; this._version = 2; this._locktime = new LockTime_1.LockTime(); } /** * Gets or sets the transaction version. Valid transaction versions * are > 1. */ get version() { return this._version; } set version(val) { this._version = val; } /** * Gets or sets the absolute locktime for the transaction */ get locktime() { return this._locktime; } set locktime(val) { this._locktime = val; } /** * Gets the inputs */ get inputs() { return this._inputs; } /** * Gets the outputs */ get outputs() { return this._outputs; } /** * Adds a new transaction input * @param outpoint the previous output represented as an outpoint */ addInput(outpoint, sequence) { if (outpoint instanceof TxIn_1.TxIn) { this._inputs.push(outpoint.clone()); } else { outpoint = outpoint instanceof OutPoint_1.OutPoint ? outpoint : OutPoint_1.OutPoint.fromString(outpoint); this._inputs.push(new TxIn_1.TxIn(outpoint, undefined, sequence)); } } /** * Adds a transaction output * @param value value sent to the lock script. When represented as a * number, the value is in Bitcoin. * @param scriptPubKey the locking script encumbering the funds send * to this output */ addOutput(value, scriptPubKey) { if (value instanceof TxOut_1.TxOut) { this._outputs.push(value.clone()); } else { value = value instanceof Value_1.Value ? value : Value_1.Value.fromBitcoin(value); this._outputs.push(new TxOut_1.TxOut(value, scriptPubKey)); } } /** * Creates a signature hash including all inputs and all outputs, * which is referred to as SIGHASH_ALL. The scriptSig of all inputs * is removed (as it is never signed), however we commit to the * signatory input using the scriptPubKey from the prevOut or the * redeemScript. The hash is constructed as the serialization of * all information (with the input scriptSig replaced as just * described) and then appending a 4-byte LE sighash type. We then * take the hash256 of that serialized transaction. * * @param input signatory input index * @param commitScript the scriptSig used for the signature input */ hashLegacy(input, commitScript) { const writer = new bufio_1.BufferWriter(); // write the version writer.writeUInt32LE(this.version); // sign all inputs as sorted by the sorting function const inputs = this._inputs; writer.writeVarInt(inputs.length); for (let i = 0; i < inputs.length; i++) { // blank out scriptSig for non-signatory inputs let scriptSig = new Script_1.Script(); // use the commit script for signatory input if (i === input) { scriptSig = commitScript; } // write the input const vin = new TxIn_1.TxIn(inputs[i].outpoint, scriptSig, inputs[i].sequence); writer.writeBytes(vin.serialize()); } // sign all outputs as sorted by the sorting function const outputs = this._outputs; writer.writeVarInt(outputs.length); for (const vout of outputs) { writer.writeBytes(vout.serialize()); } // write the sequence writer.writeBytes(this.locktime.serialize()); // write the sighash type 0x01 as 4-bytes little endian writer.writeUInt32LE(1); // return hashed value return (0, crypto_1.hash256)(writer.toBuffer()); } /** * Creates a signature hash using the new segregated witness digets * alorithm defined in BIP143. The current version only supports * SIGHASH_ALL and does not account for OP_CODESEPARATOR. * * This algorithm has side-effects in that it caches hashPrevOut, * hashSequence, and hashOutput values used. This means transaction * should not change after signing, though the code does not yet * enforce this. * * @param index signatory input index * @param commitScript the scriptSig used for the signature input * @param value the value of the input */ hashSegwitv0(index, commitScript, value) { const writer = new bufio_1.BufferWriter(); // Combines the previous outputs for all inputs in the // transaction by serializing and hash256 the concated values: // prevtx: 32-byte IBO // prevIdx: 4-byte LE if (this._hashPrevOuts === undefined) { const hashWriter = new bufio_1.BufferWriter(Buffer.alloc(this._inputs.length * 36)); for (const input of this._inputs) { hashWriter.writeBytes(input.outpoint.serialize()); } this._hashPrevOuts = (0, crypto_1.hash256)(hashWriter.toBuffer()); } // Combines the nSequence values for all inputs in the // transaction and then hash256 the values if (this._hashSequence === undefined) { const hashWriter = new bufio_1.BufferWriter(Buffer.alloc(this._inputs.length * 4)); for (const input of this._inputs) { hashWriter.writeBytes(input.sequence.serialize()); } this._hashSequence = (0, crypto_1.hash256)(hashWriter.toBuffer()); } // Combines the outputs for the transaction according by // concatenating the serialization of the outputs into a single // byte array and then hash256 the values. if (this._hashOutputs === undefined) { const hashWriter = new bufio_1.BufferWriter(); for (const vout of this._outputs) { hashWriter.writeBytes(vout.serialize()); } this._hashOutputs = (0, crypto_1.hash256)(hashWriter.toBuffer()); } writer.writeUInt32LE(this.version); writer.writeBytes(this._hashPrevOuts); writer.writeBytes(this._hashSequence); const vin = this._inputs[index]; writer.writeBytes(vin.outpoint.serialize()); writer.writeBytes(commitScript.serialize()); writer.writeUInt64LE(value.sats); writer.writeBytes(vin.sequence.serialize()); writer.writeBytes(this._hashOutputs); writer.writeBytes(this.locktime.serialize()); writer.writeUInt32LE(1); // SIGHASH_ALL return (0, crypto_1.hash256)(writer.toBuffer()); } /** * Signs an input and returns the DER encoded signature. The * script that is committed to will depend on the type of the * signature. This is usually the locking script used in the prior * output, but in the case of p2sh transactions, this is the * redeem script, or the underlying script that is hashed in the * prior output. * * @param input index of input that should be signed * @param commitScript Script that is committed during signature * @param privateKey 32-byte private key */ sign(input, commitScript, privateKey) { // create the hash of the transaction for the input const hash = this.hashLegacy(input, commitScript); // sign DER encode signature const sig = (0, crypto_1.sign)(hash, privateKey); const der = (0, crypto_1.sigToDER)(sig); // return signature with 1-byte sighash type return Buffer.concat([der, Buffer.from([1])]); } /** * Signs an SegWit v0 input and returns the DER encoded signature. * The script that is committed to will depend on the type of the * input. This is usually the locking script or redeem script. * * @param input index of input that should be signed * @param commitScript Script that is committed during signature * @param privateKey 32-byte private key * @param value value of the prior input */ signSegWitv0(input, commitScript, privateKey, value) { // create the hash of the transaction for the input const hash = this.hashSegwitv0(input, commitScript, value); // sign DER encode signature const sig = (0, crypto_1.sign)(hash, privateKey); const der = (0, crypto_1.sigToDER)(sig); // return signature with 1-byte sighash type return Buffer.concat([der, Buffer.from([1])]); } /** * Returns an immutable transaction */ toTx() { return new Tx_1.Tx(this.version, this._inputs.map((vin) => vin.clone()), this._outputs.map((vout) => vout.clone()), this.locktime.clone()); } serialize() { return this.toTx().serialize(); } toHex(pretty = false) { return this.toTx().toHex(pretty); } } exports.TxBuilder = TxBuilder; //# sourceMappingURL=TxBuilder.js.map