UNPKG

@btc-vision/transaction

Version:

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

342 lines (264 loc) 10.3 kB
import { AddressMap } from '../deterministic/AddressMap.js'; import { Address } from '../keypair/Address.js'; 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 { i32, Selector, u16, u32, u64, u8 } from '../utils/types.js'; import { BinaryReader } from './BinaryReader.js'; export class BinaryWriter { private currentOffset: u32 = 0; private buffer: DataView; constructor(length: number = 0) { this.buffer = this.getDefaultBuffer(length); } public writeU8(value: u8): void { if (value > 255) throw new Error('u8 value is too large.'); this.allocSafe(U8_BYTE_LENGTH); this.buffer.setUint8(this.currentOffset++, value); } public writeU16(value: u16, be: boolean = true): void { 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; } public writeU32(value: u32, be: boolean = true): void { 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; } public writeU64(value: u64, be: boolean = true): void { 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; } public writeSelector(value: Selector): void { this.writeU32(value, true); } public writeBoolean(value: boolean): void { this.writeU8(value ? 1 : 0); } public writeI128(bigIntValue: bigint, be: boolean = true): void { 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]); } } } public writeU256(bigIntValue: bigint, be: boolean = true): void { 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]); } } } public writeU128(bigIntValue: bigint, be: boolean = true): void { 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]); } } } public writeBytes(value: Uint8Array | Buffer): void { this.allocSafe(value.byteLength); for (let i = 0; i < value.byteLength; i++) { this.writeU8(value[i]); } } public writeString(value: string): void { const encoder = new TextEncoder(); const bytes = encoder.encode(value); this.allocSafe(bytes.length); this.writeBytes(bytes); } public writeStringWithLength(value: string): void { const encoder = new TextEncoder(); const bytes = encoder.encode(value); this.allocSafe(U32_BYTE_LENGTH + bytes.length); this.writeU32(bytes.length); this.writeBytes(bytes); } public writeAddress(value: Address): void { this.verifyAddress(value); this.writeBytes(value); } public getBuffer(clear: boolean = true): Uint8Array { const buf = new Uint8Array(this.buffer.byteLength); for (let i: u32 = 0; i < this.buffer.byteLength; i++) { buf[i] = this.buffer.getUint8(i); } if (clear) this.clear(); return buf; } public reset(): void { this.currentOffset = 0; this.buffer = this.getDefaultBuffer(4); } public toBytesReader(): BinaryReader { return new BinaryReader(this.getBuffer()); } public getOffset(): u32 { return this.currentOffset; } public setOffset(offset: u32): void { this.currentOffset = offset; } public clear(): void { this.currentOffset = 0; this.buffer = this.getDefaultBuffer(); } public allocSafe(size: u32): void { if (this.currentOffset + size > this.buffer.byteLength) { this.resize(size); } } public writeAddressValueTuple(map: AddressMap<bigint>, be: boolean = true): void { 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); } } public writeBytesWithLength(value: Uint8Array): void { this.writeU32(value.length); this.writeBytes(value); } public writeAddressArray(value: Address[]): void { 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]); } } public writeU32Array(value: u32[], be: boolean = true): void { 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); } } public writeU256Array(value: bigint[], be: boolean = true): void { 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); } } public writeU128Array(value: bigint[], be: boolean = true): void { 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); } } public writeStringArray(value: string[]): void { 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]); } } public writeU16Array(value: u16[], be: boolean = true): void { 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); } } public writeU8Array(value: u8[]): void { 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]); } } public writeU64Array(value: bigint[], be: boolean = true): void { 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); } } public writeBytesArray(value: Uint8Array[]): void { 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]); } } private verifyAddress(pubKey: Address): void { if (pubKey.byteLength > ADDRESS_BYTE_LENGTH) { throw new Error( `Address is too long ${pubKey.byteLength} > ${ADDRESS_BYTE_LENGTH} bytes`, ); } } private resize(size: u32): void { const buf: Uint8Array = new Uint8Array(this.buffer.byteLength + size); for (let i: i32 = 0; i < this.buffer.byteLength; i++) { buf[i] = this.buffer.getUint8(i); } this.buffer = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); } private getDefaultBuffer(length: number = 0): DataView { return new DataView(new ArrayBuffer(length)); } }