UNPKG

@btc-vision/transaction

Version:

OPNet transaction library allows you to create and sign transactions for the OPNet network.

283 lines (282 loc) 9.9 kB
import { BufferHelper } from '../utils/BufferHelper.js'; import { ADDRESS_BYTE_LENGTH, I128_BYTE_LENGTH, U128_BYTE_LENGTH, U16_BYTE_LENGTH, U256_BYTE_LENGTH, U32_BYTE_LENGTH, U64_BYTE_LENGTH, U8_BYTE_LENGTH, } from '../utils/lengths.js'; import { BinaryReader } from './BinaryReader.js'; export class BinaryWriter { constructor(length = 0) { this.currentOffset = 0; this.buffer = this.getDefaultBuffer(length); } static estimateArrayOfBufferLength(values) { if (values.length > 65535) throw new Error('Array size is too large'); let totalLength = U16_BYTE_LENGTH; for (let i = 0; i < values.length; i++) { totalLength += U32_BYTE_LENGTH + values[i].length; } return totalLength; } writeU8(value) { if (value > 255) throw new Error('u8 value is too large.'); this.allocSafe(U8_BYTE_LENGTH); this.buffer.setUint8(this.currentOffset++, value); } writeU16(value, be = true) { if (value > 65535) throw new Error('u16 value is too large.'); this.allocSafe(U16_BYTE_LENGTH); this.buffer.setUint16(this.currentOffset, value, !be); this.currentOffset += 2; } writeU32(value, be = true) { if (value > 4294967295) throw new Error('u32 value is too large.'); this.allocSafe(U32_BYTE_LENGTH); this.buffer.setUint32(this.currentOffset, value, !be); this.currentOffset += 4; } writeU64(value, be = true) { if (value > 18446744073709551615n) throw new Error('u64 value is too large.'); this.allocSafe(U64_BYTE_LENGTH); this.buffer.setBigUint64(this.currentOffset, value, !be); this.currentOffset += 8; } writeSelector(value) { this.writeU32(value, true); } writeBoolean(value) { this.writeU8(value ? 1 : 0); } writeI128(bigIntValue, be = true) { if (bigIntValue > 170141183460469231731687303715884105727n || bigIntValue < -170141183460469231731687303715884105728n) { throw new Error('i128 value is too large.'); } this.allocSafe(I128_BYTE_LENGTH); const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue, I128_BYTE_LENGTH); if (bytesToHex.byteLength !== I128_BYTE_LENGTH) { throw new Error(`Invalid i128 value: ${bigIntValue}`); } if (be) { for (let i = 0; i < bytesToHex.byteLength; i++) { this.writeU8(bytesToHex[i]); } } else { for (let i = bytesToHex.byteLength - 1; i >= 0; i--) { this.writeU8(bytesToHex[i]); } } } writeU256(bigIntValue, be = true) { if (bigIntValue > 115792089237316195423570985008687907853269984665640564039457584007913129639935n && bigIntValue < 0n) { throw new Error('u256 value is too large or negative.'); } this.allocSafe(U256_BYTE_LENGTH); const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue); if (bytesToHex.byteLength !== U256_BYTE_LENGTH) { throw new Error(`Invalid u256 value: ${bigIntValue}`); } if (be) { for (let i = 0; i < bytesToHex.byteLength; i++) { this.writeU8(bytesToHex[i]); } } else { for (let i = bytesToHex.byteLength - 1; i >= 0; i--) { this.writeU8(bytesToHex[i]); } } } writeU128(bigIntValue, be = true) { if (bigIntValue > 340282366920938463463374607431768211455n && bigIntValue < 0n) { throw new Error('u128 value is too large or negative.'); } this.allocSafe(U128_BYTE_LENGTH); const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue, U128_BYTE_LENGTH); if (bytesToHex.byteLength !== U128_BYTE_LENGTH) { throw new Error(`Invalid u128 value: ${bigIntValue}`); } if (be) { for (let i = 0; i < bytesToHex.byteLength; i++) { this.writeU8(bytesToHex[i]); } } else { for (let i = bytesToHex.byteLength - 1; i >= 0; i--) { this.writeU8(bytesToHex[i]); } } } writeBytes(value) { this.allocSafe(value.byteLength); for (let i = 0; i < value.byteLength; i++) { this.writeU8(value[i]); } } writeString(value) { const encoder = new TextEncoder(); const bytes = encoder.encode(value); this.allocSafe(bytes.length); this.writeBytes(bytes); } writeStringWithLength(value) { const encoder = new TextEncoder(); const bytes = encoder.encode(value); this.allocSafe(U32_BYTE_LENGTH + bytes.length); this.writeU32(bytes.length); this.writeBytes(bytes); } writeAddress(value) { this.verifyAddress(value); this.writeBytes(value); } getBuffer(clear = true) { const buf = new Uint8Array(this.buffer.byteLength); for (let i = 0; i < this.buffer.byteLength; i++) { buf[i] = this.buffer.getUint8(i); } if (clear) this.clear(); return buf; } reset() { this.currentOffset = 0; this.buffer = this.getDefaultBuffer(4); } toBytesReader() { return new BinaryReader(this.getBuffer()); } getOffset() { return this.currentOffset; } setOffset(offset) { this.currentOffset = offset; } clear() { this.currentOffset = 0; this.buffer = this.getDefaultBuffer(); } allocSafe(size) { if (this.currentOffset + size > this.buffer.byteLength) { this.resize(size); } } writeAddressValueTuple(map, be = true) { if (map.size > 65535) throw new Error('Map size is too large'); this.writeU16(map.size, be); const keys = Array.from(map.keys()); for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = map.get(key); if (value === null || value === undefined) throw new Error('Value not found'); this.writeAddress(key); this.writeU256(value, be); } } writeBytesWithLength(value) { this.writeU32(value.length); this.writeBytes(value); } writeArrayOfBuffer(values, be = true) { const totalLength = BinaryWriter.estimateArrayOfBufferLength(values); this.allocSafe(totalLength); this.writeU16(values.length, be); for (let i = 0; i < values.length; i++) { this.writeU32(values[i].length, be); this.writeBytes(values[i]); } } writeAddressArray(value) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length); for (let i = 0; i < value.length; i++) { this.writeAddress(value[i]); } } writeU32Array(value, be = true) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length, be); for (let i = 0; i < value.length; i++) { this.writeU32(value[i], be); } } writeU256Array(value, be = true) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length, be); for (let i = 0; i < value.length; i++) { this.writeU256(value[i], be); } } writeU128Array(value, be = true) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length, be); for (let i = 0; i < value.length; i++) { this.writeU128(value[i], be); } } writeStringArray(value) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length); for (let i = 0; i < value.length; i++) { this.writeStringWithLength(value[i]); } } writeU16Array(value, be = true) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length, be); for (let i = 0; i < value.length; i++) { this.writeU16(value[i], be); } } writeU8Array(value) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length); for (let i = 0; i < value.length; i++) { this.writeU8(value[i]); } } writeU64Array(value, be = true) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length, be); for (let i = 0; i < value.length; i++) { this.writeU64(value[i], be); } } writeBytesArray(value) { if (value.length > 65535) throw new Error('Array size is too large'); this.writeU16(value.length); for (let i = 0; i < value.length; i++) { this.writeBytesWithLength(value[i]); } } verifyAddress(pubKey) { if (pubKey.byteLength > ADDRESS_BYTE_LENGTH) { throw new Error(`Address is too long ${pubKey.byteLength} > ${ADDRESS_BYTE_LENGTH} bytes`); } } resize(size) { const buf = new Uint8Array(this.buffer.byteLength + size); for (let i = 0; i < this.buffer.byteLength; i++) { buf[i] = this.buffer.getUint8(i); } this.buffer = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); } getDefaultBuffer(length = 0) { return new DataView(new ArrayBuffer(length)); } }