@btc-vision/btc-runtime
Version:
Bitcoin L1 Smart Contract Runtime for OP_NET. Build decentralized applications on Bitcoin using AssemblyScript and WebAssembly. Fully audited.
166 lines (146 loc) • 4.99 kB
text/typescript
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 StoredU64
* @description Manages up to four u64 values within a single u256 storage slot.
*/
export class StoredU64 {
private readonly bufferPointer: Uint8Array;
// Internal cache for four u64 values: [lo1, lo2, hi1, hi2]
private _values: u64[] = [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,
) {
this.bufferPointer = encodePointer(pointer, subPointer, true, 'StoredU64');
}
/**
* @method get
* @description Retrieves the u64 value at the specified offset.
* @param {u8} index - The index (0 to 3) of the u64 value to retrieve.
* @returns {u64} - The u64 value at the specified index.
*/
public get(index: u8): u64 {
assert(index < 4, 'Index out of bounds for StoredU64 (0-3)');
this.ensureValues();
return this._values[index];
}
/**
* @method set
* @description Sets the u64 value at the specified offset.
* @param {u8} index - The index (0 to 3) of the u64 value to set.
* @param {u64} value - The u64 value to assign.
*/
public set(index: u8, value: u64): void {
assert(index < 4, 'Index out of bounds for StoredU64 (0-3)');
this.ensureValues();
if (this._values[index] != value) {
this._values[index] = value;
this.isChanged = true;
}
}
/**
* @method save
* @description Persists the cached u64 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 u64 values at once.
* @param {[u64, u64, u64, u64]} values - An array of four u64 values to set.
*/
public setMultiple(values: u64[]): void {
this.ensureValues();
let changed = false;
for (let i: u8 = 0; i < 4; i++) {
if (this._values[i] != values[i]) {
this._values[i] = values[i];
changed = true;
}
}
if (changed) {
this.isChanged = true;
}
}
/**
* @method getAll
* @description Retrieves all four u64 values as a tuple.
* @returns {[u64, u64, u64, u64]} - A tuple containing all four u64 values.
*/
public getAll(): u64[] {
this.ensureValues();
return this._values;
}
/**
* @method toString
* @description Returns a string representation of all four u64 values.
* @returns {string} - A string in the format "[value0, value1, value2, value3]".
*/
public toString(): string {
this.ensureValues();
return `[${this._values[0].toString()}, ${this._values[1].toString()}, ${this._values[2].toString()}, ${this._values[3].toString()}]`;
}
/**
* @method reset
* @description Resets the cached values to default and marks as changed.
*/
public reset(): void {
this._values = [0, 0, 0, 0];
this.isChanged = true;
}
/**
* @private
* @method ensureValues
* @description Loads and unpacks the u256 value from storage into four u64 cache variables.
*/
private ensureValues(): void {
if (!this.isLoaded) {
const storedU256: Uint8Array = Blockchain.getStorageAt(this.bufferPointer);
const reader = new BytesReader(storedU256);
this._values[0] = reader.readU64();
this._values[1] = reader.readU64();
this._values[2] = reader.readU64();
this._values[3] = reader.readU64();
this.isLoaded = true;
}
}
/**
* @private
* @method packValues
* @description Packs the four cached u64 values into a single u256 for storage.
* @returns {u256} - The packed u256 value.
*/
private packValues(): Uint8Array {
const writer = new BytesWriter(32);
writer.writeU64(this._values[0]);
writer.writeU64(this._values[1]);
writer.writeU64(this._values[2]);
writer.writeU64(this._values[3]);
return writer.getBuffer();
}
}