UNPKG

@bithomp/xrpl-api

Version:

A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger

499 lines (498 loc) 17.6 kB
"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; }