@typeberry/convert 0.1.3-707962d → 0.1.3-d3752d8

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 +205 -91
  2. package/index.js.map +1 -1
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -5264,6 +5264,20 @@ const result_Result = {
5264
5264
  },
5265
5265
  };
5266
5266
 
5267
+ ;// CONCATENATED MODULE: ./packages/core/utils/safe-alloc-uint8array.ts
5268
+ // about 2GB, the maximum ArrayBuffer length on Chrome confirmed by several sources:
5269
+ // - https://issues.chromium.org/issues/40055619
5270
+ // - https://stackoverflow.com/a/72124984
5271
+ // - https://onnxruntime.ai/docs/tutorials/web/large-models.html#maximum-size-of-arraybuffer
5272
+ const MAX_LENGTH = 2145386496;
5273
+ function safe_alloc_uint8array_safeAllocUint8Array(length) {
5274
+ if (length > MAX_LENGTH) {
5275
+ // biome-ignore lint/suspicious/noConsole: can't have a dependency on logger here
5276
+ console.warn(`Trying to allocate ${length} bytes, which is greater than the maximum of ${MAX_LENGTH}.`);
5277
+ }
5278
+ return new Uint8Array(Math.min(MAX_LENGTH, length));
5279
+ }
5280
+
5267
5281
  ;// CONCATENATED MODULE: external "node:assert"
5268
5282
  const external_node_assert_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:assert");
5269
5283
  ;// CONCATENATED MODULE: ./packages/core/utils/test.ts
