lib/coined.js
function Coined(options) {
  var self = this
    , dbType
    , dbDir
    , dbPath;

  if (!(this instanceof Coined)) {
    return new Coined(options);
  }

  EventEmitter.call(this);

  options = options || {};
  options.db = options.db || {};

  dbType = options.db.type || 'tiny';
  dbDir = dbType === 'level'
    ? '.coined.level'
    : '.coined';
  dbPath = dbType === 'level'
    ? path.resolve(process.env.HOME, dbDir)
    : path.resolve(process.env.HOME, dbDir, 'db');

  options.db.type = options.db.type || dbType;
  options.db.path = options.db.path || dbPath;

  if (options.db.clear) {
    cleanup(options.db.path);
  }

  if (options.db.type === 'level') {
    mkdirp(options.db.path);
    this.db = require('levelup')(options.db.path, {
      db: require('leveldown'),
      valueEncoding: 'json'
    });
  } else if (options.db.type === 'tiny') {
    mkdirp(path.resolve(options.db.path, '..'));
    this.db = require('tiny').json({
      file: options.db.path,
      saveIndex: false,
      initialCache: false
    });
  } else {
    throw new Error('Invalid DB type.');
  }

  this.options = options;

  this.socketIndex = 0;

  this.crypto = options.crypto;
  this.compressed = options.compressed != null
    ? options.compressed
    : true;

  this.walletPath = options.walletPath || options.wallet
    || process.env.HOME + '/.coined/wallet.json';

  this.addr = null;
  this.dust = 5460;
  this.fee = 10000;

  mkdirp(path.dirname(this.walletPath));

  this.account = null;
  this.accounts = [];
  this.aaccounts = {};
  this.laccounts = {};
  this.recipients = {};

  if (options.noPreload) {
    this._clearPreload();
  }

  this.pool = options.pool || bcoin.pool({
    size: options.size,
    createConnection: function() {
      if (self.socketIndex >= seeds.length) {
        self.socketIndex = 0;
      }

      if (seeds.length > 3000) {
        seeds = seeds.slice(0, 1500);
        self.socketIndex = 0;
      }

      var addr = seeds[self.socketIndex++]
        , parts = addr.split(':')
        , host = parts[0]
        , port = +parts[1] || network.port
        , socket;

      socket = net.connect(port, host);

      socket.on('connect', function() {
        var peers = [].concat(
          self.pool.peers.pending,
          self.pool.peers.block,
          self.pool.peers.load
        ).filter(Boolean);

        for (var i = 0; i < peers.length; i++) {
          var peer = peers[i];

          if (peer.socket !== socket) {
            continue;
          }

          if (peer.version) {
            return self.emit('peer', peer, socket);
          }

          return peer.parser.on('packet', function callee(payload) {
            if (payload.cmd !== 'version') return;
            peer.removeListener('packet', callee);
            return setImmediate(function() {
              self.emit('peer', peer, socket);
            });
          });
        }

        self._log('Connected to %s:%d', host, port);
      });

      return socket;
    },
    storage: this.db,
    startHeight: options.startHeight,
    fullNode: options.fullNode
    // relay: options.relay
  });

  this.blockHeight = 0;
  this.on('peer', function(peer) {
    if (!peer.version || !peer.socket) {
      return;
    }
    if (peer.version.height > self.blockHeight) {
      self.blockHeight = peer.version.height;
    }
    self._log('Connected to %s:%d (%s v%s) height=%d relay=%d',
      peer.socket.remoteAddress,
      peer.socket.remotePort,
      peer.version.agent,
      peer.version.v,
      peer.version.height,
      peer.version.relay);
  });

  if (options.noPreload) {
    network.preload = network._preload;
  }

  this.pool.on('error', function(err) {
    self._error(err);
  });

  this.salt = 'coined:';

  this.pending = {};

  this.loadWallet(null, this.passphrase);

  if (!this.account) {
    this.createAccount(options);
  }

  // Keep track of version, handle upgrades.
  this.version = Coined.version;
  this.previousVersion = '0.0.0';
  this.db.get('meta/version', function(err, data) {
    if (data) {
      self.previousVersion = data.version;
    }
    return self.db.put('meta/version', { version: self.version }, function(err) {
      if (err) return self._error(err);
      self._log('Version written: %s, previous: %s',
        self.version, self.previousVersion);
    });
  });

  // Listeners to remove on .destroy();
  this.pool._poolOnTX = null;
  this.pool._poolOnReject = null;
  this.pool._poolOnceFull = null;
  this.closed = false;

  this.init();
}
function Coined(options) {
  var self = this
    , dbType
    , dbDir
    , dbPath;

  if (!(this instanceof Coined)) {
    return new Coined(options);
  }

  EventEmitter.call(this);

  options = options || {};
  options.db = options.db || {};

  dbType = options.db.type || 'tiny';
  dbDir = dbType === 'level'
    ? '.coined.level'
    : '.coined';
  dbPath = dbType === 'level'
    ? path.resolve(process.env.HOME, dbDir)
    : path.resolve(process.env.HOME, dbDir, 'db');

  options.db.type = options.db.type || dbType;
  options.db.path = options.db.path || dbPath;

  if (options.db.clear) {
    cleanup(options.db.path);
  }

  if (options.db.type === 'level') {
    mkdirp(options.db.path);
    this.db = require('levelup')(options.db.path, {
      db: require('leveldown'),
      valueEncoding: 'json'
    });
  } else if (options.db.type === 'tiny') {
    mkdirp(path.resolve(options.db.path, '..'));
    this.db = require('tiny').json({
      file: options.db.path,
      saveIndex: false,
      initialCache: false
    });
  } else {
    throw new Error('Invalid DB type.');
  }

  this.options = options;

  this.socketIndex = 0;

  this.crypto = options.crypto;
  this.compressed = options.compressed != null
    ? options.compressed
    : true;

  this.walletPath = options.walletPath || options.wallet
    || process.env.HOME + '/.coined/wallet.json';

  this.addr = null;
  this.dust = 5460;
  this.fee = 10000;

  mkdirp(path.dirname(this.walletPath));

  this.account = null;
  this.accounts = [];
  this.aaccounts = {};
  this.laccounts = {};
  this.recipients = {};

  if (options.noPreload) {
    this._clearPreload();
  }

  this.pool = options.pool || bcoin.pool({
    size: options.size,
    createConnection: function() {
      if (self.socketIndex >= seeds.length) {
        self.socketIndex = 0;
      }

      if (seeds.length > 3000) {
        seeds = seeds.slice(0, 1500);
        self.socketIndex = 0;
      }

      var addr = seeds[self.socketIndex++]
        , parts = addr.split(':')
        , host = parts[0]
        , port = +parts[1] || network.port
        , socket;

      socket = net.connect(port, host);

      socket.on('connect', function() {
        var peers = [].concat(
          self.pool.peers.pending,
          self.pool.peers.block,
          self.pool.peers.load
        ).filter(Boolean);

        for (var i = 0; i < peers.length; i++) {
          var peer = peers[i];

          if (peer.socket !== socket) {
            continue;
          }

          if (peer.version) {
            return self.emit('peer', peer, socket);
          }

          return peer.parser.on('packet', function callee(payload) {
            if (payload.cmd !== 'version') return;
            peer.removeListener('packet', callee);
            return setImmediate(function() {
              self.emit('peer', peer, socket);
            });
          });
        }

        self._log('Connected to %s:%d', host, port);
      });

      return socket;
    },
    storage: this.db,
    startHeight: options.startHeight,
    fullNode: options.fullNode
    // relay: options.relay
  });

  this.blockHeight = 0;
  this.on('peer', function(peer) {
    if (!peer.version || !peer.socket) {
      return;
    }
    if (peer.version.height > self.blockHeight) {
      self.blockHeight = peer.version.height;
    }
    self._log('Connected to %s:%d (%s v%s) height=%d relay=%d',
      peer.socket.remoteAddress,
      peer.socket.remotePort,
      peer.version.agent,
      peer.version.v,
      peer.version.height,
      peer.version.relay);
  });

  if (options.noPreload) {
    network.preload = network._preload;
  }

  this.pool.on('error', function(err) {
    self._error(err);
  });

  this.salt = 'coined:';

  this.pending = {};

  this.loadWallet(null, this.passphrase);

  if (!this.account) {
    this.createAccount(options);
  }

  // Keep track of version, handle upgrades.
  this.version = Coined.version;
  this.previousVersion = '0.0.0';
  this.db.get('meta/version', function(err, data) {
    if (data) {
      self.previousVersion = data.version;
    }
    return self.db.put('meta/version', { version: self.version }, function(err) {
      if (err) return self._error(err);
      self._log('Version written: %s, previous: %s',
        self.version, self.previousVersion);
    });
  });

  // Listeners to remove on .destroy();
  this.pool._poolOnTX = null;
  this.pool._poolOnReject = null;
  this.pool._poolOnceFull = null;
  this.closed = false;

  this.init();
}
Coined