@skalenetwork/bite
Version:
TS Library to interact with BITE protocol
378 lines (369 loc) • 12.9 kB
JavaScript
;
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