@typeberry/lib 0.2.0-c3df163 → 0.2.0-ef1ea0e

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 +360 -383
  2. package/index.d.ts +485 -275
  3. package/index.js +360 -383
  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
@@ -1467,8 +1467,8 @@ declare class Decoder {
1467
1467
  /**
1468
1468
  * Create a new [`Decoder`] instance given a raw array of bytes as a source.
1469
1469
  */
1470
- static fromBlob(source: Uint8Array) {
1471
- return new Decoder(source);
1470
+ static fromBlob(source: Uint8Array, context?: unknown) {
1471
+ return new Decoder(source, undefined, context);
1472
1472
  }
1473
1473
 
1474
1474
  /**
@@ -1818,7 +1818,7 @@ declare class Decoder {
1818
1818
  private ensureHasBytes(bytes: number) {
1819
1819
  check`${bytes >= 0} Negative number of bytes given.`;
1820
1820
  if (this.offset + bytes > this.source.length) {
1821
- throw new Error(
1821
+ throw new EndOfDataError(
1822
1822
  `Attempting to decode more data than there is left. Need ${bytes}, left: ${this.source.length - this.offset}.`,
1823
1823
  );
1824
1824
  }
@@ -1837,6 +1837,8 @@ declare function decodeVariableLengthExtraBytes(firstByte: number) {
1837
1837
  return 0;
1838
1838
  }
1839
1839
 
1840
+ declare class EndOfDataError extends Error {}
1841
+
1840
1842
  /** Hint for how big the encoded object will be. */
1841
1843
  type SizeHint = {
1842
1844
  /** Number of bytes in the encoding. */
@@ -2879,6 +2881,15 @@ declare namespace codec$1 {
2879
2881
  };
2880
2882
  })();
2881
2883
 
2884
+ /** Zero-size `void` value. */
2885
+ export const nothing = Descriptor.new<void>(
2886
+ "void",
2887
+ { bytes: 0, isExact: true },
2888
+ (_e, _v) => {},
2889
+ (_d) => {},
2890
+ (_s) => {},
2891
+ );
2892
+
2882
2893
  /** Variable-length U32. */
2883
2894
  export const varU32 = Descriptor.new<U32>(
2884
2895
  "var_u32",
@@ -3346,6 +3357,9 @@ declare function forEachDescriptor<T>(
3346
3357
  try {
3347
3358
  f(k, descriptors[k]);
3348
3359
  } catch (e) {
3360
+ if (e instanceof EndOfDataError) {
3361
+ throw new EndOfDataError(`${key}: ${e}`);
3362
+ }
3349
3363
  throw new Error(`${key}: ${e}`);
3350
3364
  }
3351
3365
  }
@@ -3469,6 +3483,8 @@ type index$q_DescriptorRecord<T> = DescriptorRecord<T>;
3469
3483
  type index$q_Encode<T> = Encode<T>;
3470
3484
  type index$q_Encoder = Encoder;
3471
3485
  declare const index$q_Encoder: typeof Encoder;
3486
+ type index$q_EndOfDataError = EndOfDataError;
3487
+ declare const index$q_EndOfDataError: typeof EndOfDataError;
3472
3488
  type index$q_LengthRange = LengthRange;
3473
3489
  declare const index$q_MASKS: typeof MASKS;
3474
3490
  declare const index$q_MAX_LENGTH: typeof MAX_LENGTH;
@@ -3497,7 +3513,7 @@ declare const index$q_sequenceViewVarLen: typeof sequenceViewVarLen;
3497
3513
  declare const index$q_tryAsExactBytes: typeof tryAsExactBytes;
3498
3514
  declare const index$q_validateLength: typeof validateLength;
3499
3515
  declare namespace index$q {
3500
- export { index$q_DEFAULT_START_LENGTH as DEFAULT_START_LENGTH, index$q_Decoder as Decoder, index$q_Descriptor as Descriptor, index$q_Encoder as Encoder, index$q_MASKS as MASKS, index$q_MAX_LENGTH as MAX_LENGTH, index$q_ObjectView as ObjectView, index$q_SequenceView as SequenceView, index$q_TYPICAL_DICTIONARY_LENGTH as TYPICAL_DICTIONARY_LENGTH, index$q_TYPICAL_SEQUENCE_LENGTH as TYPICAL_SEQUENCE_LENGTH, index$q_ViewField as ViewField, index$q_addSizeHints as addSizeHints, codec$1 as codec, index$q_decodeVariableLengthExtraBytes as decodeVariableLengthExtraBytes, index$q_exactHint as exactHint, index$q_forEachDescriptor as forEachDescriptor, index$q_hasUniqueView as hasUniqueView, index$q_objectView as objectView, index$q_readonlyArray as readonlyArray, index$q_sequenceViewFixLen as sequenceViewFixLen, index$q_sequenceViewVarLen as sequenceViewVarLen, index$q_tryAsExactBytes as tryAsExactBytes, index$q_validateLength as validateLength };
3516
+ export { index$q_DEFAULT_START_LENGTH as DEFAULT_START_LENGTH, index$q_Decoder as Decoder, index$q_Descriptor as Descriptor, index$q_Encoder as Encoder, index$q_EndOfDataError as EndOfDataError, index$q_MASKS as MASKS, index$q_MAX_LENGTH as MAX_LENGTH, index$q_ObjectView as ObjectView, index$q_SequenceView as SequenceView, index$q_TYPICAL_DICTIONARY_LENGTH as TYPICAL_DICTIONARY_LENGTH, index$q_TYPICAL_SEQUENCE_LENGTH as TYPICAL_SEQUENCE_LENGTH, index$q_ViewField as ViewField, index$q_addSizeHints as addSizeHints, codec$1 as codec, index$q_decodeVariableLengthExtraBytes as decodeVariableLengthExtraBytes, index$q_exactHint as exactHint, index$q_forEachDescriptor as forEachDescriptor, index$q_hasUniqueView as hasUniqueView, index$q_objectView as objectView, index$q_readonlyArray as readonlyArray, index$q_sequenceViewFixLen as sequenceViewFixLen, index$q_sequenceViewVarLen as sequenceViewVarLen, index$q_tryAsExactBytes as tryAsExactBytes, index$q_validateLength as validateLength };
3501
3517
  export type { index$q_ClassConstructor as ClassConstructor, index$q_Codec as Codec, index$q_CodecRecord as CodecRecord, index$q_CodecWithView as CodecWithView, index$q_Decode as Decode, index$q_DescribedBy as DescribedBy, index$q_DescriptorRecord as DescriptorRecord, index$q_Encode as Encode, index$q_LengthRange as LengthRange, index$q_OptionalRecord as OptionalRecord, Options$1 as Options, index$q_PropertyKeys as PropertyKeys, index$q_SimpleDescriptorRecord as SimpleDescriptorRecord, index$q_SizeHint as SizeHint, index$q_ViewOf as ViewOf };
3502
3518
  }
3503
3519
 
@@ -4263,7 +4279,7 @@ declare class SortedArray<V> implements ImmutableSortedArray<V> {
4263
4279
  };
4264
4280
  }
4265
4281
 
4266
- /** Create a new SortedSet from two sorted collections. */
4282
+ /** Create a new SortedArray from two sorted collections. */
4267
4283
  static fromTwoSortedCollections<V>(first: ImmutableSortedArray<V>, second: ImmutableSortedArray<V>) {
4268
4284
  check`${first.comparator === second.comparator} Cannot merge arrays if they do not use the same comparator`;
4269
4285
  const comparator = first.comparator;
@@ -5070,29 +5086,6 @@ declare const fullChainSpec = new ChainSpec({
5070
5086
  maxLookupAnchorAge: tryAsU32(14_400),
5071
5087
  });
5072
5088
 
5073
- /**
5074
- * Configuration object for typeberry workers.
5075
- */
5076
- declare class WorkerConfig {
5077
- /**
5078
- * Since we loose prototypes when transferring the context,
5079
- * this function is re-initializing proper types.
5080
- *
5081
- * TODO [ToDr] instead of doing this hack, we might prefer to pass data
5082
- * between workers using JAM codec maybe?
5083
- */
5084
- static reInit(config: unknown) {
5085
- const { chainSpec, dbPath, omitSealVerification } = config as WorkerConfig;
5086
- return new WorkerConfig(new ChainSpec(chainSpec), dbPath, omitSealVerification);
5087
- }
5088
-
5089
- constructor(
5090
- public readonly chainSpec: ChainSpec,
5091
- public readonly dbPath: string,
5092
- public readonly omitSealVerification: boolean = false,
5093
- ) {}
5094
- }
5095
-
5096
5089
  /** Peer id. */
5097
5090
  type PeerId = Opaque<string, "peerId">;
5098
5091
 
@@ -5135,12 +5128,10 @@ declare const index$m_EST_VALIDATORS: typeof EST_VALIDATORS;
5135
5128
  declare const index$m_EST_VALIDATORS_SUPER_MAJORITY: typeof EST_VALIDATORS_SUPER_MAJORITY;
5136
5129
  type index$m_PeerAddress = PeerAddress;
5137
5130
  type index$m_PeerId = PeerId;
5138
- type index$m_WorkerConfig = WorkerConfig;
5139
- declare const index$m_WorkerConfig: typeof WorkerConfig;
5140
5131
  declare const index$m_fullChainSpec: typeof fullChainSpec;
5141
5132
  declare const index$m_tinyChainSpec: typeof tinyChainSpec;
5142
5133
  declare namespace index$m {
5143
- export { index$m_Bootnode as Bootnode, index$m_ChainSpec as ChainSpec, index$m_EC_SEGMENT_SIZE as EC_SEGMENT_SIZE, index$m_EST_CORES as EST_CORES, index$m_EST_EPOCH_LENGTH as EST_EPOCH_LENGTH, index$m_EST_VALIDATORS as EST_VALIDATORS, index$m_EST_VALIDATORS_SUPER_MAJORITY as EST_VALIDATORS_SUPER_MAJORITY, index$m_WorkerConfig as WorkerConfig, index$m_fullChainSpec as fullChainSpec, index$m_tinyChainSpec as tinyChainSpec };
5134
+ export { index$m_Bootnode as Bootnode, index$m_ChainSpec as ChainSpec, index$m_EC_SEGMENT_SIZE as EC_SEGMENT_SIZE, index$m_EST_CORES as EST_CORES, index$m_EST_EPOCH_LENGTH as EST_EPOCH_LENGTH, index$m_EST_VALIDATORS as EST_VALIDATORS, index$m_EST_VALIDATORS_SUPER_MAJORITY as EST_VALIDATORS_SUPER_MAJORITY, index$m_fullChainSpec as fullChainSpec, index$m_tinyChainSpec as tinyChainSpec };
5144
5135
  export type { index$m_PeerAddress as PeerAddress, index$m_PeerId as PeerId };
5145
5136
  }
5146
5137
 
@@ -8192,7 +8183,7 @@ declare class NodeConfiguration {
8192
8183
  version: "number",
8193
8184
  flavor: knownChainSpecFromJson,
8194
8185
  chain_spec: JipChainSpec.fromJson,
8195
- database_base_path: "string",
8186
+ database_base_path: json.optional("string"),
8196
8187
  authorship: AuthorshipOptions.fromJson,
8197
8188
  },
8198
8189
  NodeConfiguration.new,
@@ -8202,7 +8193,7 @@ declare class NodeConfiguration {
8202
8193
  if (version !== 1) {
8203
8194
  throw new Error("Only version=1 config is supported.");
8204
8195
  }
8205
- return new NodeConfiguration($schema, version, flavor, chain_spec, database_base_path, authorship);
8196
+ return new NodeConfiguration($schema, version, flavor, chain_spec, database_base_path ?? undefined, authorship);
8206
8197
  }
8207
8198
 
8208
8199
  private constructor(
@@ -8210,7 +8201,8 @@ declare class NodeConfiguration {
8210
8201
  public readonly version: number,
8211
8202
  public readonly flavor: KnownChainSpec,
8212
8203
  public readonly chainSpec: JipChainSpec,
8213
- public readonly databaseBasePath: string,
8204
+ /** If database path is not provided, we load an in-memory db. */
8205
+ public readonly databaseBasePath: string | undefined,
8214
8206
  public readonly authorship: AuthorshipOptions,
8215
8207
  ) {}
8216
8208
  }
@@ -8285,6 +8277,8 @@ interface BlocksDb {
8285
8277
  * NOTE: this is not extrinsic hash!
8286
8278
  */
8287
8279
  getExtrinsic(hash: HeaderHash): ExtrinsicView | null;
8280
+ /** Close the database and free resources. */
8281
+ close(): Promise<void>;
8288
8282
  }
8289
8283
 
8290
8284
  /** In-memory (non-persistent) blocks database. */
@@ -8343,6 +8337,8 @@ declare class InMemoryBlocks implements BlocksDb {
8343
8337
  getExtrinsic(hash: HeaderHash): ExtrinsicView | null {
8344
8338
  return this.extrinsicsByHeaderHash.get(hash) ?? null;
8345
8339
  }
8340
+
8341
+ async close() {}
8346
8342
  }
8347
8343
 
8348
8344
  type StateKey$1 = Opaque<OpaqueHash, "trieStateKey">;
@@ -10659,7 +10655,6 @@ declare enum UpdatePreimageKind {
10659
10655
  */
10660
10656
  declare class UpdatePreimage {
10661
10657
  private constructor(
10662
- public readonly serviceId: ServiceId,
10663
10658
  public readonly action:
10664
10659
  | {
10665
10660
  kind: UpdatePreimageKind.Provide;
@@ -10679,16 +10674,8 @@ declare class UpdatePreimage {
10679
10674
  ) {}
10680
10675
 
10681
10676
  /** 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, {
10677
+ static provide({ preimage, slot }: { preimage: PreimageItem; slot: TimeSlot | null }) {
10678
+ return new UpdatePreimage({
10692
10679
  kind: UpdatePreimageKind.Provide,
10693
10680
  preimage,
10694
10681
  slot,
@@ -10696,8 +10683,8 @@ declare class UpdatePreimage {
10696
10683
  }
10697
10684
 
10698
10685
  /** 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, {
10686
+ static remove({ hash, length }: { hash: PreimageHash; length: U32 }) {
10687
+ return new UpdatePreimage({
10701
10688
  kind: UpdatePreimageKind.Remove,
10702
10689
  hash,
10703
10690
  length,
@@ -10705,8 +10692,8 @@ declare class UpdatePreimage {
10705
10692
  }
10706
10693
 
10707
10694
  /** 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, {
10695
+ static updateOrAdd({ lookupHistory }: { lookupHistory: LookupHistoryItem }) {
10696
+ return new UpdatePreimage({
10710
10697
  kind: UpdatePreimageKind.UpdateOrAdd,
10711
10698
  item: lookupHistory,
10712
10699
  });
@@ -10744,12 +10731,12 @@ declare enum UpdateServiceKind {
10744
10731
  /** Create a new `Service` instance. */
10745
10732
  Create = 1,
10746
10733
  }
10734
+
10747
10735
  /**
10748
- * Update service info of a particular `ServiceId` or create a new one.
10736
+ * Update service info or create a new one.
10749
10737
  */
10750
10738
  declare class UpdateService {
10751
10739
  private constructor(
10752
- public readonly serviceId: ServiceId,
10753
10740
  public readonly action:
10754
10741
  | {
10755
10742
  kind: UpdateServiceKind.Update;
@@ -10762,23 +10749,21 @@ declare class UpdateService {
10762
10749
  },
10763
10750
  ) {}
10764
10751
 
10765
- static update({ serviceId, serviceInfo }: { serviceId: ServiceId; serviceInfo: ServiceAccountInfo }) {
10766
- return new UpdateService(serviceId, {
10752
+ static update({ serviceInfo }: { serviceInfo: ServiceAccountInfo }) {
10753
+ return new UpdateService({
10767
10754
  kind: UpdateServiceKind.Update,
10768
10755
  account: serviceInfo,
10769
10756
  });
10770
10757
  }
10771
10758
 
10772
10759
  static create({
10773
- serviceId,
10774
10760
  serviceInfo,
10775
10761
  lookupHistory,
10776
10762
  }: {
10777
- serviceId: ServiceId;
10778
10763
  serviceInfo: ServiceAccountInfo;
10779
10764
  lookupHistory: LookupHistoryItem | null;
10780
10765
  }) {
10781
- return new UpdateService(serviceId, {
10766
+ return new UpdateService({
10782
10767
  kind: UpdateServiceKind.Create,
10783
10768
  account: serviceInfo,
10784
10769
  lookupHistory,
@@ -10800,7 +10785,6 @@ declare enum UpdateStorageKind {
10800
10785
  */
10801
10786
  declare class UpdateStorage {
10802
10787
  private constructor(
10803
- public readonly serviceId: ServiceId,
10804
10788
  public readonly action:
10805
10789
  | {
10806
10790
  kind: UpdateStorageKind.Set;
@@ -10812,12 +10796,12 @@ declare class UpdateStorage {
10812
10796
  },
10813
10797
  ) {}
10814
10798
 
10815
- static set({ serviceId, storage }: { serviceId: ServiceId; storage: StorageItem }) {
10816
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Set, storage });
10799
+ static set({ storage }: { storage: StorageItem }) {
10800
+ return new UpdateStorage({ kind: UpdateStorageKind.Set, storage });
10817
10801
  }
10818
10802
 
10819
- static remove({ serviceId, key }: { serviceId: ServiceId; key: StorageKey }) {
10820
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Remove, key });
10803
+ static remove({ key }: { key: StorageKey }) {
10804
+ return new UpdateStorage({ kind: UpdateStorageKind.Remove, key });
10821
10805
  }
10822
10806
 
10823
10807
  get key() {
@@ -10835,16 +10819,17 @@ declare class UpdateStorage {
10835
10819
  }
10836
10820
  }
10837
10821
 
10838
- // TODO [ToDr] This would be more convenient to use if the data was grouped by `ServiceId`.
10839
10822
  type ServicesUpdate = {
10840
10823
  /** Service ids to remove from state alongside all their data. */
10841
- servicesRemoved: ServiceId[];
10842
- /** Services to update or create anew. */
10843
- servicesUpdates: UpdateService[];
10824
+ removed: ServiceId[];
10825
+ /** Services newly created. */
10826
+ created: ServiceId[];
10827
+ /** Services to update. */
10828
+ updated: Map<ServiceId, UpdateService>;
10844
10829
  /** Service preimages to update and potentially lookup history */
10845
- preimages: UpdatePreimage[];
10830
+ preimages: Map<ServiceId, UpdatePreimage[]>;
10846
10831
  /** Service storage to update. */
10847
- storage: UpdateStorage[];
10832
+ storage: Map<ServiceId, UpdateStorage[]>;
10848
10833
  };
10849
10834
 
10850
10835
  declare enum UpdateError {
@@ -11040,13 +11025,13 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11040
11025
  * Modify the state and apply a single state update.
11041
11026
  */
11042
11027
  applyUpdate(update: Partial<State & ServicesUpdate>): Result$2<OK, UpdateError> {
11043
- const { servicesRemoved, servicesUpdates, preimages, storage, ...rest } = update;
11028
+ const { removed, created: _, updated, preimages, storage, ...rest } = update;
11044
11029
  // just assign all other variables
11045
11030
  Object.assign(this, rest);
11046
11031
 
11047
11032
  // and update the services state
11048
11033
  let result: Result<OK, UpdateError>;
11049
- result = this.updateServices(servicesUpdates);
11034
+ result = this.updateServices(updated);
11050
11035
  if (result.isError) {
11051
11036
  return result;
11052
11037
  }
@@ -11058,7 +11043,7 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11058
11043
  if (result.isError) {
11059
11044
  return result;
11060
11045
  }
11061
- this.removeServices(servicesRemoved);
11046
+ this.removeServices(removed);
11062
11047
 
11063
11048
  return Result.ok(OK);
11064
11049
  }
@@ -11070,93 +11055,108 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11070
11055
  }
11071
11056
  }
11072
11057
 
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
- }
11058
+ private updateStorage(storageUpdates: Map<ServiceId, UpdateStorage[]> | undefined): Result$2<OK, UpdateError> {
11059
+ if (storageUpdates === undefined) {
11060
+ return Result.ok(OK);
11061
+ }
11062
+ for (const [serviceId, updates] of storageUpdates.entries()) {
11063
+ for (const update of updates) {
11064
+ const { kind } = update.action;
11065
+ const service = this.services.get(serviceId);
11066
+ if (service === undefined) {
11067
+ return Result.error(
11068
+ UpdateError.NoService,
11069
+ () => `Attempting to update storage of non-existing service: ${serviceId}`,
11070
+ );
11071
+ }
11083
11072
 
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`
11073
+ if (kind === UpdateStorageKind.Set) {
11074
+ const { key, value } = update.action.storage;
11075
+ service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
11076
+ } else if (kind === UpdateStorageKind.Remove) {
11077
+ const { key } = update.action;
11078
+ check`
11090
11079
  ${service.data.storage.has(key.toString())}
11091
- Attempting to remove non-existing storage item at ${serviceId}: ${action.key}
11080
+ Attempting to remove non-existing storage item at ${serviceId}: ${update.action.key}
11092
11081
  `;
11093
- service.data.storage.delete(key.toString());
11094
- } else {
11095
- assertNever(kind);
11082
+ service.data.storage.delete(key.toString());
11083
+ } else {
11084
+ assertNever(kind);
11085
+ }
11096
11086
  }
11097
11087
  }
11098
-
11099
11088
  return Result.ok(OK);
11100
11089
  }
11101
11090
 
11102
- private updatePreimages(preimages: UpdatePreimage[] | undefined): Result$2<OK, UpdateError> {
11103
- for (const { serviceId, action } of preimages ?? []) {
11091
+ private updatePreimages(preimagesUpdates: Map<ServiceId, UpdatePreimage[]> | undefined): Result$2<OK, UpdateError> {
11092
+ if (preimagesUpdates === undefined) {
11093
+ return Result.ok(OK);
11094
+ }
11095
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
11104
11096
  const service = this.services.get(serviceId);
11105
11097
  if (service === undefined) {
11106
11098
  return Result.error(
11107
11099
  UpdateError.NoService,
11108
- `Attempting to update preimage of non-existing service: ${serviceId}`,
11100
+ () => `Attempting to update preimage of non-existing service: ${serviceId}`,
11109
11101
  );
11110
11102
  }
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);
11103
+ for (const update of updates) {
11104
+ const { kind } = update.action;
11105
+ if (kind === UpdatePreimageKind.Provide) {
11106
+ const { preimage, slot } = update.action;
11107
+ if (service.data.preimages.has(preimage.hash)) {
11108
+ return Result.error(
11109
+ UpdateError.PreimageExists,
11110
+ () => `Overwriting existing preimage at ${serviceId}: ${preimage}`,
11111
+ );
11129
11112
  }
11113
+ service.data.preimages.set(preimage.hash, preimage);
11114
+ if (slot !== null) {
11115
+ const lookupHistory = service.data.lookupHistory.get(preimage.hash);
11116
+ const length = tryAsU32(preimage.blob.length);
11117
+ const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
11118
+ if (lookupHistory === undefined) {
11119
+ // no lookup history for that preimage at all (edge case, should be requested)
11120
+ service.data.lookupHistory.set(preimage.hash, [lookup]);
11121
+ } else {
11122
+ // insert or replace exiting entry
11123
+ const index = lookupHistory.map((x) => x.length).indexOf(length);
11124
+ lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
11125
+ }
11126
+ }
11127
+ } else if (kind === UpdatePreimageKind.Remove) {
11128
+ const { hash, length } = update.action;
11129
+ service.data.preimages.delete(hash);
11130
+ const history = service.data.lookupHistory.get(hash) ?? [];
11131
+ const idx = history.map((x) => x.length).indexOf(length);
11132
+ if (idx !== -1) {
11133
+ history.splice(idx, 1);
11134
+ }
11135
+ } else if (kind === UpdatePreimageKind.UpdateOrAdd) {
11136
+ const { item } = update.action;
11137
+ const history = service.data.lookupHistory.get(item.hash) ?? [];
11138
+ const existingIdx = history.map((x) => x.length).indexOf(item.length);
11139
+ const removeCount = existingIdx === -1 ? 0 : 1;
11140
+ history.splice(existingIdx, removeCount, item);
11141
+ service.data.lookupHistory.set(item.hash, history);
11142
+ } else {
11143
+ assertNever(kind);
11130
11144
  }
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
11145
  }
11149
11146
  }
11150
11147
  return Result.ok(OK);
11151
11148
  }
11152
11149
 
11153
- private updateServices(servicesUpdates?: UpdateService[]): Result$2<OK, UpdateError> {
11154
- for (const { serviceId, action } of servicesUpdates ?? []) {
11155
- const { kind, account } = action;
11150
+ private updateServices(servicesUpdates: Map<ServiceId, UpdateService> | undefined): Result$2<OK, UpdateError> {
11151
+ if (servicesUpdates === undefined) {
11152
+ return Result.ok(OK);
11153
+ }
11154
+ for (const [serviceId, update] of servicesUpdates.entries()) {
11155
+ const { kind, account } = update.action;
11156
11156
  if (kind === UpdateServiceKind.Create) {
11157
- const { lookupHistory } = action;
11157
+ const { lookupHistory } = update.action;
11158
11158
  if (this.services.has(serviceId)) {
11159
- return Result.error(UpdateError.DuplicateService, `${serviceId} already exists!`);
11159
+ return Result.error(UpdateError.DuplicateService, () => `${serviceId} already exists!`);
11160
11160
  }
11161
11161
  this.services.set(
11162
11162
  serviceId,
@@ -11172,7 +11172,7 @@ declare class InMemoryState extends WithDebug implements State, WithStateView, E
11172
11172
  } else if (kind === UpdateServiceKind.Update) {
11173
11173
  const existingService = this.services.get(serviceId);
11174
11174
  if (existingService === undefined) {
11175
- return Result.error(UpdateError.NoService, `Cannot update ${serviceId} because it does not exist.`);
11175
+ return Result.error(UpdateError.NoService, () => `Cannot update ${serviceId} because it does not exist.`);
11176
11176
  }
11177
11177
  existingService.data.info = account;
11178
11178
  } else {
@@ -11975,89 +11975,104 @@ declare function* serializeStateUpdate(
11975
11975
  const encode = <T>(codec: Encode<T>, val: T) => Encoder.encodeObject(codec, val, spec);
11976
11976
 
11977
11977
  // then let's proceed with service updates
11978
- yield* serializeServiceUpdates(update.servicesUpdates, encode, blake2b);
11978
+ yield* serializeServiceUpdates(update.updated, encode, blake2b);
11979
11979
  yield* serializePreimages(update.preimages, encode, blake2b);
11980
11980
  yield* serializeStorage(update.storage, blake2b);
11981
- yield* serializeRemovedServices(update.servicesRemoved);
11981
+ yield* serializeRemovedServices(update.removed);
11982
11982
  }
11983
11983
 
11984
11984
  declare function* serializeRemovedServices(servicesRemoved: ServiceId[] | undefined): Generator<StateEntryUpdate> {
11985
- for (const serviceId of servicesRemoved ?? []) {
11985
+ if (servicesRemoved === undefined) {
11986
+ return;
11987
+ }
11988
+ for (const serviceId of servicesRemoved) {
11986
11989
  // TODO [ToDr] what about all data associated with a service?
11987
11990
  const codec = serialize.serviceData(serviceId);
11988
11991
  yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
11989
11992
  }
11990
11993
  }
11991
11994
 
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;
11995
+ declare function* serializeStorage(
11996
+ storageUpdates: Map<ServiceId, UpdateStorage[]> | undefined,
11997
+ blake2b: Blake2b,
11998
+ ): Generator<StateEntryUpdate> {
11999
+ if (storageUpdates === undefined) {
12000
+ return;
12001
+ }
12002
+ for (const [serviceId, updates] of storageUpdates.entries()) {
12003
+ for (const { action } of updates) {
12004
+ switch (action.kind) {
12005
+ case UpdateStorageKind.Set: {
12006
+ const key = action.storage.key;
12007
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
12008
+ yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
12009
+ break;
12010
+ }
12011
+ case UpdateStorageKind.Remove: {
12012
+ const key = action.key;
12013
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
12014
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
12015
+ break;
12016
+ }
12006
12017
  }
12007
- default:
12008
- assertNever(action);
12009
12018
  }
12010
12019
  }
12011
12020
  }
12012
12021
 
12013
12022
  declare function* serializePreimages(
12014
- preimages: UpdatePreimage[] | undefined,
12023
+ preimagesUpdates: Map<ServiceId, UpdatePreimage[]> | undefined,
12015
12024
  encode: EncodeFun,
12016
12025
  blake2b: Blake2b,
12017
12026
  ): 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
- ];
12027
+ if (preimagesUpdates === undefined) {
12028
+ return;
12029
+ }
12030
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
12031
+ for (const { action } of updates) {
12032
+ switch (action.kind) {
12033
+ case UpdatePreimageKind.Provide: {
12034
+ const { hash, blob } = action.preimage;
12035
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
12036
+ yield [StateEntryUpdateAction.Insert, codec.key, blob];
12037
+
12038
+ if (action.slot !== null) {
12039
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
12040
+ yield [
12041
+ StateEntryUpdateAction.Insert,
12042
+ codec2.key,
12043
+ encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
12044
+ ];
12045
+ }
12046
+ break;
12032
12047
  }
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];
12048
+ case UpdatePreimageKind.UpdateOrAdd: {
12049
+ const { hash, length, slots } = action.item;
12050
+ const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12051
+ yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
12052
+ break;
12053
+ }
12054
+ case UpdatePreimageKind.Remove: {
12055
+ const { hash, length } = action;
12056
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
12057
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
12045
12058
 
12046
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12047
- yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
12048
- break;
12059
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
12060
+ yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
12061
+ break;
12062
+ }
12049
12063
  }
12050
- default:
12051
- assertNever(action);
12052
12064
  }
12053
12065
  }
12054
12066
  }
12055
12067
  declare function* serializeServiceUpdates(
12056
- servicesUpdates: UpdateService[] | undefined,
12068
+ servicesUpdates: Map<ServiceId, UpdateService> | undefined,
12057
12069
  encode: EncodeFun,
12058
12070
  blake2b: Blake2b,
12059
12071
  ): Generator<StateEntryUpdate> {
12060
- for (const { action, serviceId } of servicesUpdates ?? []) {
12072
+ if (servicesUpdates === undefined) {
12073
+ return;
12074
+ }
12075
+ for (const [serviceId, { action }] of servicesUpdates.entries()) {
12061
12076
  // new service being created or updated
12062
12077
  const codec = serialize.serviceData(serviceId);
12063
12078
  yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, action.account)];
@@ -12646,7 +12661,7 @@ interface ValuesDb {
12646
12661
  * Missing value is considered an irrecoverable error, so the implementations
12647
12662
  * are free to throw if that happens.
12648
12663
  */
12649
- get(key: Uint8Array): Uint8Array;
12664
+ get(key: ValueHash): Uint8Array;
12650
12665
  }
12651
12666
 
12652
12667
  /**
@@ -12663,7 +12678,7 @@ declare class LeafDb implements SerializedStateBackend {
12663
12678
  if (blob.length % TRIE_NODE_BYTES !== 0) {
12664
12679
  return Result.error(
12665
12680
  LeafDbError.InvalidLeafData,
12666
- `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`,
12681
+ () => `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`,
12667
12682
  );
12668
12683
  }
12669
12684
 
@@ -12671,7 +12686,7 @@ declare class LeafDb implements SerializedStateBackend {
12671
12686
  for (const nodeData of blob.chunks(TRIE_NODE_BYTES)) {
12672
12687
  const node = new TrieNode(nodeData.raw);
12673
12688
  if (node.getNodeType() === NodeType.Branch) {
12674
- return Result.error(LeafDbError.InvalidLeafData, `Branch node detected: ${nodeData}`);
12689
+ return Result.error(LeafDbError.InvalidLeafData, () => `Branch node detected: ${nodeData}`);
12675
12690
  }
12676
12691
  leaves.insert(node.asLeafNode());
12677
12692
  }
@@ -12679,15 +12694,20 @@ declare class LeafDb implements SerializedStateBackend {
12679
12694
  return Result.ok(new LeafDb(leaves, db));
12680
12695
  }
12681
12696
 
12697
+ /** Create leaf db from sorted set of leaves. */
12698
+ static fromLeaves(leaves: SortedSet<LeafNode>, db: ValuesDb): LeafDb {
12699
+ return new LeafDb(leaves, db);
12700
+ }
12701
+
12682
12702
  /** A mapping between an embedded value or db lookup key. */
12683
12703
  private readonly lookup: TruncatedHashDictionary<StateKey, Lookup>;
12684
12704
 
12685
12705
  private constructor(
12686
- public readonly leaves: SortedSet<LeafNode>,
12706
+ public readonly leafs: SortedSet<LeafNode>,
12687
12707
  public readonly db: ValuesDb,
12688
12708
  ) {
12689
12709
  this.lookup = TruncatedHashDictionary.fromEntries(
12690
- leaves.array.map((leaf) => {
12710
+ leafs.array.map((leaf) => {
12691
12711
  const key: StateKey = leaf.getKey().asOpaque();
12692
12712
  const value: Lookup = leaf.hasEmbeddedValue()
12693
12713
  ? {
@@ -12696,7 +12716,7 @@ declare class LeafDb implements SerializedStateBackend {
12696
12716
  }
12697
12717
  : {
12698
12718
  kind: LookupKind.DbKey,
12699
- key: leaf.getValueHash().raw,
12719
+ key: leaf.getValueHash(),
12700
12720
  };
12701
12721
  return [key, value];
12702
12722
  }),
@@ -12722,7 +12742,7 @@ declare class LeafDb implements SerializedStateBackend {
12722
12742
 
12723
12743
  getStateRoot(blake2b: Blake2b): StateRootHash {
12724
12744
  const blake2bTrieHasher = getBlake2bTrieHasher(blake2b);
12725
- return InMemoryTrie.computeStateRoot(blake2bTrieHasher, this.leaves).asOpaque();
12745
+ return InMemoryTrie.computeStateRoot(blake2bTrieHasher, this.leafs).asOpaque();
12726
12746
  }
12727
12747
 
12728
12748
  intoStateEntries(): StateEntries {
@@ -12756,9 +12776,42 @@ type Lookup =
12756
12776
  }
12757
12777
  | {
12758
12778
  kind: LookupKind.DbKey;
12759
- key: Uint8Array;
12779
+ key: ValueHash;
12760
12780
  };
12761
12781
 
12782
+ declare function updateLeafs(
12783
+ leafs: SortedSet<LeafNode>,
12784
+ blake2b: Blake2b,
12785
+ data: Iterable<[StateEntryUpdateAction, StateKey | TruncatedHash, BytesBlob]>,
12786
+ ): {
12787
+ values: [ValueHash, BytesBlob][];
12788
+ leafs: SortedSet<LeafNode>;
12789
+ } {
12790
+ const blake2bTrieHasher = getBlake2bTrieHasher(blake2b);
12791
+ // We will collect all values that don't fit directly into leaf nodes.
12792
+ const values: [ValueHash, BytesBlob][] = [];
12793
+ for (const [action, key, value] of data) {
12794
+ if (action === StateEntryUpdateAction.Insert) {
12795
+ const leafNode = InMemoryTrie.constructLeaf(blake2bTrieHasher, key.asOpaque(), value);
12796
+ leafs.replace(leafNode);
12797
+ if (!leafNode.hasEmbeddedValue()) {
12798
+ values.push([leafNode.getValueHash(), value]);
12799
+ }
12800
+ } else if (action === StateEntryUpdateAction.Remove) {
12801
+ const leafNode = InMemoryTrie.constructLeaf(blake2bTrieHasher, key.asOpaque(), BytesBlob.empty());
12802
+ leafs.removeOne(leafNode);
12803
+ // TODO [ToDr] Handle ref-counting values or updating some header-hash-based references.
12804
+ } else {
12805
+ assertNever(action);
12806
+ }
12807
+ }
12808
+
12809
+ return {
12810
+ values,
12811
+ leafs,
12812
+ };
12813
+ }
12814
+
12762
12815
  /** A potential error that occured during state update. */
12763
12816
  declare enum StateUpdateError {
12764
12817
  /** A conflicting state update has been provided. */
@@ -12766,6 +12819,13 @@ declare enum StateUpdateError {
12766
12819
  /** There was an error committing the changes. */
12767
12820
  Commit = 1,
12768
12821
  }
12822
+
12823
+ /** Interface to initialize states db. Typically used in conjunction with `StatesDb`. */
12824
+ interface InitStatesDb<T = State> {
12825
+ /** Insert a pre-defined initial state directly into the database. */
12826
+ insertInitialState(headerHash: HeaderHash, initialState: T): Promise<Result$2<OK, StateUpdateError>>;
12827
+ }
12828
+
12769
12829
  /**
12770
12830
  * Interface for accessing states stored in the database.
12771
12831
  *
@@ -12791,12 +12851,18 @@ interface StatesDb<T extends State = State> {
12791
12851
 
12792
12852
  /** Retrieve posterior state of given header. */
12793
12853
  getState(header: HeaderHash): T | null;
12854
+
12855
+ /** Close the database and free resources. */
12856
+ close(): Promise<void>;
12794
12857
  }
12795
12858
 
12796
12859
  declare class InMemoryStates implements StatesDb<InMemoryState> {
12797
- private readonly db: HashDictionary<HeaderHash, BytesBlob> = HashDictionary.new();
12860
+ private readonly db: HashDictionary<HeaderHash, InMemoryState> = HashDictionary.new();
12861
+ private readonly blake2b: Promise<Blake2b>;
12798
12862
 
12799
- constructor(private readonly spec: ChainSpec) {}
12863
+ constructor(private readonly spec: ChainSpec) {
12864
+ this.blake2b = Blake2b.createHasher();
12865
+ }
12800
12866
 
12801
12867
  async updateAndSetState(
12802
12868
  headerHash: HeaderHash,
@@ -12805,7 +12871,7 @@ declare class InMemoryStates implements StatesDb<InMemoryState> {
12805
12871
  ): Promise<Result$2<OK, StateUpdateError>> {
12806
12872
  const res = state.applyUpdate(update);
12807
12873
  if (res.isOk) {
12808
- return await this.insertState(headerHash, state);
12874
+ return await this.insertInitialState(headerHash, state);
12809
12875
  }
12810
12876
 
12811
12877
  switch (res.error) {
@@ -12819,32 +12885,126 @@ declare class InMemoryStates implements StatesDb<InMemoryState> {
12819
12885
  }
12820
12886
 
12821
12887
  async getStateRoot(state: InMemoryState): Promise<StateRootHash> {
12822
- const blake2b = await Blake2b.createHasher();
12888
+ const blake2b = await this.blake2b;
12823
12889
  return StateEntries.serializeInMemory(this.spec, blake2b, state).getRootHash(blake2b);
12824
12890
  }
12825
12891
 
12826
12892
  /** Insert a full state into the database. */
12827
- async insertState(headerHash: HeaderHash, state: InMemoryState): Promise<Result$2<OK, StateUpdateError>> {
12828
- const encoded = Encoder.encodeObject(inMemoryStateCodec(this.spec), state, this.spec);
12829
- this.db.set(headerHash, encoded);
12893
+ async insertInitialState(headerHash: HeaderHash, state: InMemoryState): Promise<Result$2<OK, StateUpdateError>> {
12894
+ const copy = InMemoryState.copyFrom(this.spec, state, state.intoServicesData());
12895
+ this.db.set(headerHash, copy);
12830
12896
  return Result.ok(OK);
12831
12897
  }
12832
12898
 
12833
12899
  getState(headerHash: HeaderHash): InMemoryState | null {
12834
- const encodedState = this.db.get(headerHash);
12835
- if (encodedState === undefined) {
12900
+ const state = this.db.get(headerHash);
12901
+ if (state === undefined) {
12836
12902
  return null;
12837
12903
  }
12838
12904
 
12839
- return Decoder.decodeObject(inMemoryStateCodec(this.spec), encodedState, this.spec);
12905
+ return InMemoryState.copyFrom(this.spec, state, state.intoServicesData());
12840
12906
  }
12907
+
12908
+ async close() {}
12909
+ }
12910
+
12911
+ /** Root database. */
12912
+ interface RootDb<TBlocks = BlocksDb, TStates = StatesDb> {
12913
+ /** Blocks DB. */
12914
+ getBlocksDb(): TBlocks;
12915
+
12916
+ /** States DB. */
12917
+ getStatesDb(): TStates;
12918
+
12919
+ /** Close access to the DB. */
12920
+ close(): Promise<void>;
12921
+ }
12922
+
12923
+ /** Abstract serialized-states db. */
12924
+ type SerializedStatesDb = StatesDb<SerializedState<LeafDb>> & InitStatesDb<StateEntries>;
12925
+
12926
+ /** In-memory serialized-states db. */
12927
+ declare class InMemorySerializedStates implements StatesDb<SerializedState<LeafDb>>, InitStatesDb<StateEntries> {
12928
+ private readonly db: HashDictionary<HeaderHash, SortedSet<LeafNode>> = HashDictionary.new();
12929
+ private readonly valuesDb: HashDictionary<ValueHash, BytesBlob> = HashDictionary.new();
12930
+
12931
+ constructor(
12932
+ private readonly spec: ChainSpec,
12933
+ private readonly blake2b: Blake2b,
12934
+ ) {}
12935
+
12936
+ async insertInitialState(headerHash: HeaderHash, entries: StateEntries): Promise<Result$2<OK, StateUpdateError>> {
12937
+ // convert state entries into leafdb
12938
+ const { values, leafs } = updateLeafs(
12939
+ SortedSet.fromArray(leafComparator, []),
12940
+ this.blake2b,
12941
+ Array.from(entries, (x) => [StateEntryUpdateAction.Insert, x[0], x[1]]),
12942
+ );
12943
+
12944
+ // insert values to the db.
12945
+ for (const val of values) {
12946
+ this.valuesDb.set(val[0], val[1]);
12947
+ }
12948
+
12949
+ this.db.set(headerHash, leafs);
12950
+ return Result.ok(OK);
12951
+ }
12952
+
12953
+ async getStateRoot(state: SerializedState<LeafDb>): Promise<StateRootHash> {
12954
+ return state.backend.getStateRoot(this.blake2b);
12955
+ }
12956
+
12957
+ async updateAndSetState(
12958
+ header: HeaderHash,
12959
+ state: SerializedState<LeafDb>,
12960
+ update: Partial<State & ServicesUpdate>,
12961
+ ): Promise<Result$2<OK, StateUpdateError>> {
12962
+ const blake2b = this.blake2b;
12963
+ const updatedValues = serializeStateUpdate(this.spec, blake2b, update);
12964
+ const { values, leafs } = updateLeafs(state.backend.leafs, blake2b, updatedValues);
12965
+
12966
+ // insert values to the db
12967
+ // valuesdb can be shared between all states because it's just
12968
+ // <valuehash> -> <value> mapping and existence is managed by trie leafs.
12969
+ for (const val of values) {
12970
+ this.valuesDb.set(val[0], val[1]);
12971
+ }
12972
+
12973
+ // make sure to clone the leafs before writing, since the collection is re-used.
12974
+ this.db.set(header, SortedSet.fromSortedArray(leafComparator, leafs.slice()));
12975
+
12976
+ return Result.ok(OK);
12977
+ }
12978
+
12979
+ getState(header: HeaderHash): SerializedState<LeafDb> | null {
12980
+ const leafs = this.db.get(header);
12981
+ if (leafs === undefined) {
12982
+ return null;
12983
+ }
12984
+ // now create a leafdb with shared values db.
12985
+ const leafDb = LeafDb.fromLeaves(leafs, {
12986
+ get: (key: ValueHash) => {
12987
+ const val = this.valuesDb.get(key);
12988
+ if (val === undefined) {
12989
+ throw new Error(`Missing value at key: ${key}`);
12990
+ }
12991
+ return val.raw;
12992
+ },
12993
+ });
12994
+ return SerializedState.new(this.spec, this.blake2b, leafDb);
12995
+ }
12996
+
12997
+ async close() {}
12841
12998
  }
12842
12999
 
12843
13000
  type index$c_BlocksDb = BlocksDb;
12844
13001
  type index$c_InMemoryBlocks = InMemoryBlocks;
12845
13002
  declare const index$c_InMemoryBlocks: typeof InMemoryBlocks;
13003
+ type index$c_InMemorySerializedStates = InMemorySerializedStates;
13004
+ declare const index$c_InMemorySerializedStates: typeof InMemorySerializedStates;
12846
13005
  type index$c_InMemoryStates = InMemoryStates;
12847
13006
  declare const index$c_InMemoryStates: typeof InMemoryStates;
13007
+ type index$c_InitStatesDb<T = State> = InitStatesDb<T>;
12848
13008
  type index$c_LeafDb = LeafDb;
12849
13009
  declare const index$c_LeafDb: typeof LeafDb;
12850
13010
  type index$c_LeafDbError = LeafDbError;
@@ -12852,13 +13012,16 @@ declare const index$c_LeafDbError: typeof LeafDbError;
12852
13012
  type index$c_Lookup = Lookup;
12853
13013
  type index$c_LookupKind = LookupKind;
12854
13014
  declare const index$c_LookupKind: typeof LookupKind;
13015
+ type index$c_RootDb<TBlocks = BlocksDb, TStates = StatesDb> = RootDb<TBlocks, TStates>;
13016
+ type index$c_SerializedStatesDb = SerializedStatesDb;
12855
13017
  type index$c_StateUpdateError = StateUpdateError;
12856
13018
  declare const index$c_StateUpdateError: typeof StateUpdateError;
12857
13019
  type index$c_StatesDb<T extends State = State> = StatesDb<T>;
12858
13020
  type index$c_ValuesDb = ValuesDb;
13021
+ declare const index$c_updateLeafs: typeof updateLeafs;
12859
13022
  declare namespace index$c {
12860
- export { index$c_InMemoryBlocks as InMemoryBlocks, index$c_InMemoryStates as InMemoryStates, index$c_LeafDb as LeafDb, index$c_LeafDbError as LeafDbError, index$c_LookupKind as LookupKind, index$c_StateUpdateError as StateUpdateError };
12861
- export type { index$c_BlocksDb as BlocksDb, index$c_Lookup as Lookup, index$c_StatesDb as StatesDb, index$c_ValuesDb as ValuesDb };
13023
+ export { index$c_InMemoryBlocks as InMemoryBlocks, index$c_InMemorySerializedStates as InMemorySerializedStates, index$c_InMemoryStates as InMemoryStates, index$c_LeafDb as LeafDb, index$c_LeafDbError as LeafDbError, index$c_LookupKind as LookupKind, index$c_StateUpdateError as StateUpdateError, index$c_updateLeafs as updateLeafs };
13024
+ export type { index$c_BlocksDb as BlocksDb, index$c_InitStatesDb as InitStatesDb, index$c_Lookup as Lookup, index$c_RootDb as RootDb, index$c_SerializedStatesDb as SerializedStatesDb, index$c_StatesDb as StatesDb, index$c_ValuesDb as ValuesDb };
12862
13025
  }
12863
13026
 
12864
13027
  /**
@@ -15483,7 +15646,7 @@ declare class Memory {
15483
15646
  const pagesResult = this.getPages(address, bytes.length, AccessType.WRITE);
15484
15647
 
15485
15648
  if (pagesResult.isError) {
15486
- return Result.error(pagesResult.error);
15649
+ return Result.error(pagesResult.error, pagesResult.details);
15487
15650
  }
15488
15651
 
15489
15652
  const pages = pagesResult.ok;
@@ -15516,17 +15679,23 @@ declare class Memory {
15516
15679
 
15517
15680
  for (const pageNumber of pageRange) {
15518
15681
  if (pageNumber < RESERVED_NUMBER_OF_PAGES) {
15519
- return Result.error(PageFault.fromPageNumber(pageNumber, true));
15682
+ return Result.error(
15683
+ PageFault.fromPageNumber(pageNumber, true),
15684
+ () => `Page fault: attempted to access reserved page ${pageNumber}`,
15685
+ );
15520
15686
  }
15521
15687
 
15522
15688
  const page = this.memory.get(pageNumber);
15523
15689
 
15524
15690
  if (page === undefined) {
15525
- return Result.error(PageFault.fromPageNumber(pageNumber));
15691
+ return Result.error(PageFault.fromPageNumber(pageNumber), () => `Page fault: page ${pageNumber} not allocated`);
15526
15692
  }
15527
15693
 
15528
15694
  if (accessType === AccessType.WRITE && !page.isWriteable()) {
15529
- return Result.error(PageFault.fromPageNumber(pageNumber, true));
15695
+ return Result.error(
15696
+ PageFault.fromPageNumber(pageNumber, true),
15697
+ () => `Page fault: attempted to write to read-only page ${pageNumber}`,
15698
+ );
15530
15699
  }
15531
15700
 
15532
15701
  pages.push(page);
@@ -15548,7 +15717,7 @@ declare class Memory {
15548
15717
  const pagesResult = this.getPages(startAddress, result.length, AccessType.READ);
15549
15718
 
15550
15719
  if (pagesResult.isError) {
15551
- return Result.error(pagesResult.error);
15720
+ return Result.error(pagesResult.error, pagesResult.details);
15552
15721
  }
15553
15722
 
15554
15723
  const pages = pagesResult.ok;
@@ -17533,7 +17702,7 @@ declare class ProgramDecoder {
17533
17702
  return Result.ok(new ProgramDecoder(program));
17534
17703
  } catch (e) {
17535
17704
  logger.error`Invalid program: ${e}`;
17536
- return Result.error(ProgramDecoderError.InvalidProgramError);
17705
+ return Result.error(ProgramDecoderError.InvalidProgramError, () => `Program decoder error: ${e}`);
17537
17706
  }
17538
17707
  }
17539
17708
  }
@@ -17891,7 +18060,10 @@ declare class HostCallMemory implements IHostCallMemory {
17891
18060
  }
17892
18061
 
17893
18062
  if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
17894
- return Result.error(new OutOfBounds());
18063
+ return Result.error(
18064
+ new OutOfBounds(),
18065
+ () => `Memory access out of bounds: address ${address} + length ${bytes.length} exceeds memory size`,
18066
+ );
17895
18067
  }
17896
18068
 
17897
18069
  return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
@@ -17903,7 +18075,10 @@ declare class HostCallMemory implements IHostCallMemory {
17903
18075
  }
17904
18076
 
17905
18077
  if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
17906
- return Result.error(new OutOfBounds());
18078
+ return Result.error(
18079
+ new OutOfBounds(),
18080
+ () => `Memory access out of bounds: address ${startAddress} + length ${result.length} exceeds memory size`,
18081
+ );
17907
18082
  }
17908
18083
 
17909
18084
  return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
@@ -18375,6 +18550,17 @@ type InsufficientFundsError = typeof InsufficientFundsError;
18375
18550
  type ServiceStateUpdate = Partial<Pick<State, "privilegedServices" | "authQueues" | "designatedValidatorData">> &
18376
18551
  ServicesUpdate;
18377
18552
 
18553
+ /** Deep clone of a map with array. */
18554
+ declare function deepCloneMapWithArray<K, V>(map: Map<K, V[]>): Map<K, V[]> {
18555
+ const cloned: [K, V[]][] = [];
18556
+
18557
+ for (const [k, v] of map.entries()) {
18558
+ cloned.push([k, v.slice()]);
18559
+ }
18560
+
18561
+ return new Map(cloned);
18562
+ }
18563
+
18378
18564
  /**
18379
18565
  * State updates that currently accumulating service produced.
18380
18566
  *
@@ -18402,10 +18588,11 @@ declare class AccumulationStateUpdate {
18402
18588
  static empty(): AccumulationStateUpdate {
18403
18589
  return new AccumulationStateUpdate(
18404
18590
  {
18405
- servicesUpdates: [],
18406
- servicesRemoved: [],
18407
- preimages: [],
18408
- storage: [],
18591
+ created: [],
18592
+ updated: new Map(),
18593
+ removed: [],
18594
+ preimages: new Map(),
18595
+ storage: new Map(),
18409
18596
  },
18410
18597
  [],
18411
18598
  );
@@ -18424,10 +18611,13 @@ declare class AccumulationStateUpdate {
18424
18611
  /** Create a copy of another `StateUpdate`. Used by checkpoints. */
18425
18612
  static copyFrom(from: AccumulationStateUpdate): AccumulationStateUpdate {
18426
18613
  const serviceUpdates: ServicesUpdate = {
18427
- servicesUpdates: [...from.services.servicesUpdates],
18428
- servicesRemoved: [...from.services.servicesRemoved],
18429
- preimages: [...from.services.preimages],
18430
- storage: [...from.services.storage],
18614
+ // shallow copy
18615
+ created: [...from.services.created],
18616
+ updated: new Map(from.services.updated),
18617
+ removed: [...from.services.removed],
18618
+ // deep copy
18619
+ preimages: deepCloneMapWithArray(from.services.preimages),
18620
+ storage: deepCloneMapWithArray(from.services.storage),
18431
18621
  };
18432
18622
  const transfers = [...from.transfers];
18433
18623
  const update = new AccumulationStateUpdate(serviceUpdates, transfers, new Map(from.yieldedRoots));
@@ -18485,12 +18675,10 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18485
18675
  return null;
18486
18676
  }
18487
18677
 
18488
- const maybeNewService = this.stateUpdate.services.servicesUpdates.find(
18489
- (update) => update.serviceId === destination,
18490
- );
18678
+ const maybeUpdatedServiceInfo = this.stateUpdate.services.updated.get(destination);
18491
18679
 
18492
- if (maybeNewService !== undefined) {
18493
- return maybeNewService.action.account;
18680
+ if (maybeUpdatedServiceInfo !== undefined) {
18681
+ return maybeUpdatedServiceInfo.action.account;
18494
18682
  }
18495
18683
 
18496
18684
  const maybeService = this.state.getService(destination);
@@ -18502,7 +18690,8 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18502
18690
  }
18503
18691
 
18504
18692
  getStorage(serviceId: ServiceId, rawKey: StorageKey): BytesBlob | null {
18505
- const item = this.stateUpdate.services.storage.find((x) => x.serviceId === serviceId && x.key.isEqualTo(rawKey));
18693
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
18694
+ const item = storages.find((x) => x.key.isEqualTo(rawKey));
18506
18695
  if (item !== undefined) {
18507
18696
  return item.value;
18508
18697
  }
@@ -18519,10 +18708,11 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18519
18708
  * the existence in `preimages` map.
18520
18709
  */
18521
18710
  hasPreimage(serviceId: ServiceId, hash: PreimageHash): boolean {
18522
- const providedPreimage = this.stateUpdate.services.preimages.find(
18711
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18712
+ const providedPreimage = preimages.find(
18523
18713
  // we ignore the action here, since if there is <any> update on that
18524
18714
  // hash it means it has to exist, right?
18525
- (p) => p.serviceId === serviceId && p.hash.isEqualTo(hash),
18715
+ (p) => p.hash.isEqualTo(hash),
18526
18716
  );
18527
18717
  if (providedPreimage !== undefined) {
18528
18718
  return true;
@@ -18539,9 +18729,8 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18539
18729
 
18540
18730
  getPreimage(serviceId: ServiceId, hash: PreimageHash): BytesBlob | null {
18541
18731
  // TODO [ToDr] Should we verify availability here?
18542
- const freshlyProvided = this.stateUpdate.services.preimages.find(
18543
- (x) => x.serviceId === serviceId && x.hash.isEqualTo(hash),
18544
- );
18732
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18733
+ const freshlyProvided = preimages.find((x) => x.hash.isEqualTo(hash));
18545
18734
  if (freshlyProvided !== undefined && freshlyProvided.action.kind === UpdatePreimageKind.Provide) {
18546
18735
  return freshlyProvided.action.preimage.blob;
18547
18736
  }
@@ -18557,11 +18746,12 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18557
18746
  hash: PreimageHash,
18558
18747
  length: U64,
18559
18748
  ): LookupHistoryItem | null {
18749
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18560
18750
  // TODO [ToDr] This is most likely wrong. We may have `provide` and `remove` within
18561
18751
  // the same state update. We should however switch to proper "updated state"
18562
18752
  // representation soon.
18563
- const updatedPreimage = this.stateUpdate.services.preimages.findLast(
18564
- (update) => update.serviceId === serviceId && update.hash.isEqualTo(hash) && BigInt(update.length) === length,
18753
+ const updatedPreimage = preimages.findLast(
18754
+ (update) => update.hash.isEqualTo(hash) && BigInt(update.length) === length,
18565
18755
  );
18566
18756
 
18567
18757
  const stateFallback = () => {
@@ -18604,21 +18794,19 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18604
18794
  }
18605
18795
 
18606
18796
  /* State update functions. */
18607
-
18608
18797
  updateStorage(serviceId: ServiceId, key: StorageKey, value: BytesBlob | null) {
18609
18798
  const update =
18610
18799
  value === null
18611
- ? UpdateStorage.remove({ serviceId, key })
18800
+ ? UpdateStorage.remove({ key })
18612
18801
  : UpdateStorage.set({
18613
- serviceId,
18614
18802
  storage: StorageItem.create({ key, value }),
18615
18803
  });
18616
18804
 
18617
- const index = this.stateUpdate.services.storage.findIndex(
18618
- (x) => x.serviceId === update.serviceId && x.key.isEqualTo(key),
18619
- );
18805
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
18806
+ const index = storages.findIndex((x) => x.key.isEqualTo(key));
18620
18807
  const count = index === -1 ? 0 : 1;
18621
- this.stateUpdate.services.storage.splice(index, count, update);
18808
+ storages.splice(index, count, update);
18809
+ this.stateUpdate.services.storage.set(serviceId, storages);
18622
18810
  }
18623
18811
 
18624
18812
  /**
@@ -18627,8 +18815,10 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18627
18815
  * Note we store all previous entries as well, since there might be a sequence of:
18628
18816
  * `provide` -> `remove` and both should update the end state somehow.
18629
18817
  */
18630
- updatePreimage(newUpdate: UpdatePreimage) {
18631
- this.stateUpdate.services.preimages.push(newUpdate);
18818
+ updatePreimage(serviceId: ServiceId, newUpdate: UpdatePreimage) {
18819
+ const updatePreimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
18820
+ updatePreimages.push(newUpdate);
18821
+ this.stateUpdate.services.preimages.set(serviceId, updatePreimages);
18632
18822
  }
18633
18823
 
18634
18824
  updateServiceStorageUtilisation(
@@ -18645,12 +18835,18 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18645
18835
 
18646
18836
  // TODO [ToDr] this is not specified in GP, but it seems sensible.
18647
18837
  if (overflowItems || overflowBytes) {
18648
- return Result.error(InsufficientFundsError);
18838
+ return Result.error(
18839
+ InsufficientFundsError,
18840
+ () => `Storage utilisation overflow: items=${overflowItems}, bytes=${overflowBytes}`,
18841
+ );
18649
18842
  }
18650
18843
 
18651
18844
  const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
18652
18845
  if (serviceInfo.balance < thresholdBalance) {
18653
- return Result.error(InsufficientFundsError);
18846
+ return Result.error(
18847
+ InsufficientFundsError,
18848
+ () => `Service balance (${serviceInfo.balance}) below threshold (${thresholdBalance})`,
18849
+ );
18654
18850
  }
18655
18851
 
18656
18852
  // Update service info with new details.
@@ -18666,34 +18862,38 @@ declare class PartiallyUpdatedState<T extends StateSlice = StateSlice> {
18666
18862
  }
18667
18863
 
18668
18864
  updateServiceInfo(serviceId: ServiceId, newInfo: ServiceAccountInfo) {
18669
- const idx = this.stateUpdate.services.servicesUpdates.findIndex((x) => x.serviceId === serviceId);
18670
- const toRemove = idx === -1 ? 0 : 1;
18671
- const existingItem = this.stateUpdate.services.servicesUpdates[idx];
18672
-
18673
- if (existingItem?.action.kind === UpdateServiceKind.Create) {
18674
- this.stateUpdate.services.servicesUpdates.splice(
18675
- idx,
18676
- toRemove,
18865
+ const existingUpdate = this.stateUpdate.services.updated.get(serviceId);
18866
+
18867
+ if (existingUpdate?.action.kind === UpdateServiceKind.Create) {
18868
+ this.stateUpdate.services.updated.set(
18869
+ serviceId,
18677
18870
  UpdateService.create({
18678
- serviceId,
18679
18871
  serviceInfo: newInfo,
18680
- lookupHistory: existingItem.action.lookupHistory,
18872
+ lookupHistory: existingUpdate.action.lookupHistory,
18681
18873
  }),
18682
18874
  );
18683
-
18684
18875
  return;
18685
18876
  }
18686
18877
 
18687
- this.stateUpdate.services.servicesUpdates.splice(
18688
- idx,
18689
- toRemove,
18878
+ this.stateUpdate.services.updated.set(
18879
+ serviceId,
18690
18880
  UpdateService.update({
18691
- serviceId,
18692
18881
  serviceInfo: newInfo,
18693
18882
  }),
18694
18883
  );
18695
18884
  }
18696
18885
 
18886
+ createService(serviceId: ServiceId, newInfo: ServiceAccountInfo, newLookupHistory: LookupHistoryItem) {
18887
+ this.stateUpdate.services.created.push(serviceId);
18888
+ this.stateUpdate.services.updated.set(
18889
+ serviceId,
18890
+ UpdateService.create({
18891
+ serviceInfo: newInfo,
18892
+ lookupHistory: newLookupHistory,
18893
+ }),
18894
+ );
18895
+ }
18896
+
18697
18897
  getPrivilegedServices() {
18698
18898
  if (this.stateUpdate.privilegedServices !== null) {
18699
18899
  return this.stateUpdate.privilegedServices;
@@ -18826,6 +19026,7 @@ declare const index$6_UpdatePrivilegesError: typeof UpdatePrivilegesError;
18826
19026
  type index$6_ZeroVoidError = ZeroVoidError;
18827
19027
  declare const index$6_ZeroVoidError: typeof ZeroVoidError;
18828
19028
  declare const index$6_clampU64ToU32: typeof clampU64ToU32;
19029
+ declare const index$6_deepCloneMapWithArray: typeof deepCloneMapWithArray;
18829
19030
  declare const index$6_getServiceId: typeof getServiceId;
18830
19031
  declare const index$6_getServiceIdOrCurrent: typeof getServiceIdOrCurrent;
18831
19032
  declare const index$6_preimageLenAsU32: typeof preimageLenAsU32;
@@ -18835,7 +19036,7 @@ declare const index$6_tryAsMachineId: typeof tryAsMachineId;
18835
19036
  declare const index$6_tryAsProgramCounter: typeof tryAsProgramCounter;
18836
19037
  declare const index$6_writeServiceIdAsLeBytes: typeof writeServiceIdAsLeBytes;
18837
19038
  declare namespace index$6 {
18838
- 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 };
19039
+ 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 };
18839
19040
  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 };
18840
19041
  }
18841
19042
 
@@ -19236,6 +19437,7 @@ declare const index$3_check: typeof check;
19236
19437
  declare const index$3_clampU64ToU32: typeof clampU64ToU32;
19237
19438
  declare const index$3_createResults: typeof createResults;
19238
19439
  declare const index$3_decodeStandardProgram: typeof decodeStandardProgram;
19440
+ declare const index$3_deepCloneMapWithArray: typeof deepCloneMapWithArray;
19239
19441
  declare const index$3_extractCodeAndMetadata: typeof extractCodeAndMetadata;
19240
19442
  declare const index$3_getServiceId: typeof getServiceId;
19241
19443
  declare const index$3_getServiceIdOrCurrent: typeof getServiceIdOrCurrent;
@@ -19254,7 +19456,7 @@ declare const index$3_tryAsMachineId: typeof tryAsMachineId;
19254
19456
  declare const index$3_tryAsProgramCounter: typeof tryAsProgramCounter;
19255
19457
  declare const index$3_writeServiceIdAsLeBytes: typeof writeServiceIdAsLeBytes;
19256
19458
  declare namespace index$3 {
19257
- 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 };
19459
+ 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 };
19258
19460
  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 };
19259
19461
  }
19260
19462
 
@@ -20084,12 +20286,15 @@ declare class Preimages {
20084
20286
  prevPreimage.requester > currPreimage.requester ||
20085
20287
  currPreimage.blob.compare(prevPreimage.blob).isLessOrEqual()
20086
20288
  ) {
20087
- return Result.error(PreimagesErrorCode.PreimagesNotSortedUnique);
20289
+ return Result.error(
20290
+ PreimagesErrorCode.PreimagesNotSortedUnique,
20291
+ () => `Preimages not sorted/unique at index ${i}`,
20292
+ );
20088
20293
  }
20089
20294
  }
20090
20295
 
20091
20296
  const { preimages, slot } = input;
20092
- const pendingChanges: UpdatePreimage[] = [];
20297
+ const pendingChanges = new Map<ServiceId, UpdatePreimage[]>();
20093
20298
 
20094
20299
  // select preimages for integration
20095
20300
  for (const preimage of preimages) {
@@ -20098,7 +20303,7 @@ declare class Preimages {
20098
20303
 
20099
20304
  const service = this.state.getService(requester);
20100
20305
  if (service === null) {
20101
- return Result.error(PreimagesErrorCode.AccountNotFound);
20306
+ return Result.error(PreimagesErrorCode.AccountNotFound, () => `Service not found: ${requester}`);
20102
20307
  }
20103
20308
 
20104
20309
  const hasPreimage = service.hasPreimage(hash);
@@ -20106,17 +20311,22 @@ declare class Preimages {
20106
20311
  // https://graypaper.fluffylabs.dev/#/5f542d7/181800181900
20107
20312
  // https://graypaper.fluffylabs.dev/#/5f542d7/116f0011a500
20108
20313
  if (hasPreimage || slots === null || !LookupHistoryItem.isRequested(slots)) {
20109
- return Result.error(PreimagesErrorCode.PreimageUnneeded);
20314
+ return Result.error(
20315
+ PreimagesErrorCode.PreimageUnneeded,
20316
+ () =>
20317
+ `Preimage unneeded: requester=${requester}, hash=${hash}, hasPreimage=${hasPreimage}, isRequested=${slots !== null && LookupHistoryItem.isRequested(slots)}`,
20318
+ );
20110
20319
  }
20111
20320
 
20112
20321
  // https://graypaper.fluffylabs.dev/#/5f542d7/18c00018f300
20113
- pendingChanges.push(
20322
+ const updates = pendingChanges.get(requester) ?? [];
20323
+ updates.push(
20114
20324
  UpdatePreimage.provide({
20115
- serviceId: requester,
20116
20325
  preimage: PreimageItem.create({ hash, blob }),
20117
20326
  slot,
20118
20327
  }),
20119
20328
  );
20329
+ pendingChanges.set(requester, updates);
20120
20330
  }
20121
20331
 
20122
20332
  return Result.ok({