"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const networks = require("./networks");
const bufferutils_1 = require("./bufferutils");
const transaction_1 = require("./transaction");
const ECPair = require("./ecpair");
const types = require("./types");
const baddress = require("./address");
const bcrypto = require("./crypto");
const bscript = require("./script");
const payments = require("./payments");
const classify = require("./classify");
const script_1 = require("./script");
const typeforce = require('typeforce');
const SCRIPT_TYPES = classify.types;
function txIsString(tx) {
    return typeof tx === 'string' || tx instanceof String;
}
function txIsTransaction(tx) {
    return tx instanceof transaction_1.Transaction;
}
class TransactionBuilder {
    constructor(network, maximumFeeRate) {
        this.__prevTxSet = {};
        this.network = network || networks.bitcoin;
        // WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth)
        this.maximumFeeRate = maximumFeeRate || 2500;
        this.__inputs = [];
        this.__tx = new transaction_1.Transaction();
        this.__tx.version = 2;
    }
    static fromTransaction(transaction, network) {
        const txb = new TransactionBuilder(network);
        // Copy transaction fields
        txb.setVersion(transaction.version);
        txb.setLockTime(transaction.locktime);
        // Copy outputs (done first to avoid signature invalidation)
        transaction.outs.forEach(txOut => {
            txb.addOutput(txOut.script, txOut.value);
        });
        // Copy inputs
        transaction.ins.forEach(txIn => {
            txb.__addInputUnsafe(txIn.hash, txIn.index, {
                sequence: txIn.sequence,
                script: txIn.script,
                witness: txIn.witness
            });
        });
        // fix some things not possible through the public API
        txb.__inputs.forEach((input, i) => {
            fixMultisigOrder(input, transaction, i);
        });
        return txb;
    }
    setLockTime(locktime) {
        typeforce(types.UInt32, locktime);
        // if any signatures exist, throw
        if (this.__inputs.some(input => {
            if (!input.signatures)
                return false;
            return input.signatures.some(s => s !== undefined);
        })) {
            throw new Error('No, this would invalidate signatures');
        }
        this.__tx.locktime = locktime;
    }
    setVersion(version) {
        typeforce(types.UInt32, version);
        // XXX: this might eventually become more complex depending on what the versions represent
        this.__tx.version = version;
    }
    addInput(txHash, vout, sequence, prevOutScript) {
        if (!this.__canModifyInputs()) {
            throw new Error('No, this would invalidate signatures');
        }
        let value = undefined;
        // is it a hex string?
        if (txIsString(txHash)) {
            // transaction hashs's are displayed in reverse order, un-reverse it
            txHash = bufferutils_1.reverseBuffer(Buffer.from(txHash, 'hex'));
            // is it a Transaction object?
        }
        else if (txIsTransaction(txHash)) {
            const txOut = txHash.outs[vout];
            prevOutScript = txOut.script;
            value = txOut.value;
            txHash = txHash.getHash(false);
        }
        return this.__addInputUnsafe(txHash, vout, {
            sequence: sequence,
            prevOutScript: prevOutScript,
            value: value
        });
    }
    __addInputUnsafe(txHash, vout, options) {
        if (transaction_1.Transaction.isCoinbaseHash(txHash)) {
            throw new Error('coinbase inputs not supported');
        }
        const prevTxOut = txHash.toString('hex') + ':' + vout;
        if (this.__prevTxSet[prevTxOut] !== undefined)
            throw new Error('Duplicate TxOut: ' + prevTxOut);
        let input = {};
        // derive what we can from the scriptSig
        if (options.script !== undefined) {
            input = expandInput(options.script, options.witness || []);
        }
        // if an input value was given, retain it
        if (options.value !== undefined) {
            input.value = options.value;
        }
        // derive what we can from the previous transactions output script
        if (!input.prevOutScript && options.prevOutScript) {
            let prevOutType;
            if (!input.pubkeys && !input.signatures) {
                const expanded = expandOutput(options.prevOutScript);
                if (expanded.pubkeys) {
                    input.pubkeys = expanded.pubkeys;
                    input.signatures = expanded.signatures;
                }
                prevOutType = expanded.type;
            }
            input.prevOutScript = options.prevOutScript;
            input.prevOutType = prevOutType || classify.output(options.prevOutScript);
        }
        const vin = this.__tx.addInput(txHash, vout, options.sequence, options.scriptSig);
        this.__inputs[vin] = input;
        this.__prevTxSet[prevTxOut] = true;
        return vin;
    }
    addOutput(scriptPubKey, value) {
        if (!this.__canModifyOutputs()) {
            throw new Error('No, this would invalidate signatures');
        }
        // Attempt to get a script if it's a base58 or bech32 address string
        if (typeof scriptPubKey === 'string') {
            scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network);
        }
        return this.__tx.addOutput(scriptPubKey, value);
    }
    build() {
        return this.__build(false);
    }
    buildIncomplete() {
        return this.__build(true);
    }
    __build(allowIncomplete) {
        if (!allowIncomplete) {
            if (!this.__tx.ins.length)
                throw new Error('Transaction has no inputs');
            if (!this.__tx.outs.length)
                throw new Error('Transaction has no outputs');
        }
        const tx = this.__tx.clone();
        // create script signatures from inputs
        this.__inputs.forEach((input, i) => {
            if (!input.prevOutType && !allowIncomplete)
                throw new Error('Transaction is not complete');
            const result = build(input.prevOutType, input, allowIncomplete);
            if (!result) {
                if (!allowIncomplete && input.prevOutType === SCRIPT_TYPES.NONSTANDARD)
                    throw new Error('Unknown input type');
                if (!allowIncomplete)
                    throw new Error('Not enough information');
                return;
            }
            tx.setInputScript(i, result.input);
            tx.setWitness(i, result.witness);
        });
        if (!allowIncomplete) {
            // do not rely on this, its merely a last resort
            if (this.__overMaximumFees(tx.virtualSize())) {
                throw new Error('Transaction has absurd fees');
            }
        }
        return tx;
    }
    sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) {
        // TODO: remove keyPair.network matching in 4.0.0
        if (keyPair.network && keyPair.network !== this.network)
            throw new TypeError('Inconsistent network');
        if (!this.__inputs[vin])
            throw new Error('No input at index: ' + vin);
        hashType = hashType || transaction_1.Transaction.SIGHASH_ALL;
        if (this.__needsOutputs(hashType))
            throw new Error('Transaction needs outputs');
        const input = this.__inputs[vin];
        // if redeemScript was previously provided, enforce consistency
        if (input.redeemScript !== undefined &&
            redeemScript &&
            !input.redeemScript.equals(redeemScript)) {
            throw new Error('Inconsistent redeemScript');
        }
        const ourPubKey = keyPair.publicKey || keyPair.getPublicKey();
        if (!canSign(input)) {
            if (witnessValue !== undefined) {
                if (input.value !== undefined && input.value !== witnessValue)
                    throw new Error('Input didn\'t match witnessValue');
                typeforce(types.Satoshi, witnessValue);
                input.value = witnessValue;
            }
            if (!canSign(input)) {
                const prepared = prepareInput(input, ourPubKey, redeemScript, witnessScript);
                // updates inline
                Object.assign(input, prepared);
            }
            if (!canSign(input))
                throw Error(input.prevOutType + ' not supported');
        }
        // ready to sign
        let signatureHash;
        if (input.hasWitness) {
            signatureHash = this.__tx.hashForWitnessV0(vin, input.signScript, input.value, hashType);
        }
        else {
            signatureHash = this.__tx.hashForSignature(vin, input.signScript, hashType);
        }
        // enforce in order signing of public keys
        const signed = input.pubkeys.some((pubKey, i) => {
            if (!ourPubKey.equals(pubKey))
                return false;
            if (input.signatures[i])
                throw new Error('Signature already exists');
            // TODO: add tests
            if (ourPubKey.length !== 33 && input.hasWitness) {
                throw new Error('BIP143 rejects uncompressed public keys in P2WPKH or P2WSH');
            }
            const signature = keyPair.sign(signatureHash);
            input.signatures[i] = bscript.signature.encode(signature, hashType);
            return true;
        });
        if (!signed)
            throw new Error('Key pair cannot sign for this input');
    }
    __canModifyInputs() {
        return this.__inputs.every(input => {
            if (!input.signatures)
                return true;
            return input.signatures.every(signature => {
                if (!signature)
                    return true;
                const hashType = signatureHashType(signature);
                // if SIGHASH_ANYONECANPAY is set, signatures would not
                // be invalidated by more inputs
                return (hashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY) !== 0;
            });
        });
    }
    __needsOutputs(signingHashType) {
        if (signingHashType === transaction_1.Transaction.SIGHASH_ALL) {
            return this.__tx.outs.length === 0;
        }
        // if inputs are being signed with SIGHASH_NONE, we don't strictly need outputs
        // .build() will fail, but .buildIncomplete() is OK
        return (this.__tx.outs.length === 0) && this.__inputs.some((input) => {
            if (!input.signatures)
                return false;
            return input.signatures.some((signature) => {
                if (!signature)
                    return false; // no signature, no issue
                const hashType = signatureHashType(signature);
                if (hashType & transaction_1.Transaction.SIGHASH_NONE)
                    return false; // SIGHASH_NONE doesn't care about outputs
                return true; // SIGHASH_* does care
            });
        });
    }
    __canModifyOutputs() {
        const nInputs = this.__tx.ins.length;
        const nOutputs = this.__tx.outs.length;
        return this.__inputs.every(input => {
            if (input.signatures === undefined)
                return true;
            return input.signatures.every(signature => {
                if (!signature)
                    return true;
                const hashType = signatureHashType(signature);
                const hashTypeMod = hashType & 0x1f;
                if (hashTypeMod === transaction_1.Transaction.SIGHASH_NONE)
                    return true;
                if (hashTypeMod === transaction_1.Transaction.SIGHASH_SINGLE) {
                    // if SIGHASH_SINGLE is set, and nInputs > nOutputs
                    // some signatures would be invalidated by the addition
                    // of more outputs
                    return nInputs <= nOutputs;
                }
                return false;
            });
        });
    }
    __overMaximumFees(bytes) {
        // not all inputs will have .value defined
        const incoming = this.__inputs.reduce((a, x) => a + (x.value >>> 0), 0);
        // but all outputs do, and if we have any input value
        // we can immediately determine if the outputs are too small
        const outgoing = this.__tx.outs.reduce((a, x) => a + x.value, 0);
        const fee = incoming - outgoing;
        const feeRate = fee / bytes;
        return feeRate > this.maximumFeeRate;
    }
}
exports.TransactionBuilder = TransactionBuilder;
function expandInput(scriptSig, witnessStack, type, scriptPubKey) {
    if (scriptSig.length === 0 && witnessStack.length === 0)
        return {};
    if (!type) {
        let ssType = classify.input(scriptSig, true);
        let wsType = classify.witness(witnessStack, true);
        if (ssType === SCRIPT_TYPES.NONSTANDARD)
            ssType = undefined;
        if (wsType === SCRIPT_TYPES.NONSTANDARD)
            wsType = undefined;
        type = ssType || wsType;
    }
    switch (type) {
        case SCRIPT_TYPES.P2WPKH: {
            const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack });
            return {
                prevOutScript: output,
                prevOutType: SCRIPT_TYPES.P2WPKH,
                pubkeys: [pubkey],
                signatures: [signature]
            };
        }
        case SCRIPT_TYPES.P2PKH: {
            const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig });
            return {
                prevOutScript: output,
                prevOutType: SCRIPT_TYPES.P2PKH,
                pubkeys: [pubkey],
                signatures: [signature]
            };
        }
        case SCRIPT_TYPES.P2PK: {
            const { signature } = payments.p2pk({ input: scriptSig });
            return {
                prevOutType: SCRIPT_TYPES.P2PK,
                pubkeys: [undefined],
                signatures: [signature]
            };
        }
        case SCRIPT_TYPES.P2MS: {
            const { m, pubkeys, signatures } = payments.p2ms({
                input: scriptSig,
                output: scriptPubKey
            }, { allowIncomplete: true });
            return {
                prevOutType: SCRIPT_TYPES.P2MS,
                pubkeys: pubkeys,
                signatures: signatures,
                maxSignatures: m
            };
        }
    }
    if (type === SCRIPT_TYPES.P2SH) {
        const { output, redeem } = payments.p2sh({
            input: scriptSig,
            witness: witnessStack
        });
        const outputType = classify.output(redeem.output);
        const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output);
        if (!expanded.prevOutType)
            return {};
        return {
            prevOutScript: output,
            prevOutType: SCRIPT_TYPES.P2SH,
            redeemScript: redeem.output,
            redeemScriptType: expanded.prevOutType,
            witnessScript: expanded.witnessScript,
            witnessScriptType: expanded.witnessScriptType,
            pubkeys: expanded.pubkeys,
            signatures: expanded.signatures
        };
    }
    if (type === SCRIPT_TYPES.P2WSH) {
        const { output, redeem } = payments.p2wsh({
            input: scriptSig,
            witness: witnessStack
        });
        const outputType = classify.output(redeem.output);
        let expanded;
        if (outputType === SCRIPT_TYPES.P2WPKH) {
            expanded = expandInput(redeem.input, redeem.witness, outputType);
        }
        else {
            expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output);
        }
        if (!expanded.prevOutType)
            return {};
        return {
            prevOutScript: output,
            prevOutType: SCRIPT_TYPES.P2WSH,
            witnessScript: redeem.output,
            witnessScriptType: expanded.prevOutType,
            pubkeys: expanded.pubkeys,
            signatures: expanded.signatures
        };
    }
    return {
        prevOutType: SCRIPT_TYPES.NONSTANDARD,
        prevOutScript: scriptSig
    };
}
// could be done in expandInput, but requires the original Transaction for hashForSignature
function fixMultisigOrder(input, transaction, vin) {
    if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript)
        return;
    if (input.pubkeys.length === input.signatures.length)
        return;
    const unmatched = input.signatures.concat();
    input.signatures = input.pubkeys.map(pubKey => {
        const keyPair = ECPair.fromPublicKey(pubKey);
        let match;
        // check for a signature
        unmatched.some((signature, i) => {
            // skip if undefined || OP_0
            if (!signature)
                return false;
            // TODO: avoid O(n) hashForSignature
            const parsed = bscript.signature.decode(signature);
            const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType);
            // skip if signature does not match pubKey
            if (!keyPair.verify(hash, parsed.signature))
                return false;
            // remove matched signature from unmatched
            unmatched[i] = undefined;
            match = signature;
            return true;
        });
        return match;
    });
}
function expandOutput(script, ourPubKey) {
    typeforce(types.Buffer, script);
    const type = classify.output(script);
    switch (type) {
        case SCRIPT_TYPES.P2PKH: {
            if (!ourPubKey)
                return { type };
            // does our hash160(pubKey) match the output scripts?
            const pkh1 = payments.p2pkh({ output: script }).hash;
            const pkh2 = bcrypto.hash160(ourPubKey);
            if (!pkh1.equals(pkh2))
                return { type };
            return {
                type,
                pubkeys: [ourPubKey],
                signatures: [undefined]
            };
        }
        case SCRIPT_TYPES.P2WPKH: {
            if (!ourPubKey)
                return { type };
            // does our hash160(pubKey) match the output scripts?
            const wpkh1 = payments.p2wpkh({ output: script }).hash;
            const wpkh2 = bcrypto.hash160(ourPubKey);
            if (!wpkh1.equals(wpkh2))
                return { type };
            return {
                type,
                pubkeys: [ourPubKey],
                signatures: [undefined]
            };
        }
        case SCRIPT_TYPES.P2PK: {
            const p2pk = payments.p2pk({ output: script });
            return {
                type,
                pubkeys: [p2pk.pubkey],
                signatures: [undefined]
            };
        }
        case SCRIPT_TYPES.P2MS: {
            const p2ms = payments.p2ms({ output: script });
            return {
                type,
                pubkeys: p2ms.pubkeys,
                signatures: p2ms.pubkeys.map(() => undefined),
                maxSignatures: p2ms.m
            };
        }
    }
    return { type };
}
function prepareInput(input, ourPubKey, redeemScript, witnessScript) {
    if (redeemScript && witnessScript) {
        const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } });
        const p2wshAlt = payments.p2wsh({ output: redeemScript });
        const p2sh = payments.p2sh({ redeem: { output: redeemScript } });
        const p2shAlt = payments.p2sh({ redeem: p2wsh });
        // enforces P2SH(P2WSH(...))
        if (!p2wsh.hash.equals(p2wshAlt.hash))
            throw new Error('Witness script inconsistent with prevOutScript');
        if (!p2sh.hash.equals(p2shAlt.hash))
            throw new Error('Redeem script inconsistent with prevOutScript');
        const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
        if (!expanded.pubkeys)
            throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')');
        if (input.signatures && input.signatures.some(x => x !== undefined)) {
            expanded.signatures = input.signatures;
        }
        let signScript = witnessScript;
        if (expanded.type === SCRIPT_TYPES.P2WPKH)
            throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure');
        return {
            redeemScript,
            redeemScriptType: SCRIPT_TYPES.P2WSH,
            witnessScript,
            witnessScriptType: expanded.type,
            prevOutType: SCRIPT_TYPES.P2SH,
            prevOutScript: p2sh.output,
            hasWitness: true,
            signScript,
            signType: expanded.type,
            pubkeys: expanded.pubkeys,
            signatures: expanded.signatures,
            maxSignatures: expanded.maxSignatures
        };
    }
    if (redeemScript) {
        const p2sh = payments.p2sh({ redeem: { output: redeemScript } });
        if (input.prevOutScript) {
            let p2shAlt;
            try {
                p2shAlt = payments.p2sh({ output: input.prevOutScript });
            }
            catch (e) {
                throw new Error('PrevOutScript must be P2SH');
            }
            if (!p2sh.hash.equals(p2shAlt.hash))
                throw new Error('Redeem script inconsistent with prevOutScript');
        }
        const expanded = expandOutput(p2sh.redeem.output, ourPubKey);
        if (!expanded.pubkeys)
            throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')');
        if (input.signatures && input.signatures.some(x => x !== undefined)) {
            expanded.signatures = input.signatures;
        }
        let signScript = redeemScript;
        if (expanded.type === SCRIPT_TYPES.P2WPKH) {
            signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output;
        }
        return {
            redeemScript,
            redeemScriptType: expanded.type,
            prevOutType: SCRIPT_TYPES.P2SH,
            prevOutScript: p2sh.output,
            hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
            signScript,
            signType: expanded.type,
            pubkeys: expanded.pubkeys,
            signatures: expanded.signatures,
            maxSignatures: expanded.maxSignatures
        };
    }
    if (witnessScript) {
        const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } });
        if (input.prevOutScript) {
            const p2wshAlt = payments.p2wsh({ output: input.prevOutScript });
            if (!p2wsh.hash.equals(p2wshAlt.hash))
                throw new Error('Witness script inconsistent with prevOutScript');
        }
        const expanded = expandOutput(p2wsh.redeem.output, ourPubKey);
        if (!expanded.pubkeys)
            throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')');
        if (input.signatures && input.signatures.some(x => x !== undefined)) {
            expanded.signatures = input.signatures;
        }
        let signScript = witnessScript;
        if (expanded.type === SCRIPT_TYPES.P2WPKH)
            throw new Error('P2WSH(P2WPKH) is a consensus failure');
        return {
            witnessScript,
            witnessScriptType: expanded.type,
            prevOutType: SCRIPT_TYPES.P2WSH,
            prevOutScript: p2wsh.output,
            hasWitness: true,
            signScript,
            signType: expanded.type,
            pubkeys: expanded.pubkeys,
            signatures: expanded.signatures,
            maxSignatures: expanded.maxSignatures
        };
    }
    if (input.prevOutType && input.prevOutScript) {
        // embedded scripts are not possible without extra information
        if (input.prevOutType === SCRIPT_TYPES.P2SH)
            throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript');
        if (input.prevOutType === SCRIPT_TYPES.P2WSH)
            throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript');
        if (!input.prevOutScript)
            throw new Error('PrevOutScript is missing');
        const expanded = expandOutput(input.prevOutScript, ourPubKey);
        if (!expanded.pubkeys)
            throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')');
        if (input.signatures && input.signatures.some(x => x !== undefined)) {
            expanded.signatures = input.signatures;
        }
        let signScript = input.prevOutScript;
        if (expanded.type === SCRIPT_TYPES.P2WPKH) {
            signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output;
        }
        return {
            prevOutType: expanded.type,
            prevOutScript: input.prevOutScript,
            hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
            signScript,
            signType: expanded.type,
            pubkeys: expanded.pubkeys,
            signatures: expanded.signatures,
            maxSignatures: expanded.maxSignatures
        };
    }
    const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output;
    return {
        prevOutType: SCRIPT_TYPES.P2PKH,
        prevOutScript: prevOutScript,
        hasWitness: false,
        signScript: prevOutScript,
        signType: SCRIPT_TYPES.P2PKH,
        pubkeys: [ourPubKey],
        signatures: [undefined]
    };
}
function build(type, input, allowIncomplete) {
    const pubkeys = (input.pubkeys || []);
    let signatures = (input.signatures || []);
    switch (type) {
        case SCRIPT_TYPES.P2PKH: {
            if (pubkeys.length === 0)
                break;
            if (signatures.length === 0)
                break;
            return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] });
        }
        case SCRIPT_TYPES.P2WPKH: {
            if (pubkeys.length === 0)
                break;
            if (signatures.length === 0)
                break;
            return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] });
        }
        case SCRIPT_TYPES.P2PK: {
            if (pubkeys.length === 0)
                break;
            if (signatures.length === 0)
                break;
            return payments.p2pk({ signature: signatures[0] });
        }
        case SCRIPT_TYPES.P2MS: {
            const m = input.maxSignatures;
            if (allowIncomplete) {
                signatures = signatures.map(x => x || script_1.OPS.OP_0);
            }
            else {
                signatures = signatures.filter(x => x);
            }
            // if the transaction is not not complete (complete), or if signatures.length === m, validate
            // otherwise, the number of OP_0's may be >= m, so don't validate (boo)
            const validate = !allowIncomplete || (m === signatures.length);
            return payments.p2ms({ m, pubkeys, signatures }, { allowIncomplete, validate });
        }
        case SCRIPT_TYPES.P2SH: {
            const redeem = build(input.redeemScriptType, input, allowIncomplete);
            if (!redeem)
                return;
            return payments.p2sh({
                redeem: {
                    output: redeem.output || input.redeemScript,
                    input: redeem.input,
                    witness: redeem.witness
                }
            });
        }
        case SCRIPT_TYPES.P2WSH: {
            const redeem = build(input.witnessScriptType, input, allowIncomplete);
            if (!redeem)
                return;
            return payments.p2wsh({
                redeem: {
                    output: input.witnessScript,
                    input: redeem.input,
                    witness: redeem.witness
                }
            });
        }
    }
}
function canSign(input) {
    return input.signScript !== undefined &&
        input.signType !== undefined &&
        input.pubkeys !== undefined &&
        input.signatures !== undefined &&
        input.signatures.length === input.pubkeys.length &&
        input.pubkeys.length > 0 &&
        (input.hasWitness === false ||
            input.value !== undefined);
}
function signatureHashType(buffer) {
    return buffer.readUInt8(buffer.length - 1);
}