xrpl
Version:
A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser
125 lines (109 loc) • 3.24 kB
text/typescript
import { XrplError } from '../../../errors'
import HashPrefix from '../HashPrefix'
import sha512Half from '../sha512Half'
import LeafNode from './LeafNode'
import { NodeType, Node } from './node'
const HEX_ZERO =
'0000000000000000000000000000000000000000000000000000000000000000'
const SLOT_MAX = 15
const HEX = 16
/**
* Class for SHAMap InnerNode.
*/
class InnerNode extends Node {
public leaves: { [slot: number]: Node | undefined }
public type: NodeType
public depth: number
public empty: boolean
/**
* Define an Inner (non-leaf) node in a SHAMap tree.
*
* @param depth - I.e. How many parent inner nodes.
*/
public constructor(depth = 0) {
super()
this.leaves = {}
this.type = NodeType.INNER
this.depth = depth
this.empty = true
}
/**
* Get the hash of a LeafNode.
*
* @returns Hash of the LeafNode.
*/
public get hash(): string {
if (this.empty) {
return HEX_ZERO
}
let hex = ''
for (let iter = 0; iter <= SLOT_MAX; iter++) {
const child = this.leaves[iter]
const hash: string = child == null ? HEX_ZERO : child.hash
hex += hash
}
const prefix = HashPrefix.INNER_NODE.toString(HEX)
return sha512Half(prefix + hex)
}
/**
* Adds an item to the InnerNode.
*
* @param tag - Equates to a ledger entry `index`.
* @param node - Node to add.
* @throws If there is a index collision.
*/
public addItem(tag: string, node: Node): void {
const existingNode = this.getNode(parseInt(tag[this.depth], HEX))
if (existingNode === undefined) {
this.setNode(parseInt(tag[this.depth], HEX), node)
return
}
// A node already exists in this slot
if (existingNode instanceof InnerNode) {
// There is an inner node, so we need to go deeper
existingNode.addItem(tag, node)
} else if (existingNode instanceof LeafNode) {
if (existingNode.tag === tag) {
// Collision
throw new XrplError(
'Tried to add a node to a SHAMap that was already in there.',
)
} else {
const newInnerNode = new InnerNode(this.depth + 1)
// Parent new and existing node
newInnerNode.addItem(existingNode.tag, existingNode)
newInnerNode.addItem(tag, node)
// And place the newly created inner node in the slot
this.setNode(parseInt(tag[this.depth], HEX), newInnerNode)
}
}
}
/**
* Overwrite the node that is currently in a given slot.
*
* @param slot - A number 0-15.
* @param node - To place.
* @throws If slot is out of range.
*/
public setNode(slot: number, node: Node): void {
if (slot < 0 || slot > SLOT_MAX) {
throw new XrplError('Invalid slot: slot must be between 0-15.')
}
this.leaves[slot] = node
this.empty = false
}
/**
* Get the node that is currently in a given slot.
*
* @param slot - A number 0-15.
* @returns Node currently in a slot.
* @throws If slot is out of range.
*/
public getNode(slot: number): Node | undefined {
if (slot < 0 || slot > SLOT_MAX) {
throw new XrplError('Invalid slot: slot must be between 0-15.')
}
return this.leaves[slot]
}
}
export default InnerNode