@typeberry/lib 0.2.0-c3df163 → 0.2.0-f506473

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 +250 -206
  2. package/index.d.ts +300 -227
  3. package/index.js +250 -206
  4. package/package.json +1 -1
package/index.js CHANGED
@@ -301,7 +301,7 @@ function resultToString(res) {
301
301
  if (res.isOk) {
302
302
  return `OK: ${typeof res.ok === "symbol" ? res.ok.toString() : res.ok}`;
303
303
  }
304
- return `${res.details}\nError: ${maybeTaggedErrorToString(res.error)}`;
304
+ return `${res.details()}\nError: ${maybeTaggedErrorToString(res.error)}`;
305
305
  }
306
306
  /** An indication of two possible outcomes returned from a function. */
307
307
  const Result$1 = {
@@ -315,7 +315,7 @@ const Result$1 = {
315
315
  };
316
316
  },
317
317
  /** Create new [`Result`] with `Error` status. */
318
- error: (error, details = "") => {
318
+ error: (error, details) => {
319
319
  check `${error !== undefined} 'Error' type cannot be undefined.`;
320
320
  return {
321
321
  isOk: false,
@@ -428,7 +428,7 @@ function deepEqual(actual, expected, { context = [], errorsCollector, ignore = [
428
428
  }
429
429
  if (actual.isError && expected.isError) {
430
430
  deepEqual(actual.error, expected.error, { context: ctx.concat(["error"]), errorsCollector: errors, ignore });
431
- deepEqual(actual.details, expected.details, {
431
+ deepEqual(actual.details(), expected.details(), {
432
432
  context: ctx.concat(["details"]),
433
433
  errorsCollector: errors,
434
434
  // display details when error does not match
@@ -1119,8 +1119,8 @@ class Decoder {
1119
1119
  /**
1120
1120
  * Create a new [`Decoder`] instance given a raw array of bytes as a source.
1121
1121
  */
1122
- static fromBlob(source) {
1123
- return new Decoder(source);
1122
+ static fromBlob(source, context) {
1123
+ return new Decoder(source, undefined, context);
1124
1124
  }
1125
1125
  /**
1126
1126
  * Decode a single object from all of the source bytes.
@@ -1415,7 +1415,7 @@ class Decoder {
1415
1415
  ensureHasBytes(bytes) {
1416
1416
  check `${bytes >= 0} Negative number of bytes given.`;
1417
1417
  if (this.offset + bytes > this.source.length) {
1418
- throw new Error(`Attempting to decode more data than there is left. Need ${bytes}, left: ${this.source.length - this.offset}.`);
1418
+ throw new EndOfDataError(`Attempting to decode more data than there is left. Need ${bytes}, left: ${this.source.length - this.offset}.`);
1419
1419
  }
1420
1420
  }
1421
1421
  }
@@ -1429,6 +1429,8 @@ function decodeVariableLengthExtraBytes(firstByte) {
1429
1429
  }
1430
1430
  return 0;
1431
1431
  }
1432
+ class EndOfDataError extends Error {
1433
+ }
1432
1434
 
1433
1435
  /** Wrapper for `Decoder` that can skip bytes of fields in the data buffer instead of decoding them. */
1434
1436
  class Skipper {
@@ -2445,6 +2447,9 @@ function forEachDescriptor(descriptors, f) {
2445
2447
  f(k, descriptors[k]);
2446
2448
  }
2447
2449
  catch (e) {
2450
+ if (e instanceof EndOfDataError) {
2451
+ throw new EndOfDataError(`${key}: ${e}`);
2452
+ }
2448
2453
  throw new Error(`${key}: ${e}`);
2449
2454
  }
2450
2455
  }
@@ -2522,6 +2527,7 @@ var index$q = /*#__PURE__*/Object.freeze({
2522
2527
  Decoder: Decoder,
2523
2528
  Descriptor: Descriptor,
2524
2529
  Encoder: Encoder,
2530
+ EndOfDataError: EndOfDataError,
2525
2531
  ObjectView: ObjectView,
2526
2532
  SequenceView: SequenceView,
2527
2533
  TYPICAL_DICTIONARY_LENGTH: TYPICAL_DICTIONARY_LENGTH,
@@ -7308,9 +7314,7 @@ var chain_spec$1 = {
7308
7314
  id: "typeberry-default",
7309
7315
  bootnodes: [
7310
7316
  "e3r2oc62zwfj3crnuifuvsxvbtlzetk4o5qyhetkhagsc2fgl2oka@127.0.0.1:40000",
7311
- "eecgwpgwq3noky4ijm4jmvjtmuzv44qvigciusxakq5epnrfj2utb@127.0.0.1:12345",
7312
- "en5ejs5b2tybkfh4ym5vpfh7nynby73xhtfzmazumtvcijpcsz6ma@127.0.0.1:12346",
7313
- "ekwmt37xecoq6a7otkm4ux5gfmm4uwbat4bg5m223shckhaaxdpqa@127.0.0.1:12347"
7317
+ "eyonydqt7gj7bjdek62lwdeuxdzr5q7nmxa2p5zwwtoijgamdnkka@127.0.0.1:12345"
7314
7318
  ],
7315
7319
  genesis_header: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aaff71c6c03ff88adb5ed52c9681de1629a54e702fc14729f6b50d2f0a76f185b34418fb8c85bb3985394a8c2756d3643457ce614546202a2f50b093d762499acedee6d555b82024f1ccf8a1e37e60fa60fd40b1958c4bb3006af78647950e1b91ad93247bd01307550ec7acd757ce6fb805fcf73db364063265b30a949e90d9339326edb21e5541717fde24ec085000b28709847b8aab1ac51f84e94b37ca1b66cab2b9ff25c2410fbe9b8a717abb298c716a03983c98ceb4def2087500b8e3410746846d17469fb2f95ef365efcab9f4e22fa1feb53111c995376be8019981ccf30aa5444688b3cab47697b37d5cac5707bb3289e986b19b17db437206931a8d151e5c8fe2b9d8a606966a79edd2f9e5db47e83947ce368ccba53bf6ba20a40b8b8c5d436f92ecf605421e873a99ec528761eb52a88a2f9a057b3b3003e6f32a2105650944fcd101621fd5bb3124c9fd191d114b7ad936c1d79d734f9f21392eab0084d01534b31c1dd87c81645fd762482a90027754041ca1b56133d0466c0600ffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
7316
7320
  genesis_state: {
@@ -7356,9 +7360,7 @@ var authorship = {
7356
7360
  var chain_spec = {
7357
7361
  id: "typeberry-dev",
7358
7362
  bootnodes: [
7359
- "eecgwpgwq3noky4ijm4jmvjtmuzv44qvigciusxakq5epnrfj2utb@127.0.0.1:12345",
7360
- "en5ejs5b2tybkfh4ym5vpfh7nynby73xhtfzmazumtvcijpcsz6ma@127.0.0.1:12346",
7361
- "ekwmt37xecoq6a7otkm4ux5gfmm4uwbat4bg5m223shckhaaxdpqa@127.0.0.1:12347"
7363
+ "eyonydqt7gj7bjdek62lwdeuxdzr5q7nmxa2p5zwwtoijgamdnkka@127.0.0.1:12345"
7362
7364
  ],
7363
7365
  genesis_header: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aaff71c6c03ff88adb5ed52c9681de1629a54e702fc14729f6b50d2f0a76f185b34418fb8c85bb3985394a8c2756d3643457ce614546202a2f50b093d762499acedee6d555b82024f1ccf8a1e37e60fa60fd40b1958c4bb3006af78647950e1b91ad93247bd01307550ec7acd757ce6fb805fcf73db364063265b30a949e90d9339326edb21e5541717fde24ec085000b28709847b8aab1ac51f84e94b37ca1b66cab2b9ff25c2410fbe9b8a717abb298c716a03983c98ceb4def2087500b8e3410746846d17469fb2f95ef365efcab9f4e22fa1feb53111c995376be8019981ccf30aa5444688b3cab47697b37d5cac5707bb3289e986b19b17db437206931a8d151e5c8fe2b9d8a606966a79edd2f9e5db47e83947ce368ccba53bf6ba20a40b8b8c5d436f92ecf605421e873a99ec528761eb52a88a2f9a057b3b3003e6f32a2105650944fcd101621fd5bb3124c9fd191d114b7ad936c1d79d734f9f21392eab0084d01534b31c1dd87c81645fd762482a90027754041ca1b56133d0466c0600ffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
7364
7366
  genesis_state: {
@@ -9061,31 +9063,29 @@ var UpdatePreimageKind;
9061
9063
  * 3. Update `LookupHistory` with given value.
9062
9064
  */
9063
9065
  class UpdatePreimage {
9064
- serviceId;
9065
9066
  action;
9066
- constructor(serviceId, action) {
9067
- this.serviceId = serviceId;
9067
+ constructor(action) {
9068
9068
  this.action = action;
9069
9069
  }
9070
9070
  /** A preimage is provided. We should update the lookuphistory and add the preimage to db. */
9071
- static provide({ serviceId, preimage, slot, }) {
9072
- return new UpdatePreimage(serviceId, {
9071
+ static provide({ preimage, slot }) {
9072
+ return new UpdatePreimage({
9073
9073
  kind: UpdatePreimageKind.Provide,
9074
9074
  preimage,
9075
9075
  slot,
9076
9076
  });
9077
9077
  }
9078
9078
  /** The preimage should be removed completely from the database. */
9079
- static remove({ serviceId, hash, length }) {
9080
- return new UpdatePreimage(serviceId, {
9079
+ static remove({ hash, length }) {
9080
+ return new UpdatePreimage({
9081
9081
  kind: UpdatePreimageKind.Remove,
9082
9082
  hash,
9083
9083
  length,
9084
9084
  });
9085
9085
  }
9086
9086
  /** Update the lookup history of some preimage or add a new one (request). */
9087
- static updateOrAdd({ serviceId, lookupHistory }) {
9088
- return new UpdatePreimage(serviceId, {
9087
+ static updateOrAdd({ lookupHistory }) {
9088
+ return new UpdatePreimage({
9089
9089
  kind: UpdatePreimageKind.UpdateOrAdd,
9090
9090
  item: lookupHistory,
9091
9091
  });
@@ -9122,23 +9122,21 @@ var UpdateServiceKind;
9122
9122
  UpdateServiceKind[UpdateServiceKind["Create"] = 1] = "Create";
9123
9123
  })(UpdateServiceKind || (UpdateServiceKind = {}));
9124
9124
  /**
9125
- * Update service info of a particular `ServiceId` or create a new one.
9125
+ * Update service info or create a new one.
9126
9126
  */
9127
9127
  class UpdateService {
9128
- serviceId;
9129
9128
  action;
9130
- constructor(serviceId, action) {
9131
- this.serviceId = serviceId;
9129
+ constructor(action) {
9132
9130
  this.action = action;
9133
9131
  }
9134
- static update({ serviceId, serviceInfo }) {
9135
- return new UpdateService(serviceId, {
9132
+ static update({ serviceInfo }) {
9133
+ return new UpdateService({
9136
9134
  kind: UpdateServiceKind.Update,
9137
9135
  account: serviceInfo,
9138
9136
  });
9139
9137
  }
9140
- static create({ serviceId, serviceInfo, lookupHistory, }) {
9141
- return new UpdateService(serviceId, {
9138
+ static create({ serviceInfo, lookupHistory, }) {
9139
+ return new UpdateService({
9142
9140
  kind: UpdateServiceKind.Create,
9143
9141
  account: serviceInfo,
9144
9142
  lookupHistory,
@@ -9159,17 +9157,15 @@ var UpdateStorageKind;
9159
9157
  * Can either create/modify an entry or remove it.
9160
9158
  */
9161
9159
  class UpdateStorage {
9162
- serviceId;
9163
9160
  action;
9164
- constructor(serviceId, action) {
9165
- this.serviceId = serviceId;
9161
+ constructor(action) {
9166
9162
  this.action = action;
9167
9163
  }
9168
- static set({ serviceId, storage }) {
9169
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Set, storage });
9164
+ static set({ storage }) {
9165
+ return new UpdateStorage({ kind: UpdateStorageKind.Set, storage });
9170
9166
  }
9171
- static remove({ serviceId, key }) {
9172
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Remove, key });
9167
+ static remove({ key }) {
9168
+ return new UpdateStorage({ kind: UpdateStorageKind.Remove, key });
9173
9169
  }
9174
9170
  get key() {
9175
9171
  if (this.action.kind === UpdateStorageKind.Remove) {
@@ -9354,12 +9350,12 @@ class InMemoryState extends WithDebug {
9354
9350
  * Modify the state and apply a single state update.
9355
9351
  */
9356
9352
  applyUpdate(update) {
9357
- const { servicesRemoved, servicesUpdates, preimages, storage, ...rest } = update;
9353
+ const { removed, created: _, updated, preimages, storage, ...rest } = update;
9358
9354
  // just assign all other variables
9359
9355
  Object.assign(this, rest);
9360
9356
  // and update the services state
9361
9357
  let result;
9362
- result = this.updateServices(servicesUpdates);
9358
+ result = this.updateServices(updated);
9363
9359
  if (result.isError) {
9364
9360
  return result;
9365
9361
  }
@@ -9371,7 +9367,7 @@ class InMemoryState extends WithDebug {
9371
9367
  if (result.isError) {
9372
9368
  return result;
9373
9369
  }
9374
- this.removeServices(servicesRemoved);
9370
+ this.removeServices(removed);
9375
9371
  return Result$1.ok(OK);
9376
9372
  }
9377
9373
  removeServices(servicesRemoved) {
@@ -9380,89 +9376,102 @@ class InMemoryState extends WithDebug {
9380
9376
  this.services.delete(serviceId);
9381
9377
  }
9382
9378
  }
9383
- updateStorage(storage) {
9384
- for (const { serviceId, action } of storage ?? []) {
9385
- const { kind } = action;
9386
- const service = this.services.get(serviceId);
9387
- if (service === undefined) {
9388
- return Result$1.error(UpdateError.NoService, `Attempting to update storage of non-existing service: ${serviceId}`);
9389
- }
9390
- if (kind === UpdateStorageKind.Set) {
9391
- const { key, value } = action.storage;
9392
- service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
9393
- }
9394
- else if (kind === UpdateStorageKind.Remove) {
9395
- const { key } = action;
9396
- check `
9379
+ updateStorage(storageUpdates) {
9380
+ if (storageUpdates === undefined) {
9381
+ return Result$1.ok(OK);
9382
+ }
9383
+ for (const [serviceId, updates] of storageUpdates.entries()) {
9384
+ for (const update of updates) {
9385
+ const { kind } = update.action;
9386
+ const service = this.services.get(serviceId);
9387
+ if (service === undefined) {
9388
+ return Result$1.error(UpdateError.NoService, () => `Attempting to update storage of non-existing service: ${serviceId}`);
9389
+ }
9390
+ if (kind === UpdateStorageKind.Set) {
9391
+ const { key, value } = update.action.storage;
9392
+ service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
9393
+ }
9394
+ else if (kind === UpdateStorageKind.Remove) {
9395
+ const { key } = update.action;
9396
+ check `
9397
9397
  ${service.data.storage.has(key.toString())}
9398
- Attempting to remove non-existing storage item at ${serviceId}: ${action.key}
9398
+ Attempting to remove non-existing storage item at ${serviceId}: ${update.action.key}
9399
9399
  `;
9400
- service.data.storage.delete(key.toString());
9401
- }
9402
- else {
9403
- assertNever(kind);
9400
+ service.data.storage.delete(key.toString());
9401
+ }
9402
+ else {
9403
+ assertNever(kind);
9404
+ }
9404
9405
  }
9405
9406
  }
9406
9407
  return Result$1.ok(OK);
9407
9408
  }
9408
- updatePreimages(preimages) {
9409
- for (const { serviceId, action } of preimages ?? []) {
9409
+ updatePreimages(preimagesUpdates) {
9410
+ if (preimagesUpdates === undefined) {
9411
+ return Result$1.ok(OK);
9412
+ }
9413
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
9410
9414
  const service = this.services.get(serviceId);
9411
9415
  if (service === undefined) {
9412
- return Result$1.error(UpdateError.NoService, `Attempting to update preimage of non-existing service: ${serviceId}`);
9416
+ return Result$1.error(UpdateError.NoService, () => `Attempting to update preimage of non-existing service: ${serviceId}`);
9413
9417
  }
9414
- const { kind } = action;
9415
- if (kind === UpdatePreimageKind.Provide) {
9416
- const { preimage, slot } = action;
9417
- if (service.data.preimages.has(preimage.hash)) {
9418
- return Result$1.error(UpdateError.PreimageExists, `Overwriting existing preimage at ${serviceId}: ${preimage}`);
9419
- }
9420
- service.data.preimages.set(preimage.hash, preimage);
9421
- if (slot !== null) {
9422
- const lookupHistory = service.data.lookupHistory.get(preimage.hash);
9423
- const length = tryAsU32(preimage.blob.length);
9424
- const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
9425
- if (lookupHistory === undefined) {
9426
- // no lookup history for that preimage at all (edge case, should be requested)
9427
- service.data.lookupHistory.set(preimage.hash, [lookup]);
9418
+ for (const update of updates) {
9419
+ const { kind } = update.action;
9420
+ if (kind === UpdatePreimageKind.Provide) {
9421
+ const { preimage, slot } = update.action;
9422
+ if (service.data.preimages.has(preimage.hash)) {
9423
+ return Result$1.error(UpdateError.PreimageExists, () => `Overwriting existing preimage at ${serviceId}: ${preimage}`);
9428
9424
  }
9429
- else {
9430
- // insert or replace exiting entry
9431
- const index = lookupHistory.map((x) => x.length).indexOf(length);
9432
- lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
9425
+ service.data.preimages.set(preimage.hash, preimage);
9426
+ if (slot !== null) {
9427
+ const lookupHistory = service.data.lookupHistory.get(preimage.hash);
9428
+ const length = tryAsU32(preimage.blob.length);
9429
+ const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
9430
+ if (lookupHistory === undefined) {
9431
+ // no lookup history for that preimage at all (edge case, should be requested)
9432
+ service.data.lookupHistory.set(preimage.hash, [lookup]);
9433
+ }
9434
+ else {
9435
+ // insert or replace exiting entry
9436
+ const index = lookupHistory.map((x) => x.length).indexOf(length);
9437
+ lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
9438
+ }
9433
9439
  }
9434
9440
  }
9435
- }
9436
- else if (kind === UpdatePreimageKind.Remove) {
9437
- const { hash, length } = action;
9438
- service.data.preimages.delete(hash);
9439
- const history = service.data.lookupHistory.get(hash) ?? [];
9440
- const idx = history.map((x) => x.length).indexOf(length);
9441
- if (idx !== -1) {
9442
- history.splice(idx, 1);
9441
+ else if (kind === UpdatePreimageKind.Remove) {
9442
+ const { hash, length } = update.action;
9443
+ service.data.preimages.delete(hash);
9444
+ const history = service.data.lookupHistory.get(hash) ?? [];
9445
+ const idx = history.map((x) => x.length).indexOf(length);
9446
+ if (idx !== -1) {
9447
+ history.splice(idx, 1);
9448
+ }
9449
+ }
9450
+ else if (kind === UpdatePreimageKind.UpdateOrAdd) {
9451
+ const { item } = update.action;
9452
+ const history = service.data.lookupHistory.get(item.hash) ?? [];
9453
+ const existingIdx = history.map((x) => x.length).indexOf(item.length);
9454
+ const removeCount = existingIdx === -1 ? 0 : 1;
9455
+ history.splice(existingIdx, removeCount, item);
9456
+ service.data.lookupHistory.set(item.hash, history);
9457
+ }
9458
+ else {
9459
+ assertNever(kind);
9443
9460
  }
9444
- }
9445
- else if (kind === UpdatePreimageKind.UpdateOrAdd) {
9446
- const { item } = action;
9447
- const history = service.data.lookupHistory.get(item.hash) ?? [];
9448
- const existingIdx = history.map((x) => x.length).indexOf(item.length);
9449
- const removeCount = existingIdx === -1 ? 0 : 1;
9450
- history.splice(existingIdx, removeCount, item);
9451
- service.data.lookupHistory.set(item.hash, history);
9452
- }
9453
- else {
9454
- assertNever(kind);
9455
9461
  }
9456
9462
  }
9457
9463
  return Result$1.ok(OK);
9458
9464
  }
9459
9465
  updateServices(servicesUpdates) {
9460
- for (const { serviceId, action } of servicesUpdates ?? []) {
9461
- const { kind, account } = action;
9466
+ if (servicesUpdates === undefined) {
9467
+ return Result$1.ok(OK);
9468
+ }
9469
+ for (const [serviceId, update] of servicesUpdates.entries()) {
9470
+ const { kind, account } = update.action;
9462
9471
  if (kind === UpdateServiceKind.Create) {
9463
- const { lookupHistory } = action;
9472
+ const { lookupHistory } = update.action;
9464
9473
  if (this.services.has(serviceId)) {
9465
- return Result$1.error(UpdateError.DuplicateService, `${serviceId} already exists!`);
9474
+ return Result$1.error(UpdateError.DuplicateService, () => `${serviceId} already exists!`);
9466
9475
  }
9467
9476
  this.services.set(serviceId, new InMemoryService(serviceId, {
9468
9477
  info: account,
@@ -9474,7 +9483,7 @@ class InMemoryState extends WithDebug {
9474
9483
  else if (kind === UpdateServiceKind.Update) {
9475
9484
  const existingService = this.services.get(serviceId);
9476
9485
  if (existingService === undefined) {
9477
- return Result$1.error(UpdateError.NoService, `Cannot update ${serviceId} because it does not exist.`);
9486
+ return Result$1.error(UpdateError.NoService, () => `Cannot update ${serviceId} because it does not exist.`);
9478
9487
  }
9479
9488
  existingService.data.info = account;
9480
9489
  }
@@ -10728,76 +10737,88 @@ function* serializeStateUpdate(spec, blake2b, update) {
10728
10737
  yield* serializeBasicKeys(spec, update);
10729
10738
  const encode = (codec, val) => Encoder.encodeObject(codec, val, spec);
10730
10739
  // then let's proceed with service updates
10731
- yield* serializeServiceUpdates(update.servicesUpdates, encode, blake2b);
10740
+ yield* serializeServiceUpdates(update.updated, encode, blake2b);
10732
10741
  yield* serializePreimages(update.preimages, encode, blake2b);
10733
10742
  yield* serializeStorage(update.storage, blake2b);
10734
- yield* serializeRemovedServices(update.servicesRemoved);
10743
+ yield* serializeRemovedServices(update.removed);
10735
10744
  }
10736
10745
  function* serializeRemovedServices(servicesRemoved) {
10737
- for (const serviceId of servicesRemoved ?? []) {
10746
+ if (servicesRemoved === undefined) {
10747
+ return;
10748
+ }
10749
+ for (const serviceId of servicesRemoved) {
10738
10750
  // TODO [ToDr] what about all data associated with a service?
10739
10751
  const codec = serialize.serviceData(serviceId);
10740
10752
  yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10741
10753
  }
10742
10754
  }
10743
- function* serializeStorage(storage, blake2b) {
10744
- for (const { action, serviceId } of storage ?? []) {
10745
- switch (action.kind) {
10746
- case UpdateStorageKind.Set: {
10747
- const key = action.storage.key;
10748
- const codec = serialize.serviceStorage(blake2b, serviceId, key);
10749
- yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
10750
- break;
10751
- }
10752
- case UpdateStorageKind.Remove: {
10753
- const key = action.key;
10754
- const codec = serialize.serviceStorage(blake2b, serviceId, key);
10755
- yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10756
- break;
10755
+ function* serializeStorage(storageUpdates, blake2b) {
10756
+ if (storageUpdates === undefined) {
10757
+ return;
10758
+ }
10759
+ for (const [serviceId, updates] of storageUpdates.entries()) {
10760
+ for (const { action } of updates) {
10761
+ switch (action.kind) {
10762
+ case UpdateStorageKind.Set: {
10763
+ const key = action.storage.key;
10764
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
10765
+ yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
10766
+ break;
10767
+ }
10768
+ case UpdateStorageKind.Remove: {
10769
+ const key = action.key;
10770
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
10771
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10772
+ break;
10773
+ }
10757
10774
  }
10758
- default:
10759
- assertNever(action);
10760
10775
  }
10761
10776
  }
10762
10777
  }
10763
- function* serializePreimages(preimages, encode, blake2b) {
10764
- for (const { action, serviceId } of preimages ?? []) {
10765
- switch (action.kind) {
10766
- case UpdatePreimageKind.Provide: {
10767
- const { hash, blob } = action.preimage;
10768
- const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10769
- yield [StateEntryUpdateAction.Insert, codec.key, blob];
10770
- if (action.slot !== null) {
10771
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
10772
- yield [
10773
- StateEntryUpdateAction.Insert,
10774
- codec2.key,
10775
- encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
10776
- ];
10778
+ function* serializePreimages(preimagesUpdates, encode, blake2b) {
10779
+ if (preimagesUpdates === undefined) {
10780
+ return;
10781
+ }
10782
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
10783
+ for (const { action } of updates) {
10784
+ switch (action.kind) {
10785
+ case UpdatePreimageKind.Provide: {
10786
+ const { hash, blob } = action.preimage;
10787
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10788
+ yield [StateEntryUpdateAction.Insert, codec.key, blob];
10789
+ if (action.slot !== null) {
10790
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
10791
+ yield [
10792
+ StateEntryUpdateAction.Insert,
10793
+ codec2.key,
10794
+ encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
10795
+ ];
10796
+ }
10797
+ break;
10798
+ }
10799
+ case UpdatePreimageKind.UpdateOrAdd: {
10800
+ const { hash, length, slots } = action.item;
10801
+ const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10802
+ yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
10803
+ break;
10804
+ }
10805
+ case UpdatePreimageKind.Remove: {
10806
+ const { hash, length } = action;
10807
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10808
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10809
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10810
+ yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
10811
+ break;
10777
10812
  }
10778
- break;
10779
- }
10780
- case UpdatePreimageKind.UpdateOrAdd: {
10781
- const { hash, length, slots } = action.item;
10782
- const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10783
- yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
10784
- break;
10785
- }
10786
- case UpdatePreimageKind.Remove: {
10787
- const { hash, length } = action;
10788
- const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10789
- yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10790
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10791
- yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
10792
- break;
10793
10813
  }
10794
- default:
10795
- assertNever(action);
10796
10814
  }
10797
10815
  }
10798
10816
  }
10799
10817
  function* serializeServiceUpdates(servicesUpdates, encode, blake2b) {
10800
- for (const { action, serviceId } of servicesUpdates ?? []) {
10818
+ if (servicesUpdates === undefined) {
10819
+ return;
10820
+ }
10821
+ for (const [serviceId, { action }] of servicesUpdates.entries()) {
10801
10822
  // new service being created or updated
10802
10823
  const codec = serialize.serviceData(serviceId);
10803
10824
  yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, action.account)];
@@ -11073,13 +11094,13 @@ class LeafDb {
11073
11094
  */
11074
11095
  static fromLeavesBlob(blob, db) {
11075
11096
  if (blob.length % TRIE_NODE_BYTES !== 0) {
11076
- return Result$1.error(LeafDbError.InvalidLeafData, `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`);
11097
+ return Result$1.error(LeafDbError.InvalidLeafData, () => `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`);
11077
11098
  }
11078
11099
  const leaves = SortedSet.fromArray(leafComparator, []);
11079
11100
  for (const nodeData of blob.chunks(TRIE_NODE_BYTES)) {
11080
11101
  const node = new TrieNode(nodeData.raw);
11081
11102
  if (node.getNodeType() === NodeType.Branch) {
11082
- return Result$1.error(LeafDbError.InvalidLeafData, `Branch node detected: ${nodeData}`);
11103
+ return Result$1.error(LeafDbError.InvalidLeafData, () => `Branch node detected: ${nodeData}`);
11083
11104
  }
11084
11105
  leaves.insert(node.asLeafNode());
11085
11106
  }
@@ -12394,6 +12415,14 @@ const NoMachineError = Symbol("Machine index not found.");
12394
12415
  const SegmentExportError = Symbol("Too many segments already exported.");
12395
12416
 
12396
12417
  const InsufficientFundsError = "insufficient funds";
12418
+ /** Deep clone of a map with array. */
12419
+ function deepCloneMapWithArray(map) {
12420
+ const cloned = [];
12421
+ for (const [k, v] of map.entries()) {
12422
+ cloned.push([k, v.slice()]);
12423
+ }
12424
+ return new Map(cloned);
12425
+ }
12397
12426
  /**
12398
12427
  * State updates that currently accumulating service produced.
12399
12428
  *
@@ -12423,10 +12452,11 @@ class AccumulationStateUpdate {
12423
12452
  /** Create new empty state update. */
12424
12453
  static empty() {
12425
12454
  return new AccumulationStateUpdate({
12426
- servicesUpdates: [],
12427
- servicesRemoved: [],
12428
- preimages: [],
12429
- storage: [],
12455
+ created: [],
12456
+ updated: new Map(),
12457
+ removed: [],
12458
+ preimages: new Map(),
12459
+ storage: new Map(),
12430
12460
  }, []);
12431
12461
  }
12432
12462
  /** Create a state update with some existing, yet uncommited services updates. */
@@ -12438,10 +12468,13 @@ class AccumulationStateUpdate {
12438
12468
  /** Create a copy of another `StateUpdate`. Used by checkpoints. */
12439
12469
  static copyFrom(from) {
12440
12470
  const serviceUpdates = {
12441
- servicesUpdates: [...from.services.servicesUpdates],
12442
- servicesRemoved: [...from.services.servicesRemoved],
12443
- preimages: [...from.services.preimages],
12444
- storage: [...from.services.storage],
12471
+ // shallow copy
12472
+ created: [...from.services.created],
12473
+ updated: new Map(from.services.updated),
12474
+ removed: [...from.services.removed],
12475
+ // deep copy
12476
+ preimages: deepCloneMapWithArray(from.services.preimages),
12477
+ storage: deepCloneMapWithArray(from.services.storage),
12445
12478
  };
12446
12479
  const transfers = [...from.transfers];
12447
12480
  const update = new AccumulationStateUpdate(serviceUpdates, transfers, new Map(from.yieldedRoots));
@@ -12489,9 +12522,9 @@ class PartiallyUpdatedState {
12489
12522
  if (destination === null) {
12490
12523
  return null;
12491
12524
  }
12492
- const maybeNewService = this.stateUpdate.services.servicesUpdates.find((update) => update.serviceId === destination);
12493
- if (maybeNewService !== undefined) {
12494
- return maybeNewService.action.account;
12525
+ const maybeUpdatedServiceInfo = this.stateUpdate.services.updated.get(destination);
12526
+ if (maybeUpdatedServiceInfo !== undefined) {
12527
+ return maybeUpdatedServiceInfo.action.account;
12495
12528
  }
12496
12529
  const maybeService = this.state.getService(destination);
12497
12530
  if (maybeService === null) {
@@ -12500,7 +12533,8 @@ class PartiallyUpdatedState {
12500
12533
  return maybeService.getInfo();
12501
12534
  }
12502
12535
  getStorage(serviceId, rawKey) {
12503
- const item = this.stateUpdate.services.storage.find((x) => x.serviceId === serviceId && x.key.isEqualTo(rawKey));
12536
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
12537
+ const item = storages.find((x) => x.key.isEqualTo(rawKey));
12504
12538
  if (item !== undefined) {
12505
12539
  return item.value;
12506
12540
  }
@@ -12515,10 +12549,11 @@ class PartiallyUpdatedState {
12515
12549
  * the existence in `preimages` map.
12516
12550
  */
12517
12551
  hasPreimage(serviceId, hash) {
12518
- const providedPreimage = this.stateUpdate.services.preimages.find(
12552
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12553
+ const providedPreimage = preimages.find(
12519
12554
  // we ignore the action here, since if there is <any> update on that
12520
12555
  // hash it means it has to exist, right?
12521
- (p) => p.serviceId === serviceId && p.hash.isEqualTo(hash));
12556
+ (p) => p.hash.isEqualTo(hash));
12522
12557
  if (providedPreimage !== undefined) {
12523
12558
  return true;
12524
12559
  }
@@ -12531,7 +12566,8 @@ class PartiallyUpdatedState {
12531
12566
  }
12532
12567
  getPreimage(serviceId, hash) {
12533
12568
  // TODO [ToDr] Should we verify availability here?
12534
- const freshlyProvided = this.stateUpdate.services.preimages.find((x) => x.serviceId === serviceId && x.hash.isEqualTo(hash));
12569
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12570
+ const freshlyProvided = preimages.find((x) => x.hash.isEqualTo(hash));
12535
12571
  if (freshlyProvided !== undefined && freshlyProvided.action.kind === UpdatePreimageKind.Provide) {
12536
12572
  return freshlyProvided.action.preimage.blob;
12537
12573
  }
@@ -12540,10 +12576,11 @@ class PartiallyUpdatedState {
12540
12576
  }
12541
12577
  /** Get status of a preimage of current service taking into account any updates. */
12542
12578
  getLookupHistory(currentTimeslot, serviceId, hash, length) {
12579
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12543
12580
  // TODO [ToDr] This is most likely wrong. We may have `provide` and `remove` within
12544
12581
  // the same state update. We should however switch to proper "updated state"
12545
12582
  // representation soon.
12546
- const updatedPreimage = this.stateUpdate.services.preimages.findLast((update) => update.serviceId === serviceId && update.hash.isEqualTo(hash) && BigInt(update.length) === length);
12583
+ const updatedPreimage = preimages.findLast((update) => update.hash.isEqualTo(hash) && BigInt(update.length) === length);
12547
12584
  const stateFallback = () => {
12548
12585
  // fallback to state lookup
12549
12586
  const service = this.state.getService(serviceId);
@@ -12580,14 +12617,15 @@ class PartiallyUpdatedState {
12580
12617
  /* State update functions. */
12581
12618
  updateStorage(serviceId, key, value) {
12582
12619
  const update = value === null
12583
- ? UpdateStorage.remove({ serviceId, key })
12620
+ ? UpdateStorage.remove({ key })
12584
12621
  : UpdateStorage.set({
12585
- serviceId,
12586
12622
  storage: StorageItem.create({ key, value }),
12587
12623
  });
12588
- const index = this.stateUpdate.services.storage.findIndex((x) => x.serviceId === update.serviceId && x.key.isEqualTo(key));
12624
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
12625
+ const index = storages.findIndex((x) => x.key.isEqualTo(key));
12589
12626
  const count = index === -1 ? 0 : 1;
12590
- this.stateUpdate.services.storage.splice(index, count, update);
12627
+ storages.splice(index, count, update);
12628
+ this.stateUpdate.services.storage.set(serviceId, storages);
12591
12629
  }
12592
12630
  /**
12593
12631
  * Update a preimage.
@@ -12595,8 +12633,10 @@ class PartiallyUpdatedState {
12595
12633
  * Note we store all previous entries as well, since there might be a sequence of:
12596
12634
  * `provide` -> `remove` and both should update the end state somehow.
12597
12635
  */
12598
- updatePreimage(newUpdate) {
12599
- this.stateUpdate.services.preimages.push(newUpdate);
12636
+ updatePreimage(serviceId, newUpdate) {
12637
+ const updatePreimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12638
+ updatePreimages.push(newUpdate);
12639
+ this.stateUpdate.services.preimages.set(serviceId, updatePreimages);
12600
12640
  }
12601
12641
  updateServiceStorageUtilisation(serviceId, items, bytes, serviceInfo) {
12602
12642
  check `${items >= 0} storageUtilisationCount has to be a positive number, got: ${items}`;
@@ -12605,11 +12645,11 @@ class PartiallyUpdatedState {
12605
12645
  const overflowBytes = !isU64(bytes);
12606
12646
  // TODO [ToDr] this is not specified in GP, but it seems sensible.
12607
12647
  if (overflowItems || overflowBytes) {
12608
- return Result$1.error(InsufficientFundsError);
12648
+ return Result$1.error(InsufficientFundsError, () => `Storage utilisation overflow: items=${overflowItems}, bytes=${overflowBytes}`);
12609
12649
  }
12610
12650
  const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
12611
12651
  if (serviceInfo.balance < thresholdBalance) {
12612
- return Result$1.error(InsufficientFundsError);
12652
+ return Result$1.error(InsufficientFundsError, () => `Service balance (${serviceInfo.balance}) below threshold (${thresholdBalance})`);
12613
12653
  }
12614
12654
  // Update service info with new details.
12615
12655
  this.updateServiceInfo(serviceId, ServiceAccountInfo.create({
@@ -12620,20 +12660,23 @@ class PartiallyUpdatedState {
12620
12660
  return Result$1.ok(OK);
12621
12661
  }
12622
12662
  updateServiceInfo(serviceId, newInfo) {
12623
- const idx = this.stateUpdate.services.servicesUpdates.findIndex((x) => x.serviceId === serviceId);
12624
- const toRemove = idx === -1 ? 0 : 1;
12625
- const existingItem = this.stateUpdate.services.servicesUpdates[idx];
12626
- if (existingItem?.action.kind === UpdateServiceKind.Create) {
12627
- this.stateUpdate.services.servicesUpdates.splice(idx, toRemove, UpdateService.create({
12628
- serviceId,
12663
+ const existingUpdate = this.stateUpdate.services.updated.get(serviceId);
12664
+ if (existingUpdate?.action.kind === UpdateServiceKind.Create) {
12665
+ this.stateUpdate.services.updated.set(serviceId, UpdateService.create({
12629
12666
  serviceInfo: newInfo,
12630
- lookupHistory: existingItem.action.lookupHistory,
12667
+ lookupHistory: existingUpdate.action.lookupHistory,
12631
12668
  }));
12632
12669
  return;
12633
12670
  }
12634
- this.stateUpdate.services.servicesUpdates.splice(idx, toRemove, UpdateService.update({
12635
- serviceId,
12671
+ this.stateUpdate.services.updated.set(serviceId, UpdateService.update({
12672
+ serviceInfo: newInfo,
12673
+ }));
12674
+ }
12675
+ createService(serviceId, newInfo, newLookupHistory) {
12676
+ this.stateUpdate.services.created.push(serviceId);
12677
+ this.stateUpdate.services.updated.set(serviceId, UpdateService.create({
12636
12678
  serviceInfo: newInfo,
12679
+ lookupHistory: newLookupHistory,
12637
12680
  }));
12638
12681
  }
12639
12682
  getPrivilegedServices() {
@@ -14254,7 +14297,7 @@ class ReadablePage extends MemoryPage {
14254
14297
  loadInto(result, startIndex, length) {
14255
14298
  const endIndex = startIndex + length;
14256
14299
  if (endIndex > PAGE_SIZE$1) {
14257
- return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1));
14300
+ return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1), () => `Page fault: read beyond page boundary at ${this.start + PAGE_SIZE$1}`);
14258
14301
  }
14259
14302
  const bytes = this.data.subarray(startIndex, endIndex);
14260
14303
  // we zero the bytes, since data might not yet be initialized at `endIndex`.
@@ -14263,7 +14306,7 @@ class ReadablePage extends MemoryPage {
14263
14306
  return Result$1.ok(OK);
14264
14307
  }
14265
14308
  storeFrom(_address, _data) {
14266
- return Result$1.error(PageFault.fromMemoryIndex(this.start, true));
14309
+ return Result$1.error(PageFault.fromMemoryIndex(this.start, true), () => `Page fault: attempted to write to read-only page at ${this.start}`);
14267
14310
  }
14268
14311
  setData(pageIndex, data) {
14269
14312
  this.data.set(data, pageIndex);
@@ -14292,7 +14335,7 @@ class WriteablePage extends MemoryPage {
14292
14335
  loadInto(result, startIndex, length) {
14293
14336
  const endIndex = startIndex + length;
14294
14337
  if (endIndex > PAGE_SIZE$1) {
14295
- return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1));
14338
+ return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1), () => `Page fault: read beyond page boundary at ${this.start + PAGE_SIZE$1}`);
14296
14339
  }
14297
14340
  const bytes = this.view.subarray(startIndex, endIndex);
14298
14341
  // we zero the bytes, since the view might not yet be initialized at `endIndex`.
@@ -14362,7 +14405,7 @@ class Memory {
14362
14405
  logger$3.insane `MEM[${address}] <- ${BytesBlob.blobFrom(bytes)}`;
14363
14406
  const pagesResult = this.getPages(address, bytes.length, AccessType.WRITE);
14364
14407
  if (pagesResult.isError) {
14365
- return Result$1.error(pagesResult.error);
14408
+ return Result$1.error(pagesResult.error, pagesResult.details);
14366
14409
  }
14367
14410
  const pages = pagesResult.ok;
14368
14411
  let currentPosition = address;
@@ -14387,14 +14430,14 @@ class Memory {
14387
14430
  const pages = [];
14388
14431
  for (const pageNumber of pageRange) {
14389
14432
  if (pageNumber < RESERVED_NUMBER_OF_PAGES) {
14390
- return Result$1.error(PageFault.fromPageNumber(pageNumber, true));
14433
+ return Result$1.error(PageFault.fromPageNumber(pageNumber, true), () => `Page fault: attempted to access reserved page ${pageNumber}`);
14391
14434
  }
14392
14435
  const page = this.memory.get(pageNumber);
14393
14436
  if (page === undefined) {
14394
- return Result$1.error(PageFault.fromPageNumber(pageNumber));
14437
+ return Result$1.error(PageFault.fromPageNumber(pageNumber), () => `Page fault: page ${pageNumber} not allocated`);
14395
14438
  }
14396
14439
  if (accessType === AccessType.WRITE && !page.isWriteable()) {
14397
- return Result$1.error(PageFault.fromPageNumber(pageNumber, true));
14440
+ return Result$1.error(PageFault.fromPageNumber(pageNumber, true), () => `Page fault: attempted to write to read-only page ${pageNumber}`);
14398
14441
  }
14399
14442
  pages.push(page);
14400
14443
  }
@@ -14412,7 +14455,7 @@ class Memory {
14412
14455
  }
14413
14456
  const pagesResult = this.getPages(startAddress, result.length, AccessType.READ);
14414
14457
  if (pagesResult.isError) {
14415
- return Result$1.error(pagesResult.error);
14458
+ return Result$1.error(pagesResult.error, pagesResult.details);
14416
14459
  }
14417
14460
  const pages = pagesResult.ok;
14418
14461
  let currentPosition = startAddress;
@@ -16217,7 +16260,7 @@ class ProgramDecoder {
16217
16260
  }
16218
16261
  catch (e) {
16219
16262
  logger$2.error `Invalid program: ${e}`;
16220
- return Result$1.error(ProgramDecoderError.InvalidProgramError);
16263
+ return Result$1.error(ProgramDecoderError.InvalidProgramError, () => `Program decoder error: ${e}`);
16221
16264
  }
16222
16265
  }
16223
16266
  }
@@ -16498,7 +16541,7 @@ class HostCallMemory {
16498
16541
  return Result$1.ok(OK);
16499
16542
  }
16500
16543
  if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
16501
- return Result$1.error(new OutOfBounds());
16544
+ return Result$1.error(new OutOfBounds(), () => `Memory access out of bounds: address ${address} + length ${bytes.length} exceeds memory size`);
16502
16545
  }
16503
16546
  return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
16504
16547
  }
@@ -16507,7 +16550,7 @@ class HostCallMemory {
16507
16550
  return Result$1.ok(OK);
16508
16551
  }
16509
16552
  if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
16510
- return Result$1.error(new OutOfBounds());
16553
+ return Result$1.error(new OutOfBounds(), () => `Memory access out of bounds: address ${startAddress} + length ${result.length} exceeds memory size`);
16511
16554
  }
16512
16555
  return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
16513
16556
  }
@@ -17598,32 +17641,33 @@ class Preimages {
17598
17641
  }
17599
17642
  if (prevPreimage.requester > currPreimage.requester ||
17600
17643
  currPreimage.blob.compare(prevPreimage.blob).isLessOrEqual()) {
17601
- return Result$1.error(PreimagesErrorCode.PreimagesNotSortedUnique);
17644
+ return Result$1.error(PreimagesErrorCode.PreimagesNotSortedUnique, () => `Preimages not sorted/unique at index ${i}`);
17602
17645
  }
17603
17646
  }
17604
17647
  const { preimages, slot } = input;
17605
- const pendingChanges = [];
17648
+ const pendingChanges = new Map();
17606
17649
  // select preimages for integration
17607
17650
  for (const preimage of preimages) {
17608
17651
  const { requester, blob } = preimage;
17609
17652
  const hash = this.blake2b.hashBytes(blob).asOpaque();
17610
17653
  const service = this.state.getService(requester);
17611
17654
  if (service === null) {
17612
- return Result$1.error(PreimagesErrorCode.AccountNotFound);
17655
+ return Result$1.error(PreimagesErrorCode.AccountNotFound, () => `Service not found: ${requester}`);
17613
17656
  }
17614
17657
  const hasPreimage = service.hasPreimage(hash);
17615
17658
  const slots = service.getLookupHistory(hash, tryAsU32(blob.length));
17616
17659
  // https://graypaper.fluffylabs.dev/#/5f542d7/181800181900
17617
17660
  // https://graypaper.fluffylabs.dev/#/5f542d7/116f0011a500
17618
17661
  if (hasPreimage || slots === null || !LookupHistoryItem.isRequested(slots)) {
17619
- return Result$1.error(PreimagesErrorCode.PreimageUnneeded);
17662
+ return Result$1.error(PreimagesErrorCode.PreimageUnneeded, () => `Preimage unneeded: requester=${requester}, hash=${hash}, hasPreimage=${hasPreimage}, isRequested=${slots !== null && LookupHistoryItem.isRequested(slots)}`);
17620
17663
  }
17621
17664
  // https://graypaper.fluffylabs.dev/#/5f542d7/18c00018f300
17622
- pendingChanges.push(UpdatePreimage.provide({
17623
- serviceId: requester,
17665
+ const updates = pendingChanges.get(requester) ?? [];
17666
+ updates.push(UpdatePreimage.provide({
17624
17667
  preimage: PreimageItem.create({ hash, blob }),
17625
17668
  slot,
17626
17669
  }));
17670
+ pendingChanges.set(requester, updates);
17627
17671
  }
17628
17672
  return Result$1.ok({
17629
17673
  preimages: pendingChanges,