<commit_before>var events = require('events')
var util = require('util')

var BIP39 = require('bip39')
var ccWallet = require('cc-wallet-core').Wallet
var CryptoJS = require('crypto-js')
var _ = require('lodash')
var store = require('store')
var cccoreUtil = require('cc-wallet-core').util

var AssetModels = require('./AssetModels')
var JsonFormatter = require('./JsonFormatter')
var cwpp = require('./cwpp')
var CWPPPaymentModel = require('./CWPPPaymentModel')
var HistoryEntryModel = require('./HistoryEntryModel')
var errors = require('./errors')


/**
 * @event WalletEngine#error
 * @param {Error}
 */

/**
 * @event WalletEngine#update
 */

/**
 * @event AssetModels#syncStart
 */

/**
 * @event AssetModels#syncStop
 */

/**
 * @class WalletEngine
 * @extends external:events.EventEmitter
 * @mixins external:cc-wallet-core.util.SyncMixin
 * @param {Object} [opts]
 * @param {boolean} [opts.testnet=false]
 * @param {string} [opts.network=Electrum] Available: Chain, Electrum
 * @param {string} [opts.blockchain=NaiveBlockchain] Available: NaiveBlockchain, VerifiedBlockchain
 * @param {number} [opts.storageSaveTimeout=1000]
 */
function WalletEngine(opts) {
  var self = this
  events.EventEmitter.call(self)
  self.setMaxListeners(100) // 10 by default, 0 -- unlimited
  cccoreUtil.SyncMixin.call(self)

  opts = _.extend({
    testnet: false,
    network: 'Electrum',
    blockchain: 'NaiveBlockchain',
    storageSaveTimeout: 1000
  }, opts)

  self.setCallback(function () {})
  self._assetModels = null
  self._historyEntries = []

  self._wallet = new ccWallet(opts)
  self._wallet.on('error', function (error) { self.emit('error', error) })
  self._wallet.on('syncStart', function () { self._syncEnter() })
  self._wallet.on('syncStop', function () { self._syncExit() })

  // note: can't depend on network.isConnected because it's updated
  // via events
  self._networkIsConnected = self._wallet.network.isConnected()
  self._wallet.network.on('connect', function () {
    self._networkIsConnected = true
    self._update()
  })
  self._wallet.network.on('disconnect', function () {
    self._networkIsConnected = false
    self._update()
  })

  // note: we update right away on syncStart, but use debounce on syncStop
  self.on('syncStart', function () { self._updateCallback() })
  self.on('syncStop', function () { self._delayedUpdateCallback() })

  if (self._wallet.isInitialized()) { self._initializeWalletEngine() }
}

util.inherits(WalletEngine, events.EventEmitter)


WalletEngine.prototype.isConnected = function () {
  return this._networkIsConnected
}

WalletEngine.prototype.isUpdating = function () {
  return (!this.isConnected()) || this.isSyncing()
}

/**
 * @return {external:cc-wallet-core.Wallet}
 */
WalletEngine.prototype.getWallet = function () {
  return this._wallet
}

/**
 * @callback WalletEngine~setCallbackCallback
 */

/**
 * @param {WalletEngine~setCallbackCallback} callback
 */
WalletEngine.prototype.setCallback = function (callback) {
  this._updateCallback = callback
  this._delayedUpdateCallback = cccoreUtil.debounce(callback, 100)
}

/**
 */
WalletEngine.prototype._update = function () {
  // callback is called automatically on syncStop,
  // so we only call callback when not syncing
  if (!this.isSyncing()) {
    this._delayedUpdateCallback()
  }
}

/**
 * @return {boolean}
 */
WalletEngine.prototype.isInitialized = function () {
  return !!this.getSeed() && !!this.getPin() && this._wallet.isInitialized()
}

/**
 * @param {string} mnemonic
 * @param {string} [password]
 * @param {string} pin
 * @throws {Error} If already initialized
 */
