@typeberry/convert 0.1.3-135961b → 0.1.3-3f7b9cf

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 (3) hide show
  1. package/index.js +55 -3
  2. package/index.js.map +1 -1
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -20785,6 +20785,15 @@ var ResultValues;
20785
20785
  ResultValues[ResultValues["Ok"] = 0] = "Ok";
20786
20786
  ResultValues[ResultValues["Error"] = 1] = "Error";
20787
20787
  })(ResultValues || (ResultValues = {}));
20788
+ /**
20789
+ * Getting a ring commitment is pretty expensive (hundreds of ms),
20790
+ * yet the validators do not always change.
20791
+ * For current benchmarks, we get a huge hit every epoch, hence
20792
+ * to overcome that we cache the results of getting ring commitment.
20793
+ * Note we can also tentatively populate this cache, before we even
20794
+ * reach the epoch change block.
20795
+ */
20796
+ const ringCommitmentCache = [];
20788
20797
  // TODO [ToDr] We export the entire object to allow mocking in tests.
20789
20798
  // Ideally we would just export functions and figure out how to mock
20790
20799
  // properly in ESM.
@@ -20800,9 +20809,27 @@ async function verifySeal(bandersnatch, authorKey, signature, payload, encodedUn
20800
20809
  }
20801
20810
  return result_Result.ok(bytes_Bytes.fromBlob(sealResult.subarray(1), hash_HASH_SIZE).asOpaque());
20802
20811
  }
20803
- async function getRingCommitment(bandersnatch, validators) {
20804
- const keys = bytes_BytesBlob.blobFromParts(validators.map((x) => x.raw)).raw;
20805
- const commitmentResult = await bandersnatch.getRingCommitment(keys);
20812
+ function getRingCommitment(bandersnatch, validators) {
20813
+ const keys = bytes_BytesBlob.blobFromParts(validators.map((x) => x.raw));
20814
+ // We currently compare the large bytes blob, but the number of entries in the cache
20815
+ // must be low. If the cache ever grows larger, we should rather consider hashing the keys.
20816
+ const MAX_CACHE_ENTRIES = 3;
20817
+ const cacheEntry = ringCommitmentCache.find((v) => v.keys.isEqualTo(keys));
20818
+ if (cacheEntry !== undefined) {
20819
+ return cacheEntry.value;
20820
+ }
20821
+ const value = getRingCommitmentNoCache(bandersnatch, keys);
20822
+ ringCommitmentCache.push({
20823
+ keys,
20824
+ value,
20825
+ });
20826
+ if (ringCommitmentCache.length > MAX_CACHE_ENTRIES) {
20827
+ ringCommitmentCache.shift();
20828
+ }
20829
+ return value;
20830
+ }
20831
+ async function getRingCommitmentNoCache(bandersnatch, keys) {
20832
+ const commitmentResult = await bandersnatch.getRingCommitment(keys.raw);
20806
20833
  if (commitmentResult[RESULT_INDEX] === ResultValues.Error) {
20807
20834
  return result_Result.error(null);
20808
20835
  }
@@ -20928,6 +20955,18 @@ class safrole_Safrole {
20928
20955
  }
20929
20956
  return FixedSizeArray.new([newRandomnessAcc, ...rest], 4);
20930
20957
  }
20958
+ /**
20959
+ * Pre-populate cache for validator keys, and especially the ring commitment.
20960
+ *
20961
+ * NOTE the function is still doing quite some work, so it should only be used
20962
+ * once per epoch. The optimisation relies on the fact that the `bandersnatch.getRingCommitment`
20963
+ * call will be cached.
20964
+ */
20965
+ async prepareValidatorKeysForNextEpoch(postOffenders) {
20966
+ const stateEpoch = Math.floor(this.state.timeslot / this.chainSpec.epochLength);
20967
+ const nextEpochStart = (stateEpoch + 1) * this.chainSpec.epochLength;
20968
+ return await this.getValidatorKeys(tryAsTimeSlot(nextEpochStart), postOffenders);
20969
+ }
20931
20970
  async getValidatorKeys(timeslot, postOffenders) {
20932
20971
  /**
20933
20972
  * Epoch is not changed so the previous state is returned
@@ -26225,6 +26264,7 @@ class chain_stf_OnChain {
26225
26264
  authorization;
26226
26265
  // chapter 13: https://graypaper.fluffylabs.dev/#/68eaa1f/18b60118b601?v=0.6.4
26227
26266
  statistics;
26267
+ isReadyForNextEpoch = Promise.resolve(false);
26228
26268
  constructor(chainSpec, state, blocks, hasher) {
26229
26269
  this.chainSpec = chainSpec;
26230
26270
  this.state = state;
@@ -26243,6 +26283,14 @@ class chain_stf_OnChain {
26243
26283
  this.preimages = new Preimages(state);
26244
26284
  this.authorization = new Authorization(chainSpec, state);
26245
26285
  }
26286
+ /** Pre-populate things worth caching for the next epoch. */
26287
+ async prepareForNextEpoch() {
26288
+ if (await this.isReadyForNextEpoch) {
26289
+ return;
26290
+ }
26291
+ const ready = this.safrole.prepareValidatorKeysForNextEpoch(this.state.disputesRecords.punishSet);
26292
+ this.isReadyForNextEpoch = ready.then((_) => true);
26293
+ }
26246
26294
  async verifySeal(timeSlot, block) {
26247
26295
  const sealState = this.safrole.getSafroleSealState(timeSlot);
26248
26296
  return await this.safroleSeal.verifyHeaderSeal(block.header.view(), sealState);
@@ -26251,6 +26299,10 @@ class chain_stf_OnChain {
26251
26299
  const headerView = block.header.view();
26252
26300
  const header = block.header.materialize();
26253
26301
  const timeSlot = header.timeSlotIndex;
26302
+ // reset the epoch cache state
26303
+ if (headerView.epochMarker.view() !== null) {
26304
+ this.isReadyForNextEpoch = Promise.resolve(false);
26305
+ }
26254
26306
  // safrole seal
26255
26307
  let newEntropyHash;
26256
26308
  if (omitSealVerification) {