@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.cjs CHANGED
@@ -304,7 +304,7 @@ function resultToString(res) {
304
304
  if (res.isOk) {
305
305
  return `OK: ${typeof res.ok === "symbol" ? res.ok.toString() : res.ok}`;
306
306
  }
307
- return `${res.details}\nError: ${maybeTaggedErrorToString(res.error)}`;
307
+ return `${res.details()}\nError: ${maybeTaggedErrorToString(res.error)}`;
308
308
  }
309
309
  /** An indication of two possible outcomes returned from a function. */
310
310
  const Result$1 = {
@@ -318,7 +318,7 @@ const Result$1 = {
318
318
  };
319
319
  },
320
320
  /** Create new [`Result`] with `Error` status. */
321
- error: (error, details = "") => {
321
+ error: (error, details) => {
322
322
  check `${error !== undefined} 'Error' type cannot be undefined.`;
323
323
  return {
324
324
  isOk: false,
@@ -431,7 +431,7 @@ function deepEqual(actual, expected, { context = [], errorsCollector, ignore = [
431
431
  }
432
432
  if (actual.isError && expected.isError) {
433
433
  deepEqual(actual.error, expected.error, { context: ctx.concat(["error"]), errorsCollector: errors, ignore });
434
- deepEqual(actual.details, expected.details, {
434
+ deepEqual(actual.details(), expected.details(), {
435
435
  context: ctx.concat(["details"]),
436
436
  errorsCollector: errors,
437
437
  // display details when error does not match
@@ -1122,8 +1122,8 @@ class Decoder {
1122
1122
  /**
1123
1123
  * Create a new [`Decoder`] instance given a raw array of bytes as a source.
1124
1124
  */
1125
- static fromBlob(source) {
1126
- return new Decoder(source);
1125
+ static fromBlob(source, context) {
1126
+ return new Decoder(source, undefined, context);
1127
1127
  }
1128
1128
  /**
1129
1129
  * Decode a single object from all of the source bytes.
@@ -1418,7 +1418,7 @@ class Decoder {
1418
1418
  ensureHasBytes(bytes) {
1419
1419
  check `${bytes >= 0} Negative number of bytes given.`;
1420
1420
  if (this.offset + bytes > this.source.length) {
1421
- throw new Error(`Attempting to decode more data than there is left. Need ${bytes}, left: ${this.source.length - this.offset}.`);
1421
+ throw new EndOfDataError(`Attempting to decode more data than there is left. Need ${bytes}, left: ${this.source.length - this.offset}.`);
1422
1422
  }
1423
1423
  }
1424
1424
  }
@@ -1432,6 +1432,8 @@ function decodeVariableLengthExtraBytes(firstByte) {
1432
1432
  }
1433
1433
  return 0;
1434
1434
  }
1435
+ class EndOfDataError extends Error {
1436
+ }
1435
1437
 
1436
1438
  /** Wrapper for `Decoder` that can skip bytes of fields in the data buffer instead of decoding them. */
1437
1439
  class Skipper {
@@ -2249,6 +2251,8 @@ var codec$1;
2249
2251
  return ret;
2250
2252
  };
2251
2253
  })();
2254
+ /** Zero-size `void` value. */
2255
+ codec.nothing = Descriptor.new("void", { bytes: 0, isExact: true }, (_e, _v) => { }, (_d) => { }, (_s) => { });
2252
2256
  /** Variable-length U32. */
2253
2257
  codec.varU32 = Descriptor.new("var_u32", { bytes: 4, isExact: false }, (e, v) => e.varU32(v), (d) => d.varU32(), (d) => d.varU32());
2254
2258
  /** Variable-length U64. */
@@ -2448,6 +2452,9 @@ function forEachDescriptor(descriptors, f) {
2448
2452
  f(k, descriptors[k]);
2449
2453
  }
2450
2454
  catch (e) {
2455
+ if (e instanceof EndOfDataError) {
2456
+ throw new EndOfDataError(`${key}: ${e}`);
2457
+ }
2451
2458
  throw new Error(`${key}: ${e}`);
2452
2459
  }
2453
2460
  }
@@ -2525,6 +2532,7 @@ var index$q = /*#__PURE__*/Object.freeze({
2525
2532
  Decoder: Decoder,
2526
2533
  Descriptor: Descriptor,
2527
2534
  Encoder: Encoder,
2535
+ EndOfDataError: EndOfDataError,
2528
2536
  ObjectView: ObjectView,
2529
2537
  SequenceView: SequenceView,
2530
2538
  TYPICAL_DICTIONARY_LENGTH: TYPICAL_DICTIONARY_LENGTH,
@@ -4785,7 +4793,7 @@ class SortedArray {
4785
4793
  isEqual: false,
4786
4794
  };
4787
4795
  }
4788
- /** Create a new SortedSet from two sorted collections. */
4796
+ /** Create a new SortedArray from two sorted collections. */
4789
4797
  static fromTwoSortedCollections(first, second) {
4790
4798
  check `${first.comparator === second.comparator} Cannot merge arrays if they do not use the same comparator`;
4791
4799
  const comparator = first.comparator;
@@ -5142,31 +5150,6 @@ const fullChainSpec = new ChainSpec({
5142
5150
  maxLookupAnchorAge: tryAsU32(14_400),
5143
5151
  });
5144
5152
 
5145
- /**
5146
- * Configuration object for typeberry workers.
5147
- */
5148
- class WorkerConfig {
5149
- chainSpec;
5150
- dbPath;
5151
- omitSealVerification;
5152
- /**
5153
- * Since we loose prototypes when transferring the context,
5154
- * this function is re-initializing proper types.
5155
- *
5156
- * TODO [ToDr] instead of doing this hack, we might prefer to pass data
5157
- * between workers using JAM codec maybe?
5158
- */
5159
- static reInit(config) {
5160
- const { chainSpec, dbPath, omitSealVerification } = config;
5161
- return new WorkerConfig(new ChainSpec(chainSpec), dbPath, omitSealVerification);
5162
- }
5163
- constructor(chainSpec, dbPath, omitSealVerification = false) {
5164
- this.chainSpec = chainSpec;
5165
- this.dbPath = dbPath;
5166
- this.omitSealVerification = omitSealVerification;
5167
- }
5168
- }
5169
-
5170
5153
  /** Bootnode class represents a single contact point in the network */
5171
5154
  class Bootnode {
5172
5155
  id;
@@ -5200,7 +5183,6 @@ var index$m = /*#__PURE__*/Object.freeze({
5200
5183
  EST_EPOCH_LENGTH: EST_EPOCH_LENGTH,
5201
5184
  EST_VALIDATORS: EST_VALIDATORS,
5202
5185
  EST_VALIDATORS_SUPER_MAJORITY: EST_VALIDATORS_SUPER_MAJORITY,
5203
- WorkerConfig: WorkerConfig,
5204
5186
  fullChainSpec: fullChainSpec,
5205
5187
  tinyChainSpec: tinyChainSpec
5206
5188
  });
@@ -7311,9 +7293,7 @@ var chain_spec$1 = {
7311
7293
  id: "typeberry-default",
7312
7294
  bootnodes: [
7313
7295
  "e3r2oc62zwfj3crnuifuvsxvbtlzetk4o5qyhetkhagsc2fgl2oka@127.0.0.1:40000",
7314
- "eecgwpgwq3noky4ijm4jmvjtmuzv44qvigciusxakq5epnrfj2utb@127.0.0.1:12345",
7315
- "en5ejs5b2tybkfh4ym5vpfh7nynby73xhtfzmazumtvcijpcsz6ma@127.0.0.1:12346",
7316
- "ekwmt37xecoq6a7otkm4ux5gfmm4uwbat4bg5m223shckhaaxdpqa@127.0.0.1:12347"
7296
+ "eyonydqt7gj7bjdek62lwdeuxdzr5q7nmxa2p5zwwtoijgamdnkka@127.0.0.1:12345"
7317
7297
  ],
7318
7298
  genesis_header: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aaff71c6c03ff88adb5ed52c9681de1629a54e702fc14729f6b50d2f0a76f185b34418fb8c85bb3985394a8c2756d3643457ce614546202a2f50b093d762499acedee6d555b82024f1ccf8a1e37e60fa60fd40b1958c4bb3006af78647950e1b91ad93247bd01307550ec7acd757ce6fb805fcf73db364063265b30a949e90d9339326edb21e5541717fde24ec085000b28709847b8aab1ac51f84e94b37ca1b66cab2b9ff25c2410fbe9b8a717abb298c716a03983c98ceb4def2087500b8e3410746846d17469fb2f95ef365efcab9f4e22fa1feb53111c995376be8019981ccf30aa5444688b3cab47697b37d5cac5707bb3289e986b19b17db437206931a8d151e5c8fe2b9d8a606966a79edd2f9e5db47e83947ce368ccba53bf6ba20a40b8b8c5d436f92ecf605421e873a99ec528761eb52a88a2f9a057b3b3003e6f32a2105650944fcd101621fd5bb3124c9fd191d114b7ad936c1d79d734f9f21392eab0084d01534b31c1dd87c81645fd762482a90027754041ca1b56133d0466c0600ffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
7319
7299
  genesis_state: {
@@ -7359,9 +7339,7 @@ var authorship = {
7359
7339
  var chain_spec = {
7360
7340
  id: "typeberry-dev",
7361
7341
  bootnodes: [
7362
- "eecgwpgwq3noky4ijm4jmvjtmuzv44qvigciusxakq5epnrfj2utb@127.0.0.1:12345",
7363
- "en5ejs5b2tybkfh4ym5vpfh7nynby73xhtfzmazumtvcijpcsz6ma@127.0.0.1:12346",
7364
- "ekwmt37xecoq6a7otkm4ux5gfmm4uwbat4bg5m223shckhaaxdpqa@127.0.0.1:12347"
7342
+ "eyonydqt7gj7bjdek62lwdeuxdzr5q7nmxa2p5zwwtoijgamdnkka@127.0.0.1:12345"
7365
7343
  ],
7366
7344
  genesis_header: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aaff71c6c03ff88adb5ed52c9681de1629a54e702fc14729f6b50d2f0a76f185b34418fb8c85bb3985394a8c2756d3643457ce614546202a2f50b093d762499acedee6d555b82024f1ccf8a1e37e60fa60fd40b1958c4bb3006af78647950e1b91ad93247bd01307550ec7acd757ce6fb805fcf73db364063265b30a949e90d9339326edb21e5541717fde24ec085000b28709847b8aab1ac51f84e94b37ca1b66cab2b9ff25c2410fbe9b8a717abb298c716a03983c98ceb4def2087500b8e3410746846d17469fb2f95ef365efcab9f4e22fa1feb53111c995376be8019981ccf30aa5444688b3cab47697b37d5cac5707bb3289e986b19b17db437206931a8d151e5c8fe2b9d8a606966a79edd2f9e5db47e83947ce368ccba53bf6ba20a40b8b8c5d436f92ecf605421e873a99ec528761eb52a88a2f9a057b3b3003e6f32a2105650944fcd101621fd5bb3124c9fd191d114b7ad936c1d79d734f9f21392eab0084d01534b31c1dd87c81645fd762482a90027754041ca1b56133d0466c0600ffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
7367
7345
  genesis_state: {
@@ -7784,16 +7762,18 @@ class NodeConfiguration {
7784
7762
  version: "number",
7785
7763
  flavor: knownChainSpecFromJson,
7786
7764
  chain_spec: JipChainSpec.fromJson,
7787
- database_base_path: "string",
7765
+ database_base_path: json.optional("string"),
7788
7766
  authorship: AuthorshipOptions.fromJson,
7789
7767
  }, NodeConfiguration.new);
7790
7768
  static new({ $schema, version, flavor, chain_spec, database_base_path, authorship }) {
7791
7769
  if (version !== 1) {
7792
7770
  throw new Error("Only version=1 config is supported.");
7793
7771
  }
7794
- return new NodeConfiguration($schema, version, flavor, chain_spec, database_base_path, authorship);
7772
+ return new NodeConfiguration($schema, version, flavor, chain_spec, database_base_path ?? undefined, authorship);
7795
7773
  }
7796
- constructor($schema, version, flavor, chainSpec, databaseBasePath, authorship) {
7774
+ constructor($schema, version, flavor, chainSpec,
7775
+ /** If database path is not provided, we load an in-memory db. */
7776
+ databaseBasePath, authorship) {
7797
7777
  this.$schema = $schema;
7798
7778
  this.version = version;
7799
7779
  this.flavor = flavor;
@@ -7879,6 +7859,7 @@ class InMemoryBlocks {
7879
7859
  getExtrinsic(hash) {
7880
7860
  return this.extrinsicsByHeaderHash.get(hash) ?? null;
7881
7861
  }
7862
+ async close() { }
7882
7863
  }
7883
7864
 
7884
7865
  /**
@@ -9064,31 +9045,29 @@ var UpdatePreimageKind;
9064
9045
  * 3. Update `LookupHistory` with given value.
9065
9046
  */
9066
9047
  class UpdatePreimage {
9067
- serviceId;
9068
9048
  action;
9069
- constructor(serviceId, action) {
9070
- this.serviceId = serviceId;
9049
+ constructor(action) {
9071
9050
  this.action = action;
9072
9051
  }
9073
9052
  /** A preimage is provided. We should update the lookuphistory and add the preimage to db. */
9074
- static provide({ serviceId, preimage, slot, }) {
9075
- return new UpdatePreimage(serviceId, {
9053
+ static provide({ preimage, slot }) {
9054
+ return new UpdatePreimage({
9076
9055
  kind: UpdatePreimageKind.Provide,
9077
9056
  preimage,
9078
9057
  slot,
9079
9058
  });
9080
9059
  }
9081
9060
  /** The preimage should be removed completely from the database. */
9082
- static remove({ serviceId, hash, length }) {
9083
- return new UpdatePreimage(serviceId, {
9061
+ static remove({ hash, length }) {
9062
+ return new UpdatePreimage({
9084
9063
  kind: UpdatePreimageKind.Remove,
9085
9064
  hash,
9086
9065
  length,
9087
9066
  });
9088
9067
  }
9089
9068
  /** Update the lookup history of some preimage or add a new one (request). */
9090
- static updateOrAdd({ serviceId, lookupHistory }) {
9091
- return new UpdatePreimage(serviceId, {
9069
+ static updateOrAdd({ lookupHistory }) {
9070
+ return new UpdatePreimage({
9092
9071
  kind: UpdatePreimageKind.UpdateOrAdd,
9093
9072
  item: lookupHistory,
9094
9073
  });
@@ -9125,23 +9104,21 @@ var UpdateServiceKind;
9125
9104
  UpdateServiceKind[UpdateServiceKind["Create"] = 1] = "Create";
9126
9105
  })(UpdateServiceKind || (UpdateServiceKind = {}));
9127
9106
  /**
9128
- * Update service info of a particular `ServiceId` or create a new one.
9107
+ * Update service info or create a new one.
9129
9108
  */
9130
9109
  class UpdateService {
9131
- serviceId;
9132
9110
  action;
9133
- constructor(serviceId, action) {
9134
- this.serviceId = serviceId;
9111
+ constructor(action) {
9135
9112
  this.action = action;
9136
9113
  }
9137
- static update({ serviceId, serviceInfo }) {
9138
- return new UpdateService(serviceId, {
9114
+ static update({ serviceInfo }) {
9115
+ return new UpdateService({
9139
9116
  kind: UpdateServiceKind.Update,
9140
9117
  account: serviceInfo,
9141
9118
  });
9142
9119
  }
9143
- static create({ serviceId, serviceInfo, lookupHistory, }) {
9144
- return new UpdateService(serviceId, {
9120
+ static create({ serviceInfo, lookupHistory, }) {
9121
+ return new UpdateService({
9145
9122
  kind: UpdateServiceKind.Create,
9146
9123
  account: serviceInfo,
9147
9124
  lookupHistory,
@@ -9162,17 +9139,15 @@ var UpdateStorageKind;
9162
9139
  * Can either create/modify an entry or remove it.
9163
9140
  */
9164
9141
  class UpdateStorage {
9165
- serviceId;
9166
9142
  action;
9167
- constructor(serviceId, action) {
9168
- this.serviceId = serviceId;
9143
+ constructor(action) {
9169
9144
  this.action = action;
9170
9145
  }
9171
- static set({ serviceId, storage }) {
9172
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Set, storage });
9146
+ static set({ storage }) {
9147
+ return new UpdateStorage({ kind: UpdateStorageKind.Set, storage });
9173
9148
  }
9174
- static remove({ serviceId, key }) {
9175
- return new UpdateStorage(serviceId, { kind: UpdateStorageKind.Remove, key });
9149
+ static remove({ key }) {
9150
+ return new UpdateStorage({ kind: UpdateStorageKind.Remove, key });
9176
9151
  }
9177
9152
  get key() {
9178
9153
  if (this.action.kind === UpdateStorageKind.Remove) {
@@ -9357,12 +9332,12 @@ class InMemoryState extends WithDebug {
9357
9332
  * Modify the state and apply a single state update.
9358
9333
  */
9359
9334
  applyUpdate(update) {
9360
- const { servicesRemoved, servicesUpdates, preimages, storage, ...rest } = update;
9335
+ const { removed, created: _, updated, preimages, storage, ...rest } = update;
9361
9336
  // just assign all other variables
9362
9337
  Object.assign(this, rest);
9363
9338
  // and update the services state
9364
9339
  let result;
9365
- result = this.updateServices(servicesUpdates);
9340
+ result = this.updateServices(updated);
9366
9341
  if (result.isError) {
9367
9342
  return result;
9368
9343
  }
@@ -9374,7 +9349,7 @@ class InMemoryState extends WithDebug {
9374
9349
  if (result.isError) {
9375
9350
  return result;
9376
9351
  }
9377
- this.removeServices(servicesRemoved);
9352
+ this.removeServices(removed);
9378
9353
  return Result$1.ok(OK);
9379
9354
  }
9380
9355
  removeServices(servicesRemoved) {
@@ -9383,89 +9358,102 @@ class InMemoryState extends WithDebug {
9383
9358
  this.services.delete(serviceId);
9384
9359
  }
9385
9360
  }
9386
- updateStorage(storage) {
9387
- for (const { serviceId, action } of storage ?? []) {
9388
- const { kind } = action;
9389
- const service = this.services.get(serviceId);
9390
- if (service === undefined) {
9391
- return Result$1.error(UpdateError.NoService, `Attempting to update storage of non-existing service: ${serviceId}`);
9392
- }
9393
- if (kind === UpdateStorageKind.Set) {
9394
- const { key, value } = action.storage;
9395
- service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
9396
- }
9397
- else if (kind === UpdateStorageKind.Remove) {
9398
- const { key } = action;
9399
- check `
9361
+ updateStorage(storageUpdates) {
9362
+ if (storageUpdates === undefined) {
9363
+ return Result$1.ok(OK);
9364
+ }
9365
+ for (const [serviceId, updates] of storageUpdates.entries()) {
9366
+ for (const update of updates) {
9367
+ const { kind } = update.action;
9368
+ const service = this.services.get(serviceId);
9369
+ if (service === undefined) {
9370
+ return Result$1.error(UpdateError.NoService, () => `Attempting to update storage of non-existing service: ${serviceId}`);
9371
+ }
9372
+ if (kind === UpdateStorageKind.Set) {
9373
+ const { key, value } = update.action.storage;
9374
+ service.data.storage.set(key.toString(), StorageItem.create({ key, value }));
9375
+ }
9376
+ else if (kind === UpdateStorageKind.Remove) {
9377
+ const { key } = update.action;
9378
+ check `
9400
9379
  ${service.data.storage.has(key.toString())}
9401
- Attempting to remove non-existing storage item at ${serviceId}: ${action.key}
9380
+ Attempting to remove non-existing storage item at ${serviceId}: ${update.action.key}
9402
9381
  `;
9403
- service.data.storage.delete(key.toString());
9404
- }
9405
- else {
9406
- assertNever(kind);
9382
+ service.data.storage.delete(key.toString());
9383
+ }
9384
+ else {
9385
+ assertNever(kind);
9386
+ }
9407
9387
  }
9408
9388
  }
9409
9389
  return Result$1.ok(OK);
9410
9390
  }
9411
- updatePreimages(preimages) {
9412
- for (const { serviceId, action } of preimages ?? []) {
9391
+ updatePreimages(preimagesUpdates) {
9392
+ if (preimagesUpdates === undefined) {
9393
+ return Result$1.ok(OK);
9394
+ }
9395
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
9413
9396
  const service = this.services.get(serviceId);
9414
9397
  if (service === undefined) {
9415
- return Result$1.error(UpdateError.NoService, `Attempting to update preimage of non-existing service: ${serviceId}`);
9398
+ return Result$1.error(UpdateError.NoService, () => `Attempting to update preimage of non-existing service: ${serviceId}`);
9416
9399
  }
9417
- const { kind } = action;
9418
- if (kind === UpdatePreimageKind.Provide) {
9419
- const { preimage, slot } = action;
9420
- if (service.data.preimages.has(preimage.hash)) {
9421
- return Result$1.error(UpdateError.PreimageExists, `Overwriting existing preimage at ${serviceId}: ${preimage}`);
9422
- }
9423
- service.data.preimages.set(preimage.hash, preimage);
9424
- if (slot !== null) {
9425
- const lookupHistory = service.data.lookupHistory.get(preimage.hash);
9426
- const length = tryAsU32(preimage.blob.length);
9427
- const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
9428
- if (lookupHistory === undefined) {
9429
- // no lookup history for that preimage at all (edge case, should be requested)
9430
- service.data.lookupHistory.set(preimage.hash, [lookup]);
9400
+ for (const update of updates) {
9401
+ const { kind } = update.action;
9402
+ if (kind === UpdatePreimageKind.Provide) {
9403
+ const { preimage, slot } = update.action;
9404
+ if (service.data.preimages.has(preimage.hash)) {
9405
+ return Result$1.error(UpdateError.PreimageExists, () => `Overwriting existing preimage at ${serviceId}: ${preimage}`);
9431
9406
  }
9432
- else {
9433
- // insert or replace exiting entry
9434
- const index = lookupHistory.map((x) => x.length).indexOf(length);
9435
- lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
9407
+ service.data.preimages.set(preimage.hash, preimage);
9408
+ if (slot !== null) {
9409
+ const lookupHistory = service.data.lookupHistory.get(preimage.hash);
9410
+ const length = tryAsU32(preimage.blob.length);
9411
+ const lookup = new LookupHistoryItem(preimage.hash, length, tryAsLookupHistorySlots([slot]));
9412
+ if (lookupHistory === undefined) {
9413
+ // no lookup history for that preimage at all (edge case, should be requested)
9414
+ service.data.lookupHistory.set(preimage.hash, [lookup]);
9415
+ }
9416
+ else {
9417
+ // insert or replace exiting entry
9418
+ const index = lookupHistory.map((x) => x.length).indexOf(length);
9419
+ lookupHistory.splice(index, index === -1 ? 0 : 1, lookup);
9420
+ }
9436
9421
  }
9437
9422
  }
9438
- }
9439
- else if (kind === UpdatePreimageKind.Remove) {
9440
- const { hash, length } = action;
9441
- service.data.preimages.delete(hash);
9442
- const history = service.data.lookupHistory.get(hash) ?? [];
9443
- const idx = history.map((x) => x.length).indexOf(length);
9444
- if (idx !== -1) {
9445
- history.splice(idx, 1);
9423
+ else if (kind === UpdatePreimageKind.Remove) {
9424
+ const { hash, length } = update.action;
9425
+ service.data.preimages.delete(hash);
9426
+ const history = service.data.lookupHistory.get(hash) ?? [];
9427
+ const idx = history.map((x) => x.length).indexOf(length);
9428
+ if (idx !== -1) {
9429
+ history.splice(idx, 1);
9430
+ }
9431
+ }
9432
+ else if (kind === UpdatePreimageKind.UpdateOrAdd) {
9433
+ const { item } = update.action;
9434
+ const history = service.data.lookupHistory.get(item.hash) ?? [];
9435
+ const existingIdx = history.map((x) => x.length).indexOf(item.length);
9436
+ const removeCount = existingIdx === -1 ? 0 : 1;
9437
+ history.splice(existingIdx, removeCount, item);
9438
+ service.data.lookupHistory.set(item.hash, history);
9439
+ }
9440
+ else {
9441
+ assertNever(kind);
9446
9442
  }
9447
- }
9448
- else if (kind === UpdatePreimageKind.UpdateOrAdd) {
9449
- const { item } = action;
9450
- const history = service.data.lookupHistory.get(item.hash) ?? [];
9451
- const existingIdx = history.map((x) => x.length).indexOf(item.length);
9452
- const removeCount = existingIdx === -1 ? 0 : 1;
9453
- history.splice(existingIdx, removeCount, item);
9454
- service.data.lookupHistory.set(item.hash, history);
9455
- }
9456
- else {
9457
- assertNever(kind);
9458
9443
  }
9459
9444
  }
9460
9445
  return Result$1.ok(OK);
9461
9446
  }
9462
9447
  updateServices(servicesUpdates) {
9463
- for (const { serviceId, action } of servicesUpdates ?? []) {
9464
- const { kind, account } = action;
9448
+ if (servicesUpdates === undefined) {
9449
+ return Result$1.ok(OK);
9450
+ }
9451
+ for (const [serviceId, update] of servicesUpdates.entries()) {
9452
+ const { kind, account } = update.action;
9465
9453
  if (kind === UpdateServiceKind.Create) {
9466
- const { lookupHistory } = action;
9454
+ const { lookupHistory } = update.action;
9467
9455
  if (this.services.has(serviceId)) {
9468
- return Result$1.error(UpdateError.DuplicateService, `${serviceId} already exists!`);
9456
+ return Result$1.error(UpdateError.DuplicateService, () => `${serviceId} already exists!`);
9469
9457
  }
9470
9458
  this.services.set(serviceId, new InMemoryService(serviceId, {
9471
9459
  info: account,
@@ -9477,7 +9465,7 @@ class InMemoryState extends WithDebug {
9477
9465
  else if (kind === UpdateServiceKind.Update) {
9478
9466
  const existingService = this.services.get(serviceId);
9479
9467
  if (existingService === undefined) {
9480
- return Result$1.error(UpdateError.NoService, `Cannot update ${serviceId} because it does not exist.`);
9468
+ return Result$1.error(UpdateError.NoService, () => `Cannot update ${serviceId} because it does not exist.`);
9481
9469
  }
9482
9470
  existingService.data.info = account;
9483
9471
  }
@@ -10731,76 +10719,88 @@ function* serializeStateUpdate(spec, blake2b, update) {
10731
10719
  yield* serializeBasicKeys(spec, update);
10732
10720
  const encode = (codec, val) => Encoder.encodeObject(codec, val, spec);
10733
10721
  // then let's proceed with service updates
10734
- yield* serializeServiceUpdates(update.servicesUpdates, encode, blake2b);
10722
+ yield* serializeServiceUpdates(update.updated, encode, blake2b);
10735
10723
  yield* serializePreimages(update.preimages, encode, blake2b);
10736
10724
  yield* serializeStorage(update.storage, blake2b);
10737
- yield* serializeRemovedServices(update.servicesRemoved);
10725
+ yield* serializeRemovedServices(update.removed);
10738
10726
  }
10739
10727
  function* serializeRemovedServices(servicesRemoved) {
10740
- for (const serviceId of servicesRemoved ?? []) {
10728
+ if (servicesRemoved === undefined) {
10729
+ return;
10730
+ }
10731
+ for (const serviceId of servicesRemoved) {
10741
10732
  // TODO [ToDr] what about all data associated with a service?
10742
10733
  const codec = serialize.serviceData(serviceId);
10743
10734
  yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10744
10735
  }
10745
10736
  }
10746
- function* serializeStorage(storage, blake2b) {
10747
- for (const { action, serviceId } of storage ?? []) {
10748
- switch (action.kind) {
10749
- case UpdateStorageKind.Set: {
10750
- const key = action.storage.key;
10751
- const codec = serialize.serviceStorage(blake2b, serviceId, key);
10752
- yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
10753
- break;
10754
- }
10755
- case UpdateStorageKind.Remove: {
10756
- const key = action.key;
10757
- const codec = serialize.serviceStorage(blake2b, serviceId, key);
10758
- yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10759
- break;
10737
+ function* serializeStorage(storageUpdates, blake2b) {
10738
+ if (storageUpdates === undefined) {
10739
+ return;
10740
+ }
10741
+ for (const [serviceId, updates] of storageUpdates.entries()) {
10742
+ for (const { action } of updates) {
10743
+ switch (action.kind) {
10744
+ case UpdateStorageKind.Set: {
10745
+ const key = action.storage.key;
10746
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
10747
+ yield [StateEntryUpdateAction.Insert, codec.key, action.storage.value];
10748
+ break;
10749
+ }
10750
+ case UpdateStorageKind.Remove: {
10751
+ const key = action.key;
10752
+ const codec = serialize.serviceStorage(blake2b, serviceId, key);
10753
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10754
+ break;
10755
+ }
10760
10756
  }
10761
- default:
10762
- assertNever(action);
10763
10757
  }
10764
10758
  }
10765
10759
  }
10766
- function* serializePreimages(preimages, encode, blake2b) {
10767
- for (const { action, serviceId } of preimages ?? []) {
10768
- switch (action.kind) {
10769
- case UpdatePreimageKind.Provide: {
10770
- const { hash, blob } = action.preimage;
10771
- const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10772
- yield [StateEntryUpdateAction.Insert, codec.key, blob];
10773
- if (action.slot !== null) {
10774
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
10775
- yield [
10776
- StateEntryUpdateAction.Insert,
10777
- codec2.key,
10778
- encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
10779
- ];
10760
+ function* serializePreimages(preimagesUpdates, encode, blake2b) {
10761
+ if (preimagesUpdates === undefined) {
10762
+ return;
10763
+ }
10764
+ for (const [serviceId, updates] of preimagesUpdates.entries()) {
10765
+ for (const { action } of updates) {
10766
+ switch (action.kind) {
10767
+ case UpdatePreimageKind.Provide: {
10768
+ const { hash, blob } = action.preimage;
10769
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10770
+ yield [StateEntryUpdateAction.Insert, codec.key, blob];
10771
+ if (action.slot !== null) {
10772
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, tryAsU32(blob.length));
10773
+ yield [
10774
+ StateEntryUpdateAction.Insert,
10775
+ codec2.key,
10776
+ encode(codec2.Codec, tryAsLookupHistorySlots([action.slot])),
10777
+ ];
10778
+ }
10779
+ break;
10780
+ }
10781
+ case UpdatePreimageKind.UpdateOrAdd: {
10782
+ const { hash, length, slots } = action.item;
10783
+ const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10784
+ yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
10785
+ break;
10786
+ }
10787
+ case UpdatePreimageKind.Remove: {
10788
+ const { hash, length } = action;
10789
+ const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10790
+ yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10791
+ const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10792
+ yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
10793
+ break;
10780
10794
  }
10781
- break;
10782
- }
10783
- case UpdatePreimageKind.UpdateOrAdd: {
10784
- const { hash, length, slots } = action.item;
10785
- const codec = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10786
- yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, slots)];
10787
- break;
10788
- }
10789
- case UpdatePreimageKind.Remove: {
10790
- const { hash, length } = action;
10791
- const codec = serialize.servicePreimages(blake2b, serviceId, hash);
10792
- yield [StateEntryUpdateAction.Remove, codec.key, EMPTY_BLOB];
10793
- const codec2 = serialize.serviceLookupHistory(blake2b, serviceId, hash, length);
10794
- yield [StateEntryUpdateAction.Remove, codec2.key, EMPTY_BLOB];
10795
- break;
10796
10795
  }
10797
- default:
10798
- assertNever(action);
10799
10796
  }
10800
10797
  }
10801
10798
  }
10802
10799
  function* serializeServiceUpdates(servicesUpdates, encode, blake2b) {
10803
- for (const { action, serviceId } of servicesUpdates ?? []) {
10800
+ if (servicesUpdates === undefined) {
10801
+ return;
10802
+ }
10803
+ for (const [serviceId, { action }] of servicesUpdates.entries()) {
10804
10804
  // new service being created or updated
10805
10805
  const codec = serialize.serviceData(serviceId);
10806
10806
  yield [StateEntryUpdateAction.Insert, codec.key, encode(codec.Codec, action.account)];
@@ -11069,31 +11069,35 @@ var LeafDbError;
11069
11069
  * Note that reading the actual values may require accessing the original database.
11070
11070
  */
11071
11071
  class LeafDb {
11072
- leaves;
11072
+ leafs;
11073
11073
  db;
11074
11074
  /**
11075
11075
  * Parse given blob containing concatenated leaf nodes into leaf db.
11076
11076
  */
11077
11077
  static fromLeavesBlob(blob, db) {
11078
11078
  if (blob.length % TRIE_NODE_BYTES !== 0) {
11079
- return Result$1.error(LeafDbError.InvalidLeafData, `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`);
11079
+ return Result$1.error(LeafDbError.InvalidLeafData, () => `${blob.length} is not a multiply of ${TRIE_NODE_BYTES}: ${blob}`);
11080
11080
  }
11081
11081
  const leaves = SortedSet.fromArray(leafComparator, []);
11082
11082
  for (const nodeData of blob.chunks(TRIE_NODE_BYTES)) {
11083
11083
  const node = new TrieNode(nodeData.raw);
11084
11084
  if (node.getNodeType() === NodeType.Branch) {
11085
- return Result$1.error(LeafDbError.InvalidLeafData, `Branch node detected: ${nodeData}`);
11085
+ return Result$1.error(LeafDbError.InvalidLeafData, () => `Branch node detected: ${nodeData}`);
11086
11086
  }
11087
11087
  leaves.insert(node.asLeafNode());
11088
11088
  }
11089
11089
  return Result$1.ok(new LeafDb(leaves, db));
11090
11090
  }
11091
+ /** Create leaf db from sorted set of leaves. */
11092
+ static fromLeaves(leaves, db) {
11093
+ return new LeafDb(leaves, db);
11094
+ }
11091
11095
  /** A mapping between an embedded value or db lookup key. */
11092
11096
  lookup;
11093
- constructor(leaves, db) {
11094
- this.leaves = leaves;
11097
+ constructor(leafs, db) {
11098
+ this.leafs = leafs;
11095
11099
  this.db = db;
11096
- this.lookup = TruncatedHashDictionary.fromEntries(leaves.array.map((leaf) => {
11100
+ this.lookup = TruncatedHashDictionary.fromEntries(leafs.array.map((leaf) => {
11097
11101
  const key = leaf.getKey().asOpaque();
11098
11102
  const value = leaf.hasEmbeddedValue()
11099
11103
  ? {
@@ -11102,7 +11106,7 @@ class LeafDb {
11102
11106
  }
11103
11107
  : {
11104
11108
  kind: LookupKind.DbKey,
11105
- key: leaf.getValueHash().raw,
11109
+ key: leaf.getValueHash(),
11106
11110
  };
11107
11111
  return [key, value];
11108
11112
  }));
@@ -11122,7 +11126,7 @@ class LeafDb {
11122
11126
  }
11123
11127
  getStateRoot(blake2b) {
11124
11128
  const blake2bTrieHasher = getBlake2bTrieHasher(blake2b);
11125
- return InMemoryTrie.computeStateRoot(blake2bTrieHasher, this.leaves).asOpaque();
11129
+ return InMemoryTrie.computeStateRoot(blake2bTrieHasher, this.leafs).asOpaque();
11126
11130
  }
11127
11131
  intoStateEntries() {
11128
11132
  const entries = [];
@@ -11147,144 +11151,89 @@ var LookupKind;
11147
11151
  LookupKind[LookupKind["DbKey"] = 1] = "DbKey";
11148
11152
  })(LookupKind || (LookupKind = {}));
11149
11153
 
11150
- /** Codec for a map with string keys. */
11151
- const codecMap = (value, extractKey, { typicalLength = TYPICAL_DICTIONARY_LENGTH, compare = (a, b) => {
11152
- const keyA = extractKey(a);
11153
- const keyB = extractKey(b);
11154
- if (keyA < keyB) {
11155
- return Ordering.Less;
11154
+ function updateLeafs(leafs, blake2b, data) {
11155
+ const blake2bTrieHasher = getBlake2bTrieHasher(blake2b);
11156
+ // We will collect all values that don't fit directly into leaf nodes.
11157
+ const values = [];
11158
+ for (const [action, key, value] of data) {
11159
+ if (action === StateEntryUpdateAction.Insert) {
11160
+ const leafNode = InMemoryTrie.constructLeaf(blake2bTrieHasher, key.asOpaque(), value);
11161
+ leafs.replace(leafNode);
11162
+ if (!leafNode.hasEmbeddedValue()) {
11163
+ values.push([leafNode.getValueHash(), value]);
11164
+ }
11165
+ }
11166
+ else if (action === StateEntryUpdateAction.Remove) {
11167
+ const leafNode = InMemoryTrie.constructLeaf(blake2bTrieHasher, key.asOpaque(), BytesBlob.empty());
11168
+ leafs.removeOne(leafNode);
11169
+ // TODO [ToDr] Handle ref-counting values or updating some header-hash-based references.
11170
+ }
11171
+ else {
11172
+ assertNever(action);
11173
+ }
11156
11174
  }
11157
- if (keyA > keyB) {
11158
- return Ordering.Greater;
11175
+ return {
11176
+ values,
11177
+ leafs,
11178
+ };
11179
+ }
11180
+
11181
+ /** In-memory serialized-states db. */
11182
+ class InMemorySerializedStates {
11183
+ spec;
11184
+ blake2b;
11185
+ db = HashDictionary.new();
11186
+ valuesDb = HashDictionary.new();
11187
+ constructor(spec, blake2b) {
11188
+ this.spec = spec;
11189
+ this.blake2b = blake2b;
11159
11190
  }
11160
- return Ordering.Equal;
11161
- }, } = {}) => {
11162
- return Descriptor.new(`Map<${value.name}>[?]`, {
11163
- bytes: typicalLength * value.sizeHint.bytes,
11164
- isExact: false,
11165
- }, (e, v) => {
11166
- const data = Array.from(v.values());
11167
- data.sort((a, b) => compare(a, b).value);
11168
- e.varU32(tryAsU32(data.length));
11169
- for (const v of data) {
11170
- value.encode(e, v);
11191
+ async insertInitialState(headerHash, entries) {
11192
+ // convert state entries into leafdb
11193
+ const { values, leafs } = updateLeafs(SortedSet.fromArray(leafComparator, []), this.blake2b, Array.from(entries, (x) => [StateEntryUpdateAction.Insert, x[0], x[1]]));
11194
+ // insert values to the db.
11195
+ for (const val of values) {
11196
+ this.valuesDb.set(val[0], val[1]);
11171
11197
  }
11172
- }, (d) => {
11173
- const map = new Map();
11174
- const len = d.varU32();
11175
- let prevValue = null;
11176
- for (let i = 0; i < len; i += 1) {
11177
- const v = value.decode(d);
11178
- const k = extractKey(v);
11179
- if (map.has(k)) {
11180
- throw new Error(`Duplicate item in the dictionary encoding: "${k}"!`);
11181
- }
11182
- if (prevValue !== null && compare(prevValue, v).isGreaterOrEqual()) {
11183
- throw new Error(`The keys in dictionary encoding are not sorted "${extractKey(prevValue)}" >= "${extractKey(v)}"!`);
11184
- }
11185
- map.set(k, v);
11186
- prevValue = v;
11198
+ this.db.set(headerHash, leafs);
11199
+ return Result$1.ok(OK);
11200
+ }
11201
+ async getStateRoot(state) {
11202
+ return state.backend.getStateRoot(this.blake2b);
11203
+ }
11204
+ async updateAndSetState(header, state, update) {
11205
+ const blake2b = this.blake2b;
11206
+ const updatedValues = serializeStateUpdate(this.spec, blake2b, update);
11207
+ const { values, leafs } = updateLeafs(state.backend.leafs, blake2b, updatedValues);
11208
+ // insert values to the db
11209
+ // valuesdb can be shared between all states because it's just
11210
+ // <valuehash> -> <value> mapping and existence is managed by trie leafs.
11211
+ for (const val of values) {
11212
+ this.valuesDb.set(val[0], val[1]);
11213
+ }
11214
+ // make sure to clone the leafs before writing, since the collection is re-used.
11215
+ this.db.set(header, SortedSet.fromSortedArray(leafComparator, leafs.slice()));
11216
+ return Result$1.ok(OK);
11217
+ }
11218
+ getState(header) {
11219
+ const leafs = this.db.get(header);
11220
+ if (leafs === undefined) {
11221
+ return null;
11187
11222
  }
11188
- return map;
11189
- }, (s) => {
11190
- const len = s.decoder.varU32();
11191
- s.sequenceFixLen(value, len);
11192
- });
11193
- };
11194
- const lookupHistoryItemCodec = codec$1.object({
11195
- hash: codec$1.bytes(HASH_SIZE).asOpaque(),
11196
- length: codec$1.u32,
11197
- slots: readonlyArray(codec$1.sequenceVarLen(codec$1.u32.asOpaque())).convert(seeThrough, tryAsLookupHistorySlots),
11198
- }, "LookupHistoryItem", ({ hash, length, slots }) => new LookupHistoryItem(hash, length, slots));
11199
- const lookupHistoryEntryCodec = codec$1.object({
11200
- key: codec$1.bytes(HASH_SIZE).asOpaque(),
11201
- data: codec$1.sequenceVarLen(lookupHistoryItemCodec),
11202
- });
11203
- const lookupHistoryCodec = codec$1
11204
- .sequenceVarLen(lookupHistoryEntryCodec)
11205
- .convert((dict) => {
11206
- const entries = [];
11207
- for (const [key, data] of dict) {
11208
- entries.push({
11209
- key,
11210
- data,
11223
+ // now create a leafdb with shared values db.
11224
+ const leafDb = LeafDb.fromLeaves(leafs, {
11225
+ get: (key) => {
11226
+ const val = this.valuesDb.get(key);
11227
+ if (val === undefined) {
11228
+ throw new Error(`Missing value at key: ${key}`);
11229
+ }
11230
+ return val.raw;
11231
+ },
11211
11232
  });
11233
+ return SerializedState.new(this.spec, this.blake2b, leafDb);
11212
11234
  }
11213
- return entries;
11214
- }, (items) => {
11215
- const dict = HashDictionary.new();
11216
- for (const { key, data } of items) {
11217
- const items = dict.get(key) ?? [];
11218
- items.push(...data);
11219
- dict.set(key, items);
11220
- }
11221
- return dict;
11222
- });
11223
- class ServiceWithCodec extends InMemoryService {
11224
- static Codec = codec$1.Class(ServiceWithCodec, {
11225
- serviceId: codec$1.u32.asOpaque(),
11226
- data: codec$1.object({
11227
- info: ServiceAccountInfo.Codec,
11228
- preimages: codecHashDictionary(PreimageItem.Codec, (x) => x.hash),
11229
- lookupHistory: lookupHistoryCodec,
11230
- storage: codecMap(StorageItem.Codec, (x) => x.key.toString()),
11231
- }),
11232
- });
11233
- constructor(id, data) {
11234
- super(id, data);
11235
- }
11236
- static create({ serviceId, data }) {
11237
- return new ServiceWithCodec(serviceId, data);
11238
- }
11239
- }
11240
- const inMemoryStateCodec = (spec) => codec$1.Class(class State extends InMemoryState {
11241
- static create(data) {
11242
- return InMemoryState.new(spec, data);
11243
- }
11244
- }, {
11245
- // alpha
11246
- authPools: serialize.authPools.Codec,
11247
- // phi
11248
- authQueues: serialize.authQueues.Codec,
11249
- // beta
11250
- recentBlocks: serialize.recentBlocks.Codec,
11251
- // gamma_k
11252
- nextValidatorData: codecPerValidator(ValidatorData.Codec),
11253
- // gamma_z
11254
- epochRoot: codec$1.bytes(BANDERSNATCH_RING_ROOT_BYTES).asOpaque(),
11255
- // gamma_s
11256
- sealingKeySeries: SafroleSealingKeysData.Codec,
11257
- // gamma_a
11258
- ticketsAccumulator: readonlyArray(codec$1.sequenceVarLen(Ticket.Codec)).convert((x) => x, asKnownSize),
11259
- // psi
11260
- disputesRecords: serialize.disputesRecords.Codec,
11261
- // eta
11262
- entropy: serialize.entropy.Codec,
11263
- // iota
11264
- designatedValidatorData: serialize.designatedValidators.Codec,
11265
- // kappa
11266
- currentValidatorData: serialize.currentValidators.Codec,
11267
- // lambda
11268
- previousValidatorData: serialize.previousValidators.Codec,
11269
- // rho
11270
- availabilityAssignment: serialize.availabilityAssignment.Codec,
11271
- // tau
11272
- timeslot: serialize.timeslot.Codec,
11273
- // chi
11274
- privilegedServices: serialize.privilegedServices.Codec,
11275
- // pi
11276
- statistics: serialize.statistics.Codec,
11277
- // omega
11278
- accumulationQueue: serialize.accumulationQueue.Codec,
11279
- // xi
11280
- recentlyAccumulated: serialize.recentlyAccumulated.Codec,
11281
- // theta
11282
- accumulationOutputLog: serialize.accumulationOutputLog.Codec,
11283
- // delta
11284
- services: codec$1.dictionary(codec$1.u32.asOpaque(), ServiceWithCodec.Codec, {
11285
- sortKeys: (a, b) => a - b,
11286
- }),
11287
- });
11235
+ async close() { }
11236
+ }
11288
11237
 
11289
11238
  /** A potential error that occured during state update. */
11290
11239
  var StateUpdateError;
@@ -11297,13 +11246,15 @@ var StateUpdateError;
11297
11246
  class InMemoryStates {
11298
11247
  spec;
11299
11248
  db = HashDictionary.new();
11249
+ blake2b;
11300
11250
  constructor(spec) {
11301
11251
  this.spec = spec;
11252
+ this.blake2b = Blake2b.createHasher();
11302
11253
  }
11303
11254
  async updateAndSetState(headerHash, state, update) {
11304
11255
  const res = state.applyUpdate(update);
11305
11256
  if (res.isOk) {
11306
- return await this.insertState(headerHash, state);
11257
+ return await this.insertInitialState(headerHash, state);
11307
11258
  }
11308
11259
  switch (res.error) {
11309
11260
  case UpdateError.DuplicateService:
@@ -11315,31 +11266,34 @@ class InMemoryStates {
11315
11266
  }
11316
11267
  }
11317
11268
  async getStateRoot(state) {
11318
- const blake2b = await Blake2b.createHasher();
11269
+ const blake2b = await this.blake2b;
11319
11270
  return StateEntries.serializeInMemory(this.spec, blake2b, state).getRootHash(blake2b);
11320
11271
  }
11321
11272
  /** Insert a full state into the database. */
11322
- async insertState(headerHash, state) {
11323
- const encoded = Encoder.encodeObject(inMemoryStateCodec(this.spec), state, this.spec);
11324
- this.db.set(headerHash, encoded);
11273
+ async insertInitialState(headerHash, state) {
11274
+ const copy = InMemoryState.copyFrom(this.spec, state, state.intoServicesData());
11275
+ this.db.set(headerHash, copy);
11325
11276
  return Result$1.ok(OK);
11326
11277
  }
11327
11278
  getState(headerHash) {
11328
- const encodedState = this.db.get(headerHash);
11329
- if (encodedState === undefined) {
11279
+ const state = this.db.get(headerHash);
11280
+ if (state === undefined) {
11330
11281
  return null;
11331
11282
  }
11332
- return Decoder.decodeObject(inMemoryStateCodec(this.spec), encodedState, this.spec);
11283
+ return InMemoryState.copyFrom(this.spec, state, state.intoServicesData());
11333
11284
  }
11285
+ async close() { }
11334
11286
  }
11335
11287
 
11336
11288
  var index$d = /*#__PURE__*/Object.freeze({
11337
11289
  __proto__: null,
11338
11290
  InMemoryBlocks: InMemoryBlocks,
11291
+ InMemorySerializedStates: InMemorySerializedStates,
11339
11292
  InMemoryStates: InMemoryStates,
11340
11293
  LeafDb: LeafDb,
11341
11294
  get LeafDbError () { return LeafDbError; },
11342
- get StateUpdateError () { return StateUpdateError; }
11295
+ get StateUpdateError () { return StateUpdateError; },
11296
+ updateLeafs: updateLeafs
11343
11297
  });
11344
11298
 
11345
11299
  /**
@@ -12397,6 +12351,14 @@ const NoMachineError = Symbol("Machine index not found.");
12397
12351
  const SegmentExportError = Symbol("Too many segments already exported.");
12398
12352
 
12399
12353
  const InsufficientFundsError = "insufficient funds";
12354
+ /** Deep clone of a map with array. */
12355
+ function deepCloneMapWithArray(map) {
12356
+ const cloned = [];
12357
+ for (const [k, v] of map.entries()) {
12358
+ cloned.push([k, v.slice()]);
12359
+ }
12360
+ return new Map(cloned);
12361
+ }
12400
12362
  /**
12401
12363
  * State updates that currently accumulating service produced.
12402
12364
  *
@@ -12426,10 +12388,11 @@ class AccumulationStateUpdate {
12426
12388
  /** Create new empty state update. */
12427
12389
  static empty() {
12428
12390
  return new AccumulationStateUpdate({
12429
- servicesUpdates: [],
12430
- servicesRemoved: [],
12431
- preimages: [],
12432
- storage: [],
12391
+ created: [],
12392
+ updated: new Map(),
12393
+ removed: [],
12394
+ preimages: new Map(),
12395
+ storage: new Map(),
12433
12396
  }, []);
12434
12397
  }
12435
12398
  /** Create a state update with some existing, yet uncommited services updates. */
@@ -12441,10 +12404,13 @@ class AccumulationStateUpdate {
12441
12404
  /** Create a copy of another `StateUpdate`. Used by checkpoints. */
12442
12405
  static copyFrom(from) {
12443
12406
  const serviceUpdates = {
12444
- servicesUpdates: [...from.services.servicesUpdates],
12445
- servicesRemoved: [...from.services.servicesRemoved],
12446
- preimages: [...from.services.preimages],
12447
- storage: [...from.services.storage],
12407
+ // shallow copy
12408
+ created: [...from.services.created],
12409
+ updated: new Map(from.services.updated),
12410
+ removed: [...from.services.removed],
12411
+ // deep copy
12412
+ preimages: deepCloneMapWithArray(from.services.preimages),
12413
+ storage: deepCloneMapWithArray(from.services.storage),
12448
12414
  };
12449
12415
  const transfers = [...from.transfers];
12450
12416
  const update = new AccumulationStateUpdate(serviceUpdates, transfers, new Map(from.yieldedRoots));
@@ -12492,9 +12458,9 @@ class PartiallyUpdatedState {
12492
12458
  if (destination === null) {
12493
12459
  return null;
12494
12460
  }
12495
- const maybeNewService = this.stateUpdate.services.servicesUpdates.find((update) => update.serviceId === destination);
12496
- if (maybeNewService !== undefined) {
12497
- return maybeNewService.action.account;
12461
+ const maybeUpdatedServiceInfo = this.stateUpdate.services.updated.get(destination);
12462
+ if (maybeUpdatedServiceInfo !== undefined) {
12463
+ return maybeUpdatedServiceInfo.action.account;
12498
12464
  }
12499
12465
  const maybeService = this.state.getService(destination);
12500
12466
  if (maybeService === null) {
@@ -12503,7 +12469,8 @@ class PartiallyUpdatedState {
12503
12469
  return maybeService.getInfo();
12504
12470
  }
12505
12471
  getStorage(serviceId, rawKey) {
12506
- const item = this.stateUpdate.services.storage.find((x) => x.serviceId === serviceId && x.key.isEqualTo(rawKey));
12472
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
12473
+ const item = storages.find((x) => x.key.isEqualTo(rawKey));
12507
12474
  if (item !== undefined) {
12508
12475
  return item.value;
12509
12476
  }
@@ -12518,10 +12485,11 @@ class PartiallyUpdatedState {
12518
12485
  * the existence in `preimages` map.
12519
12486
  */
12520
12487
  hasPreimage(serviceId, hash) {
12521
- const providedPreimage = this.stateUpdate.services.preimages.find(
12488
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12489
+ const providedPreimage = preimages.find(
12522
12490
  // we ignore the action here, since if there is <any> update on that
12523
12491
  // hash it means it has to exist, right?
12524
- (p) => p.serviceId === serviceId && p.hash.isEqualTo(hash));
12492
+ (p) => p.hash.isEqualTo(hash));
12525
12493
  if (providedPreimage !== undefined) {
12526
12494
  return true;
12527
12495
  }
@@ -12534,7 +12502,8 @@ class PartiallyUpdatedState {
12534
12502
  }
12535
12503
  getPreimage(serviceId, hash) {
12536
12504
  // TODO [ToDr] Should we verify availability here?
12537
- const freshlyProvided = this.stateUpdate.services.preimages.find((x) => x.serviceId === serviceId && x.hash.isEqualTo(hash));
12505
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12506
+ const freshlyProvided = preimages.find((x) => x.hash.isEqualTo(hash));
12538
12507
  if (freshlyProvided !== undefined && freshlyProvided.action.kind === UpdatePreimageKind.Provide) {
12539
12508
  return freshlyProvided.action.preimage.blob;
12540
12509
  }
@@ -12543,10 +12512,11 @@ class PartiallyUpdatedState {
12543
12512
  }
12544
12513
  /** Get status of a preimage of current service taking into account any updates. */
12545
12514
  getLookupHistory(currentTimeslot, serviceId, hash, length) {
12515
+ const preimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12546
12516
  // TODO [ToDr] This is most likely wrong. We may have `provide` and `remove` within
12547
12517
  // the same state update. We should however switch to proper "updated state"
12548
12518
  // representation soon.
12549
- const updatedPreimage = this.stateUpdate.services.preimages.findLast((update) => update.serviceId === serviceId && update.hash.isEqualTo(hash) && BigInt(update.length) === length);
12519
+ const updatedPreimage = preimages.findLast((update) => update.hash.isEqualTo(hash) && BigInt(update.length) === length);
12550
12520
  const stateFallback = () => {
12551
12521
  // fallback to state lookup
12552
12522
  const service = this.state.getService(serviceId);
@@ -12583,14 +12553,15 @@ class PartiallyUpdatedState {
12583
12553
  /* State update functions. */
12584
12554
  updateStorage(serviceId, key, value) {
12585
12555
  const update = value === null
12586
- ? UpdateStorage.remove({ serviceId, key })
12556
+ ? UpdateStorage.remove({ key })
12587
12557
  : UpdateStorage.set({
12588
- serviceId,
12589
12558
  storage: StorageItem.create({ key, value }),
12590
12559
  });
12591
- const index = this.stateUpdate.services.storage.findIndex((x) => x.serviceId === update.serviceId && x.key.isEqualTo(key));
12560
+ const storages = this.stateUpdate.services.storage.get(serviceId) ?? [];
12561
+ const index = storages.findIndex((x) => x.key.isEqualTo(key));
12592
12562
  const count = index === -1 ? 0 : 1;
12593
- this.stateUpdate.services.storage.splice(index, count, update);
12563
+ storages.splice(index, count, update);
12564
+ this.stateUpdate.services.storage.set(serviceId, storages);
12594
12565
  }
12595
12566
  /**
12596
12567
  * Update a preimage.
@@ -12598,8 +12569,10 @@ class PartiallyUpdatedState {
12598
12569
  * Note we store all previous entries as well, since there might be a sequence of:
12599
12570
  * `provide` -> `remove` and both should update the end state somehow.
12600
12571
  */
12601
- updatePreimage(newUpdate) {
12602
- this.stateUpdate.services.preimages.push(newUpdate);
12572
+ updatePreimage(serviceId, newUpdate) {
12573
+ const updatePreimages = this.stateUpdate.services.preimages.get(serviceId) ?? [];
12574
+ updatePreimages.push(newUpdate);
12575
+ this.stateUpdate.services.preimages.set(serviceId, updatePreimages);
12603
12576
  }
12604
12577
  updateServiceStorageUtilisation(serviceId, items, bytes, serviceInfo) {
12605
12578
  check `${items >= 0} storageUtilisationCount has to be a positive number, got: ${items}`;
@@ -12608,11 +12581,11 @@ class PartiallyUpdatedState {
12608
12581
  const overflowBytes = !isU64(bytes);
12609
12582
  // TODO [ToDr] this is not specified in GP, but it seems sensible.
12610
12583
  if (overflowItems || overflowBytes) {
12611
- return Result$1.error(InsufficientFundsError);
12584
+ return Result$1.error(InsufficientFundsError, () => `Storage utilisation overflow: items=${overflowItems}, bytes=${overflowBytes}`);
12612
12585
  }
12613
12586
  const thresholdBalance = ServiceAccountInfo.calculateThresholdBalance(items, bytes, serviceInfo.gratisStorage);
12614
12587
  if (serviceInfo.balance < thresholdBalance) {
12615
- return Result$1.error(InsufficientFundsError);
12588
+ return Result$1.error(InsufficientFundsError, () => `Service balance (${serviceInfo.balance}) below threshold (${thresholdBalance})`);
12616
12589
  }
12617
12590
  // Update service info with new details.
12618
12591
  this.updateServiceInfo(serviceId, ServiceAccountInfo.create({
@@ -12623,20 +12596,23 @@ class PartiallyUpdatedState {
12623
12596
  return Result$1.ok(OK);
12624
12597
  }
12625
12598
  updateServiceInfo(serviceId, newInfo) {
12626
- const idx = this.stateUpdate.services.servicesUpdates.findIndex((x) => x.serviceId === serviceId);
12627
- const toRemove = idx === -1 ? 0 : 1;
12628
- const existingItem = this.stateUpdate.services.servicesUpdates[idx];
12629
- if (existingItem?.action.kind === UpdateServiceKind.Create) {
12630
- this.stateUpdate.services.servicesUpdates.splice(idx, toRemove, UpdateService.create({
12631
- serviceId,
12599
+ const existingUpdate = this.stateUpdate.services.updated.get(serviceId);
12600
+ if (existingUpdate?.action.kind === UpdateServiceKind.Create) {
12601
+ this.stateUpdate.services.updated.set(serviceId, UpdateService.create({
12632
12602
  serviceInfo: newInfo,
12633
- lookupHistory: existingItem.action.lookupHistory,
12603
+ lookupHistory: existingUpdate.action.lookupHistory,
12634
12604
  }));
12635
12605
  return;
12636
12606
  }
12637
- this.stateUpdate.services.servicesUpdates.splice(idx, toRemove, UpdateService.update({
12638
- serviceId,
12607
+ this.stateUpdate.services.updated.set(serviceId, UpdateService.update({
12608
+ serviceInfo: newInfo,
12609
+ }));
12610
+ }
12611
+ createService(serviceId, newInfo, newLookupHistory) {
12612
+ this.stateUpdate.services.created.push(serviceId);
12613
+ this.stateUpdate.services.updated.set(serviceId, UpdateService.create({
12639
12614
  serviceInfo: newInfo,
12615
+ lookupHistory: newLookupHistory,
12640
12616
  }));
12641
12617
  }
12642
12618
  getPrivilegedServices() {
@@ -14257,7 +14233,7 @@ class ReadablePage extends MemoryPage {
14257
14233
  loadInto(result, startIndex, length) {
14258
14234
  const endIndex = startIndex + length;
14259
14235
  if (endIndex > PAGE_SIZE$1) {
14260
- return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1));
14236
+ return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1), () => `Page fault: read beyond page boundary at ${this.start + PAGE_SIZE$1}`);
14261
14237
  }
14262
14238
  const bytes = this.data.subarray(startIndex, endIndex);
14263
14239
  // we zero the bytes, since data might not yet be initialized at `endIndex`.
@@ -14266,7 +14242,7 @@ class ReadablePage extends MemoryPage {
14266
14242
  return Result$1.ok(OK);
14267
14243
  }
14268
14244
  storeFrom(_address, _data) {
14269
- return Result$1.error(PageFault.fromMemoryIndex(this.start, true));
14245
+ return Result$1.error(PageFault.fromMemoryIndex(this.start, true), () => `Page fault: attempted to write to read-only page at ${this.start}`);
14270
14246
  }
14271
14247
  setData(pageIndex, data) {
14272
14248
  this.data.set(data, pageIndex);
@@ -14295,7 +14271,7 @@ class WriteablePage extends MemoryPage {
14295
14271
  loadInto(result, startIndex, length) {
14296
14272
  const endIndex = startIndex + length;
14297
14273
  if (endIndex > PAGE_SIZE$1) {
14298
- return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1));
14274
+ return Result$1.error(PageFault.fromMemoryIndex(this.start + PAGE_SIZE$1), () => `Page fault: read beyond page boundary at ${this.start + PAGE_SIZE$1}`);
14299
14275
  }
14300
14276
  const bytes = this.view.subarray(startIndex, endIndex);
14301
14277
  // we zero the bytes, since the view might not yet be initialized at `endIndex`.
@@ -14365,7 +14341,7 @@ class Memory {
14365
14341
  logger$3.insane `MEM[${address}] <- ${BytesBlob.blobFrom(bytes)}`;
14366
14342
  const pagesResult = this.getPages(address, bytes.length, AccessType.WRITE);
14367
14343
  if (pagesResult.isError) {
14368
- return Result$1.error(pagesResult.error);
14344
+ return Result$1.error(pagesResult.error, pagesResult.details);
14369
14345
  }
14370
14346
  const pages = pagesResult.ok;
14371
14347
  let currentPosition = address;
@@ -14390,14 +14366,14 @@ class Memory {
14390
14366
  const pages = [];
14391
14367
  for (const pageNumber of pageRange) {
14392
14368
  if (pageNumber < RESERVED_NUMBER_OF_PAGES) {
14393
- return Result$1.error(PageFault.fromPageNumber(pageNumber, true));
14369
+ return Result$1.error(PageFault.fromPageNumber(pageNumber, true), () => `Page fault: attempted to access reserved page ${pageNumber}`);
14394
14370
  }
14395
14371
  const page = this.memory.get(pageNumber);
14396
14372
  if (page === undefined) {
14397
- return Result$1.error(PageFault.fromPageNumber(pageNumber));
14373
+ return Result$1.error(PageFault.fromPageNumber(pageNumber), () => `Page fault: page ${pageNumber} not allocated`);
14398
14374
  }
14399
14375
  if (accessType === AccessType.WRITE && !page.isWriteable()) {
14400
- return Result$1.error(PageFault.fromPageNumber(pageNumber, true));
14376
+ return Result$1.error(PageFault.fromPageNumber(pageNumber, true), () => `Page fault: attempted to write to read-only page ${pageNumber}`);
14401
14377
  }
14402
14378
  pages.push(page);
14403
14379
  }
@@ -14415,7 +14391,7 @@ class Memory {
14415
14391
  }
14416
14392
  const pagesResult = this.getPages(startAddress, result.length, AccessType.READ);
14417
14393
  if (pagesResult.isError) {
14418
- return Result$1.error(pagesResult.error);
14394
+ return Result$1.error(pagesResult.error, pagesResult.details);
14419
14395
  }
14420
14396
  const pages = pagesResult.ok;
14421
14397
  let currentPosition = startAddress;
@@ -16220,7 +16196,7 @@ class ProgramDecoder {
16220
16196
  }
16221
16197
  catch (e) {
16222
16198
  logger$2.error `Invalid program: ${e}`;
16223
- return Result$1.error(ProgramDecoderError.InvalidProgramError);
16199
+ return Result$1.error(ProgramDecoderError.InvalidProgramError, () => `Program decoder error: ${e}`);
16224
16200
  }
16225
16201
  }
16226
16202
  }
@@ -16501,7 +16477,7 @@ class HostCallMemory {
16501
16477
  return Result$1.ok(OK);
16502
16478
  }
16503
16479
  if (address + tryAsU64(bytes.length) > MEMORY_SIZE) {
16504
- return Result$1.error(new OutOfBounds());
16480
+ return Result$1.error(new OutOfBounds(), () => `Memory access out of bounds: address ${address} + length ${bytes.length} exceeds memory size`);
16505
16481
  }
16506
16482
  return this.memory.storeFrom(tryAsMemoryIndex(Number(address)), bytes);
16507
16483
  }
@@ -16510,7 +16486,7 @@ class HostCallMemory {
16510
16486
  return Result$1.ok(OK);
16511
16487
  }
16512
16488
  if (startAddress + tryAsU64(result.length) > MEMORY_SIZE) {
16513
- return Result$1.error(new OutOfBounds());
16489
+ return Result$1.error(new OutOfBounds(), () => `Memory access out of bounds: address ${startAddress} + length ${result.length} exceeds memory size`);
16514
16490
  }
16515
16491
  return this.memory.loadInto(result, tryAsMemoryIndex(Number(startAddress)));
16516
16492
  }
@@ -17601,32 +17577,33 @@ class Preimages {
17601
17577
  }
17602
17578
  if (prevPreimage.requester > currPreimage.requester ||
17603
17579
  currPreimage.blob.compare(prevPreimage.blob).isLessOrEqual()) {
17604
- return Result$1.error(PreimagesErrorCode.PreimagesNotSortedUnique);
17580
+ return Result$1.error(PreimagesErrorCode.PreimagesNotSortedUnique, () => `Preimages not sorted/unique at index ${i}`);
17605
17581
  }
17606
17582
  }
17607
17583
  const { preimages, slot } = input;
17608
- const pendingChanges = [];
17584
+ const pendingChanges = new Map();
17609
17585
  // select preimages for integration
17610
17586
  for (const preimage of preimages) {
17611
17587
  const { requester, blob } = preimage;
17612
17588
  const hash = this.blake2b.hashBytes(blob).asOpaque();
17613
17589
  const service = this.state.getService(requester);
17614
17590
  if (service === null) {
17615
- return Result$1.error(PreimagesErrorCode.AccountNotFound);
17591
+ return Result$1.error(PreimagesErrorCode.AccountNotFound, () => `Service not found: ${requester}`);
17616
17592
  }
17617
17593
  const hasPreimage = service.hasPreimage(hash);
17618
17594
  const slots = service.getLookupHistory(hash, tryAsU32(blob.length));
17619
17595
  // https://graypaper.fluffylabs.dev/#/5f542d7/181800181900
17620
17596
  // https://graypaper.fluffylabs.dev/#/5f542d7/116f0011a500
17621
17597
  if (hasPreimage || slots === null || !LookupHistoryItem.isRequested(slots)) {
17622
- return Result$1.error(PreimagesErrorCode.PreimageUnneeded);
17598
+ return Result$1.error(PreimagesErrorCode.PreimageUnneeded, () => `Preimage unneeded: requester=${requester}, hash=${hash}, hasPreimage=${hasPreimage}, isRequested=${slots !== null && LookupHistoryItem.isRequested(slots)}`);
17623
17599
  }
17624
17600
  // https://graypaper.fluffylabs.dev/#/5f542d7/18c00018f300
17625
- pendingChanges.push(UpdatePreimage.provide({
17626
- serviceId: requester,
17601
+ const updates = pendingChanges.get(requester) ?? [];
17602
+ updates.push(UpdatePreimage.provide({
17627
17603
  preimage: PreimageItem.create({ hash, blob }),
17628
17604
  slot,
17629
17605
  }));
17606
+ pendingChanges.set(requester, updates);
17630
17607
  }
17631
17608
  return Result$1.ok({
17632
17609
  preimages: pendingChanges,