UNPKG

@skalenetwork/bite

Version:

TS Library to interact with BITE protocol

378 lines (369 loc) 12.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { BITE: () => BITE, BITEMockup: () => BITEMockup }); module.exports = __toCommonJS(src_exports); // src/core/encrypt.ts var import_t_encrypt = require("@skalenetwork/t-encrypt"); var import_rlp = require("@ethereumjs/rlp"); // src/utils/logger.ts var import_tslog = require("tslog"); // src/utils/constants.ts var BITE_ADDRESS = "0x42495445204D452049274d20454e435259505444"; var DEFAULT_GAS_LIMIT = "0x493e0"; var NODE_ENV = "production"; // src/utils/logger.ts var isDev = NODE_ENV === "development"; var logger = new import_tslog.Logger({ name: "bite", minLevel: isDev ? "info" : 9 }); // src/utils/helper.ts function remove0xPrefixIfNeeded(str) { return str.startsWith("0x") ? str.slice(2) : str; } function validateUrl(url) { try { new URL(url); } catch { throw new Error(`Invalid provider URL: ${url}`); } } function validateHexString(str) { if (!/^[0-9a-fA-F]*$/.test(str)) { throw new Error("Invalid input: Must contain only hexadecimal characters"); } if (str.length % 2 !== 0) { throw new Error("Invalid input: Must have an even length"); } } // src/core/encrypt.ts async function encryptTransaction(tx, committees, aadTE, aadAES) { try { const validatedTx = validateAndExtractTransactionFields(tx); const txTo = validatedTx.to; const txData = validatedTx.data; const rlpEncodedData = rlpEncodeTransactionData(txTo, txData); const sanitizedAADAES = aadAES ? remove0xPrefixIfNeeded(aadAES) : void 0; const sanitizedAADTE = aadTE ? remove0xPrefixIfNeeded(aadTE) : void 0; if (sanitizedAADAES) validateHexString(sanitizedAADAES); if (sanitizedAADTE) validateHexString(sanitizedAADTE); const encryptedData = await encryptMessage(rlpEncodedData, committees, sanitizedAADTE, sanitizedAADAES); const biteGasLimit = tx.gasLimit ?? DEFAULT_GAS_LIMIT; return { ...tx, data: encryptedData, to: BITE_ADDRESS, gasLimit: biteGasLimit }; } catch (error) { logger.error("Error encrypting transaction:", error); throw error; } } async function encryptTransactionMockup(tx) { try { const validatedTx = validateAndExtractTransactionFields(tx); const txTo = validatedTx.to; const txData = validatedTx.data; const rlpEncodedData = rlpEncodeTransactionData(txTo, txData); const encryptedData = await encryptMessageMockup(rlpEncodedData); const biteGasLimit = tx.gasLimit ?? DEFAULT_GAS_LIMIT; return { ...tx, data: encryptedData, to: BITE_ADDRESS, gasLimit: biteGasLimit }; } catch (error) { logger.error("Error encrypting transaction (mockup):", error); throw error; } } async function encryptMessage(message, committees, aadTE, aadAES) { try { const data = remove0xPrefixIfNeeded(message); validateHexString(data); const sanitizedAADTE = aadTE ? remove0xPrefixIfNeeded(aadTE) : void 0; const sanitizedAADAES = aadAES ? remove0xPrefixIfNeeded(aadAES) : void 0; if (sanitizedAADAES) { validateHexString(sanitizedAADAES); } if (sanitizedAADTE) { validateHexString(sanitizedAADTE); } if (committees.length === 1) { const publicKeyResponse = committees[0]; const encryptedRawMessage = await (0, import_t_encrypt.encryptMessage)(data, publicKeyResponse.commonBLSPublicKey, sanitizedAADTE, sanitizedAADAES); const rlpEncodedResult = rlpEncodeMessageData([publicKeyResponse.epochId, Buffer.from(encryptedRawMessage, "hex")]); return `0x${rlpEncodedResult}`; } else { const encryptedRawMessage = await (0, import_t_encrypt.encryptMessageDualKey)( data, committees[0].commonBLSPublicKey, committees[1].commonBLSPublicKey, sanitizedAADTE, sanitizedAADAES ); const rlpEncodedResult = rlpEncodeMessageData([committees[0].epochId, Buffer.from(encryptedRawMessage, "hex")]); return `0x${rlpEncodedResult}`; } } catch (error) { logger.error("Error encrypting message:", error); throw error; } } async function encryptMessageMockup(message) { try { const data = remove0xPrefixIfNeeded(message); if (!/^[0-9a-fA-F]*$/.test(data) || data.length % 2 !== 0) { throw new Error("Invalid input: message must be valid hex and even length"); } const encryptedRawMessage = await (0, import_t_encrypt.encryptMessageMockup)(data); const epochId = 0; const rlpEncodedResult = rlpEncodeMessageData([epochId, Buffer.from(encryptedRawMessage, "hex")]); return `0x${rlpEncodedResult}`; } catch (error) { logger.error("Error encrypting message:", error); throw error; } } function validateAndExtractTransactionFields(tx) { const isValid = tx && typeof tx === "object" && tx.data && tx.to && typeof tx.data === "string" && typeof tx.to === "string"; if (!isValid) { throw new Error("Invalid input: Must be an object with 'data' and 'to' fields of type string"); } const txData = remove0xPrefixIfNeeded(tx.data); const txTo = remove0xPrefixIfNeeded(tx.to); validateHexString(txData); validateHexString(txTo); if (txTo.length !== 40) { throw new Error("Invalid input: 'to' field must be exactly 20 bytes (40 hex characters) long"); } return { data: txData, to: txTo }; } function rlpEncodeTransactionData(txTo, txData) { try { const toBuffer = Buffer.from(txTo, "hex"); const dataBuffer = Buffer.from(txData, "hex"); const rlpEncoded = (0, import_rlp.encode)([dataBuffer, toBuffer]); return Buffer.from(rlpEncoded).toString("hex"); } catch (error) { logger.error("Error RLP encoding transaction data:", error); throw new Error("Failed to RLP encode transaction data"); } } function rlpEncodeMessageData(data) { try { const rlpEncoded = (0, import_rlp.encode)(data); return Buffer.from(rlpEncoded).toString("hex"); } catch (error) { logger.error("Error RLP encoding message data:", error); throw new Error("Failed to RLP encode message data"); } } // src/core/biteRpc.ts async function getDecryptedTransactionData(endpoint, transactionHash) { try { validateUrl(endpoint); const requestBody = { jsonrpc: "2.0", method: "bite_getDecryptedTransactionData", params: [transactionHash], id: 1 }; const result = await sendRpcRequest(endpoint, requestBody); return result; } catch (error) { logger.error("Error fetching decrypted transaction data:", error); throw error; } } async function getCommitteesInfo(endpoint) { try { const requestBody = { jsonrpc: "2.0", method: "bite_getCommitteesInfo", params: [], id: 1 }; const result = await sendRpcRequest(endpoint, requestBody); if (!Array.isArray(result)) { throw new Error("Result is not an array"); } if (result.length === 0 || result.length > 2) { throw new Error(`Expected array of size 1 or 2, got ${result.length}`); } for (const item of result) { if (typeof item !== "object" || item === null) { throw new Error("Array element is not an object"); } if (typeof item.commonBLSPublicKey !== "string") { throw new Error("commonBLSPublicKey is not a string"); } if (typeof item.epochId !== "number") { throw new Error("epochId is not a number"); } if (!/^[0-9a-fA-F]{256}$/.test(item.commonBLSPublicKey)) { throw new Error("commonBLSPublicKey is not a valid 256-character hexadecimal string"); } } return result; } catch (error) { logger.error("Error fetching BITE common public key:", error); throw error; } } async function sendRpcRequest(endpoint, requestBody) { try { validateUrl(endpoint); const response = await fetch(endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(requestBody) }); if (response.status !== 200) { throw new Error(`Received status code: ${response.status}`); } const data = await response.json(); if ("error" in data) { throw new Error(`Error from server: ${data.error.message}`); } return data.result; } catch (error) { logger.error("Error sending RPC request:", error); throw error; } } // src/core/bite.ts var BITE = class { constructor(providerURL) { this.providerURL = providerURL; } /** * Encrypt a hex-encoded message using BLS public key. * @param message - Hex string (with or without 0x). */ async encryptMessage(message) { const committees = await getCommitteesInfo(this.providerURL); return encryptMessage(message, committees); } /** * Encrypt a transaction object using BLS public key. * @param tx - The transaction to encrypt. */ async encryptTransaction(tx) { const committees = await getCommitteesInfo(this.providerURL); return encryptTransaction(tx, committees); } /** * Encrypt a hex-encoded message using BLS public key for CTX. * @param message - Hex string (with or without 0x). * @param ctxSubmitterAddress - Smart Contract Address for aadTE. */ async encryptMessageForCTX(message, ctxSubmitterAddress) { const committees = await getCommitteesInfo(this.providerURL); return encryptMessage(message, committees, ctxSubmitterAddress); } /** * Encrypt a transaction object using BLS public key and provided committees info. * @param tx - The transaction to encrypt. * @param committees - The committees info object. */ static async encryptTransactionWithCommitteeInfo(tx, committees) { return encryptTransaction(tx, committees); } /** * Fetch the committees info from the configured endpoint. */ async getCommitteesInfo() { return getCommitteesInfo(this.providerURL); } /** * Get decrypted transaction data using the configured endpoint. * @param transactionHash - The hash of the transaction. */ async getDecryptedTransactionData(transactionHash) { return getDecryptedTransactionData(this.providerURL, transactionHash); } }; var BITEMockup = class { /** * Simulates encryption of a hex-encoded message * * @param message - Hex string (with or without 0x). */ async encryptMessage(message) { return encryptMessageMockup(message); } /** * Simulates encryption of a transaction object * * @param tx - The transaction to encrypt. */ async encryptTransaction(tx) { return encryptTransactionMockup(tx); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BITE, BITEMockup }); /** * @license * SKALE bite.ts * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ /** * @license * SKALE libte-ts * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ //# sourceMappingURL=index.js.map