<commit_before>'use strict';

define(['util/stealth', 'bitcoinjs-lib', 'model/multisig', 'model/pockets', 'util/btc'],
function(Stealth, Bitcoin, MultisigFunds, Pockets, BtcUtils) {
/**
 * Access to the identity bitcoin keys.
 * @param {Object} store Store for the object.
 * @param {Object} identity Identity for the object.
 * @constructor
 */
function Wallet(store, identity) {
    this.identity = identity;
    this.store = store;
    this.network = store.init('network', 'bitcoin');
    this.initVersions(this.network);
    this.fee = store.init('fee', 10000); // 0.1 mBTC
    this.pubKeys = store.init('pubkeys', {});
    this.scanKeys = store.init('scankeys', []);
    this.idKeys = store.init('idkeys', []);
    this.dust = 546;

    this.mpk = store.get('mpk');

    // internal bitcoinjs-lib wallet to keep track of utxo (for now)
    this.multisig = new MultisigFunds(store, identity, this);
    this.pockets = new Pockets(store, identity, this);
    this.wallet = { addresses: [], outputs: {} };

    this.stealthCache = {};

    this.loadPubKeys();

    // store balance
    this.balance = this.getBalance();
}

Wallet.prototype.initVersions = function(network) {
    network = (network === 'bitcoin') ? 'mainnet' : network;
    this.versions = {
        address: Bitcoin.network[network].addressVersion,
        p2sh: Bitcoin.network[network].p2shVersion,
        hd: Bitcoin.network[network].hdVersions
    };
    switch(network) {
        case 'mainnet':
            this.versions.stealth = {address: Stealth.version, nonce: Stealth.nonceVersion, prefix: 'v'};
            break;
        case 'testnet':
            this.versions.stealth = {address: Stealth.testnet, nonce: Stealth.nonceVersion, prefix: 'w'};
            break;
    }
};


/**
 * Initialize addresses for the wallet if empty
 */
Wallet.prototype.initIfEmpty = function() {
    var self = this;
    // If empty generate two addresses and one change for each initial pocket
    if (!Object.keys(this.pubKeys).length) {
        this.pockets.hdPockets.forEach(function (pocket, i) {
            self.getAddress([i*2,0]);
            self.getAddress([i*2,1]);
            self.getAddress([(i*2)+1,0]);
        });
        self.store.save();
    }
};

/**
 * Get addresses for given pocket / chain index.
 * @param {String|undefined} pocket Chain number, multisig id or all pockets if undefined
 * @param {Array} Array with addresses
 */
Wallet.prototype.getPocketAddresses = function(pocketId, type) {
    var allAddresses = [];

    if (pocketId === undefined || pocketId === 'all') {
        // All
        var keys = Object.keys(this.pubKeys);
        for(var i=0; i<keys.length; i++) {
            // don't add fund or readonly addresses to total funds
            if (['multisig', 'readonly'].indexOf(this.pubKeys[keys[i]].type) === -1) {
                allAddresses.push(this.pubKeys[keys[i]].address);
            }
        }
    } else{
        // Standard pocket
        allAddresses = this.pockets.getAddresses(pocketId, type);
    }
    return allAddresses;

};

/**
 * Get balance for a specific pocket or all pockets
 * @param {String|undefined} pocket Pocket number or all pockets if undefined
 * @param {Number} Balance in satoshis
 */
Wallet.prototype.getBalance = function(pocketId, type) {
    var confirmed = 0;
    var unconfirmed = 0;
    var current = 0;

    var allAddresses = this.getPocketAddresses(pocketId, type);

    // Get balance directly from available outputs
    var outputs = this.wallet.outputs;
    var keys = Object.keys(outputs);
    for(var i=0; i<keys.length; i++) {
        var out = outputs[keys[i]];
        if (allAddresses.indexOf(out.address) !== -1) {
            if (out.spend && (out.spendheight === 0)) {
                unconfirmed -= out.value;
            } else if (out.spend) {
                // spent so don't count it
            } else if (!out.height) {
                // add current balance, but not to confirmed
                if (this.identity.tx.inputsMine(out.receive.split(":")[0], null, this)) {
                    current += out.value;
                }
                // hot change also gets added to cancel unconfirmed from the spend
                unconfirmed += out.value;
            }
            else {
                confirmed += out.value;
                current += out.value;
            }
        }
    }
    var balances = {confirmed: confirmed, unconfirmed: unconfirmed, current: current};
    
    if (pocketId === undefined) {
        this.balance = balances;
    }
    return balances;
};

/**
 * Load wallet addresses into internal Bitcoin.Wallet
 * @private
 */
Wallet.prototype.loadPubKeys = function() {
    var self = this;
    Object.keys(this.pubKeys).forEach(function(index) {
        var walletAddress = self.pubKeys[index];

        // Cleanup if malformed
        if (!walletAddress){
            console.log("delete empty address", index);
            delete self.pubKeys[index];
            return;
        }
        // Add all to the wallet
        self.wallet.addresses.push(walletAddress.address);
        self.pockets.addToPocket(walletAddress);

        // TODO: Don't process previous history so we can cache
        // properly later
        if (walletAddress.history) {
              // Reload history
              walletAddress.balance = 0;
              walletAddress.nOutputs = 0;
              walletAddress.height = 0;
              self.processHistory(walletAddress, walletAddress.history, true);
        }
    });

    // Initialize if empty before creating pocket keys
    this.initIfEmpty();

    // Upgrade stealth addresses
    this.pockets.hdPockets.forEach(function(pocket, i) {
        var walletAddress = self.pubKeys[[i*2]];
        if (walletAddress && pocket) {<commit_msg>Fix readonly keys with bad indexes.<commit_after>'use strict';

define(['util/stealth', 'bitcoinjs-lib', 'model/multisig', 'model/pockets', 'util/btc'],
function(Stealth, Bitcoin, MultisigFunds, Pockets, BtcUtils) {
/**
 * Access to the identity bitcoin keys.
 * @param {Object} store Store for the object.
 * @param {Object} identity Identity for the object.
 * @constructor
 */
function Wallet(store, identity) {
    this.identity = identity;
    this.store = store;
    this.network = store.init('network', 'bitcoin');
    this.initVersions(this.network);
    this.fee = store.init('fee', 10000); // 0.1 mBTC
    this.pubKeys = store.init('pubkeys', {});
    this.scanKeys = store.init('scankeys', []);
    this.idKeys = store.init('idkeys', []);
    this.dust = 546;

    this.mpk = store.get('mpk');

    // internal bitcoinjs-lib wallet to keep track of utxo (for now)
    this.multisig = new MultisigFunds(store, identity, this);
    this.pockets = new Pockets(store, identity, this);
    this.wallet = { addresses: [], outputs: {} };

    this.stealthCache = {};

    this.loadPubKeys();

    // store balance
    this.balance = this.getBalance();
}

Wallet.prototype.initVersions = function(network) {
    network = (network === 'bitcoin') ? 'mainnet' : network;
    this.versions = {
        address: Bitcoin.network[network].addressVersion,
        p2sh: Bitcoin.network[network].p2shVersion,
        hd: Bitcoin.network[network].hdVersions
    };
    switch(network) {
        case 'mainnet':
            this.versions.stealth = {address: Stealth.version, nonce: Stealth.nonceVersion, prefix: 'v'};
            break;
        case 'testnet':
            this.versions.stealth = {address: Stealth.testnet, nonce: Stealth.nonceVersion, prefix: 'w'};
            break;
    }
};


/**
 * Initialize addresses for the wallet if empty
 */
Wallet.prototype.initIfEmpty = function() {
    var self = this;
    // If empty generate two addresses and one change for each initial pocket
    if (!Object.keys(this.pubKeys).length) {
        this.pockets.hdPockets.forEach(function (pocket, i) {
            self.getAddress([i*2,0]);
            self.getAddress([i*2,1]);
            self.getAddress([(i*2)+1,0]);
        });
        self.store.save();
    }
};

/**
 * Get addresses for given pocket / chain index.
 * @param {String|undefined} pocket Chain number, multisig id or all pockets if undefined
 * @param {Array} Array with addresses
 */
Wallet.prototype.getPocketAddresses = function(pocketId, type) {
    var allAddresses = [];

    if (pocketId === undefined || pocketId === 'all') {
        // All
        var keys = Object.keys(this.pubKeys);
        for(var i=0; i<keys.length; i++) {
            // don't add fund or readonly addresses to total funds
            if (['multisig', 'readonly'].indexOf(this.pubKeys[keys[i]].type) === -1) {
                allAddresses.push(this.pubKeys[keys[i]].address);
            }
        }
    } else{
        // Standard pocket
        allAddresses = this.pockets.getAddresses(pocketId, type);
    }
    return allAddresses;

};

/**
 * Get balance for a specific pocket or all pockets
 * @param {String|undefined} pocket Pocket number or all pockets if undefined
 * @param {Number} Balance in satoshis
 */
Wallet.prototype.getBalance = function(pocketId, type) {
    var confirmed = 0;
    var unconfirmed = 0;
    var current = 0;

    var allAddresses = this.getPocketAddresses(pocketId, type);

    // Get balance directly from available outputs
    var outputs = this.wallet.outputs;
    var keys = Object.keys(outputs);
    for(var i=0; i<keys.length; i++) {
        var out = outputs[keys[i]];
        if (allAddresses.indexOf(out.address) !== -1) {
            if (out.spend && (out.spendheight === 0)) {
                unconfirmed -= out.value;
            } else if (out.spend) {
                // spent so don't count it
            } else if (!out.height) {
                // add current balance, but not to confirmed
                if (this.identity.tx.inputsMine(out.receive.split(":")[0], null, this)) {
                    current += out.value;
                }
                // hot change also gets added to cancel unconfirmed from the spend
                unconfirmed += out.value;
            }
            else {
                confirmed += out.value;
                current += out.value;
            }
        }
    }
    var balances = {confirmed: confirmed, unconfirmed: unconfirmed, current: current};
    
    if (pocketId === undefined) {
        this.balance = balances;
    }
    return balances;
};

/**
 * Load wallet addresses into internal Bitcoin.Wallet
 * @private
 */
Wallet.prototype.loadPubKeys = function() {
    var self = this;
    Object.keys(this.pubKeys).forEach(function(index) {
        var walletAddress = self.pubKeys[index];

        // Cleanup if malformed
        if (!walletAddress){
            console.log("delete empty address", index);
            delete self.pubKeys[index];
            return;
        }
        // Reindex // delete badly indexed
        if (walletAddress.type === 'readonly' && walletAddress.index[0] === index) {
            delete self.pubKeys[index];
            if (self.pubKeys[walletAddress.index.slice(0)]) {
                // if it exists this is a duplicate, so just forget it
                return;
            } else {
                // otherwise relink it
                self.pubKeys[walletAddress.index.slice(0)] = walletAddress;
            }
        }
        // Add all to the wallet
        self.wallet.addresses.push(walletAddress.address);
        self.pockets.addToPocket(walletAddress);

        // TODO: Don't process previous history so we can cache
        // properly later
        if (walletAddress.history) {
              // Reload history
              walletAddress.balance = 0;
              walletAddress.nOutputs = 0;
              walletAddress.height = 0;
              self.processHistory(walletAddress, walletAddress.history, true);
        }
    });

    // Initialize if empty before creating pocket keys
    this.initIfEmpty();

    // Upgrade stealth addresses
    this.pockets.hdPockets.forEach(function(pocket, i) {
        var walletAddress = self.pubKeys[[i*2]];
        if (walletAddress && pocket) {
{'fraction_non_alphanumeric': 0.12875714167425686, 'fraction_numerical': 0.0055477353647429, 'mean_word_length': 2.2829573253601523, 'pattern_counts': {'":': 2, '<': 7, '<?xml version=': 0, '>': 3, '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': '66826', 'n_tokens_mistral': 3565, 'n_tokens_neox': 3454, 'n_words': 882}