Skip to content

Commit

Permalink
Added event decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
BlobMaster41 committed May 16, 2024
1 parent 1b815fe commit 0becc4a
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 19 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"type": "module",
"name": "@btc-vision/bsi-binary",
"version": "1.0.11",
"version": "1.0.12",
"description": "",
"main": "build/index.js",
"types": "build/index.d.ts",
Expand Down
25 changes: 17 additions & 8 deletions src/buffer/BinaryReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ADDRESS_BYTE_LENGTH,
ContractABIMap,
f32,
i32,
i32, MAX_EVENT_DATA_SIZE, MAX_EVENTS,
MethodMap,
PointerStorage,
PropertyABIMap,
Expand Down Expand Up @@ -33,7 +33,11 @@ export class BinaryReader {

public readEvents(): NetEvent[] {
const events: NetEvent[] = [];
const length = this.readU32();
const length = this.readU8();

if(length > MAX_EVENTS) {
throw new Error('Too many events to decode.');
}

for (let i = 0; i < length; i++) {
const event = this.readEvent();
Expand All @@ -46,14 +50,19 @@ export class BinaryReader {

public readEvent(): NetEvent {
const eventType = this.readStringWithLength();
const eventData = this.readBytesWithLength();
const eventDataSelector = this.readU64();
const eventData = this.readBytesWithLength(MAX_EVENT_DATA_SIZE);

return new NetEvent(eventType, eventData);
return new NetEvent(eventType, eventDataSelector, eventData);
}

public readBytesWithLength(): Uint8Array {
public readBytesWithLength(maxLength: number = 0): Uint8Array {
const length = this.readU32();

if(maxLength > 0 && length > maxLength) {
throw new Error('Data length exceeds maximum length.');
}

return this.readBytes(length);
}

Expand Down Expand Up @@ -155,10 +164,10 @@ export class BinaryReader {
}

public readU64(): bigint {
const low = BigInt(this.readU32());
const high = BigInt(this.readU32());
const val = this.buffer.getBigUint64(this.currentOffset, true);
this.currentOffset += 8;

return (BigInt(high) << 32n) | low;
return val;
}

public readStorage(): Map<Address, PointerStorage> {
Expand Down
60 changes: 52 additions & 8 deletions src/buffer/BinaryWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,72 @@ import {

import { BufferHelper } from '../utils/BufferHelper.js';
import { BinaryReader } from './BinaryReader.js';
import { cyrb53a } from '../utils/cyrb53.js';

export enum BufferDataType {
U8 = 0,
U16 = 1,
U32 = 2,
U64 = 3,
U256 = 4,
ADDRESS = 5,
STRING = 6,
BOOLEAN = 7,
}

export class BinaryWriter {
private currentOffset: u32 = 0;
private buffer: DataView = new DataView(new ArrayBuffer(ADDRESS_BYTE_LENGTH));
private buffer: DataView;

private selectorDatatype: u8[] = [];

constructor() {}
constructor(length: number = 4, private readonly trackDataTypes: boolean = false) {
this.buffer = this.getDefaultBuffer(length);
}

public writeU8(value: u8): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.U8);

this.allocSafe(1);
this.buffer.setUint8(this.currentOffset++, value);
}

public writeU16(value: u16): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.U16);

this.allocSafe(2);
this.buffer.setUint16(this.currentOffset, value, true);
this.currentOffset += 2;
}

public writeU32(value: u32, le: boolean = true): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.U32);

this.allocSafe(4);
this.buffer.setUint32(this.currentOffset, value, le);
this.currentOffset += 4;
}

public writeU64(value: u64): void {
this.writeU32(Number(value));
this.writeU32(Number(value >> 32n));
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.U64);

this.allocSafe(8);
this.buffer.setBigUint64(this.currentOffset, value, true);
}

public writeSelector(value: Selector): void {
this.writeU32(value, false);
}

public writeBoolean(value: boolean): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.BOOLEAN);

this.writeU8(value ? 1 : 0);
}

public writeU256(bigIntValue: bigint): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.U256);

const bytesToHex = BufferHelper.valueToUint8Array(bigIntValue);
if (bytesToHex.byteLength !== 32) {
console.log('Invalid u256 value:', bytesToHex);
Expand All @@ -73,12 +101,16 @@ export class BinaryWriter {
}

public writeString(value: string): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.STRING);

for (let i: i32 = 0; i < value.length; i++) {
this.writeU8(value.charCodeAt(i));
}
}

public writeAddress(value: Address): void {
if(this.trackDataTypes) this.selectorDatatype.push(BufferDataType.ADDRESS);

const bytes = this.fromAddress(value);

this.writeBytes(bytes);
Expand Down Expand Up @@ -112,21 +144,20 @@ export class BinaryWriter {
);
}

