@typeberry/lib 0.2.0-b6667ce → 0.2.0-c3df163

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/index.cjs +0 -3
  2. package/index.d.ts +772 -777
  3. package/index.js +0 -3
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -14196,6 +14196,82 @@ interface GasCounter {
14196
14196
  sub(g: Gas): boolean;
14197
14197
  }
14198
14198
 
14199
+ declare const NO_OF_REGISTERS$1 = 13;
14200
+
14201
+ type RegisterIndex = Opaque<number, "register index">;
14202
+
14203
+ declare class Registers {
14204
+ private asSigned: BigInt64Array;
14205
+ private asUnsigned: BigUint64Array;
14206
+
14207
+ constructor(private readonly bytes = safeAllocUint8Array(NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
14208
+ check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
14209
+ this.asSigned = new BigInt64Array(bytes.buffer, bytes.byteOffset);
14210
+ this.asUnsigned = new BigUint64Array(bytes.buffer, bytes.byteOffset);
14211
+ }
14212
+
14213
+ static fromBytes(bytes: Uint8Array) {
14214
+ check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
14215
+ return new Registers(bytes);
14216
+ }
14217
+
14218
+ getBytesAsLittleEndian(index: number, len: number) {
14219
+ const offset = index << REGISTER_SIZE_SHIFT;
14220
+ return this.bytes.subarray(offset, offset + len);
14221
+ }
14222
+
14223
+ getAllBytesAsLittleEndian() {
14224
+ return this.bytes;
14225
+ }
14226
+
14227
+ copyFrom(regs: Registers | BigUint64Array) {
14228
+ const array = regs instanceof BigUint64Array ? regs : regs.asUnsigned;
14229
+ this.asUnsigned.set(array);
14230
+ }
14231
+
14232
+ reset() {
14233
+ for (let i = 0; i < NO_OF_REGISTERS; i++) {
14234
+ this.asUnsigned[i] = 0n;
14235
+ }
14236
+ }
14237
+
14238
+ getLowerU32(registerIndex: number) {
14239
+ return Number(this.asUnsigned[registerIndex] & 0xff_ff_ff_ffn);
14240
+ }
14241
+
14242
+ getLowerI32(registerIndex: number) {
14243
+ return Number(this.getLowerU32(registerIndex)) >> 0;
14244
+ }
14245
+
14246
+ setU32(registerIndex: number, value: number) {
14247
+ this.asUnsigned[registerIndex] = signExtend32To64(value);
14248
+ }
14249
+
14250
+ setI32(registerIndex: number, value: number) {
14251
+ this.asSigned[registerIndex] = signExtend32To64(value);
14252
+ }
14253
+
14254
+ getU64(registerIndex: number) {
14255
+ return this.asUnsigned[registerIndex];
14256
+ }
14257
+
14258
+ getI64(registerIndex: number) {
14259
+ return this.asSigned[registerIndex];
14260
+ }
14261
+
14262
+ setU64(registerIndex: number, value: bigint) {
14263
+ this.asUnsigned[registerIndex] = value;
14264
+ }
14265
+
14266
+ setI64(registerIndex: number, value: bigint) {
14267
+ this.asSigned[registerIndex] = value;
14268
+ }
14269
+
14270
+ getAllU64() {
14271
+ return this.asUnsigned;
14272
+ }
14273
+ }
14274
+
14199
14275
  /**
14200
14276
  * Mask class is an implementation of skip function defined in GP.
14201
14277
  *
@@ -14365,82 +14441,6 @@ declare class ImmediateDecoder {
14365
14441
  }
14366
14442
  }
14367
14443
 
14368
- declare const NO_OF_REGISTERS$1 = 13;
14369
-
14370
- type RegisterIndex = Opaque<number, "register index">;
14371
-
14372
- declare class Registers {
14373
- private asSigned: BigInt64Array;
14374
- private asUnsigned: BigUint64Array;
14375
-
14376
- constructor(private readonly bytes = safeAllocUint8Array(NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
14377
- check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
14378
- this.asSigned = new BigInt64Array(bytes.buffer, bytes.byteOffset);
14379
- this.asUnsigned = new BigUint64Array(bytes.buffer, bytes.byteOffset);
14380
- }
14381
-
14382
- static fromBytes(bytes: Uint8Array) {
14383
- check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
14384
- return new Registers(bytes);
14385
- }
14386
-
14387
- getBytesAsLittleEndian(index: number, len: number) {
14388
- const offset = index << REGISTER_SIZE_SHIFT;
14389
- return this.bytes.subarray(offset, offset + len);
14390
- }
14391
-
14392
- getAllBytesAsLittleEndian() {
14393
- return this.bytes;
14394
- }
14395
-
14396
- copyFrom(regs: Registers | BigUint64Array) {
14397
- const array = regs instanceof BigUint64Array ? regs : regs.asUnsigned;
14398
- this.asUnsigned.set(array);
14399
- }
14400
-
14401
- reset() {
14402
- for (let i = 0; i < NO_OF_REGISTERS; i++) {
14403
- this.asUnsigned[i] = 0n;
14404
- }
14405
- }
14406
-
14407
- getLowerU32(registerIndex: number) {
14408
- return Number(this.asUnsigned[registerIndex] & 0xff_ff_ff_ffn);
14409
- }
14410
-
14411
- getLowerI32(registerIndex: number) {
14412
- return Number(this.getLowerU32(registerIndex)) >> 0;
14413
- }
14414
-
14415
- setU32(registerIndex: number, value: number) {
14416
- this.asUnsigned[registerIndex] = signExtend32To64(value);
14417
- }
14418
-
14419
- setI32(registerIndex: number, value: number) {
14420
- this.asSigned[registerIndex] = signExtend32To64(value);
14421
- }
14422
-
14423
- getU64(registerIndex: number) {
14424
- return this.asUnsigned[registerIndex];
14425
- }
14426
-
14427
- getI64(registerIndex: number) {
14428
- return this.asSigned[registerIndex];
14429
- }
14430
-
14431
- setU64(registerIndex: number, value: bigint) {
14432
- this.asUnsigned[registerIndex] = value;
14433
- }
14434
-
14435
- setI64(registerIndex: number, value: bigint) {
14436
- this.asSigned[registerIndex] = value;
14437
- }
14438
-
14439
- getAllU64() {
14440
- return this.asUnsigned;
14441
- }
14442
- }
14443
-
14444
14444
  declare class NibblesDecoder {
14445
14445
  private byte = new Int8Array(1);
14446
14446
 
@@ -17877,870 +17877,865 @@ declare namespace index$8 {
17877
17877
  export type { index$8_BigGas as BigGas, index$8_Gas as Gas, index$8_GasCounter as GasCounter, index$8_InterpreterOptions as InterpreterOptions, index$8_MemoryIndex as MemoryIndex, index$8_SbrkIndex as SbrkIndex, index$8_SmallGas as SmallGas };
17878
17878
  }
17879
17879
 
17880
- /**
17881
- * Program counter is a 64-bit unsigned integer that points to the next instruction
17882
- *
17883
- * https://graypaper.fluffylabs.dev/#/1c979cb/2e3f012e3f01?v=0.7.1
17884
- */
17885
- type ProgramCounter = Opaque<U64, "ProgramCounter[u64]">;
17886
- /** Convert a number into ProgramCounter. */
17887
- declare const tryAsProgramCounter = (v: number | bigint): ProgramCounter => asOpaqueType(tryAsU64(v));
17880
+ interface IHostCallMemory {
17881
+ storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds>;
17882
+ loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds>;
17883
+ }
17888
17884
 
17889
- /** Running PVM instance identifier. */
17890
- type MachineId = Opaque<U64, "MachineId[u64]">;
17891
- /** Convert a number into PVM instance identifier. */
17892
- declare const tryAsMachineId = (v: number | bigint): MachineId => asOpaqueType(tryAsU64(v));
17885
+ declare class HostCallMemory implements IHostCallMemory {
17886
+ constructor(private readonly memory: Memory) {}
17893
17887
 
17894
- declare class MachineInstance {
17895
- async run(gas: BigGas, registers: Registers): Promise<MachineResult> {
17896
- return {
17897
- result: {
17898
- status: Status.OK,
17899
- },
17900
- gas,
17901
- registers,
17902
- };
17888
+ storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds> {
17889
+ if (bytes.length === 0) {
17890
+ return Result.ok(OK);
17891
+ }
17892
+
17893
+ if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
17894
+ return Result.error(new OutOfBounds());
17895
+ }
17896
+
17897
+ return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
17903
17898
  }
17904
- }
17905
17899
 
17906
- type MachineStatus =
17907
- | {
17908
- status: typeof Status.HOST;
17909
- hostCallIndex: U64;
17900
+ loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds> {
17901
+ if (result.length === 0) {
17902
+ return Result.ok(OK);
17910
17903
  }
17911
- | {
17912
- status: typeof Status.FAULT;
17913
- address: U64;
17904
+
17905
+ if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
17906
+ return Result.error(new OutOfBounds());
17914
17907
  }
17915
- | {
17916
- status: typeof Status.OK | typeof Status.HALT | typeof Status.PANIC | typeof Status.OOG;
17917
- };
17918
17908
 
17919
- /** Data returned by a machine invocation. */
17920
- type MachineResult = {
17921
- result: MachineStatus;
17922
- gas: BigGas;
17923
- registers: Registers;
17924
- };
17909
+ return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
17910
+ }
17911
+ }
17925
17912
 
17926
- /** Types of possbile operations to request by Pages host call. */
17927
- declare enum MemoryOperation {
17928
- /** Zeroes memory and set access to unreadable. */
17929
- Void = 0,
17930
- /** Zeroes memory and set access to read-only. */
17931
- ZeroRead = 1,
17932
- /** Zeroes memory and set access to read-write. */
17933
- ZeroWrite = 2,
17934
- /** Preserve memory and set access to read-only. */
17935
- Read = 3,
17936
- /** Preserve memory and set access to read-write. */
17937
- Write = 4,
17913
+ interface IHostCallRegisters {
17914
+ get(registerIndex: number): U64;
17915
+ set(registerIndex: number, value: U64): void;
17938
17916
  }
17939
17917
 
17940
- /** Convert a number into MemoryOperation or null (if invalid). */
17941
- declare const toMemoryOperation = (v: number | bigint): MemoryOperation | null =>
17942
- v <= MemoryOperation.Write && v >= MemoryOperation.Void ? Number(v) : null;
17918
+ declare class HostCallRegisters implements IHostCallRegisters {
17919
+ constructor(private readonly registers: Registers) {}
17943
17920
 
17944
- /** An error that may occur during `peek` or `poke` host call. */
17945
- declare enum PeekPokeError {
17946
- /** Source page fault. */
17947
- SourcePageFault = 0,
17948
- /** Destination page fault. */
17949
- DestinationPageFault = 1,
17950
- /** No machine under given machine index. */
17951
- NoMachine = 2,
17952
- }
17921
+ get(registerIndex: number): U64 {
17922
+ return tryAsU64(this.registers.getU64(registerIndex));
17923
+ }
17953
17924
 
17954
- declare enum ZeroVoidError {
17955
- /** No machine under given machine index. */
17956
- NoMachine = 0,
17957
- /** Attempting to void or zero non-accessible page. */
17958
- InvalidPage = 1,
17925
+ set(registerIndex: number, value: U64) {
17926
+ this.registers.setU64(registerIndex, value);
17927
+ }
17959
17928
  }
17960
17929
 
17961
- declare enum PagesError {
17962
- /** No machine under given machine index. */
17963
- NoMachine = 0,
17964
- /** Invalid memory operation. */
17965
- InvalidOperation = 1,
17966
- /** Attempting to change non-accessible page or trying to preserve value of voided page. */
17967
- InvalidPage = 2,
17968
- }
17969
-
17970
- /** Error machine is not found. */
17971
- declare const NoMachineError = Symbol("Machine index not found.");
17972
- type NoMachineError = typeof NoMachineError;
17973
-
17974
- /** Too many segments already exported. */
17975
- declare const SegmentExportError = Symbol("Too many segments already exported.");
17976
- type SegmentExportError = typeof SegmentExportError;
17977
-
17978
- /** Host functions external invocations available during refine phase. */
17979
- interface RefineExternalities {
17980
- /** Forget a previously started nested VM. */
17981
- machineExpunge(machineIndex: MachineId): Promise<Result$2<ProgramCounter, NoMachineError>>;
17930
+ /** Strictly-typed host call index. */
17931
+ type HostCallIndex = Opaque<U32, "HostCallIndex[U32]">;
17932
+ /** Attempt to convert a number into `HostCallIndex`. */
17933
+ declare const tryAsHostCallIndex = (v: number): HostCallIndex => asOpaqueType(tryAsU32(v));
17982
17934
 
17983
- /** Set given range of pages as non-accessible and re-initialize them with zeros. */
17984
- machineVoidPages(machineIndex: MachineId, pageStart: U64, pageCount: U64): Promise<Result$2<OK, ZeroVoidError>>;
17935
+ /**
17936
+ * Host-call exit reason.
17937
+ *
17938
+ * https://graypaper.fluffylabs.dev/#/ab2cdbd/24a30124a501?v=0.7.2
17939
+ */
17940
+ declare enum PvmExecution {
17941
+ Halt = 0,
17942
+ Panic = 1,
17943
+ OOG = 2, // out-of-gas
17944
+ }
17985
17945
 
17986
- /** Set given range of pages as writeable and initialize them with zeros. */
17987
- machineZeroPages(machineIndex: MachineId, pageStart: U64, pageCount: U64): Promise<Result$2<OK, ZeroVoidError>>;
17946
+ /** A utility function to easily trace a bunch of registers. */
17947
+ declare function traceRegisters(...regs: number[]) {
17948
+ return regs.map(tryAsRegisterIndex);
17949
+ }
17988
17950
 
17989
- /** Copy a fragment of memory from `machineIndex` into given destination memory. */
17990
- machinePeekFrom(
17991
- machineIndex: MachineId,
17992
- destinationStart: U64,
17993
- sourceStart: U64,
17994
- length: U64,
17995
- destination: Memory,
17996
- ): Promise<Result$2<OK, PeekPokeError>>;
17951
+ /** An interface for a host call implementation */
17952
+ interface HostCallHandler {
17953
+ /** Index of that host call (i.e. what PVM invokes via `ecalli`) */
17954
+ readonly index: HostCallIndex;
17997
17955
 
17998
- /** Write a fragment of memory into `machineIndex` from given source memory. */
17999
- machinePokeInto(
18000
- machineIndex: MachineId,
18001
- sourceStart: U64,
18002
- destinationStart: U64,
18003
- length: U64,
18004
- source: Memory,
18005
- ): Promise<Result$2<OK, PeekPokeError>>;
17956
+ /**
17957
+ * The gas cost of invocation of that host call.
17958
+ *
17959
+ * NOTE: `((reg: IHostCallRegisters) => Gas)` function is for compatibility reasons: pre GP 0.7.2
17960
+ */
17961
+ readonly basicGasCost: SmallGas | ((reg: IHostCallRegisters) => Gas);
18006
17962
 
18007
- /** Start an inner PVM instance with given entry point and starting code. */
18008
- machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise<Result$2<MachineId, ProgramDecoderError>>;
17963
+ /** Currently executing service id. */
17964
+ readonly currentServiceId: U32;
18009
17965
 
18010
- /** Run a previously initialized PVM instance with given gas and registers. */
18011
- machineInvoke(
18012
- machineIndex: MachineId,
18013
- gas: BigGas,
18014
- registers: Registers,
18015
- ): Promise<Result$2<MachineResult, NoMachineError>>;
17966
+ /** Input&Output registers that we should add to tracing log. */
17967
+ readonly tracedRegisters: RegisterIndex[];
18016
17968
 
18017
17969
  /**
18018
- * Export segment for future retrieval.
17970
+ * Actually execute the host call.
18019
17971
  *
18020
- * Returns the index assigned to that segment or an error if there is too many already exported.
17972
+ * NOTE the call is ALLOWED and expected to modify registers and memory.
18021
17973
  */
18022
- exportSegment(segment: Segment): Result$2<SegmentIndex, SegmentExportError>;
18023
-
18024
- /** Lookup a historical preimage. */
18025
- historicalLookup(serviceId: ServiceId | null, hash: Blake2bHash): Promise<BytesBlob | null>;
18026
-
18027
- /** Change access to and/or zero the value of memory. */
18028
- machinePages(
18029
- machineIndex: MachineId,
18030
- pageStart: U64,
18031
- pageCount: U64,
18032
- requestType: MemoryOperation | null,
18033
- ): Promise<Result$2<OK, PagesError>>;
17974
+ execute(gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise<undefined | PvmExecution>;
18034
17975
  }
18035
17976
 
18036
- declare const InsufficientFundsError = "insufficient funds";
18037
- type InsufficientFundsError = typeof InsufficientFundsError;
18038
-
18039
- /** Update of the state entries coming from accumulation of a single service. */
18040
- type ServiceStateUpdate = Partial<Pick<State, "privilegedServices" | "authQueues" | "designatedValidatorData">> &
18041
- ServicesUpdate;
17977
+ /** Container for all available host calls. */
17978
+ declare class HostCallsManager {
17979
+ private readonly hostCalls = new Map<HostCallIndex, HostCallHandler>();
17980
+ private readonly missing;
18042
17981
 
18043
- /**
18044
- * State updates that currently accumulating service produced.
18045
- *
18046
- * `x_u`: https://graypaper.fluffylabs.dev/#/9a08063/2f31012f3101?v=0.6.6
18047
- */
18048
- declare class AccumulationStateUpdate {
18049
- /** Updated authorization queues for cores. */
18050
- public readonly authorizationQueues: Map<CoreIndex, FixedSizeArray<AuthorizerHash, AUTHORIZATION_QUEUE_SIZE>> =
18051
- new Map();
18052
- /** New validators data. */
18053
- public validatorsData: PerValidator<ValidatorData> | null = null;
18054
- /** Updated priviliged services. */
18055
- public privilegedServices: PrivilegedServices | null = null;
17982
+ constructor({
17983
+ missing,
17984
+ handlers = [],
17985
+ }: {
17986
+ missing: HostCallHandler;
17987
+ handlers?: HostCallHandler[];
17988
+ }) {
17989
+ this.missing = missing;
18056
17990
 
18057
- private constructor(
18058
- /** Services state updates. */
18059
- public readonly services: ServicesUpdate,
18060
- /** Pending transfers. */
18061
- public transfers: PendingTransfer[],
18062
- /** Yielded accumulation root. */
18063
- public readonly yieldedRoots: Map<ServiceId, OpaqueHash> = new Map(),
18064
- ) {}
17991
+ for (const handler of handlers) {
17992
+ check`${this.hostCalls.get(handler.index) === undefined} Overwriting host call handler at index ${handler.index}`;
17993
+ this.hostCalls.set(handler.index, handler);
17994
+ }
17995
+ }
18065
17996
 
18066
- /** Create new empty state update. */
18067
- static empty(): AccumulationStateUpdate {
18068
- return new AccumulationStateUpdate(
18069
- {
18070
- servicesUpdates: [],
18071
- servicesRemoved: [],
18072
- preimages: [],
18073
- storage: [],
18074
- },
18075
- [],
18076
- );
17997
+ /** Get a host call by index. */
17998
+ get(hostCallIndex: HostCallIndex): HostCallHandler {
17999
+ return this.hostCalls.get(hostCallIndex) ?? this.missing;
18077
18000
  }
18078
18001
 
18079
- /** Create a state update with some existing, yet uncommited services updates. */
18080
- static new(update: ServicesUpdate): AccumulationStateUpdate {
18081
- return new AccumulationStateUpdate(
18082
- {
18083
- ...update,
18084
- },
18085
- [],
18086
- );
18002
+ traceHostCall(
18003
+ context: string,
18004
+ hostCallIndex: HostCallIndex,
18005
+ hostCallHandler: HostCallHandler,
18006
+ registers: IHostCallRegisters,
18007
+ gas: Gas,
18008
+ ) {
18009
+ const { currentServiceId } = hostCallHandler;
18010
+ const requested = hostCallIndex !== hostCallHandler.index ? ` (${hostCallIndex})` : "";
18011
+ const name = `${hostCallHandler.constructor.name}:${hostCallHandler.index}`;
18012
+ const registerValues = hostCallHandler.tracedRegisters
18013
+ .map((idx) => [idx.toString().padStart(2, "0"), registers.get(idx)] as const)
18014
+ .filter((v) => v[1] !== 0n)
18015
+ .map(([idx, value]) => {
18016
+ return `r${idx}=${value} (0x${value.toString(16)})`;
18017
+ })
18018
+ .join(", ");
18019
+ logger.insane`[${currentServiceId}] ${context} ${name}${requested}. Gas: ${gas}. Regs: ${registerValues}.`;
18087
18020
  }
18021
+ }
18088
18022
 
18089
- /** Create a copy of another `StateUpdate`. Used by checkpoints. */
18090
- static copyFrom(from: AccumulationStateUpdate): AccumulationStateUpdate {
18091
- const serviceUpdates: ServicesUpdate = {
18092
- servicesUpdates: [...from.services.servicesUpdates],
18093
- servicesRemoved: [...from.services.servicesRemoved],
18094
- preimages: [...from.services.preimages],
18095
- storage: [...from.services.storage],
18096
- };
18097
- const transfers = [...from.transfers];
18098
- const update = new AccumulationStateUpdate(serviceUpdates, transfers, new Map(from.yieldedRoots));
18023
+ type ResolveFn = (pvm: Interpreter) => void;
18099
18024
 
18100
- // update entries
18101
- for (const [k, v] of from.authorizationQueues) {
18102
- update.authorizationQueues.set(k, v);
18103
- }
18025
+ declare class InterpreterInstanceManager {
18026
+ private instances: Interpreter[] = [];
18027
+ private waitingQueue: ResolveFn[] = [];
18104
18028
 
18105
- if (from.validatorsData !== null) {
18106
- update.validatorsData = asKnownSize([...from.validatorsData]);
18029
+ constructor(noOfPvmInstances: number) {
18030
+ for (let i = 0; i < noOfPvmInstances; i++) {
18031
+ this.instances.push(
18032
+ new Interpreter({
18033
+ useSbrkGas: false,
18034
+ }),
18035
+ );
18107
18036
  }
18037
+ }
18108
18038
 
18109
- if (from.privilegedServices !== null) {
18110
- update.privilegedServices = PrivilegedServices.create({
18111
- ...from.privilegedServices,
18112
- assigners: asKnownSize([...from.privilegedServices.assigners]),
18113
- });
18039
+ async getInstance(): Promise<Interpreter> {
18040
+ const instance = this.instances.pop();
18041
+ if (instance !== undefined) {
18042
+ return Promise.resolve(instance);
18114
18043
  }
18115
- return update;
18044
+ return new Promise((resolve) => {
18045
+ this.waitingQueue.push(resolve);
18046
+ });
18116
18047
  }
18117
18048
 
18118
- /** Retrieve and clear pending transfers. */
18119
- takeTransfers() {
18120
- const transfers = this.transfers;
18121
- this.transfers = [];
18122
- return transfers;
18049
+ releaseInstance(pvm: Interpreter) {
18050
+ const waiting = this.waitingQueue.shift();
18051
+ if (waiting !== undefined) {
18052
+ return waiting(pvm);
18053
+ }
18054
+ this.instances.push(pvm);
18123
18055
  }
18124
18056
  }
18125
18057
 
18126
- type StateSlice = Pick<State, "getService" | "privilegedServices">;
18127
-
18128
- declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18129
- /** A collection of state updates. */
18130
- public readonly stateUpdate;
18131
-
18132
- constructor(
18133
- /** Original (unmodified state). */
18134
- public readonly state: T,
18135
- stateUpdate?: AccumulationStateUpdate,
18058
+ declare class ReturnValue {
18059
+ private constructor(
18060
+ public consumedGas: Gas,
18061
+ public status: Status | null,
18062
+ public memorySlice: Uint8Array | null,
18136
18063
  ) {
18137
- this.stateUpdate =
18138
- stateUpdate === undefined ? AccumulationStateUpdate.empty() : AccumulationStateUpdate.copyFrom(stateUpdate);
18064
+ check`
18065
+ ${(status === null && memorySlice !== null) || (status !== null && memorySlice === null)}
18066
+ 'status' and 'memorySlice' must not both be null or both be non-null — exactly one must be provided
18067
+ `;
18139
18068
  }
18140
18069
 
18141
- /**
18142
- * Retrieve info of service with given id.
18143
- *
18144
- * NOTE the info may be updated compared to what is in the state.
18145
- *
18146
- * Takes into account ejected and newly created services as well.
18147
- */
18148
- getServiceInfo(destination: ServiceId | null): ServiceAccountInfo | null {
18149
- if (destination === null) {
18150
- return null;
18151
- }
18152
-
18153
- const maybeNewService = this.stateUpdate.services.servicesUpdates.find(
18154
- (update) => update.serviceId === destination,
18155
- );
18156
-
18157
- if (maybeNewService !== undefined) {
18158
- return maybeNewService.action.account;
18159
- }
18160
-
18161
- const maybeService = this.state.getService(destination);
18162
- if (maybeService === null) {
18163
- return null;
18164
- }
18070
+ static fromStatus(consumedGas: Gas, status: Status) {
18071
+ return new ReturnValue(consumedGas, status, null);
18072
+ }
18165
18073
 
18166
- return maybeService.getInfo();
18074
+ static fromMemorySlice(consumedGas: Gas, memorySlice: Uint8Array) {
18075
+ return new ReturnValue(consumedGas, null, memorySlice);
18167
18076
  }
18168
18077
 
18169
- getStorage(serviceId: ServiceId, rawKey: StorageKey): BytesBlob | null {
18170
- const item = this.stateUpdate.services.storage.find((x) => x.serviceId === serviceId && x.key.isEqualTo(rawKey));
18171
- if (item !== undefined) {
18172
- return item.value;
18173
- }
18078
+ hasMemorySlice(): this is this & { status: null; memorySlice: Uint8Array } {
18079
+ return this.memorySlice instanceof Uint8Array && this.status === null;
18080
+ }
18174
18081
 
18175
- const service = this.state.getService(serviceId);
18176
- return service?.getStorage(rawKey) ?? null;
18082
+ hasStatus(): this is this & { status: Status; memorySlice: null } {
18083
+ return !this.hasMemorySlice();
18177
18084
  }
18085
+ }
18086
+ declare class HostCalls {
18087
+ constructor(
18088
+ private pvmInstanceManager: InterpreterInstanceManager,
18089
+ private hostCalls: HostCallsManager,
18090
+ ) {}
18178
18091
 
18179
- /**
18180
- * Returns `true` if the preimage is already provided either in current
18181
- * accumulation scope or earlier.
18182
- *
18183
- * NOTE: Does not check if the preimage is available, we just check
18184
- * the existence in `preimages` map.
18185
- */
18186
- hasPreimage(serviceId: ServiceId, hash: PreimageHash): boolean {
18187
- const providedPreimage = this.stateUpdate.services.preimages.find(
18188
- // we ignore the action here, since if there is <any> update on that
18189
- // hash it means it has to exist, right?
18190
- (p) => p.serviceId === serviceId && p.hash.isEqualTo(hash),
18191
- );
18192
- if (providedPreimage !== undefined) {
18193
- return true;
18092
+ private getReturnValue(status: Status, pvmInstance: Interpreter): ReturnValue {
18093
+ const gasConsumed = pvmInstance.getGasConsumed();
18094
+ if (status === Status.OOG) {
18095
+ return ReturnValue.fromStatus(gasConsumed, status);
18194
18096
  }
18195
18097
 
18196
- // fallback to state preimages
18197
- const service = this.state.getService(serviceId);
18198
- if (service === undefined) {
18199
- return false;
18200
- }
18098
+ if (status === Status.HALT) {
18099
+ const memory = pvmInstance.getMemory();
18100
+ const regs = pvmInstance.getRegisters();
18101
+ const maybeAddress = regs.getLowerU32(7);
18102
+ const maybeLength = regs.getLowerU32(8);
18201
18103
 
18202
- return service?.hasPreimage(hash) ?? false;
18203
- }
18104
+ const result = safeAllocUint8Array(maybeLength);
18105
+ const startAddress = tryAsMemoryIndex(maybeAddress);
18106
+ const loadResult = memory.loadInto(result, startAddress);
18204
18107
 
18205
- getPreimage(serviceId: ServiceId, hash: PreimageHash): BytesBlob | null {
18206
- // TODO [ToDr] Should we verify availability here?
18207
- const freshlyProvided = this.stateUpdate.services.preimages.find(
18208
- (x) => x.serviceId === serviceId && x.hash.isEqualTo(hash),
18209
- );
18210
- if (freshlyProvided !== undefined && freshlyProvided.action.kind === UpdatePreimageKind.Provide) {
18211
- return freshlyProvided.action.preimage.blob;
18108
+ if (loadResult.isError) {
18109
+ return ReturnValue.fromMemorySlice(gasConsumed, new Uint8Array());
18110
+ }
18111
+
18112
+ return ReturnValue.fromMemorySlice(gasConsumed, result);
18212
18113
  }
18213
18114
 
18214
- const service = this.state.getService(serviceId);
18215
- return service?.getPreimage(hash) ?? null;
18115
+ return ReturnValue.fromStatus(gasConsumed, Status.PANIC);
18216
18116
  }
18217
18117
 
18218
- /** Get status of a preimage of current service taking into account any updates. */
18219
- getLookupHistory(
18220
- currentTimeslot: TimeSlot,
18221
- serviceId: ServiceId,
18222
- hash: PreimageHash,
18223
- length: U64,
18224
- ): LookupHistoryItem | null {
18225
- // TODO [ToDr] This is most likely wrong. We may have `provide` and `remove` within
18226
- // the same state update. We should however switch to proper "updated state"
18227
- // representation soon.
18228
- const updatedPreimage = this.stateUpdate.services.preimages.findLast(
18229
- (update) => update.serviceId === serviceId && update.hash.isEqualTo(hash) && BigInt(update.length) === length,
18230
- );
18231
-
18232
- const stateFallback = () => {
18233
- // fallback to state lookup
18234
- const service = this.state.getService(serviceId);
18235
- const lenU32 = preimageLenAsU32(length);
18236
- if (lenU32 === null || service === null) {
18237
- return null;
18118
+ private async execute(pvmInstance: Interpreter) {
18119
+ pvmInstance.runProgram();
18120
+ for (;;) {
18121
+ let status = pvmInstance.getStatus();
18122
+ if (status !== Status.HOST) {
18123
+ return this.getReturnValue(status, pvmInstance);
18238
18124
  }
18125
+ check`
18126
+ ${pvmInstance.getExitParam() !== null}
18127
+ "We know that the exit param is not null, because the status is 'Status.HOST'
18128
+ `;
18129
+ const hostCallIndex = pvmInstance.getExitParam() ?? -1;
18130
+ const gas = pvmInstance.getGasCounter();
18131
+ const regs = new HostCallRegisters(pvmInstance.getRegisters());
18132
+ const memory = new HostCallMemory(pvmInstance.getMemory());
18133
+ const index = tryAsHostCallIndex(hostCallIndex);
18239
18134
 
18240
- const slots = service.getLookupHistory(hash, lenU32);
18241
- return slots === null ? null : new LookupHistoryItem(hash, lenU32, slots);
18242
- };
18135
+ const hostCall = this.hostCalls.get(index);
18136
+ const gasBefore = gas.get();
18137
+ // NOTE: `basicGasCost(regs)` function is for compatibility reasons: pre GP 0.7.2
18138
+ const basicGasCost =
18139
+ typeof hostCall.basicGasCost === "number" ? hostCall.basicGasCost : hostCall.basicGasCost(regs);
18140
+ const underflow = gas.sub(basicGasCost);
18243
18141
 
18244
- if (updatedPreimage === undefined) {
18245
- return stateFallback();
18246
- }
18142
+ const pcLog = `[PC: ${pvmInstance.getPC()}]`;
18143
+ if (underflow) {
18144
+ this.hostCalls.traceHostCall(`${pcLog} OOG`, index, hostCall, regs, gas.get());
18145
+ return ReturnValue.fromStatus(pvmInstance.getGasConsumed(), Status.OOG);
18146
+ }
18147
+ this.hostCalls.traceHostCall(`${pcLog} Invoking`, index, hostCall, regs, gasBefore);
18148
+ const result = await hostCall.execute(gas, regs, memory);
18149
+ this.hostCalls.traceHostCall(
18150
+ result === undefined ? `${pcLog} Result` : `${pcLog} Status(${PvmExecution[result]})`,
18151
+ index,
18152
+ hostCall,
18153
+ regs,
18154
+ gas.get(),
18155
+ );
18247
18156
 
18248
- const { action } = updatedPreimage;
18249
- switch (action.kind) {
18250
- case UpdatePreimageKind.Provide: {
18251
- // casting to U32 is safe, since we compare with object we have in memory.
18252
- return new LookupHistoryItem(hash, updatedPreimage.length, tryAsLookupHistorySlots([currentTimeslot]));
18157
+ if (result === PvmExecution.Halt) {
18158
+ status = Status.HALT;
18159
+ return this.getReturnValue(status, pvmInstance);
18253
18160
  }
18254
- case UpdatePreimageKind.Remove: {
18255
- const state = stateFallback();
18256
- // kinda impossible, since we know it's there because it's removed.
18257
- if (state === null) {
18258
- return null;
18259
- }
18260
18161
 
18261
- return new LookupHistoryItem(hash, state.length, tryAsLookupHistorySlots([...state.slots, currentTimeslot]));
18162
+ if (result === PvmExecution.Panic) {
18163
+ status = Status.PANIC;
18164
+ return this.getReturnValue(status, pvmInstance);
18262
18165
  }
18263
- case UpdatePreimageKind.UpdateOrAdd: {
18264
- return action.item;
18166
+
18167
+ if (result === PvmExecution.OOG) {
18168
+ status = Status.OOG;
18169
+ return this.getReturnValue(status, pvmInstance);
18170
+ }
18171
+
18172
+ if (result === undefined) {
18173
+ pvmInstance.runProgram();
18174
+ status = pvmInstance.getStatus();
18175
+ continue;
18265
18176
  }
18177
+
18178
+ assertNever(result);
18266
18179
  }
18180
+ }
18267
18181
 
18268
- assertNever(action);
18182
+ async runProgram(
18183
+ rawProgram: Uint8Array,
18184
+ initialPc: number,
18185
+ initialGas: Gas,
18186
+ maybeRegisters?: Registers,
18187
+ maybeMemory?: Memory,
18188
+ ): Promise<ReturnValue> {
18189
+ const pvmInstance = await this.pvmInstanceManager.getInstance();
18190
+ pvmInstance.reset(rawProgram, initialPc, initialGas, maybeRegisters, maybeMemory);
18191
+ try {
18192
+ return await this.execute(pvmInstance);
18193
+ } finally {
18194
+ this.pvmInstanceManager.releaseInstance(pvmInstance);
18195
+ }
18269
18196
  }
18197
+ }
18270
18198
 
18271
- /* State update functions. */
18199
+ type index$7_HostCallHandler = HostCallHandler;
18200
+ type index$7_HostCallMemory = HostCallMemory;
18201
+ declare const index$7_HostCallMemory: typeof HostCallMemory;
18202
+ type index$7_HostCallRegisters = HostCallRegisters;
18203
+ declare const index$7_HostCallRegisters: typeof HostCallRegisters;
18204
+ type index$7_IHostCallMemory = IHostCallMemory;
18205
+ type index$7_IHostCallRegisters = IHostCallRegisters;
18206
+ type index$7_PvmExecution = PvmExecution;
18207
+ declare const index$7_PvmExecution: typeof PvmExecution;
18208
+ declare const index$7_traceRegisters: typeof traceRegisters;
18209
+ declare const index$7_tryAsHostCallIndex: typeof tryAsHostCallIndex;
18210
+ declare namespace index$7 {
18211
+ export { index$7_HostCallMemory as HostCallMemory, index$7_HostCallRegisters as HostCallRegisters, HostCallsManager as HostCalls, index$7_PvmExecution as PvmExecution, HostCalls as PvmHostCallExtension, InterpreterInstanceManager as PvmInstanceManager, index$7_traceRegisters as traceRegisters, index$7_tryAsHostCallIndex as tryAsHostCallIndex };
18212
+ export type { index$7_HostCallHandler as HostCallHandler, index$7_IHostCallMemory as IHostCallMemory, index$7_IHostCallRegisters as IHostCallRegisters };
18213
+ }
18272
18214
 
18273
- updateStorage(serviceId: ServiceId, key: StorageKey, value: BytesBlob | null) {
18274
- const update =
18275
- value === null
18276
- ? UpdateStorage.remove({ serviceId, key })
18277
- : UpdateStorage.set({
18278
- serviceId,
18279
- storage: StorageItem.create({ key, value }),
18280
- });
18215
+ /**
18216
+ * Program counter is a 64-bit unsigned integer that points to the next instruction
18217
+ *
18218
+ * https://graypaper.fluffylabs.dev/#/1c979cb/2e3f012e3f01?v=0.7.1
18219
+ */
18220
+ type ProgramCounter = Opaque<U64, "ProgramCounter[u64]">;
18221
+ /** Convert a number into ProgramCounter. */
18222
+ declare const tryAsProgramCounter = (v: number | bigint): ProgramCounter => asOpaqueType(tryAsU64(v));
18281
18223
 
18282
- const index = this.stateUpdate.services.storage.findIndex(
18283
- (x) => x.serviceId === update.serviceId && x.key.isEqualTo(key),
18284
- );
18285
- const count = index === -1 ? 0 : 1;
18286
- this.stateUpdate.services.storage.splice(index, count, update);
18224
+ /** Running PVM instance identifier. */
18225
+ type MachineId = Opaque<U64, "MachineId[u64]">;
18226
+ /** Convert a number into PVM instance identifier. */
18227
+ declare const tryAsMachineId = (v: number | bigint): MachineId => asOpaqueType(tryAsU64(v));
18228
+
18229
+ declare class MachineInstance {
18230
+ async run(gas: BigGas, registers: Registers): Promise<MachineResult> {
18231
+ return {
18232
+ result: {
18233
+ status: Status.OK,
18234
+ },
18235
+ gas,
18236
+ registers,
18237
+ };
18287
18238
  }
18239
+ }
18288
18240
 
18289
- /**
18290
- * Update a preimage.
18291
- *
18292
- * Note we store all previous entries as well, since there might be a sequence of:
18293
- * `provide` -> `remove` and both should update the end state somehow.
18294
- */
18295
- updatePreimage(newUpdate: UpdatePreimage) {
18296
- this.stateUpdate.services.preimages.push(newUpdate);
18297
- }
18241
+ type MachineStatus =
18242
+ | {
18243
+ status: typeof Status.HOST;
18244
+ hostCallIndex: U64;
18245
+ }
18246
+ | {
18247
+ status: typeof Status.FAULT;
18248
+ address: U64;
18249
+ }
18250
+ | {
18251
+ status: typeof Status.OK | typeof Status.HALT | typeof Status.PANIC | typeof Status.OOG;
18252
+ };
18298
18253
 
18299
- updateServiceStorageUtilisation(
18300
- serviceId: ServiceId,
18301
- items: number,
18302
- bytes: bigint,
18303
- serviceInfo: ServiceAccountInfo,
18304
- ): Result$2<OK, InsufficientFundsError> {
18305
- check`${items >= 0} storageUtilisationCount has to be a positive number, got: ${items}`;
18306
- check`${bytes >= 0} storageUtilisationBytes has to be a positive number, got: ${bytes}`;
18254
+ /** Data returned by a machine invocation. */
18255
+ type MachineResult = {
18256
+ result: MachineStatus;
18257
+ gas: BigGas;
18258
+ registers: Registers;
18259
+ };
18307
18260
 
18308
- const overflowItems = !isU32(items);
18309
- const overflowBytes = !isU64(bytes);
18261
+ /** Types of possbile operations to request by Pages host call. */
18262
+ declare enum MemoryOperation {
18263
+ /** Zeroes memory and set access to unreadable. */
18264
+ Void = 0,
18265
+ /** Zeroes memory and set access to read-only. */
18266
+ ZeroRead = 1,
18267
+ /** Zeroes memory and set access to read-write. */
18268
+ ZeroWrite = 2,
18269
+ /** Preserve memory and set access to read-only. */
18270
+ Read = 3,
18271
+ /** Preserve memory and set access to read-write. */
18272
+ Write = 4,
18273
+ }
18310
18274
 
18311
- // TODO [ToDr] this is not specified in GP, but it seems sensible.
18312
- if (overflowItems || overflowBytes) {
18313
- return Result.error(InsufficientFundsError);
18314
- }
18275
+ /** Convert a number into MemoryOperation or null (if invalid). */
18276
+ declare const toMemoryOperation = (v: number | bigint): MemoryOperation | null =>
18277
+ v <= MemoryOperation.Write && v >= MemoryOperation.Void ? Number(v) : null;
18315
18278
 
18316
- const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
18317
- if (serviceInfo.balance < thresholdBalance) {
18318
- return Result.error(InsufficientFundsError);
18319
- }
18279
+ /** An error that may occur during `peek` or `poke` host call. */
18280
+ declare enum PeekPokeError {
18281
+ /** Source page fault. */
18282
+ SourcePageFault = 0,
18283
+ /** Destination page fault. */
18284
+ DestinationPageFault = 1,
18285
+ /** No machine under given machine index. */
18286
+ NoMachine = 2,
18287
+ }
18320
18288
 
18321
- // Update service info with new details.
18322
- this.updateServiceInfo(
18323
- serviceId,
18324
- ServiceAccountInfo.create({
18325
- ...serviceInfo,
18326
- storageUtilisationBytes: bytes,
18327
- storageUtilisationCount: items,
18328
- }),
18329
- );
18330
- return Result.ok(OK);
18331
- }
18289
+ declare enum ZeroVoidError {
18290
+ /** No machine under given machine index. */
18291
+ NoMachine = 0,
18292
+ /** Attempting to void or zero non-accessible page. */
18293
+ InvalidPage = 1,
18294
+ }
18332
18295
 
18333
- updateServiceInfo(serviceId: ServiceId, newInfo: ServiceAccountInfo) {
18334
- const idx = this.stateUpdate.services.servicesUpdates.findIndex((x) => x.serviceId === serviceId);
18335
- const toRemove = idx === -1 ? 0 : 1;
18336
- const existingItem = this.stateUpdate.services.servicesUpdates[idx];
18296
+ declare enum PagesError {
18297
+ /** No machine under given machine index. */
18298
+ NoMachine = 0,
18299
+ /** Invalid memory operation. */
18300
+ InvalidOperation = 1,
18301
+ /** Attempting to change non-accessible page or trying to preserve value of voided page. */
18302
+ InvalidPage = 2,
18303
+ }
18337
18304
 
18338
- if (existingItem?.action.kind === UpdateServiceKind.Create) {
18339
- this.stateUpdate.services.servicesUpdates.splice(
18340
- idx,
18341
- toRemove,
18342
- UpdateService.create({
18343
- serviceId,
18344
- serviceInfo: newInfo,
18345
- lookupHistory: existingItem.action.lookupHistory,
18346
- }),
18347
- );
18305
+ /** Error machine is not found. */
18306
+ declare const NoMachineError = Symbol("Machine index not found.");
18307
+ type NoMachineError = typeof NoMachineError;
18348
18308
 
18349
- return;
18350
- }
18309
+ /** Too many segments already exported. */
18310
+ declare const SegmentExportError = Symbol("Too many segments already exported.");
18311
+ type SegmentExportError = typeof SegmentExportError;
18351
18312
 
18352
- this.stateUpdate.services.servicesUpdates.splice(
18353
- idx,
18354
- toRemove,
18355
- UpdateService.update({
18356
- serviceId,
18357
- serviceInfo: newInfo,
18358
- }),
18359
- );
18360
- }
18313
+ /** Host functions external invocations available during refine phase. */
18314
+ interface RefineExternalities {
18315
+ /** Forget a previously started nested VM. */
18316
+ machineExpunge(machineIndex: MachineId): Promise<Result$2<ProgramCounter, NoMachineError>>;
18361
18317
 
18362
- getPrivilegedServices() {
18363
- if (this.stateUpdate.privilegedServices !== null) {
18364
- return this.stateUpdate.privilegedServices;
18365
- }
18318
+ /** Set given range of pages as non-accessible and re-initialize them with zeros. */
18319
+ machineVoidPages(machineIndex: MachineId, pageStart: U64, pageCount: U64): Promise<Result$2<OK, ZeroVoidError>>;
18366
18320
 
18367
- return this.state.privilegedServices;
18368
- }
18369
- }
18321
+ /** Set given range of pages as writeable and initialize them with zeros. */
18322
+ machineZeroPages(machineIndex: MachineId, pageStart: U64, pageCount: U64): Promise<Result$2<OK, ZeroVoidError>>;
18370
18323
 
18371
- declare function preimageLenAsU32(length: U64) {
18372
- // Safe to convert to Number and U32: we check that len < 2^32 before conversion
18373
- return length >= 2n ** 32n ? null : tryAsU32(Number(length));
18324
+ /** Copy a fragment of memory from `machineIndex` into given destination memory. */
18325
+ machinePeekFrom(
18326
+ machineIndex: MachineId,
18327
+ destinationStart: U64,
18328
+ sourceStart: U64,
18329
+ length: U64,
18330
+ destination: IHostCallMemory,
18331
+ ): Promise<Result$2<OK, PeekPokeError>>;
18332
+
18333
+ /** Write a fragment of memory into `machineIndex` from given source memory. */
18334
+ machinePokeInto(
18335
+ machineIndex: MachineId,
18336
+ sourceStart: U64,
18337
+ destinationStart: U64,
18338
+ length: U64,
18339
+ source: IHostCallMemory,
18340
+ ): Promise<Result$2<OK, PeekPokeError>>;
18341
+
18342
+ /** Start an inner PVM instance with given entry point and starting code. */
18343
+ machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise<Result$2<MachineId, ProgramDecoderError>>;
18344
+
18345
+ /** Run a previously initialized PVM instance with given gas and registers. */
18346
+ machineInvoke(
18347
+ machineIndex: MachineId,
18348
+ gas: BigGas,
18349
+ registers: Registers,
18350
+ ): Promise<Result$2<MachineResult, NoMachineError>>;
18351
+
18352
+ /**
18353
+ * Export segment for future retrieval.
18354
+ *
18355
+ * Returns the index assigned to that segment or an error if there is too many already exported.
18356
+ */
18357
+ exportSegment(segment: Segment): Result$2<SegmentIndex, SegmentExportError>;
18358
+
18359
+ /** Lookup a historical preimage. */
18360
+ historicalLookup(serviceId: ServiceId | null, hash: Blake2bHash): Promise<BytesBlob | null>;
18361
+
18362
+ /** Change access to and/or zero the value of memory. */
18363
+ machinePages(
18364
+ machineIndex: MachineId,
18365
+ pageStart: U64,
18366
+ pageCount: U64,
18367
+ requestType: MemoryOperation | null,
18368
+ ): Promise<Result$2<OK, PagesError>>;
18374
18369
  }
18375
18370
 
18371
+ declare const InsufficientFundsError = "insufficient funds";
18372
+ type InsufficientFundsError = typeof InsufficientFundsError;
18373
+
18374
+ /** Update of the state entries coming from accumulation of a single service. */
18375
+ type ServiceStateUpdate = Partial<Pick<State, "privilegedServices" | "authQueues" | "designatedValidatorData">> &
18376
+ ServicesUpdate;
18377
+
18376
18378
  /**
18377
- * Host call result constants.
18379
+ * State updates that currently accumulating service produced.
18378
18380
  *
18379
- * https://graypaper.fluffylabs.dev/#/85129da/2c7c022c7c02?v=0.6.3
18381
+ * `x_u`: https://graypaper.fluffylabs.dev/#/9a08063/2f31012f3101?v=0.6.6
18380
18382
  */
18381
- declare const HostCallResult = {
18382
- /** The return value indicating an item does not exist. */
18383
- NONE: tryAsU64(0xffff_ffff_ffff_ffffn), // 2**64 - 1
18384
- /** Name unknown. */
18385
- WHAT: tryAsU64(0xffff_ffff_ffff_fffen), // 2**64 - 2
18386
- /** The inner PVM memory index provided for reading/writing is not accessible. */
18387
- OOB: tryAsU64(0xffff_ffff_ffff_fffdn), // 2**64 - 3
18388
- /** Index unknown. */
18389
- WHO: tryAsU64(0xffff_ffff_ffff_fffcn), // 2**64 - 4
18390
- /** Storage full or resource already allocated. */
18391
- FULL: tryAsU64(0xffff_ffff_ffff_fffbn), // 2**64 - 5
18392
- /** Core index unknown. */
18393
- CORE: tryAsU64(0xffff_ffff_ffff_fffan), // 2**64 - 6
18394
- /** Insufficient funds. */
18395
- CASH: tryAsU64(0xffff_ffff_ffff_fff9n), // 2**64 - 7
18396
- /** Gas limit too low. */
18397
- LOW: tryAsU64(0xffff_ffff_ffff_fff8n), // 2**64 - 8
18398
- /** The item is already solicited, cannot be forgotten or the operation is invalid due to privilege level. */
18399
- HUH: tryAsU64(0xffff_ffff_ffff_fff7n), // 2**64 - 9
18400
- /** The return value indicating general success. */
18401
- OK: tryAsU64(0n),
18402
- } as const;
18403
-
18404
- interface IHostCallMemory {
18405
- storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds>;
18406
- loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds>;
18407
- getMemory(): Memory;
18408
- }
18409
-
18410
- declare class HostCallMemory implements IHostCallMemory {
18411
- constructor(private readonly memory: Memory) {}
18383
+ declare class AccumulationStateUpdate {
18384
+ /** Updated authorization queues for cores. */
18385
+ public readonly authorizationQueues: Map<CoreIndex, FixedSizeArray<AuthorizerHash, AUTHORIZATION_QUEUE_SIZE>> =
18386
+ new Map();
18387
+ /** New validators data. */
18388
+ public validatorsData: PerValidator<ValidatorData> | null = null;
18389
+ /** Updated priviliged services. */
18390
+ public privilegedServices: PrivilegedServices | null = null;
18412
18391
 
18413
- storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds> {
18414
- if (bytes.length === 0) {
18415
- return Result.ok(OK);
18416
- }
18392
+ private constructor(
18393
+ /** Services state updates. */
18394
+ public readonly services: ServicesUpdate,
18395
+ /** Pending transfers. */
18396
+ public transfers: PendingTransfer[],
18397
+ /** Yielded accumulation root. */
18398
+ public readonly yieldedRoots: Map<ServiceId, OpaqueHash> = new Map(),
18399
+ ) {}
18417
18400
 
18418
- if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
18419
- return Result.error(new OutOfBounds());
18420
- }
18401
+ /** Create new empty state update. */
18402
+ static empty(): AccumulationStateUpdate {
18403
+ return new AccumulationStateUpdate(
18404
+ {
18405
+ servicesUpdates: [],
18406
+ servicesRemoved: [],
18407
+ preimages: [],
18408
+ storage: [],
18409
+ },
18410
+ [],
18411
+ );
18412
+ }
18421
18413
 
18422
- return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
18414
+ /** Create a state update with some existing, yet uncommited services updates. */
18415
+ static new(update: ServicesUpdate): AccumulationStateUpdate {
18416
+ return new AccumulationStateUpdate(
18417
+ {
18418
+ ...update,
18419
+ },
18420
+ [],
18421
+ );
18423
18422
  }
18424
18423
 
18425
- loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds> {
18426
- if (result.length === 0) {
18427
- return Result.ok(OK);
18424
+ /** Create a copy of another `StateUpdate`. Used by checkpoints. */
18425
+ static copyFrom(from: AccumulationStateUpdate): AccumulationStateUpdate {
18426
+ const serviceUpdates: ServicesUpdate = {
18427
+ servicesUpdates: [...from.services.servicesUpdates],
18428
+ servicesRemoved: [...from.services.servicesRemoved],
18429
+ preimages: [...from.services.preimages],
18430
+ storage: [...from.services.storage],
18431
+ };
18432
+ const transfers = [...from.transfers];
18433
+ const update = new AccumulationStateUpdate(serviceUpdates, transfers, new Map(from.yieldedRoots));
18434
+
18435
+ // update entries
18436
+ for (const [k, v] of from.authorizationQueues) {
18437
+ update.authorizationQueues.set(k, v);
18428
18438
  }
18429
18439
 
18430
- if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
18431
- return Result.error(new OutOfBounds());
18440
+ if (from.validatorsData !== null) {
18441
+ update.validatorsData = asKnownSize([...from.validatorsData]);
18432
18442
  }
18433
18443
 
18434
- return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
18444
+ if (from.privilegedServices !== null) {
18445
+ update.privilegedServices = PrivilegedServices.create({
18446
+ ...from.privilegedServices,
18447
+ assigners: asKnownSize([...from.privilegedServices.assigners]),
18448
+ });
18449
+ }
18450
+ return update;
18435
18451
  }
18436
18452
 
18437
- getMemory(): Memory {
18438
- return this.memory;
18453
+ /** Retrieve and clear pending transfers. */
18454
+ takeTransfers() {
18455
+ const transfers = this.transfers;
18456
+ this.transfers = [];
18457
+ return transfers;
18439
18458
  }
18440
18459
  }
18441
18460
 
18442
- interface IHostCallRegisters {
18443
- get(registerIndex: number): U64;
18444
- set(registerIndex: number, value: U64): void;
18445
- }
18446
-
18447
- declare class HostCallRegisters implements IHostCallRegisters {
18448
- constructor(private readonly registers: Registers) {}
18461
+ type StateSlice = Pick<State, "getService" | "privilegedServices">;
18449
18462
 
18450
- get(registerIndex: number): U64 {
18451
- return tryAsU64(this.registers.getU64(registerIndex));
18452
- }
18463
+ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18464
+ /** A collection of state updates. */
18465
+ public readonly stateUpdate;
18453
18466
 
18454
- set(registerIndex: number, value: U64) {
18455
- this.registers.setU64(registerIndex, value);
18467
+ constructor(
18468
+ /** Original (unmodified state). */
18469
+ public readonly state: T,
18470
+ stateUpdate?: AccumulationStateUpdate,
18471
+ ) {
18472
+ this.stateUpdate =
18473
+ stateUpdate === undefined ? AccumulationStateUpdate.empty() : AccumulationStateUpdate.copyFrom(stateUpdate);
18456
18474
  }
18457
- }
18458
18475
 
18459
- /** Strictly-typed host call index. */
18460
- type HostCallIndex = Opaque<U32, "HostCallIndex[U32]">;
18461
- /** Attempt to convert a number into `HostCallIndex`. */
18462
- declare const tryAsHostCallIndex = (v: number): HostCallIndex => asOpaqueType(tryAsU32(v));
18476
+ /**
18477
+ * Retrieve info of service with given id.
18478
+ *
18479
+ * NOTE the info may be updated compared to what is in the state.
18480
+ *
18481
+ * Takes into account ejected and newly created services as well.
18482
+ */
18483
+ getServiceInfo(destination: ServiceId | null): ServiceAccountInfo | null {
18484
+ if (destination === null) {
18485
+ return null;
18486
+ }
18463
18487
 
18464
- /**
18465
- * Host-call exit reason.
18466
- *
18467
- * https://graypaper.fluffylabs.dev/#/ab2cdbd/24a30124a501?v=0.7.2
18468
- */
18469
- declare enum PvmExecution {
18470
- Halt = 0,
18471
- Panic = 1,
18472
- OOG = 2, // out-of-gas
18473
- }
18488
+ const maybeNewService = this.stateUpdate.services.servicesUpdates.find(
18489
+ (update) => update.serviceId === destination,
18490
+ );
18474
18491
 
18475
- /** A utility function to easily trace a bunch of registers. */
18476
- declare function traceRegisters(...regs: number[]) {
18477
- return regs.map(tryAsRegisterIndex);
18478
- }
18492
+ if (maybeNewService !== undefined) {
18493
+ return maybeNewService.action.account;
18494
+ }
18479
18495
 
18480
- /** An interface for a host call implementation */
18481
- interface HostCallHandler {
18482
- /** Index of that host call (i.e. what PVM invokes via `ecalli`) */
18483
- readonly index: HostCallIndex;
18496
+ const maybeService = this.state.getService(destination);
18497
+ if (maybeService === null) {
18498
+ return null;
18499
+ }
18484
18500
 
18485
- /**
18486
- * The gas cost of invocation of that host call.
18487
- *
18488
- * NOTE: `((reg: IHostCallRegisters) => Gas)` function is for compatibility reasons: pre GP 0.7.2
18489
- */
18490
- readonly basicGasCost: SmallGas | ((reg: IHostCallRegisters) => Gas);
18501
+ return maybeService.getInfo();
18502
+ }
18491
18503
 
18492
- /** Currently executing service id. */
18493
- readonly currentServiceId: U32;
18504
+ getStorage(serviceId: ServiceId, rawKey: StorageKey): BytesBlob | null {
18505
+ const item = this.stateUpdate.services.storage.find((x) => x.serviceId === serviceId && x.key.isEqualTo(rawKey));
18506
+ if (item !== undefined) {
18507
+ return item.value;
18508
+ }
18494
18509
 
18495
- /** Input&Output registers that we should add to tracing log. */
18496
- readonly tracedRegisters: RegisterIndex[];
18510
+ const service = this.state.getService(serviceId);
18511
+ return service?.getStorage(rawKey) ?? null;
18512
+ }
18497
18513
 
18498
18514
  /**
18499
- * Actually execute the host call.
18515
+ * Returns `true` if the preimage is already provided either in current
18516
+ * accumulation scope or earlier.
18500
18517
  *
18501
- * NOTE the call is ALLOWED and expected to modify registers and memory.
18518
+ * NOTE: Does not check if the preimage is available, we just check
18519
+ * the existence in `preimages` map.
18502
18520
  */
18503
- execute(gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise<undefined | PvmExecution>;
18504
- }
18521
+ hasPreimage(serviceId: ServiceId, hash: PreimageHash): boolean {
18522
+ const providedPreimage = this.stateUpdate.services.preimages.find(
18523
+ // we ignore the action here, since if there is <any> update on that
18524
+ // hash it means it has to exist, right?
18525
+ (p) => p.serviceId === serviceId && p.hash.isEqualTo(hash),
18526
+ );
18527
+ if (providedPreimage !== undefined) {
18528
+ return true;
18529
+ }
18505
18530
 
18506
- /** Container for all available host calls. */
18507
- declare class HostCallsManager {
18508
- private readonly hostCalls = new Map<HostCallIndex, HostCallHandler>();
18509
- private readonly missing;
18531
+ // fallback to state preimages
18532
+ const service = this.state.getService(serviceId);
18533
+ if (service === undefined) {
18534
+ return false;
18535
+ }
18510
18536
 
18511
- constructor({
18512
- missing,
18513
- handlers = [],
18514
- }: {
18515
- missing: HostCallHandler;
18516
- handlers?: HostCallHandler[];
18517
- }) {
18518
- this.missing = missing;
18537
+ return service?.hasPreimage(hash) ?? false;
18538
+ }
18519
18539
 
18520
- for (const handler of handlers) {
18521
- check`${this.hostCalls.get(handler.index) === undefined} Overwriting host call handler at index ${handler.index}`;
18522
- this.hostCalls.set(handler.index, handler);
18540
+ getPreimage(serviceId: ServiceId, hash: PreimageHash): BytesBlob | null {
18541
+ // TODO [ToDr] Should we verify availability here?
18542
+ const freshlyProvided = this.stateUpdate.services.preimages.find(
18543
+ (x) => x.serviceId === serviceId && x.hash.isEqualTo(hash),
18544
+ );
18545
+ if (freshlyProvided !== undefined && freshlyProvided.action.kind === UpdatePreimageKind.Provide) {
18546
+ return freshlyProvided.action.preimage.blob;
18523
18547
  }
18524
- }
18525
18548
 
18526
- /** Get a host call by index. */
18527
- get(hostCallIndex: HostCallIndex): HostCallHandler {
18528
- return this.hostCalls.get(hostCallIndex) ?? this.missing;
18549
+ const service = this.state.getService(serviceId);
18550
+ return service?.getPreimage(hash) ?? null;
18529
18551
  }
18530
18552
 
18531
- traceHostCall(
18532
- context: string,
18533
- hostCallIndex: HostCallIndex,
18534
- hostCallHandler: HostCallHandler,
18535
- registers: IHostCallRegisters,
18536
- gas: Gas,
18537
- ) {
18538
- const { currentServiceId } = hostCallHandler;
18539
- const requested = hostCallIndex !== hostCallHandler.index ? ` (${hostCallIndex})` : "";
18540
- const name = `${hostCallHandler.constructor.name}:${hostCallHandler.index}`;
18541
- const registerValues = hostCallHandler.tracedRegisters
18542
- .map((idx) => [idx.toString().padStart(2, "0"), registers.get(idx)] as const)
18543
- .filter((v) => v[1] !== 0n)
18544
- .map(([idx, value]) => {
18545
- return `r${idx}=${value} (0x${value.toString(16)})`;
18546
- })
18547
- .join(", ");
18548
- logger.insane`[${currentServiceId}] ${context} ${name}${requested}. Gas: ${gas}. Regs: ${registerValues}.`;
18549
- }
18550
- }
18553
+ /** Get status of a preimage of current service taking into account any updates. */
18554
+ getLookupHistory(
18555
+ currentTimeslot: TimeSlot,
18556
+ serviceId: ServiceId,
18557
+ hash: PreimageHash,
18558
+ length: U64,
18559
+ ): LookupHistoryItem | null {
18560
+ // TODO [ToDr] This is most likely wrong. We may have `provide` and `remove` within
18561
+ // the same state update. We should however switch to proper "updated state"
18562
+ // representation soon.
18563
+ const updatedPreimage = this.stateUpdate.services.preimages.findLast(
18564
+ (update) => update.serviceId === serviceId && update.hash.isEqualTo(hash) && BigInt(update.length) === length,
18565
+ );
18551
18566
 
18552
- type ResolveFn = (pvm: Interpreter) => void;
18567
+ const stateFallback = () => {
18568
+ // fallback to state lookup
18569
+ const service = this.state.getService(serviceId);
18570
+ const lenU32 = preimageLenAsU32(length);
18571
+ if (lenU32 === null || service === null) {
18572
+ return null;
18573
+ }
18553
18574
 
18554
- declare class InterpreterInstanceManager {
18555
- private instances: Interpreter[] = [];
18556
- private waitingQueue: ResolveFn[] = [];
18575
+ const slots = service.getLookupHistory(hash, lenU32);
18576
+ return slots === null ? null : new LookupHistoryItem(hash, lenU32, slots);
18577
+ };
18557
18578
 
18558
- constructor(noOfPvmInstances: number) {
18559
- for (let i = 0; i < noOfPvmInstances; i++) {
18560
- this.instances.push(
18561
- new Interpreter({
18562
- useSbrkGas: false,
18563
- }),
18564
- );
18579
+ if (updatedPreimage === undefined) {
18580
+ return stateFallback();
18565
18581
  }
18566
- }
18567
18582
 
18568
- async getInstance(): Promise<Interpreter> {
18569
- const instance = this.instances.pop();
18570
- if (instance !== undefined) {
18571
- return Promise.resolve(instance);
18572
- }
18573
- return new Promise((resolve) => {
18574
- this.waitingQueue.push(resolve);
18575
- });
18576
- }
18583
+ const { action } = updatedPreimage;
18584
+ switch (action.kind) {
18585
+ case UpdatePreimageKind.Provide: {
18586
+ // casting to U32 is safe, since we compare with object we have in memory.
18587
+ return new LookupHistoryItem(hash, updatedPreimage.length, tryAsLookupHistorySlots([currentTimeslot]));
18588
+ }
18589
+ case UpdatePreimageKind.Remove: {
18590
+ const state = stateFallback();
18591
+ // kinda impossible, since we know it's there because it's removed.
18592
+ if (state === null) {
18593
+ return null;
18594
+ }
18577
18595
 
18578
- releaseInstance(pvm: Interpreter) {
18579
- const waiting = this.waitingQueue.shift();
18580
- if (waiting !== undefined) {
18581
- return waiting(pvm);
18596
+ return new LookupHistoryItem(hash, state.length, tryAsLookupHistorySlots([...state.slots, currentTimeslot]));
18597
+ }
18598
+ case UpdatePreimageKind.UpdateOrAdd: {
18599
+ return action.item;
18600
+ }
18582
18601
  }
18583
- this.instances.push(pvm);
18584
- }
18585
- }
18586
18602
 
18587
- declare class ReturnValue {
18588
- private constructor(
18589
- public consumedGas: Gas,
18590
- public status: Status | null,
18591
- public memorySlice: Uint8Array | null,
18592
- ) {
18593
- check`
18594
- ${(status === null && memorySlice !== null) || (status !== null && memorySlice === null)}
18595
- 'status' and 'memorySlice' must not both be null or both be non-null — exactly one must be provided
18596
- `;
18603
+ assertNever(action);
18597
18604
  }
18598
18605
 
18599
- static fromStatus(consumedGas: Gas, status: Status) {
18600
- return new ReturnValue(consumedGas, status, null);
18601
- }
18606
+ /* State update functions. */
18602
18607
 
18603
- static fromMemorySlice(consumedGas: Gas, memorySlice: Uint8Array) {
18604
- return new ReturnValue(consumedGas, null, memorySlice);
18605
- }
18608
+ updateStorage(serviceId: ServiceId, key: StorageKey, value: BytesBlob | null) {
18609
+ const update =
18610
+ value === null
18611
+ ? UpdateStorage.remove({ serviceId, key })
18612
+ : UpdateStorage.set({
18613
+ serviceId,
18614
+ storage: StorageItem.create({ key, value }),
18615
+ });
18606
18616
 
18607
- hasMemorySlice(): this is this & { status: null; memorySlice: Uint8Array } {
18608
- return this.memorySlice instanceof Uint8Array && this.status === null;
18617
+ const index = this.stateUpdate.services.storage.findIndex(
18618
+ (x) => x.serviceId === update.serviceId && x.key.isEqualTo(key),
18619
+ );
18620
+ const count = index === -1 ? 0 : 1;
18621
+ this.stateUpdate.services.storage.splice(index, count, update);
18609
18622
  }
18610
18623
 
18611
- hasStatus(): this is this & { status: Status; memorySlice: null } {
18612
- return !this.hasMemorySlice();
18624
+ /**
18625
+ * Update a preimage.
18626
+ *
18627
+ * Note we store all previous entries as well, since there might be a sequence of:
18628
+ * `provide` -> `remove` and both should update the end state somehow.
18629
+ */
18630
+ updatePreimage(newUpdate: UpdatePreimage) {
18631
+ this.stateUpdate.services.preimages.push(newUpdate);
18613
18632
  }
18614
- }
18615
- declare class HostCalls {
18616
- constructor(
18617
- private pvmInstanceManager: InterpreterInstanceManager,
18618
- private hostCalls: HostCallsManager,
18619
- ) {}
18620
-
18621
- private getReturnValue(status: Status, pvmInstance: Interpreter): ReturnValue {
18622
- const gasConsumed = pvmInstance.getGasConsumed();
18623
- if (status === Status.OOG) {
18624
- return ReturnValue.fromStatus(gasConsumed, status);
18625
- }
18626
18633
 
18627
- if (status === Status.HALT) {
18628
- const memory = pvmInstance.getMemory();
18629
- const regs = pvmInstance.getRegisters();
18630
- const maybeAddress = regs.getLowerU32(7);
18631
- const maybeLength = regs.getLowerU32(8);
18634
+ updateServiceStorageUtilisation(
18635
+ serviceId: ServiceId,
18636
+ items: number,
18637
+ bytes: bigint,
18638
+ serviceInfo: ServiceAccountInfo,
18639
+ ): Result$2<OK, InsufficientFundsError> {
18640
+ check`${items >= 0} storageUtilisationCount has to be a positive number, got: ${items}`;
18641
+ check`${bytes >= 0} storageUtilisationBytes has to be a positive number, got: ${bytes}`;
18632
18642
 
18633
- const result = safeAllocUint8Array(maybeLength);
18634
- const startAddress = tryAsMemoryIndex(maybeAddress);
18635
- const loadResult = memory.loadInto(result, startAddress);
18643
+ const overflowItems = !isU32(items);
18644
+ const overflowBytes = !isU64(bytes);
18636
18645
 
18637
- if (loadResult.isError) {
18638
- return ReturnValue.fromMemorySlice(gasConsumed, new Uint8Array());
18639
- }
18646
+ // TODO [ToDr] this is not specified in GP, but it seems sensible.
18647
+ if (overflowItems || overflowBytes) {
18648
+ return Result.error(InsufficientFundsError);
18649
+ }
18640
18650
 
18641
- return ReturnValue.fromMemorySlice(gasConsumed, result);
18651
+ const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
18652
+ if (serviceInfo.balance < thresholdBalance) {
18653
+ return Result.error(InsufficientFundsError);
18642
18654
  }
18643
18655
 
18644
- return ReturnValue.fromStatus(gasConsumed, Status.PANIC);
18656
+ // Update service info with new details.
18657
+ this.updateServiceInfo(
18658
+ serviceId,
18659
+ ServiceAccountInfo.create({
18660
+ ...serviceInfo,
18661
+ storageUtilisationBytes: bytes,
18662
+ storageUtilisationCount: items,
18663
+ }),
18664
+ );
18665
+ return Result.ok(OK);
18645
18666
  }
18646
18667
 
18647
- private async execute(pvmInstance: Interpreter) {
18648
- pvmInstance.runProgram();
18649
- for (;;) {
18650
- let status = pvmInstance.getStatus();
18651
- if (status !== Status.HOST) {
18652
- return this.getReturnValue(status, pvmInstance);
18653
- }
18654
- check`
18655
- ${pvmInstance.getExitParam() !== null}
18656
- "We know that the exit param is not null, because the status is 'Status.HOST'
18657
- `;
18658
- const hostCallIndex = pvmInstance.getExitParam() ?? -1;
18659
- const gas = pvmInstance.getGasCounter();
18660
- const regs = new HostCallRegisters(pvmInstance.getRegisters());
18661
- const memory = new HostCallMemory(pvmInstance.getMemory());
18662
- const index = tryAsHostCallIndex(hostCallIndex);
18663
-
18664
- const hostCall = this.hostCalls.get(index);
18665
- const gasBefore = gas.get();
18666
- // NOTE: `basicGasCost(regs)` function is for compatibility reasons: pre GP 0.7.2
18667
- const basicGasCost =
18668
- typeof hostCall.basicGasCost === "number" ? hostCall.basicGasCost : hostCall.basicGasCost(regs);
18669
- const underflow = gas.sub(basicGasCost);
18668
+ updateServiceInfo(serviceId: ServiceId, newInfo: ServiceAccountInfo) {
18669
+ const idx = this.stateUpdate.services.servicesUpdates.findIndex((x) => x.serviceId === serviceId);
18670
+ const toRemove = idx === -1 ? 0 : 1;
18671
+ const existingItem = this.stateUpdate.services.servicesUpdates[idx];
18670
18672
 
18671
- const pcLog = `[PC: ${pvmInstance.getPC()}]`;
18672
- if (underflow) {
18673
- this.hostCalls.traceHostCall(`${pcLog} OOG`, index, hostCall, regs, gas.get());
18674
- return ReturnValue.fromStatus(pvmInstance.getGasConsumed(), Status.OOG);
18675
- }
18676
- this.hostCalls.traceHostCall(`${pcLog} Invoking`, index, hostCall, regs, gasBefore);
18677
- const result = await hostCall.execute(gas, regs, memory);
18678
- this.hostCalls.traceHostCall(
18679
- result === undefined ? `${pcLog} Result` : `${pcLog} Status(${PvmExecution[result]})`,
18680
- index,
18681
- hostCall,
18682
- regs,
18683
- gas.get(),
18673
+ if (existingItem?.action.kind === UpdateServiceKind.Create) {
18674
+ this.stateUpdate.services.servicesUpdates.splice(
18675
+ idx,
18676
+ toRemove,
18677
+ UpdateService.create({
18678
+ serviceId,
18679
+ serviceInfo: newInfo,
18680
+ lookupHistory: existingItem.action.lookupHistory,
18681
+ }),
18684
18682
  );
18685
18683
 
18686
- if (result === PvmExecution.Halt) {
18687
- status = Status.HALT;
18688
- return this.getReturnValue(status, pvmInstance);
18689
- }
18690
-
18691
- if (result === PvmExecution.Panic) {
18692
- status = Status.PANIC;
18693
- return this.getReturnValue(status, pvmInstance);
18694
- }
18695
-
18696
- if (result === PvmExecution.OOG) {
18697
- status = Status.OOG;
18698
- return this.getReturnValue(status, pvmInstance);
18699
- }
18700
-
18701
- if (result === undefined) {
18702
- pvmInstance.runProgram();
18703
- status = pvmInstance.getStatus();
18704
- continue;
18705
- }
18706
-
18707
- assertNever(result);
18684
+ return;
18708
18685
  }
18686
+
18687
+ this.stateUpdate.services.servicesUpdates.splice(
18688
+ idx,
18689
+ toRemove,
18690
+ UpdateService.update({
18691
+ serviceId,
18692
+ serviceInfo: newInfo,
18693
+ }),
18694
+ );
18709
18695
  }
18710
18696
 
18711
- async runProgram(
18712
- rawProgram: Uint8Array,
18713
- initialPc: number,
18714
- initialGas: Gas,
18715
- maybeRegisters?: Registers,
18716
- maybeMemory?: Memory,
18717
- ): Promise<ReturnValue> {
18718
- const pvmInstance = await this.pvmInstanceManager.getInstance();
18719
- pvmInstance.reset(rawProgram, initialPc, initialGas, maybeRegisters, maybeMemory);
18720
- try {
18721
- return await this.execute(pvmInstance);
18722
- } finally {
18723
- this.pvmInstanceManager.releaseInstance(pvmInstance);
18697
+ getPrivilegedServices() {
18698
+ if (this.stateUpdate.privilegedServices !== null) {
18699
+ return this.stateUpdate.privilegedServices;
18724
18700
  }
18701
+
18702
+ return this.state.privilegedServices;
18725
18703
  }
18726
18704
  }
18727
18705
 
18728
- type index$7_HostCallHandler = HostCallHandler;
18729
- type index$7_HostCallMemory = HostCallMemory;
18730
- declare const index$7_HostCallMemory: typeof HostCallMemory;
18731
- type index$7_HostCallRegisters = HostCallRegisters;
18732
- declare const index$7_HostCallRegisters: typeof HostCallRegisters;
18733
- type index$7_IHostCallMemory = IHostCallMemory;
18734
- type index$7_IHostCallRegisters = IHostCallRegisters;
18735
- type index$7_PvmExecution = PvmExecution;
18736
- declare const index$7_PvmExecution: typeof PvmExecution;
18737
- declare const index$7_traceRegisters: typeof traceRegisters;
18738
- declare const index$7_tryAsHostCallIndex: typeof tryAsHostCallIndex;
18739
- declare namespace index$7 {
18740
- export { index$7_HostCallMemory as HostCallMemory, index$7_HostCallRegisters as HostCallRegisters, HostCallsManager as HostCalls, index$7_PvmExecution as PvmExecution, HostCalls as PvmHostCallExtension, InterpreterInstanceManager as PvmInstanceManager, index$7_traceRegisters as traceRegisters, index$7_tryAsHostCallIndex as tryAsHostCallIndex };
18741
- export type { index$7_HostCallHandler as HostCallHandler, index$7_IHostCallMemory as IHostCallMemory, index$7_IHostCallRegisters as IHostCallRegisters };
18706
+ declare function preimageLenAsU32(length: U64) {
18707
+ // Safe to convert to Number and U32: we check that len < 2^32 before conversion
18708
+ return length >= 2n ** 32n ? null : tryAsU32(Number(length));
18742
18709
  }
18743
18710
 
18711
+ /**
18712
+ * Host call result constants.
18713
+ *
18714
+ * https://graypaper.fluffylabs.dev/#/85129da/2c7c022c7c02?v=0.6.3
18715
+ */
18716
+ declare const HostCallResult = {
18717
+ /** The return value indicating an item does not exist. */
18718
+ NONE: tryAsU64(0xffff_ffff_ffff_ffffn), // 2**64 - 1
18719
+ /** Name unknown. */
18720
+ WHAT: tryAsU64(0xffff_ffff_ffff_fffen), // 2**64 - 2
18721
+ /** The inner PVM memory index provided for reading/writing is not accessible. */
18722
+ OOB: tryAsU64(0xffff_ffff_ffff_fffdn), // 2**64 - 3
18723
+ /** Index unknown. */
18724
+ WHO: tryAsU64(0xffff_ffff_ffff_fffcn), // 2**64 - 4
18725
+ /** Storage full or resource already allocated. */
18726
+ FULL: tryAsU64(0xffff_ffff_ffff_fffbn), // 2**64 - 5
18727
+ /** Core index unknown. */
18728
+ CORE: tryAsU64(0xffff_ffff_ffff_fffan), // 2**64 - 6
18729
+ /** Insufficient funds. */
18730
+ CASH: tryAsU64(0xffff_ffff_ffff_fff9n), // 2**64 - 7
18731
+ /** Gas limit too low. */
18732
+ LOW: tryAsU64(0xffff_ffff_ffff_fff8n), // 2**64 - 8
18733
+ /** The item is already solicited, cannot be forgotten or the operation is invalid due to privilege level. */
18734
+ HUH: tryAsU64(0xffff_ffff_ffff_fff7n), // 2**64 - 9
18735
+ /** The return value indicating general success. */
18736
+ OK: tryAsU64(0n),
18737
+ } as const;
18738
+
18744
18739
  declare const MAX_U32 = tryAsU32(2 ** 32 - 1);
18745
18740
  declare const MAX_U32_BIG_INT = tryAsU64(MAX_U32);
18746
18741
  declare const SERVICE_ID_BYTES = 4;