@typeberry/lib 0.2.0-74f246e → 0.2.0-adde0dd

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 +241 -200
  2. package/index.d.ts +1014 -953
  3. package/index.js +241 -200
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -322,7 +322,7 @@ type ErrorResult<Error> = {
322
322
  isOk: false;
323
323
  isError: true;
324
324
  error: Error;
325
- details: string;
325
+ details: () => string;
326
326
  };
327
327
 
328
328
  /**
@@ -383,7 +383,7 @@ declare function resultToString<Ok, Error>(res: Result$2<Ok, Error>) {
383
383
  if (res.isOk) {
384
384
  return `OK: ${typeof res.ok === "symbol" ? res.ok.toString() : res.ok}`;
385
385
  }
386
- return `${res.details}\nError: ${maybeTaggedErrorToString(res.error)}`;
386
+ return `${res.details()}\nError: ${maybeTaggedErrorToString(res.error)}`;
387
387
  }
388
388
 
389
389
  /** An indication of two possible outcomes returned from a function. */
@@ -402,7 +402,7 @@ declare const Result$2 = {
402
402
  },
403
403
 
404
404
  /** Create new [`Result`] with `Error` status. */
405
- error: <Error>(error: Error, details = ""): ErrorResult<Error> => {
405
+ error: <Error>(error: Error, details: () => string): ErrorResult<Error> => {
406
406
  check`${error !== undefined} 'Error' type cannot be undefined.`;
407
407
  return {
408
408
  isOk: false,
@@ -556,7 +556,7 @@ declare function deepEqual<T>(
556
556
 
557
557
  if (actual.isError && expected.isError) {
558
558
  deepEqual(actual.error, expected.error, { context: ctx.concat(["error"]), errorsCollector: errors, ignore });
559
- deepEqual(actual.details, expected.details, {
559
+ deepEqual(actual.details(), expected.details(), {
560
560
  context: ctx.concat(["details"]),
561
561
  errorsCollector: errors,
562
562
  // display details when error does not match
@@ -10659,7 +10659,6 @@ declare enum UpdatePreimageKind {
10659
10659
  */
10660
10660
  declare class UpdatePreimage {
10661
10661
  private constructor(
10662
- public readonly serviceId: ServiceId,
10663
10662
  public readonly action:
10664
10663
  | {
10665
10664
  kind: UpdatePreimageKind.Provide;
@@ -10679,16 +10678,8 @@ declare class UpdatePreimage {
10679
10678
  ) {}
10680
10679
 
10681
10680
  /** A preimage is provided. We should update the lookuphistory and add the preimage to db. */
10682
- static provide({
10683
- serviceId,
10684
- preimage,
10685
- slot,
10686
- }: {
10687
- serviceId: ServiceId;
10688
- preimage: PreimageItem;
10689
- slot: TimeSlot | null;
10690
- }) {
10691
- return new UpdatePreimage(serviceId, {
10681
+ static provide({ preimage, slot }: { preimage: PreimageItem; slot: TimeSlot | null }) {
10682
+ return new UpdatePreimage({
10692
10683
  kind: UpdatePreimageKind.Provide,
10693
10684
  preimage,
10694
10685
  slot,
@@ -10696,8 +10687,8 @@ declare class UpdatePreimage {
10696
10687
  }
10697
10688
 
10698
10689
  /** The preimage should be removed completely from the database. */
10699
- static remove({ serviceId, hash, length }: { serviceId: ServiceId; hash: PreimageHash; length: U32 }) {
10700
- return new UpdatePreimage(serviceId, {
10690
+ static remove({ hash, length }: { hash: PreimageHash; length: U32 }) {
10691
+ return new UpdatePreimage({
10701
10692
  kind: UpdatePreimageKind.Remove,
10702
10693
  hash,
10703
10694
  length,
@@ -10705,8 +10696,8 @@ declare class UpdatePreimage {
10705
10696
  }
10706
10697
 
10707
10698
  /** Update the lookup history of some preimage or add a new one (request). */
10708
- static updateOrAdd({ serviceId, lookupHistory }: { serviceId: ServiceId; lookupHistory: LookupHistoryItem }) {
10709
- return new UpdatePreimage(serviceId, {
10699
+ static updateOrAdd({ lookupHistory }: { lookupHistory: LookupHistoryItem }) {
10700
+ return new UpdatePreimage({
10710
10701
  kind: UpdatePreimageKind.UpdateOrAdd,
10711
10702
  item: lookupHistory,
10712
10703
  });
@@ -10744,12 +10735,12 @@ declare enum UpdateServiceKind {
10744
10735
  /** Create a new `Service` instance. */
10745
10736
  Create = 1,
10746
10737
  }
10738
+
10747
10739
  /**
10748
- * Update service info of a particular `ServiceId` or create a new one.
10740
+ * Update service info or create a new one.
10749
10741
  */
10750
10742
  declare class UpdateService {
10751
10743
  private constructor(
10752
- public readonly serviceId: ServiceId,
10753
10744
  public readonly action:
10754
10745
  | {
10755
10746
  kind: UpdateServiceKind.Update;
@@ -10762,23 +10753,21 @@ declare class UpdateService {
10762
10753
  },
10763
10754
  ) {}
10764
10755
 
10765
- static update({ serviceId, serviceInfo }: { serviceId: ServiceId; serviceInfo: ServiceAccountInfo }) {
10766
- return new UpdateService(serviceId, {
10756
+ static update({ serviceInfo }: { serviceInfo: ServiceAccountInfo }) {
10757
+ return new UpdateService({
10767
10758
  kind: UpdateServiceKind.Update,
10768
10759
  account: serviceInfo,
10769
10760
  });
10770
10761
  }
10771
10762
 
10772
10763
  static create({
10773
- serviceId,
10774
10764
  serviceInfo,
10775
10765
  lookupHistory,
10776
10766
  }: {
10777
- serviceId: ServiceId;
10778
10767
  serviceInfo: ServiceAccountInfo;
10779
10768
  lookupHistory: LookupHistoryItem | null;
10780
10769
  }) {
10781
- return new UpdateService(serviceId, {
10770
+ return new UpdateService({
10782
10771
  kind: UpdateServiceKind.Create,
10783
10772
  account: serviceInfo,
10784
10773
  lookupHistory,
@@ -10800,7 +10789,6 @@ declare enum UpdateStorageKind {
10800
10789
  */
10801
10790
  declare class UpdateStorage {
10802
10791
  private constructor(
10803
- public readonly serviceId: ServiceId,
10804
10792
  public readonly action:
10805
10793
  | {
10806
10794
  kind: UpdateStorageKind.Set;
@@ -10812,12 +10800,12 @@ declare class UpdateStorage {
10812
10800
  },
10813
10801
  ) {}
10814
10802
 
10815
- static set({ serviceId, storage }: { serviceId: ServiceId; storage: StorageItem }) {
10816
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Set, storage });
10803
+ static set({ storage }: { storage: StorageItem }) {
10804
+ return new UpdateStorage({ kind: UpdateStorageKind.Set, storage });
10817
10805
  }
10818
10806
 
10819
- static remove({ serviceId, key }: { serviceId: ServiceId; key: StorageKey }) {
10820
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Remove, key });
10807
+ static remove({ key }: { key: StorageKey }) {
10808
+ return new UpdateStorage({ kind: UpdateStorageKind.Remove, key });
10821
10809
  }
10822
10810
 
10823
10811
  get key() {
@@ -10835,16 +10823,17 @@ declare class UpdateStorage {
10835
10823
  }
10836
10824
  }
10837
10825
 
10838
- // TODO [ToDr] This would be more convenient to use if the data was grouped by `ServiceId`.
10839
10826
  type ServicesUpdate = {
10840
10827
  /** Service ids to remove from state alongside all their data. */
10841
- servicesRemoved: ServiceId[];
10842
- /** Services to update or create anew. */
10843
- servicesUpdates: UpdateService[];
10828
+ removed: ServiceId[];
10829
+ /** Services newly created. */
10830
+ created: ServiceId[];
10831
+ /** Services to update. */
10832
+ updated: Map<ServiceId, UpdateService>;
10844
10833
  /** Service preimages to update and potentially lookup history */
10845
- preimages: UpdatePreimage[];
10834
+ preimages: Map<ServiceId, UpdatePreimage[]>;
10846
10835
  /** Service storage to update. */
10847
- storage: UpdateStorage[];
10836
+ storage: Map<ServiceId, UpdateStorage[]>;
10848
10837
  };
10849
10838
 
10850
10839
  declare enum UpdateError {
@@ -11040,13 +11029,13 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11040
11029
  * Modify the state and apply a single state update.
11041
11030
  */
11042
11031
  applyUpdate(update: Partial<State & ServicesUpdate>): Result$2<OK, UpdateError> {
11043
- const { servicesRemoved, servicesUpdates, preimages, storage, ...rest } = update;
11032
+ const { removed, created: _, updated, preimages, storage, ...rest } = update;
11044
11033
  // just assign all other variables
11045
11034
  Object.assign(this, rest);
11046
11035
 
11047
11036
  // and update the services state
11048
11037
  let result: Result<OK, UpdateError>;
11049
- result = this.updateServices(servicesUpdates);
11038
+ result = this.updateServices(updated);
11050
11039
  if (result.isError) {
11051
11040
  return result;
11052
11041
  }
@@ -11058,7 +11047,7 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11058
11047
  if (result.isError) {
11059
11048
  return result;
11060
11049
  }
11061
- this.removeServices(servicesRemoved);
11050
+ this.removeServices(removed);
11062
11051
 
11063
11052
  return Result.ok(OK);
11064
11053
  }
@@ -11070,93 +11059,108 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11070
11059
  }
11071
11060
  }
11072
11061
 
11073
- private updateStorage(storage: UpdateStorage[] | undefined): Result$2<OK, UpdateError> {
11074
- for (const { serviceId, action } of storage ?? []) {
11075
- const { kind } = action;
11076
- const service = this.services.get(serviceId);
11077
- if (service === undefined) {
11078
- return Result.error(
11079
- UpdateError.NoService,
11080
- `Attempting to update storage of non-existing service: ${serviceId}`,
11081
- );
11082
- }
11062
+ private updateStorage(storageUpdates: Map<ServiceId, UpdateStorage[]> | undefined): Result$2<OK, UpdateError> {
11063
+ if (storageUpdates === undefined) {
11064
+ return Result.ok(OK);
11065
+ }
11066
+ for (const [serviceId, updates] of storageUpdates.entries()) {
11067
+ for (const update of updates) {
11068
+ const { kind } = update.action;
11069
+ const service = this.services.get(serviceId);
11070
+ if (service === undefined) {
11071
+ return Result.error(
11072
+ UpdateError.NoService,
11073
+ () => `Attempting to update storage of non-existing service: ${serviceId}`,
11074
+ );
11075
+ }
11083
11076
 
11084
- if (kind === UpdateStorageKind.Set) {
11085
- const { key, value } = action.storage;
11086
- service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
11087
- } else if (kind === UpdateStorageKind.Remove) {
11088
- const { key } = action;
11089
- check`
11077
+ if (kind === UpdateStorageKind.Set) {
11078
+ const { key, value } = update.action.storage;
11079
+ service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
11080
+ } else if (kind === UpdateStorageKind.Remove) {
11081
+ const { key } = update.action;
11082
+ check`
11090
11083
  ${service.data.storage.has(key.toString())}
11091
- Attempting to remove non-existing storage item at ${serviceId}: ${action.key}
11084
+ Attempting to remove non-existing storage item at ${serviceId}: ${update.action.key}
11092
11085
  `;
11093
- service.data.storage.delete(key.toString());
11094
- } else {
11095
- assertNever(kind);
11086
+ service.data.storage.delete(key.toString());
11087
+ } else {
11088
+ assertNever(kind);
11089
+ }
11096
11090
  }
11097
11091
  }
11098
-
11099
11092
  return Result.ok(OK);
11100
11093
  }
11101
11094
 
11102
- private updatePreimages(preimages: UpdatePreimage[] | undefined): Result$2<OK, UpdateError> {
11103
- for (const { serviceId, action } of preimages ?? []) {
11095
+ private updatePreimages(preimagesUpdates: Map<ServiceId, UpdatePreimage[]> | undefined): Result$2<OK, UpdateError> {
11096
+ if (preimagesUpdates === undefined) {
11097
+ return Result.ok(OK);
11098
+ }
11099
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
11104
11100
  const service = this.services.get(serviceId);
11105
11101
  if (service === undefined) {
11106
11102
  return Result.error(
11107
11103
  UpdateError.NoService,
11108
- `Attempting to update preimage of non-existing service: ${serviceId}`,
11104
+ () => `Attempting to update preimage of non-existing service: ${serviceId}`,
11109
11105
  );
11110
11106
  }
11111
- const { kind } = action;
11112
- if (kind === UpdatePreimageKind.Provide) {
11113
- const { preimage, slot } = action;
11114
- if (service.data.preimages.has(preimage.hash)) {
11115
- return Result.error(UpdateError.PreimageExists, `Overwriting existing preimage at ${serviceId}: ${preimage}`);
11116
- }
11117
- service.data.preimages.set(preimage.hash, preimage);
11118
- if (slot !== null) {
11119
- const lookupHistory = service.data.lookupHistory.get(preimage.hash);
11120
- const length = tryAsU32(preimage.blob.length);
11121
- const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
11122
- if (lookupHistory === undefined) {
11123
- // no lookup history for that preimage at all (edge case, should be requested)
11124
- service.data.lookupHistory.set(preimage.hash, [lookup]);
11125
- } else {
11126
- // insert or replace exiting entry
11127
- const index = lookupHistory.map((x) => x.length).indexOf(length);
11128
- lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
11107
+ for (const update of updates) {
11108
+ const { kind } = update.action;
11109
+ if (kind === UpdatePreimageKind.Provide) {
11110
+ const { preimage, slot } = update.action;
11111
+ if (service.data.preimages.has(preimage.hash)) {
11112
+ return Result.error(
11113
+ UpdateError.PreimageExists,
11114
+ () => `Overwriting existing preimage at ${serviceId}: ${preimage}`,
11115
+ );
11129
11116
  }
11117
+ service.data.preimages.set(preimage.hash, preimage);
11118
+ if (slot !== null) {
11119
+ const lookupHistory = service.data.lookupHistory.get(preimage.hash);
11120
+ const length = tryAsU32(preimage.blob.length);
11121
+ const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
11122
+ if (lookupHistory === undefined) {
11123
+ // no lookup history for that preimage at all (edge case, should be requested)
11124
+ service.data.lookupHistory.set(preimage.hash, [lookup]);
11125
+ } else {
11126
+ // insert or replace exiting entry
11127
+ const index = lookupHistory.map((x) => x.length).indexOf(length);
11128
+ lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
11129
+ }
11130
+ }
11131
+ } else if (kind === UpdatePreimageKind.Remove) {
11132
+ const { hash, length } = update.action;
11133
+ service.data.preimages.delete(hash);
11134
+ const history = service.data.lookupHistory.get(hash) ?? [];
11135
+ const idx = history.map((x) => x.length).indexOf(length);
11136
+ if (idx !== -1) {
11137
+ history.splice(idx, 1);
11138
+ }
11139
+ } else if (kind === UpdatePreimageKind.UpdateOrAdd) {
11140
+ const { item } = update.action;
11141
+ const history = service.data.lookupHistory.get(item.hash) ?? [];
11142
+ const existingIdx = history.map((x) => x.length).indexOf(item.length);
11143
+ const removeCount = existingIdx === -1 ? 0 : 1;
11144
+ history.splice(existingIdx, removeCount, item);
11145
+ service.data.lookupHistory.set(item.hash, history);
11146
+ } else {
11147
+ assertNever(kind);
11130
11148
  }
11131
- } else if (kind === UpdatePreimageKind.Remove) {
11132
- const { hash, length } = action;
11133
- service.data.preimages.delete(hash);
11134
- const history = service.data.lookupHistory.get(hash) ?? [];
11135
- const idx = history.map((x) => x.length).indexOf(length);
11136
- if (idx !== -1) {
11137
- history.splice(idx, 1);
11138
- }
11139
- } else if (kind === UpdatePreimageKind.UpdateOrAdd) {
11140
- const { item } = action;
11141
- const history = service.data.lookupHistory.get(item.hash) ?? [];
11142
- const existingIdx = history.map((x) => x.length).indexOf(item.length);
11143
- const removeCount = existingIdx === -1 ? 0 : 1;
11144
- history.splice(existingIdx, removeCount, item);
11145
- service.data.lookupHistory.set(item.hash, history);
11146
- } else {
11147
- assertNever(kind);
11148
11149
  }
11149
11150
  }
11150
11151
  return Result.ok(OK);
11151
11152
  }
11152
11153
 
11153
- private updateServices(servicesUpdates?: UpdateService[]): Result$2<OK, UpdateError> {
11154
- for (const { serviceId, action } of servicesUpdates ?? []) {
11155
- const { kind, account } = action;
11154
+ private updateServices(servicesUpdates: Map<ServiceId, UpdateService> | undefined): Result$2<OK, UpdateError> {
11155
+ if (servicesUpdates === undefined) {
11156
+ return Result.ok(OK);
11157
+ }
11158
+ for (const [serviceId, update] of servicesUpdates.entries()) {
11159
+ const { kind, account } = update.action;
11156
11160
  if (kind === UpdateServiceKind.Create) {
11157
- const { lookupHistory } = action;
11161
+ const { lookupHistory } = update.action;
11158
11162
  if (this.services.has(serviceId)) {
11159
- return Result.error(UpdateError.DuplicateService, `${serviceId} already exists!`);
11163
+ return Result.error(UpdateError.DuplicateService, () => `${serviceId} already exists!`);
11160
11164
  }
11161
11165
  this.services.set(
11162
11166
  serviceId,
@@ -11172,7 +11176,7 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11172
11176
  } else if (kind === UpdateServiceKind.Update) {
11173
11177
  const existingService = this.services.get(serviceId);
11174
11178
  if (existingService === undefined) {
11175
- return Result.error(UpdateError.NoService, `Cannot update ${serviceId} because it does not exist.`);
11179
+ return Result.error(UpdateError.NoService, () => `Cannot update ${serviceId} because it does not exist.`);
11176
11180
  }
11177
11181
  existingService.data.info = account;
11178
11182
  } else {
@@ -11975,89 +11979,104 @@ declare function* serializeStateUpdate(
11975
11979
  const encode = <T>(codec: Encode<T>, val: T) => Encoder.encodeObject(codec, val, spec);
11976
11980
 
11977
11981
  // then let's proceed with service updates
11978
- yield* serializeServiceUpdates(update.servicesUpdates, encode, blake2b);
11982
+ yield* serializeServiceUpdates(update.updated, encode, blake2b);
11979
11983
  yield* serializePreimages(update.preimages, encode, blake2b);
11980
11984
  yield* serializeStorage(update.storage, blake2b);
11981
- yield* serializeRemovedServices(update.servicesRemoved);
11985
+ yield* serializeRemovedServices(update.removed);
11982
11986
  }
11983
11987
 
11984
11988
  declare function* serializeRemovedServices(servicesRemoved: ServiceId[] | undefined): Generator<StateEntryUpdate> {
11985
- for (const serviceId of servicesRemoved ?? []) {
11989
+ if (servicesRemoved === undefined) {
11990
+ return;
11991
+ }
11992
+ for (const serviceId of servicesRemoved) {
11986
11993
  // TODO [ToDr] what about all data associated with a service?
11987
11994
  const codec = serialize.serviceData(serviceId);
11988
11995
  yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
11989
11996
  }
11990
11997
  }
11991
11998
 
11992
- declare function* serializeStorage(storage: UpdateStorage[] | undefined, blake2b: Blake2b): Generator<StateEntryUpdate> {
11993
- for (const { action, serviceId } of storage ?? []) {
11994
- switch (action.kind) {
11995
- case UpdateStorageKind.Set: {
11996
- const key = action.storage.key;
11997
- const codec = serialize.serviceStorage(blake2b, serviceId, key);
11998
- yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
11999
- break;
12000
- }
12001
- case UpdateStorageKind.Remove: {
12002
- const key = action.key;
12003
- const codec = serialize.serviceStorage(blake2b, serviceId, key);
12004
- yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
12005
- break;
11999
+ declare function* serializeStorage(
12000
+ storageUpdates: Map<ServiceId, UpdateStorage[]> | undefined,
12001
+ blake2b: Blake2b,
12002
+ ): Generator<StateEntryUpdate> {
12003
+ if (storageUpdates === undefined) {
12004
+ return;
12005
+ }
12006
+ for (const [serviceId, updates] of storageUpdates.entries()) {
12007
+ for (const { action } of updates) {
12008
+ switch (action.kind) {
12009
+ case UpdateStorageKind.Set: {
12010
+ const key = action.storage.key;
12011
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
12012
+ yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
12013
+ break;
12014
+ }
12015
+ case UpdateStorageKind.Remove: {
12016
+ const key = action.key;
12017
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
12018
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
12019
+ break;
12020
+ }
12006
12021
  }
12007
- default:
12008
- assertNever(action);
12009
12022
  }
12010
12023
  }
12011
12024
  }
12012
12025
 
12013
12026
  declare function* serializePreimages(
12014
- preimages: UpdatePreimage[] | undefined,
12027
+ preimagesUpdates: Map<ServiceId, UpdatePreimage[]> | undefined,
12015
12028
  encode: EncodeFun,
12016
12029
  blake2b: Blake2b,
12017
12030
  ): Generator<StateEntryUpdate> {
12018
- for (const { action, serviceId } of preimages ?? []) {
12019
- switch (action.kind) {
12020
- case UpdatePreimageKind.Provide: {
12021
- const { hash, blob } = action.preimage;
12022
- const codec = serialize.servicePreimages(blake2b, serviceId, hash);
12023
- yield [StateEntryUpdateAction.Insert, codec.key, blob];
12024
-
12025
- if (action.slot !== null) {
12026
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
12027
- yield [
12028
- StateEntryUpdateAction.Insert,
12029
- codec2.key,
12030
- encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
12031
- ];
12031
+ if (preimagesUpdates === undefined) {
12032
+ return;
12033
+ }
12034
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
12035
+ for (const { action } of updates) {
12036
+ switch (action.kind) {
12037
+ case UpdatePreimageKind.Provide: {
12038
+ const { hash, blob } = action.preimage;
12039
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
12040
+ yield [StateEntryUpdateAction.Insert, codec.key, blob];
12041
+
12042
+ if (action.slot !== null) {
12043
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
12044
+ yield [
12045
+ StateEntryUpdateAction.Insert,
12046
+ codec2.key,
12047
+ encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
12048
+ ];
12049
+ }
12050
+ break;
12032
12051
  }
12033
- break;
12034
- }
12035
- case UpdatePreimageKind.UpdateOrAdd: {
12036
- const { hash, length, slots } = action.item;
12037
- const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12038
- yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
12039
- break;
12040
- }
12041
- case UpdatePreimageKind.Remove: {
12042
- const { hash, length } = action;
12043
- const codec = serialize.servicePreimages(blake2b, serviceId, hash);
12044
- yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
12052
+ case UpdatePreimageKind.UpdateOrAdd: {
12053
+ const { hash, length, slots } = action.item;
12054
+ const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12055
+ yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
12056
+ break;
12057
+ }
12058
+ case UpdatePreimageKind.Remove: {
12059
+ const { hash, length } = action;
12060
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
12061
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
12045
12062
 
12046
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12047
- yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
12048
- break;
12063
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12064
+ yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
12065
+ break;
12066
+ }
12049
12067
  }
12050
- default:
12051
- assertNever(action);
12052
12068
  }
12053
12069
  }
12054
12070
  }
12055
12071
  declare function* serializeServiceUpdates(
12056
- servicesUpdates: UpdateService[] | undefined,
12072
+ servicesUpdates: Map<ServiceId, UpdateService> | undefined,
12057
12073
  encode: EncodeFun,
12058
12074
  blake2b: Blake2b,
12059
12075
  ): Generator<StateEntryUpdate> {
12060
- for (const { action, serviceId } of servicesUpdates ?? []) {
12076
+ if (servicesUpdates === undefined) {
12077
+ return;
12078
+ }
12079
+ for (const [serviceId, { action }] of servicesUpdates.entries()) {
12061
12080
  // new service being created or updated
12062
12081
  const codec = serialize.serviceData(serviceId);
12063
12082
  yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, action.account)];
@@ -12663,7 +12682,7 @@ declare class LeafDb implements SerializedStateBackend {
12663
12682
  if (blob.length % TRIE_NODE_BYTES !== 0) {
12664
12683
  return Result.error(
12665
12684
  LeafDbError.InvalidLeafData,
12666
- `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`,
12685
+ () => `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`,
12667
12686
  );
12668
12687
  }
12669
12688
 
@@ -12671,7 +12690,7 @@ declare class LeafDb implements SerializedStateBackend {
12671
12690
  for (const nodeData of blob.chunks(TRIE_NODE_BYTES)) {
12672
12691
  const node = new TrieNode(nodeData.raw);
12673
12692
  if (node.getNodeType() === NodeType.Branch) {
12674
- return Result.error(LeafDbError.InvalidLeafData, `Branch node detected: ${nodeData}`);
12693
+ return Result.error(LeafDbError.InvalidLeafData, () => `Branch node detected: ${nodeData}`);
12675
12694
  }
12676
12695
  leaves.insert(node.asLeafNode());
12677
12696
  }
@@ -14196,6 +14215,82 @@ interface GasCounter {
14196
14215
  sub(g: Gas): boolean;
14197
14216
  }
14198
14217
 
14218
+ declare const NO_OF_REGISTERS$1 = 13;
14219
+
14220
+ type RegisterIndex = Opaque<number, "register index">;
14221
+
14222
+ declare class Registers {
14223
+ private asSigned: BigInt64Array;
14224
+ private asUnsigned: BigUint64Array;
14225
+
14226
+ constructor(private readonly bytes = safeAllocUint8Array(NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
14227
+ check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
14228
+ this.asSigned = new BigInt64Array(bytes.buffer, bytes.byteOffset);
14229
+ this.asUnsigned = new BigUint64Array(bytes.buffer, bytes.byteOffset);
14230
+ }
14231
+
14232
+ static fromBytes(bytes: Uint8Array) {
14233
+ check`${bytes.length === NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
14234
+ return new Registers(bytes);
14235
+ }
14236
+
14237
+ getBytesAsLittleEndian(index: number, len: number) {
14238
+ const offset = index << REGISTER_SIZE_SHIFT;
14239
+ return this.bytes.subarray(offset, offset + len);
14240
+ }
14241
+
14242
+ getAllBytesAsLittleEndian() {
14243
+ return this.bytes;
14244
+ }
14245
+
14246
+ copyFrom(regs: Registers | BigUint64Array) {
14247
+ const array = regs instanceof BigUint64Array ? regs : regs.asUnsigned;
14248
+ this.asUnsigned.set(array);
14249
+ }
14250
+
14251
+ reset() {
14252
+ for (let i = 0; i < NO_OF_REGISTERS; i++) {
14253
+ this.asUnsigned[i] = 0n;
14254
+ }
14255
+ }
14256
+
14257
+ getLowerU32(registerIndex: number) {
14258
+ return Number(this.asUnsigned[registerIndex] & 0xff_ff_ff_ffn);
14259
+ }
14260
+
14261
+ getLowerI32(registerIndex: number) {
14262
+ return Number(this.getLowerU32(registerIndex)) >> 0;
14263
+ }
14264
+
14265
+ setU32(registerIndex: number, value: number) {
14266
+ this.asUnsigned[registerIndex] = signExtend32To64(value);
14267
+ }
14268
+
14269
+ setI32(registerIndex: number, value: number) {
14270
+ this.asSigned[registerIndex] = signExtend32To64(value);
14271
+ }
14272
+
14273
+ getU64(registerIndex: number) {
14274
+ return this.asUnsigned[registerIndex];
14275
+ }
14276
+
14277
+ getI64(registerIndex: number) {
14278
+ return this.asSigned[registerIndex];
14279
+ }
14280
+
14281
+ setU64(registerIndex: number, value: bigint) {
14282
+ this.asUnsigned[registerIndex] = value;
14283
+ }
14284
+
14285
+ setI64(registerIndex: number, value: bigint) {
14286
+ this.asSigned[registerIndex] = value;
14287
+ }
14288
+
14289
+ getAllU64() {
14290
+ return this.asUnsigned;
14291
+ }
14292
+ }
14293
+
14199
14294
  /**
14200
14295
  * Mask class is an implementation of skip function defined in GP.
14201
14296
  *
@@ -14365,82 +14460,6 @@ declare class ImmediateDecoder {
14365
14460
  }
14366
14461
  }
14367
14462
 
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
14463
  declare class NibblesDecoder {
14445
14464
  private byte = new Int8Array(1);
14446
14465
 
@@ -15483,7 +15502,7 @@ declare class Memory {
15483
15502
  const pagesResult = this.getPages(address, bytes.length, AccessType.WRITE);
15484
15503
 
15485
15504
  if (pagesResult.isError) {
15486
- return Result.error(pagesResult.error);
15505
+ return Result.error(pagesResult.error, pagesResult.details);
15487
15506
  }
15488
15507
 
15489
15508
  const pages = pagesResult.ok;
@@ -15516,17 +15535,23 @@ declare class Memory {
15516
15535
 
15517
15536
  for (const pageNumber of pageRange) {
15518
15537
  if (pageNumber < RESERVED_NUMBER_OF_PAGES) {
15519
- return Result.error(PageFault.fromPageNumber(pageNumber, true));
15538
+ return Result.error(
15539
+ PageFault.fromPageNumber(pageNumber, true),
15540
+ () => `Page fault: attempted to access reserved page ${pageNumber}`,
15541
+ );
15520
15542
  }
15521
15543
 
15522
15544
  const page = this.memory.get(pageNumber);
15523
15545
 
15524
15546
  if (page === undefined) {
15525
- return Result.error(PageFault.fromPageNumber(pageNumber));
15547
+ return Result.error(PageFault.fromPageNumber(pageNumber), () => `Page fault: page ${pageNumber} not allocated`);
15526
15548
  }
15527
15549
 
15528
15550
  if (accessType === AccessType.WRITE && !page.isWriteable()) {
15529
- return Result.error(PageFault.fromPageNumber(pageNumber, true));
15551
+ return Result.error(
15552
+ PageFault.fromPageNumber(pageNumber, true),
15553
+ () => `Page fault: attempted to write to read-only page ${pageNumber}`,
15554
+ );
15530
15555
  }
15531
15556
 
15532
15557
  pages.push(page);
@@ -15548,7 +15573,7 @@ declare class Memory {
15548
15573
  const pagesResult = this.getPages(startAddress, result.length, AccessType.READ);
15549
15574
 
15550
15575
  if (pagesResult.isError) {
15551
- return Result.error(pagesResult.error);
15576
+ return Result.error(pagesResult.error, pagesResult.details);
15552
15577
  }
15553
15578
 
15554
15579
  const pages = pagesResult.ok;
@@ -17533,7 +17558,7 @@ declare class ProgramDecoder {
17533
17558
  return Result.ok(new ProgramDecoder(program));
17534
17559
  } catch (e) {
17535
17560
  logger.error`Invalid program: ${e}`;
17536
- return Result.error(ProgramDecoderError.InvalidProgramError);
17561
+ return Result.error(ProgramDecoderError.InvalidProgramError, () => `Program decoder error: ${e}`);
17537
17562
  }
17538
17563
  }
17539
17564
  }
@@ -17877,870 +17902,896 @@ declare namespace index$8 {
17877
17902
  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
17903
  }
17879
17904
 
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));
17888
-
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));
17893
-
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
- };
17903
- }
17905
+ interface IHostCallMemory {
17906
+ storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds>;
17907
+ loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds>;
17904
17908
  }
17905
17909
 
17906
- type MachineStatus =
17907
- | {
17908
- status: typeof Status.HOST;
17909
- hostCallIndex: U64;
17910
+ declare class HostCallMemory implements IHostCallMemory {
17911
+ constructor(private readonly memory: Memory) {}
17912
+
17913
+ storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds> {
17914
+ if (bytes.length === 0) {
17915
+ return Result.ok(OK);
17910
17916
  }
17911
- | {
17912
- status: typeof Status.FAULT;
17913
- address: U64;
17917
+
17918
+ if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
17919
+ return Result.error(
17920
+ new OutOfBounds(),
17921
+ () => `Memory access out of bounds: address ${address} + length ${bytes.length} exceeds memory size`,
17922
+ );
17914
17923
  }
17915
- | {
17916
- status: typeof Status.OK | typeof Status.HALT | typeof Status.PANIC | typeof Status.OOG;
17917
- };
17918
17924
 
17919
- /** Data returned by a machine invocation. */
17920
- type MachineResult = {
17921
- result: MachineStatus;
17922
- gas: BigGas;
17923
- registers: Registers;
17924
- };
17925
+ return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
17926
+ }
17925
17927
 
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,
17938
- }
17928
+ loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds> {
17929
+ if (result.length === 0) {
17930
+ return Result.ok(OK);
17931
+ }
17939
17932
 
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;
17933
+ if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
17934
+ return Result.error(
17935
+ new OutOfBounds(),
17936
+ () => `Memory access out of bounds: address ${startAddress} + length ${result.length} exceeds memory size`,
17937
+ );
17938
+ }
17943
17939
 
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,
17940
+ return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
17941
+ }
17952
17942
  }
17953
17943
 
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,
17944
+ interface IHostCallRegisters {
17945
+ get(registerIndex: number): U64;
17946
+ set(registerIndex: number, value: U64): void;
17959
17947
  }
17960
17948
 
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
- }
17949
+ declare class HostCallRegisters implements IHostCallRegisters {
17950
+ constructor(private readonly registers: Registers) {}
17969
17951
 
17970
- /** Error machine is not found. */
17971
- declare const NoMachineError = Symbol("Machine index not found.");
17972
- type NoMachineError = typeof NoMachineError;
17952
+ get(registerIndex: number): U64 {
17953
+ return tryAsU64(this.registers.getU64(registerIndex));
17954
+ }
17973
17955
 
17974
- /** Too many segments already exported. */
17975
- declare const SegmentExportError = Symbol("Too many segments already exported.");
17976
- type SegmentExportError = typeof SegmentExportError;
17956
+ set(registerIndex: number, value: U64) {
17957
+ this.registers.setU64(registerIndex, value);
17958
+ }
17959
+ }
17977
17960
 
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>>;
17961
+ /** Strictly-typed host call index. */
17962
+ type HostCallIndex = Opaque<U32, "HostCallIndex[U32]">;
17963
+ /** Attempt to convert a number into `HostCallIndex`. */
17964
+ declare const tryAsHostCallIndex = (v: number): HostCallIndex => asOpaqueType(tryAsU32(v));
17982
17965
 
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>>;
17966
+ /**
17967
+ * Host-call exit reason.
17968
+ *
17969
+ * https://graypaper.fluffylabs.dev/#/ab2cdbd/24a30124a501?v=0.7.2
17970
+ */
17971
+ declare enum PvmExecution {
17972
+ Halt = 0,
17973
+ Panic = 1,
17974
+ OOG = 2, // out-of-gas
17975
+ }
17985
17976
 
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>>;
17977
+ /** A utility function to easily trace a bunch of registers. */
17978
+ declare function traceRegisters(...regs: number[]) {
17979
+ return regs.map(tryAsRegisterIndex);
17980
+ }
17988
17981
 
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>>;
17982
+ /** An interface for a host call implementation */
17983
+ interface HostCallHandler {
17984
+ /** Index of that host call (i.e. what PVM invokes via `ecalli`) */
17985
+ readonly index: HostCallIndex;
17997
17986
 
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>>;
17987
+ /**
17988
+ * The gas cost of invocation of that host call.
17989
+ *
17990
+ * NOTE: `((reg: IHostCallRegisters) => Gas)` function is for compatibility reasons: pre GP 0.7.2
17991
+ */
17992
+ readonly basicGasCost: SmallGas | ((reg: IHostCallRegisters) => Gas);
18006
17993
 
18007
- /** Start an inner PVM instance with given entry point and starting code. */
18008
- machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise<Result$2<MachineId, ProgramDecoderError>>;
17994
+ /** Currently executing service id. */
17995
+ readonly currentServiceId: U32;
18009
17996
 
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>>;
17997
+ /** Input&Output registers that we should add to tracing log. */
17998
+ readonly tracedRegisters: RegisterIndex[];
18016
17999
 
18017
18000
  /**
18018
- * Export segment for future retrieval.
18001
+ * Actually execute the host call.
18019
18002
  *
18020
- * Returns the index assigned to that segment or an error if there is too many already exported.
18003
+ * NOTE the call is ALLOWED and expected to modify registers and memory.
18021
18004
  */
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>>;
18005
+ execute(gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise<undefined | PvmExecution>;
18034
18006
  }
18035
18007
 
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;
18008
+ /** Container for all available host calls. */
18009
+ declare class HostCallsManager {
18010
+ private readonly hostCalls = new Map<HostCallIndex, HostCallHandler>();
18011
+ private readonly missing;
18042
18012
 
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;
18013
+ constructor({
18014
+ missing,
18015
+ handlers = [],
18016
+ }: {
18017
+ missing: HostCallHandler;
18018
+ handlers?: HostCallHandler[];
18019
+ }) {
18020
+ this.missing = missing;
18056
18021
 
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
- ) {}
18022
+ for (const handler of handlers) {
18023
+ check`${this.hostCalls.get(handler.index) === undefined} Overwriting host call handler at index ${handler.index}`;
18024
+ this.hostCalls.set(handler.index, handler);
18025
+ }
18026
+ }
18065
18027
 
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
- );
18028
+ /** Get a host call by index. */
18029
+ get(hostCallIndex: HostCallIndex): HostCallHandler {
18030
+ return this.hostCalls.get(hostCallIndex) ?? this.missing;
18077
18031
  }
18078
18032
 
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
- );
18033
+ traceHostCall(
18034
+ context: string,
18035
+ hostCallIndex: HostCallIndex,
18036
+ hostCallHandler: HostCallHandler,
18037
+ registers: IHostCallRegisters,
18038
+ gas: Gas,
18039
+ ) {
18040
+ const { currentServiceId } = hostCallHandler;
18041
+ const requested = hostCallIndex !== hostCallHandler.index ? ` (${hostCallIndex})` : "";
18042
+ const name = `${hostCallHandler.constructor.name}:${hostCallHandler.index}`;
18043
+ const registerValues = hostCallHandler.tracedRegisters
18044
+ .map((idx) => [idx.toString().padStart(2, "0"), registers.get(idx)] as const)
18045
+ .filter((v) => v[1] !== 0n)
18046
+ .map(([idx, value]) => {
18047
+ return `r${idx}=${value} (0x${value.toString(16)})`;
18048
+ })
18049
+ .join(", ");
18050
+ logger.insane`[${currentServiceId}] ${context} ${name}${requested}. Gas: ${gas}. Regs: ${registerValues}.`;
18087
18051
  }
18052
+ }
18088
18053
 
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));
18054
+ type ResolveFn = (pvm: Interpreter) => void;
18099
18055
 
18100
- // update entries
18101
- for (const [k, v] of from.authorizationQueues) {
18102
- update.authorizationQueues.set(k, v);
18103
- }
18056
+ declare class InterpreterInstanceManager {
18057
+ private instances: Interpreter[] = [];
18058
+ private waitingQueue: ResolveFn[] = [];
18104
18059
 
18105
- if (from.validatorsData !== null) {
18106
- update.validatorsData = asKnownSize([...from.validatorsData]);
18060
+ constructor(noOfPvmInstances: number) {
18061
+ for (let i = 0; i < noOfPvmInstances; i++) {
18062
+ this.instances.push(
18063
+ new Interpreter({
18064
+ useSbrkGas: false,
18065
+ }),
18066
+ );
18107
18067
  }
18068
+ }
18108
18069
 
18109
- if (from.privilegedServices !== null) {
18110
- update.privilegedServices = PrivilegedServices.create({
18111
- ...from.privilegedServices,
18112
- assigners: asKnownSize([...from.privilegedServices.assigners]),
18113
- });
18070
+ async getInstance(): Promise<Interpreter> {
18071
+ const instance = this.instances.pop();
18072
+ if (instance !== undefined) {
18073
+ return Promise.resolve(instance);
18114
18074
  }
18115
- return update;
18075
+ return new Promise((resolve) => {
18076
+ this.waitingQueue.push(resolve);
18077
+ });
18116
18078
  }
18117
18079
 
18118
- /** Retrieve and clear pending transfers. */
18119
- takeTransfers() {
18120
- const transfers = this.transfers;
18121
- this.transfers = [];
18122
- return transfers;
18080
+ releaseInstance(pvm: Interpreter) {
18081
+ const waiting = this.waitingQueue.shift();
18082
+ if (waiting !== undefined) {
18083
+ return waiting(pvm);
18084
+ }
18085
+ this.instances.push(pvm);
18123
18086
  }
18124
18087
  }
18125
18088
 
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,
18089
+ declare class ReturnValue {
18090
+ private constructor(
18091
+ public consumedGas: Gas,
18092
+ public status: Status | null,
18093
+ public memorySlice: Uint8Array | null,
18136
18094
  ) {
18137
- this.stateUpdate =
18138
- stateUpdate === undefined ? AccumulationStateUpdate.empty() : AccumulationStateUpdate.copyFrom(stateUpdate);
18095
+ check`
18096
+ ${(status === null && memorySlice !== null) || (status !== null && memorySlice === null)}
18097
+ 'status' and 'memorySlice' must not both be null or both be non-null — exactly one must be provided
18098
+ `;
18139
18099
  }
18140
18100
 
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
- }
18101
+ static fromStatus(consumedGas: Gas, status: Status) {
18102
+ return new ReturnValue(consumedGas, status, null);
18103
+ }
18165
18104
 
18166
- return maybeService.getInfo();
18105
+ static fromMemorySlice(consumedGas: Gas, memorySlice: Uint8Array) {
18106
+ return new ReturnValue(consumedGas, null, memorySlice);
18167
18107
  }
18168
18108
 
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
- }
18109
+ hasMemorySlice(): this is this & { status: null; memorySlice: Uint8Array } {
18110
+ return this.memorySlice instanceof Uint8Array && this.status === null;
18111
+ }
18174
18112
 
18175
- const service = this.state.getService(serviceId);
18176
- return service?.getStorage(rawKey) ?? null;
18113
+ hasStatus(): this is this & { status: Status; memorySlice: null } {
18114
+ return !this.hasMemorySlice();
18177
18115
  }
18116
+ }
18117
+ declare class HostCalls {
18118
+ constructor(
18119
+ private pvmInstanceManager: InterpreterInstanceManager,
18120
+ private hostCalls: HostCallsManager,
18121
+ ) {}
18178
18122
 
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;
18123
+ private getReturnValue(status: Status, pvmInstance: Interpreter): ReturnValue {
18124
+ const gasConsumed = pvmInstance.getGasConsumed();
18125
+ if (status === Status.OOG) {
18126
+ return ReturnValue.fromStatus(gasConsumed, status);
18194
18127
  }
18195
18128
 
18196
- // fallback to state preimages
18197
- const service = this.state.getService(serviceId);
18198
- if (service === undefined) {
18199
- return false;
18200
- }
18129
+ if (status === Status.HALT) {
18130
+ const memory = pvmInstance.getMemory();
18131
+ const regs = pvmInstance.getRegisters();
18132
+ const maybeAddress = regs.getLowerU32(7);
18133
+ const maybeLength = regs.getLowerU32(8);
18201
18134
 
18202
- return service?.hasPreimage(hash) ?? false;
18203
- }
18135
+ const result = safeAllocUint8Array(maybeLength);
18136
+ const startAddress = tryAsMemoryIndex(maybeAddress);
18137
+ const loadResult = memory.loadInto(result, startAddress);
18204
18138
 
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;
18139
+ if (loadResult.isError) {
18140
+ return ReturnValue.fromMemorySlice(gasConsumed, new Uint8Array());
18141
+ }
18142
+
18143
+ return ReturnValue.fromMemorySlice(gasConsumed, result);
18212
18144
  }
18213
18145
 
18214
- const service = this.state.getService(serviceId);
18215
- return service?.getPreimage(hash) ?? null;
18146
+ return ReturnValue.fromStatus(gasConsumed, Status.PANIC);
18216
18147
  }
18217
18148
 
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;
18149
+ private async execute(pvmInstance: Interpreter) {
18150
+ pvmInstance.runProgram();
18151
+ for (;;) {
18152
+ let status = pvmInstance.getStatus();
18153
+ if (status !== Status.HOST) {
18154
+ return this.getReturnValue(status, pvmInstance);
18238
18155
  }
18156
+ check`
18157
+ ${pvmInstance.getExitParam() !== null}
18158
+ "We know that the exit param is not null, because the status is 'Status.HOST'
18159
+ `;
18160
+ const hostCallIndex = pvmInstance.getExitParam() ?? -1;
18161
+ const gas = pvmInstance.getGasCounter();
18162
+ const regs = new HostCallRegisters(pvmInstance.getRegisters());
18163
+ const memory = new HostCallMemory(pvmInstance.getMemory());
18164
+ const index = tryAsHostCallIndex(hostCallIndex);
18239
18165
 
18240
- const slots = service.getLookupHistory(hash, lenU32);
18241
- return slots === null ? null : new LookupHistoryItem(hash, lenU32, slots);
18242
- };
18243
-
18244
- if (updatedPreimage === undefined) {
18245
- return stateFallback();
18246
- }
18166
+ const hostCall = this.hostCalls.get(index);
18167
+ const gasBefore = gas.get();
18168
+ // NOTE: `basicGasCost(regs)` function is for compatibility reasons: pre GP 0.7.2
18169
+ const basicGasCost =
18170
+ typeof hostCall.basicGasCost === "number" ? hostCall.basicGasCost : hostCall.basicGasCost(regs);
18171
+ const underflow = gas.sub(basicGasCost);
18247
18172
 
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]));
18173
+ const pcLog = `[PC: ${pvmInstance.getPC()}]`;
18174
+ if (underflow) {
18175
+ this.hostCalls.traceHostCall(`${pcLog} OOG`, index, hostCall, regs, gas.get());
18176
+ return ReturnValue.fromStatus(pvmInstance.getGasConsumed(), Status.OOG);
18253
18177
  }
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
- }
18178
+ this.hostCalls.traceHostCall(`${pcLog} Invoking`, index, hostCall, regs, gasBefore);
18179
+ const result = await hostCall.execute(gas, regs, memory);
18180
+ this.hostCalls.traceHostCall(
18181
+ result === undefined ? `${pcLog} Result` : `${pcLog} Status(${PvmExecution[result]})`,
18182
+ index,
18183
+ hostCall,
18184
+ regs,
18185
+ gas.get(),
18186
+ );
18260
18187
 
18261
- return new LookupHistoryItem(hash, state.length, tryAsLookupHistorySlots([...state.slots, currentTimeslot]));
18188
+ if (result === PvmExecution.Halt) {
18189
+ status = Status.HALT;
18190
+ return this.getReturnValue(status, pvmInstance);
18262
18191
  }
18263
- case UpdatePreimageKind.UpdateOrAdd: {
18264
- return action.item;
18192
+
18193
+ if (result === PvmExecution.Panic) {
18194
+ status = Status.PANIC;
18195
+ return this.getReturnValue(status, pvmInstance);
18265
18196
  }
18266
- }
18267
18197
 
18268
- assertNever(action);
18269
- }
18198
+ if (result === PvmExecution.OOG) {
18199
+ status = Status.OOG;
18200
+ return this.getReturnValue(status, pvmInstance);
18201
+ }
18270
18202
 
18271
- /* State update functions. */
18203
+ if (result === undefined) {
18204
+ pvmInstance.runProgram();
18205
+ status = pvmInstance.getStatus();
18206
+ continue;
18207
+ }
18272
18208
 
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
- });
18209
+ assertNever(result);
18210
+ }
18211
+ }
18281
18212
 
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);
18213
+ async runProgram(
18214
+ rawProgram: Uint8Array,
18215
+ initialPc: number,
18216
+ initialGas: Gas,
18217
+ maybeRegisters?: Registers,
18218
+ maybeMemory?: Memory,
18219
+ ): Promise<ReturnValue> {
18220
+ const pvmInstance = await this.pvmInstanceManager.getInstance();
18221
+ pvmInstance.reset(rawProgram, initialPc, initialGas, maybeRegisters, maybeMemory);
18222
+ try {
18223
+ return await this.execute(pvmInstance);
18224
+ } finally {
18225
+ this.pvmInstanceManager.releaseInstance(pvmInstance);
18226
+ }
18287
18227
  }
18228
+ }
18288
18229
 
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
- }
18230
+ type index$7_HostCallHandler = HostCallHandler;
18231
+ type index$7_HostCallMemory = HostCallMemory;
18232
+ declare const index$7_HostCallMemory: typeof HostCallMemory;
18233
+ type index$7_HostCallRegisters = HostCallRegisters;
18234
+ declare const index$7_HostCallRegisters: typeof HostCallRegisters;
18235
+ type index$7_IHostCallMemory = IHostCallMemory;
18236
+ type index$7_IHostCallRegisters = IHostCallRegisters;
18237
+ type index$7_PvmExecution = PvmExecution;
18238
+ declare const index$7_PvmExecution: typeof PvmExecution;
18239
+ declare const index$7_traceRegisters: typeof traceRegisters;
18240
+ declare const index$7_tryAsHostCallIndex: typeof tryAsHostCallIndex;
18241
+ declare namespace index$7 {
18242
+ 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 };
18243
+ export type { index$7_HostCallHandler as HostCallHandler, index$7_IHostCallMemory as IHostCallMemory, index$7_IHostCallRegisters as IHostCallRegisters };
18244
+ }
18298
18245
 
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}`;
18246
+ /**
18247
+ * Program counter is a 64-bit unsigned integer that points to the next instruction
18248
+ *
18249
+ * https://graypaper.fluffylabs.dev/#/1c979cb/2e3f012e3f01?v=0.7.1
18250
+ */
18251
+ type ProgramCounter = Opaque<U64, "ProgramCounter[u64]">;
18252
+ /** Convert a number into ProgramCounter. */
18253
+ declare const tryAsProgramCounter = (v: number | bigint): ProgramCounter => asOpaqueType(tryAsU64(v));
18307
18254
 
18308
- const overflowItems = !isU32(items);
18309
- const overflowBytes = !isU64(bytes);
18255
+ /** Running PVM instance identifier. */
18256
+ type MachineId = Opaque<U64, "MachineId[u64]">;
18257
+ /** Convert a number into PVM instance identifier. */
18258
+ declare const tryAsMachineId = (v: number | bigint): MachineId => asOpaqueType(tryAsU64(v));
18310
18259
 
18311
- // TODO [ToDr] this is not specified in GP, but it seems sensible.
18312
- if (overflowItems || overflowBytes) {
18313
- return Result.error(InsufficientFundsError);
18314
- }
18260
+ declare class MachineInstance {
18261
+ async run(gas: BigGas, registers: Registers): Promise<MachineResult> {
18262
+ return {
18263
+ result: {
18264
+ status: Status.OK,
18265
+ },
18266
+ gas,
18267
+ registers,
18268
+ };
18269
+ }
18270
+ }
18315
18271
 
18316
- const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
18317
- if (serviceInfo.balance < thresholdBalance) {
18318
- return Result.error(InsufficientFundsError);
18272
+ type MachineStatus =
18273
+ | {
18274
+ status: typeof Status.HOST;
18275
+ hostCallIndex: U64;
18276
+ }
18277
+ | {
18278
+ status: typeof Status.FAULT;
18279
+ address: U64;
18319
18280
  }
18281
+ | {
18282
+ status: typeof Status.OK | typeof Status.HALT | typeof Status.PANIC | typeof Status.OOG;
18283
+ };
18320
18284
 
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
- }
18285
+ /** Data returned by a machine invocation. */
18286
+ type MachineResult = {
18287
+ result: MachineStatus;
18288
+ gas: BigGas;
18289
+ registers: Registers;
18290
+ };
18332
18291
 
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];
18337
-
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
- );
18292
+ /** Types of possbile operations to request by Pages host call. */
18293
+ declare enum MemoryOperation {
18294
+ /** Zeroes memory and set access to unreadable. */
18295
+ Void = 0,
18296
+ /** Zeroes memory and set access to read-only. */
18297
+ ZeroRead = 1,
18298
+ /** Zeroes memory and set access to read-write. */
18299
+ ZeroWrite = 2,
18300
+ /** Preserve memory and set access to read-only. */
18301
+ Read = 3,
18302
+ /** Preserve memory and set access to read-write. */
18303
+ Write = 4,
18304
+ }
18348
18305
 
18349
- return;
18350
- }
18306
+ /** Convert a number into MemoryOperation or null (if invalid). */
18307
+ declare const toMemoryOperation = (v: number | bigint): MemoryOperation | null =>
18308
+ v <= MemoryOperation.Write && v >= MemoryOperation.Void ? Number(v) : null;
18351
18309
 
18352
- this.stateUpdate.services.servicesUpdates.splice(
18353
- idx,
18354
- toRemove,
18355
- UpdateService.update({
18356
- serviceId,
18357
- serviceInfo: newInfo,
18358
- }),
18359
- );
18360
- }
18310
+ /** An error that may occur during `peek` or `poke` host call. */
18311
+ declare enum PeekPokeError {
18312
+ /** Source page fault. */
18313
+ SourcePageFault = 0,
18314
+ /** Destination page fault. */
18315
+ DestinationPageFault = 1,
18316
+ /** No machine under given machine index. */
18317
+ NoMachine = 2,
18318
+ }
18361
18319
 
18362
- getPrivilegedServices() {
18363
- if (this.stateUpdate.privilegedServices !== null) {
18364
- return this.stateUpdate.privilegedServices;
18365
- }
18320
+ declare enum ZeroVoidError {
18321
+ /** No machine under given machine index. */
18322
+ NoMachine = 0,
18323
+ /** Attempting to void or zero non-accessible page. */
18324
+ InvalidPage = 1,
18325
+ }
18366
18326
 
18367
- return this.state.privilegedServices;
18368
- }
18327
+ declare enum PagesError {
18328
+ /** No machine under given machine index. */
18329
+ NoMachine = 0,
18330
+ /** Invalid memory operation. */
18331
+ InvalidOperation = 1,
18332
+ /** Attempting to change non-accessible page or trying to preserve value of voided page. */
18333
+ InvalidPage = 2,
18369
18334
  }
18370
18335
 
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));
18336
+ /** Error machine is not found. */
18337
+ declare const NoMachineError = Symbol("Machine index not found.");
18338
+ type NoMachineError = typeof NoMachineError;
18339
+
18340
+ /** Too many segments already exported. */
18341
+ declare const SegmentExportError = Symbol("Too many segments already exported.");
18342
+ type SegmentExportError = typeof SegmentExportError;
18343
+
18344
+ /** Host functions external invocations available during refine phase. */
18345
+ interface RefineExternalities {
18346
+ /** Forget a previously started nested VM. */
18347
+ machineExpunge(machineIndex: MachineId): Promise<Result$2<ProgramCounter, NoMachineError>>;
18348
+
18349
+ /** Set given range of pages as non-accessible and re-initialize them with zeros. */
18350
+ machineVoidPages(machineIndex: MachineId, pageStart: U64, pageCount: U64): Promise<Result$2<OK, ZeroVoidError>>;
18351
+
18352
+ /** Set given range of pages as writeable and initialize them with zeros. */
18353
+ machineZeroPages(machineIndex: MachineId, pageStart: U64, pageCount: U64): Promise<Result$2<OK, ZeroVoidError>>;
18354
+
18355
+ /** Copy a fragment of memory from `machineIndex` into given destination memory. */
18356
+ machinePeekFrom(
18357
+ machineIndex: MachineId,
18358
+ destinationStart: U64,
18359
+ sourceStart: U64,
18360
+ length: U64,
18361
+ destination: IHostCallMemory,
18362
+ ): Promise<Result$2<OK, PeekPokeError>>;
18363
+
18364
+ /** Write a fragment of memory into `machineIndex` from given source memory. */
18365
+ machinePokeInto(
18366
+ machineIndex: MachineId,
18367
+ sourceStart: U64,
18368
+ destinationStart: U64,
18369
+ length: U64,
18370
+ source: IHostCallMemory,
18371
+ ): Promise<Result$2<OK, PeekPokeError>>;
18372
+
18373
+ /** Start an inner PVM instance with given entry point and starting code. */
18374
+ machineInit(code: BytesBlob, programCounter: ProgramCounter): Promise<Result$2<MachineId, ProgramDecoderError>>;
18375
+
18376
+ /** Run a previously initialized PVM instance with given gas and registers. */
18377
+ machineInvoke(
18378
+ machineIndex: MachineId,
18379
+ gas: BigGas,
18380
+ registers: Registers,
18381
+ ): Promise<Result$2<MachineResult, NoMachineError>>;
18382
+
18383
+ /**
18384
+ * Export segment for future retrieval.
18385
+ *
18386
+ * Returns the index assigned to that segment or an error if there is too many already exported.
18387
+ */
18388
+ exportSegment(segment: Segment): Result$2<SegmentIndex, SegmentExportError>;
18389
+
18390
+ /** Lookup a historical preimage. */
18391
+ historicalLookup(serviceId: ServiceId | null, hash: Blake2bHash): Promise<BytesBlob | null>;
18392
+
18393
+ /** Change access to and/or zero the value of memory. */
18394
+ machinePages(
18395
+ machineIndex: MachineId,
18396
+ pageStart: U64,
18397
+ pageCount: U64,
18398
+ requestType: MemoryOperation | null,
18399
+ ): Promise<Result$2<OK, PagesError>>;
18374
18400
  }
18375
18401
 
18376
- /**
18377
- * Host call result constants.
18378
- *
18379
- * https://graypaper.fluffylabs.dev/#/85129da/2c7c022c7c02?v=0.6.3
18380
- */
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;
18402
+ declare const InsufficientFundsError = "insufficient funds";
18403
+ type InsufficientFundsError = typeof InsufficientFundsError;
18403
18404
 
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;
18405
+ /** Update of the state entries coming from accumulation of a single service. */
18406
+ type ServiceStateUpdate = Partial<Pick<State, "privilegedServices" | "authQueues" | "designatedValidatorData">> &
18407
+ ServicesUpdate;
18408
+
18409
+ /** Deep clone of a map with array. */
18410
+ declare function deepCloneMapWithArray<K, V>(map: Map<K, V[]>): Map<K, V[]> {
18411
+ const cloned: [K, V[]][] = [];
18412
+
18413
+ for (const [k, v] of map.entries()) {
18414
+ cloned.push([k, v.slice()]);
18415
+ }
18416
+
18417
+ return new Map(cloned);
18408
18418
  }
18409
18419
 
18410
- declare class HostCallMemory implements IHostCallMemory {
18411
- constructor(private readonly memory: Memory) {}
18420
+ /**
18421
+ * State updates that currently accumulating service produced.
18422
+ *
18423
+ * `x_u`: https://graypaper.fluffylabs.dev/#/9a08063/2f31012f3101?v=0.6.6
18424
+ */
18425
+ declare class AccumulationStateUpdate {
18426
+ /** Updated authorization queues for cores. */
18427
+ public readonly authorizationQueues: Map<CoreIndex, FixedSizeArray<AuthorizerHash, AUTHORIZATION_QUEUE_SIZE>> =
18428
+ new Map();
18429
+ /** New validators data. */
18430
+ public validatorsData: PerValidator<ValidatorData> | null = null;
18431
+ /** Updated priviliged services. */
18432
+ public privilegedServices: PrivilegedServices | null = null;
18412
18433
 
18413
- storeFrom(address: U64, bytes: Uint8Array): Result$2<OK, PageFault | OutOfBounds> {
18414
- if (bytes.length === 0) {
18415
- return Result.ok(OK);
18416
- }
18434
+ private constructor(
18435
+ /** Services state updates. */
18436
+ public readonly services: ServicesUpdate,
18437
+ /** Pending transfers. */
18438
+ public transfers: PendingTransfer[],
18439
+ /** Yielded accumulation root. */
18440
+ public readonly yieldedRoots: Map<ServiceId, OpaqueHash> = new Map(),
18441
+ ) {}
18417
18442
 
18418
- if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
18419
- return Result.error(new OutOfBounds());
18420
- }
18443
+ /** Create new empty state update. */
18444
+ static empty(): AccumulationStateUpdate {
18445
+ return new AccumulationStateUpdate(
18446
+ {
18447
+ created: [],
18448
+ updated: new Map(),
18449
+ removed: [],
18450
+ preimages: new Map(),
18451
+ storage: new Map(),
18452
+ },
18453
+ [],
18454
+ );
18455
+ }
18421
18456
 
18422
- return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
18457
+ /** Create a state update with some existing, yet uncommited services updates. */
18458
+ static new(update: ServicesUpdate): AccumulationStateUpdate {
18459
+ return new AccumulationStateUpdate(
18460
+ {
18461
+ ...update,
18462
+ },
18463
+ [],
18464
+ );
18423
18465
  }
18424
18466
 
18425
- loadInto(result: Uint8Array, startAddress: U64): Result$2<OK, PageFault | OutOfBounds> {
18426
- if (result.length === 0) {
18427
- return Result.ok(OK);
18467
+ /** Create a copy of another `StateUpdate`. Used by checkpoints. */
18468
+ static copyFrom(from: AccumulationStateUpdate): AccumulationStateUpdate {
18469
+ const serviceUpdates: ServicesUpdate = {
18470
+ // shallow copy
18471
+ created: [...from.services.created],
18472
+ updated: new Map(from.services.updated),
18473
+ removed: [...from.services.removed],
18474
+ // deep copy
18475
+ preimages: deepCloneMapWithArray(from.services.preimages),
18476
+ storage: deepCloneMapWithArray(from.services.storage),
18477
+ };
18478
+ const transfers = [...from.transfers];
18479
+ const update = new AccumulationStateUpdate(serviceUpdates, transfers, new Map(from.yieldedRoots));
18480
+
18481
+ // update entries
18482
+ for (const [k, v] of from.authorizationQueues) {
18483
+ update.authorizationQueues.set(k, v);
18428
18484
  }
18429
18485
 
18430
- if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
18431
- return Result.error(new OutOfBounds());
18486
+ if (from.validatorsData !== null) {
18487
+ update.validatorsData = asKnownSize([...from.validatorsData]);
18432
18488
  }
18433
18489
 
18434
- return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
18490
+ if (from.privilegedServices !== null) {
18491
+ update.privilegedServices = PrivilegedServices.create({
18492
+ ...from.privilegedServices,
18493
+ assigners: asKnownSize([...from.privilegedServices.assigners]),
18494
+ });
18495
+ }
18496
+ return update;
18435
18497
  }
18436
18498
 
18437
- getMemory(): Memory {
18438
- return this.memory;
18499
+ /** Retrieve and clear pending transfers. */
18500
+ takeTransfers() {
18501
+ const transfers = this.transfers;
18502
+ this.transfers = [];
18503
+ return transfers;
18439
18504
  }
18440
18505
  }
18441
18506
 
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) {}
18507
+ type StateSlice = Pick<State, "getService" | "privilegedServices">;
18449
18508
 
18450
- get(registerIndex: number): U64 {
18451
- return tryAsU64(this.registers.getU64(registerIndex));
18452
- }
18509
+ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18510
+ /** A collection of state updates. */
18511
+ public readonly stateUpdate;
18453
18512
 
18454
- set(registerIndex: number, value: U64) {
18455
- this.registers.setU64(registerIndex, value);
18513
+ constructor(
18514
+ /** Original (unmodified state). */
18515
+ public readonly state: T,
18516
+ stateUpdate?: AccumulationStateUpdate,
18517
+ ) {
18518
+ this.stateUpdate =
18519
+ stateUpdate === undefined ? AccumulationStateUpdate.empty() : AccumulationStateUpdate.copyFrom(stateUpdate);
18456
18520
  }
18457
- }
18458
18521
 
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));
18522
+ /**
18523
+ * Retrieve info of service with given id.
18524
+ *
18525
+ * NOTE the info may be updated compared to what is in the state.
18526
+ *
18527
+ * Takes into account ejected and newly created services as well.
18528
+ */
18529
+ getServiceInfo(destination: ServiceId | null): ServiceAccountInfo | null {
18530
+ if (destination === null) {
18531
+ return null;
18532
+ }
18463
18533
 
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
- }
18534
+ const maybeUpdatedServiceInfo = this.stateUpdate.services.updated.get(destination);
18474
18535
 
18475
- /** A utility function to easily trace a bunch of registers. */
18476
- declare function traceRegisters(...regs: number[]) {
18477
- return regs.map(tryAsRegisterIndex);
18478
- }
18536
+ if (maybeUpdatedServiceInfo !== undefined) {
18537
+ return maybeUpdatedServiceInfo.action.account;
18538
+ }
18479
18539
 
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;
18540
+ const maybeService = this.state.getService(destination);
18541
+ if (maybeService === null) {
18542
+ return null;
18543
+ }
18484
18544
 
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);
18545
+ return maybeService.getInfo();
18546
+ }
18491
18547
 
18492
- /** Currently executing service id. */
18493
- readonly currentServiceId: U32;
18548
+ getStorage(serviceId: ServiceId, rawKey: StorageKey): BytesBlob | null {
18549
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
18550
+ const item = storages.find((x) => x.key.isEqualTo(rawKey));
18551
+ if (item !== undefined) {
18552
+ return item.value;
18553
+ }
18494
18554
 
18495
- /** Input&Output registers that we should add to tracing log. */
18496
- readonly tracedRegisters: RegisterIndex[];
18555
+ const service = this.state.getService(serviceId);
18556
+ return service?.getStorage(rawKey) ?? null;
18557
+ }
18497
18558
 
18498
18559
  /**
18499
- * Actually execute the host call.
18560
+ * Returns `true` if the preimage is already provided either in current
18561
+ * accumulation scope or earlier.
18500
18562
  *
18501
- * NOTE the call is ALLOWED and expected to modify registers and memory.
18563
+ * NOTE: Does not check if the preimage is available, we just check
18564
+ * the existence in `preimages` map.
18502
18565
  */
18503
- execute(gas: GasCounter, regs: IHostCallRegisters, memory: IHostCallMemory): Promise<undefined | PvmExecution>;
18504
- }
18566
+ hasPreimage(serviceId: ServiceId, hash: PreimageHash): boolean {
18567
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18568
+ const providedPreimage = preimages.find(
18569
+ // we ignore the action here, since if there is <any> update on that
18570
+ // hash it means it has to exist, right?
18571
+ (p) => p.hash.isEqualTo(hash),
18572
+ );
18573
+ if (providedPreimage !== undefined) {
18574
+ return true;
18575
+ }
18505
18576
 
18506
- /** Container for all available host calls. */
18507
- declare class HostCallsManager {
18508
- private readonly hostCalls = new Map<HostCallIndex, HostCallHandler>();
18509
- private readonly missing;
18577
+ // fallback to state preimages
18578
+ const service = this.state.getService(serviceId);
18579
+ if (service === undefined) {
18580
+ return false;
18581
+ }
18510
18582
 
18511
- constructor({
18512
- missing,
18513
- handlers = [],
18514
- }: {
18515
- missing: HostCallHandler;
18516
- handlers?: HostCallHandler[];
18517
- }) {
18518
- this.missing = missing;
18583
+ return service?.hasPreimage(hash) ?? false;
18584
+ }
18519
18585
 
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);
18586
+ getPreimage(serviceId: ServiceId, hash: PreimageHash): BytesBlob | null {
18587
+ // TODO [ToDr] Should we verify availability here?
18588
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18589
+ const freshlyProvided = preimages.find((x) => x.hash.isEqualTo(hash));
18590
+ if (freshlyProvided !== undefined && freshlyProvided.action.kind === UpdatePreimageKind.Provide) {
18591
+ return freshlyProvided.action.preimage.blob;
18523
18592
  }
18524
- }
18525
18593
 
18526
- /** Get a host call by index. */
18527
- get(hostCallIndex: HostCallIndex): HostCallHandler {
18528
- return this.hostCalls.get(hostCallIndex) ?? this.missing;
18594
+ const service = this.state.getService(serviceId);
18595
+ return service?.getPreimage(hash) ?? null;
18529
18596
  }
18530
18597
 
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
- }
18598
+ /** Get status of a preimage of current service taking into account any updates. */
18599
+ getLookupHistory(
18600
+ currentTimeslot: TimeSlot,
18601
+ serviceId: ServiceId,
18602
+ hash: PreimageHash,
18603
+ length: U64,
18604
+ ): LookupHistoryItem | null {
18605
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18606
+ // TODO [ToDr] This is most likely wrong. We may have `provide` and `remove` within
18607
+ // the same state update. We should however switch to proper "updated state"
18608
+ // representation soon.
18609
+ const updatedPreimage = preimages.findLast(
18610
+ (update) => update.hash.isEqualTo(hash) && BigInt(update.length) === length,
18611
+ );
18551
18612
 
18552
- type ResolveFn = (pvm: Interpreter) => void;
18613
+ const stateFallback = () => {
18614
+ // fallback to state lookup
18615
+ const service = this.state.getService(serviceId);
18616
+ const lenU32 = preimageLenAsU32(length);
18617
+ if (lenU32 === null || service === null) {
18618
+ return null;
18619
+ }
18553
18620
 
18554
- declare class InterpreterInstanceManager {
18555
- private instances: Interpreter[] = [];
18556
- private waitingQueue: ResolveFn[] = [];
18621
+ const slots = service.getLookupHistory(hash, lenU32);
18622
+ return slots === null ? null : new LookupHistoryItem(hash, lenU32, slots);
18623
+ };
18557
18624
 
18558
- constructor(noOfPvmInstances: number) {
18559
- for (let i = 0; i < noOfPvmInstances; i++) {
18560
- this.instances.push(
18561
- new Interpreter({
18562
- useSbrkGas: false,
18563
- }),
18564
- );
18625
+ if (updatedPreimage === undefined) {
18626
+ return stateFallback();
18565
18627
  }
18566
- }
18567
18628
 
18568
- async getInstance(): Promise<Interpreter> {
18569
- const instance = this.instances.pop();
18570
- if (instance !== undefined) {
18571
- return Promise.resolve(instance);
18629
+ const { action } = updatedPreimage;
18630
+ switch (action.kind) {
18631
+ case UpdatePreimageKind.Provide: {
18632
+ // casting to U32 is safe, since we compare with object we have in memory.
18633
+ return new LookupHistoryItem(hash, updatedPreimage.length, tryAsLookupHistorySlots([currentTimeslot]));
18634
+ }
18635
+ case UpdatePreimageKind.Remove: {
18636
+ const state = stateFallback();
18637
+ // kinda impossible, since we know it's there because it's removed.
18638
+ if (state === null) {
18639
+ return null;
18640
+ }
18641
+
18642
+ return new LookupHistoryItem(hash, state.length, tryAsLookupHistorySlots([...state.slots, currentTimeslot]));
18643
+ }
18644
+ case UpdatePreimageKind.UpdateOrAdd: {
18645
+ return action.item;
18646
+ }
18572
18647
  }
18573
- return new Promise((resolve) => {
18574
- this.waitingQueue.push(resolve);
18575
- });
18576
- }
18577
18648
 
18578
- releaseInstance(pvm: Interpreter) {
18579
- const waiting = this.waitingQueue.shift();
18580
- if (waiting !== undefined) {
18581
- return waiting(pvm);
18582
- }
18583
- this.instances.push(pvm);
18649
+ assertNever(action);
18584
18650
  }
18585
- }
18586
18651
 
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
- `;
18597
- }
18652
+ /* State update functions. */
18653
+ updateStorage(serviceId: ServiceId, key: StorageKey, value: BytesBlob | null) {
18654
+ const update =
18655
+ value === null
18656
+ ? UpdateStorage.remove({ key })
18657
+ : UpdateStorage.set({
18658
+ storage: StorageItem.create({ key, value }),
18659
+ });
18598
18660
 
18599
- static fromStatus(consumedGas: Gas, status: Status) {
18600
- return new ReturnValue(consumedGas, status, null);
18661
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
18662
+ const index = storages.findIndex((x) => x.key.isEqualTo(key));
18663
+ const count = index === -1 ? 0 : 1;
18664
+ storages.splice(index, count, update);
18665
+ this.stateUpdate.services.storage.set(serviceId, storages);
18601
18666
  }
18602
18667
 
18603
- static fromMemorySlice(consumedGas: Gas, memorySlice: Uint8Array) {
18604
- return new ReturnValue(consumedGas, null, memorySlice);
18668
+ /**
18669
+ * Update a preimage.
18670
+ *
18671
+ * Note we store all previous entries as well, since there might be a sequence of:
18672
+ * `provide` -> `remove` and both should update the end state somehow.
18673
+ */
18674
+ updatePreimage(serviceId: ServiceId, newUpdate: UpdatePreimage) {
18675
+ const updatePreimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18676
+ updatePreimages.push(newUpdate);
18677
+ this.stateUpdate.services.preimages.set(serviceId, updatePreimages);
18605
18678
  }
18606
18679
 
18607
- hasMemorySlice(): this is this & { status: null; memorySlice: Uint8Array } {
18608
- return this.memorySlice instanceof Uint8Array && this.status === null;
18609
- }
18680
+ updateServiceStorageUtilisation(
18681
+ serviceId: ServiceId,
18682
+ items: number,
18683
+ bytes: bigint,
18684
+ serviceInfo: ServiceAccountInfo,
18685
+ ): Result$2<OK, InsufficientFundsError> {
18686
+ check`${items >= 0} storageUtilisationCount has to be a positive number, got: ${items}`;
18687
+ check`${bytes >= 0} storageUtilisationBytes has to be a positive number, got: ${bytes}`;
18610
18688
 
18611
- hasStatus(): this is this & { status: Status; memorySlice: null } {
18612
- return !this.hasMemorySlice();
18613
- }
18614
- }
18615
- declare class HostCalls {
18616
- constructor(
18617
- private pvmInstanceManager: InterpreterInstanceManager,
18618
- private hostCalls: HostCallsManager,
18619
- ) {}
18689
+ const overflowItems = !isU32(items);
18690
+ const overflowBytes = !isU64(bytes);
18620
18691
 
18621
- private getReturnValue(status: Status, pvmInstance: Interpreter): ReturnValue {
18622
- const gasConsumed = pvmInstance.getGasConsumed();
18623
- if (status === Status.OOG) {
18624
- return ReturnValue.fromStatus(gasConsumed, status);
18692
+ // TODO [ToDr] this is not specified in GP, but it seems sensible.
18693
+ if (overflowItems || overflowBytes) {
18694
+ return Result.error(
18695
+ InsufficientFundsError,
18696
+ () => `Storage utilisation overflow: items=${overflowItems}, bytes=${overflowBytes}`,
18697
+ );
18625
18698
  }
18626
18699
 
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);
18632
-
18633
- const result = safeAllocUint8Array(maybeLength);
18634
- const startAddress = tryAsMemoryIndex(maybeAddress);
18635
- const loadResult = memory.loadInto(result, startAddress);
18636
-
18637
- if (loadResult.isError) {
18638
- return ReturnValue.fromMemorySlice(gasConsumed, new Uint8Array());
18639
- }
18640
-
18641
- return ReturnValue.fromMemorySlice(gasConsumed, result);
18700
+ const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
18701
+ if (serviceInfo.balance < thresholdBalance) {
18702
+ return Result.error(
18703
+ InsufficientFundsError,
18704
+ () => `Service balance (${serviceInfo.balance}) below threshold (${thresholdBalance})`,
18705
+ );
18642
18706
  }
18643
18707
 
18644
- return ReturnValue.fromStatus(gasConsumed, Status.PANIC);
18708
+ // Update service info with new details.
18709
+ this.updateServiceInfo(
18710
+ serviceId,
18711
+ ServiceAccountInfo.create({
18712
+ ...serviceInfo,
18713
+ storageUtilisationBytes: bytes,
18714
+ storageUtilisationCount: items,
18715
+ }),
18716
+ );
18717
+ return Result.ok(OK);
18645
18718
  }
18646
18719
 
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);
18720
+ updateServiceInfo(serviceId: ServiceId, newInfo: ServiceAccountInfo) {
18721
+ const existingUpdate = this.stateUpdate.services.updated.get(serviceId);
18670
18722
 
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(),
18723
+ if (existingUpdate?.action.kind === UpdateServiceKind.Create) {
18724
+ this.stateUpdate.services.updated.set(
18725
+ serviceId,
18726
+ UpdateService.create({
18727
+ serviceInfo: newInfo,
18728
+ lookupHistory: existingUpdate.action.lookupHistory,
18729
+ }),
18684
18730
  );
18731
+ return;
18732
+ }
18685
18733
 
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
- }
18734
+ this.stateUpdate.services.updated.set(
18735
+ serviceId,
18736
+ UpdateService.update({
18737
+ serviceInfo: newInfo,
18738
+ }),
18739
+ );
18740
+ }
18706
18741
 
18707
- assertNever(result);
18708
- }
18742
+ createService(serviceId: ServiceId, newInfo: ServiceAccountInfo, newLookupHistory: LookupHistoryItem) {
18743
+ this.stateUpdate.services.created.push(serviceId);
18744
+ this.stateUpdate.services.updated.set(
18745
+ serviceId,
18746
+ UpdateService.create({
18747
+ serviceInfo: newInfo,
18748
+ lookupHistory: newLookupHistory,
18749
+ }),
18750
+ );
18709
18751
  }
18710
18752
 
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);
18753
+ getPrivilegedServices() {
18754
+ if (this.stateUpdate.privilegedServices !== null) {
18755
+ return this.stateUpdate.privilegedServices;
18724
18756
  }
18757
+
18758
+ return this.state.privilegedServices;
18725
18759
  }
18726
18760
  }
18727
18761
 
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 };
18762
+ declare function preimageLenAsU32(length: U64) {
18763
+ // Safe to convert to Number and U32: we check that len < 2^32 before conversion
18764
+ return length >= 2n ** 32n ? null : tryAsU32(Number(length));
18742
18765
  }
18743
18766
 
18767
+ /**
18768
+ * Host call result constants.
18769
+ *
18770
+ * https://graypaper.fluffylabs.dev/#/85129da/2c7c022c7c02?v=0.6.3
18771
+ */
18772
+ declare const HostCallResult = {
18773
+ /** The return value indicating an item does not exist. */
18774
+ NONE: tryAsU64(0xffff_ffff_ffff_ffffn), // 2**64 - 1
18775
+ /** Name unknown. */
18776
+ WHAT: tryAsU64(0xffff_ffff_ffff_fffen), // 2**64 - 2
18777
+ /** The inner PVM memory index provided for reading/writing is not accessible. */
18778
+ OOB: tryAsU64(0xffff_ffff_ffff_fffdn), // 2**64 - 3
18779
+ /** Index unknown. */
18780
+ WHO: tryAsU64(0xffff_ffff_ffff_fffcn), // 2**64 - 4
18781
+ /** Storage full or resource already allocated. */
18782
+ FULL: tryAsU64(0xffff_ffff_ffff_fffbn), // 2**64 - 5
18783
+ /** Core index unknown. */
18784
+ CORE: tryAsU64(0xffff_ffff_ffff_fffan), // 2**64 - 6
18785
+ /** Insufficient funds. */
18786
+ CASH: tryAsU64(0xffff_ffff_ffff_fff9n), // 2**64 - 7
18787
+ /** Gas limit too low. */
18788
+ LOW: tryAsU64(0xffff_ffff_ffff_fff8n), // 2**64 - 8
18789
+ /** The item is already solicited, cannot be forgotten or the operation is invalid due to privilege level. */
18790
+ HUH: tryAsU64(0xffff_ffff_ffff_fff7n), // 2**64 - 9
18791
+ /** The return value indicating general success. */
18792
+ OK: tryAsU64(0n),
18793
+ } as const;
18794
+
18744
18795
  declare const MAX_U32 = tryAsU32(2 ** 32 - 1);
18745
18796
  declare const MAX_U32_BIG_INT = tryAsU64(MAX_U32);
18746
18797
  declare const SERVICE_ID_BYTES = 4;
@@ -18831,6 +18882,7 @@ declare const index$6_UpdatePrivilegesError: typeof UpdatePrivilegesError;
18831
18882
  type index$6_ZeroVoidError = ZeroVoidError;
18832
18883
  declare const index$6_ZeroVoidError: typeof ZeroVoidError;
18833
18884
  declare const index$6_clampU64ToU32: typeof clampU64ToU32;
18885
+ declare const index$6_deepCloneMapWithArray: typeof deepCloneMapWithArray;
18834
18886
  declare const index$6_getServiceId: typeof getServiceId;
18835
18887
  declare const index$6_getServiceIdOrCurrent: typeof getServiceIdOrCurrent;
18836
18888
  declare const index$6_preimageLenAsU32: typeof preimageLenAsU32;
@@ -18840,7 +18892,7 @@ declare const index$6_tryAsMachineId: typeof tryAsMachineId;
18840
18892
  declare const index$6_tryAsProgramCounter: typeof tryAsProgramCounter;
18841
18893
  declare const index$6_writeServiceIdAsLeBytes: typeof writeServiceIdAsLeBytes;
18842
18894
  declare namespace index$6 {
18843
- export { index$6_AccumulationStateUpdate as AccumulationStateUpdate, index$6_CURRENT_SERVICE_ID as CURRENT_SERVICE_ID, index$6_EjectError as EjectError, index$6_ForgetPreimageError as ForgetPreimageError, index$6_HostCallResult as HostCallResult, index$6_MAX_U32 as MAX_U32, index$6_MAX_U32_BIG_INT as MAX_U32_BIG_INT, index$6_MachineInstance as MachineInstance, index$6_MemoryOperation as MemoryOperation, index$6_NewServiceError as NewServiceError, index$6_PagesError as PagesError, index$6_PartiallyUpdatedState as PartiallyUpdatedState, index$6_PeekPokeError as PeekPokeError, index$6_PendingTransfer as PendingTransfer, index$6_PreimageStatusKind as PreimageStatusKind, index$6_ProvidePreimageError as ProvidePreimageError, index$6_RequestPreimageError as RequestPreimageError, index$6_SERVICE_ID_BYTES as SERVICE_ID_BYTES, index$6_TransferError as TransferError, index$6_UpdatePrivilegesError as UpdatePrivilegesError, index$6_ZeroVoidError as ZeroVoidError, index$6_clampU64ToU32 as clampU64ToU32, index$6_getServiceId as getServiceId, index$6_getServiceIdOrCurrent as getServiceIdOrCurrent, index$6_preimageLenAsU32 as preimageLenAsU32, index$6_slotsToPreimageStatus as slotsToPreimageStatus, index$6_toMemoryOperation as toMemoryOperation, index$6_tryAsMachineId as tryAsMachineId, index$6_tryAsProgramCounter as tryAsProgramCounter, index$6_writeServiceIdAsLeBytes as writeServiceIdAsLeBytes };
18895
+ export { index$6_AccumulationStateUpdate as AccumulationStateUpdate, index$6_CURRENT_SERVICE_ID as CURRENT_SERVICE_ID, index$6_EjectError as EjectError, index$6_ForgetPreimageError as ForgetPreimageError, index$6_HostCallResult as HostCallResult, index$6_MAX_U32 as MAX_U32, index$6_MAX_U32_BIG_INT as MAX_U32_BIG_INT, index$6_MachineInstance as MachineInstance, index$6_MemoryOperation as MemoryOperation, index$6_NewServiceError as NewServiceError, index$6_PagesError as PagesError, index$6_PartiallyUpdatedState as PartiallyUpdatedState, index$6_PeekPokeError as PeekPokeError, index$6_PendingTransfer as PendingTransfer, index$6_PreimageStatusKind as PreimageStatusKind, index$6_ProvidePreimageError as ProvidePreimageError, index$6_RequestPreimageError as RequestPreimageError, index$6_SERVICE_ID_BYTES as SERVICE_ID_BYTES, index$6_TransferError as TransferError, index$6_UpdatePrivilegesError as UpdatePrivilegesError, index$6_ZeroVoidError as ZeroVoidError, index$6_clampU64ToU32 as clampU64ToU32, index$6_deepCloneMapWithArray as deepCloneMapWithArray, index$6_getServiceId as getServiceId, index$6_getServiceIdOrCurrent as getServiceIdOrCurrent, index$6_preimageLenAsU32 as preimageLenAsU32, index$6_slotsToPreimageStatus as slotsToPreimageStatus, index$6_toMemoryOperation as toMemoryOperation, index$6_tryAsMachineId as tryAsMachineId, index$6_tryAsProgramCounter as tryAsProgramCounter, index$6_writeServiceIdAsLeBytes as writeServiceIdAsLeBytes };
18844
18896
  export type { index$6_InsufficientFundsError as InsufficientFundsError, index$6_MachineId as MachineId, index$6_MachineResult as MachineResult, index$6_MachineStatus as MachineStatus, index$6_NoMachineError as NoMachineError, index$6_PartialState as PartialState, index$6_PreimageStatus as PreimageStatus, index$6_ProgramCounter as ProgramCounter, index$6_RefineExternalities as RefineExternalities, index$6_SegmentExportError as SegmentExportError, index$6_ServiceStateUpdate as ServiceStateUpdate, index$6_StateSlice as StateSlice, index$6_TRANSFER_MEMO_BYTES as TRANSFER_MEMO_BYTES, index$6_UnprivilegedError as UnprivilegedError };
18845
18897
  }
18846
18898
 
@@ -19241,6 +19293,7 @@ declare const index$3_check: typeof check;
19241
19293
  declare const index$3_clampU64ToU32: typeof clampU64ToU32;
19242
19294
  declare const index$3_createResults: typeof createResults;
19243
19295
  declare const index$3_decodeStandardProgram: typeof decodeStandardProgram;
19296
+ declare const index$3_deepCloneMapWithArray: typeof deepCloneMapWithArray;
19244
19297
  declare const index$3_extractCodeAndMetadata: typeof extractCodeAndMetadata;
19245
19298
  declare const index$3_getServiceId: typeof getServiceId;
19246
19299
  declare const index$3_getServiceIdOrCurrent: typeof getServiceIdOrCurrent;
@@ -19259,7 +19312,7 @@ declare const index$3_tryAsMachineId: typeof tryAsMachineId;
19259
19312
  declare const index$3_tryAsProgramCounter: typeof tryAsProgramCounter;
19260
19313
  declare const index$3_writeServiceIdAsLeBytes: typeof writeServiceIdAsLeBytes;
19261
19314
  declare namespace index$3 {
19262
- export { index$3_AccumulationStateUpdate as AccumulationStateUpdate, index$3_ArgsDecoder as ArgsDecoder, index$3_ArgumentType as ArgumentType, index$3_BasicBlocks as BasicBlocks, index$3_CURRENT_SERVICE_ID as CURRENT_SERVICE_ID, index$3_EjectError as EjectError, index$3_ExtendedWitdthImmediateDecoder as ExtendedWitdthImmediateDecoder, index$3_ForgetPreimageError as ForgetPreimageError, index$3_HostCallMemory as HostCallMemory, index$3_HostCallRegisters as HostCallRegisters, index$3_HostCallResult as HostCallResult, index$3_ImmediateDecoder as ImmediateDecoder, index$3_MAX_U32 as MAX_U32, index$3_MAX_U32_BIG_INT as MAX_U32_BIG_INT, index$3_MachineInstance as MachineInstance, index$3_Mask as Mask, index$3_MemoryOperation as MemoryOperation, index$3_MemorySegment as MemorySegment, NO_OF_REGISTERS$1 as NO_OF_REGISTERS, index$3_NewServiceError as NewServiceError, index$3_NibblesDecoder as NibblesDecoder, index$3_PagesError as PagesError, index$3_PartiallyUpdatedState as PartiallyUpdatedState, index$3_PeekPokeError as PeekPokeError, index$3_PendingTransfer as PendingTransfer, index$3_PreimageStatusKind as PreimageStatusKind, index$3_Program as Program, index$3_ProgramDecoder as ProgramDecoder, index$3_ProvidePreimageError as ProvidePreimageError, DebuggerAdapter as Pvm, index$3_Registers as Registers, index$3_RequestPreimageError as RequestPreimageError, Result$2 as Result, index$3_RichTaggedError as RichTaggedError, index$3_SERVICE_ID_BYTES as SERVICE_ID_BYTES, index$3_SpiMemory as SpiMemory, index$3_SpiProgram as SpiProgram, index$3_TransferError as TransferError, index$3_UpdatePrivilegesError as UpdatePrivilegesError, index$3_WithDebug as WithDebug, index$3_ZeroVoidError as ZeroVoidError, index$3___OPAQUE_TYPE__ as __OPAQUE_TYPE__, index$3_asOpaqueType as asOpaqueType, index$3_assertEmpty as assertEmpty, index$3_assertNever as assertNever, index$l as block, index$s as bytes, index$3_check as check, index$3_clampU64ToU32 as clampU64ToU32, index$3_createResults as createResults, index$3_decodeStandardProgram as decodeStandardProgram, index$3_extractCodeAndMetadata as extractCodeAndMetadata, index$3_getServiceId as getServiceId, index$3_getServiceIdOrCurrent as getServiceIdOrCurrent, index$p as hash, index$3_inspect as inspect, index$3_instructionArgumentTypeMap as instructionArgumentTypeMap, index$8 as interpreter, index$3_isBrowser as isBrowser, index$3_isTaggedError as isTaggedError, index$3_maybeTaggedErrorToString as maybeTaggedErrorToString, index$3_measure as measure, index$r as numbers, index$3_preimageLenAsU32 as preimageLenAsU32, index$3_resultToString as resultToString, index$3_seeThrough as seeThrough, index$3_slotsToPreimageStatus as slotsToPreimageStatus, index$3_toMemoryOperation as toMemoryOperation, index$3_tryAsMachineId as tryAsMachineId, index$3_tryAsProgramCounter as tryAsProgramCounter, index$3_writeServiceIdAsLeBytes as writeServiceIdAsLeBytes };
19315
+ export { index$3_AccumulationStateUpdate as AccumulationStateUpdate, index$3_ArgsDecoder as ArgsDecoder, index$3_ArgumentType as ArgumentType, index$3_BasicBlocks as BasicBlocks, index$3_CURRENT_SERVICE_ID as CURRENT_SERVICE_ID, index$3_EjectError as EjectError, index$3_ExtendedWitdthImmediateDecoder as ExtendedWitdthImmediateDecoder, index$3_ForgetPreimageError as ForgetPreimageError, index$3_HostCallMemory as HostCallMemory, index$3_HostCallRegisters as HostCallRegisters, index$3_HostCallResult as HostCallResult, index$3_ImmediateDecoder as ImmediateDecoder, index$3_MAX_U32 as MAX_U32, index$3_MAX_U32_BIG_INT as MAX_U32_BIG_INT, index$3_MachineInstance as MachineInstance, index$3_Mask as Mask, index$3_MemoryOperation as MemoryOperation, index$3_MemorySegment as MemorySegment, NO_OF_REGISTERS$1 as NO_OF_REGISTERS, index$3_NewServiceError as NewServiceError, index$3_NibblesDecoder as NibblesDecoder, index$3_PagesError as PagesError, index$3_PartiallyUpdatedState as PartiallyUpdatedState, index$3_PeekPokeError as PeekPokeError, index$3_PendingTransfer as PendingTransfer, index$3_PreimageStatusKind as PreimageStatusKind, index$3_Program as Program, index$3_ProgramDecoder as ProgramDecoder, index$3_ProvidePreimageError as ProvidePreimageError, DebuggerAdapter as Pvm, index$3_Registers as Registers, index$3_RequestPreimageError as RequestPreimageError, Result$2 as Result, index$3_RichTaggedError as RichTaggedError, index$3_SERVICE_ID_BYTES as SERVICE_ID_BYTES, index$3_SpiMemory as SpiMemory, index$3_SpiProgram as SpiProgram, index$3_TransferError as TransferError, index$3_UpdatePrivilegesError as UpdatePrivilegesError, index$3_WithDebug as WithDebug, index$3_ZeroVoidError as ZeroVoidError, index$3___OPAQUE_TYPE__ as __OPAQUE_TYPE__, index$3_asOpaqueType as asOpaqueType, index$3_assertEmpty as assertEmpty, index$3_assertNever as assertNever, index$l as block, index$s as bytes, index$3_check as check, index$3_clampU64ToU32 as clampU64ToU32, index$3_createResults as createResults, index$3_decodeStandardProgram as decodeStandardProgram, index$3_deepCloneMapWithArray as deepCloneMapWithArray, index$3_extractCodeAndMetadata as extractCodeAndMetadata, index$3_getServiceId as getServiceId, index$3_getServiceIdOrCurrent as getServiceIdOrCurrent, index$p as hash, index$3_inspect as inspect, index$3_instructionArgumentTypeMap as instructionArgumentTypeMap, index$8 as interpreter, index$3_isBrowser as isBrowser, index$3_isTaggedError as isTaggedError, index$3_maybeTaggedErrorToString as maybeTaggedErrorToString, index$3_measure as measure, index$r as numbers, index$3_preimageLenAsU32 as preimageLenAsU32, index$3_resultToString as resultToString, index$3_seeThrough as seeThrough, index$3_slotsToPreimageStatus as slotsToPreimageStatus, index$3_toMemoryOperation as toMemoryOperation, index$3_tryAsMachineId as tryAsMachineId, index$3_tryAsProgramCounter as tryAsProgramCounter, index$3_writeServiceIdAsLeBytes as writeServiceIdAsLeBytes };
19263
19316
  export type { index$3_Args as Args, index$3_EnumMapping as EnumMapping, index$3_ErrorResult as ErrorResult, index$3_IHostCallMemory as IHostCallMemory, index$3_IHostCallRegisters as IHostCallRegisters, index$3_InsufficientFundsError as InsufficientFundsError, index$3_MachineId as MachineId, index$3_MachineResult as MachineResult, index$3_MachineStatus as MachineStatus, index$3_NoMachineError as NoMachineError, index$3_OK as OK, index$3_OkResult as OkResult, index$3_Opaque as Opaque, index$3_PartialState as PartialState, index$3_PreimageStatus as PreimageStatus, index$3_ProgramCounter as ProgramCounter, index$3_RefineExternalities as RefineExternalities, index$3_SegmentExportError as SegmentExportError, index$3_ServiceStateUpdate as ServiceStateUpdate, index$3_StateSlice as StateSlice, index$3_StringLiteral as StringLiteral, index$3_TRANSFER_MEMO_BYTES as TRANSFER_MEMO_BYTES, index$3_TaggedError as TaggedError, index$3_TokenOf as TokenOf, index$3_Uninstantiable as Uninstantiable, index$3_UnprivilegedError as UnprivilegedError, index$3_WithOpaque as WithOpaque };
19264
19317
  }
19265
19318
 
@@ -20089,12 +20142,15 @@ declare class Preimages {
20089
20142
  prevPreimage.requester > currPreimage.requester ||
20090
20143
  currPreimage.blob.compare(prevPreimage.blob).isLessOrEqual()
20091
20144
  ) {
20092
- return Result.error(PreimagesErrorCode.PreimagesNotSortedUnique);
20145
+ return Result.error(
20146
+ PreimagesErrorCode.PreimagesNotSortedUnique,
20147
+ () => `Preimages not sorted/unique at index ${i}`,
20148
+ );
20093
20149
  }
20094
20150
  }
20095
20151
 
20096
20152
  const { preimages, slot } = input;
20097
- const pendingChanges: UpdatePreimage[] = [];
20153
+ const pendingChanges = new Map<ServiceId, UpdatePreimage[]>();
20098
20154
 
20099
20155
  // select preimages for integration
20100
20156
  for (const preimage of preimages) {
@@ -20103,7 +20159,7 @@ declare class Preimages {
20103
20159
 
20104
20160
  const service = this.state.getService(requester);
20105
20161
  if (service === null) {
20106
- return Result.error(PreimagesErrorCode.AccountNotFound);
20162
+ return Result.error(PreimagesErrorCode.AccountNotFound, () => `Service not found: ${requester}`);
20107
20163
  }
20108
20164
 
20109
20165
  const hasPreimage = service.hasPreimage(hash);
@@ -20111,17 +20167,22 @@ declare class Preimages {
20111
20167
  // https://graypaper.fluffylabs.dev/#/5f542d7/181800181900
20112
20168
  // https://graypaper.fluffylabs.dev/#/5f542d7/116f0011a500
20113
20169
  if (hasPreimage || slots === null || !LookupHistoryItem.isRequested(slots)) {
20114
- return Result.error(PreimagesErrorCode.PreimageUnneeded);
20170
+ return Result.error(
20171
+ PreimagesErrorCode.PreimageUnneeded,
20172
+ () =>
20173
+ `Preimage unneeded: requester=${requester}, hash=${hash}, hasPreimage=${hasPreimage}, isRequested=${slots !== null && LookupHistoryItem.isRequested(slots)}`,
20174
+ );
20115
20175
  }
20116
20176
 
20117
20177
  // https://graypaper.fluffylabs.dev/#/5f542d7/18c00018f300
20118
- pendingChanges.push(
20178
+ const updates = pendingChanges.get(requester) ?? [];
20179
+ updates.push(
20119
20180
  UpdatePreimage.provide({
20120
- serviceId: requester,
20121
20181
  preimage: PreimageItem.create({ hash, blob }),
20122
20182
  slot,
20123
20183
  }),
20124
20184
  );
20185
+ pendingChanges.set(requester, updates);
20125
20186
  }
20126
20187
 
20127
20188
  return Result.ok({