UNPKG

opnet

Version:

The perfect library for building Bitcoin-based applications.

317 lines (316 loc) 12.9 kB
import { fromHex } from '@btc-vision/bitcoin'; import { BinaryReader, BinaryWriter } from '@btc-vision/transaction'; export var NetworkName; (function (NetworkName) { NetworkName["Mainnet"] = "mainnet"; NetworkName["Testnet"] = "testnet"; NetworkName["OpnetTestnet"] = "opnetTestnet"; NetworkName["Regtest"] = "regtest"; })(NetworkName || (NetworkName = {})); const SERIALIZATION_VERSION = 1; export class CallResultSerializer { static FEE_PRECISION = 1000000; static serialize(data) { const writer = new BinaryWriter(); writer.writeU8(SERIALIZATION_VERSION); writer.writeU8(this.networkNameToU8(data.network)); writer.writeBytesWithLength(data.calldata); writer.writeStringWithLength(data.to); writer.writeStringWithLength(data.contractAddress); writer.writeU256(data.estimatedSatGas); writer.writeU256(data.estimatedRefundedGasInSat); writer.writeU256(data.estimatedGas ?? 0n); writer.writeU256(data.refundedGas ?? 0n); if (data.revert !== undefined) { writer.writeBoolean(true); writer.writeStringWithLength(data.revert); } else { writer.writeBoolean(false); } writer.writeBytesWithLength(data.result); this.writeAccessList(writer, data.accessList); if (data.bitcoinFees) { writer.writeBoolean(true); this.writeBitcoinFees(writer, data.bitcoinFees); } else { writer.writeBoolean(false); } this.writeChallenge(writer, data.challenge); writer.writeBytesWithLength(data.challengeOriginalPublicKey); this.writeUTXOs(writer, data.utxos); if (data.csvAddress) { writer.writeBoolean(true); writer.writeStringWithLength(data.csvAddress.address); writer.writeBytesWithLength(data.csvAddress.witnessScript); } else { writer.writeBoolean(false); } return writer.getBuffer(); } static deserialize(buffer) { const reader = new BinaryReader(buffer); const version = reader.readU8(); if (version !== SERIALIZATION_VERSION) { throw new Error(`Unsupported serialization version: ${version}. Expected: ${SERIALIZATION_VERSION}`); } const network = this.u8ToNetworkName(reader.readU8()); const calldata = reader.readBytesWithLength(); const to = reader.readStringWithLength(); const contractAddress = reader.readStringWithLength(); const estimatedSatGas = reader.readU256(); const estimatedRefundedGasInSat = reader.readU256(); const estimatedGasRaw = reader.readU256(); const refundedGasRaw = reader.readU256(); const estimatedGas = estimatedGasRaw > 0n ? estimatedGasRaw : undefined; const refundedGas = refundedGasRaw > 0n ? refundedGasRaw : undefined; const hasRevert = reader.readBoolean(); const revert = hasRevert ? reader.readStringWithLength() : undefined; const result = reader.readBytesWithLength(); const accessList = this.readAccessList(reader); const hasBitcoinFees = reader.readBoolean(); const bitcoinFees = hasBitcoinFees ? this.readBitcoinFees(reader) : undefined; const challenge = this.readChallenge(reader); const challengeOriginalPublicKey = reader.readBytesWithLength(); const utxos = this.readUTXOs(reader); const hasCsvAddress = reader.readBoolean(); const csvAddress = hasCsvAddress ? { address: reader.readStringWithLength(), witnessScript: reader.readBytesWithLength(), } : undefined; return { calldata, to, contractAddress, estimatedSatGas, estimatedRefundedGasInSat, estimatedGas, refundedGas, revert, result, accessList, bitcoinFees, network, challenge, challengeOriginalPublicKey, utxos, csvAddress, }; } static networkNameToU8(network) { switch (network) { case NetworkName.Mainnet: return 0; case NetworkName.Testnet: return 1; case NetworkName.Regtest: return 2; case NetworkName.OpnetTestnet: return 3; default: return 2; } } static u8ToNetworkName(value) { switch (value) { case 0: return NetworkName.Mainnet; case 1: return NetworkName.Testnet; case 2: return NetworkName.Regtest; case 3: return NetworkName.OpnetTestnet; default: return NetworkName.Regtest; } } static writeAccessList(writer, accessList) { const contracts = Object.keys(accessList); writer.writeU16(contracts.length); for (const contract of contracts) { writer.writeStringWithLength(contract); const slots = accessList[contract]; const slotKeys = Object.keys(slots); writer.writeU16(slotKeys.length); for (const key of slotKeys) { writer.writeStringWithLength(key); writer.writeStringWithLength(slots[key]); } } } static readAccessList(reader) { const accessList = {}; const contractCount = reader.readU16(); for (let i = 0; i < contractCount; i++) { const contract = reader.readStringWithLength(); const slotCount = reader.readU16(); accessList[contract] = {}; for (let j = 0; j < slotCount; j++) { const key = reader.readStringWithLength(); const value = reader.readStringWithLength(); accessList[contract][key] = value; } } return accessList; } static writeBitcoinFees(writer, fees) { writer.writeU64(BigInt(Math.round(fees.conservative * this.FEE_PRECISION))); writer.writeU64(BigInt(Math.round(fees.recommended.low * this.FEE_PRECISION))); writer.writeU64(BigInt(Math.round(fees.recommended.medium * this.FEE_PRECISION))); writer.writeU64(BigInt(Math.round(fees.recommended.high * this.FEE_PRECISION))); } static readBitcoinFees(reader) { return { conservative: Number(reader.readU64()) / this.FEE_PRECISION, recommended: { low: Number(reader.readU64()) / this.FEE_PRECISION, medium: Number(reader.readU64()) / this.FEE_PRECISION, high: Number(reader.readU64()) / this.FEE_PRECISION, }, }; } static writeChallenge(writer, challenge) { writer.writeStringWithLength(challenge.epochNumber); writer.writeStringWithLength(challenge.mldsaPublicKey); writer.writeStringWithLength(challenge.legacyPublicKey); writer.writeStringWithLength(challenge.solution); writer.writeStringWithLength(challenge.salt); writer.writeStringWithLength(challenge.graffiti); writer.writeU256(BigInt(challenge.difficulty)); writer.writeStringWithLength(challenge.verification.epochHash); writer.writeStringWithLength(challenge.verification.epochRoot); writer.writeStringWithLength(challenge.verification.targetHash); writer.writeStringWithLength(challenge.verification.targetChecksum); writer.writeStringWithLength(challenge.verification.startBlock); writer.writeStringWithLength(challenge.verification.endBlock); writer.writeU16(challenge.verification.proofs.length); for (const proof of challenge.verification.proofs) { writer.writeStringWithLength(proof); } if (challenge.submission) { writer.writeBoolean(true); writer.writeStringWithLength(challenge.submission.mldsaPublicKey); writer.writeStringWithLength(challenge.submission.legacyPublicKey); writer.writeStringWithLength(challenge.submission.solution); writer.writeStringWithLength(challenge.submission.graffiti || ''); writer.writeStringWithLength(challenge.submission.signature); } else { writer.writeBoolean(false); } } static readChallenge(reader) { const epochNumber = reader.readStringWithLength(); const mldsaPublicKey = reader.readStringWithLength(); const legacyPublicKey = reader.readStringWithLength(); const solution = reader.readStringWithLength(); const salt = reader.readStringWithLength(); const graffiti = reader.readStringWithLength(); const difficulty = Number(reader.readU256()); const epochHash = reader.readStringWithLength(); const epochRoot = reader.readStringWithLength(); const targetHash = reader.readStringWithLength(); const targetChecksum = reader.readStringWithLength(); const startBlock = reader.readStringWithLength(); const endBlock = reader.readStringWithLength(); const proofCount = reader.readU16(); const proofs = []; for (let i = 0; i < proofCount; i++) { proofs.push(reader.readStringWithLength()); } const hasSubmission = reader.readBoolean(); const submission = hasSubmission ? { mldsaPublicKey: reader.readStringWithLength(), legacyPublicKey: reader.readStringWithLength(), solution: reader.readStringWithLength(), graffiti: reader.readStringWithLength() || undefined, signature: reader.readStringWithLength(), } : undefined; return { epochNumber, mldsaPublicKey, legacyPublicKey, solution, salt, graffiti, difficulty, verification: { epochHash, epochRoot, targetHash: targetHash, targetChecksum, startBlock, endBlock, proofs, }, submission, }; } static writeUTXOs(writer, utxos) { writer.writeU16(utxos.length); for (const utxo of utxos) { writer.writeStringWithLength(utxo.transactionId); writer.writeU32(utxo.outputIndex); writer.writeU64(utxo.value); writer.writeStringWithLength(utxo.scriptPubKey.hex); writer.writeStringWithLength(utxo.scriptPubKey.address || ''); writer.writeBoolean(!!utxo.isCSV); if (utxo.witnessScript) { writer.writeBoolean(true); const witnessScriptBytes = typeof utxo.witnessScript === 'string' ? fromHex(utxo.witnessScript) : utxo.witnessScript; writer.writeBytesWithLength(witnessScriptBytes); } else { writer.writeBoolean(false); } if (utxo.redeemScript) { writer.writeBoolean(true); const redeemScriptBytes = typeof utxo.redeemScript === 'string' ? fromHex(utxo.redeemScript) : utxo.redeemScript; writer.writeBytesWithLength(redeemScriptBytes); } else { writer.writeBoolean(false); } } } static readUTXOs(reader) { const count = reader.readU16(); const utxos = []; for (let i = 0; i < count; i++) { const transactionId = reader.readStringWithLength(); const outputIndex = reader.readU32(); const value = reader.readU64(); const hex = reader.readStringWithLength(); const addressStr = reader.readStringWithLength(); const isCSV = reader.readBoolean(); const hasWitnessScript = reader.readBoolean(); const witnessScript = hasWitnessScript ? reader.readBytesWithLength() : undefined; const hasRedeemScript = reader.readBoolean(); const redeemScript = hasRedeemScript ? reader.readBytesWithLength() : undefined; utxos.push({ transactionId, outputIndex, value, scriptPubKey: { hex, address: addressStr || undefined, }, isCSV, witnessScript, redeemScript, }); } return utxos; } }