UNPKG

@anythread/gsoc

Version:

Graffiti Several Owner Chunk implementation on Swarm network

278 lines (277 loc) 11.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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.uploadSingleOwnerChunkData = uploadSingleOwnerChunkData; exports.gsocSubscribe = gsocSubscribe; exports.createPostageBatch = createPostageBatch; exports.listPostageBatches = listPostageBatches; exports.getPostageBatch = getPostageBatch; exports.getNodeAddresses = getNodeAddresses; const bmt_js_1 = require("@nugaon/bmt-js"); const isomorphic_ws_1 = __importDefault(require("isomorphic-ws")); const soc_1 = require("./soc"); const utils_1 = require("./utils"); const axios_1 = __importStar(require("axios")); /** * Helper function to create and upload SOC. */ function uploadSingleOwnerChunkData(requestOptions, postageBatchId, signer, identifier, payload, options) { return __awaiter(this, void 0, void 0, function* () { const cac = (0, bmt_js_1.makeChunk)(payload); const soc = yield (0, soc_1.makeSingleOwnerChunk)(cac, identifier, signer); const owner = (0, utils_1.bytesToHex)(soc.owner()); const id = (0, utils_1.bytesToHex)(identifier); const signature = (0, utils_1.bytesToHex)(soc.signature()); const data = (0, utils_1.serializeBytes)(soc.span(), soc.payload); yield uploadSoc(requestOptions, owner, id, signature, data, postageBatchId, options); return soc; }); } /** * Subscribe to messages for given topic with GSOC * * @param address SOC address under which payloads * @param handler Message handler interface * * @returns close() function on websocket connection */ function gsocSubscribe(baseUrl, address, handler) { assertSubscriptionHandler(handler); if (typeof address !== 'string' || address.length !== 64) { throw new TypeError('soc address has to be an string and 32 bytes!'); } const ws = webSocket(baseUrl, `gsoc/subscribe/${address}`); let closed = false; const close = () => { if (closed === false) { closed = true; // although the WebSocket API offers a `close` function, it seems that // with the library that we are using (isomorphic-ws) it doesn't close // the websocket properly, whereas `terminate` does if (ws.terminate) ws.terminate(); else ws.close(); // standard Websocket in browser does not have terminate function } }; ws.onmessage = (ev) => __awaiter(this, void 0, void 0, function* () { const data = prepareWebsocketData(ev.data); // ignore empty messages if (data.length > 0) { handler.onMessage((0, utils_1.wrapBytesWithHelpers)(data)); } }); ws.onerror = ev => { // ignore errors after subscription was cancelled if (!closed) { handler.onError(new BeeError(ev.message)); } }; return close; } /** * Upload single owner chunk (SOC) to a Bee node * * @param requestOptions BeeRequestOptions * @param owner Owner's ethereum address in hex * @param identifier Arbitrary identifier in hex * @param signature Signature in hex * @param data Content addressed chunk data to be uploaded * @param postageBatchId Postage BatchId that will be assigned to uploaded data * @param options Additional options like tag, encryption, pinning */ function uploadSoc(requestOptions, owner, identifier, signature, data, postage, options) { return __awaiter(this, void 0, void 0, function* () { const response = yield http(Object.assign(Object.assign({}, requestOptions), { method: 'post', url: `soc/${owner}/${identifier}`, data, headers: Object.assign({ 'content-type': 'application/octet-stream' }, extractUploadHeaders(postage, options)), responseType: 'json', params: { sig: signature } })); return response.data.reference; }); } /** * create postage batch * @returns postage batch id */ function createPostageBatch(requestOptions, amount, depth, options) { return __awaiter(this, void 0, void 0, function* () { const headers = requestOptions.headers || {}; if (options === null || options === void 0 ? void 0 : options.gasPrice) { headers['gas-price'] = options.gasPrice.toString(); } if ((options === null || options === void 0 ? void 0 : options.immutableFlag) !== undefined) { headers.immutable = String(options.immutableFlag); } const response = yield http(Object.assign(Object.assign({}, requestOptions), { method: 'post', url: `stamps/${amount}/${depth}`, responseType: 'json', params: { label: options === null || options === void 0 ? void 0 : options.label }, headers })); if (options === null || options === void 0 ? void 0 : options.waitForUsable) { const timeout = 100000; for (let time = 0; time < timeout; time += 3000) { try { const batch = yield getPostageBatch(requestOptions, response.data.batchID); if (batch.usable) { break; } } catch (e) { // ignore errors and try again } yield new Promise(resolve => setTimeout(resolve, 3000)); // wait 3s } } return response.data.batchID; }); } /** * list postage batches * @returns postage batch id */ function listPostageBatches(requestOptions) { return __awaiter(this, void 0, void 0, function* () { const response = yield http(Object.assign(Object.assign({}, requestOptions), { method: 'get', url: 'stamps', responseType: 'json' })); return response.data.stamps; }); } function getPostageBatch(requestOptions, postageBatchId) { return __awaiter(this, void 0, void 0, function* () { const response = yield http(Object.assign(Object.assign({}, requestOptions), { method: 'get', url: `stamps/${postageBatchId}`, responseType: 'json' })); return response.data; }); } function getNodeAddresses(requestOptions) { return __awaiter(this, void 0, void 0, function* () { const response = yield http(Object.assign(Object.assign({}, requestOptions), { url: 'addresses', responseType: 'json' })); return response.data; }); } function assertSubscriptionHandler(value) { if (!(0, utils_1.isStrictlyObject)(value)) { throw new TypeError('SubscriptionHandler has to be object!'); } const handler = value; if (typeof handler.onMessage !== 'function') { throw new TypeError('onMessage property of SubscriptionHandler has to be function!'); } if (typeof handler.onError !== 'function') { throw new TypeError('onError property of SubscriptionHandler has to be function!'); } } function prepareWebsocketData(data) { if (typeof data === 'string') return new TextEncoder().encode(data); if (data instanceof Buffer) return new Uint8Array(data); if (data instanceof ArrayBuffer) return new Uint8Array(data); throw new TypeError('unknown websocket data type'); } /** * Creates websocket on the given path * * @param url Bee node URL * @param topic Topic name */ function webSocket(url, path) { const wsUrl = url.replace(/^http/i, 'ws'); return new isomorphic_ws_1.default(`${wsUrl}/${path}`); } function extractUploadHeaders(postage, options) { const headers = {}; if ((0, utils_1.isPostageBatchId)(postage)) { headers['swarm-postage-batch-id'] = postage; } else if ((0, utils_1.isPostageStamp)(postage)) { headers['swarm-postage-stamp'] = postage; } else { throw Error('Postage is invalid. Define either a postage batch id or a postage stamp (coming from envelope)'); } if (options === null || options === void 0 ? void 0 : options.pin) { headers['swarm-pin'] = String(options.pin); } if (options === null || options === void 0 ? void 0 : options.encrypt) { headers['swarm-encrypt'] = String(options.encrypt); } if (options === null || options === void 0 ? void 0 : options.tag) { headers['swarm-tag'] = String(options.tag); } if (typeof (options === null || options === void 0 ? void 0 : options.deferred) === 'boolean') { headers['swarm-deferred-upload'] = options.deferred.toString(); } return headers; } /** * Main function to make HTTP requests. * @param options User defined settings * @param config Internal settings and/or Bee settings */ function http(config) { return __awaiter(this, void 0, void 0, function* () { var _a; try { config.headers || (config.headers = {}); config.headers = Object.assign(Object.assign({}, DEFAULT_HTTP_CONFIG.headers), config.headers); const response = yield (0, axios_1.default)(config); return response; } catch (e) { if (e instanceof axios_1.AxiosError) { throw new BeeResponseError(e.message, e.code, e.status, (_a = e.response) === null || _a === void 0 ? void 0 : _a.status, e.config, e.response); } throw e; } }); } const DEFAULT_HTTP_CONFIG = { headers: { accept: 'application/json, text/plain, */*', }, maxBodyLength: Infinity, maxContentLength: Infinity, }; /** ERRORS */ class BeeError extends Error { constructor(message) { super(message); } } class BeeResponseError extends BeeError { constructor(message, code, axiosStatus, status, config, response) { super(message); this.code = code; this.axiosStatus = axiosStatus; this.status = status; this.config = config; this.response = response; } }