WalletEngine.prototype.initialize = function (mnemonic, password, pin) {
  // @todo AlreadyInitialize check?
  this.setSeed(mnemonic, password)
  this._wallet.initialize(this.getSeed())
  this._initializeWalletEngine()
  this.setPin(pin)
  store.set('cc-wallet-engine__mnemonic', mnemonic)
  store.set('cc-wallet-engine__encryptedpin', this.getPinEncrypted())
}

/**
 */
WalletEngine.prototype._initializeWalletEngine = function () {
  var self = this

  self._assetModels = new AssetModels(self)
  self._assetModels.on('error', function (error) { self.emit('error', error) })
  self._assetModels.on('update', function () { self._update() })
  self._assetModels.on('syncStart', function () { self._syncEnter() })
  self._assetModels.on('syncStop', function () { self._syncExit() })

  function updateHistory() {
    var entries = self._wallet.getHistory()

    function entryEqualFn(entry, index) { return entry.getHistoryEntry().isEqual(entries[index]) }
    var isEqual = self._historyEntries.length === entries.length && self._historyEntries.every(entryEqualFn)
    if (isEqual) { return }

    self._historyEntries = entries.map(function (entry) {
      return new HistoryEntryModel(entry)
    }).reverse()

    self._update()
  }

  var historyUpdateTrigger = false
  self._wallet.on('historyUpdate', function () { historyUpdateTrigger = true })
  self._wallet.on('syncStop', function () {
    if (historyUpdateTrigger) {
      updateHistory()
      historyUpdateTrigger = false
    }
  })

  function subscribeCallback(error) {
    self._syncExit()
    if (error !== null) { self.emit('error', error) }
  }

  self._wallet.on('newAddress', function (address) {
    self._syncEnter()
    self._wallet.subscribeAndSyncAddress(address.getAddress(), subscribeCallback)<commit_msg>initialize history on start<commit_after>var events = require('events')
var util = require('util')

var BIP39 = require('bip39')
var ccWallet = require('cc-wallet-core').Wallet
var CryptoJS = require('crypto-js')
var _ = require('lodash')
var store = require('store')
var cccoreUtil = require('cc-wallet-core').util

var AssetModels = require('./AssetModels')
var JsonFormatter = require('./JsonFormatter')
var cwpp = require('./cwpp')
var CWPPPaymentModel = require('./CWPPPaymentModel')
var HistoryEntryModel = require('./HistoryEntryModel')
var errors = require('./errors')


/**
 * @event WalletEngine#error
 * @param {Error}
 */

/**
 * @event WalletEngine#update
 */

/**
 * @event AssetModels#syncStart
 */

/**
 * @event AssetModels#syncStop
 */

/**
 * @class WalletEngine
 * @extends external:events.EventEmitter
 * @mixins external:cc-wallet-core.util.SyncMixin
 * @param {Object} [opts]
 * @param {boolean} [opts.testnet=false]
 * @param {string} [opts.network=Electrum] Available: Chain, Electrum
 * @param {string} [opts.blockchain=NaiveBlockchain] Available: NaiveBlockchain, VerifiedBlockchain
 * @param {number} [opts.storageSaveTimeout=1000]
 */
function WalletEngine(opts) {
  var self = this
  events.EventEmitter.call(self)
  self.setMaxListeners(100) // 10 by default, 0 -- unlimited
  cccoreUtil.SyncMixin.call(self)

  opts = _.extend({
    testnet: false,
    network: 'Electrum',
    blockchain: 'NaiveBlockchain',
    storageSaveTimeout: 1000
  }, opts)

  self.setCallback(function () {})
  self._assetModels = null
  self._historyEntries = []

  self._wallet = new ccWallet(opts)
  self._wallet.on('error', function (error) { self.emit('error', error) })
  self._wallet.on('syncStart', function () { self._syncEnter() })
  self._wallet.on('syncStop', function () { self._syncExit() })

  // note: can't depend on network.isConnected because it's updated
  // via events
  self._networkIsConnected = self._wallet.network.isConnected()
  self._wallet.network.on('connect', function () {
    self._networkIsConnected = true
    self._update()
  })
  self._wallet.network.on('disconnect', function () {
    self._networkIsConnected = false
    self._update()
  })

  // note: we update right away on syncStart, but use debounce on syncStop
  self.on('syncStart', function () { self._updateCallback() })
  self.on('syncStop', function () { self._delayedUpdateCallback() })

  if (self._wallet.isInitialized()) { self._initializeWalletEngine() }
}

util.inherits(WalletEngine, events.EventEmitter)


WalletEngine.prototype.isConnected = function () {
  return this._networkIsConnected
}

WalletEngine.prototype.isUpdating = function () {
  return (!this.isConnected()) || this.isSyncing()
}

/**
 * @return {external:cc-wallet-core.Wallet}
 */
WalletEngine.prototype.getWallet = function () {
  return this._wallet
}

/**
 * @callback WalletEngine~setCallbackCallback
 */

/**
 * @param {WalletEngine~setCallbackCallback} callback
 */
WalletEngine.prototype.setCallback = function (callback) {
  this._updateCallback = callback
  this._delayedUpdateCallback = cccoreUtil.debounce(callback, 100)
}

/**
 */
WalletEngine.prototype._update = function () {
  // callback is called automatically on syncStop,
  // so we only call callback when not syncing
  if (!this.isSyncing()) {
    this._delayedUpdateCallback()
  }
}

/**
 * @return {boolean}
 */
WalletEngine.prototype.isInitialized = function () {
  return !!this.getSeed() && !!this.getPin() && this._wallet.isInitialized()
}

/**
 * @param {string} mnemonic
 * @param {string} [password]
 * @param {string} pin
 * @throws {Error} If already initialized
 */
WalletEngine.prototype.initialize = function (mnemonic, password, pin) {
  // @todo AlreadyInitialize check?
  this.setSeed(mnemonic, password)
  this._wallet.initialize(this.getSeed())
  this._initializeWalletEngine()
  this.setPin(pin)
  store.set('cc-wallet-engine__mnemonic', mnemonic)
  store.set('cc-wallet-engine__encryptedpin', this.getPinEncrypted())
}

/**
 */
WalletEngine.prototype._initializeWalletEngine = function () {
  var self = this

  self._assetModels = new AssetModels(self)
  self._assetModels.on('error', function (error) { self.emit('error', error) })
  self._assetModels.on('update', function () { self._update() })
  self._assetModels.on('syncStart', function () { self._syncEnter() })
  self._assetModels.on('syncStop', function () { self._syncExit() })

  function updateHistory() {
    var entries = self._wallet.getHistory()

    function entryEqualFn(entry, index) { return entry.getHistoryEntry().isEqual(entries[index]) }
    var isEqual = self._historyEntries.length === entries.length && self._historyEntries.every(entryEqualFn)
    if (isEqual) { return }

    self._historyEntries = entries.map(function (entry) {
      return new HistoryEntryModel(entry)
    }).reverse()

    self._update()
  }

  var historyUpdateTrigger = true
  self._wallet.on('historyUpdate', function () { historyUpdateTrigger = true })
  self._wallet.on('syncStop', function () {
    if (historyUpdateTrigger) {
      updateHistory()
      historyUpdateTrigger = false
    }
  })

  function subscribeCallback(error) {
    self._syncExit()
    if (error !== null) { self.emit('error', error) }
  }

  self._wallet.on('newAddress', function (address) {
    self._syncEnter()
    self._wallet.subscribeAndSyncAddress(address.getAddress(), subscribeCallback)
{'fraction_non_alphanumeric': 0.14821676702176934, 'fraction_numerical': 0.003890690134321445, 'mean_word_length': 4.463562753036437, 'pattern_counts': {'":': 0, '<': 3, '<?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': '5481874', 'n_tokens_mistral': 3340, 'n_tokens_neox': 3332, 'n_words': 656}