<commit_before>var q = require('q');
var async = require('async');
var bitcoin = require('bitcoinjs-lib');
var blocktrail = require('./blocktrail');

// apply patch to Q to add spreadNodeify
blocktrail.patchQ(q);

var Wallet = function (sdk, identifier, primaryMnemonic, primaryPrivateKey, backupPublicKey, blocktrailPublicKeys, keyIndex, testnet) {
    var self = this;

    self.sdk = sdk;
    self.identifier = identifier;

    self.testnet = testnet;
    if(self.testnet) {
        self.network = bitcoin.networks.testnet;
    } else {
        self.network = bitcoin.networks.bitcoin;
    }

    self.primaryMnemonic = primaryMnemonic;
    self.primaryPrivateKey = primaryPrivateKey;
    self.backupPublicKey = new bitcoin.HDNode.fromBase58(backupPublicKey[0], self.network);
    self.blocktrailPublicKeys = blocktrailPublicKeys;
    self.keyIndex = keyIndex;
};

/**
 * sort list of pubkeys to be used in a multisig redeemscript
 *  sorted in lexicographical order on the hex of the pubkey
 *
 * @param pubKeys
 * @returns string[]
 */
Wallet.sortMultiSigKeys = function(pubKeys) {
    pubKeys.sort(function(key1, key2) {
        return key1.toHex().localeCompare(key2.toHex());
    });

    return pubKeys;
};

Wallet.estimateIncompleteTxFee = function(tx) {
    var size = 4 + 4;

    size += tx.outs.length * 34;

    tx.ins.forEach(function(txin) {
        var scriptSig = bitcoin.Script.fromBuffer(txin.script.buffer),
            scriptType = bitcoin.scripts.classifyInput(scriptSig);

        // Re-classify if P2SH
        if (scriptType === 'scripthash') {
            var redeemScript = bitcoin.Script.fromBuffer(scriptSig.chunks.slice(-1)[0]);
            scriptSig = bitcoin.Script.fromChunks(scriptSig.chunks.slice(0, -1));
            scriptType = bitcoin.scripts.classifyInput(scriptSig);

            if (bitcoin.scripts.classifyOutput(redeemScript) != scriptType) {
                throw new Error('Non-matching scriptSig and scriptPubKey in input');
            }
        }

        // @TODO: get the N of M from the above code
        var multisig = [2, 3]; // we only do 2 of 3 multisig atm

        if (multisig) {
            size += 32 // txhash
                 + 4 // idx
                 + (72 * multisig[0]) // sig
                 + 106 // script
                 + 4 // ?
                 + 4; // sequence

        } else {
            size += 32 // txhash
                + 4 // idx
                + 72 // sig
                + 32 // script
                + 4 // ?
                + 4; // sequence<commit_msg>determine multisig M of N<commit_after>var q = require('q');
var async = require('async');
var bitcoin = require('bitcoinjs-lib');
var blocktrail = require('./blocktrail');

// apply patch to Q to add spreadNodeify
blocktrail.patchQ(q);

var Wallet = function (sdk, identifier, primaryMnemonic, primaryPrivateKey, backupPublicKey, blocktrailPublicKeys, keyIndex, testnet) {
    var self = this;

    self.sdk = sdk;
    self.identifier = identifier;

    self.testnet = testnet;
    if(self.testnet) {
        self.network = bitcoin.networks.testnet;
    } else {
        self.network = bitcoin.networks.bitcoin;
    }

    self.primaryMnemonic = primaryMnemonic;
    self.primaryPrivateKey = primaryPrivateKey;
    self.backupPublicKey = new bitcoin.HDNode.fromBase58(backupPublicKey[0], self.network);
    self.blocktrailPublicKeys = blocktrailPublicKeys;
    self.keyIndex = keyIndex;
};

/**
 * sort list of pubkeys to be used in a multisig redeemscript
 *  sorted in lexicographical order on the hex of the pubkey
 *
 * @param pubKeys
 * @returns string[]
 */
Wallet.sortMultiSigKeys = function(pubKeys) {
    pubKeys.sort(function(key1, key2) {
        return key1.toHex().localeCompare(key2.toHex());
    });

    return pubKeys;
};

/**
 * determine how much fee is required based on the inputs and outputs
 *  this is an estimation, not a proper 100% correct calculation
 *
 * @param {bitcoin.Transaction} tx
 * @returns {number}
 */
Wallet.estimateIncompleteTxFee = function(tx) {
    var size = 4 + 4;

    size += tx.outs.length * 34;

    tx.ins.forEach(function(txin) {
        var scriptSig = bitcoin.Script.fromBuffer(txin.script.buffer),
            scriptType = bitcoin.scripts.classifyInput(scriptSig);

        var multiSig = false;

        // Re-classify if P2SH
        if (scriptType === 'scripthash') {
            var redeemScript = bitcoin.Script.fromBuffer(scriptSig.chunks.slice(-1)[0]);
            scriptSig = bitcoin.Script.fromChunks(scriptSig.chunks.slice(0, -1));
            scriptType = bitcoin.scripts.classifyInput(scriptSig);

            if (bitcoin.scripts.classifyOutput(redeemScript) != scriptType) {
                throw new Error('Non-matching scriptSig and scriptPubKey in input');
            }

            // figure out M of N for multisig (code from internal usage of bitcoinjs)
            if (scriptType == 'multisig') {
                var mOp = redeemScript.chunks[0];
                if (mOp === bitcoin.opcodes.OP_0 || mOp < bitcoin.opcodes.OP_1 || mOp > bitcoin.opcodes.OP_16) {
                    throw new Error("Invalid multisig redeemScript");
                }

                var nOp = redeemScript.chunks[redeemScript.chunks.length - 2];
                if (mOp === bitcoin.opcodes.OP_0 || mOp < bitcoin.opcodes.OP_1 || mOp > bitcoin.opcodes.OP_16) {
                    throw new Error("Invalid multisig redeemScript");
                }

                var m = mOp - (bitcoin.opcodes.OP_1 - 1);
                var n = nOp - (bitcoin.opcodes.OP_1 - 1);
                if (n < m) {
                    throw new Error("Invalid multisig redeemScript");
                }

                multiSig = [m, n];
            }
        }

        if (multiSig) {
            size += 32 // txhash
                 + 4 // idx
                 + (72 * multiSig[0]) // sig
                 + 106 // script
                 + 4 // ?
                 + 4; // sequence

        } else {
            size += 32 // txhash
                + 4 // idx
                + 72 // sig
                + 32 // script
                + 4 // ?
                + 4; // sequence
{'fraction_non_alphanumeric': 0.1148418491484185, 'fraction_numerical': 0.01508515815085158, 'mean_word_length': 2.013685239491691, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6666273', 'n_tokens_mistral': 1937, 'n_tokens_neox': 1789, 'n_words': 441}