UNPKG

@btc-vision/btc-runtime

Version:

Bitcoin Smart Contract Runtime

179 lines (158 loc) 5.69 kB
import { u256 } from '@btc-vision/as-bignum/assembly'; import { BytesWriter } from '../buffer/BytesWriter'; import { Blockchain } from '../env'; import { encodePointer } from '../math/abi'; import { BytesReader } from '../buffer/BytesReader'; /** * @class StoredU32 * @description Manages up to height u32 values within a single u256 storage slot. */ @final export class StoredU32 { private readonly bufferPointer: Uint8Array; // Internal cache for four u32 values private _values: u32[] = [0, 0, 0, 0, 0, 0, 0, 0]; // Flag to indicate if values are loaded from storage private isLoaded: bool = false; // Flag to indicate if any value has been changed private isChanged: bool = false; /** * @constructor * @param {u16} pointer - The primary pointer identifier. * @param {Uint8Array} subPointer - The sub-pointer for memory slot addressing. */ constructor( public pointer: u16, public subPointer: Uint8Array, ) { assert( subPointer.length <= 30, `You must pass a 30 bytes sub-pointer. (StoredU32, got ${subPointer.length})`, ); this.bufferPointer = encodePointer(pointer, subPointer); } /** * @method get * @description Retrieves the u32 value at the specified offset. * @param {u8} index - The index (0 to 7) of the u32 value to retrieve. * @returns {u32} - The u32 value at the specified index. */ @inline public get(index: u8): u32 { assert(index < 8, 'Index out of bounds for StoredU32 (0-7)'); this.ensureValues(); return this._values[index]; } /** * @method set * @description Sets the u32 value at the specified offset. * @param {u8} index - The index (0 to 7) of the u32 value to set. * @param {u32} value - The u32 value to assign. */ @inline public set(index: u8, value: u32): void { assert(index < 8, 'Index out of bounds for StoredU32 (0-7)'); this.ensureValues(); if (this._values[index] != value) { this._values[index] = value; this.isChanged = true; } } /** * @method save * @description Persists the cached u32 values to storage if any have been modified. */ public save(): void { if (this.isChanged) { const packed = this.packValues(); Blockchain.setStorageAt(this.bufferPointer, packed); this.isChanged = false; } } /** * @method setMultiple * @description Sets multiple u32 values at once. * @param {[u32, u32, u32, u32,u32, u32, u32, u32]} values - An array of four u32 values to set. */ @inline public setMultiple(values: u32[]): void { this.ensureValues(); let changed = false; for (let i: u8 = 0; i < 8; i++) { if (this._values[i] != values[i]) { this._values[i] = values[i]; changed = true; } } if (changed) { this.isChanged = true; } } /** * @method getAll * @description Retrieves all height u32 values as a tuple. * @returns {[u32, u32, u32, u32,u32, u32, u32, u32]} - A tuple containing all four u32 values. */ @inline public getAll(): u32[] { this.ensureValues(); return this._values; } /** * @method toString * @description Returns a string representation of all height u32 values. * @returns {string} - A string in the format "[value0, value1, value2, value3, value4, value5, value6, value7]". */ @inline public toString(): string { this.ensureValues(); return `[${this._values[0].toString()}, ${this._values[1].toString()}, ${this._values[2].toString()}, ${this._values[3].toString()},, ${this._values[4].toString()},, ${this._values[5].toString()},, ${this._values[6].toString()},, ${this._values[7].toString()}]`; } /** * @method reset * @description Resets the cached values to default and marks as changed. */ @inline public reset(): void { this._values = [0, 0, 0, 0, 0, 0, 0, 0]; this.isChanged = true; } /** * @private * @method ensureValues * @description Loads and unpacks the u256 value from storage into height u32 cache variables. */ private ensureValues(): void { if (!this.isLoaded) { const storedU256: Uint8Array = Blockchain.getStorageAt(this.bufferPointer); const reader = new BytesReader(storedU256); this._values[0] = reader.readU32(); this._values[1] = reader.readU32(); this._values[2] = reader.readU32(); this._values[3] = reader.readU32(); this._values[4] = reader.readU32(); this._values[5] = reader.readU32(); this._values[6] = reader.readU32(); this._values[7] = reader.readU32(); this.isLoaded = true; } } /** * @private * @method packValues * @description Packs the height cached u32 values into a single u256 for storage. * @returns {u256} - The packed u256 value. */ private packValues(): Uint8Array { const writer = new BytesWriter(32); writer.writeU32(this._values[0]); writer.writeU32(this._values[1]); writer.writeU32(this._values[2]); writer.writeU32(this._values[3]); writer.writeU32(this._values[4]); writer.writeU32(this._values[5]); writer.writeU32(this._values[6]); writer.writeU32(this._values[7]); return writer.getBuffer(); } }