var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _Witness_scriptHash;
import { hash160, num2VarInt, reverseHex, StringStream, HexString, } from "../../u";
import { getPublicKeysFromVerificationScript, getSignaturesFromInvocationScript, getSigningThresholdFromVerificationScript, getVerificationScriptFromPublicKey, verify, } from "../../wallet";
/**
 * A Witness is a section of VM code that is ran during the verification of the transaction.
 *
 * For example, the most common witness is the VM Script that pushes the ECDSA signature into the VM and calling CHECKSIG to prove the authority to spend the TransactionInputs in the transaction.
 */
export class Witness {
    constructor(obj = {}) {
        _Witness_scriptHash.set(this, "");
        if (typeof obj.invocationScript === "undefined" ||
            typeof obj.verificationScript === "undefined") {
            throw new Error("Witness requires invocationScript and verificationScript fields");
        }
        this.invocationScript = HexString.fromHex(obj.invocationScript);
        this.verificationScript = HexString.fromHex(obj.verificationScript);
    }
    static deserialize(hex) {
        const ss = new StringStream(hex);
        return this.fromStream(ss);
    }
    static fromJson(input) {
        return new Witness({
            invocationScript: HexString.fromBase64(input.invocation),
            verificationScript: HexString.fromBase64(input.verification),
        });
    }
    static fromStream(ss) {
        const invocationScript = ss.readVarBytes();
        const verificationScript = ss.readVarBytes();
        return new Witness({ invocationScript, verificationScript });
    }
    static fromSignature(sig, publicKey) {
        const invocationScript = "0c40" + sig;
        const verificationScript = getVerificationScriptFromPublicKey(publicKey);
        return new Witness({ invocationScript, verificationScript });
    }
    /**
     * Builds a multi-sig Witness object.
     * @param tx - hexstring to be signed.
     * @param sigs - unordered list of signatures.
     * @param acctOrVerificationScript - account or verification script. Account needs to be the multi-sig account and not one of the public keys.
     */
    static buildMultiSig(tx, sigs, acctOrVerificationScript) {
        const verificationScript = typeof acctOrVerificationScript === "string"
            ? acctOrVerificationScript
            : HexString.fromBase64(acctOrVerificationScript.contract.script).toBigEndian();
        const publicKeys = getPublicKeysFromVerificationScript(verificationScript);
        const orderedSigs = Array(publicKeys.length).fill("");
        sigs.forEach((element) => {
            if (typeof element === "string") {
                const position = publicKeys.findIndex((key) => verify(tx, element, key));
                if (position === -1) {
                    throw new Error(`Invalid signature given: ${element}`);
                }
                orderedSigs[position] = element;
            }
            else if (element instanceof Witness) {
                const keys = getPublicKeysFromVerificationScript(element.verificationScript.toBigEndian());
                if (keys.length !== 1) {
                    throw new Error("Given witness contains more than 1 public key!");
                }
                const position = publicKeys.indexOf(keys[0]);
                orderedSigs[position] = getSignaturesFromInvocationScript(element.invocationScript.toBigEndian())[0];
            }
            else {
                throw new Error("Unable to process given signature");
            }
        });
        const signingThreshold = getSigningThresholdFromVerificationScript(verificationScript);
        const validSigs = orderedSigs.filter((s) => s !== "");
        if (validSigs.length < signingThreshold) {
            throw new Error(`Insufficient signatures: expected ${signingThreshold} but got ${validSigs.length} instead`);
        }
        return new Witness({
            invocationScript: validSigs
                .slice(0, signingThreshold)
                .map((s) => "0c40" + s)
                .join(""),
            verificationScript,
        });
    }
    get size() {
        return (num2VarInt(this.invocationScript.byteLength).length / 2 +
            num2VarInt(this.verificationScript.byteLength).length / 2 +
            this.verificationScript.byteLength +
            this.invocationScript.byteLength);
    }
    get scriptHash() {
        if (__classPrivateFieldGet(this, _Witness_scriptHash, "f")) {
            return __classPrivateFieldGet(this, _Witness_scriptHash, "f");
        }
        else if (this.verificationScript) {
            __classPrivateFieldSet(this, _Witness_scriptHash, reverseHex(hash160(this.verificationScript.toBigEndian())), "f");
            return __classPrivateFieldGet(this, _Witness_scriptHash, "f");
        }
        else {
            throw new Error("Unable to produce scriptHash from empty verificationScript");
        }
    }
    serialize() {
        const invoLength = num2VarInt(this.invocationScript.byteLength);
        const veriLength = num2VarInt(this.verificationScript.byteLength);
        return (invoLength +
            this.invocationScript.toBigEndian() +
            veriLength +
            this.verificationScript.toBigEndian());
    }
    export() {
        return {
            invocationScript: this.invocationScript.toBigEndian(),
            verificationScript: this.verificationScript.toBigEndian(),
        };
    }
    toJson() {
        return {
            invocation: this.invocationScript.toBase64(),
            verification: this.verificationScript.toBase64(),
        };
    }
    equals(other) {
        var _a, _b;
        return (this.invocationScript.equals((_a = other.invocationScript) !== null && _a !== void 0 ? _a : "") &&
            this.verificationScript.equals((_b = other.verificationScript) !== null && _b !== void 0 ? _b : ""));
    }
    generateScriptHash() {
        __classPrivateFieldSet(this, _Witness_scriptHash, reverseHex(hash160(this.verificationScript.toBigEndian())), "f");
    }
}
_Witness_scriptHash = new WeakMap();
export default Witness;
//# sourceMappingURL=Witness.js.map