PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo-express/node_modules/ripple-hashes/src

Просмотр файла: shamap.js

'use strict';

var util = require('util');
var hashprefixes = require('./hashprefixes');
var sha512half = require('./sha512half');
var HEX_ZERO = '00000000000000000000000000000000' +
               '00000000000000000000000000000000';

/**
 * Abstract class representing a node in a SHAMap tree.
 *
 * Can be either SHAMapTreeNodeInner, SHAMapTreeNodeInnerV2
 * or SHAMapTreeNodeLeaf.
 *
 * @class
 */
function SHAMapTreeNode() { }

SHAMapTreeNode.TYPE_INNER = 1;
SHAMapTreeNode.TYPE_TRANSACTION_NM = 2;
SHAMapTreeNode.TYPE_TRANSACTION_MD = 3;
SHAMapTreeNode.TYPE_ACCOUNT_STATE = 4;

function hash(hex) {
  return sha512half(Buffer.from(hex, 'hex'));
}

/**
* @param {String} tag (64 hexadecimal characters)
* @param {SHAMapTreeNode} node
* @return {void}
* @virtual
*/
/* eslint-disable no-unused-vars*/
SHAMapTreeNode.prototype.add_item = function(tag, node) {
  throw new Error(
    'Called unimplemented virtual method SHAMapTreeNode#add_item.');
};
/* eslint-enable no-unused-vars*/

SHAMapTreeNode.prototype.hash = function() {
  throw new Error('Called unimplemented virtual method SHAMapTreeNode#hash.');
};

/**
 * Inner (non-leaf) node in a SHAMap tree.
 * @param {Number} depth (i.e. how many parent inner nodes)
 * @class
 */
function SHAMapTreeNodeInner(depth) {
  SHAMapTreeNode.call(this);
  this.leaves = {};
  this.type = SHAMapTreeNode.INNER;
  this.depth = depth === undefined ? 0 : depth;
  this.empty = true;
}

util.inherits(SHAMapTreeNodeInner, SHAMapTreeNode);

/**
 * @param {String} tag (equates to a ledger entry `index`)
 * @param {SHAMapTreeNode} node (to add)
 * @return {void}
 */
SHAMapTreeNodeInner.prototype.add_item = function(tag, node) {
  var depth = this.depth;
  var existing_node = this.get_node(tag[depth]);

  if (existing_node) {
    // A node already exists in this slot
    if (existing_node instanceof SHAMapTreeNodeInner) {
      // There is an inner node, so we need to go deeper
      existing_node.add_item(tag, node);
    } else if (existing_node.tag === tag) {
      // Collision
      throw new Error(
          'Tried to add a node to a SHAMap that was already in there.');
    } else {
      // Turn it into an inner node
      var new_inner_node = new SHAMapTreeNodeInner(depth + 1);

      // Parent new and existing node
      new_inner_node.add_item(existing_node.tag, existing_node);
      new_inner_node.add_item(tag, node);

      // And place the newly created inner node in the slot
      this.set_node(tag[depth], new_inner_node);
    }
  } else {
    // Neat, we have a nice open spot for the new node
    this.set_node(tag[depth], node);
  }
};

/**
 * Overwrite the node that is currently in a given slot.
 * @param {String} slot (a character 0-F)
 * @param {SHAMapTreeNode} node (to place)
 * @return {void}
 */
SHAMapTreeNodeInner.prototype.set_node = function(slot, node) {
  this.leaves[slot] = node;
  this.empty = false;
};

SHAMapTreeNodeInner.prototype.get_node = function(slot) {
  return this.leaves[slot];
};

SHAMapTreeNodeInner.prototype.hash = function() {
  if (this.empty) {
    return HEX_ZERO;
  }

  var hex = '';
  for (var i = 0; i < 16; i++) {
    var slot = i.toString(16).toUpperCase();
    hex += this.leaves[slot] ? this.leaves[slot].hash() : HEX_ZERO;
  }

  var prefix = hashprefixes.HASH_INNER_NODE.toString(16);
  return hash(prefix + hex);
};

/**
 * V2 inner (non-leaf) node in a SHAMap tree.
 * @param {Number} depth ()
 * @class
 */
function SHAMapTreeNodeInnerV2(depth) {
  SHAMapTreeNodeInner.call(this, depth);
  this.common = '';
}

util.inherits(SHAMapTreeNodeInnerV2, SHAMapTreeNodeInner);

/**
 * @param {String} key (equates to a ledger entry `index`)
 * @return {Number} (common prefix depth)
 */
SHAMapTreeNodeInnerV2.prototype.get_common_prefix = function(key) {
  var depth = 0;
  for (var i = 0; i < this.depth; i++) {
    if (this.common[i] === key[i]) {
      depth++
    }
  }

  return depth;
};

/**
 * @param {String} key (equates to a ledger entry `index`)
 * @return {bool} (returns true if common prefix exists)
 */
SHAMapTreeNodeInnerV2.prototype.has_common_prefix = function(key) {
  for (var i = 0; i < this.depth; i++) {
    if (this.common[i] !== key[i]) {
      return false;
    }
  }

  return true;
};