public getBuffer(): Uint8Array {
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);
}

this.clear();
if(clear) this.clear();

return buf;
}

public reset(): void {
this.currentOffset = 0;

this.buffer = new DataView(new ArrayBuffer(4));
this.buffer = this.getDefaultBuffer(4);
}

public writeStorage(storage: BlockchainStorage): void {
Expand Down Expand Up @@ -181,6 +212,7 @@ export class BinaryWriter {
public clear(): void {
this.currentOffset = 0;
this.buffer = new DataView(new ArrayBuffer(4));
this.selectorDatatype = [];
}

public allocSafe(size: u32): void {
Expand All @@ -194,6 +226,14 @@ export class BinaryWriter {
this.writeSelector(selector);
}

public getSelectorDataType(): bigint {
let hash: bigint = 0n;

if (this.selectorDatatype.length === 0) return hash;

return cyrb53a(this.selectorDatatype);
}

private getChecksum(): u32 {
let checksum: u32 = 0;
for (let i = 0; i < this.buffer.byteLength; i++) {
Expand Down Expand Up @@ -245,4 +285,8 @@ export class BinaryWriter {

this.buffer = new DataView(buf.buffer);
}

private getDefaultBuffer(length: number = 0): DataView {
return new DataView(new ArrayBuffer(length));
}
}
3 changes: 3 additions & 0 deletions src/buffer/types/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ export type ContractABIMap = Set<Selector>;
export type PropertyABIMap = Map<string, Selector>;
export type SelectorsMap = Map<Address, PropertyABIMap>;
export type MethodMap = Map<Address, ContractABIMap>;

export const MAX_EVENT_DATA_SIZE: number = 256; // 256 bytes max
export const MAX_EVENTS: number = 8; // 8 events max per transactions.
5 changes: 3 additions & 2 deletions src/events/NetEvent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export class NetEvent {
public constructor(
public eventType: string,
public eventData: Uint8Array,
public readonly eventType: string,
public readonly eventDataSelector: bigint,
public readonly eventData: Uint8Array,
) {}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './buffer/BinaryReader.js';
export * from './events/NetEvent.js';
export * from './utils/BufferHelper.js';
export * from './abi/ABICoder.js';
export * from './utils/cyrb53.js';
71 changes: 71 additions & 0 deletions src/utils/cyrb53.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
cyrb53 (c) 2018 bryc (github.com/bryc)
License: Public domain (or MIT if needed). Attribution appreciated.
A fast and simple 53-bit string hash function with decent collision resistance.
Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
*/
import { u8 } from '../buffer/types/math.js';

export const cyrb53 = (str: string, seed: number = 0) => {
let h1 = 0xdeadbeef ^ seed,
h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}

h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);

return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};

export function imul64(a: bigint, b: bigint): bigint {
// Get the low and high parts of a
const aLow = BigInt.asUintN(32, a);
const aHigh = a >> 32n;

// Get the low and high parts of b
const bLow = BigInt.asUintN(32, b);
const bHigh = b >> 32n;

// Calculate the low part of the result
const low = aLow * bLow;

// Calculate the middle parts of the result
const middle1 = (aHigh * bLow) << 32n;
const middle2 = (aLow * bHigh) << 32n;

// Calculate the high part of the result
const high = (aHigh * bHigh) << 64n;

// Add the parts together
return low + middle1 + middle2 + high;
}

/*
cyrb53a beta (c) 2023 bryc (github.com/bryc)
License: Public domain (or MIT if needed). Attribution appreciated.
This is a work-in-progress, and changes to the algorithm are expected.
The original cyrb53 has a slight mixing bias in the low bits of h1.
This doesn't affect collision rate, but I want to try to improve it.
This new version has preliminary improvements in avalanche behavior.
*/
export const cyrb53a = function (str: u8[], seed: number = 0): bigint {
let h1 = BigInt(0xdeadbeef ^ seed);
let h2 = BigInt(0x41c6ce57 ^ seed);

for (let i = 0, ch; i < str.length; i++) {
ch = BigInt(str[i]);
h1 = imul64(h1 ^ ch, 0x85ebca77n);
h2 = imul64(h2 ^ ch, 0xc2b2ae3dn);
}

h1 ^= imul64(h1 ^ (h2 >> 15n), 0x735a2d97n);
h2 ^= imul64(h2 ^ (h1 >> 15n), 0xcaf649a9n);
h1 ^= h2 >> 16n;
h2 ^= h1 >> 16n;

return (2097152n * (h2 & 0xFFFFFFFFFFFFFFFFn) + (h1 >> 11n)) & 0xFFFFFFFFFFFFFFFFn;
};

0 comments on commit 0becc4a

Please sign in to comment.