@aeternity/aepp-sdk
Version:
SDK for the æternity blockchain
223 lines • 8.99 kB
JavaScript
import { Buffer as _Buffer } from "buffer";
var _MPTree;
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; }
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
import { encode as rlpEncode } from 'rlp';
import { hash } from '../../../utils/crypto.js';
import { MerkleTreeHashMismatchError, MissingNodeInTreeError, UnknownPathNibbleError, UnexpectedTsError, UnknownNodeLengthError, InternalError } from '../../../utils/errors.js';
import { decode, encode, Encoding } from '../../../utils/encoder.js';
var NodeType = /*#__PURE__*/function (NodeType) {
NodeType[NodeType["Branch"] = 0] = "Branch";
NodeType[NodeType["Extension"] = 1] = "Extension";
NodeType[NodeType["Leaf"] = 2] = "Leaf";
return NodeType;
}(NodeType || {});
var _rootHash = /*#__PURE__*/new WeakMap();
var _isComplete = /*#__PURE__*/new WeakMap();
var _nodes = /*#__PURE__*/new WeakMap();
var _encoding = /*#__PURE__*/new WeakMap();
var _tag = /*#__PURE__*/new WeakMap();
var _unpackEntry = /*#__PURE__*/new WeakMap();
var _MPTree_brand = /*#__PURE__*/new WeakSet();
class MPTree {
get isComplete() {
return _classPrivateFieldGet(_isComplete, this);
}
/**
* Deserialize Merkle Patricia Tree
* @param binary - Binary
* @param tag - Tag to use to decode value
* @param unpEnt - Implementation of unpackEntry use to decode values
* @returns Merkle Patricia Tree
*/
constructor(binary, encoding, tag, unpEnt) {
/**
* Retrieve value from Merkle Patricia Tree
* @param _key - The key of the element to retrieve
* @returns Value associated to the specified key
*/
_classPrivateMethodInitSpec(this, _MPTree_brand);
_classPrivateFieldInitSpec(this, _rootHash, void 0);
_classPrivateFieldInitSpec(this, _isComplete, true);
_classPrivateFieldInitSpec(this, _nodes, void 0);
_classPrivateFieldInitSpec(this, _encoding, void 0);
_classPrivateFieldInitSpec(this, _tag, void 0);
_classPrivateFieldInitSpec(this, _unpackEntry, void 0);
_classPrivateFieldSet(_encoding, this, encoding);
_classPrivateFieldSet(_tag, this, tag);
_classPrivateFieldSet(_unpackEntry, this, unpEnt);
_classPrivateFieldSet(_rootHash, this, binary[0].toString('hex'));
_classPrivateFieldSet(_nodes, this, Object.fromEntries(binary[1].map(node => [node[0].toString('hex'), node[1]])));
if (_classPrivateFieldGet(_nodes, this)[_classPrivateFieldGet(_rootHash, this)] == null) {
if (Object.keys(_classPrivateFieldGet(_nodes, this)).length !== 0) {
throw new MissingNodeInTreeError("Can't find a node by root hash");
}
_classPrivateFieldSet(_isComplete, this, false);
return;
}
Object.entries(_classPrivateFieldGet(_nodes, this)).forEach(([key, node]) => {
if (_nodeHash.call(MPTree, node) !== key) throw new MerkleTreeHashMismatchError();
const {
type
} = _parseNode.call(MPTree, node);
switch (type) {
case NodeType.Branch:
node.slice(0, 16).filter(n => n.length).forEach(n => {
// TODO: enable after resolving https://github.com/aeternity/aeternity/issues/4066
// if (n.length !== 32) {
// throw new ArgumentError('MPTree branch item length', 32, n.length);
// }
if (_classPrivateFieldGet(_nodes, this)[n.toString('hex')] == null) _classPrivateFieldSet(_isComplete, this, false);
});
break;
case NodeType.Extension:
if (_classPrivateFieldGet(_nodes, this)[node[1].toString('hex')] == null) {
throw new MissingNodeInTreeError("Can't find a node by hash in extension node");
}
break;
case NodeType.Leaf:
break;
default:
throw new InternalError(`Unknown MPTree node type: ${type}`);
}
});
}
isEqual(tree) {
return _classPrivateFieldGet(_rootHash, this) === _classPrivateFieldGet(_rootHash, tree);
}
/**
* Serialize Merkle Patricia Tree
* @returns Binary
*/
serialize() {
return [_Buffer.from(_classPrivateFieldGet(_rootHash, this), 'hex'), Object.entries(_classPrivateFieldGet(_nodes, this)).map(([mptHash, value]) => [_Buffer.from(mptHash, 'hex'), value])];
}
/**
* Retrieve value from Merkle Patricia Tree
* @param key - The key of the element to retrieve
* @returns Value associated to the specified key
*/
get(key) {
const d = _assertClassBrand(_MPTree_brand, this, _getRaw).call(this, decode(key).toString('hex'));
if (d == null) return d;
return _classPrivateFieldGet(_unpackEntry, this).call(this, encode(d, Encoding.Bytearray), _classPrivateFieldGet(_tag, this));
}
toObject() {
return Object.fromEntries(_assertClassBrand(_MPTree_brand, this, _entriesRaw).call(this) // TODO: remove after resolving https://github.com/aeternity/aeternity/issues/4066
.filter(([k]) => _classPrivateFieldGet(_encoding, this) !== Encoding.ContractAddress || k.length !== 66).map(([k, v]) => [encode(_Buffer.from(k, 'hex'), _classPrivateFieldGet(_encoding, this)), _classPrivateFieldGet(_unpackEntry, this).call(this, encode(v, Encoding.Bytearray), _classPrivateFieldGet(_tag, this))]));
}
}
_MPTree = MPTree;
function _nodeHash(node) {
return _Buffer.from(hash(rlpEncode(node))).toString('hex');
}
function _parseNode(node) {
switch (node.length) {
case 17:
return {
type: NodeType.Branch,
...(node[16].length !== 0 && {
value: node[16]
})
};
case 2:
{
const nibble = node[0][0] >> 4; // eslint-disable-line no-bitwise
if (nibble > 3) throw new UnknownPathNibbleError(nibble);
const type = nibble <= 1 ? NodeType.Extension : NodeType.Leaf;
const slice = [0, 2].includes(nibble) ? 2 : 1;
return {
type,
...(type === NodeType.Leaf && {
value: node[1]
}),
path: node[0].toString('hex').slice(slice)
};
}
default:
throw new UnknownNodeLengthError(node.length);
}
}
function _getRaw(_key) {
let searchFrom = _classPrivateFieldGet(_rootHash, this);
let key = _key;
while (true) {
// eslint-disable-line no-constant-condition
const node = _classPrivateFieldGet(_nodes, this)[searchFrom];
if (node == null) {
if (!this.isComplete) return undefined;
throw new InternalError("Can't find node in complete tree");
}
const {
type,
value,
path
} = _parseNode.call(_MPTree, node);
switch (type) {
case NodeType.Branch:
if (key.length === 0) return value;
searchFrom = node[+`0x${key[0]}`].toString('hex');
key = key.substring(1);
break;
case NodeType.Extension:
if (key.substring(0, path?.length) !== path) return undefined;
searchFrom = node[1].toString('hex');
key = key.substring(path.length);
break;
case NodeType.Leaf:
if (path !== key) return undefined;
return value;
default:
throw new InternalError(`Unknown MPTree node type: ${type}`);
}
}
}
function _entriesRaw() {
const entries = [];
const rec = (searchFrom, key) => {
const node = _classPrivateFieldGet(_nodes, this)[searchFrom];
if (node == null) {
if (!this.isComplete) return;
throw new InternalError("Can't find node in complete tree");
}
const {
type,
value,
path
} = _parseNode.call(_MPTree, node);
switch (type) {
case NodeType.Branch:
node.slice(0, 16).map((t, idx) => [t, idx]).filter(([t]) => t.length).forEach(([t, idx]) => rec(t.toString('hex'), key + idx.toString(16)));
if (value != null) entries.push([key, value]);
break;
case NodeType.Extension:
rec(node[1].toString('hex'), key + path);
break;
case NodeType.Leaf:
if (value == null) throw new UnexpectedTsError();
entries.push([key + path, value]);
break;
default:
throw new InternalError(`Unknown MPTree node type: ${type}`);
}
};
rec(_classPrivateFieldGet(_rootHash, this), '');
return entries;
}
export default function genMPTreeField(encoding, tag) {
return {
serialize(value) {
return value.serialize();
},
deserialize(value, {
unpackEntry
}) {
return new MPTree(value, encoding, tag, unpackEntry);
}
};
}
//# sourceMappingURL=mptree.js.map