/**
 * @param {String} tag (equates to a ledger entry `index`)
 * @param {SHAMapTreeNode} node (to add)
 * @return {void}
 */
SHAMapTreeNodeInnerV2.prototype.add_item = function(tag, node) {
  var depth = this.depth;
  var existing_node = this.get_node(tag[depth]);

  if (existing_node) {
    // A node already exists in this slot
    if (existing_node instanceof SHAMapTreeNodeInnerV2) {
      if (existing_node.has_common_prefix(tag)) {
        // There is an inner node, so we need to go deeper
        existing_node.add_item(tag, node);
      } else {
        // Create new inner node and move existing node under it
        var new_depth = existing_node.get_common_prefix(tag);
        var new_inner_node = new SHAMapTreeNodeInnerV2(new_depth);
        new_inner_node.common = tag.slice(0, new_depth - 64);

        // Parent new and existing node
        new_inner_node.set_node(existing_node.common[new_depth], existing_node);
        new_inner_node.add_item(tag, node);

        // And place the newly created inner node in the slot
        this.set_node(tag[depth], new_inner_node);
      }
    } else if (existing_node.tag === tag) {
      // Collision
      throw new Error(
          'Tried to add a node to a SHAMap that was already in there.');
    } else {
      // Turn it into an inner node
      var new_inner_node = new SHAMapTreeNodeInnerV2(0);

      for (var i = 0; tag[i] === existing_node.tag[i]; i++) {
        new_inner_node.common += tag[i];
        new_inner_node.depth++;
      }

      // Parent new and existing node
      new_inner_node.add_item(existing_node.tag, existing_node);
      new_inner_node.add_item(tag, node);

      // And place the newly created inner node in the slot
      this.set_node(tag[depth], new_inner_node);
    }
  } else {
    // Neat, we have a nice open spot for the new node
    this.set_node(tag[depth], node);
  }
};

SHAMapTreeNodeInnerV2.prototype.hash = function() {
  if (this.empty) {
    return HEX_ZERO;
  }

  var hex = '';
  for (var i = 0; i < 16; i++) {
    var slot = i.toString(16).toUpperCase();
    hex += this.leaves[slot] ? this.leaves[slot].hash() : HEX_ZERO;
  }

  if (this.depth < 16) {
    hex += '0';
  }
  hex += this.depth.toString(16).toUpperCase();

  hex += this.common;
  if (this.common.length % 2) {
    hex += '0';
  }

  var prefix = hashprefixes.HASH_INNER_NODE_V2.toString(16);

  return hash(prefix + hex);
};

/**
 * Leaf node in a SHAMap tree.
 * @param {String} tag (equates to a ledger entry `index`)
 * @param {String} data (hex of account state, transaction etc)
 * @param {Number} type (one of TYPE_ACCOUNT_STATE, TYPE_TRANSACTION_MD etc)
 * @class
 */
function SHAMapTreeNodeLeaf(tag, data, type) {
  SHAMapTreeNode.call(this);

  if (typeof tag !== 'string') {
    throw new Error('Tag is unexpected type.');
  }

  this.tag = tag;
  this.type = type;
  this.data = data;
}

util.inherits(SHAMapTreeNodeLeaf, SHAMapTreeNode);

SHAMapTreeNodeLeaf.prototype.hash = function() {
  switch (this.type) {
    case SHAMapTreeNode.TYPE_ACCOUNT_STATE:
      var leafPrefix = hashprefixes.HASH_LEAF_NODE.toString(16);
      return hash(leafPrefix + this.data + this.tag);
    case SHAMapTreeNode.TYPE_TRANSACTION_NM:
      var txPrefix = hashprefixes.HASH_TX_ID.toString(16);
      return hash(txPrefix + this.data);
    case SHAMapTreeNode.TYPE_TRANSACTION_MD:
      var txPrefix = hashprefixes.HASH_TX_NODE.toString(16);
      return hash(txPrefix + this.data + this.tag);
    default:
      throw new Error('Tried to hash a SHAMap node of unknown type.');
  }
};

/**
 * SHAMap tree.
 * @param {Number} version (inner node version number)
 * @class
 */
function SHAMap(version) {
  this.version = version === undefined ? 1 : version;
  this.root = this.version === 1 ? new SHAMapTreeNodeInner(0) :
    new SHAMapTreeNodeInnerV2(0);
}

SHAMap.prototype.add_item = function(tag, data, type) {
  this.root.add_item(tag, new SHAMapTreeNodeLeaf(tag, data, type));
};

SHAMap.prototype.hash = function() {
  return this.root.hash();
};

exports.SHAMap = SHAMap;
exports.SHAMapTreeNode = SHAMapTreeNode;
exports.SHAMapTreeNodeInner = SHAMapTreeNodeInner;
exports.SHAMapTreeNodeInnerV2 = SHAMapTreeNodeInnerV2;
exports.SHAMapTreeNodeLeaf = SHAMapTreeNodeLeaf;

Выполнить команду


Для локальной разработки. Не используйте в интернете!