diff --git a/eslint-rules/no-uint8array-tostring.ts b/eslint-rules/no-uint8array-tostring.ts new file mode 100644 index 000000000..02295599c --- /dev/null +++ b/eslint-rules/no-uint8array-tostring.ts @@ -0,0 +1,124 @@ +import { AST_NODE_TYPES, ESLintUtils, TSESTree } from '@typescript-eslint/utils'; +import type * as ts from 'typescript'; + +function hasOwnToString(type: ts.Type): boolean { + const symbol = type.getSymbol(); + if (!symbol) return false; + + const declarations = symbol.getDeclarations(); + if (!declarations) return false; + + for (const decl of declarations) { + const sourceFile = decl.getSourceFile(); + // Skip Uint8Array's own toString — we only care about user-defined overrides + if (sourceFile.fileName.includes('lib.es') || sourceFile.fileName.includes('lib.dom')) { + continue; + } + + if ('members' in symbol && symbol.members) { + if (symbol.members.has('toString' as ts.__String)) { + return true; + } + } + } + + return false; +} + +function isUint8ArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { + const symbol = type.getSymbol(); + if (symbol?.getName() === 'Uint8Array') { + return true; + } + + const baseTypes = type.getBaseTypes?.(); + if (baseTypes) { + for (const baseType of baseTypes) { + if (isUint8ArrayType(baseType, checker)) { + return true; + } + } + } + + if (type.isIntersection()) { + for (const subType of type.types) { + if (isUint8ArrayType(subType, checker)) { + return true; + } + } + } + + if (type.isUnion()) { + return type.types.length > 0 && type.types.every((subType) => isUint8ArrayType(subType, checker)); + } + + const constraint = type.getConstraint?.(); + if (constraint && isUint8ArrayType(constraint, checker)) { + return true; + } + + return false; +} + +const createRule = ESLintUtils.RuleCreator((name) => `https://github.com/btc-vision/eslint-rules#${name}`); + +const rule = createRule({ + name: 'no-uint8array-tostring', + meta: { + type: 'problem', + docs: { + description: + 'Disallow .toString() on Uint8Array and branded types (Script, Bytes32, etc.) which produces comma-separated decimals instead of hex' + }, + messages: { + noUint8ArrayToString: + '{{typeName}}.toString() returns comma-separated decimals (e.g. "0,32,70,107"), not a hex string. ' + + 'Use Buffer.from(arr).toString("hex") or toHex() instead.' + }, + schema: [] + }, + defaultOptions: [], + create(context) { + const services = ESLintUtils.getParserServices(context); + const checker = services.program.getTypeChecker(); + + return { + CallExpression(node: TSESTree.CallExpression): void { + if ( + node.callee.type !== AST_NODE_TYPES.MemberExpression || + node.callee.property.type !== AST_NODE_TYPES.Identifier || + node.callee.property.name !== 'toString' || + node.arguments.length > 0 + ) { + return; + } + + const objectNode = node.callee.object; + const tsNode = services.esTreeNodeToTSNodeMap.get(objectNode); + const type = checker.getTypeAtLocation(tsNode); + + if (isUint8ArrayType(type, checker) && !hasOwnToString(type)) { + const typeName = checker.typeToString(type); + context.report({ + node, + messageId: 'noUint8ArrayToString', + data: { typeName } + }); + } + } + }; + } +}); + +const plugin = { + meta: { + name: 'eslint-plugin-no-uint8array-tostring', + version: '1.0.0' + }, + rules: { + 'no-uint8array-tostring': rule + } +}; + +export default plugin; +export { rule }; diff --git a/eslint.config.js b/eslint.config.js index 53c11db9f..5e32e0fd9 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,11 +2,15 @@ import tseslint from 'typescript-eslint'; import eslint from '@eslint/js'; +import noUint8ArrayToString from './eslint-rules/no-uint8array-tostring.ts'; export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.strictTypeChecked, { + plugins: { + 'opnet': noUint8ArrayToString, + }, languageOptions: { parserOptions: { projectService: true, @@ -35,6 +39,7 @@ export default tseslint.config( '@typescript-eslint/no-unnecessary-type-arguments': 'off', 'no-debugger': 'off', '@typescript-eslint/no-unnecessary-type-conversion': 'warn', + 'opnet/no-uint8array-tostring': 'error', }, }, { diff --git a/package.json b/package.json index 128dc9f39..a741c920c 100644 --- a/package.json +++ b/package.json @@ -37,18 +37,18 @@ "@asyncapi/generator": "^3.1.2", "@asyncapi/html-template": "^3.5.5", "@bitauth/libauth": "^3.0.0", - "@btc-vision/bip32": "^7.0.2", - "@btc-vision/bitcoin": "^7.0.0-rc.0", + "@btc-vision/bip32": "^7.1.1", + "@btc-vision/bitcoin": "^7.0.0-rc.3", "@btc-vision/bitcoin-rpc": "^1.0.6", "@btc-vision/bsi-common": "^1.2.1", - "@btc-vision/ecpair": "^4.0.2", + "@btc-vision/ecpair": "^4.0.4", "@btc-vision/hyper-express": "^6.17.4", "@btc-vision/logger": "^1.0.8", "@btc-vision/op-vm": "file:../op-vm", "@btc-vision/plugin-sdk": "^1.0.0", "@btc-vision/post-quantum": "^0.5.3", "@btc-vision/rust-merkle-tree": "^0.0.5", - "@btc-vision/transaction": "^1.8.0-rc.1", + "@btc-vision/transaction": "^1.8.0-rc.2", "@btc-vision/uwebsockets.js": "^20.57.0", "@chainsafe/libp2p-noise": "^17.0.0", "@chainsafe/libp2p-quic": "^1.1.8", @@ -89,7 +89,6 @@ "lru-cache": "^11.2.6", "mongodb": "^7.1.0", "openapi-comment-parser": "^1.0.0", - "opnet": "^1.8.1-rc.1", "protobufjs": "^8.0.0", "sodium-native": "^5.0.10", "ssh2": "^1.17.0", diff --git a/src/src/api/data-converter/TransactionConverterForAPI.ts b/src/src/api/data-converter/TransactionConverterForAPI.ts index a3e94a0da..80f30cd26 100644 --- a/src/src/api/data-converter/TransactionConverterForAPI.ts +++ b/src/src/api/data-converter/TransactionConverterForAPI.ts @@ -1,4 +1,5 @@ import { DataConverter } from '@btc-vision/bsi-common'; +import { toBase64, toHex } from '@btc-vision/bitcoin'; import { Binary } from 'mongodb'; import { OPNetTransactionTypes } from '../../blockchain-indexer/processor/transaction/enums/OPNetTransactionTypes.js'; import { @@ -41,14 +42,14 @@ export class TransactionConverterForAPI { const newTx: TransactionDocumentForAPI = { ...transaction, - hash: transaction.hash.toString('hex'), - id: transaction.id.toString('hex'), + hash: toHex(transaction.hash), + id: toHex(transaction.id), blockNumber: '0x' + DataConverter.fromDecimal128(transaction.blockHeight || 0n).toString(16), inputs: transaction.inputs?.map((input) => { return { ...input, - originalTransactionId: input.originalTransactionId?.toString('hex'), + originalTransactionId: input.originalTransactionId ? toHex(input.originalTransactionId) : undefined, scriptSignature: input.scriptSignature, }; }), diff --git a/src/src/api/routes/api/v1/epochs/SubmitEpochRoute.ts b/src/src/api/routes/api/v1/epochs/SubmitEpochRoute.ts index 1f23eedcf..e2ae75a04 100644 --- a/src/src/api/routes/api/v1/epochs/SubmitEpochRoute.ts +++ b/src/src/api/routes/api/v1/epochs/SubmitEpochRoute.ts @@ -21,6 +21,7 @@ import { MLDSASecurityLevel, QuantumBIP32Factory, } from '@btc-vision/transaction'; +import { equals } from '@btc-vision/bitcoin'; import { isEmptyBuffer } from '../../../../../utils/BufferUtils.js'; // Validate that all strings contain only valid hex characters @@ -316,8 +317,8 @@ export class SubmitEpochRoute extends Route< let message = 'Submission accepted'; if (bestSubmission) { - const currentBestSalt = Buffer.from(bestSubmission.salt.buffer); - const isWinning = bestSubmission && currentBestSalt.equals(validationParams.salt); + const currentBestSalt = new Uint8Array(bestSubmission.salt.buffer); + const isWinning = bestSubmission && equals(currentBestSalt, validationParams.salt); if (isWinning) { message = 'Current best submission'; @@ -334,7 +335,7 @@ export class SubmitEpochRoute extends Route< }; } - private async validateSignature(data: EpochValidationParams): Promise { + private async validateSignature(data: EpochValidationParams): Promise { if (!this.storage) { throw new Error('Storage not initialized for signature validation'); } @@ -382,7 +383,7 @@ export class SubmitEpochRoute extends Route< const keyPair = QuantumBIP32Factory.fromPublicKey( mldsaPublicKeyData.publicKey, - Buffer.alloc(32), + new Uint8Array(32), this.network, MLDSASecurityLevel.LEVEL2, ); diff --git a/src/src/api/routes/api/v1/shared/DeploymentTxEncoder.ts b/src/src/api/routes/api/v1/shared/DeploymentTxEncoder.ts index 633b0b3b6..493587b87 100644 --- a/src/src/api/routes/api/v1/shared/DeploymentTxEncoder.ts +++ b/src/src/api/routes/api/v1/shared/DeploymentTxEncoder.ts @@ -1,3 +1,4 @@ +import { toBase64, toHex } from '@btc-vision/bitcoin'; import { IContractAPIDocument } from '../../../../../db/documents/interfaces/IContractDocument.js'; import { ContractInformation } from '../../../../../blockchain-indexer/processor/transaction/contract/ContractInformation.js'; import { VMStorage } from '../../../../../vm/storage/VMStorage.js'; @@ -67,15 +68,13 @@ export class DeploymentTxEncoder { private convertToBlockHeaderAPIDocument(data: ContractInformation): IContractAPIDocument { return { contractAddress: data.contractAddress, - contractPublicKey: Buffer.from(data.contractPublicKey.buffer).toString('base64'), - deployedTransactionId: Buffer.from(data.deployedTransactionId.buffer).toString('hex'), - deployedTransactionHash: Buffer.from(data.deployedTransactionHash.buffer).toString( - 'hex', - ), - bytecode: data.bytecode.toString('base64'), - deployerPubKey: data.deployerPubKey.toString('base64'), - contractSeed: data.contractSeed.toString('base64'), - contractSaltHash: data.contractSaltHash.toString('hex'), + contractPublicKey: toBase64(data.contractPublicKey.toBuffer()), + deployedTransactionId: toHex(data.deployedTransactionId), + deployedTransactionHash: toHex(data.deployedTransactionHash), + bytecode: toBase64(data.bytecode), + deployerPubKey: toBase64(data.deployerPubKey), + contractSeed: toBase64(data.contractSeed), + contractSaltHash: toHex(data.contractSaltHash), wasCompressed: data.wasCompressed, deployerAddress: data.deployerAddress.toHex(), }; diff --git a/src/src/api/routes/api/v1/states/Call.ts b/src/src/api/routes/api/v1/states/Call.ts index bd20c686b..f5d672eb1 100644 --- a/src/src/api/routes/api/v1/states/Call.ts +++ b/src/src/api/routes/api/v1/states/Call.ts @@ -1,4 +1,5 @@ import { AddressVerificator, BufferHelper, NetEvent } from '@btc-vision/transaction'; +import { toBase64 } from '@btc-vision/bitcoin'; import { Request } from '@btc-vision/hyper-express/types/components/http/Request.js'; import { Response } from '@btc-vision/hyper-express/types/components/http/Response.js'; import { MiddlewareNext } from '@btc-vision/hyper-express/types/components/middleware/MiddlewareNext.js'; @@ -229,8 +230,8 @@ export class Call extends Route { // Parse opcode and payload let opcode: WebSocketRequestOpcode; - let payload: Buffer; + let payload: Uint8Array; try { const parsed = parseWebSocketMessage(raw); @@ -165,7 +166,7 @@ export class ProtocolHandler extends Logger { /** * Handle ping request */ - private handlePing(client: WebSocketClient, payload: Buffer): boolean { + private handlePing(client: WebSocketClient, payload: Uint8Array): boolean { try { const pingPacket = APIRegistry.getPacketBuilder(APIPacketType.PingRequest); const pongPacket = APIRegistry.getPacketBuilder(APIPacketType.PongResponse); @@ -207,7 +208,7 @@ export class ProtocolHandler extends Logger { /** * Handle handshake request */ - private handleHandshake(client: WebSocketClient, payload: Buffer): boolean { + private handleHandshake(client: WebSocketClient, payload: Uint8Array): boolean { try { // Check if already handshaked if (client.isHandshakeCompleted()) { @@ -266,7 +267,7 @@ export class ProtocolHandler extends Logger { // Send response with dynamic data from WSManager const response = { protocolVersion: PROTOCOL_VERSION, - sessionId: Buffer.from(client.clientId, 'hex'), + sessionId: fromHex(client.clientId), serverVersion: WSManager.getServerVersion(), currentBlockHeight: WSManager.getCurrentBlockHeight(), chainId: WSManager.getChainId(), @@ -295,7 +296,7 @@ export class ProtocolHandler extends Logger { private async handlePluginOpcode( client: WebSocketClient, opcode: number, - payload: Buffer, + payload: Uint8Array, ): Promise { // Check handshake requirement (all plugin opcodes require handshake) if (!client.isHandshakeCompleted()) { @@ -319,7 +320,7 @@ export class ProtocolHandler extends Logger { private async handlePluginOpcodeLocal( client: WebSocketClient, opcode: number, - payload: Buffer, + payload: Uint8Array, ): Promise { if (!this.pluginRegistry) { client.closeWithError(InternalError.INTERNAL_ERROR); @@ -395,7 +396,7 @@ export class ProtocolHandler extends Logger { private async handlePluginOpcodeCrossThread( client: WebSocketClient, opcode: number, - payload: Buffer, + payload: Uint8Array, ): Promise { // Check if WSManager has this opcode registered if (!WSManager.isPluginOpcode(opcode)) { diff --git a/src/src/api/websocket/WebSocketClient.ts b/src/src/api/websocket/WebSocketClient.ts index c91a2a807..7085338ff 100644 --- a/src/src/api/websocket/WebSocketClient.ts +++ b/src/src/api/websocket/WebSocketClient.ts @@ -1,6 +1,7 @@ import { Logger } from '@btc-vision/bsi-common'; import { Websocket } from '@btc-vision/hyper-express/types/components/ws/Websocket.js'; import { randomBytes } from 'crypto'; +import { toHex } from '@btc-vision/bitcoin'; import { AuthError, getErrorMessage, @@ -431,7 +432,7 @@ export class WebSocketClient extends Logger { * Generate a unique client ID */ private generateClientId(): string { - return randomBytes(16).toString('hex'); + return toHex(randomBytes(16)); } /** diff --git a/src/src/api/websocket/WebSocketManager.ts b/src/src/api/websocket/WebSocketManager.ts index 714017af4..20146e778 100644 --- a/src/src/api/websocket/WebSocketManager.ts +++ b/src/src/api/websocket/WebSocketManager.ts @@ -319,7 +319,7 @@ export class WebSocketManager extends Logger { public onClose(socket: Websocket, code: number, reason: ArrayBuffer): void { const client = this.socketToClient.get(socket); if (client) { - const reasonStr = Buffer.from(reason).toString(); + const reasonStr = new TextDecoder().decode(reason); this.log(`Client ${client.clientId} disconnected: ${code} ${reasonStr}`); client.markClosed(); diff --git a/src/src/api/websocket/handlers/HandlerRegistry.ts b/src/src/api/websocket/handlers/HandlerRegistry.ts index 308f25293..c10451190 100644 --- a/src/src/api/websocket/handlers/HandlerRegistry.ts +++ b/src/src/api/websocket/handlers/HandlerRegistry.ts @@ -1,4 +1,5 @@ import { Logger } from '@btc-vision/bsi-common'; +import { fromBase64, toHex } from '@btc-vision/bitcoin'; import { APIRegistry } from '../OpcodeRegistry.js'; import { WebSocketRequestOpcode } from '../types/opcodes/WebSocketOpcodes.js'; import { WSManager } from '../WebSocketManager.js'; @@ -108,17 +109,16 @@ function convertOPNetTypeToProtoEnum(type: OPNetTransactionTypes | string | unde * Uses oneof for type-specific data (interaction vs deployment). */ function convertTransactionResponse(tx: Record): PackedMessage { - // Helper to safely convert to Buffer for bytes fields - const toBuffer = (value: unknown): Buffer => { - if (!value) return Buffer.alloc(0); - if (Buffer.isBuffer(value)) return value; - if (typeof value === 'string') return Buffer.from(value, 'base64'); - if (value instanceof Uint8Array) return Buffer.from(value); + // Helper to safely convert to Uint8Array for bytes fields + const toBytes = (value: unknown): Uint8Array => { + if (!value) return new Uint8Array(0); + if (value instanceof Uint8Array) return value; + if (typeof value === 'string') return fromBase64(value); // Handle MongoDB Binary if (typeof value === 'object' && 'buffer' in value) { - return Buffer.from((value as { buffer: Buffer }).buffer); + return new Uint8Array((value as { buffer: Uint8Array }).buffer); } - return Buffer.alloc(0); + return new Uint8Array(0); }; // Convert inputs to ensure all fields are proper types @@ -171,7 +171,7 @@ function convertTransactionResponse(tx: Record): PackedMessage priorityFee: tx.priorityFee ?? '0x0', outputs, inputs, - raw: toBuffer(tx.raw), + raw: toBytes(tx.raw), index: typeof tx.index === 'number' ? tx.index : 0, OPNetType: opnetType, }; @@ -422,9 +422,9 @@ export class HandlerRegistry extends Logger { WebSocketRequestOpcode.BROADCAST_TRANSACTION, async (request: PackedMessage) => { const route = DefinedRoutes[Routes.BROADCAST_TRANSACTION] as BroadcastTransaction; - const txHex = Buffer.isBuffer(request.transaction) - ? request.transaction.toString('hex') - : Buffer.from(request.transaction).toString('hex'); + const txHex = request.transaction instanceof Uint8Array + ? toHex(request.transaction) + : toHex(new Uint8Array(request.transaction)); const result = await route.getData({ data: txHex, diff --git a/src/src/api/websocket/packets/APIPacket.ts b/src/src/api/websocket/packets/APIPacket.ts index c5c9fba00..0b006ee9f 100644 --- a/src/src/api/websocket/packets/APIPacket.ts +++ b/src/src/api/websocket/packets/APIPacket.ts @@ -23,7 +23,7 @@ function convertBigIntToLong(obj: unknown): unknown { return obj.map(convertBigIntToLong); } - if (obj instanceof Long || obj instanceof Buffer || obj instanceof Uint8Array) { + if (obj instanceof Long || obj instanceof Uint8Array) { return obj; } @@ -148,7 +148,7 @@ export abstract class APIPacket< const objOutput = this.packet.toObject(message, { longs: Long, enums: Number, - bytes: Buffer, + bytes: Uint8Array, defaults: true, arrays: true, objects: true, @@ -180,7 +180,7 @@ export abstract class APIPacket< */ export interface WebSocketMessage { readonly opcode: WebSocketOpcode; - readonly payload: Buffer; + readonly payload: Uint8Array; } /** @@ -192,7 +192,7 @@ export function parseWebSocketMessage(raw: Uint8Array): WebSocketMessage { } const opcode = raw[0] as WebSocketOpcode; - const payload = Buffer.from(raw.slice(1)); + const payload = raw.slice(1); return { opcode, payload }; } diff --git a/src/src/api/websocket/types/requests/WebSocketRequestTypes.ts b/src/src/api/websocket/types/requests/WebSocketRequestTypes.ts index a4b451a48..3b624e2c1 100644 --- a/src/src/api/websocket/types/requests/WebSocketRequestTypes.ts +++ b/src/src/api/websocket/types/requests/WebSocketRequestTypes.ts @@ -60,7 +60,7 @@ export interface GetTransactionReceiptRequest extends BaseRequest { } export interface BroadcastTransactionRequest extends BaseRequest { - readonly transaction: Buffer | Uint8Array; + readonly transaction: Uint8Array; readonly psbt: boolean; } diff --git a/src/src/blockchain-indexer/processor/block/Block.ts b/src/src/blockchain-indexer/processor/block/Block.ts index 794ae67c3..23d26a9f9 100644 --- a/src/src/blockchain-indexer/processor/block/Block.ts +++ b/src/src/blockchain-indexer/processor/block/Block.ts @@ -1,7 +1,7 @@ import { Address, AddressMap } from '@btc-vision/transaction'; import { TransactionData } from '@btc-vision/bitcoin-rpc'; import { DataConverter, DebugLevel, Logger } from '@btc-vision/bsi-common'; -import { Network } from '@btc-vision/bitcoin'; +import { fromHex, Network, toHex } from '@btc-vision/bitcoin'; import { Config } from '../../../config/Config.js'; import { BlockHeaderChecksumProof, BlockHeaderDocument, } from '../../../db/interfaces/IBlockHeaderBlockDocument.js'; import { ITransactionDocumentBasic, TransactionDocument, } from '../../../db/interfaces/ITransactionDocument.js'; @@ -119,7 +119,7 @@ export class Block { private blockUsedGas: bigint = 0n; private readonly processEverythingAsGeneric: boolean = false; - private readonly _blockHashBuffer: Buffer; + private readonly _blockHashBuffer: Uint8Array; private readonly addressCache: AddressCache; private epochSubmissions: Map = new Map(); @@ -140,7 +140,7 @@ export class Block { this.signal = this.abortController.signal; this.header = new BlockHeader(params.header); - this._blockHashBuffer = Buffer.from(this.header.hash, 'hex'); + this._blockHashBuffer = fromHex(this.header.hash); this._allowedSolutions = params.allowedSolutions; @@ -187,7 +187,7 @@ export class Block { return this.header.hash; } - public get blockHashBuffer(): Buffer { + public get blockHashBuffer(): Uint8Array { return this._blockHashBuffer; } @@ -333,7 +333,7 @@ export class Block { header: dto.header, abortController, network: network, - allowedPreimages: dto.allowedPreimages.map((ab) => Buffer.from(ab)), + allowedPreimages: dto.allowedPreimages.map((ab) => new Uint8Array(ab)), processEverythingAsGeneric: dto.processEverythingAsGeneric, }); @@ -738,7 +738,7 @@ export class Block { submission: submissionData, validationResult: null, transactionId: transaction.transactionIdString, - txHash: transaction.hash.toString('hex'), + txHash: toHex(transaction.hash), }); } } @@ -815,7 +815,7 @@ export class Block { } private generateSubmissionKey(submission: Submission): string { - return `${submission.salt.toString('hex')}-${submission.mldsaPublicKey.toString('hex')}`; + return `${toHex(submission.salt)}-${toHex(submission.mldsaPublicKey)}`; } private async saveEpochSubmissions(vmManager: VMManager): Promise { @@ -888,8 +888,8 @@ export class Block { currentEpoch * OPNetConsensus.consensus.EPOCH.BLOCKS_PER_EPOCH, ), - submissionTxId: new Binary(Buffer.from(data.transactionId, 'hex')), - submissionTxHash: new Binary(Buffer.from(data.txHash, 'hex')), + submissionTxId: new Binary(fromHex(data.transactionId)), + submissionTxHash: new Binary(fromHex(data.txHash)), submissionHash: new Binary(validationResult.hash), @@ -944,7 +944,7 @@ export class Block { vmManager.updateBlockValuesFromResult( evaluation, evaluation.contractAddress, - evaluation.transactionId.toString('hex'), + toHex(evaluation.transactionId), Config.OP_NET.DISABLE_SCANNED_BLOCK_STORAGE_CHECK, ); } diff --git a/src/src/blockchain-indexer/processor/block/merkle/EpochMerkleTree.ts b/src/src/blockchain-indexer/processor/block/merkle/EpochMerkleTree.ts index 455d36336..f038040f7 100644 --- a/src/src/blockchain-indexer/processor/block/merkle/EpochMerkleTree.ts +++ b/src/src/blockchain-indexer/processor/block/merkle/EpochMerkleTree.ts @@ -5,7 +5,7 @@ import { EpochSubmissionWinner } from '../../../../db/documents/interfaces/IEpoc import { OPNetConsensus } from '../../../../poc/configurations/OPNetConsensus.js'; import { NetworkConverter } from '../../../../config/network/NetworkConverter.js'; import { getChainId } from '../../../../vm/rust/ChainIdHex.js'; -import { sha256 } from '@btc-vision/bitcoin'; +import { fromHex, sha256, toHex } from '@btc-vision/bitcoin'; import { stringToBuffer } from '../../../../utils/StringToBuffer.js'; export enum AttestationType { @@ -16,8 +16,8 @@ export enum AttestationType { export interface Attestation { readonly type: AttestationType; readonly blockNumber: bigint; - readonly checksumRoot: Buffer; - readonly signature: Buffer; + readonly checksumRoot: Uint8Array; + readonly signature: Uint8Array; readonly timestamp: number; readonly publicKey: Address; } @@ -26,12 +26,12 @@ export interface EpochData { readonly epochNumber: bigint; readonly startBlock: bigint; readonly endBlock: bigint; - readonly checksumRoot: Buffer; - readonly previousEpochHash: Buffer; + readonly checksumRoot: Uint8Array; + readonly previousEpochHash: Uint8Array; // Attestation about 4 epochs ago readonly attestedEpochNumber: bigint; - readonly attestedChecksumRoot: Buffer; + readonly attestedChecksumRoot: Uint8Array; winner?: EpochSubmissionWinner; } @@ -52,7 +52,7 @@ export interface EpochDataProof { readonly solutionHash: string; readonly graffiti: string; }; - readonly proof: Buffer[]; + readonly proof: Uint8Array[]; readonly leafHash: string; } @@ -61,11 +61,11 @@ export interface AttestationProof { readonly type: AttestationType; readonly blockNumber: bigint; readonly checksumRoot: string; - readonly signature: Buffer; + readonly signature: Uint8Array; readonly timestamp: number; readonly publicKey: Address; }; - readonly proofs: Buffer[]; + readonly proofs: Uint8Array[]; readonly leafHash: string; readonly index: number; } @@ -106,7 +106,7 @@ export interface EpochTreeVerification { export interface AttestationVerificationProof { readonly root: string; readonly attestation: Attestation; - readonly proof: Buffer[]; + readonly proof: Uint8Array[]; } const chainId = getChainId(NetworkConverter.networkToBitcoinNetwork(NetworkConverter.getNetwork())); @@ -151,9 +151,9 @@ export class EpochMerkleTree { return this.tree.root(); } - private _epochHash: Buffer | undefined; + private _epochHash: Uint8Array | undefined; - public get epochHash(): Buffer { + public get epochHash(): Uint8Array { if (!this._epochHash) { throw new Error('Epoch hash not generated yet'); } @@ -162,16 +162,16 @@ export class EpochMerkleTree { } public static verifyAttestation( - root: Buffer | Uint8Array, + root: Uint8Array, attestation: Attestation, - proof: Buffer[], + proof: Uint8Array[], ): boolean { const attestationBytes = EpochMerkleTree.attestationToBytes(attestation); const merkleProof = new MerkleProof(proof); return merkleProof.verify(root, RustMerkleTree.hash(attestationBytes)); } - public static verifyEpochData(root: Buffer, proofs: Buffer[], data: Uint8Array): boolean { + public static verifyEpochData(root: Uint8Array, proofs: Uint8Array[], data: Uint8Array): boolean { const merkleProof = new MerkleProof(proofs); return merkleProof.verifyData(root, data); } @@ -196,10 +196,10 @@ export class EpochMerkleTree { epochNumber: treeExport.epoch.epochNumber, matchingBits: winner.matchingBits, salt: stringToBuffer(winner.salt), - mldsaPublicKey: Buffer.from(winner.mldsaPublicKey, 'hex'), - legacyPublicKey: Buffer.from(winner.legacyPublicKey, 'hex'), + mldsaPublicKey: fromHex(winner.mldsaPublicKey), + legacyPublicKey: fromHex(winner.legacyPublicKey), solutionHash: stringToBuffer(winner.solutionHash), - graffiti: Buffer.from(winner.graffiti, 'hex'), + graffiti: fromHex(winner.graffiti), }, }; @@ -221,10 +221,7 @@ export class EpochMerkleTree { const attestation: Attestation = { type: attPackage.attestation.type, blockNumber: attPackage.attestation.blockNumber, - checksumRoot: Buffer.from( - attPackage.attestation.checksumRoot.replace('0x', ''), - 'hex', - ), + checksumRoot: fromHex(attPackage.attestation.checksumRoot.replace('0x', '')), signature: attPackage.attestation.signature, timestamp: attPackage.attestation.timestamp, publicKey: attPackage.attestation.publicKey, @@ -232,7 +229,7 @@ export class EpochMerkleTree { const isValid = EpochMerkleTree.verifyAttestation(root, attestation, attPackage.proofs); const computedRoot = new MerkleProof(attPackage.proofs).rootHex( - Buffer.from(attPackage.leafHash.replace('0x', ''), 'hex'), + fromHex(attPackage.leafHash.replace('0x', '')), ); return { @@ -308,14 +305,14 @@ export class EpochMerkleTree { throw new Error(`Graffiti too long, was ${epochData.winner.graffiti.length}`); } - const resizedGraffiti = Buffer.alloc(OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH, 0); - epochData.winner.graffiti.copy( - resizedGraffiti, - 0, - 0, - Math.min( - epochData.winner.graffiti.length, - OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH, + const resizedGraffiti = new Uint8Array(OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH); + resizedGraffiti.set( + epochData.winner.graffiti.subarray( + 0, + Math.min( + epochData.winner.graffiti.length, + OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH, + ), ), ); @@ -378,7 +375,7 @@ export class EpochMerkleTree { const treeLeaves: Uint8Array[] = []; this.epochBytes = EpochMerkleTree.epochDataToBytes(this.epochData); - this._epochHash = Buffer.from(sha256(Buffer.from(this.epochBytes))); + this._epochHash = sha256(this.epochBytes); // Add epoch data as the first leaf treeLeaves.push(this.epochBytes); @@ -397,7 +394,7 @@ export class EpochMerkleTree { this.frozen = true; } - public getProof(attestationIndex: number): Buffer[] { + public getProof(attestationIndex: number): Uint8Array[] { if (!this.tree) { throw new Error('Tree not generated'); } @@ -414,10 +411,10 @@ export class EpochMerkleTree { return this.tree .getProof(this.tree.getIndexData(attestationBytes)) .proofHashes() - .map((hash) => Buffer.from(hash)); + .map((hash) => new Uint8Array(hash)); } - public getEpochDataProof(epochDataBytes: Uint8Array | undefined = this.epochBytes): Buffer[] { + public getEpochDataProof(epochDataBytes: Uint8Array | undefined = this.epochBytes): Uint8Array[] { if (!epochDataBytes) { throw new Error('Epoch data bytes are not provided'); } @@ -429,7 +426,7 @@ export class EpochMerkleTree { return this.tree .getProof(this.tree.getIndexData(epochDataBytes)) .proofHashes() - .map((hash) => Buffer.from(hash)); + .map((hash) => new Uint8Array(hash)); } public getEpochData(): EpochDataProof { @@ -448,22 +445,22 @@ export class EpochMerkleTree { epochNumber: this.epochData.epochNumber, startBlock: this.epochData.startBlock, endBlock: this.epochData.endBlock, - checksumRoot: this.epochData.checksumRoot.toString('hex'), - previousEpochHash: this.epochData.previousEpochHash.toString('hex'), + checksumRoot: toHex(this.epochData.checksumRoot), + previousEpochHash: toHex(this.epochData.previousEpochHash), attestedEpochNumber: this.epochData.attestedEpochNumber, - attestedChecksumRoot: this.epochData.attestedChecksumRoot.toString('hex'), + attestedChecksumRoot: toHex(this.epochData.attestedChecksumRoot), winner: this.epochData.winner ? { - mldsaPublicKey: this.epochData.winner.mldsaPublicKey.toString('hex'), - legacyPublicKey: this.epochData.winner.legacyPublicKey.toString('hex'), + mldsaPublicKey: toHex(this.epochData.winner.mldsaPublicKey), + legacyPublicKey: toHex(this.epochData.winner.legacyPublicKey), matchingBits: this.epochData.winner.matchingBits, - salt: this.epochData.winner.salt.toString('hex'), - solutionHash: this.epochData.winner.solutionHash.toString('hex'), - graffiti: this.epochData.winner.graffiti.toString('hex'), + salt: toHex(this.epochData.winner.salt), + solutionHash: toHex(this.epochData.winner.solutionHash), + graffiti: toHex(this.epochData.winner.graffiti), } : undefined, proof, - leafHash: '0x' + Buffer.from(leafHash).toString('hex'), + leafHash: '0x' + toHex(new Uint8Array(leafHash)), }; } @@ -485,13 +482,13 @@ export class EpochMerkleTree { attestation: { type: attestation.type, blockNumber: attestation.blockNumber, - checksumRoot: attestation.checksumRoot.toString('hex'), + checksumRoot: toHex(attestation.checksumRoot), signature: attestation.signature, timestamp: attestation.timestamp, publicKey: attestation.publicKey, }, proofs: proof, - leafHash: '0x' + Buffer.from(leafHash).toString('hex'), + leafHash: '0x' + toHex(new Uint8Array(leafHash)), index: attestationIndex, }; } @@ -511,12 +508,12 @@ export class EpochMerkleTree { return { root: this.root, - hash: '0x' + this.epochHash.toString('hex'), + hash: '0x' + toHex(this.epochHash), epoch: this.getEpochData(), metadata: { - chainId: '0x' + Buffer.from(chainId).toString('hex'), + chainId: '0x' + toHex(new Uint8Array(chainId)), protocolId: - '0x' + Buffer.from(OPNetConsensus.consensus.PROTOCOL_ID).toString('hex'), + '0x' + toHex(new Uint8Array(OPNetConsensus.consensus.PROTOCOL_ID)), treeHeight: Math.ceil(Math.log2(this.attestations.length + 1)), leafCount: this.attestations.length + 1, generatedAt: Date.now(), @@ -525,7 +522,7 @@ export class EpochMerkleTree { }; } - public verifyProof(leafData: Uint8Array, proof: Buffer[]): boolean { + public verifyProof(leafData: Uint8Array, proof: Uint8Array[]): boolean { if (!this.tree) { throw new Error('Tree not generated'); } @@ -554,8 +551,8 @@ export class EpochMerkleTree { const dummyAttestation1: Attestation = { type: AttestationType.EMPTY_ATTESTATION, blockNumber: this.epochData.startBlock, - checksumRoot: Buffer.from(ZERO_HASH, 'hex'), - signature: Buffer.alloc(64, 0), + checksumRoot: fromHex(ZERO_HASH), + signature: new Uint8Array(64), timestamp: 0, publicKey: Address.dead(), }; @@ -563,8 +560,8 @@ export class EpochMerkleTree { const dummyAttestation2: Attestation = { type: AttestationType.EMPTY_ATTESTATION, blockNumber: this.epochData.endBlock - 1n, - checksumRoot: Buffer.from(ZERO_HASH, 'hex'), - signature: Buffer.alloc(64, 1), + checksumRoot: fromHex(ZERO_HASH), + signature: new Uint8Array(64).fill(1), timestamp: 1, publicKey: Address.dead(), }; diff --git a/src/src/blockchain-indexer/processor/block/merkle/MerkleTree.ts b/src/src/blockchain-indexer/processor/block/merkle/MerkleTree.ts index ab26dd4e2..3539adf88 100644 --- a/src/src/blockchain-indexer/processor/block/merkle/MerkleTree.ts +++ b/src/src/blockchain-indexer/processor/block/merkle/MerkleTree.ts @@ -1,11 +1,12 @@ import { Address, AddressMap } from '@btc-vision/transaction'; import { BTC_FAKE_ADDRESS } from '../types/ZeroValue.js'; import { MerkleTree as RustMerkleTree, safeInitRust } from '@btc-vision/rust-merkle-tree'; +import { fromHex } from '@btc-vision/bitcoin'; safeInitRust(); -export function toBytes(bytesStr: string): Buffer { - return Buffer.from(bytesStr.replace('0x', ''), 'hex'); +export function toBytes(bytesStr: string): Uint8Array { + return fromHex(bytesStr.replace('0x', '')); } export abstract class MerkleTree { @@ -34,7 +35,7 @@ export abstract class MerkleTree { public abstract toBytes(value: unknown[]): Uint8Array; - public getProofHashes(data: Buffer[]): Array { + public getProofHashes(data: Uint8Array[]): Array { return this.tree.getProof(this.tree.getIndexData(this.toBytes(data))).proofHashesHex(); } @@ -110,7 +111,7 @@ export abstract class MerkleTree { public abstract updateValues(address: Address, val: Map): void; - public abstract getValues(): [Buffer, Buffer][]; + public abstract getValues(): [Uint8Array, Uint8Array][]; protected abstract getDummyValues(): AddressMap>; diff --git a/src/src/blockchain-indexer/processor/block/merkle/ReceiptMerkleTree.ts b/src/src/blockchain-indexer/processor/block/merkle/ReceiptMerkleTree.ts index 523e7370f..c2791310d 100644 --- a/src/src/blockchain-indexer/processor/block/merkle/ReceiptMerkleTree.ts +++ b/src/src/blockchain-indexer/processor/block/merkle/ReceiptMerkleTree.ts @@ -2,9 +2,10 @@ import { BTC_FAKE_ADDRESS, MAX_HASH, MAX_MINUS_ONE } from '../types/ZeroValue.js import { Address, AddressMap, BinaryWriter } from '@btc-vision/transaction'; import { MerkleTree } from './MerkleTree.js'; import { FastStringMap } from '../../../../utils/fast/FastStringMap.js'; +import { fromHex, toHex } from '@btc-vision/bitcoin'; -export class ReceiptMerkleTree extends MerkleTree { - public toBytes(values: Buffer[]): Uint8Array { +export class ReceiptMerkleTree extends MerkleTree { + public toBytes(values: Uint8Array[]): Uint8Array { const writer = new BinaryWriter(32 * values.length); for (const value of values) { writer.writeBytes(value); @@ -17,7 +18,7 @@ export class ReceiptMerkleTree extends MerkleTree { const proofs = new AddressMap>(); for (const [address, val] of this.values) { for (const [key, value] of val.entries()) { - const transactionBuf = Buffer.from(key, 'hex'); + const transactionBuf = fromHex(key); const proof: string[] = this.getProofHashes([transactionBuf, value]); if (!proof || !proof.length) { @@ -39,7 +40,7 @@ export class ReceiptMerkleTree extends MerkleTree { } /** We have to replace the value of the given address and key with the new value */ - public updateValues(address: Address, val: FastStringMap): void { + public updateValues(address: Address, val: FastStringMap): void { this.ensureAddress(address); const map = this.values.get(address); @@ -82,11 +83,11 @@ export class ReceiptMerkleTree extends MerkleTree { return; } - map.set(transactionId, Buffer.from(result)); + map.set(transactionId, new Uint8Array(result)); this.valueChanged = true; } - public getValue(address: Address, key: string): Buffer | undefined { + public getValue(address: Address, key: string): Uint8Array | undefined { if (!this.values.has(address)) { return; } @@ -99,12 +100,12 @@ export class ReceiptMerkleTree extends MerkleTree { return map.get(key); } - public getValueWithProofs(address: Address, key: string): [Buffer, string[]] | undefined { + public getValueWithProofs(address: Address, key: string): [Uint8Array, string[]] | undefined { if (!this._tree) { return; } - const keyBuf = Buffer.from(key, 'hex'); + const keyBuf = fromHex(key); const value = this.getValue(address, key); if (value == undefined) { return undefined; @@ -112,14 +113,14 @@ export class ReceiptMerkleTree extends MerkleTree { const proof: string[] = this.getProofHashes([keyBuf, value]); if (!proof || !proof.length) { - throw new Error(`Proof not found for ${keyBuf.toString('hex')}`); + throw new Error(`Proof not found for ${toHex(keyBuf)}`); } return [value, proof]; } - public getValuesWithProofs(address: Address): FastStringMap<[Buffer, string[]]> { - const proofs = new FastStringMap<[Buffer, string[]]>(); + public getValuesWithProofs(address: Address): FastStringMap<[Uint8Array, string[]]> { + const proofs = new FastStringMap<[Uint8Array, string[]]>(); if (!this.values.has(address)) { return proofs; } @@ -130,7 +131,7 @@ export class ReceiptMerkleTree extends MerkleTree { } for (const [key, value] of map.entries()) { - const keyBuf = Buffer.from(key, 'hex'); + const keyBuf = fromHex(key); const proof: string[] = this.getProofHashes([keyBuf, value]); if (!proof || !proof.length) { @@ -143,12 +144,12 @@ export class ReceiptMerkleTree extends MerkleTree { return proofs; } - public getEverythingWithProofs(): AddressMap> | undefined { + public getEverythingWithProofs(): AddressMap> | undefined { if (!this._tree) { return; } - const proofs = new AddressMap>(); + const proofs = new AddressMap>(); for (const address of this.values.keys()) { const map = this.getValuesWithProofs(address); @@ -158,12 +159,12 @@ export class ReceiptMerkleTree extends MerkleTree { return proofs; } - public getValues(): [Buffer, Buffer][] { - const entries: [Buffer, Buffer][] = []; + public getValues(): [Uint8Array, Uint8Array][] { + const entries: [Uint8Array, Uint8Array][] = []; for (const map of this.values.values()) { for (const [key, value] of map.entries()) { - const keyBuf = Buffer.from(key, 'hex'); + const keyBuf = fromHex(key); entries.push([keyBuf, value]); } @@ -172,13 +173,13 @@ export class ReceiptMerkleTree extends MerkleTree { return entries; } - protected getDummyValues(): AddressMap> { - const dummyValues = new AddressMap>(); - const dummyMap = new FastStringMap(); + protected getDummyValues(): AddressMap> { + const dummyValues = new AddressMap>(); + const dummyMap = new FastStringMap(); // Ensure minimum tree requirements - dummyMap.set(MAX_HASH, Buffer.from([1])); - dummyMap.set(MAX_MINUS_ONE, Buffer.from([1])); + dummyMap.set(MAX_HASH, new Uint8Array([1])); + dummyMap.set(MAX_MINUS_ONE, new Uint8Array([1])); // Add dummy values for the contract dummyValues.set(BTC_FAKE_ADDRESS, dummyMap); diff --git a/src/src/blockchain-indexer/processor/block/merkle/StateMerkleTree.ts b/src/src/blockchain-indexer/processor/block/merkle/StateMerkleTree.ts index d3b1b4867..c136fac56 100644 --- a/src/src/blockchain-indexer/processor/block/merkle/StateMerkleTree.ts +++ b/src/src/blockchain-indexer/processor/block/merkle/StateMerkleTree.ts @@ -9,9 +9,10 @@ import { } from '@btc-vision/transaction'; import { MerkleProof, MerkleTree as MerkleTreeRust } from '@btc-vision/rust-merkle-tree'; import { FastBigIntMap } from '../../../../utils/fast/FastBigintMap.js'; +import { toHex } from '@btc-vision/bitcoin'; export class StateMerkleTree extends MerkleTree> { - public static verify(root: string, values: Buffer[] | Uint8Array[], proof: string[]): boolean { + public static verify(root: string, values: Uint8Array[], proof: string[]): boolean { const writer = new BinaryWriter(32 * values.length); for (const value of values) { writer.writeBytes(value); @@ -24,7 +25,7 @@ export class StateMerkleTree extends MerkleTree { return this.storage.submissionExists(mldsaPublicKey, salt, epochNumber); } public async getPendingEpochTarget(currentEpoch: bigint): Promise { if (currentEpoch === 0n) { - const target = Buffer.alloc(32); + const target = new Uint8Array(32); return { checksumRoot: target, targetHash: SHA1.hashBuffer(target), @@ -123,7 +124,7 @@ export class EpochManager extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), + hash: new Uint8Array(0), }; } @@ -131,7 +132,7 @@ export class EpochManager extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), + hash: new Uint8Array(0), }; } @@ -260,9 +261,9 @@ export class EpochManager extends Logger { }; } - private async getPreviousEpochHash(epochNumber: bigint): Promise { + private async getPreviousEpochHash(epochNumber: bigint): Promise { if (epochNumber === 0n) { - return Buffer.alloc(32); + return new Uint8Array(32); } const epoch = await this.storage.getEpochByNumber(epochNumber - 1n); @@ -270,7 +271,7 @@ export class EpochManager extends Logger { throw new Error(`No epoch found for number ${epochNumber - 1n}`); } - return Buffer.from(epoch.epochHash.buffer); + return new Uint8Array(epoch.epochHash.buffer); } private getMiningTargetBlock(epochNumber: bigint): bigint | null { @@ -288,7 +289,7 @@ export class EpochManager extends Logger { return epochNumber * OPNetConsensus.consensus.EPOCH.BLOCKS_PER_EPOCH - 1n; } - private async getMiningTargetChecksum(targetBlock: bigint | null): Promise { + private async getMiningTargetChecksum(targetBlock: bigint | null): Promise { if (targetBlock === null) { return null; } @@ -298,7 +299,7 @@ export class EpochManager extends Logger { throw new Error(`No block header found for mining target block ${targetBlock}`); } - const checksumRoot = Buffer.from(header.checksumRoot.replace('0x', ''), 'hex'); + const checksumRoot = fromHex(header.checksumRoot.replace('0x', '')); if (checksumRoot.length !== 32) { throw new Error( `Invalid checksum root length: ${checksumRoot.length}. Expected 32 bytes.`, @@ -311,7 +312,7 @@ export class EpochManager extends Logger { private async getChecksumRoots( startBlock: bigint, endBlock: bigint, - ): Promise> { + ): Promise> { const promises: Promise[] = []; for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber++) { @@ -319,12 +320,12 @@ export class EpochManager extends Logger { } const headers = await Promise.safeAll(promises); - const checkSumRoots = new Map(); + const checkSumRoots = new Map(); for (const header of headers) { if (header) { const blockNumber = DataConverter.fromDecimal128(header.height); - const checksumRoot = Buffer.from(header.checksumRoot.replace('0x', ''), 'hex'); + const checksumRoot = fromHex(header.checksumRoot.replace('0x', '')); if (checksumRoot.length !== 32) { throw new Error( `Invalid checksum root length: ${checksumRoot.length}. Expected 32 bytes.`, @@ -340,7 +341,7 @@ export class EpochManager extends Logger { private getBestSubmission( submissions: IEpochSubmissionsDocument[], - targetHash: Buffer, + targetHash: Uint8Array, ): EpochSubmissionWinner | null { if (submissions.length === 0) { return null; @@ -351,7 +352,7 @@ export class EpochManager extends Logger { let bestMatchingBits = 0; for (const submission of submissions) { - const solutionHash = Buffer.from(submission.epochProposed.solution.buffer); + const solutionHash = new Uint8Array(submission.epochProposed.solution.buffer); if (solutionHash.length !== 20) { this.log( `Invalid solution hash length: ${solutionHash.length}. Expected 20 bytes.`, @@ -376,39 +377,47 @@ export class EpochManager extends Logger { return { epochNumber: DataConverter.fromDecimal128(winningSubmission.epochNumber), matchingBits: bestMatchingBits, - salt: Buffer.from(winningSubmission.epochProposed.salt.buffer), - mldsaPublicKey: Buffer.from(winningSubmission.epochProposed.mldsaPublicKey.buffer), - legacyPublicKey: Buffer.from(winningSubmission.epochProposed.legacyPublicKey.buffer), - solutionHash: Buffer.from(winningSubmission.submissionHash.buffer), + salt: new Uint8Array(winningSubmission.epochProposed.salt.buffer), + mldsaPublicKey: new Uint8Array(winningSubmission.epochProposed.mldsaPublicKey.buffer), + legacyPublicKey: new Uint8Array(winningSubmission.epochProposed.legacyPublicKey.buffer), + solutionHash: new Uint8Array(winningSubmission.submissionHash.buffer), graffiti: winningSubmission.epochProposed.graffiti - ? Buffer.from(winningSubmission.epochProposed.graffiti.buffer) - : Buffer.alloc(OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH), + ? new Uint8Array(winningSubmission.epochProposed.graffiti.buffer) + : new Uint8Array(OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH), }; } + private static compareBytes(a: Uint8Array, b: Uint8Array): number { + const len = Math.min(a.length, b.length); + for (let i = 0; i < len; i++) { + if (a[i] !== b[i]) return a[i] - b[i]; + } + return a.length - b.length; + } + private getWinningSubmission( submissions: IEpochSubmissionsDocument[], - targetHash: Buffer, + targetHash: Uint8Array, ): IEpochSubmissionsDocument { const winner = [...submissions].sort((a, b) => { // Compare public keys (without pairing byte) - lower wins - const aPublicKey = Buffer.from(a.epochProposed.mldsaPublicKey.buffer); - const bPublicKey = Buffer.from(b.epochProposed.mldsaPublicKey.buffer); + const aPublicKey = new Uint8Array(a.epochProposed.mldsaPublicKey.buffer); + const bPublicKey = new Uint8Array(b.epochProposed.mldsaPublicKey.buffer); if (aPublicKey.length < 32 || bPublicKey.length < 32) { throw new Error('Invalid public key length for comparison tiebreaker.'); } - const pubKeyComparison = aPublicKey.compare(bPublicKey); + const pubKeyComparison = EpochManager.compareBytes(aPublicKey, bPublicKey); if (pubKeyComparison !== 0) { return pubKeyComparison; // Lower public key wins } // Submission tx hash - lower wins - const aTxHash = Buffer.from(a.submissionTxHash.buffer); - const bTxHash = Buffer.from(b.submissionTxHash.buffer); + const aTxHash = new Uint8Array(a.submissionTxHash.buffer); + const bTxHash = new Uint8Array(b.submissionTxHash.buffer); - const hashCompare = aTxHash.compare(bTxHash); + const hashCompare = EpochManager.compareBytes(aTxHash, bTxHash); if (hashCompare !== 0) { return hashCompare; // Lower tx hash wins } @@ -437,18 +446,18 @@ export class EpochManager extends Logger { } // Compare salts - lower wins - const aSalt = Buffer.from(a.epochProposed.salt.buffer); - const bSalt = Buffer.from(b.epochProposed.salt.buffer); - const saltComparison = aSalt.compare(bSalt); + const aSalt = new Uint8Array(a.epochProposed.salt.buffer); + const bSalt = new Uint8Array(b.epochProposed.salt.buffer); + const saltComparison = EpochManager.compareBytes(aSalt, bSalt); if (saltComparison !== 0) { return saltComparison; // Lower salt wins } // Finally, submission tx id - lower wins - const aTxId = Buffer.from(a.submissionTxId.buffer); - const bTxId = Buffer.from(b.submissionTxId.buffer); + const aTxId = new Uint8Array(a.submissionTxId.buffer); + const bTxId = new Uint8Array(b.submissionTxId.buffer); - return aTxId.compare(bTxId); // Lower tx id wins + return EpochManager.compareBytes(aTxId, bTxId); // Lower tx id wins })[0]; if (!winner) { @@ -461,26 +470,26 @@ export class EpochManager extends Logger { private async finalizeEpoch( startBlock: bigint, endBlock: bigint, - checksumRoots: Map, + checksumRoots: Map, submissions: IEpochSubmissionsDocument[], witnesses: IParsedBlockWitnessDocument[], epochNumber: bigint, - previousEpochHash: Buffer, + previousEpochHash: Uint8Array, attestationChecksumRoot: AttestationEpoch, - miningTargetChecksum: Buffer | null, + miningTargetChecksum: Uint8Array | null, ): Promise { // For epoch 0, there's no mining target - let checksumRoot: Buffer; + let checksumRoot: Uint8Array; if (epochNumber === 0n || !miningTargetChecksum) { // Epoch 0 can't be mined, use a zero hash - checksumRoot = Buffer.alloc(32); + checksumRoot = new Uint8Array(32); } else { // Use the mining target checksum (from the first block of the previous epoch) checksumRoot = miningTargetChecksum; } - const targetHash: Buffer = SHA1.hashBuffer(checksumRoot); + const targetHash: Uint8Array = SHA1.hashBuffer(checksumRoot); const winningSubmission = this.getBestSubmission(submissions, targetHash); if (winningSubmission && winningSubmission.epochNumber !== epochNumber) { @@ -489,18 +498,18 @@ export class EpochManager extends Logger { ); } - let salt: Buffer; - let mldsaPublicKey: Buffer; - let legacyPublicKey: Buffer; - let graffiti: Buffer; + let salt: Uint8Array; + let mldsaPublicKey: Uint8Array; + let legacyPublicKey: Uint8Array; + let graffiti: Uint8Array; if (!winningSubmission || epochNumber === 0n) { // No valid submission or epoch 0, use genesis proposer salt = GENESIS_SALT; // All 0xFF for genesis - mldsaPublicKey = Buffer.from(OPNetConsensus.consensus.EPOCH.GENESIS_PROPOSER_PUBLIC_KEY.toBuffer()); + mldsaPublicKey = OPNetConsensus.consensus.EPOCH.GENESIS_PROPOSER_PUBLIC_KEY.toBuffer(); legacyPublicKey = - Buffer.from(OPNetConsensus.consensus.EPOCH.GENESIS_PROPOSER_PUBLIC_KEY.originalPublicKeyBuffer()); - graffiti = Buffer.alloc(OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH); + OPNetConsensus.consensus.EPOCH.GENESIS_PROPOSER_PUBLIC_KEY.originalPublicKeyBuffer(); + graffiti = new Uint8Array(OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH); } else { salt = winningSubmission.salt; mldsaPublicKey = winningSubmission.mldsaPublicKey; @@ -567,7 +576,7 @@ export class EpochManager extends Logger { graffiti: graffiti, solutionBits: matchingBits, - epochRoot: Buffer.from(epoch.rootBuffer), + epochRoot: new Uint8Array(epoch.rootBuffer), epochHash: epoch.epochHash, proofs: epoch.getEpochDataProof(), // Get proofs for this epoch }; @@ -583,12 +592,12 @@ export class EpochManager extends Logger { if (Config.EPOCH.LOG_FINALIZATION) { this.debugBright( - `Epoch ${epochNumber} finalized with root: ${epochDocument.epochRoot.toString('hex')} (Hash: ${epochDocument.epochHash.toString('hex')} | Difficulty: ${EpochDifficultyConverter.formatDifficulty(BigInt(epochDocument.difficultyScaled))}) | Winner: ${finalEpoch.mldsaPublicKey.toString('hex')} | Solution: ${finalEpoch.solution.toString('hex')}) | Salt: ${finalEpoch.salt.toString('hex')} | Graffiti: ${finalEpoch.graffiti ? finalEpoch.graffiti.toString('hex') : 'None'}`, + `Epoch ${epochNumber} finalized with root: ${toHex(new Uint8Array(epochDocument.epochRoot.buffer))} (Hash: ${toHex(new Uint8Array(epochDocument.epochHash.buffer))} | Difficulty: ${EpochDifficultyConverter.formatDifficulty(BigInt(epochDocument.difficultyScaled))}) | Winner: ${toHex(finalEpoch.mldsaPublicKey)} | Solution: ${toHex(finalEpoch.solution)}) | Salt: ${toHex(finalEpoch.salt)} | Graffiti: ${finalEpoch.graffiti ? toHex(finalEpoch.graffiti) : 'None'}`, ); } this.log( - `!! -- Finalized epoch ${epochNumber} [${epochDocument.proposer.solution.toString('hex')} (Diff: ${EpochDifficultyConverter.formatDifficulty(BigInt(epochDocument.difficultyScaled))})] (${epochDocument.epochHash.toString('hex')}) -- !!`, + `!! -- Finalized epoch ${epochNumber} [${toHex(new Uint8Array(epochDocument.proposer.solution.buffer))} (Diff: ${EpochDifficultyConverter.formatDifficulty(BigInt(epochDocument.difficultyScaled))})] (${toHex(new Uint8Array(epochDocument.epochHash.buffer))}) -- !!`, ); // Dispatch onEpochFinalized hook - epoch merkle tree is complete @@ -596,13 +605,13 @@ export class EpochManager extends Logger { epochNumber, startBlock, endBlock, - checksumRoot: epochDocument.epochRoot.toString('hex'), + checksumRoot: toHex(new Uint8Array(epochDocument.epochRoot.buffer)), }); } private witnessToAttestation( witness: IParsedBlockWitnessDocument, - checkSumRoots: Map, + checkSumRoots: Map, ): Attestation | null { const root = checkSumRoots.get(witness.blockNumber); if (!root) { @@ -619,7 +628,7 @@ export class EpochManager extends Logger { type: AttestationType.BLOCK_WITNESS, blockNumber: witness.blockNumber, checksumRoot: root, - signature: Buffer.from(witness.signature.buffer), + signature: new Uint8Array(witness.signature.buffer), timestamp: witness.timestamp.getTime(), publicKey: new Address(witness.publicKey.buffer), }; @@ -630,7 +639,7 @@ export class EpochManager extends Logger { if (epochNumber < 4n) { // For epochs 0-3, return zero hash as there's no history to attest to return { - root: Buffer.alloc(32), + root: new Uint8Array(32), epochNumber: targetEpochNumber, }; } @@ -651,7 +660,7 @@ export class EpochManager extends Logger { ); } - const root = Buffer.from(blockHeader.checksumRoot.replace('0x', ''), 'hex'); + const root = fromHex(blockHeader.checksumRoot.replace('0x', '')); if (root.length !== 32) { throw new Error(`Invalid checksum root length: ${root.length}. Expected 32 bytes.`); } diff --git a/src/src/blockchain-indexer/processor/interfaces/TransactionPreimage.ts b/src/src/blockchain-indexer/processor/interfaces/TransactionPreimage.ts index 8c6b7dcc6..80a10e26c 100644 --- a/src/src/blockchain-indexer/processor/interfaces/TransactionPreimage.ts +++ b/src/src/blockchain-indexer/processor/interfaces/TransactionPreimage.ts @@ -1,6 +1,6 @@ import { AddressMap } from '@btc-vision/transaction'; export interface ChallengeSolution { - readonly solutions: AddressMap; - readonly legacyPublicKeys: AddressMap; + readonly solutions: AddressMap; + readonly legacyPublicKeys: AddressMap; } diff --git a/src/src/blockchain-indexer/processor/tasks/IndexingTask.ts b/src/src/blockchain-indexer/processor/tasks/IndexingTask.ts index 9315393b6..93fa5625b 100644 --- a/src/src/blockchain-indexer/processor/tasks/IndexingTask.ts +++ b/src/src/blockchain-indexer/processor/tasks/IndexingTask.ts @@ -8,7 +8,7 @@ import { MessageType } from '../../../threading/enum/MessageType.js'; import { ThreadData } from '../../../threading/interfaces/ThreadData.js'; import { Config } from '../../../config/Config.js'; import { Block, DeserializedBlock } from '../block/Block.js'; -import { Network } from '@btc-vision/bitcoin'; +import { Network, toHex } from '@btc-vision/bitcoin'; import { VMManager } from '../../../vm/VMManager.js'; import { SpecialManager } from '../special-transaction/SpecialManager.js'; import { BlockGasPredictor } from '../gas/BlockGasPredictor.js'; @@ -268,7 +268,7 @@ export class IndexingTask extends Logger { const batchDummies: DummyMempoolTx[] = batch.map((tx) => ({ id: tx.transactionIdString, inputs: tx.inputs.map((input) => ({ - transactionId: input.originalTransactionId.toString('hex'), + transactionId: toHex(input.originalTransactionId), outputIndex: input.outputTransactionIndex, })) as DummyTxInput[], })); diff --git a/src/src/blockchain-indexer/processor/transaction/Transaction.ts b/src/src/blockchain-indexer/processor/transaction/Transaction.ts index ef04626ba..87b480a66 100644 --- a/src/src/blockchain-indexer/processor/transaction/Transaction.ts +++ b/src/src/blockchain-indexer/processor/transaction/Transaction.ts @@ -1,6 +1,6 @@ import { TransactionData, VIn, VOut } from '@btc-vision/bitcoin-rpc'; import { DataConverter } from '@btc-vision/bsi-common'; -import { Bytes32, Network, Script, Satoshi, script, Transaction as BitcoinTransaction } from '@btc-vision/bitcoin'; +import { alloc, concat, equals, fromHex, fromUtf8, Bytes32, Network, Script, Satoshi, script, Transaction as BitcoinTransaction } from '@btc-vision/bitcoin'; import { createBytes32, createPublicKey, createSatoshi } from '@btc-vision/ecpair'; import crypto from 'crypto'; import { Binary, Long } from 'mongodb'; @@ -22,11 +22,11 @@ import * as ecc from 'tiny-secp256k1'; import { AddressCache } from '../AddressCache.js'; import { Submission } from './features/Submission.js'; -export const OPNet_MAGIC: Buffer = Buffer.from('op', 'utf-8'); -const GZIP_HEADER: Buffer = Buffer.from([0x1f, 0x8b]); +export const OPNet_MAGIC: Uint8Array = fromUtf8('op'); +const GZIP_HEADER: Uint8Array = new Uint8Array([0x1f, 0x8b]); // We need ECDSA/ECC functionality: -if (!ecc.isPoint(Buffer.alloc(33, 2))) { +if (!ecc.isPoint(alloc(33, 2))) { throw new Error('tiny-secp256k1 initialization check failed'); } @@ -37,7 +37,7 @@ export abstract class Transaction { public readonly outputs: TransactionOutput[] = []; public readonly txidHex: string; - public readonly raw: Buffer; + public readonly raw: Uint8Array; public readonly inActiveChain: boolean | undefined; public readonly size: number; @@ -52,12 +52,12 @@ export abstract class Transaction { public wasCompressed: boolean = false; - protected readonly _computedIndexingHash: Buffer; - protected readonly transactionHash: Buffer; + protected readonly _computedIndexingHash: Uint8Array; + protected readonly transactionHash: Uint8Array; protected readonly vInputIndex: number; protected receiptProofs: string[] | undefined; - private readonly txid: Buffer; + private readonly txid: Uint8Array; protected constructor( rawTransactionData: TransactionData, @@ -75,12 +75,12 @@ export abstract class Transaction { this.vInputIndex = vInputIndex; - this.txid = Buffer.from(rawTransactionData.txid, 'hex'); + this.txid = fromHex(rawTransactionData.txid); this.txidHex = rawTransactionData.txid; - this.transactionHash = Buffer.from(rawTransactionData.hash, 'hex'); + this.transactionHash = fromHex(rawTransactionData.hash); this.raw = rawTransactionData.hex - ? Buffer.from(rawTransactionData.hex, 'hex') - : Buffer.alloc(0); + ? fromHex(rawTransactionData.hex) + : new Uint8Array(0); this.inActiveChain = rawTransactionData.in_active_chain || false; this.size = rawTransactionData.size; @@ -102,31 +102,31 @@ export abstract class Transaction { return this._submission; } - protected _preimage: Buffer | undefined; - public get preimage(): Buffer { - const preimage = Buffer.alloc(this._preimage?.length || 0); + protected _preimage: Uint8Array | undefined; + public get preimage(): Uint8Array { + const preimage = new Uint8Array(this._preimage?.length || 0); if (this._preimage) { - this._preimage.copy(preimage); + preimage.set(this._preimage); } return preimage; } - protected _minerLegacyPublicKey: Buffer | undefined; + protected _minerLegacyPublicKey: Uint8Array | undefined; - public get minerLegacyPublicKey(): Buffer { - const miner = Buffer.alloc(this._minerLegacyPublicKey?.length || 0); + public get minerLegacyPublicKey(): Uint8Array { + const miner = new Uint8Array(this._minerLegacyPublicKey?.length || 0); if (this._minerLegacyPublicKey) { - this._minerLegacyPublicKey.copy(miner); + miner.set(this._minerLegacyPublicKey); } return miner; } - protected _miner: Buffer | undefined; + protected _miner: Uint8Array | undefined; - public get miner(): Buffer { - const miner = Buffer.alloc(this._miner?.length || 0); + public get miner(): Uint8Array { + const miner = new Uint8Array(this._miner?.length || 0); if (this._miner) { - this._miner.copy(miner); + miner.set(this._miner); } return miner; } @@ -153,7 +153,7 @@ export abstract class Transaction { return outputs.filter((output): output is StrippedTransactionOutput => !!output); } - public get computedIndexingHash(): Buffer { + public get computedIndexingHash(): Uint8Array { return this._computedIndexingHash; } @@ -230,7 +230,7 @@ export abstract class Transaction { return this._gasSatFee; } - public get transactionId(): Buffer { + public get transactionId(): Uint8Array { return this.txid; } @@ -238,7 +238,7 @@ export abstract class Transaction { return this.txidHex; } - public get hash(): Buffer { + public get hash(): Uint8Array { return this.transactionHash; } @@ -252,37 +252,36 @@ export abstract class Transaction { public static dataIncludeOPNetMagic(data: Array): boolean { return data.some((value) => { if (typeof value === 'number') return false; - const buffer: Buffer = Buffer.isBuffer(value) ? value : Buffer.from(value); - if (buffer.byteLength !== OPNet_MAGIC.byteLength) return false; - return buffer.equals(OPNet_MAGIC); + if (value.byteLength !== OPNet_MAGIC.byteLength) return false; + return equals(value, OPNet_MAGIC); }); } - public static verifyChecksum(scriptData: (number | Uint8Array)[], typeChecksum: Buffer): boolean { - const checksum: Buffer = this.getDataChecksum(scriptData); - return checksum.equals(typeChecksum); + public static verifyChecksum(scriptData: (number | Uint8Array)[], typeChecksum: Uint8Array): boolean { + const checksum = this.getDataChecksum(scriptData); + return equals(checksum, typeChecksum); } - public static decompressBuffer(buffer: Buffer): { out: Buffer; compressed: boolean } { + public static decompressBuffer(buffer: Uint8Array): { out: Uint8Array; compressed: boolean } { if (!buffer) { throw new Error('Buffer is undefined. Cannot decompress.'); } const zlibHeader = buffer.subarray(0, 2); - if (zlibHeader.equals(GZIP_HEADER)) { + if (equals(zlibHeader, GZIP_HEADER)) { try { - buffer = zlib.unzipSync(buffer, { + const decompressed = zlib.unzipSync(buffer, { finishFlush: zlib.constants.Z_SYNC_FLUSH, maxOutputLength: OPNetConsensus.consensus.COMPRESSION.MAX_DECOMPRESSED_SIZE, }); + return { out: new Uint8Array(decompressed.buffer, decompressed.byteOffset, decompressed.byteLength), compressed: true }; } catch { throw new Error('OP_NET: Invalid compressed data.'); } - return { out: buffer, compressed: true }; } return { out: buffer, compressed: false }; } - protected static _is(data: TransactionData, typeChecksum: Buffer): number { + protected static _is(data: TransactionData, typeChecksum: Uint8Array): number { let isCorrectType: number = -1; for (let y = 0; y < data.vin.length; y++) { @@ -308,7 +307,7 @@ export abstract class Transaction { } const rawScriptHex = witnesses[3]; //witnesses.length - 2 - const rawScriptBuf = Buffer.from(rawScriptHex, 'hex'); + const rawScriptBuf = fromHex(rawScriptHex); let decodedScript: (number | Uint8Array)[] | null; try { @@ -337,17 +336,17 @@ export abstract class Transaction { return isCorrectType; } - protected static getDataChecksum(data: Array): Buffer { + protected static getDataChecksum(data: Array): Uint8Array { const checksum: number[] = []; for (let i = 0; i < data.length; i++) { if (typeof data[i] === 'number') { checksum.push(data[i] as number); } } - return Buffer.from(checksum); + return new Uint8Array(checksum); } - public setMiner(miner: Buffer, preimage: Buffer) { + public setMiner(miner: Uint8Array, preimage: Uint8Array) { const legacyPublicKey = this.verifyPreImage(new Address(miner), preimage); this._preimage = preimage; @@ -355,9 +354,9 @@ export abstract class Transaction { this._minerLegacyPublicKey = legacyPublicKey; } - public verifyPreImage: (miner: Address, preimage: Buffer) => Buffer | undefined = ( + public verifyPreImage: (miner: Address, preimage: Uint8Array) => Uint8Array | undefined = ( _miner: Address, - _preimage: Buffer, + _preimage: Uint8Array, ) => { throw new Error('Verify preimage method not implemented.'); }; @@ -445,11 +444,11 @@ export abstract class Transaction { * @param prevOutValue The UTXO's value in satoshis */ protected verifySenderSignature( - senderPubKey: Buffer, - senderSig: Buffer, - leafScript: Buffer, + senderPubKey: Uint8Array, + senderSig: Uint8Array, + leafScript: Uint8Array, leafVersion: number, - prevOutScript: Buffer, + prevOutScript: Uint8Array, prevOutValue: number, ): boolean { if (!senderPubKey) { @@ -463,7 +462,7 @@ export abstract class Transaction { prevOutValue, ); - let xOnlyPub: Buffer; + let xOnlyPub: Uint8Array; if (senderPubKey.length === 33 && (senderPubKey[0] === 0x02 || senderPubKey[0] === 0x03)) { xOnlyPub = senderPubKey.subarray(1); } else if (senderPubKey.length === 32) { @@ -592,43 +591,51 @@ export abstract class Transaction { } // ADDED: Compute the TapLeaf hash: leafVersion || varint(script.length) || script => taggedHash("TapLeaf", ...) - private computeTapLeafHash(leafScript: Buffer, leafVersion: number): Buffer { + private computeTapLeafHash(leafScript: Uint8Array, leafVersion: number): Uint8Array { // BIP341: leafVersion(1 byte) + varint(script.length) + script const varint = this.encodeVarint(leafScript.length); - const toHash = Buffer.concat([Buffer.from([leafVersion]), varint, leafScript]); + const toHash = concat([new Uint8Array([leafVersion]), varint, leafScript]); // "TapLeaf" tagged hash return this.taggedHash('TapLeaf', toHash); } // ADDED: replicate BIP341 "TapLeaf" or "TapSighash" tagged hashing - private taggedHash(prefix: string, data: Buffer): Buffer { + private taggedHash(prefix: string, data: Uint8Array): Uint8Array { // This is the same approach as bip341, bip340, etc. const h1 = crypto.createHash('sha256').update(prefix).digest(); const h2 = crypto.createHash('sha256').update(prefix).digest(); - const tagHash = Buffer.concat([h1, h2]); // 64 bytes - return crypto.createHash('sha256').update(tagHash).update(data).digest(); + const tagHash = concat([new Uint8Array(h1.buffer, h1.byteOffset, h1.byteLength), new Uint8Array(h2.buffer, h2.byteOffset, h2.byteLength)]); // 64 bytes + const result = crypto.createHash('sha256').update(tagHash).update(data).digest(); + return new Uint8Array(result.buffer, result.byteOffset, result.byteLength); } // ADDED: minimal varint encoder for script length - private encodeVarint(num: number): Buffer { + private encodeVarint(num: number): Uint8Array { if (num < 0xfd) { - return Buffer.from([num]); + return new Uint8Array([num]); } else if (num <= 0xffff) { - const buf = Buffer.alloc(3); + const buf = new Uint8Array(3); buf[0] = 0xfd; - buf.writeUInt16LE(num, 1); + buf[1] = num & 0xff; + buf[2] = (num >> 8) & 0xff; return buf; } else if (num <= 0xffffffff) { - const buf = Buffer.alloc(5); + const buf = new Uint8Array(5); buf[0] = 0xfe; - buf.writeUInt32LE(num, 1); + buf[1] = num & 0xff; + buf[2] = (num >> 8) & 0xff; + buf[3] = (num >> 16) & 0xff; + buf[4] = (num >> 24) & 0xff; return buf; } else { - const buf = Buffer.alloc(9); + const buf = new Uint8Array(9); buf[0] = 0xff; - buf.writeBigUInt64LE(BigInt(num), 1); + const big = BigInt(num); + for (let i = 0; i < 8; i++) { + buf[1 + i] = Number((big >> BigInt(i * 8)) & 0xffn); + } return buf; } } @@ -643,11 +650,11 @@ export abstract class Transaction { * @param prevOutValue The value (satoshis) of that UTXO */ private generateTapscriptSighashAll( - leafScript: Buffer, + leafScript: Uint8Array, leafVersion: number, - prevOutScript: Buffer, + prevOutScript: Uint8Array, prevOutValue: number, - ): Buffer { + ): Uint8Array { // 1) parse the transaction from this.raw const txObj = BitcoinTransaction.fromBuffer(this.raw); @@ -666,12 +673,12 @@ export abstract class Transaction { const values = new Array(nIn).fill(createSatoshi(0n)); // fill our input with the real data - prevOutScripts[this.vInputIndex] = new Uint8Array(prevOutScript) as Script; + prevOutScripts[this.vInputIndex] = prevOutScript as Script; values[this.vInputIndex] = createSatoshi(BigInt(prevOutValue)); // 4) call hashForWitnessV1 // -> If leafHash is provided, it's Tapscript path - return Buffer.from(txObj.hashForWitnessV1(this.vInputIndex, prevOutScripts, values, hashType, createBytes32(leafHash))); + return txObj.hashForWitnessV1(this.vInputIndex, prevOutScripts, values, hashType, createBytes32(leafHash)); } private strToBuffer(str: string): Uint8Array { @@ -680,10 +687,11 @@ export abstract class Transaction { return writer.getBuffer(); } - private computeHashForTransaction(): Buffer { + private computeHashForTransaction(): Uint8Array { const hash = crypto.createHash('sha256'); hash.update(this.transactionHash); - hash.update(Buffer.from(this.blockHash, 'hex')); - return hash.digest(); + hash.update(fromHex(this.blockHash)); + const result = hash.digest(); + return new Uint8Array(result.buffer, result.byteOffset, result.byteLength); } } diff --git a/src/src/blockchain-indexer/processor/transaction/contract/ContractInformation.ts b/src/src/blockchain-indexer/processor/transaction/contract/ContractInformation.ts index a0db0450f..19be7bf82 100644 --- a/src/src/blockchain-indexer/processor/transaction/contract/ContractInformation.ts +++ b/src/src/blockchain-indexer/processor/transaction/contract/ContractInformation.ts @@ -1,4 +1,5 @@ import { DataConverter } from '@btc-vision/bsi-common'; +import { fromBase64 } from '@btc-vision/bitcoin'; import { Binary } from 'mongodb'; import { IContractDocument } from '../../../../db/documents/interfaces/IContractDocument.js'; import { DeploymentTransaction } from '../transactions/DeploymentTransaction.js'; @@ -23,74 +24,54 @@ export class ContractInformation { public readonly blockHeight: bigint, public readonly contractAddress: string, public readonly contractPublicKey: Address, - public readonly bytecode: Buffer, + public readonly bytecode: Uint8Array, public readonly wasCompressed: boolean, - public readonly deployedTransactionId: Buffer, - public readonly deployedTransactionHash: Buffer, - public readonly deployerPubKey: Buffer, - public readonly contractSeed: Buffer, - public readonly contractSaltHash: Buffer, + public readonly deployedTransactionId: Uint8Array, + public readonly deployedTransactionHash: Uint8Array, + public readonly deployerPubKey: Uint8Array, + public readonly contractSeed: Uint8Array, + public readonly contractSaltHash: Uint8Array, public readonly deployerAddress: Address, ) {} public static fromDocument(contractDocument: IContractDocument): ContractInformation { - let bytecodeBuffer: Buffer; - if (Buffer.isBuffer(contractDocument.bytecode)) { - bytecodeBuffer = contractDocument.bytecode; - } else { - bytecodeBuffer = Buffer.from(contractDocument.bytecode.buffer); - } + const bytecodeBytes = contractDocument.bytecode instanceof Uint8Array + ? contractDocument.bytecode + : new Uint8Array(contractDocument.bytecode.buffer); - let deployerPubKeyBuffer: Buffer; - if (Buffer.isBuffer(contractDocument.deployerPubKey)) { - deployerPubKeyBuffer = contractDocument.deployerPubKey; - } else { - deployerPubKeyBuffer = Buffer.from(contractDocument.deployerPubKey.buffer); - } + const deployerPubKeyBytes = contractDocument.deployerPubKey instanceof Uint8Array + ? contractDocument.deployerPubKey + : new Uint8Array(contractDocument.deployerPubKey.buffer); - let contractSeedBuffer: Buffer; - if (Buffer.isBuffer(contractDocument.contractSeed)) { - contractSeedBuffer = contractDocument.contractSeed; - } else { - contractSeedBuffer = Buffer.from(contractDocument.contractSeed.buffer); - } + const contractSeedBytes = contractDocument.contractSeed instanceof Uint8Array + ? contractDocument.contractSeed + : new Uint8Array(contractDocument.contractSeed.buffer); - let contractSaltHashBuffer: Buffer; - if (Buffer.isBuffer(contractDocument.contractSaltHash)) { - contractSaltHashBuffer = contractDocument.contractSaltHash; - } else { - contractSaltHashBuffer = Buffer.from(contractDocument.contractSaltHash.buffer); - } + const contractSaltHashBytes = contractDocument.contractSaltHash instanceof Uint8Array + ? contractDocument.contractSaltHash + : new Uint8Array(contractDocument.contractSaltHash.buffer); - let transactionIdBuffer: Buffer; - if (Buffer.isBuffer(contractDocument.deployedTransactionId)) { - transactionIdBuffer = contractDocument.deployedTransactionId; - } else { - transactionIdBuffer = Buffer.from(contractDocument.deployedTransactionId.buffer); - } + const transactionIdBytes = contractDocument.deployedTransactionId instanceof Uint8Array + ? contractDocument.deployedTransactionId + : new Uint8Array(contractDocument.deployedTransactionId.buffer); - let deployedTransactionHashBuffer: Buffer; - if (Buffer.isBuffer(contractDocument.deployedTransactionHash)) { - deployedTransactionHashBuffer = contractDocument.deployedTransactionHash; - } else { - deployedTransactionHashBuffer = Buffer.from( - contractDocument.deployedTransactionHash.buffer, - ); - } + const deployedTransactionHashBytes = contractDocument.deployedTransactionHash instanceof Uint8Array + ? contractDocument.deployedTransactionHash + : new Uint8Array(contractDocument.deployedTransactionHash.buffer); return new ContractInformation( DataConverter.fromDecimal128(contractDocument.blockHeight), contractDocument.contractAddress, typeof contractDocument.contractPublicKey === 'string' - ? new Address(Buffer.from(contractDocument.contractPublicKey, 'base64')) + ? new Address(fromBase64(contractDocument.contractPublicKey)) : new Address(contractDocument.contractPublicKey.buffer), - bytecodeBuffer, + bytecodeBytes, contractDocument.wasCompressed, - transactionIdBuffer, - deployedTransactionHashBuffer, - deployerPubKeyBuffer, - contractSeedBuffer, - contractSaltHashBuffer, + transactionIdBytes, + deployedTransactionHashBytes, + deployerPubKeyBytes, + contractSeedBytes, + contractSaltHashBytes, new Address( contractDocument.deployerAddress.buffer, contractDocument.deployerPubKey.buffer, diff --git a/src/src/blockchain-indexer/processor/transaction/features/Features.ts b/src/src/blockchain-indexer/processor/transaction/features/Features.ts index cdae4f0d3..67c969814 100644 --- a/src/src/blockchain-indexer/processor/transaction/features/Features.ts +++ b/src/src/blockchain-indexer/processor/transaction/features/Features.ts @@ -1,13 +1,13 @@ import { Feature, Features } from '@btc-vision/transaction'; export interface AccessListFeature extends Feature { - data: Buffer; + data: Uint8Array; } export interface EpochSubmissionFeature extends Feature { - data: Buffer; + data: Uint8Array; } export interface MLDSALinkRequest extends Feature { - data: Buffer; + data: Uint8Array; } diff --git a/src/src/blockchain-indexer/processor/transaction/features/MLDSARequestData.ts b/src/src/blockchain-indexer/processor/transaction/features/MLDSARequestData.ts index 5b35fcab4..66b00f91e 100644 --- a/src/src/blockchain-indexer/processor/transaction/features/MLDSARequestData.ts +++ b/src/src/blockchain-indexer/processor/transaction/features/MLDSARequestData.ts @@ -2,10 +2,10 @@ import { MLDSASecurityLevel } from '@btc-vision/transaction'; export interface MLDSARequestData { readonly verifyRequest: boolean; - readonly publicKey: Buffer | null; - readonly hashedPublicKey: Buffer; + readonly publicKey: Uint8Array | null; + readonly hashedPublicKey: Uint8Array; readonly level: MLDSASecurityLevel; - readonly mldsaSignature: Buffer | null; - readonly legacySignature: Buffer; + readonly mldsaSignature: Uint8Array | null; + readonly legacySignature: Uint8Array; } diff --git a/src/src/blockchain-indexer/processor/transaction/features/Submission.ts b/src/src/blockchain-indexer/processor/transaction/features/Submission.ts index 5b9636640..5a31b0bc1 100644 --- a/src/src/blockchain-indexer/processor/transaction/features/Submission.ts +++ b/src/src/blockchain-indexer/processor/transaction/features/Submission.ts @@ -1,9 +1,9 @@ export interface Submission { - readonly mldsaPublicKey: Buffer; - readonly salt: Buffer; - readonly graffiti?: Buffer; + readonly mldsaPublicKey: Uint8Array; + readonly salt: Uint8Array; + readonly graffiti?: Uint8Array; } export interface ExtendedSubmission extends Submission { - readonly legacyPublicKey: Buffer; + readonly legacyPublicKey: Uint8Array; } diff --git a/src/src/blockchain-indexer/processor/transaction/inputs/TransactionInput.ts b/src/src/blockchain-indexer/processor/transaction/inputs/TransactionInput.ts index 2ec0e19ae..2cb9369e2 100644 --- a/src/src/blockchain-indexer/processor/transaction/inputs/TransactionInput.ts +++ b/src/src/blockchain-indexer/processor/transaction/inputs/TransactionInput.ts @@ -1,15 +1,16 @@ import { ScriptSig, VIn } from '@btc-vision/bitcoin-rpc'; +import { fromHex } from '@btc-vision/bitcoin'; import { TransactionInputFlags } from '../../../../poc/configurations/types/IOPNetConsensus.js'; import { OPNetConsensus } from '../../../../poc/configurations/OPNetConsensus.js'; export interface ITransactionInput { - readonly originalTransactionId: Buffer | undefined; + readonly originalTransactionId: Uint8Array | undefined; readonly outputTransactionIndex: number | undefined; // consumer output index readonly scriptSignature?: ScriptSig; readonly sequenceId: number; - readonly transactionInWitness: Buffer[]; + readonly transactionInWitness: Uint8Array[]; } export interface ITransactionInputWithoutWitnesses extends Omit< @@ -25,13 +26,13 @@ export interface APIDocumentInput extends Omit< } export interface StrippedTransactionInput { - readonly txId: Uint8Array | Buffer; + readonly txId: Uint8Array; readonly outputIndex: number; - readonly scriptSig: Uint8Array | Buffer; - readonly witnesses: (Uint8Array | Buffer)[]; + readonly scriptSig: Uint8Array; + readonly witnesses: Uint8Array[]; readonly flags: number; - readonly coinbase: Buffer | undefined; + readonly coinbase: Uint8Array | undefined; } export interface StrippedTransactionInputAPI { @@ -45,30 +46,30 @@ export interface StrippedTransactionInputAPI { } export class TransactionInput implements ITransactionInput { - public readonly originalTransactionId: Buffer; + public readonly originalTransactionId: Uint8Array; public readonly outputTransactionIndex: number | undefined; // consumer output index public readonly scriptSignature: ScriptSig | undefined; public readonly sequenceId: number; - public readonly transactionInWitness: Buffer[] = []; + public readonly transactionInWitness: Uint8Array[] = []; // New properties to hold the decoded public key or hash - public readonly decodedPubKey: Buffer | null; - public readonly decodedPubKeyHash: Buffer | null; + public readonly decodedPubKey: Uint8Array | null; + public readonly decodedPubKeyHash: Uint8Array | null; - private readonly coinbase: Buffer | undefined = undefined; + private readonly coinbase: Uint8Array | undefined = undefined; constructor(data: VIn) { this.originalTransactionId = - data.txid && data.txid !== '' ? Buffer.from(data.txid, 'hex') : Buffer.alloc(0); + data.txid && data.txid !== '' ? fromHex(data.txid) : new Uint8Array(0); this.outputTransactionIndex = data.vout; this.scriptSignature = data.scriptSig; this.sequenceId = data.sequence; this.transactionInWitness = data.txinwitness - ? data.txinwitness.map((w) => Buffer.from(w, 'hex')) + ? data.txinwitness.map((w) => fromHex(w)) : []; // for P2PK, P2WPKH, and P2PKH @@ -77,7 +78,7 @@ export class TransactionInput implements ITransactionInput { // for coinbase if (data.coinbase) { - this.coinbase = Buffer.from(data.coinbase, 'hex'); + this.coinbase = fromHex(data.coinbase); } } @@ -111,7 +112,7 @@ export class TransactionInput implements ITransactionInput { return { txId: this.originalTransactionId, outputIndex: this.outputTransactionIndex || 0, - scriptSig: Buffer.from(this.scriptSignature?.hex || '', 'hex'), + scriptSig: this.scriptSignature?.hex ? fromHex(this.scriptSignature.hex) : new Uint8Array(0), witnesses: this.transactionInWitness, flags: flags, coinbase: this.coinbase, @@ -119,18 +120,18 @@ export class TransactionInput implements ITransactionInput { } // Decode public key for P2PK, SegWit (P2WPKH), and P2PKH - private decodePubKey(): Buffer | null { + private decodePubKey(): Uint8Array | null { const secondWitness = this.transactionInWitness[1]; const secondWitnessLength = secondWitness?.length || 0; // Decode from SegWit witness (P2WPKH) or P2PKH - // Note: witnesses are Buffers, so we check for byte lengths (33/65), not hex string lengths (66/130) + // Note: witnesses are Uint8Arrays, so we check for byte lengths (33/65), not hex string lengths (66/130) if ( this.transactionInWitness.length === 2 && secondWitness && - this.isValidPublicKeyBuffer(secondWitness, secondWitnessLength) + this.isValidPublicKeyBytes(secondWitness, secondWitnessLength) ) { - return secondWitness; // Return the public key as Buffer + return secondWitness; // Return the public key } // Decode from scriptSig (P2PKH - signature + pubkey) @@ -140,10 +141,10 @@ export class TransactionInput implements ITransactionInput { // Check for P2PKH with compressed (66 hex = 33 bytes) or uncompressed (130 hex = 65 bytes) public key if (parts.length === 2 && secondPart && (secondPart.length === 66 || secondPart.length === 130)) { - const pubkeyBuffer = Buffer.from(secondPart, 'hex'); + const pubkeyBytes = fromHex(secondPart); // Validate the public key prefix to avoid mistaking scripts for pubkeys - if (this.isValidPublicKeyBuffer(pubkeyBuffer, pubkeyBuffer.length)) { - return pubkeyBuffer; + if (this.isValidPublicKeyBytes(pubkeyBytes, pubkeyBytes.length)) { + return pubkeyBytes; } } } @@ -154,10 +155,10 @@ export class TransactionInput implements ITransactionInput { // for P2PKH and P2WPKH // Note: This method has limited usefulness as P2WPKH witness[0] is a signature, not a pubkey hash. // The pubkey hash is in the scriptPubKey of the UTXO being spent, not in the witness. - private decodePubKeyHash(): Buffer | null { + private decodePubKeyHash(): Uint8Array | null { // Check for P2WPKH in witness data if (this.transactionInWitness.length === 2 && this.transactionInWitness[0].length === 20) { - return this.transactionInWitness[0]; // Return the public key hash as Buffer + return this.transactionInWitness[0]; // Return the public key hash } // Check for P2PKH in scriptSig @@ -165,25 +166,25 @@ export class TransactionInput implements ITransactionInput { if (this.scriptSignature && this.scriptSignature.asm) { const parts = this.scriptSignature.asm.split(' '); if (parts.length === 2 && parts[1].length === 40) { - return Buffer.from(parts[1], 'hex'); // Return the public key hash as Buffer + return fromHex(parts[1]); // Return the public key hash } } return null; // No public key hash found } - // Validate that a buffer contains a valid EC public key by checking the prefix byte + // Validate that bytes contain a valid EC public key by checking the prefix byte // Compressed keys (33 bytes): must start with 0x02 (even y) or 0x03 (odd y) // Uncompressed keys (65 bytes): must start with 0x04 // This prevents mistaking scripts (like P2WSH witness scripts) for public keys - private isValidPublicKeyBuffer(buffer: Buffer, length: number): boolean { + private isValidPublicKeyBytes(bytes: Uint8Array, length: number): boolean { if (length === 33) { // Compressed public key must start with 0x02 or 0x03 - const prefix = buffer[0]; + const prefix = bytes[0]; return prefix === 0x02 || prefix === 0x03; } else if (length === 65) { // Uncompressed public key must start with 0x04 - return buffer[0] === 0x04; + return bytes[0] === 0x04; } return false; } diff --git a/src/src/blockchain-indexer/processor/transaction/inputs/TransactionOutput.ts b/src/src/blockchain-indexer/processor/transaction/inputs/TransactionOutput.ts index c86a9ff70..42484fa2a 100644 --- a/src/src/blockchain-indexer/processor/transaction/inputs/TransactionOutput.ts +++ b/src/src/blockchain-indexer/processor/transaction/inputs/TransactionOutput.ts @@ -1,6 +1,6 @@ import { ScriptPubKey, VOut } from '@btc-vision/bitcoin-rpc'; import BigNumber from 'bignumber.js'; -import { opcodes, script } from '@btc-vision/bitcoin'; +import { fromHex, opcodes, script } from '@btc-vision/bitcoin'; import { Decimal128 } from 'mongodb'; import { TransactionOutputFlags } from '../../../../poc/configurations/types/IOPNetConsensus.js'; import { OPNetConsensus } from '../../../../poc/configurations/OPNetConsensus.js'; @@ -45,12 +45,12 @@ export class TransactionOutput { public readonly scriptPubKey: ScriptPubKey; public readonly script: Array | null; - public readonly scriptPubKeyBuffer: Buffer; + public readonly scriptPubKeyBuffer: Uint8Array; // New properties to hold the decoded public key or hash - public readonly decodedPubKeyHash: Buffer | null; - public readonly decodedPublicKeys: Buffer[] | null; - public readonly decodedSchnorrPublicKey: Buffer | null; // For Taproot + public readonly decodedPubKeyHash: Uint8Array | null; + public readonly decodedPublicKeys: Uint8Array[] | null; + public readonly decodedSchnorrPublicKey: Uint8Array | null; // For Taproot constructor(data: VOut) { this.value = this.convertValue(data.value); @@ -64,7 +64,7 @@ export class TransactionOutput { ? (this.scriptPubKey.addresses || [])[0] : undefined); - this.scriptPubKeyBuffer = Buffer.from(this.scriptPubKey.hex, 'hex'); + this.scriptPubKeyBuffer = fromHex(this.scriptPubKey.hex); this.script = script.decompile(this.scriptPubKeyBuffer); // Decode the public key hash or public keys based on the script type @@ -125,7 +125,7 @@ export class TransactionOutput { }; } - private decodeSchnorrPublicKey(): Buffer | null { + private decodeSchnorrPublicKey(): Uint8Array | null { if (!this.script) return null; // Check for Taproot (P2TR): OP_1 <32-byte Schnorr public key> @@ -135,14 +135,14 @@ export class TransactionOutput { this.script[1] instanceof Uint8Array && this.script[1].length === 32 ) { - return Buffer.from(this.script[1]); // Return the Schnorr public key + return this.script[1]; // Return the Schnorr public key } return null; // Not a Taproot output } // for P2PKH or P2WPKH - private decodePubKeyHash(): Buffer | null { + private decodePubKeyHash(): Uint8Array | null { if (!this.script) return null; // Check for P2PKH: OP_DUP OP_HASH160 <20-byte pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG @@ -155,7 +155,7 @@ export class TransactionOutput { this.script[3] === opcodes.OP_EQUALVERIFY && this.script[4] === opcodes.OP_CHECKSIG ) { - return Buffer.from(this.script[2]); // Return the public key hash + return this.script[2]; // Return the public key hash } // Check for P2WPKH: OP_0 <20-byte pubKeyHash> @@ -165,14 +165,14 @@ export class TransactionOutput { this.script[1] instanceof Uint8Array && this.script[1].length === 20 ) { - return Buffer.from(this.script[1]); // Return the public key hash + return this.script[1]; // Return the public key hash } return null; // No public key hash found } // for P2MS multisig - private decodePublicKeys(): Buffer[] | null { + private decodePublicKeys(): Uint8Array[] | null { if (!this.script) return null; // Check for P2MS (multisig) output: OP_M ... OP_N OP_CHECKMULTISIG @@ -180,11 +180,11 @@ export class TransactionOutput { this.script.length >= 4 && this.script[this.script.length - 1] === opcodes.OP_CHECKMULTISIG ) { - const pubKeys: Buffer[] = []; + const pubKeys: Uint8Array[] = []; for (let i = 1; i < this.script.length - 2; i++) { const next = this.script[i]; if (next instanceof Uint8Array && next.length === 33) { - pubKeys.push(Buffer.from(next)); // Add each public key + pubKeys.push(next); // Add each public key } } return pubKeys.length > 0 ? pubKeys : null; @@ -197,7 +197,7 @@ export class TransactionOutput { this.script[0].length === 33 && // Compressed public key this.script[1] === opcodes.OP_CHECKSIG ) { - return [Buffer.from(this.script[0])]; // Return the public key + return [this.script[0]]; // Return the public key } return null; // No public keys found diff --git a/src/src/blockchain-indexer/processor/transaction/interfaces/OPNetHeader.ts b/src/src/blockchain-indexer/processor/transaction/interfaces/OPNetHeader.ts index 335195828..69f6a73b0 100644 --- a/src/src/blockchain-indexer/processor/transaction/interfaces/OPNetHeader.ts +++ b/src/src/blockchain-indexer/processor/transaction/interfaces/OPNetHeader.ts @@ -16,9 +16,9 @@ export class OPNetHeader { private _flags: number = 0; constructor( - header: Buffer, - public readonly minerMLDSAPublicKey: Buffer, - public readonly solution: Buffer, + header: Uint8Array, + public readonly minerMLDSAPublicKey: Uint8Array, + public readonly solution: Uint8Array, ) { this.reader = new BinaryReader(header); this._headerBytes = this.reader.readBytes(4); @@ -77,7 +77,10 @@ export class OPNetHeader { throw new Error('Invalid public key prefix'); } - const flagBuffer = Buffer.from(this._headerBytes.slice(1)); - this._flags = flagBuffer.readUIntBE(0, 3); + // Read 3-byte big-endian unsigned integer from bytes [1..3] + this._flags = + (this._headerBytes[1] << 16) | + (this._headerBytes[2] << 8) | + this._headerBytes[3]; } } diff --git a/src/src/blockchain-indexer/processor/transaction/transaction-factory/TransactionFactory.ts b/src/src/blockchain-indexer/processor/transaction/transaction-factory/TransactionFactory.ts index b7d50a94b..7c1b06d32 100644 --- a/src/src/blockchain-indexer/processor/transaction/transaction-factory/TransactionFactory.ts +++ b/src/src/blockchain-indexer/processor/transaction/transaction-factory/TransactionFactory.ts @@ -1,5 +1,5 @@ import { TransactionData } from '@btc-vision/bitcoin-rpc'; -import { networks } from '@btc-vision/bitcoin'; +import { equals, networks } from '@btc-vision/bitcoin'; import { OPNetTransactionTypes } from '../enums/OPNetTransactionTypes.js'; import { PossibleOPNetTransactions, TransactionInformation } from '../PossibleOPNetTransactions.js'; import { Transaction } from '../Transaction.js'; @@ -32,7 +32,7 @@ export class TransactionFactory { const index = parser.vInIndex; const tx = transactionObj.parse(data, index, blockHash, blockHeight, network, addressCache); - tx.verifyPreImage = (miner: Address, preimage: Buffer): Buffer | undefined => { + tx.verifyPreImage = (miner: Address, preimage: Uint8Array): Uint8Array | undefined => { //if (!enableVerification) { // console.log('allowedChallenges', allowedChallenges); // return; @@ -44,7 +44,7 @@ export class TransactionFactory { } const hasSolution = hasMiner.some((challenge) => { - return challenge.equals(preimage); + return equals(challenge, preimage); }); if (!hasSolution) { diff --git a/src/src/blockchain-indexer/processor/transaction/transaction-sorter/TransactionSorter.ts b/src/src/blockchain-indexer/processor/transaction/transaction-sorter/TransactionSorter.ts index 8a682ce78..6e9088eed 100644 --- a/src/src/blockchain-indexer/processor/transaction/transaction-sorter/TransactionSorter.ts +++ b/src/src/blockchain-indexer/processor/transaction/transaction-sorter/TransactionSorter.ts @@ -1,3 +1,4 @@ +import { compare, toHex } from '@btc-vision/bitcoin'; import { OPNetTransactionTypes } from '../enums/OPNetTransactionTypes.js'; import { Transaction } from '../Transaction.js'; import { PriorityQueue } from '@datastructures-js/priority-queue'; @@ -245,12 +246,12 @@ export class TransactionSorter { txA: Transaction, txB: Transaction, ): number { - return Buffer.compare(txA.computedIndexingHash, txB.computedIndexingHash); + return compare(txA.computedIndexingHash, txB.computedIndexingHash); } - private getInputTransactionId(originalTransactionId?: Buffer): string | undefined { + private getInputTransactionId(originalTransactionId?: Uint8Array): string | undefined { if (!originalTransactionId || originalTransactionId.length === 0) return; - return originalTransactionId.toString('hex'); + return toHex(originalTransactionId); } private getTransactionPriority(tx: Transaction): bigint { diff --git a/src/src/blockchain-indexer/processor/transaction/transactions/DeploymentTransaction.ts b/src/src/blockchain-indexer/processor/transaction/transactions/DeploymentTransaction.ts index d489a91d8..68d07b50c 100644 --- a/src/src/blockchain-indexer/processor/transaction/transactions/DeploymentTransaction.ts +++ b/src/src/blockchain-indexer/processor/transaction/transactions/DeploymentTransaction.ts @@ -1,5 +1,5 @@ import { TransactionData, VIn, VOut } from '@btc-vision/bitcoin-rpc'; -import bitcoin, { networks, opcodes, toXOnly } from '@btc-vision/bitcoin'; +import bitcoin, { alloc, concat, equals as bytesEquals, networks, opcodes, toXOnly } from '@btc-vision/bitcoin'; import { createPublicKey, UniversalSigner } from '@btc-vision/ecpair'; import { DeploymentTransactionDocument } from '../../../../db/interfaces/ITransactionDocument.js'; import { OPNetTransactionTypes } from '../enums/OPNetTransactionTypes.js'; @@ -30,19 +30,19 @@ import { AddressCache } from '../../AddressCache.js'; interface DeploymentWitnessData { readonly header: OPNetHeader; - readonly senderPubKey: Buffer; - hashedSenderPubKey: Buffer; + readonly senderPubKey: Uint8Array; + hashedSenderPubKey: Uint8Array; - contractSaltPubKey: Buffer; - contractSaltHash: Buffer; + contractSaltPubKey: Uint8Array; + contractSaltHash: Uint8Array; - readonly bytecode: Buffer; - readonly calldata?: Buffer; + readonly bytecode: Uint8Array; + readonly calldata?: Uint8Array; readonly features: Feature[]; } export class DeploymentTransaction extends SharedInteractionParameters { - public static LEGACY_DEPLOYMENT_SCRIPT: Buffer = Buffer.from([ + public static LEGACY_DEPLOYMENT_SCRIPT: Uint8Array = new Uint8Array([ opcodes.OP_TOALTSTACK, // HEADER opcodes.OP_TOALTSTACK, // MINER opcodes.OP_TOALTSTACK, // PREIMAGE @@ -73,13 +73,13 @@ export class DeploymentTransaction extends SharedInteractionParameters 0 + this._calldata && this._calldata.length > 0 ? this._calldata : undefined, challenge: unsafePreimage, @@ -470,7 +470,7 @@ export class DeploymentTransaction extends SharedInteractionParameters[]; @@ -36,7 +36,7 @@ export interface InteractionWitnessData { initEccLib(createLegacyBackend(ecc)); export class InteractionTransaction extends SharedInteractionParameters { - public static LEGACY_INTERACTION: Buffer = Buffer.from([ + public static LEGACY_INTERACTION: Uint8Array = new Uint8Array([ opcodes.OP_TOALTSTACK, // HEADER opcodes.OP_TOALTSTACK, // MINER opcodes.OP_TOALTSTACK, // PREIMAGE @@ -65,11 +65,11 @@ export class InteractionTransaction extends SharedInteractionParameters | undefined; - protected _calldata: Buffer | undefined; + protected _calldata: Uint8Array | undefined; - public get calldata(): Buffer { - const calldata = Buffer.alloc(this._calldata?.length || 0); - - if (this._calldata) { - this._calldata.copy(calldata); - } + public get calldata(): Uint8Array { + if (!this._calldata) return new Uint8Array(0); + const calldata = new Uint8Array(this._calldata.length); + calldata.set(this._calldata); return calldata; } @@ -64,8 +62,8 @@ export abstract class SharedInteractionParameters< public static getDataFromScript( scriptData: Array, breakWhenReachOpcode: number = opcodes.OP_ELSE, - ): Buffer | undefined { - let data: Buffer | undefined; + ): Uint8Array | undefined { + let data: Uint8Array | undefined; // Keep reading until we see the break opcode or run out of script data. while (scriptData.length > 0) { @@ -86,14 +84,14 @@ export abstract class SharedInteractionParameters< } // Accumulate the data - data = data ? Buffer.concat([data, currentItem]) : Buffer.from(currentItem); + data = data ? concat([data, currentItem]) : currentItem; } return data; } - public static getDataUntilBufferEnd(scriptData: Array): Buffer | undefined { - let data: Buffer | undefined; + public static getDataUntilBufferEnd(scriptData: Array): Uint8Array | undefined { + let data: Uint8Array | undefined; // Keep reading until we see the break opcode or run out of script data. while (scriptData.length > 0) { @@ -108,7 +106,7 @@ export abstract class SharedInteractionParameters< scriptData.shift(); // Accumulate the data - data = data ? Buffer.concat([data, currentItem]) : Buffer.from(currentItem); + data = data ? concat([data, currentItem]) : currentItem; } return data; @@ -147,7 +145,7 @@ export abstract class SharedInteractionParameters< return; } - return new OPNetHeader(Buffer.from(header), Buffer.from(minerMLDSAPublicKey), Buffer.from(preimage)); + return new OPNetHeader(header, minerMLDSAPublicKey, preimage); } protected static decodeFeatures( @@ -188,8 +186,8 @@ export abstract class SharedInteractionParameters< return; } - const tweakedKey = Buffer.from(this.from.tweakedPublicKeyToBuffer()); - const originalKey = Buffer.from(this.from.originalPublicKeyBuffer()); + const tweakedKey = this.from.tweakedPublicKeyToBuffer(); + const originalKey = this.from.originalPublicKeyBuffer(); const chainId = getChainId(NetworkConverter.networkToBitcoinNetwork(this.network)); const writer = new BinaryWriter(); @@ -253,8 +251,8 @@ export abstract class SharedInteractionParameters< public async verifyMLDSASignature( mldsaLinkRequest: MLDSARequestData, - originalKey: Buffer, - tweakedKey: Buffer, + originalKey: Uint8Array, + tweakedKey: Uint8Array, chainId: Uint8Array, vmManager: VMManager, ): Promise { @@ -293,7 +291,7 @@ export abstract class SharedInteractionParameters< // Then we check the ML-DSA signature const mldsaKeyPair = QuantumBIP32Factory.fromPublicKey( mldsaLinkRequest.publicKey, - Buffer.alloc(32, 0), + alloc(32), this.network, mldsaLinkRequest.level, ); @@ -319,7 +317,7 @@ export abstract class SharedInteractionParameters< }); } - protected safeEq(a: Buffer, b: Buffer): boolean { + protected safeEq(a: Uint8Array, b: Uint8Array): boolean { if (a.length !== b.length) return false; return timingSafeEqual(a, b); } @@ -327,18 +325,12 @@ export abstract class SharedInteractionParameters< /*public getAddress(str: string): Address { if (this.addressCache) { const addr: string | undefined = this.addressCache.get(str); - - if (!addr) { - const newAddr = new Address(, str); - this.addressCache.set(str, newAddr.toHex()); - - return newAddr; - } else { - return Address.fromString(str); + if (addr) { + return Address.fromString(addr); } - } else { - return Address.fromString(str); } + + return Address.fromString(str); }*/ protected decodeAddress(outputWitness: TransactionOutput): string | undefined { @@ -348,14 +340,14 @@ export abstract class SharedInteractionParameters< } const { address } = payments.p2op({ - output: new Uint8Array(Buffer.from(outputWitness.scriptPubKey.hex, 'hex')) as Script, + output: fromHex(outputWitness.scriptPubKey.hex) as Script, network: this.network, }); return address; } - protected decompressData(buffer: Buffer): Buffer { + protected decompressData(buffer: Uint8Array): Uint8Array { const decompressed = Transaction.decompressBuffer(buffer); if (decompressed.compressed) { this.wasCompressed = true; @@ -375,7 +367,7 @@ export abstract class SharedInteractionParameters< } private async regenerateProvenance(vmManager: VMManager): Promise { - const originalKey = Buffer.from(this.from.tweakedPublicKeyToBuffer()); + const originalKey = this.from.tweakedPublicKeyToBuffer(); // Get the key assigned to the legacy address. const keyData = await vmManager.getMLDSAPublicKeyFromLegacyKey(originalKey); @@ -411,7 +403,7 @@ export abstract class SharedInteractionParameters< } private decodeMLDSALinkRequest(feature: MLDSALinkRequest): MLDSARequestData { - const data: Buffer = feature.data; + const data = feature.data; const reader = new BinaryReader(data); const level: MLDSASecurityLevel = reader.readU8() as MLDSASecurityLevel; @@ -420,21 +412,21 @@ export abstract class SharedInteractionParameters< throw new Error(`OP_NET: ML-DSA level ${level} is not enabled.`); } - const hashedPublicKey = Buffer.from(reader.readBytes(32)); + const hashedPublicKey = reader.readBytes(32); if (isEmptyBuffer(hashedPublicKey)) { throw new Error(`OP_NET: ML-DSA hashed public key cannot be empty.`); } const verifyRequest = reader.readBoolean(); - let publicKey: Buffer | null = null; - let signature: Buffer | null = null; + let publicKey: Uint8Array | null = null; + let signature: Uint8Array | null = null; if (verifyRequest) { const publicKeyLength = MLDSAMetadata.fromLevel(level); const signatureLength = MLDSAMetadata.signatureLen(publicKeyLength); - publicKey = Buffer.from(reader.readBytes(publicKeyLength)); - signature = Buffer.from(reader.readBytes(signatureLength)); + publicKey = reader.readBytes(publicKeyLength); + signature = reader.readBytes(signatureLength); if (isEmptyBuffer(publicKey)) { throw new Error(`OP_NET: ML-DSA public key cannot be empty.`); @@ -450,12 +442,12 @@ export abstract class SharedInteractionParameters< publicKey: publicKey, level: level, mldsaSignature: signature, - legacySignature: Buffer.from(legacySignature), + legacySignature, }; } private decodeEpochSubmission(feature: EpochSubmissionFeature): Submission { - const data: Buffer = feature.data; + const data = feature.data; if (data.length > 32 + 32 + OPNetConsensus.consensus.EPOCH.GRAFFITI_LENGTH) { throw new Error( @@ -482,9 +474,9 @@ export abstract class SharedInteractionParameters< } return { - mldsaPublicKey: Buffer.from(mldsaPublicKey), - salt: Buffer.from(salt), - graffiti: graffiti ? Buffer.from(graffiti) : undefined, + mldsaPublicKey, + salt, + graffiti, }; } diff --git a/src/src/blockchain-indexer/rpc/thread/BitcoinRPCThread.ts b/src/src/blockchain-indexer/rpc/thread/BitcoinRPCThread.ts index abd05f2a6..66ab152a4 100644 --- a/src/src/blockchain-indexer/rpc/thread/BitcoinRPCThread.ts +++ b/src/src/blockchain-indexer/rpc/thread/BitcoinRPCThread.ts @@ -1,4 +1,5 @@ import { BitcoinRawTransactionParams, BitcoinRPC } from '@btc-vision/bitcoin-rpc'; +import { fromHex } from '@btc-vision/bitcoin'; import { DataConverter } from '@btc-vision/bsi-common'; import { Config } from '../../../config/Config.js'; import { @@ -114,7 +115,7 @@ export class BitcoinRPCThread extends Thread { if (response.revert) { revertData = typeof response.revert === 'string' - ? Uint8Array.from(Buffer.from(response.revert, 'hex')) + ? fromHex(response.revert) : response.revert; } @@ -131,7 +132,7 @@ export class BitcoinRPCThread extends Thread { : undefined, result: typeof response.result === 'string' - ? Uint8Array.from(Buffer.from(response.result, 'hex')) + ? fromHex(response.result) : response.result, revert: revertData, deployedContracts: [], @@ -155,7 +156,7 @@ export class BitcoinRPCThread extends Thread { const innerValue = value[i]; const event: NetEvent = new NetEvent( innerValue[0], - Uint8Array.from(Buffer.from(innerValue[1], 'hex')), + fromHex(innerValue[1]), ); events.push(event); diff --git a/src/src/blockchain-indexer/sync/classes/ChainSynchronisation.ts b/src/src/blockchain-indexer/sync/classes/ChainSynchronisation.ts index 541a68900..a051282db 100644 --- a/src/src/blockchain-indexer/sync/classes/ChainSynchronisation.ts +++ b/src/src/blockchain-indexer/sync/classes/ChainSynchronisation.ts @@ -3,7 +3,7 @@ import { ThreadTypes } from '../../../threading/thread/enums/ThreadTypes.js'; import { ThreadMessageBase } from '../../../threading/interfaces/thread-messages/ThreadMessageBase.js'; import { MessageType } from '../../../threading/enum/MessageType.js'; import { ThreadData } from '../../../threading/interfaces/ThreadData.js'; -import { Network } from '@btc-vision/bitcoin'; +import { Network, toHex } from '@btc-vision/bitcoin'; import { NetworkConverter } from '../../../config/network/NetworkConverter.js'; import { BlockFetcher } from '../../fetcher/abstract/BlockFetcher.js'; import { Config } from '../../../config/Config.js'; @@ -307,7 +307,7 @@ export class ChainSynchronisation extends Logger { const toSort: readonly Utxo[] = utxos .map((o) => { return o.outputs.map((output) => ({ - txid: o.id.toString('hex'), + txid: toHex(o.id), output, })); }) diff --git a/src/src/blockchain-indexer/sync/solver/AnyoneCanSpendDetector.ts b/src/src/blockchain-indexer/sync/solver/AnyoneCanSpendDetector.ts index 3608ba1f3..237319bab 100644 --- a/src/src/blockchain-indexer/sync/solver/AnyoneCanSpendDetector.ts +++ b/src/src/blockchain-indexer/sync/solver/AnyoneCanSpendDetector.ts @@ -78,11 +78,11 @@ export class AnyoneCanSpendDetector extends Logger { private readonly truthCache = new LRUCache({ max: 65_536 }); private readonly ENABLED_WITNESS_VERSIONS = new Set([0, 1]); - private readonly TRUE_SCRIPTS: Buffer[] = [ - Buffer.from([opcodes.OP_1]), - Buffer.from([0x01, 0x01]), - Buffer.from([0x50]), - Buffer.from([opcodes.OP_0, opcodes.OP_0, opcodes.OP_WITHIN]), + private readonly TRUE_SCRIPTS: Uint8Array[] = [ + new Uint8Array([opcodes.OP_1]), + new Uint8Array([0x01, 0x01]), + new Uint8Array([0x50]), + new Uint8Array([opcodes.OP_0, opcodes.OP_0, opcodes.OP_WITHIN]), ]; private readonly P2SH_H160: Set; private readonly P2WSH_SHA256: Set; @@ -290,7 +290,7 @@ export class AnyoneCanSpendDetector extends Logger { private detectHashedTrue(out: TransactionOutput): AnyoneCanSpendHit | undefined { const t = out.scriptPubKey.type; if (t === 'scripthash') { - const h = out.scriptPubKeyBuffer.subarray(2, 22).toString('hex'); + const h = toHex(out.scriptPubKeyBuffer.subarray(2, 22)); if (this.P2SH_H160.has(h)) { this.log('[detectHashedTrue] matched P2SH hash of known TRUE script'); return { reason: AnyoneCanSpendReason.P2SH_True }; @@ -298,7 +298,7 @@ export class AnyoneCanSpendDetector extends Logger { } if (t === 'witness_v0_scripthash') { - const h = out.scriptPubKeyBuffer.subarray(2, 34).toString('hex'); + const h = toHex(out.scriptPubKeyBuffer.subarray(2, 34)); if (this.P2WSH_SHA256.has(h)) { this.log('[detectHashedTrue] matched P2WSH hash of known TRUE script'); return { reason: AnyoneCanSpendReason.P2WSH_True }; @@ -306,8 +306,8 @@ export class AnyoneCanSpendDetector extends Logger { } } - private evaluatesTrue(lock: Buffer): boolean { - const key = lock.length > 80 ? '' : lock.toString('hex'); + private evaluatesTrue(lock: Uint8Array): boolean { + const key = lock.length > 80 ? '' : toHex(lock); if (key) { const memo = this.truthCache.get(key); if (memo !== undefined) { @@ -357,7 +357,7 @@ export class AnyoneCanSpendDetector extends Logger { private readScriptNum(buf: Uint8Array): bigint { if (!buf.length) return 0n; const neg = (buf[buf.length - 1] & 0x80) !== 0; - const clone = Buffer.from(buf); + const clone = new Uint8Array(buf); clone[clone.length - 1] &= 0x7f; let v = 0n; for (let i = 0; i < clone.length; i++) v |= BigInt(clone[i]) << (8n * BigInt(i)); diff --git a/src/src/blockchain-indexer/sync/solver/UTXOSorter.ts b/src/src/blockchain-indexer/sync/solver/UTXOSorter.ts index a91e283cf..56c87b296 100644 --- a/src/src/blockchain-indexer/sync/solver/UTXOSorter.ts +++ b/src/src/blockchain-indexer/sync/solver/UTXOSorter.ts @@ -48,7 +48,7 @@ const solverCache: LRUCache = new LRUCache toHex(btcCrypto.sha256(Buffer.from(u))); +const h256 = (u: Uint8Array) => toHex(btcCrypto.sha256(u)); export class UtxoSorter extends Logger { public readonly logColor: string = '#ff9100'; // Bright green for UTXO sorter logs diff --git a/src/src/blockchain-indexer/worker/TransactionProcessor.ts b/src/src/blockchain-indexer/worker/TransactionProcessor.ts index 3be381b28..f06ce7fc1 100644 --- a/src/src/blockchain-indexer/worker/TransactionProcessor.ts +++ b/src/src/blockchain-indexer/worker/TransactionProcessor.ts @@ -5,6 +5,7 @@ import { NetworkConverter } from '../../config/network/NetworkConverter.js'; import { MsgError, MsgFromMain, MsgResult, MsgToMain } from './interfaces.js'; import { OPNetConsensus } from '../../poc/configurations/OPNetConsensus.js'; import { Address } from '@btc-vision/transaction'; +import { equals, fromHex } from '@btc-vision/bitcoin'; const port: MessagePort = (() => { if (parentPort == null) { @@ -28,11 +29,11 @@ port.on('message', (msg: MsgFromMain): void => { undefined, ); - const buf = msg.allowedPreimages.map((preimage) => Buffer.from(preimage, 'hex')); - itx.verifyPreImage = (_miner: Address, preimage: Buffer): Buffer | undefined => { + const buf = msg.allowedPreimages.map((preimage) => fromHex(preimage)); + itx.verifyPreImage = (_miner: Address, preimage: Uint8Array): Uint8Array | undefined => { console.warn('!!! verifyPreImage is not implemented in TxParseWorker !!!'); - const isValid = buf.some((allowedPreimage) => allowedPreimage.equals(preimage)); + const isValid = buf.some((allowedPreimage) => equals(allowedPreimage, preimage)); if (!isValid) { throw new Error('Invalid preimage'); } diff --git a/src/src/db/documents/interfaces/IEpochDocument.ts b/src/src/db/documents/interfaces/IEpochDocument.ts index 6e5505c5e..c3bc6b319 100644 --- a/src/src/db/documents/interfaces/IEpochDocument.ts +++ b/src/src/db/documents/interfaces/IEpochDocument.ts @@ -29,16 +29,16 @@ export interface IEpoch { readonly startBlock: bigint; readonly endBlock: bigint; - readonly targetHash: Buffer; - readonly target: Buffer; - readonly solution: Buffer; - readonly salt: Buffer; - readonly mldsaPublicKey: Buffer; - readonly legacyPublicKey: Buffer; - readonly graffiti?: Buffer; + readonly targetHash: Uint8Array; + readonly target: Uint8Array; + readonly solution: Uint8Array; + readonly salt: Uint8Array; + readonly mldsaPublicKey: Uint8Array; + readonly legacyPublicKey: Uint8Array; + readonly graffiti?: Uint8Array; readonly solutionBits: number; - readonly epochRoot: Buffer; - readonly epochHash: Buffer; + readonly epochRoot: Uint8Array; + readonly epochHash: Uint8Array; - readonly proofs: Buffer[]; + readonly proofs: Uint8Array[]; } diff --git a/src/src/db/documents/interfaces/IEpochSubmissionsDocument.ts b/src/src/db/documents/interfaces/IEpochSubmissionsDocument.ts index d9796ab75..133745fc1 100644 --- a/src/src/db/documents/interfaces/IEpochSubmissionsDocument.ts +++ b/src/src/db/documents/interfaces/IEpochSubmissionsDocument.ts @@ -18,11 +18,11 @@ export interface IEpochSubmissionsDocument { export interface EpochSubmissionWinner { readonly epochNumber: bigint; readonly matchingBits: number; - readonly salt: Buffer; - readonly mldsaPublicKey: Buffer; - readonly legacyPublicKey: Buffer; - readonly solutionHash: Buffer; - readonly graffiti: Buffer; + readonly salt: Uint8Array; + readonly mldsaPublicKey: Uint8Array; + readonly legacyPublicKey: Uint8Array; + readonly solutionHash: Uint8Array; + readonly graffiti: Uint8Array; } export interface EpochSubmissionAPIResult { diff --git a/src/src/db/documents/interfaces/ITargetEpochDocument.ts b/src/src/db/documents/interfaces/ITargetEpochDocument.ts index b10685100..a0575aecb 100644 --- a/src/src/db/documents/interfaces/ITargetEpochDocument.ts +++ b/src/src/db/documents/interfaces/ITargetEpochDocument.ts @@ -13,7 +13,7 @@ export interface ITargetEpochDocument { } export interface PendingTargetEpoch { - readonly checksumRoot: Buffer; + readonly checksumRoot: Uint8Array; readonly nextEpochNumber: bigint; - readonly targetHash: Buffer; + readonly targetHash: Uint8Array; } diff --git a/src/src/db/interfaces/IBlockHeaderBlockDocument.ts b/src/src/db/interfaces/IBlockHeaderBlockDocument.ts index 1141cfa9f..b74d669ab 100644 --- a/src/src/db/interfaces/IBlockHeaderBlockDocument.ts +++ b/src/src/db/interfaces/IBlockHeaderBlockDocument.ts @@ -56,7 +56,7 @@ export interface BlockHeaderAPIBlockDocument export interface BlockHeader extends Omit { readonly height: bigint; - readonly hash: Buffer; + readonly hash: Uint8Array; } export type IBlockHeaderBlockDocument = BlockHeaderDocument & IBaseDocument; diff --git a/src/src/db/interfaces/IMLDSAPublicKey.ts b/src/src/db/interfaces/IMLDSAPublicKey.ts index 22a7c2c47..0748d7716 100644 --- a/src/src/db/interfaces/IMLDSAPublicKey.ts +++ b/src/src/db/interfaces/IMLDSAPublicKey.ts @@ -3,11 +3,11 @@ import { MLDSASecurityLevel } from '@btc-vision/transaction'; export interface IMLDSAPublicKey { readonly level: MLDSASecurityLevel; - readonly hashedPublicKey: Buffer; - readonly legacyPublicKey: Buffer; - readonly tweakedPublicKey: Buffer; + readonly hashedPublicKey: Uint8Array; + readonly legacyPublicKey: Uint8Array; + readonly tweakedPublicKey: Uint8Array; // If null, wallet owner did not expose his public key on-chain yet. - readonly publicKey: Buffer | null; + readonly publicKey: Uint8Array | null; // Can be temporally wrong. readonly insertedBlockHeight: bigint | null; diff --git a/src/src/db/interfaces/IMempoolTransaction.ts b/src/src/db/interfaces/IMempoolTransaction.ts index 6e85687e2..deab55d23 100644 --- a/src/src/db/interfaces/IMempoolTransaction.ts +++ b/src/src/db/interfaces/IMempoolTransaction.ts @@ -33,7 +33,7 @@ export interface IMempoolTransactionObj IMempoolTransaction, 'data' | 'blockHeight' | 'outputs' | 'inputs' | 'theoreticalGasLimit' | 'priorityFee' > { - readonly data: Buffer; + readonly data: Uint8Array; readonly blockHeight: bigint; isOPNet: boolean; @@ -46,7 +46,7 @@ export interface IMempoolTransactionObj }[]; readonly outputs: { - readonly data: Buffer; + readonly data: Uint8Array; readonly address: string | null; readonly outputIndex: number; value: Long; diff --git a/src/src/db/interfaces/ITransactionDocument.ts b/src/src/db/interfaces/ITransactionDocument.ts index ce3cd5622..e2e6d105c 100644 --- a/src/src/db/interfaces/ITransactionDocument.ts +++ b/src/src/db/interfaces/ITransactionDocument.ts @@ -15,9 +15,9 @@ import { import { Address } from '@btc-vision/transaction'; export interface TransactionDocumentBasic { - readonly id: Buffer; - readonly hash: Buffer; - readonly raw: Buffer; + readonly id: Uint8Array; + readonly hash: Uint8Array; + readonly raw: Uint8Array; readonly index: number; // Mark the order of the transaction in the block readonly blockHeight: Decimal128 | string | undefined; @@ -61,12 +61,12 @@ export interface TransactionSafeThread { } export interface InteractionTransactionSafeThread extends TransactionSafeThread { - readonly calldata: Buffer; - readonly preimage: Buffer; - readonly miner: Buffer; - readonly senderPubKeyHash: Buffer; - readonly contractSecret: Buffer; - readonly interactionPubKey: Buffer; + readonly calldata: Uint8Array; + readonly preimage: Uint8Array; + readonly miner: Uint8Array; + readonly senderPubKeyHash: Uint8Array; + readonly contractSecret: Uint8Array; + readonly interactionPubKey: Uint8Array; readonly contractAddress: Uint8Array; readonly from: Uint8Array; readonly fromLegacy: Uint8Array; diff --git a/src/src/db/interfaces/IUnspentTransaction.ts b/src/src/db/interfaces/IUnspentTransaction.ts index 1881aea2a..62fd34c12 100644 --- a/src/src/db/interfaces/IUnspentTransaction.ts +++ b/src/src/db/interfaces/IUnspentTransaction.ts @@ -9,7 +9,7 @@ export interface ShortScriptPubKey { export interface IUnspentTransaction { blockHeight: Long; - readonly transactionId: Binary | Buffer; + readonly transactionId: Binary | Uint8Array; readonly outputIndex: number; value: Long; @@ -19,7 +19,7 @@ export interface IUnspentTransaction { } export interface ISpentTransaction { - readonly transactionId: Buffer; + readonly transactionId: Uint8Array; readonly outputIndex: number; readonly deletedAtBlock?: Long; } diff --git a/src/src/db/repositories/ContractRepository.ts b/src/src/db/repositories/ContractRepository.ts index eae287b95..790020678 100644 --- a/src/src/db/repositories/ContractRepository.ts +++ b/src/src/db/repositories/ContractRepository.ts @@ -20,6 +20,7 @@ import { import { ContractInformation } from '../../blockchain-indexer/processor/transaction/contract/ContractInformation.js'; import { IContractDocument } from '../documents/interfaces/IContractDocument.js'; import { Address } from '@btc-vision/transaction'; +import { fromBase64 } from '@btc-vision/bitcoin'; import { OPNetCollections } from '../indexes/required/IndexedCollection.js'; export class ContractRepository extends BaseRepository { @@ -54,7 +55,7 @@ export class ContractRepository extends BaseRepository { const contractAddresses = data.map((doc) => { if (typeof doc.contractPublicKey === 'string') { - return new Address(Buffer.from(doc.contractPublicKey, 'base64')); + return new Address(fromBase64(doc.contractPublicKey)); } else { if (!doc.contractPublicKey) { throw new Error('Contract tweaked public key is undefined'); diff --git a/src/src/db/repositories/EpochRepository.ts b/src/src/db/repositories/EpochRepository.ts index 70e0b39af..30152eacf 100644 --- a/src/src/db/repositories/EpochRepository.ts +++ b/src/src/db/repositories/EpochRepository.ts @@ -59,7 +59,7 @@ export class EpochRepository extends BaseRepository { * Get epoch by epoch hash */ public async getEpochByHash( - epochHash: Buffer | Binary, + epochHash: Uint8Array | Binary, currentSession?: ClientSession, ): Promise { const binaryHash = epochHash instanceof Binary ? epochHash : new Binary(epochHash); @@ -119,7 +119,7 @@ export class EpochRepository extends BaseRepository { * Get epochs by proposer public key */ public async getEpochsByProposer( - proposerPublicKey: Buffer | Binary, + proposerPublicKey: Uint8Array | Binary, currentSession?: ClientSession, ): Promise { const binaryKey = @@ -188,11 +188,11 @@ export class EpochRepository extends BaseRepository { const minerAddress = new Address(epoch.proposer.mldsaPublicKey.buffer); const solutionArray = challengeSolution.solutions.get(minerAddress) || []; - solutionArray.push(Buffer.from(epoch.proposer.solution.buffer)); + solutionArray.push(new Uint8Array(epoch.proposer.solution.buffer)); challengeSolution.legacyPublicKeys.set( minerAddress, - Buffer.from(epoch.proposer.legacyPublicKey.buffer), + new Uint8Array(epoch.proposer.legacyPublicKey.buffer), ); challengeSolution.solutions.set(minerAddress, solutionArray); @@ -205,7 +205,7 @@ export class EpochRepository extends BaseRepository { * Get epochs by target hash */ public async getEpochsByTargetHash( - targetHash: Buffer | Binary, + targetHash: Uint8Array | Binary, currentSession?: ClientSession, ): Promise { const binaryHash = targetHash instanceof Binary ? targetHash : new Binary(targetHash); @@ -223,7 +223,7 @@ export class EpochRepository extends BaseRepository { * Count epochs by proposer */ public async countEpochsByProposer( - proposerPublicKey: Buffer | Binary, + proposerPublicKey: Uint8Array | Binary, currentSession?: ClientSession, ): Promise { const binaryKey = diff --git a/src/src/db/repositories/EpochSubmissionsRepository.ts b/src/src/db/repositories/EpochSubmissionsRepository.ts index 9613f27e2..8f9b960bf 100644 --- a/src/src/db/repositories/EpochSubmissionsRepository.ts +++ b/src/src/db/repositories/EpochSubmissionsRepository.ts @@ -30,7 +30,7 @@ export class EpochSubmissionRepository extends BaseRepository { const binaryHash = txHash instanceof Binary ? txHash : new Binary(txHash); @@ -55,7 +55,7 @@ export class EpochSubmissionRepository extends BaseRepository { const binaryId = txId instanceof Binary ? txId : new Binary(txId); @@ -103,7 +103,7 @@ export class EpochSubmissionRepository extends BaseRepository { const binaryKey = @@ -141,7 +141,7 @@ export class EpochSubmissionRepository extends BaseRepository { const binaryHash = @@ -235,8 +235,8 @@ export class EpochSubmissionRepository extends BaseRepository { const criteria: Partial> = { diff --git a/src/src/db/repositories/MLDSAPublicKeysRepository.ts b/src/src/db/repositories/MLDSAPublicKeysRepository.ts index e6946e7fa..451023150 100644 --- a/src/src/db/repositories/MLDSAPublicKeysRepository.ts +++ b/src/src/db/repositories/MLDSAPublicKeysRepository.ts @@ -16,6 +16,7 @@ import { } from '../interfaces/IMLDSAPublicKey.js'; import { ExtendedBaseRepository } from './ExtendedBaseRepository.js'; import { MLDSASecurityLevel } from '@btc-vision/transaction'; +import { fromHex } from '@btc-vision/bitcoin'; export interface MLDSAPublicKeyExists { readonly hashedExists: boolean; @@ -119,7 +120,7 @@ export class MLDSAPublicKeyRepository extends ExtendedBaseRepository { @@ -139,7 +140,7 @@ export class MLDSAPublicKeyRepository extends ExtendedBaseRepository { @@ -159,7 +160,7 @@ export class MLDSAPublicKeyRepository extends ExtendedBaseRepository { @@ -183,8 +184,8 @@ export class MLDSAPublicKeyRepository extends ExtendedBaseRepository { // Convert to binary first, then validate lengths (handles hex strings correctly) const binHashed: Binary = this.toBinary(hashedPublicKey); @@ -224,13 +225,13 @@ export class MLDSAPublicKeyRepository extends ExtendedBaseRepository { private convertToObj(data: IMempoolTransaction): IMempoolTransactionObj { return { ...data, - data: Buffer.from(data.data.buffer), + data: new Uint8Array(data.data.buffer), blockHeight: DataConverter.fromDecimal128(data.blockHeight), theoreticalGasLimit: Long.isLong(data.theoreticalGasLimit) ? data.theoreticalGasLimit.toBigInt() @@ -624,7 +624,7 @@ export class MempoolRepository extends BaseRepository { }), outputs: data.outputs.map((output) => { return { - data: Buffer.from(output.data.buffer), + data: new Uint8Array(output.data.buffer), outputIndex: output.outputIndex, value: output.value instanceof Long ? output.value : Long.fromNumber(output.value), diff --git a/src/src/db/repositories/PublicKeysRepository.ts b/src/src/db/repositories/PublicKeysRepository.ts index ff64f3a34..d53b770c0 100644 --- a/src/src/db/repositories/PublicKeysRepository.ts +++ b/src/src/db/repositories/PublicKeysRepository.ts @@ -11,7 +11,7 @@ import { OPNetCollections } from '../indexes/required/IndexedCollection.js'; import { PublicKeyDocument } from '../interfaces/PublicKeyDocument.js'; import { ExtendedBaseRepository } from './ExtendedBaseRepository.js'; import { ProcessUnspentTransactionList } from './UnspentTransactionRepository.js'; -import { Network, networks, payments, toXOnly } from '@btc-vision/bitcoin'; +import { fromHex, Network, networks, payments, toHex, toXOnly } from '@btc-vision/bitcoin'; import { createPublicKey } from '@btc-vision/ecpair'; import { TransactionOutput } from '../../blockchain-indexer/processor/transaction/inputs/TransactionOutput.js'; import { NetworkConverter } from '../../config/network/NetworkConverter.js'; @@ -69,13 +69,13 @@ export class PublicKeysRepository extends ExtendedBaseRepository { + public async addTweakedPublicKey(tweaked: Uint8Array, session?: ClientSession): Promise { const filter = { tweakedPublicKey: new Binary(tweaked), p2tr: this.tweakedPubKeyToAddress(tweaked, this.network), @@ -223,7 +223,7 @@ export class PublicKeysRepository extends ExtendedBaseRepository { @@ -299,7 +299,7 @@ export class PublicKeysRepository extends ExtendedBaseRepository p2tr // mldsa.hashedPublicKey -> p2op (when MLDSA exists) const isCompressed = originalPubKey !== null; - const tweakedKey = isCompressed ? this.tweakPublicKey(bufferKey) : null; - const tweakedXOnly = tweakedKey ? Buffer.from(toXOnly(createPublicKey(tweakedKey))) : bufferKey; + const tweakedKey = isCompressed ? this.tweakPublicKey(keyBytes) : null; + const tweakedXOnly = tweakedKey ? toXOnly(createPublicKey(tweakedKey)) : keyBytes; const info: PublicKeyInfo = { - tweakedPubkey: tweakedXOnly.toString('hex'), + tweakedPubkey: toHex(tweakedXOnly), p2tr: this.tweakedPubKeyToAddress(tweakedXOnly, this.network), }; if (isCompressed && tweakedKey) { - const ecKeyPair = EcKeyPair.fromPublicKey(bufferKey, this.network); + const ecKeyPair = EcKeyPair.fromPublicKey(keyBytes, this.network); info.originalPubKey = originalPubKey; info.p2pkh = EcKeyPair.getLegacyAddress(ecKeyPair, this.network); //info.p2shp2wpkh = EcKeyPair.getLegacySegwitAddress(ecKeyPair, this.network); @@ -325,20 +325,20 @@ export class PublicKeysRepository extends ExtendedBaseRepository = { $or: [ { contractAddress: key }, - { contractPublicKey: new Binary(Buffer.from(key, 'hex')) }, + { contractPublicKey: new Binary(fromHex(key)) }, ], }; @@ -409,8 +411,8 @@ export class PublicKeysRepository extends ExtendedBaseRepository { - const contractPublicKeyBuffer = Buffer.from((contract.contractPublicKey as Binary).buffer); - const p2tr = this.tweakedPubKeyToAddress(contractPublicKeyBuffer, this.network); + const contractPublicKeyBytes = new Uint8Array((contract.contractPublicKey as Binary).buffer); + const p2tr = this.tweakedPubKeyToAddress(contractPublicKeyBytes, this.network); const baseDocument: PublicKeyWithMLDSA = { tweakedPublicKey: contract.contractPublicKey as Binary, @@ -452,13 +454,13 @@ export class PublicKeysRepository extends ExtendedBaseRepository { try { - const keyBuffer = new Binary(Buffer.from(key, 'hex')); + const keyBinary = new Binary(fromHex(key)); const filter: Filter = { $or: [ { p2op: key }, { p2tr: key }, - { tweakedPublicKey: keyBuffer }, - { publicKey: keyBuffer }, + { tweakedPublicKey: keyBinary }, + { publicKey: keyBinary }, { p2pkh: key }, //{ p2shp2wpkh: key }, { p2wpkh: key }, @@ -504,8 +506,8 @@ export class PublicKeysRepository extends ExtendedBaseRepository */ public async targetEpochExists( epochNumber: bigint, - salt: Buffer | Binary, - mldsaPublicKey: Buffer | Binary, + salt: Uint8Array | Binary, + mldsaPublicKey: Uint8Array | Binary, ): Promise { const binarySalt = salt instanceof Binary ? salt : new Binary(salt); diff --git a/src/src/db/repositories/TransactionRepository.ts b/src/src/db/repositories/TransactionRepository.ts index 71b41d4a1..864d9334b 100644 --- a/src/src/db/repositories/TransactionRepository.ts +++ b/src/src/db/repositories/TransactionRepository.ts @@ -15,6 +15,7 @@ import { import { OPNetTransactionTypes } from '../../blockchain-indexer/processor/transaction/enums/OPNetTransactionTypes.js'; import { ITransactionDocument, TransactionDocument } from '../interfaces/ITransactionDocument.js'; import { OPNetCollections } from '../indexes/required/IndexedCollection.js'; +import { fromHex } from '@btc-vision/bitcoin'; /** * Reworked repository that stores hash/id purely as binary. @@ -125,19 +126,19 @@ export class TransactionRepository extends BaseRepository< * Retrieves a single transaction by its hash or id (both are stored as binary). * * If you still have external code that provides the hash as a hex string, - * you must convert it to Binary/Buffer here. + * you must convert it to Binary/Uint8Array here. * - * @param hashOrId - a Buffer or a string (hex) to be matched against `hash` or `id` in binary form + * @param hashOrId - a Uint8Array or a string (hex) to be matched against `hash` or `id` in binary form * @param currentSession */ public async getTransactionByHash( - hashOrId: Buffer | string, + hashOrId: Uint8Array | string, currentSession?: ClientSession, ): Promise | undefined> { - // If `hashOrId` is string (hex?), convert to a Buffer. Then wrap in Binary to query. + // If `hashOrId` is string (hex?), convert to a Uint8Array. Then wrap in Binary to query. let binHash: Binary; if (typeof hashOrId === 'string') { - binHash = new Binary(Buffer.from(hashOrId, 'hex')); + binHash = new Binary(fromHex(hashOrId)); } else { binHash = new Binary(hashOrId); } diff --git a/src/src/plugins/api/PluginBlockchainAPI.ts b/src/src/plugins/api/PluginBlockchainAPI.ts index 4df12149d..c704c2419 100644 --- a/src/src/plugins/api/PluginBlockchainAPI.ts +++ b/src/src/plugins/api/PluginBlockchainAPI.ts @@ -1,5 +1,6 @@ import { Binary, Db } from 'mongodb'; import { DataConverter } from '@btc-vision/bsi-common'; +import { fromHex, toHex } from '@btc-vision/bitcoin'; import { Address } from '@btc-vision/transaction'; import { BlockRepository } from '../../db/repositories/BlockRepository.js'; import { TransactionRepository } from '../../db/repositories/TransactionRepository.js'; @@ -74,7 +75,7 @@ export interface ITransactionDocument { export interface IContractEvent { readonly contractAddress: string; readonly eventType: string; - readonly data: Buffer; + readonly data: Uint8Array; readonly blockHeight: bigint; readonly txid: string; readonly eventIndex: number; @@ -88,7 +89,7 @@ export interface ITransactionReceipt { readonly gasUsed: bigint; readonly events: readonly IContractEvent[]; readonly revertReason?: string; - readonly returnData?: Buffer; + readonly returnData?: Uint8Array; } /** @@ -98,7 +99,7 @@ export interface IContractInfo { readonly address: string; readonly deploymentHeight: bigint; readonly deploymentTxid: string; - readonly bytecode?: Buffer; + readonly bytecode?: Uint8Array; readonly deployer?: string; readonly isActive: boolean; } @@ -135,7 +136,7 @@ export interface IPluginBlockchainAPI { getTransaction(txid: string): Promise; getTransactionsByBlock(height: bigint): Promise; getContract(address: string): Promise; - getContractStorage(address: string, pointer: bigint): Promise; + getContractStorage(address: string, pointer: bigint): Promise; getContractEvents( address: string, fromBlock: bigint, @@ -275,7 +276,7 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { return { address: contract.contractAddress, deploymentHeight: contract.blockHeight, - deploymentTxid: contract.deployedTransactionId.toString('hex'), + deploymentTxid: toHex(contract.deployedTransactionId), bytecode: contract.bytecode, deployer: contract.deployerAddress.toString(), isActive: true, @@ -285,7 +286,7 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { /** * Get contract storage value at a specific pointer */ - public async getContractStorage(address: string, pointer: bigint): Promise { + public async getContractStorage(address: string, pointer: bigint): Promise { this.checkPermission('contracts'); const contractAddress = Address.fromString(address); @@ -299,7 +300,7 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { return null; } - return Buffer.from(result.value); + return new Uint8Array(result.value); } /** @@ -438,12 +439,12 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { * Map a transaction document to the plugin-friendly format */ private mapTransaction(tx: { - id?: Binary | Buffer; - hash?: Binary | Buffer; + id?: Binary | Uint8Array; + hash?: Binary | Uint8Array; blockHeight?: { toString(): string }; index: number; inputs: Array<{ - originalTransactionId?: Binary | Buffer; + originalTransactionId?: Binary | Uint8Array; outputTransactionIndex?: number; sequence?: number; }>; @@ -463,11 +464,11 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { const inputs: ITransactionInput[] = tx.inputs.map((input) => { let txid = '0000000000000000000000000000000000000000000000000000000000000000'; if (input.originalTransactionId) { - const buf = + const bytes = input.originalTransactionId instanceof Binary - ? Buffer.from(input.originalTransactionId.buffer) + ? new Uint8Array(input.originalTransactionId.buffer) : input.originalTransactionId; - txid = buf.toString('hex'); + txid = toHex(bytes); } return { txid, @@ -479,7 +480,7 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { const outputs: ITransactionOutput[] = tx.outputs.map((output) => { const hexValue = output.scriptPubKey.hex instanceof Binary - ? Buffer.from(output.scriptPubKey.hex.buffer).toString('hex') + ? toHex(new Uint8Array(output.scriptPubKey.hex.buffer)) : output.scriptPubKey.hex; return { value: @@ -501,26 +502,26 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { let receipt: ITransactionReceipt | undefined; if (tx.revert !== undefined) { - const revertBuffer = Buffer.from(tx.revert.buffer); + const revertBytes = new Uint8Array(tx.revert.buffer); receipt = { - success: revertBuffer.length === 0, + success: revertBytes.length === 0, gasUsed: gasUsed ?? 0n, events: [], - revertReason: revertBuffer.length > 0 ? revertBuffer.toString('utf8') : undefined, + revertReason: revertBytes.length > 0 ? new TextDecoder().decode(revertBytes) : undefined, }; } - // Convert id and hash from Binary/Buffer to hex string + // Convert id and hash from Binary/Uint8Array to hex string let txidHex = ''; if (tx.id) { - const idBuf = tx.id instanceof Binary ? Buffer.from(tx.id.buffer) : tx.id; - txidHex = idBuf.toString('hex'); + const idBytes = tx.id instanceof Binary ? new Uint8Array(tx.id.buffer) : tx.id; + txidHex = toHex(idBytes); } let hashHex = ''; if (tx.hash) { - const hashBuf = tx.hash instanceof Binary ? Buffer.from(tx.hash.buffer) : tx.hash; - hashHex = hashBuf.toString('hex'); + const hashBytes = tx.hash instanceof Binary ? new Uint8Array(tx.hash.buffer) : tx.hash; + hashHex = toHex(hashBytes); } return { @@ -564,7 +565,6 @@ export class PluginBlockchainAPI implements IPluginBlockchainAPI { */ private bigintToPointer(value: bigint): Uint8Array { const hex = value.toString(16).padStart(64, '0'); - const buffer = Buffer.from(hex, 'hex'); - return new Uint8Array(buffer); + return fromHex(hex); } } diff --git a/src/src/plugins/api/PluginFilesystemAPI.ts b/src/src/plugins/api/PluginFilesystemAPI.ts index 8660d0e24..7f411b29c 100644 --- a/src/src/plugins/api/PluginFilesystemAPI.ts +++ b/src/src/plugins/api/PluginFilesystemAPI.ts @@ -36,12 +36,12 @@ export class PluginFilesystemAPI implements IPluginFilesystemAPI { } /** - * Read a file as a Buffer + * Read a file as a Uint8Array */ - public async readFile(filePath: string): Promise { + public async readFile(filePath: string): Promise { const resolvedPath = await this.validateAndResolvePath(filePath); try { - return await fs.readFile(resolvedPath); + return new Uint8Array(await fs.readFile(resolvedPath)); } catch (error) { throw new PluginFilesystemError( `Failed to read file: ${error instanceof Error ? error.message : String(error)}`, @@ -54,7 +54,7 @@ export class PluginFilesystemAPI implements IPluginFilesystemAPI { /** * Write data to a file */ - public async writeFile(filePath: string, data: Buffer | string): Promise { + public async writeFile(filePath: string, data: Uint8Array | string): Promise { const resolvedPath = await this.validateAndResolvePath(filePath); // Ensure parent directory exists diff --git a/src/src/plugins/api/websocket/PluginOpcodeRegistry.ts b/src/src/plugins/api/websocket/PluginOpcodeRegistry.ts index 3e60e3e01..81c50600b 100644 --- a/src/src/plugins/api/websocket/PluginOpcodeRegistry.ts +++ b/src/src/plugins/api/websocket/PluginOpcodeRegistry.ts @@ -281,9 +281,9 @@ export class PluginOpcodeRegistry extends Logger { /** * Load a plugin's proto schema */ - private loadProtoSchema(pluginId: string, protoContent: Buffer, _namespace?: string): void { + private loadProtoSchema(pluginId: string, protoContent: Uint8Array, _namespace?: string): void { try { - const protoString = protoContent.toString('utf8'); + const protoString = new TextDecoder().decode(protoContent); const root = protobuf.parse(protoString).root; this.protoRoots.set(pluginId, root); this.info(`Loaded proto schema for plugin ${pluginId}`); diff --git a/src/src/plugins/context/PluginContext.ts b/src/src/plugins/context/PluginContext.ts index c1f330fbb..acb0465a8 100644 --- a/src/src/plugins/context/PluginContext.ts +++ b/src/src/plugins/context/PluginContext.ts @@ -80,8 +80,8 @@ export interface IPluginCursor { * Plugin filesystem API interface */ export interface IPluginFilesystemAPI { - readFile(path: string): Promise; - writeFile(path: string, data: Buffer | string): Promise; + readFile(path: string): Promise; + writeFile(path: string, data: Uint8Array | string): Promise; exists(path: string): Promise; mkdir(path: string): Promise; readdir(path: string): Promise; diff --git a/src/src/plugins/interfaces/IPluginFile.ts b/src/src/plugins/interfaces/IPluginFile.ts index f586d32d8..4531a4759 100644 --- a/src/src/plugins/interfaces/IPluginFile.ts +++ b/src/src/plugins/interfaces/IPluginFile.ts @@ -4,7 +4,7 @@ import { IPluginMetadata } from './IPluginMetadata.js'; * Magic bytes identifying .opnet files * ASCII: "OPNETPLG" */ -export const PLUGIN_MAGIC_BYTES = Buffer.from('OPNETPLG', 'ascii'); +export const PLUGIN_MAGIC_BYTES = new TextEncoder().encode('OPNETPLG'); /** * Current format version @@ -49,10 +49,10 @@ export interface IParsedPluginFile { readonly mldsaLevel: MLDSALevel; /** MLDSA public key */ - readonly publicKey: Buffer; + readonly publicKey: Uint8Array; /** MLDSA signature over checksum */ - readonly signature: Buffer; + readonly signature: Uint8Array; /** Parsed metadata JSON */ readonly metadata: IPluginMetadata; @@ -61,13 +61,13 @@ export interface IParsedPluginFile { readonly rawMetadata: string; /** Compiled bytecode (.jsc) */ - readonly bytecode: Buffer; + readonly bytecode: Uint8Array; /** Optional protobuf schema for WebSocket */ - readonly proto?: Buffer; + readonly proto?: Uint8Array; /** SHA-256 checksum of metadata + bytecode + proto */ - readonly checksum: Buffer; + readonly checksum: Uint8Array; } /** @@ -75,7 +75,7 @@ export interface IParsedPluginFile { */ export interface IPluginFileHeader { /** Magic bytes (8 bytes) */ - readonly magic: Buffer; + readonly magic: Uint8Array; /** Format version (4 bytes, uint32 LE) */ readonly version: number; @@ -84,10 +84,10 @@ export interface IPluginFileHeader { readonly mldsaLevel: MLDSALevel; /** Public key (variable size based on level) */ - readonly publicKey: Buffer; + readonly publicKey: Uint8Array; /** Signature (variable size based on level) */ - readonly signature: Buffer; + readonly signature: Uint8Array; } /** diff --git a/src/src/plugins/loader/PluginLoader.ts b/src/src/plugins/loader/PluginLoader.ts index 7ce6326fa..8ea0aed63 100644 --- a/src/src/plugins/loader/PluginLoader.ts +++ b/src/src/plugins/loader/PluginLoader.ts @@ -1,4 +1,5 @@ import { Logger } from '@btc-vision/bsi-common'; +import { equals, toHex } from '@btc-vision/bitcoin'; import * as fs from 'fs'; import * as path from 'path'; import * as crypto from 'crypto'; @@ -158,9 +159,9 @@ export class PluginLoader extends Logger { this.info(`Parsing plugin file: ${filePath}`); // Read file - let buffer: Buffer; + let buffer: Uint8Array; try { - buffer = fs.readFileSync(filePath); + buffer = new Uint8Array(fs.readFileSync(filePath)); } catch (error) { throw new PluginLoadError( `Failed to read plugin file: ${error}`, @@ -193,7 +194,8 @@ export class PluginLoader extends Logger { filePath, ); } - const metadataLength = buffer.readUInt32LE(offset); + const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); + const metadataLength = view.getUint32(offset, true); offset += 4; if (metadataLength > MAX_METADATA_SIZE) { @@ -212,7 +214,7 @@ export class PluginLoader extends Logger { filePath, ); } - const rawMetadata = buffer.subarray(offset, offset + metadataLength).toString('utf8'); + const rawMetadata = new TextDecoder().decode(buffer.subarray(offset, offset + metadataLength)); offset += metadataLength; let metadata: IPluginMetadata; @@ -234,7 +236,7 @@ export class PluginLoader extends Logger { filePath, ); } - const bytecodeLength = buffer.readUInt32LE(offset); + const bytecodeLength = view.getUint32(offset, true); offset += 4; if (bytecodeLength > MAX_BYTECODE_SIZE) { @@ -264,10 +266,10 @@ export class PluginLoader extends Logger { filePath, ); } - const protoLength = buffer.readUInt32LE(offset); + const protoLength = view.getUint32(offset, true); offset += 4; - let proto: Buffer | undefined; + let proto: Uint8Array | undefined; if (protoLength > 0) { if (protoLength > MAX_PROTO_SIZE) { throw new PluginLoadError( @@ -300,9 +302,9 @@ export class PluginLoader extends Logger { // Verify checksum const computedChecksum = this.computeChecksum(rawMetadata, bytecode, proto); - if (!checksum.equals(computedChecksum)) { + if (!equals(checksum, computedChecksum)) { throw new PluginLoadError( - `Checksum mismatch: expected ${checksum.toString('hex')}, got ${computedChecksum.toString('hex')}`, + `Checksum mismatch: expected ${toHex(checksum)}, got ${toHex(computedChecksum)}`, 'CHECKSUM_MISMATCH', filePath, ); @@ -344,7 +346,7 @@ export class PluginLoader extends Logger { /** * Parse the file header */ - private parseHeader(buffer: Buffer, filePath: string): IPluginFileHeader { + private parseHeader(buffer: Uint8Array, filePath: string): IPluginFileHeader { let offset = 0; // Minimum header size check (magic + version + mldsa level = 13 bytes) @@ -360,16 +362,17 @@ export class PluginLoader extends Logger { const magic = buffer.subarray(offset, offset + 8); offset += 8; - if (!magic.equals(PLUGIN_MAGIC_BYTES)) { + if (!equals(magic, PLUGIN_MAGIC_BYTES)) { throw new PluginLoadError( - `Invalid magic bytes: expected ${PLUGIN_MAGIC_BYTES.toString('hex')}, got ${magic.toString('hex')}`, + `Invalid magic bytes: expected ${toHex(PLUGIN_MAGIC_BYTES)}, got ${toHex(magic)}`, 'INVALID_MAGIC', filePath, ); } // Version - const version = buffer.readUInt32LE(offset); + const headerView = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); + const version = headerView.getUint32(offset, true); offset += 4; if (version !== PLUGIN_FORMAT_VERSION) { @@ -381,7 +384,7 @@ export class PluginLoader extends Logger { } // MLDSA level - const mldsaLevel = buffer.readUInt8(offset) as MLDSALevel; + const mldsaLevel = buffer[offset] as MLDSALevel; offset += 1; if (!(mldsaLevel in MLDSALevel)) { @@ -427,13 +430,13 @@ export class PluginLoader extends Logger { /** * Compute SHA-256 checksum of metadata + bytecode + proto */ - private computeChecksum(rawMetadata: string, bytecode: Buffer, proto?: Buffer): Buffer { + private computeChecksum(rawMetadata: string, bytecode: Uint8Array, proto?: Uint8Array): Uint8Array { const hash = crypto.createHash('sha256'); hash.update(rawMetadata); hash.update(bytecode); if (proto) { hash.update(proto); } - return hash.digest(); + return new Uint8Array(hash.digest()); } } diff --git a/src/src/plugins/validator/PluginValidator.ts b/src/src/plugins/validator/PluginValidator.ts index 9d3df8c4f..740ea438f 100644 --- a/src/src/plugins/validator/PluginValidator.ts +++ b/src/src/plugins/validator/PluginValidator.ts @@ -1,6 +1,6 @@ import { Logger } from '@btc-vision/bsi-common'; import { MessageSigner, MLDSASecurityLevel, QuantumBIP32Factory } from '@btc-vision/transaction'; -import { Network } from '@btc-vision/bitcoin'; +import { alloc, Network } from '@btc-vision/bitcoin'; import * as semver from 'semver'; import { IParsedPluginFile, MLDSALevel } from '../interfaces/IPluginFile.js'; @@ -346,7 +346,7 @@ export class PluginValidator extends Logger { try { const keyPair = QuantumBIP32Factory.fromPublicKey( plugin.publicKey, - Buffer.alloc(32), // Chain code can be zeroed for verification + alloc(32), // Chain code can be zeroed for verification this.network, securityLevel, ); diff --git a/src/src/plugins/workers/WorkerMessages.ts b/src/src/plugins/workers/WorkerMessages.ts index c1b774ae2..39e0a936e 100644 --- a/src/src/plugins/workers/WorkerMessages.ts +++ b/src/src/plugins/workers/WorkerMessages.ts @@ -112,7 +112,7 @@ export interface ISerializedPluginInstallState { export interface ILoadPluginMessage extends IWorkerMessage { readonly type: WorkerMessageType.LOAD_PLUGIN; readonly pluginId: string; - readonly bytecode: Buffer; + readonly bytecode: Uint8Array; readonly metadata: string; // JSON serialized readonly dataDir: string; readonly config: string; // JSON serialized diff --git a/src/src/poc/configurations/P2PConfigurations.ts b/src/src/poc/configurations/P2PConfigurations.ts index a5907d191..1f4fa705b 100644 --- a/src/src/poc/configurations/P2PConfigurations.ts +++ b/src/src/poc/configurations/P2PConfigurations.ts @@ -17,6 +17,7 @@ import { PeerToPeerMethod } from '../../config/interfaces/PeerToPeerMethod.js'; import { OPNetPathFinder } from '../identity/OPNetPathFinder.js'; import { BootstrapNodes } from './BootstrapNodes.js'; import { P2PMajorVersion, P2PVersion } from './P2PVersion.js'; +import { fromBase64, toBase64 } from '@btc-vision/bitcoin'; import { generateKeyPair, privateKeyFromRaw } from '@libp2p/crypto/keys'; import { Config } from '../../config/Config.js'; import { Multiaddr, multiaddr } from '@multiformats/multiaddr'; @@ -24,7 +25,7 @@ import { AutoNATv2ServiceInit } from '@libp2p/autonat-v2'; interface BackedUpPeer { id: string; - privKey?: Buffer; + privKey?: Uint8Array; pubKey?: string; } @@ -320,7 +321,7 @@ export class P2PConfigurations extends OPNetPathFinder { const peerIdentity: { id: string; - privKey: string | Buffer; + privKey: string; pubKey: string; } = { id: peer.toString(), @@ -403,11 +404,15 @@ export class P2PConfigurations extends OPNetPathFinder { const decrypted = this.decryptToString(new Uint8Array(lastPeerIdentity)); const decoded = JSON.parse(decrypted) as { id: string; - privKey: string | Buffer; + privKey: string; pubKey: string; }; - decoded.privKey = Buffer.from(decoded.privKey as string, 'base64'); - return decoded as BackedUpPeer; + const peer: BackedUpPeer = { + id: decoded.id, + privKey: fromBase64(decoded.privKey), + pubKey: decoded.pubKey, + }; + return peer; } catch (e) { const error = e as Error; if (error.message.includes('no such file or directory')) { @@ -429,6 +434,6 @@ export class P2PConfigurations extends OPNetPathFinder { } private uint8ArrayToString(uint8Array: Uint8Array): string { - return Buffer.from(uint8Array).toString('base64'); + return toBase64(uint8Array); } } diff --git a/src/src/poc/configurations/consensus/RoswellConsensus.ts b/src/src/poc/configurations/consensus/RoswellConsensus.ts index 4f400001d..addee91ab 100644 --- a/src/src/poc/configurations/consensus/RoswellConsensus.ts +++ b/src/src/poc/configurations/consensus/RoswellConsensus.ts @@ -6,6 +6,7 @@ import { SPECIAL_CONTRACTS_ROSWELL_REGTEST, SPECIAL_CONTRACTS_ROSWELL_TESTNET, } from './roswell/SpecialContractsRoswell.js'; +import { fromHex } from '@btc-vision/bitcoin'; import { ChainIds } from '../../../config/enums/ChainIds.js'; import { Address, MLDSASecurityLevel } from '@btc-vision/transaction'; import { ConsensusRules } from '../../../vm/consensus/ConsensusRules.js'; @@ -36,11 +37,8 @@ export const RoswellConsensus: IOPNetConsensus = { }, }, - PROTOCOL_ID: Uint8Array.from( - Buffer.from( - 'e784995a412d773988c4b8e333d7b39dfb3cabf118d0d645411a916ca2407939', // sha256("OP_NET") - 'hex', - ), + PROTOCOL_ID: fromHex( + 'e784995a412d773988c4b8e333d7b39dfb3cabf118d0d645411a916ca2407939', // sha256("OP_NET") ), EPOCH: { diff --git a/src/src/poc/configurations/manager/TrustedAuthority.ts b/src/src/poc/configurations/manager/TrustedAuthority.ts index 5382151cf..ff60d6068 100644 --- a/src/src/poc/configurations/manager/TrustedAuthority.ts +++ b/src/src/poc/configurations/manager/TrustedAuthority.ts @@ -1,3 +1,4 @@ +import { fromBase64, fromHex, toBase64 } from '@btc-vision/bitcoin'; import { Logger } from '@btc-vision/bsi-common'; import { AuthorityBufferKey, @@ -62,8 +63,8 @@ export class TrustedAuthority extends Logger { } public verifyTrustedSignature( - data: Buffer, - signature: Buffer, + data: Uint8Array, + signature: Uint8Array, ): { validity: boolean; identity: string } { for (const trustedPublicKeyCompany in this.trustedKeys) { const trustedPublicKeys = this.trustedKeys[trustedPublicKeyCompany as TrustedEntities]; @@ -154,12 +155,12 @@ export class TrustedAuthority extends Logger { }) .map((key: AuthorityKey): AuthorityBufferKey => { return { - publicKey: Buffer.from(key.publicKey, 'base64'), - opnet: Buffer.from(key.opnet, 'base64'), - signature: Buffer.from(key.signature, 'base64'), + publicKey: fromBase64(key.publicKey), + opnet: fromBase64(key.opnet), + signature: fromBase64(key.signature), wallet: new Address( - Buffer.from(key.mldsaPublicKey.replace('0x', ''), 'hex'), - Buffer.from(key.walletPubKey.replace('0x', ''), 'hex'), + fromHex(key.mldsaPublicKey.replace('0x', '')), + fromHex(key.walletPubKey.replace('0x', '')), ), }; }) @@ -193,7 +194,7 @@ export class TrustedAuthority extends Logger { if (allKeys.includes(keyHash)) { throw new Error( - `Duplicate key found for ${trustedCompany} -> ${key.opnet.toString('base64')}`, + `Duplicate key found for ${trustedCompany} -> ${toBase64(key.opnet)}`, ); } diff --git a/src/src/poc/configurations/types/TrustedPublicKeys.ts b/src/src/poc/configurations/types/TrustedPublicKeys.ts index dee8d077a..2d8aefdc7 100644 --- a/src/src/poc/configurations/types/TrustedPublicKeys.ts +++ b/src/src/poc/configurations/types/TrustedPublicKeys.ts @@ -21,9 +21,9 @@ export interface AuthorityKey { } export interface AuthorityBufferKey { - readonly opnet: Buffer; - readonly publicKey: Buffer; - readonly signature: Buffer; + readonly opnet: Uint8Array; + readonly publicKey: Uint8Array; + readonly signature: Uint8Array; readonly wallet: Address; } diff --git a/src/src/poc/epoch/EpochValidator.ts b/src/src/poc/epoch/EpochValidator.ts index a6e044f80..e4c62f5f5 100644 --- a/src/src/poc/epoch/EpochValidator.ts +++ b/src/src/poc/epoch/EpochValidator.ts @@ -1,4 +1,5 @@ import { DataConverter, Logger } from '@btc-vision/bsi-common'; +import { equals, toHex } from '@btc-vision/bitcoin'; import { Binary } from 'mongodb'; import crypto from 'crypto'; import { ITargetEpochDocument, PendingTargetEpoch, } from '../../db/documents/interfaces/ITargetEpochDocument.js'; @@ -9,19 +10,19 @@ import { stringToBuffer } from '../../utils/StringToBuffer.js'; export interface EpochValidationParams { readonly epochNumber: bigint; - readonly checksumRoot: Buffer; - readonly salt: Buffer; - readonly mldsaPublicKey: Buffer; - readonly graffiti?: Buffer; - readonly signature: Buffer; + readonly checksumRoot: Uint8Array; + readonly salt: Uint8Array; + readonly mldsaPublicKey: Uint8Array; + readonly graffiti?: Uint8Array; + readonly signature: Uint8Array; } export interface EpochValidationResult { readonly valid: boolean; readonly matchingBits: number; - readonly hash: Buffer; - readonly targetPattern: Buffer; - readonly challenge: Buffer; + readonly hash: Uint8Array; + readonly targetPattern: Uint8Array; + readonly challenge: Uint8Array; readonly message?: string; } @@ -56,19 +57,19 @@ export class EpochValidator extends Logger { * Calculate mining preimage using XOR operations */ public static calculatePreimage( - checksumRoot: Buffer, - mldsaPublicKey: Buffer, - salt: Buffer, - ): Buffer { - const target32 = Buffer.alloc(32); - const pubKey32 = Buffer.alloc(32); - const salt32 = Buffer.alloc(32); - - checksumRoot.copy(target32, 0, 0, Math.min(32, checksumRoot.length)); - mldsaPublicKey.copy(pubKey32, 0, 0, Math.min(32, mldsaPublicKey.length)); - salt.copy(salt32, 0, 0, Math.min(32, salt.length)); - - const preimage = Buffer.alloc(32); + checksumRoot: Uint8Array, + mldsaPublicKey: Uint8Array, + salt: Uint8Array, + ): Uint8Array { + const target32 = new Uint8Array(32); + const pubKey32 = new Uint8Array(32); + const salt32 = new Uint8Array(32); + + target32.set(checksumRoot.subarray(0, Math.min(32, checksumRoot.length))); + pubKey32.set(mldsaPublicKey.subarray(0, Math.min(32, mldsaPublicKey.length))); + salt32.set(salt.subarray(0, Math.min(32, salt.length))); + + const preimage = new Uint8Array(32); for (let i = 0; i < 32; i++) { preimage[i] = target32[i] ^ pubKey32[i] ^ salt32[i]; } @@ -90,9 +91,9 @@ export class EpochValidator extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), - targetPattern: Buffer.alloc(0), - challenge: Buffer.alloc(0), + hash: new Uint8Array(0), + targetPattern: new Uint8Array(0), + challenge: new Uint8Array(0), message: 'Epoch 0 cannot be mined', }; } @@ -106,9 +107,9 @@ export class EpochValidator extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), - targetPattern: Buffer.alloc(0), - challenge: Buffer.alloc(0), + hash: new Uint8Array(0), + targetPattern: new Uint8Array(0), + challenge: new Uint8Array(0), message: `Cannot submit for epoch ${params.epochNumber} at block ${currentHeight}. Can only submit for epoch ${currentEpoch}`, }; } @@ -120,9 +121,9 @@ export class EpochValidator extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), - targetPattern: Buffer.alloc(0), - challenge: Buffer.alloc(0), + hash: new Uint8Array(0), + targetPattern: new Uint8Array(0), + challenge: new Uint8Array(0), message: 'Epoch not found', }; } @@ -132,10 +133,10 @@ export class EpochValidator extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), - targetPattern: Buffer.alloc(0), - challenge: Buffer.alloc(0), - message: `Checksum root does not match epoch. Expected: ${epoch.checksumRoot.toString('hex')}, got: ${params.checksumRoot.toString('hex')}`, + hash: new Uint8Array(0), + targetPattern: new Uint8Array(0), + challenge: new Uint8Array(0), + message: `Checksum root does not match epoch. Expected: ${toHex(epoch.checksumRoot)}, got: ${toHex(params.checksumRoot)}`, }; } @@ -180,9 +181,9 @@ export class EpochValidator extends Logger { return { valid: false, matchingBits: 0, - hash: Buffer.alloc(0), - targetPattern: Buffer.alloc(0), - challenge: Buffer.alloc(0), + hash: new Uint8Array(0), + targetPattern: new Uint8Array(0), + challenge: new Uint8Array(0), message: `Validation error: ${error}`, }; } @@ -193,8 +194,8 @@ export class EpochValidator extends Logger { */ public async solutionExists( epochNumber: bigint, - salt: Buffer, - mldsaPublicKey: Buffer | Binary, + salt: Uint8Array, + mldsaPublicKey: Uint8Array | Binary, ): Promise { return await this.storage.targetEpochExists(epochNumber, salt, mldsaPublicKey); } @@ -212,7 +213,7 @@ export class EpochValidator extends Logger { public async saveEpochSolution( params: EpochValidationParams, validationResult: EpochValidationResult, - legacyPublicKey: Buffer, + legacyPublicKey: Uint8Array, ): Promise { const targetEpoch: ITargetEpochDocument = { epochNumber: DataConverter.toDecimal128(params.epochNumber), @@ -232,7 +233,7 @@ export class EpochValidator extends Logger { return targetEpoch; } - public countMatchingBits(hash1: Buffer, hash2: Buffer): number { + public countMatchingBits(hash1: Uint8Array, hash2: Uint8Array): number { let matchingBits = 0; const minLength = Math.min(hash1.length, hash2.length); @@ -262,7 +263,7 @@ export class EpochValidator extends Logger { * Calculate submission hash for unique identification */ public calculateSubmissionHash(params: EpochValidationParams): string { - const data = `${params.epochNumber}:${params.checksumRoot.toString('hex')}:${params.salt.toString('hex')}:${params.mldsaPublicKey.toString('hex')}`; + const data = `${params.epochNumber}:${toHex(params.checksumRoot)}:${toHex(params.salt)}:${toHex(params.mldsaPublicKey)}`; return crypto.createHash('sha256').update(data).digest('hex'); } @@ -296,12 +297,12 @@ export class EpochValidator extends Logger { /** * Verify the target hash matches the epoch */ - private verifyChecksumRoot(epoch: PendingTargetEpoch, targetHash: Buffer): boolean { + private verifyChecksumRoot(epoch: PendingTargetEpoch, targetHash: Uint8Array): boolean { const epochTargetHash = epoch.checksumRoot; - if (!Buffer.isBuffer(epochTargetHash)) { - throw new Error('Epoch checksumRoot is not a Buffer'); + if (!(epochTargetHash instanceof Uint8Array)) { + throw new Error('Epoch checksumRoot is not a Uint8Array'); } - return epochTargetHash.equals(targetHash); + return equals(epochTargetHash, targetHash); } } diff --git a/src/src/poc/identity/OPNetIdentity.ts b/src/src/poc/identity/OPNetIdentity.ts index 132da9570..49627da78 100644 --- a/src/src/poc/identity/OPNetIdentity.ts +++ b/src/src/poc/identity/OPNetIdentity.ts @@ -1,4 +1,4 @@ -import { Network, Signer, toXOnly } from '@btc-vision/bitcoin'; +import { concat, Network, Signer, toBase64, toHex, toXOnly } from '@btc-vision/bitcoin'; import { UniversalSigner } from '@btc-vision/ecpair'; import fs from 'fs'; import path from 'path'; @@ -18,7 +18,7 @@ export class OPNetIdentity extends OPNetPathFinder { private keyPairGenerator: KeyPairGenerator; - private readonly opnetAuthKeyBin: Buffer; + private readonly opnetAuthKeyBin: Uint8Array; private readonly opnetWallet: UniversalSigner; private readonly keyPair: OPNetKeyPair; @@ -26,7 +26,7 @@ export class OPNetIdentity extends OPNetPathFinder { readonly #xPubKey: Uint8Array; - private readonly opnetWalletPubKeyBuffer: Buffer; + private readonly opnetWalletPubKeyBytes: Uint8Array; public constructor( private readonly config: BtcIndexerConfig, @@ -39,7 +39,7 @@ export class OPNetIdentity extends OPNetPathFinder { this.opnetWallet = this.loadOPNetWallet(); this.deriveKey(this.opnetWallet.privateKey); - this.opnetWalletPubKeyBuffer = Buffer.from(this.opnetWallet.publicKey); + this.opnetWalletPubKeyBytes = new Uint8Array(this.opnetWallet.publicKey); this.opnetAuthKeyBin = this.loadOPNetAuthKeys(); this.keyPair = this.restoreKeyPair(this.opnetAuthKeyBin); @@ -72,28 +72,28 @@ export class OPNetIdentity extends OPNetPathFinder { } public get opnetPubKey(): string { - return this.keyPair.trusted.publicKey.toString('base64'); + return toBase64(this.keyPair.trusted.publicKey); } public get pubKeyBase64(): string { - return this.publicKey.toString('base64'); + return toBase64(this.publicKey); } public get xPubKey(): Uint8Array { return this.#xPubKey; } - public get publicKey(): Buffer { - return this.opnetWalletPubKeyBuffer; + public get publicKey(): Uint8Array { + return this.opnetWalletPubKeyBytes; } public get signedTrustedWalletConfirmation(): string { - const signature: Buffer = this.keyPairGenerator.sign( - this.opnetWalletPubKeyBuffer, + const signature: Uint8Array = this.keyPairGenerator.sign( + this.opnetWalletPubKeyBytes, this.keyPair.trusted.privateKey, ); - return signature.toString('base64'); + return toBase64(signature); } public get trustedPublicKey(): string { @@ -109,14 +109,14 @@ export class OPNetIdentity extends OPNetPathFinder { } public get pubKey(): string { - return '0x' + Buffer.from(this.opnetWallet.publicKey).toString('hex'); + return '0x' + toHex(this.opnetWallet.publicKey); } public get opnetAddress(): string { - return '0x' + this.keyPair.identity.hash.toString('hex'); + return '0x' + toHex(this.keyPair.identity.hash); } - public get opnetAddressAsBuffer(): Buffer { + public get opnetAddressAsBuffer(): Uint8Array { return this.keyPair.identity.hash; } @@ -136,24 +136,24 @@ export class OPNetIdentity extends OPNetPathFinder { return this.opnetWallet; } - public hash(data: Buffer): Buffer { + public hash(data: Uint8Array): Uint8Array { return this.keyPairGenerator.hash(data); } - public identityChallenge(salt: Buffer | Uint8Array): Buffer { + public identityChallenge(salt: Uint8Array): Uint8Array { return this.keyPairGenerator.hashChallenge(this.keyPair, salt); } public verifyChallenge( - challenge: Buffer | Uint8Array, - signature: Buffer | Uint8Array, - pubKey: Buffer | Uint8Array, + challenge: Uint8Array, + signature: Uint8Array, + pubKey: Uint8Array, ): boolean { return this.keyPairGenerator.verifyChallenge(challenge, signature, pubKey); } // PoC: Proof of Computational Acknowledgment - public verifyAcknowledgment(data: Buffer, witness: OPNetBlockWitness): boolean { + public verifyAcknowledgment(data: Uint8Array, witness: OPNetBlockWitness): boolean { if (!data) return false; if (!witness.publicKey) return false; if (!witness.identity) return false; @@ -169,7 +169,7 @@ export class OPNetIdentity extends OPNetPathFinder { // PoV: Proof of Validation public verifyTrustedAcknowledgment( - data: Buffer, + data: Uint8Array, witness: OPNetBlockWitness, identity: string | undefined, ): boolean { @@ -184,11 +184,11 @@ export class OPNetIdentity extends OPNetPathFinder { return validWitness.identity === identity; } - public verifyOPNetIdentity(identity: string, pubKey: Buffer): boolean { + public verifyOPNetIdentity(identity: string, pubKey: Uint8Array): boolean { return this.keyPairGenerator.verifyOPNetIdentity(identity, pubKey); } - public acknowledgeData(data: Buffer): OPNetBlockWitness { + public acknowledgeData(data: Uint8Array): OPNetBlockWitness { const now = BigInt(Date.now()); const witnessData = this.mergeDataAndWitness(data, now); @@ -200,15 +200,16 @@ export class OPNetIdentity extends OPNetPathFinder { }; } - public mergeDataAndWitness(blockChecksumHash: Buffer, timestamp: bigint): Buffer { - const data = Buffer.alloc(40); - blockChecksumHash.copy(data, 0, 0, 32); - data.writeBigUint64BE(timestamp, 32); + public mergeDataAndWitness(blockChecksumHash: Uint8Array, timestamp: bigint): Uint8Array { + const data = new Uint8Array(40); + data.set(blockChecksumHash.subarray(0, 32), 0); + const view = new DataView(data.buffer); + view.setBigUint64(32, timestamp, false); return data; } - public acknowledgeTrustedData(data: Buffer): OPNetBlockWitness { + public acknowledgeTrustedData(data: Uint8Array): OPNetBlockWitness { if (!this.opnetWallet.privateKey) throw new Error('Private key not found'); const now = BigInt(Date.now()); @@ -229,10 +230,10 @@ export class OPNetIdentity extends OPNetPathFinder { return path.join(this.getBinPath(), `wallet.bin`); } - private loadOPNetAuthKeys(): Buffer { + private loadOPNetAuthKeys(): Uint8Array { try { const lastKeys = fs.readFileSync(this.getOPNetAuthKeysPath()); - return Buffer.from(this.decrypt(new Uint8Array(lastKeys))); + return this.decrypt(new Uint8Array(lastKeys)); } catch (e) { const error = e as Error; if (error.message.includes('no such file or directory')) { @@ -275,8 +276,8 @@ export class OPNetIdentity extends OPNetPathFinder { } } - private generateNewOPNetIdentity(): Buffer { - const key: Buffer = this.generateDefaultOPNetAuthKeys(); + private generateNewOPNetIdentity(): Uint8Array { + const key: Uint8Array = this.generateDefaultOPNetAuthKeys(); if (fs.existsSync(this.getOPNetAuthKeysPath())) { throw new Error( @@ -289,7 +290,7 @@ export class OPNetIdentity extends OPNetPathFinder { return key; } - private restoreKeyPair(buf: Buffer): OPNetKeyPair { + private restoreKeyPair(buf: Uint8Array): OPNetKeyPair { const privateKey = buf.subarray(0, 64); const publicKey = buf.subarray(64, 96); @@ -299,23 +300,23 @@ export class OPNetIdentity extends OPNetPathFinder { const trustedPrivateKey = buf.subarray(256); return { - privateKey: Buffer.from(privateKey), - publicKey: Buffer.from(publicKey), + privateKey: new Uint8Array(privateKey), + publicKey: new Uint8Array(publicKey), identity: { - hash: Buffer.from(identity.subarray(0, 64)), - proof: Buffer.from(identity.subarray(64)), + hash: new Uint8Array(identity.subarray(0, 64)), + proof: new Uint8Array(identity.subarray(64)), }, trusted: { - privateKey: trustedPrivateKey, - publicKey: trustedPublicKey, + privateKey: new Uint8Array(trustedPrivateKey), + publicKey: new Uint8Array(trustedPublicKey), }, }; } - private generateDefaultOPNetAuthKeys(): Buffer { + private generateDefaultOPNetAuthKeys(): Uint8Array { const keyPair = this.keyPairGenerator.generateKey(); - return Buffer.concat([ + return concat([ keyPair.privateKey, // 64 bytes keyPair.publicKey, // 32 bytes keyPair.identity.hash, // 64 bytes diff --git a/src/src/poc/identity/OPNetPathFinder.ts b/src/src/poc/identity/OPNetPathFinder.ts index 508e4e50a..8b2c32221 100644 --- a/src/src/poc/identity/OPNetPathFinder.ts +++ b/src/src/poc/identity/OPNetPathFinder.ts @@ -1,3 +1,4 @@ +import { fromBase64, toBase64, fromUtf8, toUtf8 } from '@btc-vision/bitcoin'; import fs from 'fs'; import path from 'path'; import { Config } from '../../config/Config.js'; @@ -25,12 +26,12 @@ export class OPNetPathFinder { } protected encrypt(dataJson: string): Uint8Array { - const data: string = Buffer.from(dataJson).toString('base64'); + const data: string = toBase64(fromUtf8(dataJson)); - return this.encryptRaw(Buffer.from(data, 'utf8')); + return this.encryptRaw(fromUtf8(data)); } - protected encryptRaw(data: Uint8Array | Buffer): Uint8Array { + protected encryptRaw(data: Uint8Array): Uint8Array { const encrypted: Uint8Array = new Uint8Array(data.length); for (let i = 0; i < data.length; i++) { encrypted[i] = data[i] ^ this.encKey[i % this.encKey.byteLength]; @@ -39,7 +40,7 @@ export class OPNetPathFinder { return encrypted; } - protected decrypt(encrypted: Uint8Array | Buffer): Uint8Array { + protected decrypt(encrypted: Uint8Array): Uint8Array { const data: Uint8Array = new Uint8Array(encrypted.byteLength); for (let i = 0; i < encrypted.byteLength; i++) { data[i] = encrypted[i] ^ this.encKey[i % this.encKey.byteLength]; @@ -52,7 +53,7 @@ export class OPNetPathFinder { const decrypted = this.decrypt(encrypted); const decoded = this.textDecoder.decode(decrypted); - return Buffer.from(decoded, 'base64').toString('utf8'); + return toUtf8(fromBase64(decoded)); } protected deriveKey(derivateKey?: Uint8Array): void { diff --git a/src/src/poc/mempool/bitcoin-mempool/MempoolManager.ts b/src/src/poc/mempool/bitcoin-mempool/MempoolManager.ts index cce09c7ee..d6e19cc4a 100644 --- a/src/src/poc/mempool/bitcoin-mempool/MempoolManager.ts +++ b/src/src/poc/mempool/bitcoin-mempool/MempoolManager.ts @@ -21,7 +21,7 @@ import { LargeJSONProcessor } from '../../../utils/LargeJSONProcessor.js'; import { RPCMessageData } from '../../../threading/interfaces/thread-messages/messages/api/RPCMessage.js'; import { BitcoinRPCThreadMessageType } from '../../../blockchain-indexer/rpc/thread/messages/BitcoinRPCThreadMessage.js'; import { TransactionVerifierManager } from '../transaction/TransactionVerifierManager.js'; -import { Network } from '@btc-vision/bitcoin'; +import { fromHex, Network } from '@btc-vision/bitcoin'; import { NetworkConverter } from '../../../config/network/NetworkConverter.js'; import { getMongodbMajorVersion } from '../../../vm/storage/databases/MongoUtils.js'; @@ -212,7 +212,7 @@ export class MempoolManager extends Logger { tx: TransactionDetail; txid: string; }): Promise { - const data = Buffer.from(txData.tx.hex, 'hex'); + const data = fromHex(txData.tx.hex); const resp: IMempoolTransactionObj = { id: txData.txid, psbt: false, diff --git a/src/src/poc/mempool/manager/Mempool.ts b/src/src/poc/mempool/manager/Mempool.ts index 42988413e..78d73f147 100644 --- a/src/src/poc/mempool/manager/Mempool.ts +++ b/src/src/poc/mempool/manager/Mempool.ts @@ -18,7 +18,7 @@ import { BitcoinRPC, FeeEstimation, SmartFeeEstimation } from '@btc-vision/bitco import { Config } from '../../../config/Config.js'; import { MempoolRepository } from '../../../db/repositories/MempoolRepository.js'; import { NetworkConverter } from '../../../config/network/NetworkConverter.js'; -import { Network } from '@btc-vision/bitcoin'; +import { concat, Network, toBase64, toHex } from '@btc-vision/bitcoin'; import { IMempoolTransactionObj } from '../../../db/interfaces/IMempoolTransaction.js'; import { OPNetConsensus } from '../../configurations/OPNetConsensus.js'; import { BlockchainInfoRepository } from '../../../db/repositories/BlockchainInfoRepository.js'; @@ -351,7 +351,7 @@ export class Mempool extends Logger { theoreticalGasLimit: 0n, isOPNet: false, priorityFee: 0n, - data: Buffer.from(raw), + data: raw, firstSeen: new Date(), blockHeight: OPNetConsensus.getBlockHeight(), inputs: [], @@ -390,14 +390,13 @@ export class Mempool extends Logger { }; } - const buf = Buffer.from(transaction.data); - const rawHex: string = buf.toString('hex'); + const rawHex: string = toHex(transaction.data); const broadcast = await this.broadcastBitcoinTransaction(rawHex); if (broadcast && broadcast.result) { transaction.id = broadcast.result; - parseAndStoreInputOutputs(buf, transaction); + parseAndStoreInputOutputs(transaction.data, transaction); const stored = await this.mempoolRepository.storeTransaction(transaction); if (!stored) { @@ -530,7 +529,7 @@ export class Mempool extends Logger { const txBuffer = finalized.toBuffer(); const finalTransaction: IMempoolTransactionObj = { - id: finalized.getHash(false).toString('hex'), + id: toHex(finalized.getHash(false)), previousPsbtId: transaction.previousPsbtId || decodedPsbt.data.hash || transaction.id, @@ -570,7 +569,7 @@ export class Mempool extends Logger { return { ...broadcastResult, id: finalTransaction.id, - modifiedTransaction: Buffer.from(finalTransaction.data).toString('base64'), + modifiedTransaction: toBase64(finalTransaction.data), finalizedTransaction: true, }; } else { @@ -583,11 +582,11 @@ export class Mempool extends Logger { } } else if (processed.modified) { const buffer = processed.psbt.toBuffer(); - const header = Buffer.from([decodedPsbt.type, decodedPsbt.version]); + const header = new Uint8Array([decodedPsbt.type, decodedPsbt.version]); const modifiedTransaction = processed.finalized ? buffer - : Buffer.concat([header, buffer]); + : concat([header, buffer]); const newTransaction: IMempoolTransactionObj = { data: modifiedTransaction, @@ -605,7 +604,7 @@ export class Mempool extends Logger { success: true, result: 'PSBT decoded successfully', id: newTransaction.id, - modifiedTransaction: modifiedTransaction.toString('base64'), + modifiedTransaction: toBase64(modifiedTransaction), finalizedTransaction: processed.finalized ?? false, }; } else { diff --git a/src/src/poc/mempool/transaction/TransactionVerifierManager.ts b/src/src/poc/mempool/transaction/TransactionVerifierManager.ts index 71597e430..0d5916aa6 100644 --- a/src/src/poc/mempool/transaction/TransactionVerifierManager.ts +++ b/src/src/poc/mempool/transaction/TransactionVerifierManager.ts @@ -1,5 +1,5 @@ import { TransactionTypes } from './TransactionTypes.js'; -import { Network, networks, Psbt, Transaction as BitcoinTransaction } from '@btc-vision/bitcoin'; +import { Network, networks, Psbt, toBase64, Transaction as BitcoinTransaction } from '@btc-vision/bitcoin'; import { ConfigurableDBManager, Logger } from '@btc-vision/bsi-common'; import { TransactionVerifier } from '../verificator/TransactionVerifier.js'; import { Consensus } from '../../configurations/consensus/Consensus.js'; @@ -85,16 +85,16 @@ export class TransactionVerifierManager extends Logger { } } - private getPSBT(data: Buffer): Psbt | undefined { + private getPSBT(data: Uint8Array): Psbt | undefined { try { - return Psbt.fromBase64(data.toString('base64'), { network: this.network }); + return Psbt.fromBase64(toBase64(data), { network: this.network }); } catch (e) { console.log(e); this.warn(`Failed to decode PSBT. Invalid transaction data.`); } } - private getTransaction(data: Buffer): BitcoinTransaction | undefined { + private getTransaction(data: Uint8Array): BitcoinTransaction | undefined { try { return BitcoinTransaction.fromBuffer(data); } catch (e) { diff --git a/src/src/poc/mempool/verificator/TransactionVerifier.ts b/src/src/poc/mempool/verificator/TransactionVerifier.ts index 759b989c5..4c4c21374 100644 --- a/src/src/poc/mempool/verificator/TransactionVerifier.ts +++ b/src/src/poc/mempool/verificator/TransactionVerifier.ts @@ -112,8 +112,8 @@ export abstract class TransactionVerifier< if (input.partialSig) { input.partialSig = input.partialSig.map(() => { return { - pubkey: Buffer.alloc(33), - signature: Buffer.alloc(65), + pubkey: new Uint8Array(33), + signature: new Uint8Array(65), }; }); } else if (input.finalScriptWitness) { @@ -122,7 +122,7 @@ export abstract class TransactionVerifier< ); const decoded = [ - Buffer.alloc(decodedData.length - 2), + new Uint8Array(decodedData.length - 2), decodedData[decodedData.length - 1], decodedData[decodedData.length - 2], ]; diff --git a/src/src/poc/mempool/verificator/bitcoin/v2/BitcoinTransactionVerificatorV2.ts b/src/src/poc/mempool/verificator/bitcoin/v2/BitcoinTransactionVerificatorV2.ts index 85a12fc13..e1ecc380f 100644 --- a/src/src/poc/mempool/verificator/bitcoin/v2/BitcoinTransactionVerificatorV2.ts +++ b/src/src/poc/mempool/verificator/bitcoin/v2/BitcoinTransactionVerificatorV2.ts @@ -16,7 +16,7 @@ import { ChallengeSolution } from '../../../../../blockchain-indexer/processor/i import { AddressMap } from '@btc-vision/transaction'; import { EpochRepository } from '../../../../../db/repositories/EpochRepository.js'; -const EMPTY_BLOCK_HASH = Buffer.alloc(32).toString('hex'); +const EMPTY_BLOCK_HASH = toHex(new Uint8Array(32)); export class BitcoinTransactionVerificatorV2 extends TransactionVerifier { public readonly type: TransactionTypes[] = [ @@ -111,7 +111,7 @@ export class BitcoinTransactionVerificatorV2 extends TransactionVerifier { try { - const txRegenerated = Transaction.fromBuffer(Buffer.from(tx.transaction)); + const txRegenerated = Transaction.fromBuffer(tx.transaction); const txHash = txRegenerated.getId(); /** Already broadcasted. */ @@ -864,7 +864,7 @@ export class P2PManager extends Logger { } const modifiedTransaction: Uint8Array = verifiedTransaction.modifiedTransaction - ? Buffer.from(verifiedTransaction.modifiedTransaction, 'base64') + ? fromBase64(verifiedTransaction.modifiedTransaction) : tx.transaction; const isPsbt: boolean = tx.psbt ? !verifiedTransaction.finalizedTransaction : false; diff --git a/src/src/poc/networking/client/managers/ClientAuthenticationManager.ts b/src/src/poc/networking/client/managers/ClientAuthenticationManager.ts index f558857d7..e0d65eee7 100644 --- a/src/src/poc/networking/client/managers/ClientAuthenticationManager.ts +++ b/src/src/poc/networking/client/managers/ClientAuthenticationManager.ts @@ -117,7 +117,7 @@ export abstract class ClientAuthenticationManager extends SharedAuthenticationMa return; } - const challengeResponse: Buffer = this.selfIdentity.identityChallenge(challenge); + const challengeResponse: Uint8Array = this.selfIdentity.identityChallenge(challenge); const keyCipherExchangeData: IClientKeyCipherExchangePacket = { clientKeyCipher: this.#OPNetClientKeyCipher, clientAuthCipher: this.#OPNetAuthKey, @@ -237,12 +237,8 @@ export abstract class ClientAuthenticationManager extends SharedAuthenticationMa } return { - publicKey: new Uint8Array( - Buffer.from(publicKey.buffer, publicKey.byteOffset, publicKey.byteLength), - ), - authKey: new Uint8Array( - Buffer.from(authKey.buffer, authKey.byteOffset, authKey.byteLength), - ), + publicKey: new Uint8Array(publicKey), + authKey: new Uint8Array(authKey), }; } @@ -309,8 +305,8 @@ export abstract class ClientAuthenticationManager extends SharedAuthenticationMa } private setCipherKeys(serverKeyCipher: Uint8Array, serverSigningCipher: Uint8Array): void { - this.encryptemClient.setServerPublicKey(Buffer.from(serverKeyCipher)); - this.encryptemClient.setServerSignaturePublicKey(Buffer.from(serverSigningCipher)); + this.encryptemClient.setServerPublicKey(serverKeyCipher); + this.encryptemClient.setServerSignaturePublicKey(serverSigningCipher); } private async handleAuthenticationStatusPacket(packet: OPNetPacket): Promise { diff --git a/src/src/poc/networking/default/AbstractPacketManager.ts b/src/src/poc/networking/default/AbstractPacketManager.ts index dbb782cbf..1ba5ba9c0 100644 --- a/src/src/poc/networking/default/AbstractPacketManager.ts +++ b/src/src/poc/networking/default/AbstractPacketManager.ts @@ -34,7 +34,7 @@ export abstract class AbstractPacketManager extends Logger { this.eventHandlers.get(event)?.push(eventHandler as NetworkingEventHandler); } - protected async sendMsg(data: Uint8Array | Buffer): Promise { + protected async sendMsg(data: Uint8Array): Promise { await this.emit(CommonHandlers.SEND, data); } diff --git a/src/src/poc/networking/encryptem/EncryptemClient.ts b/src/src/poc/networking/encryptem/EncryptemClient.ts index 28284693e..9d907d579 100644 --- a/src/src/poc/networking/encryptem/EncryptemClient.ts +++ b/src/src/poc/networking/encryptem/EncryptemClient.ts @@ -21,43 +21,43 @@ export class EncryptemClient extends Logger { super(); } - public setClientSecretKey(key: Buffer): void { - this.#clientSecretKey = key; + public setClientSecretKey(key: Uint8Array): void { + this.#clientSecretKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public setClientPublicKey(key: Buffer): void { - this.#clientPublicKey = key; + public setClientPublicKey(key: Uint8Array): void { + this.#clientPublicKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public setClientSignaturePublicKey(key: Buffer): void { - this.#clientSignaturePublicKey = key; + public setClientSignaturePublicKey(key: Uint8Array): void { + this.#clientSignaturePublicKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public setClientSignaturePrivateKey(key: Buffer): void { - this.#clientSignaturePrivateKey = key; + public setClientSignaturePrivateKey(key: Uint8Array): void { + this.#clientSignaturePrivateKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public setServerSignaturePublicKey(key: Buffer): void { - this.#serverSignaturePublicKey = key; + public setServerSignaturePublicKey(key: Uint8Array): void { + this.#serverSignaturePublicKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public setServerPublicKey(key: Buffer): void { - this.#serverPublicKey = key; + public setServerPublicKey(key: Uint8Array): void { + this.#serverPublicKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public getClientPublicKey(): Buffer | null { + public getClientPublicKey(): Uint8Array | null { return this.#clientPublicKey; } - public getClientSignaturePublicKey(): Buffer | null { + public getClientSignaturePublicKey(): Uint8Array | null { return this.#clientSignaturePublicKey; } - public getServerPublicKey(): Buffer | null { + public getServerPublicKey(): Uint8Array | null { return this.#serverPublicKey; } - public getServerSignaturePublicKey(): Buffer | null { + public getServerSignaturePublicKey(): Uint8Array | null { return this.#serverSignaturePublicKey; } @@ -93,7 +93,7 @@ export class EncryptemClient extends Logger { throw new Error('One of the client key is null.'); } return this.#encrypt( - Buffer.from(msg), + Buffer.from(msg.buffer, msg.byteOffset, msg.byteLength), this.#serverPublicKey, this.#clientSecretKey, this.#clientSignaturePublicKey, @@ -105,9 +105,12 @@ export class EncryptemClient extends Logger { if (!(this.#serverPublicKey && this.#clientSecretKey && this.#serverSignaturePublicKey)) { throw new Error('One of the client key is null.'); } - const auth = Buffer.from(msg.slice(0, this.sodium.crypto_auth_BYTES)); - const signature = Buffer.from(msg.slice(auth.length, auth.length + 64)); - const data = Buffer.from(msg.slice(auth.length + 64, msg.length)); + const authSlice = msg.slice(0, this.sodium.crypto_auth_BYTES); + const auth = Buffer.from(authSlice.buffer, authSlice.byteOffset, authSlice.byteLength); + const sigSlice = msg.slice(auth.length, auth.length + 64); + const signature = Buffer.from(sigSlice.buffer, sigSlice.byteOffset, sigSlice.byteLength); + const dataSlice = msg.slice(auth.length + 64, msg.length); + const data = Buffer.from(dataSlice.buffer, dataSlice.byteOffset, dataSlice.byteLength); return this.#decrypt( data, @@ -130,15 +133,17 @@ export class EncryptemClient extends Logger { this.#serverSignaturePublicKey = null; } - public verifyAuth(out: Buffer, input: Buffer): boolean { + public verifyAuth(out: Uint8Array, input: Uint8Array): boolean { if (!this.#serverSignaturePublicKey) { throw new Error('Client signature public key is null.'); } + const outBuf = Buffer.from(out.buffer, out.byteOffset, out.byteLength); + const inputBuf = Buffer.from(input.buffer, input.byteOffset, input.byteLength); const k = this.sodium.sodium_malloc(this.sodium.crypto_auth_KEYBYTES); this.sodium.randombytes_buf_deterministic(k, this.#serverSignaturePublicKey); - return this.sodium.crypto_auth_verify(out, input, k); + return this.sodium.crypto_auth_verify(outBuf, inputBuf, k); } private generateNewCipherKey(): { @@ -271,8 +276,8 @@ export class EncryptemClient extends Logger { const publicKey = authKey.slice(64, 96); return { - publicKey: Buffer.from(publicKey.buffer), - privateKey: Buffer.from(privateKey.buffer), + publicKey: Buffer.from(publicKey.buffer, publicKey.byteOffset, publicKey.byteLength), + privateKey: Buffer.from(privateKey.buffer, privateKey.byteOffset, privateKey.byteLength), }; } } diff --git a/src/src/poc/networking/encryptem/EncryptemServer.ts b/src/src/poc/networking/encryptem/EncryptemServer.ts index 97dc5d22f..2e94d7b79 100644 --- a/src/src/poc/networking/encryptem/EncryptemServer.ts +++ b/src/src/poc/networking/encryptem/EncryptemServer.ts @@ -34,27 +34,27 @@ export class EncryptemServer extends Logger { this.#clientPublicKey = null; } - public setClientPublicKey(key: Buffer): void { - this.#clientPublicKey = key; + public setClientPublicKey(key: Uint8Array): void { + this.#clientPublicKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public setClientSignaturePublicKey(key: Buffer): void { - this.#clientSignaturePublicKey = key; + public setClientSignaturePublicKey(key: Uint8Array): void { + this.#clientSignaturePublicKey = Buffer.from(key.buffer, key.byteOffset, key.byteLength); } - public getClientPublicKey(): Buffer | null { + public getClientPublicKey(): Uint8Array | null { return this.#clientPublicKey; } - public getClientSignaturePublicKey(): Buffer | null { + public getClientSignaturePublicKey(): Uint8Array | null { return this.#clientSignaturePublicKey; } - public getServerPublicKey(): Buffer | null { + public getServerPublicKey(): Uint8Array | null { return this.#serverPublicKey; } - public getServerSignaturePublicKey(): Buffer | null { + public getServerSignaturePublicKey(): Uint8Array | null { return this.#serverSignaturePublicKey; } @@ -74,18 +74,20 @@ export class EncryptemServer extends Logger { throw new Error('Encryption failed. Client public key or server private key is null.'); } - return this.#encrypt(Buffer.from(msg), this.#clientPublicKey, this.#serverPrivateKey); + return this.#encrypt(Buffer.from(msg.buffer, msg.byteOffset, msg.byteLength), this.#clientPublicKey, this.#serverPrivateKey); } - public verifyAuth(out: Buffer, input: Buffer): boolean { + public verifyAuth(out: Uint8Array, input: Uint8Array): boolean { if (!this.#clientSignaturePublicKey) { throw new Error('Client signature public key is null.'); } + const outBuf = Buffer.from(out.buffer, out.byteOffset, out.byteLength); + const inputBuf = Buffer.from(input.buffer, input.byteOffset, input.byteLength); const k = this.sodium.sodium_malloc(this.sodium.crypto_auth_KEYBYTES); this.sodium.randombytes_buf_deterministic(k, this.#clientSignaturePublicKey); - return this.sodium.crypto_auth_verify(out, input, k); + return this.sodium.crypto_auth_verify(outBuf, inputBuf, k); } public decrypt(msg: Uint8Array): Uint8Array | null { @@ -93,9 +95,12 @@ export class EncryptemServer extends Logger { throw new Error('Decryption failed. Client public key or server private key is null.'); } - const auth: Buffer = Buffer.from(msg.slice(0, this.sodium.crypto_auth_BYTES)); - const signature: Buffer = Buffer.from(msg.slice(auth.length, auth.length + 64)); - const data: Buffer = Buffer.from(msg.slice(auth.length + 64, msg.length)); + const authSlice = msg.slice(0, this.sodium.crypto_auth_BYTES); + const auth: Buffer = Buffer.from(authSlice.buffer, authSlice.byteOffset, authSlice.byteLength); + const sigSlice = msg.slice(auth.length, auth.length + 64); + const signature: Buffer = Buffer.from(sigSlice.buffer, sigSlice.byteOffset, sigSlice.byteLength); + const dataSlice = msg.slice(auth.length + 64, msg.length); + const data: Buffer = Buffer.from(dataSlice.buffer, dataSlice.byteOffset, dataSlice.byteLength); if (!this.verifyAuth(auth, signature)) { throw new Error('[Server] Bad AHEAD authentication.'); diff --git a/src/src/poc/networking/encryptem/KeyPairGenerator.ts b/src/src/poc/networking/encryptem/KeyPairGenerator.ts index 606f2aaa8..b5ab8ca78 100644 --- a/src/src/poc/networking/encryptem/KeyPairGenerator.ts +++ b/src/src/poc/networking/encryptem/KeyPairGenerator.ts @@ -1,23 +1,24 @@ import crypto from 'crypto'; import sodium from 'sodium-native'; import { Logger } from '@btc-vision/bsi-common'; +import { toHex } from '@btc-vision/bitcoin'; export interface OPNetKeyPair { - publicKey: Buffer; - privateKey: Buffer; + publicKey: Uint8Array; + privateKey: Uint8Array; identity: OPNetProvenIdentity; trusted: SodiumKeyPair; } export type OPNetProvenIdentity = { - hash: Buffer; - proof: Buffer; + hash: Uint8Array; + proof: Uint8Array; }; type SodiumKeyPair = { - publicKey: Buffer; - privateKey: Buffer; + publicKey: Uint8Array; + privateKey: Uint8Array; }; export class KeyPairGenerator extends Logger { @@ -41,60 +42,64 @@ export class KeyPairGenerator extends Logger { }; } - public verifyOPNetIdentity(identity: string, pubKey: Buffer): boolean { + public verifyOPNetIdentity(identity: string, pubKey: Uint8Array): boolean { const sha = crypto.createHash('sha512'); sha.update(pubKey); - const hash: Buffer = sha.digest(); - const hashStr = `0x${hash.toString('hex')}`; + const hash = sha.digest(); + const hashStr = `0x${toHex(hash)}`; return hashStr === identity; } - public opnetHash(data: Buffer): string { + public opnetHash(data: Uint8Array): string { const hashed = this.hash(data); - return `0x${hashed.toString('hex')}`; + return `0x${toHex(hashed)}`; } public verifyChallenge( - challenge: Buffer | Uint8Array, - signature: Buffer | Uint8Array, - pubKey: Buffer | Uint8Array, + challenge: Uint8Array, + signature: Uint8Array, + pubKey: Uint8Array, ): boolean { - const hashedData: Buffer = this.hashWithPubKey(pubKey, challenge); + const hashedData: Uint8Array = this.hashWithPubKey(pubKey, challenge); return this.verifyOPNetSignature(hashedData, signature, pubKey); } public verifyOPNetSignature( - data: Buffer, - signature: Buffer | Uint8Array, - pubKey: Buffer | Uint8Array, + data: Uint8Array, + signature: Uint8Array, + pubKey: Uint8Array, ): boolean { return sodium.crypto_sign_verify_detached( Buffer.from(signature.buffer, signature.byteOffset, signature.byteLength), - data, + Buffer.from(data.buffer, data.byteOffset, data.byteLength), Buffer.from(pubKey.buffer, pubKey.byteOffset, pubKey.byteLength), ); } - public hash(data: Buffer): Buffer { + public hash(data: Uint8Array): Uint8Array { const hash = crypto.createHash('sha512'); hash.update(data); return hash.digest(); } - public hashChallenge(keyPair: SodiumKeyPair, salt: Buffer | Uint8Array): Buffer { + public hashChallenge(keyPair: SodiumKeyPair, salt: Uint8Array): Uint8Array { const result = this.hashWithPubKey(keyPair.publicKey, salt); return this.sign(result, keyPair.privateKey); } - public sign(data: Buffer, privateKey: Buffer): Buffer { + public sign(data: Uint8Array, privateKey: Uint8Array): Uint8Array { const signature = sodium.sodium_malloc(sodium.crypto_sign_BYTES); - sodium.crypto_sign_detached(signature, data, privateKey); + sodium.crypto_sign_detached( + signature, + Buffer.from(data.buffer, data.byteOffset, data.byteLength), + Buffer.from(privateKey.buffer, privateKey.byteOffset, privateKey.byteLength), + ); if (!signature.byteLength) { throw new Error('Invalid signature.'); @@ -103,11 +108,11 @@ export class KeyPairGenerator extends Logger { return signature; } - public secureRandomBytes(length: number): Buffer { + public secureRandomBytes(length: number): Uint8Array { return crypto.randomBytes(length); } - private hashWithPubKey(pubKey: Buffer | Uint8Array, data: Buffer | Uint8Array): Buffer { + private hashWithPubKey(pubKey: Uint8Array, data: Uint8Array): Uint8Array { const hash = crypto.createHash('sha512'); hash.update(pubKey); hash.update(data); @@ -124,7 +129,7 @@ export class KeyPairGenerator extends Logger { return this.generateKeyPair(seed); } - private generateAuthKey(): Buffer { + private generateAuthKey(): Uint8Array { const key = Buffer.alloc(32); return crypto.getRandomValues(key); @@ -135,7 +140,7 @@ export class KeyPairGenerator extends Logger { sha.update(keypair.publicKey); const hash: Buffer = sha.digest(); - const proof: Buffer = this.sign(hash, keypair.privateKey); + const proof: Uint8Array = this.sign(hash, keypair.privateKey); return { hash, @@ -143,10 +148,14 @@ export class KeyPairGenerator extends Logger { }; } - private generateKeyPair(seed: Buffer): SodiumKeyPair { + private generateKeyPair(seed: Uint8Array): SodiumKeyPair { const publicKey = sodium.sodium_malloc(sodium.crypto_sign_PUBLICKEYBYTES); const privateKey = sodium.sodium_malloc(sodium.crypto_sign_SECRETKEYBYTES); - sodium.crypto_sign_seed_keypair(publicKey, privateKey, seed); + sodium.crypto_sign_seed_keypair( + publicKey, + privateKey, + Buffer.from(seed.buffer, seed.byteOffset, seed.byteLength), + ); return { publicKey, diff --git a/src/src/poc/networking/p2p/BlockWitnessManager.ts b/src/src/poc/networking/p2p/BlockWitnessManager.ts index 0835768b9..82e786f8f 100644 --- a/src/src/poc/networking/p2p/BlockWitnessManager.ts +++ b/src/src/poc/networking/p2p/BlockWitnessManager.ts @@ -1,3 +1,4 @@ +import { concat, fromHex } from '@btc-vision/bitcoin'; import { DebugLevel, Logger } from '@btc-vision/bsi-common'; import Long from 'long'; import { BitcoinRPCThreadMessageType } from '../../../blockchain-indexer/rpc/thread/messages/BitcoinRPCThreadMessage.js'; @@ -438,7 +439,7 @@ export class BlockWitnessManager extends Logger { ); } - const blockChecksumHash: Buffer = this.generateBlockHeaderChecksumHash(witnessData); + const blockChecksumHash: Uint8Array = this.generateBlockHeaderChecksumHash(witnessData); const selfSignedWitness = this.identity.acknowledgeData(blockChecksumHash); /** @@ -490,8 +491,8 @@ export class BlockWitnessManager extends Logger { return { identity: w.identity, timestamp: Long.fromValue(w.timestamp.getTime(), true), - signature: Buffer.from(w.signature.buffer), - publicKey: w.publicKey ? Buffer.from(w.publicKey.buffer) : undefined, + signature: new Uint8Array(w.signature.buffer), + publicKey: w.publicKey ? new Uint8Array(w.publicKey.buffer) : undefined, }; }); } @@ -529,7 +530,7 @@ export class BlockWitnessManager extends Logger { private validateBlockHeaderSignatures( blockWitness: IBlockHeaderWitness, ): ValidWitnesses | undefined { - const blockChecksumHash: Buffer = this.generateBlockHeaderChecksumHash(blockWitness); + const blockChecksumHash: Uint8Array = this.generateBlockHeaderChecksumHash(blockWitness); const validatorWitnesses: OPNetBlockWitness[] = blockWitness.validatorWitnesses; const trustedWitnesses: OPNetBlockWitness[] = blockWitness.trustedWitnesses; @@ -557,7 +558,7 @@ export class BlockWitnessManager extends Logger { } private getValidTrustedWitnesses( - blockChecksumHash: Buffer, + blockChecksumHash: Uint8Array, witnesses: OPNetBlockWitness[], ): OPNetBlockWitness[] { if (witnesses.length === 0) return []; @@ -577,7 +578,7 @@ export class BlockWitnessManager extends Logger { } private validateOPNetWitnesses( - blockChecksumHash: Buffer, + blockChecksumHash: Uint8Array, witnesses: OPNetBlockWitness[], ): OPNetBlockWitness[] { if (witnesses.length === 0) return []; @@ -666,12 +667,12 @@ export class BlockWitnessManager extends Logger { private generateBlockHeaderChecksumHash( data: BlockProcessedData | IBlockHeaderWitness, - ): Buffer { - const generatedChecksum = Buffer.concat([ - Buffer.from(data.blockHash, 'hex'), - Buffer.from(data.previousBlockHash || '', 'hex'), // Will generate empty buffer if genesis block - Buffer.from(data.checksumHash.replace('0x', ''), 'hex'), - Buffer.from(data.previousBlockChecksum.replace('0x', ''), 'hex'), + ): Uint8Array { + const generatedChecksum = concat([ + fromHex(data.blockHash), + fromHex(data.previousBlockHash || ''), // Will generate empty buffer if genesis block + fromHex(data.checksumHash.replace('0x', '')), + fromHex(data.previousBlockChecksum.replace('0x', '')), ]); return this.identity.hash(generatedChecksum); diff --git a/src/src/poc/networking/protobuf/packets/Packet.ts b/src/src/poc/networking/protobuf/packets/Packet.ts index c17c355fa..e93df92cb 100644 --- a/src/src/poc/networking/protobuf/packets/Packet.ts +++ b/src/src/poc/networking/protobuf/packets/Packet.ts @@ -41,7 +41,7 @@ export abstract class Packet< const objOutput = this.packet.toObject(message, { longs: Long, enums: Number, - bytes: Buffer, + bytes: Uint8Array, defaults: true, arrays: true, objects: true, diff --git a/src/src/poc/networking/protobuf/packets/authentication/exchange/ServerKeyCipherExchange.ts b/src/src/poc/networking/protobuf/packets/authentication/exchange/ServerKeyCipherExchange.ts index 32270226c..f64d99db1 100644 --- a/src/src/poc/networking/protobuf/packets/authentication/exchange/ServerKeyCipherExchange.ts +++ b/src/src/poc/networking/protobuf/packets/authentication/exchange/ServerKeyCipherExchange.ts @@ -4,8 +4,8 @@ import { ServerOutBound } from '../../../types/messages/OPNetMessages.js'; import { PackedMessage, Packet } from '../../Packet.js'; export interface IServerKeyCipherExchangePacket extends PackedMessage { - serverKeyCipher: Buffer; - serverSigningCipher: Buffer; + serverKeyCipher: Uint8Array; + serverSigningCipher: Uint8Array; encryptionEnabled: boolean; } diff --git a/src/src/poc/networking/protobuf/packets/blockchain/common/BlockHeaderWitness.ts b/src/src/poc/networking/protobuf/packets/blockchain/common/BlockHeaderWitness.ts index 68e28386e..c8480f1b9 100644 --- a/src/src/poc/networking/protobuf/packets/blockchain/common/BlockHeaderWitness.ts +++ b/src/src/poc/networking/protobuf/packets/blockchain/common/BlockHeaderWitness.ts @@ -7,8 +7,8 @@ import { PackedMessage, Packet } from '../../Packet.js'; export interface OPNetBlockWitness { identity?: string; - readonly publicKey?: Buffer; - readonly signature: Buffer; + readonly publicKey?: Uint8Array; + readonly signature: Uint8Array; readonly timestamp: Long; } diff --git a/src/src/poc/networking/server/managers/AuthenticationManager.ts b/src/src/poc/networking/server/managers/AuthenticationManager.ts index 9e231c3c4..1ceccad1c 100644 --- a/src/src/poc/networking/server/managers/AuthenticationManager.ts +++ b/src/src/poc/networking/server/managers/AuthenticationManager.ts @@ -1,3 +1,4 @@ +import { equals, toHex } from '@btc-vision/bitcoin'; import { ChainIds } from '../../../../config/enums/ChainIds.js'; import { TRUSTED_CHECKSUM } from '../../../configurations/P2PVersion.js'; import { OPNetIdentity } from '../../../identity/OPNetIdentity.js'; @@ -43,7 +44,7 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager private passVersionCheck: boolean = false; private timeoutAuth: NodeJS.Timeout | null = null; - private identityChallenge: Uint8Array | Buffer | undefined; + private identityChallenge: Uint8Array | undefined; protected constructor(selfIdentity: OPNetIdentity | undefined) { super(selfIdentity); @@ -164,7 +165,7 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager } const challenge = crypto.getRandomValues(new Uint8Array(128)); - this.identityChallenge = Buffer.from(challenge); + this.identityChallenge = challenge; } private async sendServerHandshake(): Promise { @@ -269,9 +270,9 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager ); } - const clientKeyCipherBuffer = unpackedAuthData.clientKeyCipher as Buffer; - const clientAuthCipherBuffer = unpackedAuthData.clientAuthCipher as Buffer; - const clientIdentityBuffer = unpackedAuthData.identity as Buffer; + const clientKeyCipherBuffer = unpackedAuthData.clientKeyCipher; + const clientAuthCipherBuffer = unpackedAuthData.clientAuthCipher; + const clientIdentityBuffer = unpackedAuthData.identity; const challengeResponse = unpackedAuthData.challenge; // sha512 @@ -296,7 +297,7 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager } // TODO: Verify peer identity. - this._clientIdentity = Buffer.from(clientIdentityBuffer).toString('hex'); + this._clientIdentity = toHex(new Uint8Array(clientIdentityBuffer)); this.encryptem.setClientPublicKey(clientKeyCipherBuffer); @@ -305,7 +306,7 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager this.isAuthenticated = true; } - private async verifySignaturePublicKey(signaturePubKey: Uint8Array | Buffer): Promise { + private async verifySignaturePublicKey(signaturePubKey: Uint8Array): Promise { const encryptemSignaturePubKey = this.encryptem.getClientSignaturePublicKey(); if (!encryptemSignaturePubKey) { await this.disconnectPeer( @@ -315,7 +316,7 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager return false; } - if (!encryptemSignaturePubKey.equals(signaturePubKey)) { + if (!equals(encryptemSignaturePubKey, signaturePubKey)) { await this.disconnectPeer( DisconnectionCode.BAD_AUTH_CIPHER, 'Invalid client authentication cipher. Signature public key mismatch.', @@ -327,8 +328,8 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager } private async verifyChallenge( - signaturePubKey: Uint8Array | Buffer, - challengeResponse: Uint8Array | Buffer, + signaturePubKey: Uint8Array, + challengeResponse: Uint8Array, ): Promise { if (!this.identityChallenge) { throw new Error(`Challenge not set.`); @@ -489,7 +490,7 @@ export abstract class AuthenticationManager extends SharedAuthenticationManager await this.verifyNetwork(); } - this.encryptem.setClientSignaturePublicKey(Buffer.from(unpackedAuthData.clientAuthCipher)); + this.encryptem.setClientSignaturePublicKey(new Uint8Array(unpackedAuthData.clientAuthCipher)); await this.onPassedVersionCheck(); } } diff --git a/src/src/poc/networking/shared/PeerNetworkingManager.ts b/src/src/poc/networking/shared/PeerNetworkingManager.ts index cbca872fa..cadc69aa9 100644 --- a/src/src/poc/networking/shared/PeerNetworkingManager.ts +++ b/src/src/poc/networking/shared/PeerNetworkingManager.ts @@ -1,7 +1,7 @@ import { Logger } from '@btc-vision/bsi-common'; export abstract class PeerNetworkingManager extends Logger { - public send: (data: Uint8Array | Buffer) => Promise = () => { + public send: (data: Uint8Array) => Promise = () => { throw new Error('Method not implemented.'); }; diff --git a/src/src/poc/networking/shared/managers/SharedAuthenticationManager.ts b/src/src/poc/networking/shared/managers/SharedAuthenticationManager.ts index 6af8b7a66..1ba701e56 100644 --- a/src/src/poc/networking/shared/managers/SharedAuthenticationManager.ts +++ b/src/src/poc/networking/shared/managers/SharedAuthenticationManager.ts @@ -82,7 +82,7 @@ export abstract class SharedAuthenticationManager extends PeerNetworkingManager const opcode: number = raw[0]; const packet: OPNetPacket = { opcode: opcode, - packet: Buffer.from(raw.slice(1)), + packet: raw.slice(1), }; const managed: boolean = await this.onPacket(packet); diff --git a/src/src/poc/peer/OPNetPeer.ts b/src/src/poc/peer/OPNetPeer.ts index f64a635df..8fe14eea7 100644 --- a/src/src/poc/peer/OPNetPeer.ts +++ b/src/src/poc/peer/OPNetPeer.ts @@ -1,3 +1,4 @@ +import { concat } from '@btc-vision/bitcoin'; import { DebugLevel, Logger } from '@btc-vision/bsi-common'; import { PeerId } from '@libp2p/interface'; import { Config } from '../../config/Config.js'; @@ -164,7 +165,7 @@ export class OPNetPeer extends Logger { await this.authenticate(); } - public sendMsg: (peerId: PeerId, data: Uint8Array | Buffer) => Promise = () => { + public sendMsg: (peerId: PeerId, data: Uint8Array) => Promise = () => { throw new Error('Method not implemented.'); }; @@ -254,11 +255,11 @@ export class OPNetPeer extends Logger { delete this._peerIdentity; } - public sendFromServer(data: Uint8Array | Buffer): Promise { + public sendFromServer(data: Uint8Array): Promise { return this.serverNetworkingManager.sendPacket(data); } - protected async sendInternal(data: Uint8Array | Buffer): Promise { + protected async sendInternal(data: Uint8Array): Promise { if (this.isDestroyed) return; await this.sendMsg(this.peerId, data); @@ -293,11 +294,11 @@ export class OPNetPeer extends Logger { private defineServerNetworkingEvents(): void { this.serverNetworkingManager.disconnectPeer = this.disconnect.bind(this); - this.serverNetworkingManager.send = async (data: Uint8Array | Buffer) => { + this.serverNetworkingManager.send = async (data: Uint8Array) => { // to client - data = Buffer.concat([Buffer.from([0x01]), Buffer.from(data)]); + const prefixed = concat([new Uint8Array([0x01]), data]); - return this.sendInternal(data); + return this.sendInternal(prefixed); }; this.serverNetworkingManager.onServerAuthenticationCompleted = () => { @@ -328,11 +329,11 @@ export class OPNetPeer extends Logger { private defineClientNetworkingEvents(): void { this.clientNetworkingManager.disconnectPeer = this.disconnect.bind(this); - this.clientNetworkingManager.send = async (data: Uint8Array | Buffer) => { + this.clientNetworkingManager.send = async (data: Uint8Array) => { // to server - data = Buffer.concat([Buffer.from([0x00]), Buffer.from(data)]); + const prefixed = concat([new Uint8Array([0x00]), data]); - return this.sendInternal(data); + return this.sendInternal(prefixed); }; this.clientNetworkingManager.onClientAuthenticationCompleted = () => { diff --git a/src/src/ssh/client/SSHClient.ts b/src/src/ssh/client/SSHClient.ts index 69ed5eb1e..ecf4491a9 100644 --- a/src/src/ssh/client/SSHClient.ts +++ b/src/src/ssh/client/SSHClient.ts @@ -92,7 +92,7 @@ export class SSHClient extends Logger { return this._session; } - private get password(): Buffer { + private get password(): Uint8Array { if (!this.sshConfig.PASSWORD) { throw new Error('Password not set'); } @@ -151,7 +151,7 @@ export class SSHClient extends Logger { return; } - const password: Buffer = Buffer.from(ctx.password, 'utf8'); + const password: Uint8Array = Buffer.from(ctx.password, 'utf8'); if (this.verifySafeBuffer(password, this.password)) { this.authorize(ctx); @@ -176,7 +176,7 @@ export class SSHClient extends Logger { } // AUDIT FIX: Timing Attack - private verifySafeBuffer(buffer: Buffer, buffer2: Buffer): boolean { + private verifySafeBuffer(buffer: Uint8Array, buffer2: Uint8Array): boolean { // too big. reject if (buffer.length > 1024 || buffer2.length > 1024) { return false; @@ -184,11 +184,11 @@ export class SSHClient extends Logger { const maxLen = Math.max(buffer.length, buffer2.length); - const paddedBuffer1 = Buffer.alloc(maxLen, 0); - const paddedBuffer2 = Buffer.alloc(maxLen, 0); + const paddedBuffer1 = new Uint8Array(maxLen); + const paddedBuffer2 = new Uint8Array(maxLen); - buffer.copy(paddedBuffer1); - buffer2.copy(paddedBuffer2); + paddedBuffer1.set(buffer); + paddedBuffer2.set(buffer2); // Perform a constant-time comparison on equally sized buffers const isMatch = timingSafeEqual(paddedBuffer1, paddedBuffer2); diff --git a/src/src/utils/AddressDecoder.ts b/src/src/utils/AddressDecoder.ts index 13f507251..9945c277f 100644 --- a/src/src/utils/AddressDecoder.ts +++ b/src/src/utils/AddressDecoder.ts @@ -13,8 +13,8 @@ export interface ScriptAddress { /** Single address when one exists (P2PKH, P2SH, bech32, …) */ address?: string; /** - * For bare‐multisig we expose every constituent key’s P2PKH address - * (same idea as Bitcoin Core’s “addresses” array). + * For bare‐multisig we expose every constituent key's P2PKH address + * (same idea as Bitcoin Core's "addresses" array). */ addresses?: string[]; /** Bitcoin Core–style script classification */ @@ -34,8 +34,8 @@ export interface ScriptAddress { | 'nonstandard'; } -export function scriptToAddress(output: Buffer, network: Network): ScriptAddress { - const outputScript = new Uint8Array(output) as Script; +export function scriptToAddress(output: Uint8Array, network: Network): ScriptAddress { + const outputScript = output as Script; if (output[0] === opcodes.OP_RETURN) { return { type: 'nulldata' }; @@ -94,7 +94,7 @@ export function scriptToAddress(output: Buffer, network: Network): ScriptAddress .filter((addr): addr is string => typeof addr === 'string'); return { addresses: addresses, type: 'multisig' }; - } catch {} // fall through if the pattern didn’t match + } catch {} // fall through if the pattern didn't match try { return { address: _toFutureSegwitAddress(output, network), type: 'witness_unknown' }; diff --git a/src/src/utils/BufferUtils.ts b/src/src/utils/BufferUtils.ts index 6f01ab695..51b64a24a 100644 --- a/src/src/utils/BufferUtils.ts +++ b/src/src/utils/BufferUtils.ts @@ -1,9 +1,5 @@ -export function isEmptyBuffer(buffer: Buffer | Uint8Array): boolean { - for (let i = 0; i < buffer.length; i++) { - if (buffer[i] !== 0) { - return false; - } - } +import { isZero } from '@btc-vision/bitcoin'; - return true; +export function isEmptyBuffer(buffer: Uint8Array): boolean { + return isZero(buffer); } diff --git a/src/src/utils/RPCSubWorker.ts b/src/src/utils/RPCSubWorker.ts index ebdcff9f7..9c6878ce4 100644 --- a/src/src/utils/RPCSubWorker.ts +++ b/src/src/utils/RPCSubWorker.ts @@ -2,6 +2,7 @@ import { VMStorage } from '../vm/storage/VMStorage.js'; import { VMManager } from '../vm/VMManager.js'; import { Config } from '../config/Config.js'; import { Logger } from '@btc-vision/bsi-common'; +import { fromBase64, fromHex, toBase64, toHex } from '@btc-vision/bitcoin'; import { BitcoinRPC } from '@btc-vision/bitcoin-rpc'; import { CallRequestData, @@ -69,8 +70,8 @@ class RPCManager extends Logger { if (result && !('error' in result)) { result = Object.assign(result, { - result: result.result ? Buffer.from(result.result).toString('hex') : '', - revert: result.revert ? Buffer.from(result.revert).toString('hex') : '', + result: result.result ? toHex(result.result) : '', + revert: result.revert ? toHex(result.revert) : '', changedStorage: result.changedStorage ? this.convertMapToArray(result.changedStorage) : [], @@ -141,13 +142,13 @@ class RPCManager extends Logger { blockHeight: contract.blockHeight.toString(), contractAddress: contract.contractAddress, contractPublicKey: contract.contractPublicKey.toString(), - bytecode: contract.bytecode.toString('hex'), + bytecode: toHex(contract.bytecode), wasCompressed: contract.wasCompressed, - deployedTransactionId: contract.deployedTransactionId.toString('hex'), - deployedTransactionHash: contract.deployedTransactionHash.toString('hex'), - deployerPubKey: contract.deployerPubKey.toString('hex'), - contractSeed: contract.contractSeed.toString('hex'), - contractSaltHash: contract.contractSaltHash.toString('hex'), + deployedTransactionId: toHex(contract.deployedTransactionId), + deployedTransactionHash: toHex(contract.deployedTransactionHash), + deployerPubKey: toHex(contract.deployerPubKey), + contractSeed: toHex(contract.contractSeed), + contractSaltHash: toHex(contract.contractSaltHash), deployerAddress: contract.deployerAddress.toHex(), }; @@ -163,7 +164,7 @@ class RPCManager extends Logger { for (const [key, value] of events) { const innerArray: [string, string][] = []; for (const event of value) { - innerArray.push([event.type, Buffer.from(event.data).toString('hex')]); + innerArray.push([event.type, toHex(event.data)]); } array.push([key.toString(), innerArray]); @@ -179,7 +180,7 @@ class RPCManager extends Logger { const innerArray: string[] = []; for (const innerKey of value.keys()) { innerArray.push( - Buffer.from(BufferHelper.pointerToUint8Array(innerKey)).toString('base64'), + toBase64(BufferHelper.pointerToUint8Array(innerKey)), ); } @@ -258,13 +259,13 @@ class RPCManager extends Logger { return { inputs: transaction.inputs.map((input) => { return { - txId: Buffer.from(input.txId, 'base64'), + txId: fromBase64(input.txId), outputIndex: input.outputIndex, - scriptSig: Buffer.from(input.scriptSig, 'base64'), + scriptSig: fromBase64(input.scriptSig), witnesses: input.witnesses - ? input.witnesses.map((w) => Buffer.from(w, 'base64')) + ? input.witnesses.map((w) => fromBase64(w)) : [], - coinbase: input.coinbase ? Buffer.from(input.coinbase, 'base64') : undefined, + coinbase: input.coinbase ? fromBase64(input.coinbase) : undefined, flags: input.flags || 0, }; }), @@ -285,7 +286,7 @@ class RPCManager extends Logger { to: output.to, flags: output.flags || 0, scriptPubKey: output.scriptPubKey - ? Buffer.from(output.scriptPubKey, 'base64') + ? fromBase64(output.scriptPubKey) : undefined, }; }), @@ -304,7 +305,7 @@ class RPCManager extends Logger { const address = Address.fromString(key); const innerMap: Uint8Array[] = value.map((innerValue: string) => { - return Buffer.from(innerValue, 'base64'); + return fromBase64(innerValue); }); storageMap.set(address, innerMap); @@ -338,7 +339,7 @@ class RPCManager extends Logger { return await vmManager.execute( data.to, fromAddress, - Buffer.from(data.calldata, 'hex'), + fromHex(data.calldata), data.blockNumber, parsedTransaction, data.accessList, diff --git a/src/src/utils/SHA1.ts b/src/src/utils/SHA1.ts index c0851f682..bb33ddd14 100644 --- a/src/src/utils/SHA1.ts +++ b/src/src/utils/SHA1.ts @@ -1,11 +1,12 @@ import crypto from 'crypto'; export class SHA1 { - public static hash(data: Buffer): string { + public static hash(data: Uint8Array): string { return crypto.createHash('sha1').update(data).digest('hex'); } - public static hashBuffer(data: Buffer): Buffer { - return crypto.createHash('sha1').update(data).digest(); + public static hashBuffer(data: Uint8Array): Uint8Array { + const digest = crypto.createHash('sha1').update(data).digest(); + return new Uint8Array(digest.buffer, digest.byteOffset, digest.byteLength); } } diff --git a/src/src/utils/StringToBuffer.ts b/src/src/utils/StringToBuffer.ts index cd5063555..ad9db0df8 100644 --- a/src/src/utils/StringToBuffer.ts +++ b/src/src/utils/StringToBuffer.ts @@ -1,3 +1,5 @@ -export function stringToBuffer(str: string): Buffer { - return Buffer.from(str.replace('0x', ''), 'hex'); +import { fromHex } from '@btc-vision/bitcoin'; + +export function stringToBuffer(str: string): Uint8Array { + return fromHex(str.replace('0x', '')); } diff --git a/src/src/utils/TransactionMempoolUtils.ts b/src/src/utils/TransactionMempoolUtils.ts index cc1267afe..9fc17b9d4 100644 --- a/src/src/utils/TransactionMempoolUtils.ts +++ b/src/src/utils/TransactionMempoolUtils.ts @@ -1,11 +1,11 @@ import { IMempoolTransactionObj } from '../db/interfaces/IMempoolTransaction.js'; -import bitcoin, { toHex, Transaction } from '@btc-vision/bitcoin'; +import bitcoin, { reverseCopy, toHex, Transaction } from '@btc-vision/bitcoin'; import { Long } from 'mongodb'; import { NetworkConverter } from '../config/network/NetworkConverter.js'; const network = NetworkConverter.getNetwork(); -export function getOutputAddressForScript(script: Buffer): string | null { +export function getOutputAddressForScript(script: Uint8Array): string | null { try { let address: string; if (bitcoin.script.toASM(script).startsWith('OP_1')) { @@ -23,13 +23,13 @@ export function getOutputAddressForScript(script: Buffer): string | null { } } -export function parseAndStoreInputOutputs(data: Buffer, transaction: IMempoolTransactionObj): void { +export function parseAndStoreInputOutputs(data: Uint8Array, transaction: IMempoolTransactionObj): void { try { const decoded = Transaction.fromBuffer(data); for (const input of decoded.ins) { transaction.inputs.push({ - transactionId: toHex(Buffer.from(input.hash).reverse()), + transactionId: toHex(reverseCopy(input.hash)), outputIndex: input.index, }); } @@ -37,9 +37,9 @@ export function parseAndStoreInputOutputs(data: Buffer, transaction: IMempoolTra for (let i = 0; i < decoded.outs.length; i++) { const out = decoded.outs[i]; - const outputAddress = getOutputAddressForScript(Buffer.from(out.script)); + const outputAddress = getOutputAddressForScript(out.script); transaction.outputs.push({ - data: Buffer.from(out.script), + data: out.script, outputIndex: i, value: Long.fromBigInt(out.value, true), address: outputAddress, diff --git a/src/src/vm/BlockHeaderValidator.ts b/src/src/vm/BlockHeaderValidator.ts index 9b0ae87a1..8312637ab 100644 --- a/src/src/vm/BlockHeaderValidator.ts +++ b/src/src/vm/BlockHeaderValidator.ts @@ -5,6 +5,7 @@ import { import { DataConverter, DebugLevel, Logger } from '@btc-vision/bsi-common'; import { IBtcIndexerConfig } from '../config/interfaces/IBtcIndexerConfig.js'; import { VMStorage } from './storage/VMStorage.js'; +import { fromHex } from '@btc-vision/bitcoin'; import { BufferHelper } from '@btc-vision/transaction'; import { ChecksumMerkle } from '../blockchain-indexer/processor/block/merkle/ChecksumMerkle.js'; import { ZERO_HASH } from '../blockchain-indexer/processor/block/types/ZeroValue.js'; @@ -87,8 +88,8 @@ export class BlockHeaderValidator extends Logger { const blockStorage: string | undefined = blockHeader.storageRoot; const blockHash: string | undefined = blockHeader.hash; const blockMerkelRoot: string | undefined = blockHeader.merkleRoot; - const checksumRoot: Uint8Array | undefined = Uint8Array.from( - Buffer.from(blockHeader.checksumRoot.replace('0x', ''), 'hex'), + const checksumRoot: Uint8Array | undefined = fromHex( + blockHeader.checksumRoot.replace('0x', ''), ); const proofs: BlockHeaderChecksumProof | undefined = blockHeader.checksumProofs; diff --git a/src/src/vm/Blockchain.ts b/src/src/vm/Blockchain.ts index 7968f8cc8..b88ce3600 100644 --- a/src/src/vm/Blockchain.ts +++ b/src/src/vm/Blockchain.ts @@ -92,7 +92,7 @@ class BlockchainBase { ): Promise => { if (this.enableDebug) console.log('ACCOUNT TYPE', value.buffer); - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found'); @@ -107,7 +107,7 @@ class BlockchainBase { ): Promise => { return new Promise((resolve) => { if (Config.DEV.ENABLE_CONTRACT_DEBUG) { - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found'); @@ -127,7 +127,7 @@ class BlockchainBase { value: ThreadSafeJsImportResponse, ): Promise => { return new Promise((resolve) => { - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found'); @@ -194,7 +194,7 @@ class BlockchainBase { ): Promise => { if (this.enableDebug) console.log('LOAD', value.buffer); - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found (load)'); @@ -212,7 +212,7 @@ class BlockchainBase { ): Promise => { if (this.enableDebug) console.log('STORE', value.buffer); - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found (store)'); @@ -230,7 +230,7 @@ class BlockchainBase { ): Promise => { if (this.enableDebug) console.log('CALL', value.buffer); - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found (call)'); @@ -248,7 +248,7 @@ class BlockchainBase { ): Promise => { if (this.enableDebug) console.log('DEPLOY', value.buffer); - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found (deploy)'); @@ -266,7 +266,7 @@ class BlockchainBase { ): Promise => { if (this.enableDebug) console.log('DEPLOY', value.buffer); - const buf = Buffer.from(Array.from(value.buffer)); + const buf = new Uint8Array(value.buffer); const c = this.bindings.get(BigInt(`${value.contractId}`)); // otherwise unsafe. if (!c) { throw new Error('Binding not found (deploy)'); diff --git a/src/src/vm/VMManager.ts b/src/src/vm/VMManager.ts index e07fa0b8c..d189c18c3 100644 --- a/src/src/vm/VMManager.ts +++ b/src/src/vm/VMManager.ts @@ -43,7 +43,7 @@ import { import { ContractEvaluation } from './runtime/classes/ContractEvaluation.js'; import { GasTracker } from './runtime/GasTracker.js'; import { OPNetConsensus } from '../poc/configurations/OPNetConsensus.js'; -import bitcoin, { Network } from '@btc-vision/bitcoin'; +import bitcoin, { alloc, equals, fromHex, Network } from '@btc-vision/bitcoin'; import { NetworkConverter } from '../config/network/NetworkConverter.js'; import { Blockchain } from './Blockchain.js'; import { BlockHeaderValidator } from './BlockHeaderValidator.js'; @@ -58,26 +58,23 @@ import { IMLDSAPublicKey, MLDSAUpdateData } from '../db/interfaces/IMLDSAPublicK Globals.register(); -const EMPTY_BLOCK_HASH = Buffer.alloc(32); -const SIMULATION_TRANSACTION_ID = Buffer.from( +const EMPTY_BLOCK_HASH = alloc(32); +const SIMULATION_TRANSACTION_ID = fromHex( '61e1ca05754b6990c56d8f0f06c33da411f086c5abae59572e63549361c8f5fc', - 'hex', ); -const SIMULATION_TRANSACTION_HASH = Buffer.from( +const SIMULATION_TRANSACTION_HASH = fromHex( '947d267bb393648af57e3a498b4cfa30f50e7b3263223b8077416f342133ec9c', - 'hex', ); -const SIMULATION_DEFAULT_INPUT_TX_HASH = Buffer.from( +const SIMULATION_DEFAULT_INPUT_TX_HASH = fromHex( '61e1ca05754b6990c56d8f0f06c33da411f086c5abae59572e63549361c8f5fc', - 'hex', ); const SIMULATION_DEFAULT_INPUT: StrippedTransactionInput = { txId: SIMULATION_DEFAULT_INPUT_TX_HASH, outputIndex: 0, - scriptSig: Buffer.alloc(0), + scriptSig: new Uint8Array(0), witnesses: [], coinbase: undefined, flags: 0, @@ -118,7 +115,7 @@ export class VMManager extends Logger { private mldsaToStore: AddressMap = new AddressMap(); private mldsaToStoreLegacy: AddressMap
= new AddressMap(); - private mldsaToStoreByHash: AddressMap = new AddressMap(); + private mldsaToStoreByHash: AddressMap = new AddressMap(); constructor( private readonly config: IBtcIndexerConfig, @@ -222,7 +219,7 @@ export class VMManager extends Logger { public async execute( to: string, from: Address, - calldata: Buffer, + calldata: Uint8Array, height?: bigint, transaction?: ParsedSimulatedTransaction, accessList?: AccessList, @@ -245,7 +242,7 @@ export class VMManager extends Logger { throw new Error('Contract not found'); } - let blockHash: Buffer; + let blockHash: Uint8Array; let currentHeight: BlockHeader; let median: bigint; if (height != undefined) { @@ -336,7 +333,7 @@ export class VMManager extends Logger { } public async executeTransaction( - blockHash: Buffer, + blockHash: Uint8Array, blockHeight: bigint, blockMedian: bigint, baseGas: bigint, @@ -443,7 +440,7 @@ export class VMManager extends Logger { } public async deployContract( - blockHash: Buffer, + blockHash: Uint8Array, blockHeight: bigint, median: bigint, baseGas: bigint, @@ -678,7 +675,7 @@ export class VMManager extends Logger { } const publicKeyData = this.vmStorage.getMLDSAPublicKeyFromHash( - Buffer.from(address.toBuffer()), + address.toBuffer(), this.vmBitcoinBlock.height, ); @@ -688,7 +685,7 @@ export class VMManager extends Logger { } public async getMLDSAPublicKeyFromLegacyKey( - tweakedPublicKey: Buffer, + tweakedPublicKey: Uint8Array, ): Promise { const tweakedAddress = new Address(tweakedPublicKey); @@ -785,7 +782,7 @@ export class VMManager extends Logger { // Verify no conflicting pending write for this MLDSA hashed public key const existingEntry = this.mldsaToStore.get(address); if (existingEntry) { - if (existingEntry.data.legacyPublicKey.equals(mldsaPublicKey.legacyPublicKey)) { + if (equals(existingEntry.data.legacyPublicKey, mldsaPublicKey.legacyPublicKey)) { return null; // Same user sending duplicate tx, first one will be stored } @@ -805,7 +802,7 @@ export class VMManager extends Logger { // Defense in depth (verify hashedPublicKey not claimed by different legacy key) const existingLegacyKey = this.mldsaToStoreByHash.get(hashedAddress); - if (existingLegacyKey && !existingLegacyKey.equals(mldsaPublicKey.legacyPublicKey)) { + if (existingLegacyKey && !equals(existingLegacyKey, mldsaPublicKey.legacyPublicKey)) { throw new Error( 'MLDSA hashed public key is already pending to be linked to a different legacy key in current block.', ); @@ -815,8 +812,8 @@ export class VMManager extends Logger { } private async shouldInsertMLDSAKey( - hashedPublicKey: Buffer, - legacyPublicKey: Buffer, + hashedPublicKey: Uint8Array, + legacyPublicKey: Uint8Array, level: MLDSASecurityLevel, isExpose: boolean = false, ): Promise { @@ -989,7 +986,7 @@ export class VMManager extends Logger { private convertAPIBlockHeaderToBlockHeader(block: BlockHeaderAPIBlockDocument): BlockHeader { return { ...block, - hash: Buffer.from(block.hash, 'hex'), + hash: fromHex(block.hash), height: BigInt(block.height), }; } @@ -1015,9 +1012,9 @@ export class VMManager extends Logger { return this.cachedLastBlockHeight; } - private generateAddress(salt: Buffer, deployer: Address, bytecode: Buffer): Address { + private generateAddress(salt: Uint8Array, deployer: Address, bytecode: Uint8Array): Address { const contractPublicKey = TapscriptVerificator.getContractSeed( - bitcoin.crypto.hash256(Buffer.from(deployer)), + bitcoin.crypto.hash256(deployer), bytecode, salt, ); @@ -1027,7 +1024,7 @@ export class VMManager extends Logger { private async deployContractAtAddress( address: Address, - salt: Buffer, + salt: Uint8Array, evaluation: ContractEvaluation, ): Promise< | { @@ -1070,16 +1067,16 @@ export class VMManager extends Logger { const deployerKeyPair = contractInfo.contractPublicKey; const bytecodeLength: number = contractInfo.bytecode.byteLength; - const contractSaltHash = Buffer.from(bitcoin.crypto.hash256(salt)); + const contractSaltHash = bitcoin.crypto.hash256(salt); const contractInformation: ContractInformation = new ContractInformation( evaluation.blockNumber, deployResult.p2op(this.network), deployResult, contractInfo.bytecode, false, - evaluation.transactionId || Buffer.alloc(32), - evaluation.transactionHash || Buffer.alloc(32), - Buffer.from(deployerKeyPair), + evaluation.transactionId || alloc(32), + evaluation.transactionHash || alloc(32), + deployerKeyPair, salt, contractSaltHash, evaluation.contractAddress, @@ -1179,17 +1176,17 @@ export class VMManager extends Logger { ); if (lastChecksum && lastChecksum !== ZERO_HASH) { - const checksumBuffer = Buffer.from(lastChecksum.replace('0x', ''), 'hex'); + const checksumBuffer = fromHex(lastChecksum.replace('0x', '')); if (checksumBuffer.length !== 32) { throw new Error('Invalid checksum length retrieved from block header validator.'); } this.receiptState.updateValue(BTC_FAKE_ADDRESS, MAX_HASH, checksumBuffer); } else { - this.receiptState.updateValue(BTC_FAKE_ADDRESS, MAX_HASH, Buffer.alloc(0)); + this.receiptState.updateValue(BTC_FAKE_ADDRESS, MAX_HASH, new Uint8Array(0)); } - this.receiptState.updateValue(BTC_FAKE_ADDRESS, MAX_MINUS_ONE, Buffer.from([1])); // version + this.receiptState.updateValue(BTC_FAKE_ADDRESS, MAX_MINUS_ONE, new Uint8Array([1])); // version this.receiptState.freeze(); } diff --git a/src/src/vm/runtime/ContractEvaluator.ts b/src/src/vm/runtime/ContractEvaluator.ts index eb777bdbf..09f84630a 100644 --- a/src/src/vm/runtime/ContractEvaluator.ts +++ b/src/src/vm/runtime/ContractEvaluator.ts @@ -45,7 +45,7 @@ import { IMLDSAPublicKey } from '../../db/interfaces/IMLDSAPublicKey.js'; interface InternalCallParameters { readonly evaluation: ContractEvaluation; - readonly calldata: Buffer; + readonly calldata: Uint8Array; readonly isDeployment: boolean; readonly isUpdate: boolean; readonly contractAddress: Address; @@ -53,7 +53,7 @@ interface InternalCallParameters { interface InternalCallResponse { readonly isWarm: boolean; - readonly result: Buffer; + readonly result: Uint8Array; readonly status: number; readonly gasUsed: bigint; } @@ -66,7 +66,7 @@ export class ContractEvaluator extends Logger { private deployerAddress: Address | undefined; private contractAddress: Address | undefined; - private bytecode: Buffer | undefined; + private bytecode: Uint8Array | undefined; private version: number | undefined; constructor(private readonly network: Network) { @@ -109,7 +109,7 @@ export class ContractEvaluator extends Logger { throw new Error('Method not implemented. [callExternal]'); } - public getBlockHashForBlockNumber(_blockNumber: bigint): Promise { + public getBlockHashForBlockNumber(_blockNumber: bigint): Promise { throw new Error('Method not implemented. [getBlockHashForBlockNumber]'); } @@ -119,7 +119,7 @@ export class ContractEvaluator extends Logger { public deployContractAtAddress( _address: Address, - _salt: Buffer, + _salt: Uint8Array, _evaluation: ContractEvaluation, ): Promise< | { @@ -358,7 +358,7 @@ export class ContractEvaluator extends Logger { } /** Load a pointer */ - private async load(data: Buffer, evaluation: ContractEvaluation): Promise { + private async load(data: Uint8Array, evaluation: ContractEvaluation): Promise { const reader: BinaryReader = new BinaryReader(data); const pointer: bigint = reader.readU256(); @@ -379,7 +379,7 @@ export class ContractEvaluator extends Logger { } /** Store a pointer */ - private store(data: Buffer, evaluation: ContractEvaluation): Buffer | Uint8Array { + private store(data: Uint8Array, evaluation: ContractEvaluation): Buffer | Uint8Array { const reader = new BinaryReader(data); const pointer: bigint = reader.readU256(); const value: bigint = reader.readU256(); @@ -390,7 +390,7 @@ export class ContractEvaluator extends Logger { } /** Call a contract */ - private async call(data: Buffer, evaluation: ContractEvaluation): Promise { + private async call(data: Uint8Array, evaluation: ContractEvaluation): Promise { let gasUsed: bigint = evaluation.gasUsed; try { @@ -412,7 +412,7 @@ export class ContractEvaluator extends Logger { const response = await this.internalCall({ evaluation, - calldata: Buffer.copyBytesFrom(calldata), + calldata: Uint8Array.from(calldata), isDeployment: false, isUpdate: false, contractAddress, @@ -502,11 +502,11 @@ export class ContractEvaluator extends Logger { evaluation.merge(response); const status = response.revert ? 1 : 0; - const result = (status ? response.revert : response.result) || Buffer.alloc(0); + const result = (status ? response.revert : response.result) || new Uint8Array(0); return { isWarm, - result: Buffer.from(result.buffer, result.byteOffset, result.byteLength), + result: result, status, gasUsed: response.gasUsed, }; @@ -528,7 +528,7 @@ export class ContractEvaluator extends Logger { } private async deployContractFromAddressRaw( - data: Buffer, + data: Uint8Array, evaluation: ContractEvaluation, ): Promise { let usedGas: bigint = evaluation.gasUsed; @@ -545,11 +545,10 @@ export class ContractEvaluator extends Logger { // Read the contract address and salt. const address: Address = reader.readAddress(); - const original = reader.readBytes(32); - const salt: Buffer = Buffer.from(original); + const salt: Uint8Array = reader.readBytes(32); // Read the calldata. - const calldata: Buffer = Buffer.from(reader.readBytes(reader.bytesLeft())); + const calldata: Uint8Array = reader.readBytes(reader.bytesLeft()); const deployResult = await this.deployContractAtAddress(address, salt, evaluation); if (!deployResult) { @@ -599,7 +598,7 @@ export class ContractEvaluator extends Logger { } private async updateContractFromAddressRaw( - data: Buffer, + data: Uint8Array, evaluation: ContractEvaluation, ): Promise { let usedGas: bigint = evaluation.gasUsed; @@ -613,7 +612,7 @@ export class ContractEvaluator extends Logger { evaluation.setGasUsed(usedGas); const sourceAddress: Address = reader.readAddress(); - const calldata: Buffer = Buffer.from(reader.readBytes(reader.bytesLeft())); + const calldata: Uint8Array = reader.readBytes(reader.bytesLeft()); const updateResult = await this.updateFromAddressJsFunction(sourceAddress, evaluation); if (!updateResult) { @@ -690,14 +689,14 @@ export class ContractEvaluator extends Logger { return writer.getBuffer(); } - private onDebug(buffer: Buffer): void { - const reader = new BinaryReader(buffer); - const logData = reader.readString(buffer.byteLength); + private onDebug(data: Uint8Array): void { + const reader = new BinaryReader(data); + const logData = reader.readString(data.byteLength); this.warn(`Contract log: ${logData}`); } - private onEvent(data: Buffer, evaluation: ContractEvaluation): void { + private onEvent(data: Uint8Array, evaluation: ContractEvaluation): void { const reader = new BinaryReader(data); const eventName = reader.readStringWithLength(); const eventData = reader.readBytesWithLength(); @@ -706,16 +705,16 @@ export class ContractEvaluator extends Logger { evaluation.emitEvent(event); } - private onInputsRequested(evaluation: ContractEvaluation): Promise { + private onInputsRequested(evaluation: ContractEvaluation): Promise { return Promise.resolve(evaluation.getSerializeInputUTXOs()); } - private onOutputsRequested(evaluation: ContractEvaluation): Promise { + private onOutputsRequested(evaluation: ContractEvaluation): Promise { return Promise.resolve(evaluation.getSerializeOutputUTXOs()); } private async loadMLDSA( - data: Buffer, + data: Uint8Array, evaluation: ContractEvaluation, ): Promise { const reader = new BinaryReader(data); @@ -755,7 +754,7 @@ export class ContractEvaluator extends Logger { } private async getAccountType( - data: Buffer, + data: Uint8Array, evaluation: ContractEvaluation, ): Promise { const reader = new BinaryReader(data); @@ -785,7 +784,7 @@ export class ContractEvaluator extends Logger { } return { - blockHash: blockHash, + blockHash: Buffer.from(blockHash.buffer, blockHash.byteOffset, blockHash.byteLength), isBlockWarm: false, }; } @@ -814,36 +813,36 @@ export class ContractEvaluator extends Logger { gasMax: evaluation.maxGasVM, memoryPagesUsed: evaluation.memoryPagesUsed, isDebugMode: enableDebug, - accountType: async (data: Buffer): Promise => { + accountType: async (data: Uint8Array): Promise => { return await this.getAccountType(data, evaluation); }, blockHash: async (blockNumber: bigint): Promise => { return await this.getBlockHashImport(blockNumber); }, - load: async (data: Buffer) => { + load: async (data: Uint8Array) => { return await this.load(data, evaluation); }, - store: (data: Buffer) => { + store: (data: Uint8Array) => { return new Promise((resolve) => { const resp = this.store(data, evaluation); resolve(resp); }); }, - call: async (data: Buffer) => { + call: async (data: Uint8Array) => { return await this.call(data, evaluation); }, - deployContractAtAddress: async (data: Buffer) => { + deployContractAtAddress: async (data: Uint8Array) => { return await this.deployContractFromAddressRaw(data, evaluation); }, - updateFromAddress: async (data: Buffer) => { + updateFromAddress: async (data: Uint8Array) => { return await this.updateContractFromAddressRaw(data, evaluation); }, - log: (buffer: Buffer) => { - this.onDebug(buffer); + log: (data: Uint8Array) => { + this.onDebug(data); }, - emit: (buffer: Buffer) => { - this.onEvent(buffer, evaluation); + emit: (data: Uint8Array) => { + this.onEvent(data, evaluation); }, inputs: () => { return this.onInputsRequested(evaluation); @@ -851,16 +850,16 @@ export class ContractEvaluator extends Logger { outputs: () => { return this.onOutputsRequested(evaluation); }, - loadMLDSA: async (data: Buffer) => { + loadMLDSA: async (data: Uint8Array) => { return await this.loadMLDSA(data, evaluation); }, // NOT SUPPORTED YET. - tLoad(_: Buffer): Promise { - return Promise.resolve(Buffer.alloc(0)); + tLoad(_: Uint8Array): Promise { + return Promise.resolve(new Uint8Array(0)); }, - tStore(_: Buffer): Promise { - return Promise.resolve(Buffer.alloc(0)); + tStore(_: Uint8Array): Promise { + return Promise.resolve(new Uint8Array(0)); }, }; } diff --git a/src/src/vm/runtime/classes/ContractEvaluation.ts b/src/src/vm/runtime/classes/ContractEvaluation.ts index 91f52d741..428f5361a 100644 --- a/src/src/vm/runtime/classes/ContractEvaluation.ts +++ b/src/src/vm/runtime/classes/ContractEvaluation.ts @@ -1,3 +1,4 @@ +import { fromBase64, toHex } from '@btc-vision/bitcoin'; import { ExecutionParameters } from '../types/InternalContractCallParameters.js'; import { Address, @@ -56,9 +57,9 @@ export class ContractEvaluation implements ExecutionParameters { public contractDeployDepth: MutableNumber; public contractUpdateDepth: MutableNumber; - public readonly blockHash: Buffer; - public readonly transactionId: Buffer; - public readonly transactionHash: Buffer; + public readonly blockHash: Uint8Array; + public readonly transactionId: Uint8Array; + public readonly transactionHash: Uint8Array; public readonly gasTracker: GasTracker; @@ -197,20 +198,20 @@ export class ContractEvaluation implements ExecutionParameters { return this.touchedAddresses.get(address); } - public getSerializeInputUTXOs(): Buffer { + public getSerializeInputUTXOs(): Uint8Array { if (!this.serializedInputs) { this.serializedInputs = this.computeInputUTXOs(); } - return Buffer.copyBytesFrom(this.serializedInputs); + return Uint8Array.from(this.serializedInputs); } - public getSerializeOutputUTXOs(): Buffer { + public getSerializeOutputUTXOs(): Uint8Array { if (!this.serializedOutputs) { this.serializedOutputs = this.computeOutputUTXOs(); } - return Buffer.copyBytesFrom(this.serializedOutputs); + return Uint8Array.from(this.serializedOutputs); } public setGasUsed(gas: bigint, specialGas: bigint = 0n): void { @@ -554,15 +555,15 @@ export class ContractEvaluation implements ExecutionParameters { }); for (const [key, value] of Object.entries(storageKeys)) { - const bigIntBuf = Buffer.from(key, 'base64'); - const valueBuf = Buffer.from(value, 'base64'); + const bigIntBuf = fromBase64(key); + const valueBuf = fromBase64(value); if (bigIntBuf.length !== 32 || valueBuf.length !== 32) { throw new Error(`OP_NET: Invalid access list key or value.`); } - const pointerKey = BigInt('0x' + bigIntBuf.toString('hex')); - const pointerValue = BigInt('0x' + valueBuf.toString('hex')); + const pointerKey = BigInt('0x' + toHex(bigIntBuf)); + const pointerValue = BigInt('0x' + toHex(valueBuf)); current.set(pointerKey, pointerValue); } diff --git a/src/src/vm/runtime/types/InternalContractCallParameters.ts b/src/src/vm/runtime/types/InternalContractCallParameters.ts index 2d8cee415..c281e8618 100644 --- a/src/src/vm/runtime/types/InternalContractCallParameters.ts +++ b/src/src/vm/runtime/types/InternalContractCallParameters.ts @@ -21,12 +21,12 @@ export interface InternalContractCallParameters { readonly mldsaLoadCounter: MutableNumber; readonly gasTracker: GasTracker; - readonly calldata: Buffer; + readonly calldata: Uint8Array; readonly externalCall: boolean; - readonly transactionId: Buffer; - readonly transactionHash: Buffer; - readonly blockHash: Buffer; + readonly transactionId: Uint8Array; + readonly transactionHash: Uint8Array; + readonly blockHash: Uint8Array; readonly blockHeight: bigint; readonly blockMedian: bigint; @@ -66,9 +66,9 @@ export interface ExecutionParameters { readonly txOrigin: Address; readonly msgSender: Address; - readonly transactionId: Buffer; - readonly transactionHash: Buffer; - readonly blockHash: Buffer; + readonly transactionId: Uint8Array; + readonly transactionHash: Uint8Array; + readonly blockHash: Uint8Array; readonly blockNumber: bigint; readonly blockMedian: bigint; diff --git a/src/src/vm/rust/ChainIdHex.ts b/src/src/vm/rust/ChainIdHex.ts index 2e3b9ab1c..b81ea1f6d 100644 --- a/src/src/vm/rust/ChainIdHex.ts +++ b/src/src/vm/rust/ChainIdHex.ts @@ -1,3 +1,4 @@ +import { fromHex } from '@btc-vision/bitcoin'; import { BitcoinNetworkRequest } from '@btc-vision/op-vm'; export function getChainIdHex(network: BitcoinNetworkRequest): string { @@ -14,5 +15,5 @@ export function getChainIdHex(network: BitcoinNetworkRequest): string { } export function getChainId(network: BitcoinNetworkRequest): Uint8Array { - return Uint8Array.from(Buffer.from(getChainIdHex(network), 'hex')); + return fromHex(getChainIdHex(network)); } diff --git a/src/src/vm/rust/RustContract.ts b/src/src/vm/rust/RustContract.ts index 46e8a6500..9aeb88c8d 100644 --- a/src/src/vm/rust/RustContract.ts +++ b/src/src/vm/rust/RustContract.ts @@ -17,7 +17,7 @@ process.on('uncaughtException', (error) => { export interface ContractParameters extends Omit { readonly address: string; - readonly bytecode: Buffer; + readonly bytecode: Uint8Array; readonly gasMax: bigint; readonly gasUsed: bigint; readonly memoryPagesUsed: bigint; @@ -106,7 +106,7 @@ export class RustContract { return errorWriter.getBuffer(); } - public static decodeRevertData(revertDataBytes: Uint8Array | Buffer): Error { + public static decodeRevertData(revertDataBytes: Uint8Array): Error { if (RustContract.startsWithErrorSelector(revertDataBytes)) { const decoder = new TextDecoder(); const revertMessage = decoder.decode( @@ -197,7 +197,7 @@ export class RustContract { } } - public async execute(calldata: Uint8Array | Buffer): Promise> { + public async execute(calldata: Uint8Array): Promise> { if (this.enableDebug) console.log('execute', calldata); try { @@ -230,23 +230,17 @@ export class RustContract { blockMedianTime: BigInt( environmentVariables.blockMedianTime.toString(), ), - blockHash: Buffer.copyBytesFrom(environmentVariables.blockHash), - txId: Buffer.copyBytesFrom(environmentVariables.txId), - txHash: Buffer.copyBytesFrom(environmentVariables.txHash), - contractAddress: Buffer.copyBytesFrom( - environmentVariables.contractAddress, - ), - contractDeployer: Buffer.copyBytesFrom( - environmentVariables.contractDeployer, - ), - caller: Buffer.copyBytesFrom(environmentVariables.caller), - origin: Buffer.copyBytesFrom(environmentVariables.origin), + blockHash: Uint8Array.from(environmentVariables.blockHash), + txId: Uint8Array.from(environmentVariables.txId), + txHash: Uint8Array.from(environmentVariables.txHash), + contractAddress: Uint8Array.from(environmentVariables.contractAddress), + contractDeployer: Uint8Array.from(environmentVariables.contractDeployer), + caller: Uint8Array.from(environmentVariables.caller), + origin: Uint8Array.from(environmentVariables.origin), chainId: getChainId(this.params.network), protocolId: OPNetConsensus.consensus.PROTOCOL_ID, consensusFlags: BigInt(environmentVariables.consensusFlags.toString()), - originTweakedPublicKey: Buffer.copyBytesFrom( - environmentVariables.originTweakedPublicKey, - ), + originTweakedPublicKey: Uint8Array.from(environmentVariables.originTweakedPublicKey), }), ), ), @@ -259,7 +253,7 @@ export class RustContract { } } - public async onUpdate(calldata: Uint8Array | Buffer): Promise> { + public async onUpdate(calldata: Uint8Array): Promise> { if (this.enableDebug) console.log('Setting onUpdate', calldata); try { @@ -277,7 +271,7 @@ export class RustContract { } } - public async onDeploy(calldata: Uint8Array | Buffer): Promise> { + public async onDeploy(calldata: Uint8Array): Promise> { if (this.enableDebug) console.log('Setting onDeployment', calldata); try { @@ -297,7 +291,7 @@ export class RustContract { public getRevertError(): Error { const revertInfo = this.contractManager.getExitData(this.id); - const revertData = Buffer.copyBytesFrom(revertInfo.data); + const revertData = new Uint8Array(revertInfo.data); try { this.dispose(); @@ -332,8 +326,8 @@ export class RustContract { gasUsed: BigInt(result.gasUsed.toString()), proofs: result.proofs?.map((proof) => { return { - proof: Buffer.copyBytesFrom(proof.proof), - vk: Buffer.copyBytesFrom(proof.vk), + proof: proof.proof, + vk: proof.vk, }; }), }), diff --git a/src/src/vm/rust/RustContractBindings.ts b/src/src/vm/rust/RustContractBindings.ts index a75a0bee6..02a458cfc 100644 --- a/src/src/vm/rust/RustContractBindings.ts +++ b/src/src/vm/rust/RustContractBindings.ts @@ -2,18 +2,18 @@ import { AccountTypeResponse, BlockHashResponse } from '@btc-vision/op-vm'; export interface RustContractBinding { readonly id: bigint; - readonly loadMLDSA: (data: Buffer) => Promise; - readonly load: (data: Buffer) => Promise; - readonly store: (data: Buffer) => Promise; - readonly tLoad: (data: Buffer) => Promise; - readonly tStore: (data: Buffer) => Promise; - readonly call: (data: Buffer) => Promise; - readonly deployContractAtAddress: (data: Buffer) => Promise; - readonly updateFromAddress: (data: Buffer) => Promise; - readonly log: (data: Buffer) => void; - readonly emit: (data: Buffer) => void; + readonly loadMLDSA: (data: Uint8Array) => Promise; + readonly load: (data: Uint8Array) => Promise; + readonly store: (data: Uint8Array) => Promise; + readonly tLoad: (data: Uint8Array) => Promise; + readonly tStore: (data: Uint8Array) => Promise; + readonly call: (data: Uint8Array) => Promise; + readonly deployContractAtAddress: (data: Uint8Array) => Promise; + readonly updateFromAddress: (data: Uint8Array) => Promise; + readonly log: (data: Uint8Array) => void; + readonly emit: (data: Uint8Array) => void; readonly inputs: () => Promise; readonly outputs: () => Promise; - readonly accountType: (data: Buffer) => Promise; + readonly accountType: (data: Uint8Array) => Promise; readonly blockHash: (blockNumber: bigint) => Promise; } diff --git a/src/src/vm/storage/VMStorage.ts b/src/src/vm/storage/VMStorage.ts index dc73f9225..e75be4798 100644 --- a/src/src/vm/storage/VMStorage.ts +++ b/src/src/vm/storage/VMStorage.ts @@ -132,7 +132,7 @@ export abstract class VMStorage extends Logger { public abstract getLatestBlock(): Promise; - public abstract addTweakedPublicKey(buffer: Buffer): Promise; + public abstract addTweakedPublicKey(buffer: Uint8Array): Promise; public abstract getBlockTransactions( height?: bigint | -1, @@ -181,7 +181,7 @@ export abstract class VMStorage extends Logger { /** * Get epoch by epoch hash */ - public abstract getEpochByHash(epochHash: Buffer | Binary): Promise; + public abstract getEpochByHash(epochHash: Uint8Array | Binary): Promise; /** * Get epoch by block height (find which epoch contains this block) @@ -197,13 +197,13 @@ export abstract class VMStorage extends Logger { * Get epochs by proposer public key */ public abstract getEpochsByProposer( - proposerPublicKey: Buffer | Binary, + proposerPublicKey: Uint8Array | Binary, ): Promise; /** * Get epochs by target hash */ - public abstract getEpochsByTargetHash(targetHash: Buffer | Binary): Promise; + public abstract getEpochsByTargetHash(targetHash: Uint8Array | Binary): Promise; /** * Save or update an epoch @@ -243,14 +243,14 @@ export abstract class VMStorage extends Logger { * Get submission by transaction hash */ public abstract getSubmissionByTxHash( - txHash: Buffer | Binary, + txHash: Uint8Array | Binary, ): Promise; /** * Get submission by transaction ID */ public abstract getSubmissionByTxId( - txId: Buffer | Binary, + txId: Uint8Array | Binary, ): Promise; /** @@ -265,7 +265,7 @@ export abstract class VMStorage extends Logger { * Get submissions by proposer public key */ public abstract getSubmissionsByProposer( - proposerPublicKey: Buffer | Binary, + proposerPublicKey: Uint8Array | Binary, ): Promise; /** @@ -277,22 +277,22 @@ export abstract class VMStorage extends Logger { * Get submissions by submission hash */ public abstract getSubmissionByHash( - submissionHash: Buffer | Binary, + submissionHash: Uint8Array | Binary, ): Promise; /** * Check if a submission exists */ public abstract submissionExists( - mldsaPublicKey: Buffer | Binary, - salt: Buffer | Binary, + mldsaPublicKey: Uint8Array | Binary, + salt: Uint8Array | Binary, epochNumber: bigint, ): Promise; public abstract targetEpochExists( epochNumber: bigint, - salt: Buffer | Binary, - mldsaPublicKey: Buffer | Binary, + salt: Uint8Array | Binary, + mldsaPublicKey: Uint8Array | Binary, ): Promise; public abstract getBestTargetEpoch(epochNumber: bigint): Promise; @@ -302,19 +302,19 @@ export abstract class VMStorage extends Logger { public abstract deleteOldTargetEpochs(epochNumber: bigint): Promise; public abstract getMLDSAPublicKeyFromHash( - publicKey: Buffer | Binary, + publicKey: Uint8Array | Binary, blockHeight: bigint, ): Promise; public abstract saveMLDSAPublicKeys(publicKeys: MLDSAUpdateData[]): Promise; public abstract getMLDSAByLegacy( - publicKey: Buffer | Binary, + publicKey: Uint8Array | Binary, blockHeight: bigint, ): Promise; public abstract mldsaPublicKeyExists( - hashedPublicKey: Buffer | Binary | string, - legacyPublicKey: Buffer | Binary | string, + hashedPublicKey: Uint8Array | Binary | string, + legacyPublicKey: Uint8Array | Binary | string, ): Promise; } diff --git a/src/src/vm/storage/databases/VMMongoStorage.ts b/src/src/vm/storage/databases/VMMongoStorage.ts index 33a46c130..e93ead025 100644 --- a/src/src/vm/storage/databases/VMMongoStorage.ts +++ b/src/src/vm/storage/databases/VMMongoStorage.ts @@ -84,7 +84,7 @@ export class VMMongoStorage extends VMStorage { } public getMLDSAPublicKeyFromHash( - publicKey: Buffer | Binary, + publicKey: Uint8Array | Binary, blockHeight: bigint, ): Promise { if (!this.mldsaPublicKeysRepository) { @@ -103,8 +103,8 @@ export class VMMongoStorage extends VMStorage { } public mldsaPublicKeyExists( - hashedPublicKey: Buffer | Binary, - legacyPublicKey: Buffer | Binary, + hashedPublicKey: Uint8Array | Binary, + legacyPublicKey: Uint8Array | Binary, ): Promise { if (!this.mldsaPublicKeysRepository) { throw new Error('MLDSA Public Key repository not initialized'); @@ -114,7 +114,7 @@ export class VMMongoStorage extends VMStorage { } public getMLDSAByLegacy( - publicKey: Buffer | Binary, + publicKey: Uint8Array | Binary, blockHeight: bigint, ): Promise { if (!this.mldsaPublicKeysRepository) { @@ -126,8 +126,8 @@ export class VMMongoStorage extends VMStorage { public targetEpochExists( epochNumber: bigint, - salt: Buffer | Binary, - mldsaPublicKey: Buffer | Binary, + salt: Uint8Array | Binary, + mldsaPublicKey: Uint8Array | Binary, ): Promise { if (!this.targetEpochRepository) { throw new Error('Target epoch repository not initialized'); @@ -334,7 +334,7 @@ export class VMMongoStorage extends VMStorage { ); } - public async addTweakedPublicKey(tweaked: Buffer): Promise { + public async addTweakedPublicKey(tweaked: Uint8Array): Promise { if (!this.publicKeysRepository) { throw new Error('Public key repository not initialized'); } @@ -556,7 +556,7 @@ export class VMMongoStorage extends VMStorage { height, ); - if (Buffer.isBuffer(value)) { + if (value instanceof Uint8Array) { throw new Error('The value returned was not an Uint8Array!'); } @@ -728,7 +728,7 @@ export class VMMongoStorage extends VMStorage { return this.epochRepository.getEpochByNumber(epochNumber); } - public getEpochByHash(epochHash: Buffer | Binary): Promise { + public getEpochByHash(epochHash: Uint8Array | Binary): Promise { if (!this.epochRepository) { throw new Error('Epoch repository not initialized'); } @@ -760,7 +760,7 @@ export class VMMongoStorage extends VMStorage { return this.epochRepository.getActiveEpoch(); } - public getEpochsByProposer(proposerPublicKey: Buffer | Binary): Promise { + public getEpochsByProposer(proposerPublicKey: Uint8Array | Binary): Promise { if (!this.epochRepository) { throw new Error('Epoch repository not initialized'); } @@ -768,7 +768,7 @@ export class VMMongoStorage extends VMStorage { return this.epochRepository.getEpochsByProposer(proposerPublicKey); } - public getEpochsByTargetHash(targetHash: Buffer | Binary): Promise { + public getEpochsByTargetHash(targetHash: Uint8Array | Binary): Promise { if (!this.epochRepository) { throw new Error('Epoch repository not initialized'); } @@ -809,7 +809,7 @@ export class VMMongoStorage extends VMStorage { } public getSubmissionByTxHash( - txHash: Buffer | Binary, + txHash: Uint8Array | Binary, ): Promise { if (!this.epochSubmissionRepository) { throw new Error('Epoch submission repository not initialized'); @@ -819,7 +819,7 @@ export class VMMongoStorage extends VMStorage { } public getSubmissionByTxId( - txId: Buffer | Binary, + txId: Uint8Array | Binary, ): Promise { if (!this.epochSubmissionRepository) { throw new Error('Epoch submission repository not initialized'); @@ -840,7 +840,7 @@ export class VMMongoStorage extends VMStorage { } public getSubmissionsByProposer( - proposerPublicKey: Buffer | Binary, + proposerPublicKey: Uint8Array | Binary, ): Promise { if (!this.epochSubmissionRepository) { throw new Error('Epoch submission repository not initialized'); @@ -858,7 +858,7 @@ export class VMMongoStorage extends VMStorage { } public getSubmissionByHash( - submissionHash: Buffer | Binary, + submissionHash: Uint8Array | Binary, ): Promise { if (!this.epochSubmissionRepository) { throw new Error('Epoch submission repository not initialized'); @@ -868,8 +868,8 @@ export class VMMongoStorage extends VMStorage { } public submissionExists( - publicKey: Buffer | Binary, - salt: Buffer | Binary, + publicKey: Uint8Array | Binary, + salt: Uint8Array | Binary, epochNumber: bigint, ): Promise { if (!this.epochSubmissionRepository) { diff --git a/tests/plugins/api/PluginBlockchainAPI.test.ts b/tests/plugins/api/PluginBlockchainAPI.test.ts index 44f723d4d..1826d37b0 100644 --- a/tests/plugins/api/PluginBlockchainAPI.test.ts +++ b/tests/plugins/api/PluginBlockchainAPI.test.ts @@ -301,8 +301,8 @@ describe('PluginBlockchainAPI', () => { it('should return mapped transaction', async () => { const mockTx = { - id: 'txid123', - hash: 'txhash456', + id: new Binary(Buffer.from('aa'.repeat(32), 'hex')), + hash: new Binary(Buffer.from('bb'.repeat(32), 'hex')), blockHeight: { toString: () => '100' }, index: 0, inputs: [], @@ -318,8 +318,8 @@ describe('PluginBlockchainAPI', () => { const result = await api.getTransaction('txid123'); expect(result).not.toBeNull(); - expect(result?.txid).toBe('txid123'); - expect(result?.hash).toBe('txhash456'); + expect(result?.txid).toBe('aa'.repeat(32)); + expect(result?.hash).toBe('bb'.repeat(32)); }); }); @@ -339,8 +339,8 @@ describe('PluginBlockchainAPI', () => { const mockTxs = [ { - id: 'txid1', - hash: 'hash1', + id: new Binary(Buffer.from('cc'.repeat(32), 'hex')), + hash: new Binary(Buffer.from('dd'.repeat(32), 'hex')), blockHeight: { toString: () => '100' }, index: 0, inputs: [], @@ -357,7 +357,7 @@ describe('PluginBlockchainAPI', () => { const result = await api.getTransactionsByBlock(100n); expect(result).toHaveLength(1); - expect(result[0].txid).toBe('txid1'); + expect(result[0].txid).toBe('cc'.repeat(32)); }); }); @@ -407,7 +407,7 @@ describe('PluginBlockchainAPI', () => { const result = await api.getContractStorage('addr123', 0n); expect(result).not.toBeNull(); - expect(result).toBeInstanceOf(Buffer); + expect(result).toBeInstanceOf(Uint8Array); }); }); @@ -537,14 +537,15 @@ describe('PluginBlockchainAPI', () => { describe('transaction mapping', () => { it('should handle Binary inputs correctly', async () => { + const inputTxId = new Binary(Buffer.from('ee'.repeat(32), 'hex')); const mockTx = { - id: 'txid123', - hash: 'txhash456', + id: new Binary(Buffer.from('aa'.repeat(32), 'hex')), + hash: new Binary(Buffer.from('bb'.repeat(32), 'hex')), blockHeight: { toString: () => '100' }, index: 0, inputs: [ { - originalTransactionId: 'input-txid', + originalTransactionId: inputTxId, outputIndex: 0, scriptSig: new Binary(Buffer.from([0x01, 0x02])), witness: [new Binary(Buffer.from([0x03, 0x04]))], @@ -572,14 +573,14 @@ describe('PluginBlockchainAPI', () => { expect(result).not.toBeNull(); expect(result?.inputs).toHaveLength(1); - expect(result?.inputs[0].txid).toBe('input-txid'); + expect(result?.inputs[0].txid).toBe('ee'.repeat(32)); }); it('should handle revert data correctly', async () => { const revertBuffer = Buffer.from('Revert reason'); const mockTx = { - id: 'txid123', - hash: 'txhash456', + id: new Binary(Buffer.from('aa'.repeat(32), 'hex')), + hash: new Binary(Buffer.from('bb'.repeat(32), 'hex')), blockHeight: { toString: () => '100' }, index: 0, inputs: [], @@ -601,8 +602,8 @@ describe('PluginBlockchainAPI', () => { it('should handle successful transaction (no revert)', async () => { const mockTx = { - id: 'txid123', - hash: 'txhash456', + id: new Binary(Buffer.from('aa'.repeat(32), 'hex')), + hash: new Binary(Buffer.from('bb'.repeat(32), 'hex')), blockHeight: { toString: () => '100' }, index: 0, inputs: [], @@ -625,8 +626,8 @@ describe('PluginBlockchainAPI', () => { describe('script type detection', () => { const createOutputWithScript = (hex: string) => ({ - id: 'txid123', - hash: 'txhash456', + id: new Binary(Buffer.from('aa'.repeat(32), 'hex')), + hash: new Binary(Buffer.from('bb'.repeat(32), 'hex')), blockHeight: { toString: () => '100' }, index: 0, inputs: [], diff --git a/tests/plugins/api/PluginFilesystemAPI.test.ts b/tests/plugins/api/PluginFilesystemAPI.test.ts index 5bc72d74d..ee901a5ad 100644 --- a/tests/plugins/api/PluginFilesystemAPI.test.ts +++ b/tests/plugins/api/PluginFilesystemAPI.test.ts @@ -62,7 +62,7 @@ describe('PluginFilesystemAPI', () => { // Should be able to read it const content = await api.readFile('test.txt'); - expect(content.toString()).toBe('test content'); + expect(new TextDecoder().decode(content)).toBe('test content'); }); it('should allow paths within temp directory', async () => { @@ -73,7 +73,7 @@ describe('PluginFilesystemAPI', () => { // Should be able to read it const content = await api.readFile(tempPath); - expect(content.toString()).toBe('temp content'); + expect(new TextDecoder().decode(content)).toBe('temp content'); }); }); @@ -85,7 +85,7 @@ describe('PluginFilesystemAPI', () => { const content = await api.readFile('data.txt'); - expect(content.toString()).toBe('hello world'); + expect(new TextDecoder().decode(content)).toBe('hello world'); }); it('should throw for non-existent file', async () => { @@ -96,7 +96,7 @@ describe('PluginFilesystemAPI', () => { it('should read binary files', async () => { const configDir = path.join(tempDir, pluginId, 'config'); fs.mkdirSync(configDir, { recursive: true }); - const binaryData = Buffer.from([0x00, 0x01, 0x02, 0xff]); + const binaryData = new Uint8Array([0x00, 0x01, 0x02, 0xff]); fs.writeFileSync(path.join(configDir, 'binary.bin'), binaryData); const content = await api.readFile('binary.bin'); @@ -325,14 +325,14 @@ describe('PluginFilesystemAPI', () => { await api.writeFile('empty.txt', ''); const content = await api.readFile('empty.txt'); - expect(content.toString()).toBe(''); + expect(new TextDecoder().decode(content)).toBe(''); }); it('should handle files with special characters in name', async () => { await api.writeFile('file with spaces.txt', 'content'); const content = await api.readFile('file with spaces.txt'); - expect(content.toString()).toBe('content'); + expect(new TextDecoder().decode(content)).toBe('content'); }); it('should handle files with unicode content', async () => { @@ -340,7 +340,7 @@ describe('PluginFilesystemAPI', () => { await api.writeFile('unicode.txt', unicodeContent); const content = await api.readFile('unicode.txt'); - expect(content.toString()).toBe(unicodeContent); + expect(new TextDecoder().decode(content)).toBe(unicodeContent); }); it('should handle large files', async () => { diff --git a/tests/plugins/loader/PluginLoader.test.ts b/tests/plugins/loader/PluginLoader.test.ts index c1922081e..98b6656a0 100644 --- a/tests/plugins/loader/PluginLoader.test.ts +++ b/tests/plugins/loader/PluginLoader.test.ts @@ -230,7 +230,7 @@ describe('PluginLoader', () => { const parsed = loader.parsePluginFile(filePath); expect(parsed.proto).toBeDefined(); - expect(parsed.proto?.toString()).toContain('proto3'); + expect(new TextDecoder().decode(parsed.proto)).toContain('proto3'); }); it('should parse file without proto', () => {