@@ -5507,6 +5521,7 @@ function isResult(x) {
5507
5521
 
5508
5522
 
5509
5523
 
5524
+
5510
5525
  // EXTERNAL MODULE: ./node_modules/minimist/index.js
5511
5526
  var minimist = __nccwpck_require__(595);
5512
5527
  var minimist_default = /*#__PURE__*/__nccwpck_require__.n(minimist);
@@ -5533,7 +5548,7 @@ class bitvec_BitVec {
5533
5548
  * Create new [`BitVec`] with all values set to `false`.
5534
5549
  */
5535
5550
  static empty(bitLength) {
5536
- const data = new Uint8Array(Math.ceil(bitLength / 8));
5551
+ const data = safe_alloc_uint8array_safeAllocUint8Array(Math.ceil(bitLength / 8));
5537
5552
  return new bitvec_BitVec(data, bitLength);
5538
5553
  }
5539
5554
  byteLength;
@@ -5733,7 +5748,7 @@ class bytes_BytesBlob {
5733
5748
  static blobFromParts(v, ...rest) {
5734
5749
  const vArr = v instanceof Uint8Array ? [v] : v;
5735
5750
  const totalLength = vArr.reduce((a, v) => a + v.length, 0) + rest.reduce((a, v) => a + v.length, 0);
5736
- const buffer = new Uint8Array(totalLength);
5751
+ const buffer = safe_alloc_uint8array_safeAllocUint8Array(totalLength);
5737
5752
  let offset = 0;
5738
5753
  for (const r of vArr) {
5739
5754
  buffer.set(r, offset);
@@ -5806,7 +5821,7 @@ class bytes_Bytes extends bytes_BytesBlob {
5806
5821
  }
5807
5822
  /** Create an empty [`Bytes<X>`] of given length. */
5808
5823
  static zero(len) {
5809
- return new bytes_Bytes(new Uint8Array(len), len);
5824
+ return new bytes_Bytes(safe_alloc_uint8array_safeAllocUint8Array(len), len);
5810
5825
  }
5811
5826
  // TODO [ToDr] `fill` should have the argments swapped to align with the rest.
5812
5827
  /** Create a [`Bytes<X>`] with all bytes filled with given input number. */
@@ -6476,7 +6491,7 @@ function addSizeHints(a, b) {
6476
6491
  };
6477
6492
  }
6478
6493
  const DEFAULT_START_LENGTH = 512; // 512B
6479
- const MAX_LENGTH = 10 * 1024 * 1024; // 10MB
6494
+ const encoder_MAX_LENGTH = 10 * 1024 * 1024; // 10MB
6480
6495
  /**
6481
6496
  * JAM encoder.
6482
6497
  */
@@ -6492,7 +6507,7 @@ class encoder_Encoder {
6492
6507
  return new encoder_Encoder(options.destination);
6493
6508
  }
6494
6509
  const startLength = options?.expectedLength ?? DEFAULT_START_LENGTH;
6495
- const buffer = new ArrayBuffer(Math.min(MAX_LENGTH, startLength), { maxByteLength: MAX_LENGTH });
6510
+ const buffer = new ArrayBuffer(Math.min(encoder_MAX_LENGTH, startLength), { maxByteLength: encoder_MAX_LENGTH });
6496
6511
  const destination = new Uint8Array(buffer);
6497
6512
  return new encoder_Encoder(destination, buffer);
6498
6513
  }
@@ -6825,11 +6840,11 @@ class encoder_Encoder {
6825
6840
  ensureBigEnough(length, options = { silent: false }) {
6826
6841
  debug_check `${length >= 0} Negative length given`;
6827
6842
  const newLength = this.offset + length;
6828
- if (newLength > MAX_LENGTH) {
6843
+ if (newLength > encoder_MAX_LENGTH) {
6829
6844
  if (options.silent) {
6830
6845
  return;
6831
6846
  }
6832
- throw new Error(`The encoded size would reach the maximum of ${MAX_LENGTH}.`);
6847
+ throw new Error(`The encoded size would reach the maximum of ${encoder_MAX_LENGTH}.`);
6833
6848
  }
6834
6849
  if (newLength > this.destination.length) {
6835
6850
  // we can try to resize the underlying buffer
@@ -6837,7 +6852,7 @@ class encoder_Encoder {
6837
6852
  // make sure we at least double the size of the buffer every time.
6838
6853
  const minExtend = Math.max(newLength, this.buffer.byteLength << 1);
6839
6854
  // but we must never exceed the max length.
6840
- this.buffer.resize(Math.min(MAX_LENGTH, minExtend));
6855
+ this.buffer.resize(Math.min(encoder_MAX_LENGTH, minExtend));
6841
6856
  }
6842
6857
  // and then check again
6843
6858
  if (newLength > this.destination.length) {
@@ -8169,7 +8184,7 @@ async function verify(input) {
8169
8184
  return Promise.resolve([]);
8170
8185
  }
8171
8186
  const dataLength = input.reduce((acc, { message, key, signature }) => acc + key.length + signature.length + message.length + 1, 0);
8172
- const data = new Uint8Array(dataLength);
8187
+ const data = safeAllocUint8Array(dataLength);
8173
8188
  let offset = 0;
8174
8189
  for (const { key, message, signature } of input) {
8175
8190
  data.set(key.raw, offset);
@@ -8256,7 +8271,7 @@ class allocator_SimpleAllocator {
8256
8271
  /** An allocator that works by allocating larger (continuous) pages of memory. */
8257
8272
  class PageAllocator {
8258
8273
  hashesPerPage;
8259
- page = new Uint8Array(0);
8274
+ page = safeAllocUint8Array(0);
8260
8275
  currentHash = 0;
8261
8276
  // TODO [ToDr] Benchmark the performance!
8262
8277
  constructor(hashesPerPage) {
@@ -8267,7 +8282,7 @@ class PageAllocator {
8267
8282
  resetPage() {
8268
8283
  const pageSizeBytes = this.hashesPerPage * HASH_SIZE;
8269
8284
  this.currentHash = 0;
8270
- this.page = new Uint8Array(pageSizeBytes);
8285
+ this.page = safeAllocUint8Array(pageSizeBytes);
8271
8286
  }
8272
8287
  emptyHash() {
8273
8288
  const startIdx = this.currentHash * HASH_SIZE;
@@ -12190,6 +12205,10 @@ class disputes_DisputesRecords {
12190
12205
  static create({ goodSet, badSet, wonkySet, punishSet }) {
12191
12206
  return new disputes_DisputesRecords(goodSet, badSet, wonkySet, punishSet);
12192
12207
  }
12208
+ goodSetDict;
12209
+ badSetDict;
12210
+ wonkySetDict;
12211
+ punishSetDict;
12193
12212
  constructor(
12194
12213
  /** `goodSet`: all work-reports hashes which were judged to be correct */
12195
12214
  goodSet,
@@ -12203,6 +12222,18 @@ class disputes_DisputesRecords {
12203
12222
  this.badSet = badSet;
12204
12223
  this.wonkySet = wonkySet;
12205
12224
  this.punishSet = punishSet;
12225
+ this.goodSetDict = hash_set_HashSet.from(goodSet.array);
12226
+ this.badSetDict = hash_set_HashSet.from(badSet.array);
12227
+ this.wonkySetDict = hash_set_HashSet.from(wonkySet.array);
12228
+ this.punishSetDict = hash_set_HashSet.from(punishSet.array);
12229
+ }
12230
+ asDictionaries() {
12231
+ return {
12232
+ goodSet: this.goodSetDict,
12233
+ badSet: this.badSetDict,
12234
+ wonkySet: this.wonkySetDict,
12235
+ punishSet: this.punishSetDict,
12236
+ };
12206
12237
  }
12207
12238
  static fromSortedArrays({ goodSet, badSet, wonkySet, punishSet, }) {
12208
12239
  return new disputes_DisputesRecords(sorted_set_SortedSet.fromSortedArray(disputes_hashComparator, goodSet), sorted_set_SortedSet.fromSortedArray(disputes_hashComparator, badSet), sorted_set_SortedSet.fromSortedArray(disputes_hashComparator, wonkySet), sorted_set_SortedSet.fromSortedArray(disputes_hashComparator, punishSet));
@@ -14645,7 +14676,7 @@ class SerializedService {
14645
14676
  getStorage(rawKey) {
14646
14677
  if (compatibility_Compatibility.isLessThan(compatibility_GpVersion.V0_6_7)) {
14647
14678
  const SERVICE_ID_BYTES = 4;
14648
- const serviceIdAndKey = new Uint8Array(SERVICE_ID_BYTES + rawKey.length);
14679
+ const serviceIdAndKey = safe_alloc_uint8array_safeAllocUint8Array(SERVICE_ID_BYTES + rawKey.length);
14649
14680
  serviceIdAndKey.set(numbers_u32AsLeBytes(this.serviceId));
14650
14681
  serviceIdAndKey.set(rawKey.raw, SERVICE_ID_BYTES);
14651
14682
  const key = opaque_asOpaqueType(bytes_BytesBlob.blobFrom(hashBytes(serviceIdAndKey).raw));
@@ -14736,7 +14767,7 @@ class nodes_TrieNode {
14736
14767
  raw;
14737
14768
  constructor(
14738
14769
  /** Exactly 512 bits / 64 bytes */
14739
- raw = new Uint8Array(nodes_TRIE_NODE_BYTES)) {
14770
+ raw = safe_alloc_uint8array_safeAllocUint8Array(nodes_TRIE_NODE_BYTES)) {
14740
14771
  this.raw = raw;
14741
14772
  }
14742
14773
  /** Returns the type of the node */
@@ -16171,7 +16202,7 @@ class registers_Registers {
16171
16202
  bytes;
16172
16203
  asSigned;
16173
16204
  asUnsigned;
16174
- constructor(bytes = new Uint8Array(registers_NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
16205
+ constructor(bytes = safeAllocUint8Array(registers_NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
16175
16206
  this.bytes = bytes;
16176
16207
  check `${bytes.length === registers_NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
16177
16208
  this.asSigned = new BigInt64Array(bytes.buffer, bytes.byteOffset);
@@ -16247,10 +16278,16 @@ function registers_signExtend32To64(value) {
16247
16278
 
16248
16279
  /** Attempt to convert a number into `HostCallIndex`. */
16249
16280
  const host_call_handler_tryAsHostCallIndex = (v) => opaque_asOpaqueType(numbers_tryAsU32(v));
16281
+ /**
16282
+ * Host-call exit reason.
16283
+ *
16284
+ * https://graypaper.fluffylabs.dev/#/ab2cdbd/24a30124a501?v=0.7.2
16285
+ */
16250
16286
  var host_call_handler_PvmExecution;
16251
16287
  (function (PvmExecution) {
16252
16288
  PvmExecution[PvmExecution["Halt"] = 0] = "Halt";
16253
16289
  PvmExecution[PvmExecution["Panic"] = 1] = "Panic";
16290
+ PvmExecution[PvmExecution["OOG"] = 2] = "OOG";
16254
16291
  })(host_call_handler_PvmExecution || (host_call_handler_PvmExecution = {}));
16255
16292
  /** A utility function to easily trace a bunch of registers. */
16256
16293
  function host_call_handler_traceRegisters(...regs) {
@@ -16328,7 +16365,7 @@ class mask_Mask {
16328
16365
  return Math.min(this.lookupTableForward[index] ?? 0, MAX_INSTRUCTION_DISTANCE);
16329
16366
  }
16330
16367
  buildLookupTableForward(mask) {
16331
- const table = new Uint8Array(mask.bitLength);
16368
+ const table = safeAllocUint8Array(mask.bitLength);
16332
16369
  let lastInstructionOffset = 0;
16333
16370
  for (let i = mask.bitLength - 1; i >= 0; i--) {
16334
16371
  if (mask.isSet(i)) {
@@ -19839,7 +19876,7 @@ class host_calls_HostCalls {
19839
19876
  const regs = pvmInstance.getRegisters();
19840
19877
  const maybeAddress = regs.getLowerU32(7);
19841
19878
  const maybeLength = regs.getLowerU32(8);
19842
- const result = new Uint8Array(maybeLength);
19879
+ const result = safeAllocUint8Array(maybeLength);
19843
19880
  const startAddress = tryAsMemoryIndex(maybeAddress);
19844
19881
  const loadResult = memory.loadInto(result, startAddress);
19845
19882
  if (loadResult.isError) {
@@ -19867,8 +19904,9 @@ class host_calls_HostCalls {
19867
19904
  const index = tryAsHostCallIndex(hostCallIndex);
19868
19905
  const hostCall = this.hostCalls.get(index);
19869
19906
  const gasBefore = gas.get();
19870
- const gasCost = typeof hostCall.gasCost === "number" ? hostCall.gasCost : hostCall.gasCost(regs);
19871
- const underflow = gas.sub(gasCost);
19907
+ // NOTE: `basicGasCost(regs)` function is for compatibility reasons: pre GP 0.7.2
19908
+ const basicGasCost = typeof hostCall.basicGasCost === "number" ? hostCall.basicGasCost : hostCall.basicGasCost(regs);
19909
+ const underflow = gas.sub(basicGasCost);
19872
19910
  const pcLog = `[PC: ${pvmInstance.getPC()}]`;
19873
19911
  if (underflow) {
19874
19912
  this.hostCalls.traceHostCall(`${pcLog} OOG`, index, hostCall, regs, gas.get());
@@ -19885,6 +19923,10 @@ class host_calls_HostCalls {
19885
19923
  status = Status.PANIC;
19886
19924
  return this.getReturnValue(status, pvmInstance);
19887
19925
  }
19926
+ if (result === PvmExecution.OOG) {
19927
+ status = Status.OOG;
19928
+ return this.getReturnValue(status, pvmInstance);
19929
+ }
19888
19930
  if (result === undefined) {
19889
19931
  pvmInstance.runProgram();
19890
19932
  status = pvmInstance.getStatus();
@@ -19943,7 +19985,7 @@ class host_calls_manager_HostCallsManager {
19943
19985
  }
19944
19986
  class NoopMissing {
19945
19987
  index = tryAsHostCallIndex(2 ** 32 - 1);
19946
- gasCost = tryAsSmallGas(0);
19988
+ basicGasCost = tryAsSmallGas(0);
19947
19989
  currentServiceId = tryAsU32(0);
19948
19990
  tracedRegisters = [];
19949
19991
  async execute() {
@@ -20057,7 +20099,7 @@ function utils_clampU64ToU32(value) {
20057
20099
 
20058
20100
  class missing_Missing {
20059
20101
  index = tryAsHostCallIndex(2 ** 32 - 1);
20060
- gasCost = tryAsSmallGas(10);
20102
+ basicGasCost = tryAsSmallGas(10);
20061
20103
  currentServiceId = CURRENT_SERVICE_ID;
20062
20104
  tracedRegisters = traceRegisters(7);
20063
20105
  execute(_gas, regs, _memory) {
@@ -20434,7 +20476,7 @@ class disputes_Disputes {
20434
20476
  const { key, workReportHash } = disputes.culprits[i];
20435
20477
  // check if some offenders weren't reported earlier
20436
20478
  // https://graypaper.fluffylabs.dev/#/579bd12/125501125501
20437
- const isInPunishSet = this.state.disputesRecords.punishSet.findExact(key) !== undefined;
20479
+ const isInPunishSet = this.state.disputesRecords.asDictionaries().punishSet.has(key);
20438
20480
  if (isInPunishSet) {
20439
20481
  return Result.error(DisputesErrorCode.OffenderAlreadyReported);
20440
20482
  }
@@ -20445,8 +20487,8 @@ class disputes_Disputes {
20445
20487
  }
20446
20488
  // verify if the culprit will be in new bad set
20447
20489
  // https://graypaper.fluffylabs.dev/#/579bd12/124601124601
20448
- const isInNewBadSet = newItems.toAddToBadSet.findExact(workReportHash);
20449
- if (isInNewBadSet === undefined) {
20490
+ const isInNewBadSet = newItems.asDictionaries().badSet.has(workReportHash);
20491
+ if (!isInNewBadSet) {
20450
20492
  return Result.error(DisputesErrorCode.CulpritsVerdictNotBad);
20451
20493
  }
20452
20494
  // verify culprit signature
@@ -20469,7 +20511,7 @@ class disputes_Disputes {
20469
20511
  const { key, workReportHash, wasConsideredValid } = disputes.faults[i];
20470
20512
  // check if some offenders weren't reported earlier
20471
20513
  // https://graypaper.fluffylabs.dev/#/579bd12/12a20112a201
20472
- const isInPunishSet = this.state.disputesRecords.punishSet.findExact(key) !== undefined;
20514
+ const isInPunishSet = this.state.disputesRecords.asDictionaries().punishSet.has(key);
20473
20515
  if (isInPunishSet) {
20474
20516
  return Result.error(DisputesErrorCode.OffenderAlreadyReported);
20475
20517
  }
@@ -20484,9 +20526,10 @@ class disputes_Disputes {
20484
20526
  // but it does not pass the tests
20485
20527
  // https://graypaper.fluffylabs.dev/#/579bd12/128a01129601
20486
20528
  if (wasConsideredValid) {
20487
- const isInNewGoodSet = newItems.toAddToGoodSet.findExact(workReportHash);
20488
- const isInNewBadSet = newItems.toAddToBadSet.findExact(workReportHash);
20489
- if (isInNewGoodSet !== undefined || isInNewBadSet === undefined) {
20529
+ const { goodSet, badSet } = newItems.asDictionaries();
20530
+ const isInNewGoodSet = goodSet.has(workReportHash);
20531
+ const isInNewBadSet = badSet.has(workReportHash);
20532
+ if (isInNewGoodSet || !isInNewBadSet) {
20490
20533
  return Result.error(DisputesErrorCode.FaultVerdictWrong);
20491
20534
  }
20492
20535
  }
@@ -20539,10 +20582,11 @@ class disputes_Disputes {
20539
20582
  for (const verdict of disputes.verdicts) {
20540
20583
  // current verdicts should not be reported earlier
20541
20584
  // https://graypaper.fluffylabs.dev/#/579bd12/122202122202
20542
- const isInGoodSet = this.state.disputesRecords.goodSet.findExact(verdict.workReportHash);
20543
- const isInBadSet = this.state.disputesRecords.badSet.findExact(verdict.workReportHash);
20544
- const isInWonkySet = this.state.disputesRecords.wonkySet.findExact(verdict.workReportHash);
20545
- if (isInGoodSet !== undefined || isInBadSet !== undefined || isInWonkySet !== undefined) {
20585
+ const { goodSet, badSet, wonkySet } = this.state.disputesRecords.asDictionaries();
20586
+ const isInGoodSet = goodSet.has(verdict.workReportHash);
20587
+ const isInBadSet = badSet.has(verdict.workReportHash);
20588
+ const isInWonkySet = wonkySet.has(verdict.workReportHash);
20589
+ if (isInGoodSet || isInBadSet || isInWonkySet) {
20546
20590
  return Result.error(DisputesErrorCode.AlreadyJudged);
20547
20591
  }
20548
20592
  }
@@ -20612,11 +20656,12 @@ class disputes_Disputes {
20612
20656
  toAddToWonkySet.push(r);
20613
20657
  }
20614
20658
  }
20615
- return {
20616
- toAddToGoodSet: SortedSet.fromArrayUnique(hashComparator, toAddToGoodSet),
20617
- toAddToBadSet: SortedSet.fromArrayUnique(hashComparator, toAddToBadSet),
20618
- toAddToWonkySet: SortedSet.fromArrayUnique(hashComparator, toAddToWonkySet),
20619
- };
20659
+ return DisputesRecords.create({
20660
+ goodSet: SortedSet.fromArrayUnique(hashComparator, toAddToGoodSet),
20661
+ badSet: SortedSet.fromArrayUnique(hashComparator, toAddToBadSet),
20662
+ wonkySet: SortedSet.fromArrayUnique(hashComparator, toAddToWonkySet),
20663
+ punishSet: SortedSet.fromArray(hashComparator, []),
20664
+ });
20620
20665
  }
20621
20666
  getClearedCoreAssignment(v) {
20622
20667
  /**
@@ -20651,9 +20696,9 @@ class disputes_Disputes {
20651
20696
  const toAddToPunishSet = SortedArray.fromArray(hashComparator, Array.from(offenders));
20652
20697
  return DisputesRecords.create({
20653
20698
  // https://graypaper.fluffylabs.dev/#/579bd12/12690312bc03
20654
- goodSet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.goodSet, newItems.toAddToGoodSet),
20655
- badSet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.badSet, newItems.toAddToBadSet),
20656
- wonkySet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.wonkySet, newItems.toAddToWonkySet),
20699
+ goodSet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.goodSet, newItems.goodSet),
20700
+ badSet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.badSet, newItems.badSet),
20701
+ wonkySet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.wonkySet, newItems.wonkySet),
20657
20702
  punishSet: SortedSet.fromTwoSortedCollections(this.state.disputesRecords.punishSet, toAddToPunishSet),
20658
20703
  });
20659
20704
  }
@@ -20755,6 +20800,15 @@ var ResultValues;
20755
20800
  ResultValues[ResultValues["Ok"] = 0] = "Ok";
20756
20801
  ResultValues[ResultValues["Error"] = 1] = "Error";
20757
20802
  })(ResultValues || (ResultValues = {}));
20803
+ /**
20804
+ * Getting a ring commitment is pretty expensive (hundreds of ms),
20805
+ * yet the validators do not always change.
20806
+ * For current benchmarks, we get a huge hit every epoch, hence
20807
+ * to overcome that we cache the results of getting ring commitment.
20808
+ * Note we can also tentatively populate this cache, before we even
20809
+ * reach the epoch change block.
20810
+ */
20811
+ const ringCommitmentCache = [];
20758
20812
  // TODO [ToDr] We export the entire object to allow mocking in tests.
20759
20813
  // Ideally we would just export functions and figure out how to mock
20760
20814
  // properly in ESM.
@@ -20770,9 +20824,27 @@ async function verifySeal(bandersnatch, authorKey, signature, payload, encodedUn
20770
20824
  }
20771
20825
  return result_Result.ok(bytes_Bytes.fromBlob(sealResult.subarray(1), hash_HASH_SIZE).asOpaque());
20772
20826
  }
20773
- async function getRingCommitment(bandersnatch, validators) {
20774
- const keys = bytes_BytesBlob.blobFromParts(validators.map((x) => x.raw)).raw;
20775
- const commitmentResult = await bandersnatch.getRingCommitment(keys);
20827
+ function getRingCommitment(bandersnatch, validators) {
20828
+ const keys = bytes_BytesBlob.blobFromParts(validators.map((x) => x.raw));
20829
+ // We currently compare the large bytes blob, but the number of entries in the cache
20830
+ // must be low. If the cache ever grows larger, we should rather consider hashing the keys.
20831
+ const MAX_CACHE_ENTRIES = 3;
20832
+ const cacheEntry = ringCommitmentCache.find((v) => v.keys.isEqualTo(keys));
20833
+ if (cacheEntry !== undefined) {
20834
+ return cacheEntry.value;
20835
+ }
20836
+ const value = getRingCommitmentNoCache(bandersnatch, keys);
20837
+ ringCommitmentCache.push({
20838
+ keys,
20839
+ value,
20840
+ });
20841
+ if (ringCommitmentCache.length > MAX_CACHE_ENTRIES) {
20842
+ ringCommitmentCache.shift();
20843
+ }
20844
+ return value;
20845
+ }
20846
+ async function getRingCommitmentNoCache(bandersnatch, keys) {
20847
+ const commitmentResult = await bandersnatch.getRingCommitment(keys.raw);
20776
20848
  if (commitmentResult[RESULT_INDEX] === ResultValues.Error) {
20777
20849
  return result_Result.error(null);
20778
20850
  }
@@ -20898,6 +20970,18 @@ class safrole_Safrole {
20898
20970
  }
20899
20971
  return FixedSizeArray.new([newRandomnessAcc, ...rest], 4);
20900
20972
  }
20973
+ /**
20974
+ * Pre-populate cache for validator keys, and especially the ring commitment.
20975
+ *
20976
+ * NOTE the function is still doing quite some work, so it should only be used
20977
+ * once per epoch. The optimisation relies on the fact that the `bandersnatch.getRingCommitment`
20978
+ * call will be cached.
20979
+ */
20980
+ async prepareValidatorKeysForNextEpoch(postOffenders) {
20981
+ const stateEpoch = Math.floor(this.state.timeslot / this.chainSpec.epochLength);
20982
+ const nextEpochStart = (stateEpoch + 1) * this.chainSpec.epochLength;
20983
+ return await this.getValidatorKeys(tryAsTimeSlot(nextEpochStart), postOffenders);
20984
+ }
20901
20985
  async getValidatorKeys(timeslot, postOffenders) {
20902
20986
  /**
20903
20987
  * Epoch is not changed so the previous state is returned
@@ -23037,7 +23121,7 @@ class Assign {
23037
23121
  partialState;
23038
23122
  chainSpec;
23039
23123
  index = host_call_handler_tryAsHostCallIndex(15);
23040
- gasCost = gas_tryAsSmallGas(10);
23124
+ basicGasCost = gas_tryAsSmallGas(10);
23041
23125
  tracedRegisters = host_call_handler_traceRegisters(IN_OUT_REG, 8);
23042
23126
  constructor(currentServiceId, partialState, chainSpec) {
23043
23127
  this.currentServiceId = currentServiceId;
@@ -23051,7 +23135,7 @@ class Assign {
23051
23135
  const authorizationQueueStart = regs.get(8);
23052
23136
  // a
23053
23137
  const authManager = getServiceId(regs.get(9));
23054
- const res = new Uint8Array(hash_HASH_SIZE * gp_constants_AUTHORIZATION_QUEUE_SIZE);
23138
+ const res = safe_alloc_uint8array_safeAllocUint8Array(hash_HASH_SIZE * gp_constants_AUTHORIZATION_QUEUE_SIZE);
23055
23139
  const memoryReadResult = memory.loadInto(res, authorizationQueueStart);
23056
23140
  // error while reading the memory.
23057
23141
  if (memoryReadResult.isError) {
@@ -23115,7 +23199,7 @@ class Bless {
23115
23199
  partialState;
23116
23200
  chainSpec;
23117
23201
  index = host_call_handler_tryAsHostCallIndex(14);
23118
- gasCost = gas_tryAsSmallGas(10);
23202
+ basicGasCost = gas_tryAsSmallGas(10);
23119
23203
  tracedRegisters = host_call_handler_traceRegisters(bless_IN_OUT_REG, 8, 9, 10, 11);
23120
23204
  constructor(currentServiceId, partialState, chainSpec) {
23121
23205
  this.currentServiceId = currentServiceId;
@@ -23139,7 +23223,7 @@ class Bless {
23139
23223
  * https://graypaper.fluffylabs.dev/#/7e6ff6a/368100368100?v=0.6.7
23140
23224
  */
23141
23225
  const autoAccumulateEntries = [];
23142
- const result = new Uint8Array(tryAsExactBytes(serviceIdAndGasCodec.sizeHint));
23226
+ const result = safe_alloc_uint8array_safeAllocUint8Array(tryAsExactBytes(serviceIdAndGasCodec.sizeHint));
23143
23227
  const decoder = decoder_Decoder.fromBlob(result);
23144
23228
  let memIndex = sourceStart;
23145
23229
  for (let i = 0n; i < numberOfItems; i += 1n) {
@@ -23156,7 +23240,7 @@ class Bless {
23156
23240
  memIndex = numbers_tryAsU64(memIndex + numbers_tryAsU64(decoder.bytesRead()));
23157
23241
  }
23158
23242
  // https://graypaper.fluffylabs.dev/#/7e6ff6a/367200367200?v=0.6.7
23159
- const res = new Uint8Array(tryAsExactBytes(descriptors_codec.u32.sizeHint) * this.chainSpec.coresCount);
23243
+ const res = safe_alloc_uint8array_safeAllocUint8Array(tryAsExactBytes(descriptors_codec.u32.sizeHint) * this.chainSpec.coresCount);
23160
23244
  const authorizersDecoder = decoder_Decoder.fromBlob(res);
23161
23245
  const memoryReadResult = memory.loadInto(res, authorization);
23162
23246
  if (memoryReadResult.isError) {
@@ -23200,7 +23284,7 @@ class Bless {
23200
23284
  class gas_GasHostCall {
23201
23285
  currentServiceId;
23202
23286
  index = host_call_handler_tryAsHostCallIndex(0);
23203
- gasCost = gas_tryAsSmallGas(10);
23287
+ basicGasCost = gas_tryAsSmallGas(10);
23204
23288
  tracedRegisters = host_call_handler_traceRegisters(7);
23205
23289
  constructor(currentServiceId) {
23206
23290
  this.currentServiceId = currentServiceId;
@@ -23227,7 +23311,7 @@ class Checkpoint {
23227
23311
  currentServiceId;
23228
23312
  partialState;
23229
23313
  index = host_call_handler_tryAsHostCallIndex(17);
23230
- gasCost = gas_tryAsSmallGas(10);
23314
+ basicGasCost = gas_tryAsSmallGas(10);
23231
23315
  tracedRegisters;
23232
23316
  gasHostCall;
23233
23317
  constructor(currentServiceId, partialState) {
@@ -23252,6 +23336,7 @@ class Checkpoint {
23252
23336
 
23253
23337
 
23254
23338
 
23339
+
23255
23340
  const designate_IN_OUT_REG = 7;
23256
23341
  const VALIDATOR_DATA_BYTES = tryAsExactBytes(validator_data_ValidatorData.Codec.sizeHint);
23257
23342
  /**
@@ -23264,7 +23349,7 @@ class Designate {
23264
23349
  partialState;
23265
23350
  chainSpec;
23266
23351
  index = host_call_handler_tryAsHostCallIndex(16);
23267
- gasCost = gas_tryAsSmallGas(10);
23352
+ basicGasCost = gas_tryAsSmallGas(10);
23268
23353
  tracedRegisters = host_call_handler_traceRegisters(designate_IN_OUT_REG);
23269
23354
  constructor(currentServiceId, partialState, chainSpec) {
23270
23355
  this.currentServiceId = currentServiceId;
@@ -23274,7 +23359,7 @@ class Designate {
23274
23359
  async execute(_gas, regs, memory) {
23275
23360
  // `o`
23276
23361
  const validatorsStart = regs.get(designate_IN_OUT_REG);
23277
- const res = new Uint8Array(VALIDATOR_DATA_BYTES * this.chainSpec.validatorsCount);
23362
+ const res = safe_alloc_uint8array_safeAllocUint8Array(VALIDATOR_DATA_BYTES * this.chainSpec.validatorsCount);
23278
23363
  const memoryReadResult = memory.loadInto(res, validatorsStart);
23279
23364
  // error while reading the memory.
23280
23365
  if (memoryReadResult.isError) {
@@ -23315,7 +23400,7 @@ class Eject {
23315
23400
  currentServiceId;
23316
23401
  partialState;
23317
23402
  index = host_call_handler_tryAsHostCallIndex(21);
23318
- gasCost = gas_tryAsSmallGas(10);
23403
+ basicGasCost = gas_tryAsSmallGas(10);
23319
23404
  tracedRegisters = host_call_handler_traceRegisters(eject_IN_OUT_REG, 8);
23320
23405
  constructor(currentServiceId, partialState) {
23321
23406
  this.currentServiceId = currentServiceId;
@@ -23377,7 +23462,7 @@ class Forget {
23377
23462
  currentServiceId;
23378
23463
  partialState;
23379
23464
  index = host_call_handler_tryAsHostCallIndex(24);
23380
- gasCost = gas_tryAsSmallGas(10);
23465
+ basicGasCost = gas_tryAsSmallGas(10);
23381
23466
  tracedRegisters = host_call_handler_traceRegisters(forget_IN_OUT_REG, 8);
23382
23467
  constructor(currentServiceId, partialState) {
23383
23468
  this.currentServiceId = currentServiceId;
@@ -23427,7 +23512,7 @@ class New {
23427
23512
  currentServiceId;
23428
23513
  partialState;
23429
23514
  index = host_call_handler_tryAsHostCallIndex(18);
23430
- gasCost = gas_tryAsSmallGas(10);
23515
+ basicGasCost = gas_tryAsSmallGas(10);
23431
23516
  tracedRegisters = host_call_handler_traceRegisters(new_IN_OUT_REG, 8, 9, 10, 11);
23432
23517
  constructor(currentServiceId, partialState) {
23433
23518
  this.currentServiceId = currentServiceId;
@@ -23490,7 +23575,7 @@ class Provide {
23490
23575
  currentServiceId;
23491
23576
  partialState;
23492
23577
  index = host_call_handler_tryAsHostCallIndex(26);
23493
- gasCost = gas_tryAsSmallGas(10);
23578
+ basicGasCost = gas_tryAsSmallGas(10);
23494
23579
  tracedRegisters = host_call_handler_traceRegisters(provide_IN_OUT_REG, 8, 9);
23495
23580
  constructor(currentServiceId, partialState) {
23496
23581
  this.currentServiceId = currentServiceId;
@@ -23505,7 +23590,7 @@ class Provide {
23505
23590
  const preimageLength = regs.get(9);
23506
23591
  const length = utils_clampU64ToU32(preimageLength);
23507
23592
  // `i`
23508
- const preimage = bytes_BytesBlob.blobFrom(new Uint8Array(length));
23593
+ const preimage = bytes_BytesBlob.blobFrom(safe_alloc_uint8array_safeAllocUint8Array(length));
23509
23594
  const memoryReadResult = memory.loadInto(preimage.raw, preimageStart);
23510
23595
  if (memoryReadResult.isError) {
23511
23596
  logger_logger.trace `PROVIDE(${serviceId}, ${preimage.toStringTruncated()}) <- PANIC`;
@@ -23551,7 +23636,7 @@ class Query {
23551
23636
  currentServiceId;
23552
23637
  partialState;
23553
23638
  index = host_call_handler_tryAsHostCallIndex(22);
23554
- gasCost = gas_tryAsSmallGas(10);
23639
+ basicGasCost = gas_tryAsSmallGas(10);
23555
23640
  tracedRegisters = host_call_handler_traceRegisters(IN_OUT_REG_1, IN_OUT_REG_2);
23556
23641
  constructor(currentServiceId, partialState) {
23557
23642
  this.currentServiceId = currentServiceId;
@@ -23617,7 +23702,7 @@ class Solicit {
23617
23702
  currentServiceId;
23618
23703
  partialState;
23619
23704
  index = host_call_handler_tryAsHostCallIndex(23);
23620
- gasCost = gas_tryAsSmallGas(10);
23705
+ basicGasCost = gas_tryAsSmallGas(10);
23621
23706
  tracedRegisters = host_call_handler_traceRegisters(solicit_IN_OUT_REG, 8);
23622
23707
  constructor(currentServiceId, partialState) {
23623
23708
  this.currentServiceId = currentServiceId;
@@ -23665,12 +23750,12 @@ class Solicit {
23665
23750
 
23666
23751
  const transfer_IN_OUT_REG = 7; // `d`
23667
23752
  const AMOUNT_REG = 8; // `a`
23668
- const ON_TRANSFER_GAS_REG = 9; // `l`
23753
+ const TRANSFER_GAS_FEE_REG = 9; // `l`
23669
23754
  const MEMO_START_REG = 10; // `o`
23670
23755
  /**
23671
23756
  * Transfer balance from one service account to another.
23672
23757
  *
23673
- * https://graypaper.fluffylabs.dev/#/7e6ff6a/373b00373b00?v=0.6.7
23758
+ * https://graypaper.fluffylabs.dev/#/ab2cdbd/373f00373f00?v=0.7.2
23674
23759
  */
23675
23760
  class Transfer {
23676
23761
  currentServiceId;
@@ -23682,38 +23767,50 @@ class Transfer {
23682
23767
  },
23683
23768
  }));
23684
23769
  /**
23685
- * `g = 10 + ω9`
23686
- * https://graypaper.fluffylabs.dev/#/7e6ff6a/373d00373d00?v=0.6.7
23770
+ * `g = 10 + t`
23771
+ *
23772
+ * `t` has positive value, only when status of a transfer is `OK`
23773
+ * `0` otherwise
23774
+ *
23775
+ * Pre0.7.2: `g = 10 + ω9`
23776
+ *
23777
+ * https://graypaper.fluffylabs.dev/#/ab2cdbd/373f00373f00?v=0.7.2
23687
23778
  */
23688
- gasCost = (regs) => {
23689
- const gas = 10n + regs.get(ON_TRANSFER_GAS_REG);
23690
- return gas_tryAsGas(gas);
23691
- };
23692
- tracedRegisters = host_call_handler_traceRegisters(transfer_IN_OUT_REG, AMOUNT_REG, ON_TRANSFER_GAS_REG, MEMO_START_REG);
23779
+ basicGasCost = compatibility_Compatibility.isGreaterOrEqual(compatibility_GpVersion.V0_7_2)
23780
+ ? gas_tryAsSmallGas(10)
23781
+ : (regs) => gas_tryAsGas(10n + regs.get(TRANSFER_GAS_FEE_REG));
23782
+ tracedRegisters = host_call_handler_traceRegisters(transfer_IN_OUT_REG, AMOUNT_REG, TRANSFER_GAS_FEE_REG, MEMO_START_REG);
23693
23783
  constructor(currentServiceId, partialState) {
23694
23784
  this.currentServiceId = currentServiceId;
23695
23785
  this.partialState = partialState;
23696
23786
  }
23697
- async execute(_gas, regs, memory) {
23787
+ async execute(gas, regs, memory) {
23698
23788
  // `d`: destination
23699
23789
  const destination = getServiceId(regs.get(transfer_IN_OUT_REG));
23700
23790
  // `a`: amount
23701
23791
  const amount = regs.get(AMOUNT_REG);
23702
23792
  // `l`: gas
23703
- const onTransferGas = common_tryAsServiceGas(regs.get(ON_TRANSFER_GAS_REG));
23793
+ const transferGasFee = common_tryAsServiceGas(regs.get(TRANSFER_GAS_FEE_REG));
23704
23794
  // `o`: transfer memo
23705
23795
  const memoStart = regs.get(MEMO_START_REG);
23706
23796
  const memo = bytes_Bytes.zero(TRANSFER_MEMO_BYTES);
23707
23797
  const memoryReadResult = memory.loadInto(memo.raw, memoStart);
23708
23798
  // page fault while reading the memory.
23709
23799
  if (memoryReadResult.isError) {
23710
- logger_logger.trace `TRANSFER(${destination}, ${amount}, ${onTransferGas}, ${memo}) <- PANIC`;
23800
+ logger_logger.trace `TRANSFER(${destination}, ${amount}, ${transferGasFee}, ${memo}) <- PANIC`;
23711
23801
  return host_call_handler_PvmExecution.Panic;
23712
23802
  }
23713
- const transferResult = this.partialState.transfer(destination, amount, onTransferGas, memo);
23714
- logger_logger.trace `TRANSFER(${destination}, ${amount}, ${onTransferGas}, ${memo}) <- ${result_resultToString(transferResult)}`;
23803
+ const transferResult = this.partialState.transfer(destination, amount, transferGasFee, memo);
23804
+ logger_logger.trace `TRANSFER(${destination}, ${amount}, ${transferGasFee}, ${memo}) <- ${result_resultToString(transferResult)}`;
23715
23805
  // All good!
23716
23806
  if (transferResult.isOk) {
23807
+ if (compatibility_Compatibility.isGreaterOrEqual(compatibility_GpVersion.V0_7_2)) {
23808
+ // substracting value `t`
23809
+ const underflow = gas.sub(gas_tryAsGas(transferGasFee));
23810
+ if (underflow) {
23811
+ return host_call_handler_PvmExecution.OOG;
23812
+ }
23813
+ }
23717
23814
  regs.set(transfer_IN_OUT_REG, results_HostCallResult.OK);
23718
23815
  return;
23719
23816
  }
@@ -23753,7 +23850,7 @@ class Upgrade {
23753
23850
  currentServiceId;
23754
23851
  partialState;
23755
23852
  index = host_call_handler_tryAsHostCallIndex(19);
23756
- gasCost = gas_tryAsSmallGas(10);
23853
+ basicGasCost = gas_tryAsSmallGas(10);
23757
23854
  tracedRegisters = host_call_handler_traceRegisters(upgrade_IN_OUT_REG, GAS_REG, ALLOWANCE_REG);
23758
23855
  constructor(currentServiceId, partialState) {
23759
23856
  this.currentServiceId = currentServiceId;
@@ -23796,7 +23893,7 @@ class Yield {
23796
23893
  currentServiceId;
23797
23894
  partialState;
23798
23895
  index = host_call_handler_tryAsHostCallIndex(25);
23799
- gasCost = gas_tryAsSmallGas(10);
23896
+ basicGasCost = gas_tryAsSmallGas(10);
23800
23897
  tracedRegisters = host_call_handler_traceRegisters(yield_IN_OUT_REG);
23801
23898
  constructor(currentServiceId, partialState) {
23802
23899
  this.currentServiceId = currentServiceId;
@@ -23832,7 +23929,7 @@ class fetch_Fetch {
23832
23929
  currentServiceId;
23833
23930
  fetch;
23834
23931
  index = tryAsHostCallIndex(1);
23835
- gasCost = tryAsSmallGas(10);
23932
+ basicGasCost = tryAsSmallGas(10);
23836
23933
  tracedRegisters = traceRegisters(fetch_IN_OUT_REG, 8, 9, 10, 11, 12);
23837
23934
  constructor(currentServiceId, fetch) {
23838
23935
  this.currentServiceId = currentServiceId;
@@ -23989,7 +24086,7 @@ class info_Info {
23989
24086
  currentServiceId;
23990
24087
  account;
23991
24088
  index = tryAsHostCallIndex(5);
23992
- gasCost = tryAsSmallGas(10);
24089
+ basicGasCost = tryAsSmallGas(10);
23993
24090
  tracedRegisters = traceRegisters(info_IN_OUT_REG, 8, OFFSET_REG, LEN_REG);
23994
24091
  constructor(currentServiceId, account) {
23995
24092
  this.currentServiceId = currentServiceId;
@@ -24055,6 +24152,7 @@ const codecServiceAccountInfoWithThresholdBalance = descriptors_codec.object({
24055
24152
 
24056
24153
 
24057
24154
 
24155
+
24058
24156
  const decoder = new TextDecoder("utf8");
24059
24157
  /**
24060
24158
  * Log message to the console
@@ -24064,7 +24162,7 @@ const decoder = new TextDecoder("utf8");
24064
24162
  class log_LogHostCall {
24065
24163
  currentServiceId;
24066
24164
  index = tryAsHostCallIndex(100);
24067
- gasCost = tryAsSmallGas(0);
24165
+ basicGasCost = tryAsSmallGas(0);
24068
24166
  // intentionally not tracing anything here, since the message will be printed anyway.
24069
24167
  tracedRegisters = traceRegisters();
24070
24168
  constructor(currentServiceId) {
@@ -24076,8 +24174,8 @@ class log_LogHostCall {
24076
24174
  const targetLength = regs.get(9);
24077
24175
  const msgStart = regs.get(10);
24078
24176
  const msgLength = regs.get(11);
24079
- const target = new Uint8Array(clampU64ToU32(targetLength));
24080
- const message = new Uint8Array(clampU64ToU32(msgLength));
24177
+ const target = safeAllocUint8Array(clampU64ToU32(targetLength));
24178
+ const message = safeAllocUint8Array(clampU64ToU32(msgLength));
24081
24179
  if (targetStart !== 0n) {
24082
24180
  memory.loadInto(target, targetStart);
24083
24181
  }
@@ -24096,6 +24194,7 @@ class log_LogHostCall {
24096
24194
 
24097
24195
 
24098
24196
 
24197
+
24099
24198
  const lookup_IN_OUT_REG = 7;
24100
24199
  /**
24101
24200
  * Lookup a preimage.
@@ -24106,7 +24205,7 @@ class lookup_Lookup {
24106
24205
  currentServiceId;
24107
24206
  account;
24108
24207
  index = tryAsHostCallIndex(2);
24109
- gasCost = tryAsSmallGas(10);
24208
+ basicGasCost = tryAsSmallGas(10);
24110
24209
  tracedRegisters = traceRegisters(lookup_IN_OUT_REG, 8, 9, 10, 11);
24111
24210
  constructor(currentServiceId, account) {
24112
24211
  this.currentServiceId = currentServiceId;
@@ -24138,7 +24237,7 @@ class lookup_Lookup {
24138
24237
  // NOTE [MaSo] this is ok to cast to number, because we are bounded by the
24139
24238
  // valueLength in both cases and valueLength is WC (4,000,000,000) + metadata
24140
24239
  // which is less than 2^32
24141
- const chunk = preImage === null ? new Uint8Array(0) : preImage.raw.subarray(Number(offset), Number(offset + length));
24240
+ const chunk = preImage === null ? safeAllocUint8Array(0) : preImage.raw.subarray(Number(offset), Number(offset + length));
24142
24241
  const memoryWriteResult = memory.storeFrom(destinationAddress, chunk);
24143
24242
  if (memoryWriteResult.isError) {
24144
24243
  return PvmExecution.Panic;
@@ -24159,6 +24258,7 @@ class lookup_Lookup {
24159
24258
 
24160
24259
 
24161
24260
 
24261
+
24162
24262
  const read_IN_OUT_REG = 7;
24163
24263
  /**
24164
24264
  * Read account storage.
@@ -24169,7 +24269,7 @@ class read_Read {
24169
24269
  currentServiceId;
24170
24270
  account;
24171
24271
  index = tryAsHostCallIndex(3);
24172
- gasCost = tryAsSmallGas(10);
24272
+ basicGasCost = tryAsSmallGas(10);
24173
24273
  tracedRegisters = traceRegisters(read_IN_OUT_REG, 8, 9, 10, 11, 12);
24174
24274
  constructor(currentServiceId, account) {
24175
24275
  this.currentServiceId = currentServiceId;
@@ -24186,7 +24286,7 @@ class read_Read {
24186
24286
  const destinationAddress = regs.get(10);
24187
24287
  const storageKeyLengthClamped = clampU64ToU32(storageKeyLength);
24188
24288
  // k
24189
- const rawKey = BytesBlob.blobFrom(new Uint8Array(storageKeyLengthClamped));
24289
+ const rawKey = BytesBlob.blobFrom(safeAllocUint8Array(storageKeyLengthClamped));
24190
24290
  const memoryReadResult = memory.loadInto(rawKey.raw, storageKeyStartAddress);
24191
24291
  if (memoryReadResult.isError) {
24192
24292
  logger.trace `READ(${serviceId}, ${rawKey}) <- PANIC`;
@@ -24204,7 +24304,7 @@ class read_Read {
24204
24304
  // NOTE [MaSo] this is ok to cast to number, because we are bounded by the
24205
24305
  // valueLength in both cases and valueLength is WC (4,000,000,000) + metadata
24206
24306
  // which is less than 2^32
24207
- const chunk = value === null ? new Uint8Array(0) : value.raw.subarray(Number(offset), Number(offset + blobLength));
24307
+ const chunk = value === null ? safeAllocUint8Array(0) : value.raw.subarray(Number(offset), Number(offset + blobLength));
24208
24308
  const memoryWriteResult = memory.storeFrom(destinationAddress, chunk);
24209
24309
  if (memoryWriteResult.isError) {
24210
24310
  logger.trace `READ(${serviceId}, ${rawKey}) <- PANIC`;
@@ -24239,7 +24339,7 @@ class write_Write {
24239
24339
  currentServiceId;
24240
24340
  account;
24241
24341
  index = tryAsHostCallIndex(4);
24242
- gasCost = tryAsSmallGas(10);
24342
+ basicGasCost = tryAsSmallGas(10);
24243
24343
  tracedRegisters = traceRegisters(write_IN_OUT_REG, 8, 9, 10);
24244
24344
  constructor(currentServiceId, account) {
24245
24345
  this.currentServiceId = currentServiceId;
@@ -24255,7 +24355,7 @@ class write_Write {
24255
24355
  // v_z
24256
24356
  const valueLength = regs.get(10);
24257
24357
  const storageKeyLengthClamped = clampU64ToU32(storageKeyLength);
24258
- const rawStorageKey = new Uint8Array(storageKeyLengthClamped);
24358
+ const rawStorageKey = safeAllocUint8Array(storageKeyLengthClamped);
24259
24359
  const keyLoadingResult = memory.loadInto(rawStorageKey, storageKeyStartAddress);
24260
24360
  if (keyLoadingResult.isError) {
24261
24361
  logger.trace `WRITE() <- PANIC`;
@@ -24264,7 +24364,7 @@ class write_Write {
24264
24364
  // k
24265
24365
  const storageKey = asOpaqueType(BytesBlob.blobFrom(rawStorageKey));
24266
24366
  const valueLengthClamped = clampU64ToU32(valueLength);
24267
- const value = new Uint8Array(valueLengthClamped);
24367
+ const value = safeAllocUint8Array(valueLengthClamped);
24268
24368
  const valueLoadingResult = memory.loadInto(value, valueStart);
24269
24369
  // Note [MaSo] this is ok to return bcs if valueLength is 0, then this panic won't happen
24270
24370
  if (valueLoadingResult.isError) {
@@ -25153,7 +25253,7 @@ function shuffling_fisherYatesShuffle(arr, entropy) {
25153
25253
  }
25154
25254
  function hashToNumberSequence(entropy, length) {
25155
25255
  const result = new Array(length);
25156
- const randomBytes = new Uint8Array(ENTROPY_BYTES + 4);
25256
+ const randomBytes = safeAllocUint8Array(ENTROPY_BYTES + 4);
25157
25257
  randomBytes.set(entropy.raw);
25158
25258
  for (let i = 0; i < length; i++) {
25159
25259
  randomBytes.set(u32AsLeBytes(tryAsU32(Math.floor(i / 8))), ENTROPY_BYTES);
@@ -26183,6 +26283,7 @@ class chain_stf_OnChain {
26183
26283
  authorization;
26184
26284
  // chapter 13: https://graypaper.fluffylabs.dev/#/68eaa1f/18b60118b601?v=0.6.4
26185
26285
  statistics;
26286
+ isReadyForNextEpoch = Promise.resolve(false);
26186
26287
  constructor(chainSpec, state, blocks, hasher) {
26187
26288
  this.chainSpec = chainSpec;
26188
26289
  this.state = state;
@@ -26201,6 +26302,14 @@ class chain_stf_OnChain {
26201
26302
  this.preimages = new Preimages(state);
26202
26303
  this.authorization = new Authorization(chainSpec, state);
26203
26304
  }
26305
+ /** Pre-populate things worth caching for the next epoch. */
26306
+ async prepareForNextEpoch() {
26307
+ if (await this.isReadyForNextEpoch) {
26308
+ return;
26309
+ }
26310
+ const ready = this.safrole.prepareValidatorKeysForNextEpoch(this.state.disputesRecords.punishSet);
26311
+ this.isReadyForNextEpoch = ready.then((_) => true);
26312
+ }
26204
26313
  async verifySeal(timeSlot, block) {
26205
26314
  const sealState = this.safrole.getSafroleSealState(timeSlot);
26206
26315
  return await this.safroleSeal.verifyHeaderSeal(block.header.view(), sealState);
@@ -26209,6 +26318,10 @@ class chain_stf_OnChain {
26209
26318
  const headerView = block.header.view();
26210
26319
  const header = block.header.materialize();
26211
26320
  const timeSlot = header.timeSlotIndex;
26321
+ // reset the epoch cache state
26322
+ if (headerView.epochMarker.view() !== null) {
26323
+ this.isReadyForNextEpoch = Promise.resolve(false);
26324
+ }
26212
26325
  // safrole seal
26213
26326
  let newEntropyHash;
26214
26327
  if (omitSealVerification) {
@@ -26673,6 +26786,7 @@ async function runWorkPackageTest(test, file) {
26673
26786
 
26674
26787
 
26675
26788
 
26789
+
26676
26790
  class MemoryChunkItem {
26677
26791
  static fromJson = {
26678
26792
  address: "number",
@@ -26730,10 +26844,10 @@ async function runPvmTest(testContent) {
26730
26844
  const endPageIndex = tryAsMemoryIndex(startPageIndex + page.length);
26731
26845
  const isWriteable = page["is-writable"];
26732
26846
  if (isWriteable) {
26733
- memoryBuilder.setWriteablePages(startPageIndex, endPageIndex, new Uint8Array(page.length));
26847
+ memoryBuilder.setWriteablePages(startPageIndex, endPageIndex, safeAllocUint8Array(page.length));
26734
26848
  }
26735
26849
  else {
26736
- memoryBuilder.setReadablePages(startPageIndex, endPageIndex, new Uint8Array(page.length));
26850
+ memoryBuilder.setReadablePages(startPageIndex, endPageIndex, safeAllocUint8Array(page.length));
26737
26851
  }
26738
26852
  }
26739
26853
  for (const memoryChunk of initialMemory) {
@@ -26785,7 +26899,7 @@ async function runPvmTest(testContent) {
26785
26899
  }, {});
26786
26900
  for (const [pageNumberAsString, memoryChunks] of Object.entries(expectedMemoryByPageNumber)) {
26787
26901
  const pageNumber = tryAsPageNumber(Number(pageNumberAsString));
26788
- const expectedPage = new Uint8Array(PAGE_SIZE);
26902
+ const expectedPage = safeAllocUint8Array(PAGE_SIZE);
26789
26903
  for (const memoryChunk of memoryChunks) {
26790
26904
  const pageIndex = memoryChunk.address % PAGE_SIZE;
26791
26905
  expectedPage.set(memoryChunk.contents, pageIndex);