bedrock-ledger-node/lib/ledger-node-config.js

/*!
 * Ledger node config management class.
 *
 * Copyright (c) 2018 Digital Bazaar, Inc. All rights reserved.
 */
'use strict';

const async = require('async');
const validator = require('./validator');

/**
 * The LedgerNodeConfig class exposes the config management API.
 */
module.exports = class LedgerNodeConfig {
  constructor({ledgerNode}) {
    this.ledgerNode = ledgerNode;
    this.storage = ledgerNode.storage;
    this.consensus = ledgerNode.consensus;
  }

  /**
   * Submits a new ledgerConfiguration for the ledger. The change will not occur
   * until the current consensus mechanism accepts the ledgerConfiguration
   * and the state machine processes it.
   *
   * ledgerConfiguration - the new ledgerConfiguration for the ledger.
   * [genesis] - boolean - is this genesis config
   * [genesisBlock] - a genesis block for a ledger
   * callback(err) - the callback to call when finished.
   *   err - An Error if an error occurred, null otherwise.
   */
  change({ledgerConfiguration, genesis = false, genesisBlock}, callback) {
    if(!ledgerConfiguration) {
      throw new TypeError('`ledgerConfiguration` is required.');
    }
    const {consensus, ledgerNode} = this;
    this.validate(ledgerConfiguration, err => {
      if(err) {
        return callback(err);
      }
      consensus.config.change(
        {ledgerConfiguration, ledgerNode, genesis, genesisBlock}, callback);
    });
  }

  /**
   * Gets the current ledgerConfiguration for the ledger.
   *
   * callback(err) - the callback to call when finished.
   *   err - An Error if an error occurred, null otherwise.
   *   ledgerConfiguration - the latest ledgerConfiguration or `null` if none.
   */
  get(callback) {
    this.storage.events.getLatestConfig((err, result) => {
      if(err && err.name === 'NotFoundError') {
        // no config yet
        return callback(null, null);
      }
      if(err) {
        return callback(err);
      }
      callback(null, result.event.ledgerConfiguration);
    });
  }

  /**
   * Validates a ledgerConfiguration.
   *
   * ledgerConfiguration - the ledgerConfiguration to validate.
   * callback(err) - the callback to call when finished.
   *   err - An Error if an error occurred, null otherwise.
   */
  validate(ledgerConfiguration, callback) {
    const {ledgerNode} = this;
    async.auto({
      config: callback => ledgerNode.config.get(callback),
      validate: ['config', (results, callback) => {
        // if `results.config` is null, then the config we are validating
        // must be treated as the genesis config, so validate against itself
        const config = results.config || ledgerConfiguration;
        const validatorConfigs = config.ledgerConfigurationValidator || [];
        validator.validate(
          ledgerConfiguration, validatorConfigs, {ledgerNode}, callback);
      }]
    }, err => callback(err));
  }
};