UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

100 lines 4.49 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.StorageDownloader = void 0; const index_js_1 = require("../overlay-tools/index.js"); const index_js_2 = require("./index.js"); const PushDrop_js_1 = __importDefault(require("../script/templates/PushDrop.js")); const Transaction_js_1 = __importDefault(require("../transaction/Transaction.js")); const index_js_3 = require("../primitives/index.js"); class StorageDownloader { constructor(config) { this.networkPreset = 'mainnet'; this.networkPreset = config?.networkPreset ?? 'mainnet'; this.lookupResolver = new index_js_1.LookupResolver({ networkPreset: this.networkPreset }); } /** * Resolves the UHRP URL to a list of HTTP URLs where content can be downloaded. * @param uhrpUrl The UHRP URL to resolve. * @returns A promise that resolves to an array of HTTP URLs. */ async resolve(uhrpUrl) { // Use UHRP lookup service const response = await this.lookupResolver.query({ service: 'ls_uhrp', query: { uhrpUrl } }); if (response.type !== 'output-list') { throw new Error('Lookup answer must be an output list'); } const decodedResults = []; const currentTime = Math.floor(Date.now() / 1000); for (let i = 0; i < response.outputs.length; i++) { const tx = Transaction_js_1.default.fromBEEF(response.outputs[i].beef); const { fields } = PushDrop_js_1.default.decode(tx.outputs[response.outputs[i].outputIndex].lockingScript); const expiryTime = new index_js_3.Utils.Reader(fields[3]).readVarIntNum(); if (expiryTime < currentTime) { continue; } decodedResults.push(index_js_3.Utils.toUTF8(fields[2])); } return decodedResults; } /** * Downloads the content from the UHRP URL after validating the hash for integrity. * @param uhrpUrl The UHRP URL to download. * @returns A promise that resolves to the downloaded content. */ async download(uhrpUrl) { if (!index_js_2.StorageUtils.isValidURL(uhrpUrl)) { throw new Error('Invalid parameter UHRP url'); } const hash = index_js_2.StorageUtils.getHashFromURL(uhrpUrl); const expected = index_js_3.Utils.toHex(hash); const downloadURLs = await this.resolve(uhrpUrl); if (!Array.isArray(downloadURLs) || downloadURLs.length === 0) { throw new Error('No one currently hosts this file!'); } for (let i = 0; i < downloadURLs.length; i++) { try { // The url is fetched const result = await fetch(downloadURLs[i], { method: 'GET' }); // If the request fails, continue to the next url if (!result.ok || result.status >= 400 || result.body == null) { continue; } const reader = result.body.getReader(); const hashStream = new index_js_3.Hash.SHA256(); const chunks = []; let totalLength = 0; while (true) { const { done, value } = await reader.read(); if (done) break; hashStream.update(Array.from(value)); chunks.push(value); totalLength += value.length; } const digest = index_js_3.Utils.toHex(hashStream.digest()); if (digest !== expected) { throw new Error('Data integrity error: value of content does not match hash of the url given'); } const data = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { data.set(chunk, offset); offset += chunk.length; } return { data, mimeType: result.headers.get('Content-Type') }; } catch (error) { continue; } } throw new Error(`Unable to download content from ${uhrpUrl}`); } } exports.StorageDownloader = StorageDownloader; //# sourceMappingURL=StorageDownloader.js.map