@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
244 lines (243 loc) • 7.77 kB
JavaScript
import { AddressMap } from '../deterministic/AddressMap.js';
import { Address } from '../keypair/Address.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';
export class BinaryReader {
constructor(bytes) {
this.currentOffset = 0;
this.buffer = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
}
static stringCompare(a, b) {
return a.localeCompare(b);
}
static bigintCompare(a, b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
static numberCompare(a, b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
setBuffer(bytes) {
this.buffer = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
this.currentOffset = 0;
}
length() {
return this.buffer.byteLength;
}
bytesLeft() {
return this.buffer.byteLength - this.currentOffset;
}
readU8() {
this.verifyEnd(this.currentOffset + U8_BYTE_LENGTH);
const value = this.buffer.getUint8(this.currentOffset);
this.currentOffset += U8_BYTE_LENGTH;
return value;
}
readU16(be = true) {
this.verifyEnd(this.currentOffset + U16_BYTE_LENGTH);
const value = this.buffer.getUint16(this.currentOffset, !be);
this.currentOffset += U16_BYTE_LENGTH;
return value;
}
readU32(be = true) {
this.verifyEnd(this.currentOffset + U32_BYTE_LENGTH);
const value = this.buffer.getUint32(this.currentOffset, !be);
this.currentOffset += U32_BYTE_LENGTH;
return value;
}
readU64(be = true) {
this.verifyEnd(this.currentOffset + U64_BYTE_LENGTH);
const value = this.buffer.getBigUint64(this.currentOffset, !be);
this.currentOffset += U64_BYTE_LENGTH;
return value;
}
readU128(be = true) {
const raw = this.readBytes(U128_BYTE_LENGTH);
let bytes = raw;
if (!be) {
bytes = this.reverseBytes(raw);
}
return BigInt('0x' + this.toHexString(bytes));
}
readU256(be = true) {
const raw = this.readBytes(U256_BYTE_LENGTH);
let bytes = raw;
if (!be) {
bytes = this.reverseBytes(raw);
}
return BigInt('0x' + this.toHexString(bytes));
}
readI128(be = true) {
const raw = this.readBytes(I128_BYTE_LENGTH);
let bytes = raw;
if (!be) {
bytes = this.reverseBytes(raw);
}
let value = BigInt('0x' + this.toHexString(bytes));
const signBitMask = 0x80;
if (bytes[0] & signBitMask) {
const twoTo128 = BigInt(1) << BigInt(128);
value = value - twoTo128;
}
return value;
}
readBoolean() {
return this.readU8() !== 0;
}
readSelector() {
return this.readU32(true);
}
readBytes(length, zeroStop = false) {
this.verifyEnd(this.currentOffset + length);
let bytes = new Uint8Array(length);
for (let i = 0; i < length; i++) {
const b = this.buffer.getUint8(this.currentOffset++);
if (zeroStop && b === 0) {
bytes = bytes.subarray(0, i);
break;
}
bytes[i] = b;
}
return bytes;
}
readString(length) {
const textDecoder = new TextDecoder();
const bytes = this.readBytes(length, false);
return textDecoder.decode(bytes);
}
readStringWithLength(be = true) {
const length = this.readU32(be);
return this.readString(length);
}
readAddress() {
const bytes = Array.from(this.readBytes(ADDRESS_BYTE_LENGTH));
return new Address(bytes);
}
readBytesWithLength(maxLength = 0, be = true) {
const length = this.readU32(be);
if (maxLength > 0 && length > maxLength) {
throw new Error('Data length exceeds maximum length.');
}
return this.readBytes(length);
}
readArrayOfBuffer(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readBytesWithLength();
}
return result;
}
readAddressArray(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readAddress();
}
return result;
}
readU256Array(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readU256(be);
}
return result;
}
readU128Array(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readU128(be);
}
return result;
}
readU64Array(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readU64(be);
}
return result;
}
readU32Array(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readU32(be);
}
return result;
}
readU16Array(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readU16(be);
}
return result;
}
readU8Array() {
const length = this.readU16(true);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readU8();
}
return result;
}
readStringArray(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readStringWithLength(be);
}
return result;
}
readBytesArray(be = true) {
const length = this.readU16(be);
const result = new Array(length);
for (let i = 0; i < length; i++) {
result[i] = this.readBytesWithLength(0, be);
}
return result;
}
readAddressValueTuple(be = true) {
const length = this.readU16(be);
const result = new AddressMap();
for (let i = 0; i < length; i++) {
const address = this.readAddress();
const value = this.readU256(be);
if (result.has(address)) {
throw new Error('Duplicate address found in map');
}
result.set(address, value);
}
return result;
}
getOffset() {
return this.currentOffset;
}
setOffset(offset) {
this.currentOffset = offset;
}
verifyEnd(size) {
if (size > this.buffer.byteLength) {
throw new Error(`Attempt to read beyond buffer length: requested up to byte offset ${size}, but buffer is only ${this.buffer.byteLength} bytes.`);
}
}
reverseBytes(bytes) {
const out = new Uint8Array(bytes.length);
for (let i = 0; i < bytes.length; i++) {
out[i] = bytes[bytes.length - 1 - i];
}
return out;
}
toHexString(bytes) {
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
}
}