Source: types/aggregator.js

'use strict';

// Dependencies
const merge = require('lodash.merge');

// Fabric Types
const Service = require('./service');
const Actor = require('./actor');
const Tree = require('./tree');

/**
 * Aggregates a set of balances (inputs).
 */
class Aggregator extends Service {
  /**
   * Create a new Aggregator.
   * @param {Object} [settings] Map of configuration values.
   * @param {Array} [settings.inputs] Array of {@link AnchorBalance} instances.
   * @returns {Aggregator} Instance of the {@link Aggregator}.
   */
  constructor (settings = {}) {
    super(settings);

    this.settings = merge({
      inputs: [],
      version: 0
    }, this.settings, settings);

    this.actor = new Actor({ inputs: this.settings.inputs });
    this._tree = new Tree();
    this._state = {
      balances: {
        total: 0,
        confirmed: 0,
        unconfirmed: 0
      },
      inputs: this.settings.inputs,
      history: []
    };

    return this;
  }

  get id () {
    return this.actor.id;
  }

  get balances () {
    return Object.assign({}, this._state.balances);
  }

  get tree () {
    return new Tree({ leaves: this.settings.inputs.map(x => x.hash) });
  }

  /**
   * Import a list of {@link AnchorBalance} instances.
   * @param {Array} list List of inputs to add.
   * @returns {AnchorBalance} Summary of resulting balances.
   */
  _importBalances (list = []) {
    for (let i = 0; i < list.length; i++) {
      this._state.inputs.push(list[i]);
    }
    this.commit();
    return this.balances;
  }

  /**
   * Updates the state to reflect balances from current inputs.
   * @returns {AnchorBalance} Summary of balances.
   */
  _computeBalances () {
    this._state.balances = this._state.inputs.reduce((o, e) => {
      o.total += e.total;
      o.confirmed += e.confirmed;
      o.unconfirmed += e.unconfirmed;
      return o;
    }, {
      total: 0,
      confirmed: 0,
      unconfirmed: 0
    });
    return this.balances;
  }

  /**
   * Commits the balance of all input.
   * @fires Aggregator#commit
   * @returns {AggregatorCommit} Commit instance.
   */
  commit () {
    this._computeBalances();

    const actor = new Actor(this.balances);
    // const signature = null;
    const message = {
      id: actor.id,
      type: 'AggregatorCommit',
      actor: this.id,
      object: actor.toObject(),
      target: '/commits',
      // signature: signature,
      version: this.settings.version
    };

    // Update Tree
    this._tree.addLeaf(actor.id);
    this._state.history.push(this._tree.root);

    // Assign Root & Leaves
    message.root = this._tree.root;
    message.leaves = this._tree.getLeaves();

    /**
     * Commit event.
     * @event Aggregator#commit
     * @type {Object}
     * @property {Uint8Array} root Root of the {@link Tree}.
     * @property {Array} leaves Leaves of the {@link Tree}.
     */
    this.emit('commit', message);

    return message;
  }
}

module.exports = Aggregator;