@bithomp/xrpl-api
Version:
A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger
499 lines (498 loc) • 17.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseVL = parseVL;
exports.isValidVL = isValidVL;
exports.encodeVLBlob = encodeVLBlob;
exports.parseValidationData = parseValidationData;
const manifest_1 = require("./manifest");
const utils_1 = require("../parse/utils");
const ledger_1 = require("./ledger");
const Validator = __importStar(require("../validator"));
function parseVL(vl) {
const decoded = {};
decoded.version = vl.version;
if (decoded.version !== 1 && decoded.version !== 2) {
decoded.error = "Invalid version";
}
decoded.PublicKey = vl.public_key;
decoded.manifest = vl.manifest;
let error = isValidVLFormat(vl);
if (error) {
decoded.error = error;
}
decoded.decodedManifest = (0, manifest_1.parseManifest)(vl.manifest);
if (!decoded.error && decoded.decodedManifest.error) {
decoded.error = decoded.decodedManifest.error;
}
if (!decoded.error &&
decoded.PublicKey !== decoded.decodedManifest.PublicKey &&
decoded.PublicKey !== decoded.decodedManifest.SigningPubKey) {
decoded.error = "PublicKey does not match manifest";
}
if (decoded.version === 1) {
decoded.signature = vl.signature;
if (!decoded.error && !decoded.signature) {
decoded.error = "Signature (blob) is missing";
}
if (decoded.signature && decoded.decodedManifest.SigningPubKey && vl.blob) {
if (!decoded.error &&
!Validator.verify(Buffer.from(vl.blob, "base64"), decoded.signature, decoded.decodedManifest.SigningPubKey)) {
decoded.error = "Signature is not valid";
}
}
const blob = decodeVLBlob(vl.blob);
error = isValidVLBlob(blob);
if (!decoded.error && error) {
decoded.error = error;
}
decoded.blob = {
sequence: blob?.sequence,
expiration: (0, ledger_1.ledgerTimeToUnixTime)(blob?.expiration),
validators: [],
};
for (const validator of blob?.validators) {
error = isValidVLBlobValidator(validator);
if (!decoded.error && error) {
decoded.error = error;
}
const validatorManifest = (0, manifest_1.parseManifest)(validator.manifest);
if (!decoded.error && validatorManifest.error) {
decoded.error = validatorManifest.error;
}
if (decoded.blob.validators) {
decoded.blob.validators.push({
PublicKey: validator.validation_public_key,
manifest: validator.manifest,
decodedManifest: validatorManifest,
});
}
}
}
else if (decoded.version === 2) {
const blobs = (vl["blobs-v2"] || vl["blobs_v2"]);
if (!decoded.blobs) {
decoded.blobs = [];
}
if (!blobs) {
return decoded;
}
for (const blobInfo of blobs) {
const blob = decodeVLBlob(blobInfo.blob);
error = isValidVLBlob(blob);
if (!decoded.error && error) {
decoded.error = error;
}
const decodedBlob = {
sequence: blob?.sequence,
expiration: (0, ledger_1.ledgerTimeToUnixTime)(blob?.expiration),
signature: blobInfo.signature,
validators: [],
};
if (blob?.effective) {
decodedBlob.effective = (0, ledger_1.ledgerTimeToUnixTime)(blob.effective);
}
if (blobInfo?.manifest) {
decodedBlob.manifest = blobInfo.manifest;
decodedBlob.decodedManifest = (0, manifest_1.parseManifest)(blobInfo.manifest);
if (!decoded.error && decodedBlob.decodedManifest.error) {
decoded.error = decodedBlob.decodedManifest.error;
}
}
const decodedManifest = decodedBlob.decodedManifest || decoded.decodedManifest;
if (!decoded.error &&
decoded.PublicKey !== decodedManifest.PublicKey &&
decoded.PublicKey !== decodedManifest.SigningPubKey) {
decoded.error = "PublicKey does not match manifest";
}
if (!decoded.error && !decodedBlob.signature) {
decoded.error = "Signature (blob) is missing";
}
if (decodedBlob.signature && decodedManifest.SigningPubKey && blobInfo.blob) {
if (!decoded.error &&
!Validator.verify(Buffer.from(blobInfo.blob, "base64"), decodedBlob.signature, decodedManifest.SigningPubKey)) {
decoded.error = "Signature is not valid";
}
}
for (const validator of blob?.validators) {
error = isValidVLBlobValidator(validator);
if (!decoded.error && error) {
decoded.error = error;
}
const validatorManifest = (0, manifest_1.parseManifest)(validator.manifest);
if (!decoded.error && validatorManifest.error) {
decoded.error = validatorManifest.error;
}
if (decodedBlob.validators) {
decodedBlob.validators.push({
PublicKey: validator.validation_public_key,
manifest: validator.manifest,
decodedManifest: validatorManifest,
});
}
}
decoded.blobs.push(decodedBlob);
}
}
return decoded;
}
function isValidVL(vl) {
let error = isValidVLFormat(vl);
if (error) {
return error;
}
const vlManifest = (0, manifest_1.parseManifest)(vl.manifest);
if (vlManifest.error) {
return vlManifest.error;
}
if (vl.version === 1) {
const blob = decodeVLBlob(vl.blob);
error = isValidVLBlob(blob);
if (error) {
return error;
}
for (const validator of blob?.validators) {
error = isValidVLBlobValidator(validator);
if (error) {
return error;
}
}
}
else if (vl.version === 2) {
const blobs = (vl["blobs-v2"] || vl["blobs_v2"]);
if (!blobs) {
return "blobs_v2 missing from vl";
}
for (const blobInfo of blobs) {
const blob = decodeVLBlob(blobInfo.blob);
error = isValidVLBlob(blob);
if (error) {
return error;
}
for (const validator of blob?.validators) {
error = isValidVLBlobValidator(validator);
if (error) {
return error;
}
}
}
}
else {
return "Invalid version";
}
return error;
}
function isValidVLFormat(vl) {
const { version, public_key, manifest, blob } = vl;
let error = null;
if (version === undefined) {
error = "Version missing from vl";
}
else if (public_key === undefined) {
error = "Public key missing from vl";
}
else if (manifest === undefined) {
error = "Manifest missing from vl";
}
else if (version === 1) {
if (blob === undefined) {
error = "Blob missing from vl";
}
}
else if (version === 2) {
const blobs = (vl["blobs-v2"] || vl["blobs_v2"]);
if (blob !== undefined) {
error = "Blob should not be present in vl version 2";
}
else if (blobs === undefined) {
error = "blobs_v2 missing from vl";
}
else if (!Array.isArray(blobs)) {
error = "blobs_v2 should be an array";
}
else if (blobs.length === 0) {
error = "blobs_v2 should not be empty";
}
}
else {
error = "Version is not supported";
}
return error;
}
function decodeVLBlob(blob) {
const decoded = Buffer.from(blob, "base64").toString("ascii");
return JSON.parse(decoded);
}
function encodeVLBlob(vlBlob) {
return Buffer.from(JSON.stringify(vlBlob)).toString("base64");
}
function isValidVLBlob(blob) {
if (blob === null) {
return "Blob is not valid";
}
const { sequence, expiration, validators } = blob;
let error = null;
if (sequence === undefined) {
error = "Sequence missing from blob";
}
if (expiration === undefined) {
error = "Expiration missing from blob";
}
if (validators === undefined) {
error = "Validators missing from blob";
}
if (validators && validators.length === 0) {
error = "Validators is empty";
}
return error;
}
function isValidVLBlobValidator(validator) {
const { validation_public_key, manifest } = validator;
let error = null;
if (validation_public_key === undefined) {
error = "Validation public key missing from validator";
}
if (manifest === undefined) {
error = "Manifest missing from validator";
}
const parsedManifest = (0, manifest_1.parseManifest)(manifest);
if (parsedManifest.error) {
return parsedManifest.error;
}
return error;
}
function parseValidationData(data, publicKey) {
const buf = Buffer.from(data, "hex");
const decoded = {};
let cur = 0;
if (buf[cur++] !== 0x22 || buf.length - cur < 5) {
decoded.error = "sfFlags missing or incomplete";
return decoded;
}
decoded.Flags = parseInt((0, utils_1.parseUint32)(buf, cur), 10);
cur += 4;
if (buf[cur++] !== 0x26 || buf.length - cur < 5) {
decoded.error = "sfLedgerSequnece missing or incomplete";
return decoded;
}
decoded.LedgerSequence = parseInt((0, utils_1.parseUint32)(buf, cur), 10);
cur += 4;
if (buf[cur] === 0x27) {
cur++;
if (buf.length - cur < 4) {
decoded.error = "sfCloseTime missing or incomplete";
return decoded;
}
decoded.CloseTime = parseInt((0, utils_1.parseUint32)(buf, cur), 10);
cur += 4;
}
if (buf[cur++] !== 0x29 || buf.length - cur < 5) {
decoded.error = "sfSigningTime missing or incomplete";
return decoded;
}
decoded.SigningTime = parseInt((0, utils_1.parseUint32)(buf, cur), 10);
cur += 4;
if (buf[cur] === 0x20 && buf.length - cur >= 1 && buf[cur + 1] === 0x18) {
cur += 2;
if (buf.length - cur < 4) {
decoded.error = "sfLoadFee payload missing";
return decoded;
}
decoded.LoadFee = (0, utils_1.parseUint32)(buf, cur);
cur += 4;
}
if (buf[cur] === 0x20 && buf.length - cur >= 1 && buf[cur + 1] === 0x1f) {
cur += 2;
if (buf.length - cur < 4) {
decoded.error = "sfReserveBase payload missing";
return decoded;
}
decoded.ReserveBase = (0, utils_1.parseUint32)(buf, cur);
cur += 4;
}
if (buf[cur] === 0x20 && buf.length - cur >= 1 && buf[cur + 1] === 0x20) {
cur += 2;
if (buf.length - cur < 4) {
decoded.error = "sfReserveIncrement payload missing";
return decoded;
}
decoded.ReserveIncrement = (0, utils_1.parseUint32)(buf, cur);
cur += 4;
}
if (buf[cur] === 0x35) {
cur++;
if (buf.length - cur < 8) {
decoded.error = "sfBaseFee missing or incomplete";
return decoded;
}
decoded.BaseFee = (0, utils_1.parseUint64)(buf, cur);
cur += 8;
}
if (buf[cur] === 0x3a) {
cur++;
if (buf.length - cur < 8) {
decoded.error = "sfCookie missing or incomplete";
return decoded;
}
decoded.Cookie = (0, utils_1.parseUint64)(buf, cur);
cur += 8;
}
if (buf[cur] === 0x3b) {
cur++;
if (buf.length - cur < 8) {
decoded.error = "sfServerVersion missing or incomplete";
return decoded;
}
decoded.ServerVersion = (0, utils_1.parseUint64)(buf, cur);
cur += 8;
}
if (buf[cur++] !== 0x51 || buf.length - cur < 5) {
decoded.error = "sfLedgerHash missing or incomplete";
return decoded;
}
decoded.LedgerHash = buf
.slice(cur, cur + 32)
.toString("hex")
.toUpperCase();
cur += 32;
if (buf[cur] === 0x50 && buf.length - cur >= 1 && buf[cur + 1] === 0x17) {
cur += 2;
if (buf.length - cur < 32) {
decoded.error = "sfConsensusHash payload missing";
return decoded;
}
decoded.ConsensusHash = buf
.slice(cur, cur + 32)
.toString("hex")
.toUpperCase();
cur += 32;
}
if (buf[cur] === 0x50 && buf.length - cur >= 1 && buf[cur + 1] === 0x19) {
cur += 2;
if (buf.length - cur < 32) {
decoded.error = "sfValidatedHash payload missing";
return decoded;
}
decoded.ValidatedHash = buf
.slice(cur, cur + 32)
.toString("hex")
.toUpperCase();
cur += 32;
}
if (buf[cur++] !== 0x73 || buf.length - cur < 2) {
decoded.error = "sfSigningPubKey missing or incomplete";
return decoded;
}
const keySize = buf[cur++];
if (buf.length - cur < keySize) {
decoded.error = "sfSigningPubKey payload missing";
return decoded;
}
decoded.SigningPubKey = buf
.slice(cur, cur + keySize)
.toString("hex")
.toUpperCase();
cur += keySize;
const sigStart = cur;
if (buf[cur++] !== 0x76 || buf.length - cur < 2) {
decoded.error = "sfSignature missing or incomplete";
return decoded;
}
const sigSize = buf[cur++];
if (buf.length - cur < sigSize) {
decoded.error = "sfSignature payload missing";
return decoded;
}
decoded.Signature = buf
.slice(cur, cur + sigSize)
.toString("hex")
.toUpperCase();
cur += sigSize;
const sigEnd = cur;
if (buf.length - cur >= 1 && buf[cur] === 0x03 && buf[cur + 1] === 0x13) {
cur += 2;
if (buf.length - cur < 1) {
decoded.error = "sfAmendments payload missing or incomplete [1]";
return decoded;
}
let len = buf[cur++];
if (len <= 192) {
}
else if (len >= 193 && len <= 240) {
if (buf.length - cur < 1) {
decoded.error = "sfAmendments payload missing or incomplete [2]";
return decoded;
}
len = 193 + (len - 193) * 256 + buf[cur++];
}
else if (len >= 241 && len <= 254) {
if (buf.length - cur < 2) {
decoded.error = "sfAmendments payload missing or incomplete [3]";
return decoded;
}
len = 12481 + (len - 241) * 65536 + buf[cur + 1] * 256 + buf[cur + 2];
cur += 2;
}
if (buf.length - cur < len) {
decoded.error = "sfAmendments payload missing or incomplete [3]";
return decoded;
}
decoded.Amendments = [];
const amendmentsCurEnd = cur + len;
while (cur < amendmentsCurEnd) {
decoded.Amendments.push(buf.slice(cur, cur + 32).toString("hex"));
cur += 32;
}
}
if (publicKey.toUpperCase() !== decoded.SigningPubKey) {
decoded._verified = false;
decoded.error = "SigningPubKey did not match or was not present";
return decoded;
}
const verifyFields = Buffer.concat([
Buffer.from("VAL\x00", "utf-8"),
buf.slice(0, sigStart),
buf.slice(sigEnd, buf.length),
]);
if (decoded.SigningPubKey && decoded.Signature) {
if (!Validator.verify(verifyFields, decoded.Signature, decoded.SigningPubKey)) {
decoded._verified = false;
decoded.error = "Signature did not match or was not present";
return decoded;
}
}
decoded._verified = true;
return decoded;
}