@typeberry/convert 0.1.3-af70ed0 → 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 +167 -72
  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;
@@ -14661,7 +14676,7 @@ class SerializedService {
14661
14676
  getStorage(rawKey) {
14662
14677
  if (compatibility_Compatibility.isLessThan(compatibility_GpVersion.V0_6_7)) {
14663
14678
  const SERVICE_ID_BYTES = 4;
14664
- const serviceIdAndKey = new Uint8Array(SERVICE_ID_BYTES + rawKey.length);
14679
+ const serviceIdAndKey = safe_alloc_uint8array_safeAllocUint8Array(SERVICE_ID_BYTES + rawKey.length);
14665
14680
  serviceIdAndKey.set(numbers_u32AsLeBytes(this.serviceId));
14666
14681
  serviceIdAndKey.set(rawKey.raw, SERVICE_ID_BYTES);
14667
14682
  const key = opaque_asOpaqueType(bytes_BytesBlob.blobFrom(hashBytes(serviceIdAndKey).raw));
@@ -14752,7 +14767,7 @@ class nodes_TrieNode {
14752
14767
  raw;
14753
14768
  constructor(
14754
14769
  /** Exactly 512 bits / 64 bytes */
14755
- raw = new Uint8Array(nodes_TRIE_NODE_BYTES)) {
14770
+ raw = safe_alloc_uint8array_safeAllocUint8Array(nodes_TRIE_NODE_BYTES)) {
14756
14771
  this.raw = raw;
14757
14772
  }
14758
14773
  /** Returns the type of the node */
@@ -16187,7 +16202,7 @@ class registers_Registers {
16187
16202
  bytes;
16188
16203
  asSigned;
16189
16204
  asUnsigned;
16190
- constructor(bytes = new Uint8Array(registers_NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
16205
+ constructor(bytes = safeAllocUint8Array(registers_NO_OF_REGISTERS << REGISTER_SIZE_SHIFT)) {
16191
16206
  this.bytes = bytes;
16192
16207
  check `${bytes.length === registers_NO_OF_REGISTERS << REGISTER_SIZE_SHIFT} Invalid size of registers array.`;
16193
16208
  this.asSigned = new BigInt64Array(bytes.buffer, bytes.byteOffset);
@@ -16263,10 +16278,16 @@ function registers_signExtend32To64(value) {
16263
16278
 
16264
16279
  /** Attempt to convert a number into `HostCallIndex`. */
16265
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
+ */
16266
16286
  var host_call_handler_PvmExecution;
16267
16287
  (function (PvmExecution) {
16268
16288
  PvmExecution[PvmExecution["Halt"] = 0] = "Halt";
16269
16289
  PvmExecution[PvmExecution["Panic"] = 1] = "Panic";
16290
+ PvmExecution[PvmExecution["OOG"] = 2] = "OOG";
16270
16291
  })(host_call_handler_PvmExecution || (host_call_handler_PvmExecution = {}));
16271
16292
  /** A utility function to easily trace a bunch of registers. */
16272
16293
  function host_call_handler_traceRegisters(...regs) {
@@ -16344,7 +16365,7 @@ class mask_Mask {
16344
16365
  return Math.min(this.lookupTableForward[index] ?? 0, MAX_INSTRUCTION_DISTANCE);
16345
16366
  }
16346
16367
  buildLookupTableForward(mask) {
16347
- const table = new Uint8Array(mask.bitLength);
16368
+ const table = safeAllocUint8Array(mask.bitLength);
16348
16369
  let lastInstructionOffset = 0;
16349
16370
  for (let i = mask.bitLength - 1; i >= 0; i--) {
16350
16371
  if (mask.isSet(i)) {
@@ -19855,7 +19876,7 @@ class host_calls_HostCalls {
19855
19876
  const regs = pvmInstance.getRegisters();
19856
19877
  const maybeAddress = regs.getLowerU32(7);
19857
19878
  const maybeLength = regs.getLowerU32(8);
19858
- const result = new Uint8Array(maybeLength);
19879
+ const result = safeAllocUint8Array(maybeLength);
19859
19880
  const startAddress = tryAsMemoryIndex(maybeAddress);
19860
19881
  const loadResult = memory.loadInto(result, startAddress);
19861
19882
  if (loadResult.isError) {
@@ -19883,8 +19904,9 @@ class host_calls_HostCalls {
19883
19904
  const index = tryAsHostCallIndex(hostCallIndex);
19884
19905
  const hostCall = this.hostCalls.get(index);
19885
19906
  const gasBefore = gas.get();
19886
- const gasCost = typeof hostCall.gasCost === "number" ? hostCall.gasCost : hostCall.gasCost(regs);
19887
- 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);
19888
19910
  const pcLog = `[PC: ${pvmInstance.getPC()}]`;
19889
19911
  if (underflow) {
19890
19912
  this.hostCalls.traceHostCall(`${pcLog} OOG`, index, hostCall, regs, gas.get());
@@ -19901,6 +19923,10 @@ class host_calls_HostCalls {
19901
19923
  status = Status.PANIC;
19902
19924
  return this.getReturnValue(status, pvmInstance);
19903
19925
  }
19926
+ if (result === PvmExecution.OOG) {
19927
+ status = Status.OOG;
19928
+ return this.getReturnValue(status, pvmInstance);
19929
+ }
19904
19930
  if (result === undefined) {
19905
19931
  pvmInstance.runProgram();
19906
19932
  status = pvmInstance.getStatus();
@@ -19959,7 +19985,7 @@ class host_calls_manager_HostCallsManager {
19959
19985
  }
19960
19986
  class NoopMissing {
19961
19987
  index = tryAsHostCallIndex(2 ** 32 - 1);
19962
- gasCost = tryAsSmallGas(0);
19988
+ basicGasCost = tryAsSmallGas(0);
19963
19989
  currentServiceId = tryAsU32(0);
19964
19990
  tracedRegisters = [];
19965
19991
  async execute() {
@@ -20073,7 +20099,7 @@ function utils_clampU64ToU32(value) {
20073
20099
 
20074
20100
  class missing_Missing {
20075
20101
  index = tryAsHostCallIndex(2 ** 32 - 1);
20076
- gasCost = tryAsSmallGas(10);
20102
+ basicGasCost = tryAsSmallGas(10);
20077
20103
  currentServiceId = CURRENT_SERVICE_ID;
20078
20104
  tracedRegisters = traceRegisters(7);
20079
20105
  execute(_gas, regs, _memory) {
@@ -20774,6 +20800,15 @@ var ResultValues;
20774
20800
  ResultValues[ResultValues["Ok"] = 0] = "Ok";
20775
20801
  ResultValues[ResultValues["Error"] = 1] = "Error";
20776
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 = [];
20777
20812
  // TODO [ToDr] We export the entire object to allow mocking in tests.
20778
20813
  // Ideally we would just export functions and figure out how to mock
20779
20814
  // properly in ESM.
@@ -20789,9 +20824,27 @@ async function verifySeal(bandersnatch, authorKey, signature, payload, encodedUn
20789
20824
  }
20790
20825
  return result_Result.ok(bytes_Bytes.fromBlob(sealResult.subarray(1), hash_HASH_SIZE).asOpaque());
20791
20826
  }
20792
- async function getRingCommitment(bandersnatch, validators) {
20793
- const keys = bytes_BytesBlob.blobFromParts(validators.map((x) => x.raw)).raw;
20794
- 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);
20795
20848
  if (commitmentResult[RESULT_INDEX] === ResultValues.Error) {
20796
20849
  return result_Result.error(null);
20797
20850
  }
@@ -20917,6 +20970,18 @@ class safrole_Safrole {
20917
20970
  }
20918
20971
  return FixedSizeArray.new([newRandomnessAcc, ...rest], 4);
20919
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
+ }
20920
20985
  async getValidatorKeys(timeslot, postOffenders) {
20921
20986
  /**
20922
20987
  * Epoch is not changed so the previous state is returned
@@ -23056,7 +23121,7 @@ class Assign {
23056
23121
  partialState;
23057
23122
  chainSpec;
23058
23123
  index = host_call_handler_tryAsHostCallIndex(15);
23059
- gasCost = gas_tryAsSmallGas(10);
23124
+ basicGasCost = gas_tryAsSmallGas(10);
23060
23125
  tracedRegisters = host_call_handler_traceRegisters(IN_OUT_REG, 8);
23061
23126
  constructor(currentServiceId, partialState, chainSpec) {
23062
23127
  this.currentServiceId = currentServiceId;
@@ -23070,7 +23135,7 @@ class Assign {
23070
23135
  const authorizationQueueStart = regs.get(8);
23071
23136
  // a
23072
23137
  const authManager = getServiceId(regs.get(9));
23073
- 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);
23074
23139
  const memoryReadResult = memory.loadInto(res, authorizationQueueStart);
23075
23140
  // error while reading the memory.
23076
23141
  if (memoryReadResult.isError) {
@@ -23134,7 +23199,7 @@ class Bless {
23134
23199
  partialState;
23135
23200
  chainSpec;
23136
23201
  index = host_call_handler_tryAsHostCallIndex(14);
23137
- gasCost = gas_tryAsSmallGas(10);
23202
+ basicGasCost = gas_tryAsSmallGas(10);
23138
23203
  tracedRegisters = host_call_handler_traceRegisters(bless_IN_OUT_REG, 8, 9, 10, 11);
23139
23204
  constructor(currentServiceId, partialState, chainSpec) {
23140
23205
  this.currentServiceId = currentServiceId;
@@ -23158,7 +23223,7 @@ class Bless {
23158
23223
  * https://graypaper.fluffylabs.dev/#/7e6ff6a/368100368100?v=0.6.7
23159
23224
  */
23160
23225
  const autoAccumulateEntries = [];
23161
- const result = new Uint8Array(tryAsExactBytes(serviceIdAndGasCodec.sizeHint));
23226
+ const result = safe_alloc_uint8array_safeAllocUint8Array(tryAsExactBytes(serviceIdAndGasCodec.sizeHint));
23162
23227
  const decoder = decoder_Decoder.fromBlob(result);
23163
23228
  let memIndex = sourceStart;
23164
23229
  for (let i = 0n; i < numberOfItems; i += 1n) {
@@ -23175,7 +23240,7 @@ class Bless {
23175
23240
  memIndex = numbers_tryAsU64(memIndex + numbers_tryAsU64(decoder.bytesRead()));
23176
23241
  }
23177
23242
  // https://graypaper.fluffylabs.dev/#/7e6ff6a/367200367200?v=0.6.7
23178
- 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);
23179
23244
  const authorizersDecoder = decoder_Decoder.fromBlob(res);
23180
23245
  const memoryReadResult = memory.loadInto(res, authorization);
23181
23246
  if (memoryReadResult.isError) {
@@ -23219,7 +23284,7 @@ class Bless {
23219
23284
  class gas_GasHostCall {
23220
23285
  currentServiceId;
23221
23286
  index = host_call_handler_tryAsHostCallIndex(0);
23222
- gasCost = gas_tryAsSmallGas(10);
23287
+ basicGasCost = gas_tryAsSmallGas(10);
23223
23288
  tracedRegisters = host_call_handler_traceRegisters(7);
23224
23289
  constructor(currentServiceId) {
23225
23290
  this.currentServiceId = currentServiceId;
@@ -23246,7 +23311,7 @@ class Checkpoint {
23246
23311
  currentServiceId;
23247
23312
  partialState;
23248
23313
  index = host_call_handler_tryAsHostCallIndex(17);
23249
- gasCost = gas_tryAsSmallGas(10);
23314
+ basicGasCost = gas_tryAsSmallGas(10);
23250
23315
  tracedRegisters;
23251
23316
  gasHostCall;
23252
23317
  constructor(currentServiceId, partialState) {
@@ -23271,6 +23336,7 @@ class Checkpoint {
23271
23336
 
23272
23337
 
23273
23338
 
23339
+
23274
23340
  const designate_IN_OUT_REG = 7;
23275
23341
  const VALIDATOR_DATA_BYTES = tryAsExactBytes(validator_data_ValidatorData.Codec.sizeHint);
23276
23342
  /**
@@ -23283,7 +23349,7 @@ class Designate {
23283
23349
  partialState;
23284
23350
  chainSpec;
23285
23351
  index = host_call_handler_tryAsHostCallIndex(16);
23286
- gasCost = gas_tryAsSmallGas(10);
23352
+ basicGasCost = gas_tryAsSmallGas(10);
23287
23353
  tracedRegisters = host_call_handler_traceRegisters(designate_IN_OUT_REG);
23288
23354
  constructor(currentServiceId, partialState, chainSpec) {
23289
23355
  this.currentServiceId = currentServiceId;
@@ -23293,7 +23359,7 @@ class Designate {
23293
23359
  async execute(_gas, regs, memory) {
23294
23360
  // `o`
23295
23361
  const validatorsStart = regs.get(designate_IN_OUT_REG);
23296
- const res = new Uint8Array(VALIDATOR_DATA_BYTES * this.chainSpec.validatorsCount);
23362
+ const res = safe_alloc_uint8array_safeAllocUint8Array(VALIDATOR_DATA_BYTES * this.chainSpec.validatorsCount);
23297
23363
  const memoryReadResult = memory.loadInto(res, validatorsStart);
23298
23364
  // error while reading the memory.
23299
23365
  if (memoryReadResult.isError) {
@@ -23334,7 +23400,7 @@ class Eject {
23334
23400
  currentServiceId;
23335
23401
  partialState;
23336
23402
  index = host_call_handler_tryAsHostCallIndex(21);
23337
- gasCost = gas_tryAsSmallGas(10);
23403
+ basicGasCost = gas_tryAsSmallGas(10);
23338
23404
  tracedRegisters = host_call_handler_traceRegisters(eject_IN_OUT_REG, 8);
23339
23405
  constructor(currentServiceId, partialState) {
23340
23406
  this.currentServiceId = currentServiceId;
@@ -23396,7 +23462,7 @@ class Forget {
23396
23462
  currentServiceId;
23397
23463
  partialState;
23398
23464
  index = host_call_handler_tryAsHostCallIndex(24);
23399
- gasCost = gas_tryAsSmallGas(10);
23465
+ basicGasCost = gas_tryAsSmallGas(10);
23400
23466
  tracedRegisters = host_call_handler_traceRegisters(forget_IN_OUT_REG, 8);
23401
23467
  constructor(currentServiceId, partialState) {
23402
23468
  this.currentServiceId = currentServiceId;
@@ -23446,7 +23512,7 @@ class New {
23446
23512
  currentServiceId;
23447
23513
  partialState;
23448
23514
  index = host_call_handler_tryAsHostCallIndex(18);
23449
- gasCost = gas_tryAsSmallGas(10);
23515
+ basicGasCost = gas_tryAsSmallGas(10);
23450
23516
  tracedRegisters = host_call_handler_traceRegisters(new_IN_OUT_REG, 8, 9, 10, 11);
23451
23517
  constructor(currentServiceId, partialState) {
23452
23518
  this.currentServiceId = currentServiceId;
@@ -23509,7 +23575,7 @@ class Provide {
23509
23575
  currentServiceId;
23510
23576
  partialState;
23511
23577
  index = host_call_handler_tryAsHostCallIndex(26);
23512
- gasCost = gas_tryAsSmallGas(10);
23578
+ basicGasCost = gas_tryAsSmallGas(10);
23513
23579
  tracedRegisters = host_call_handler_traceRegisters(provide_IN_OUT_REG, 8, 9);
23514
23580
  constructor(currentServiceId, partialState) {
23515
23581
  this.currentServiceId = currentServiceId;
@@ -23524,7 +23590,7 @@ class Provide {
23524
23590
  const preimageLength = regs.get(9);
23525
23591
  const length = utils_clampU64ToU32(preimageLength);
23526
23592
  // `i`
23527
- const preimage = bytes_BytesBlob.blobFrom(new Uint8Array(length));
23593
+ const preimage = bytes_BytesBlob.blobFrom(safe_alloc_uint8array_safeAllocUint8Array(length));
23528
23594
  const memoryReadResult = memory.loadInto(preimage.raw, preimageStart);
23529
23595
  if (memoryReadResult.isError) {
23530
23596
  logger_logger.trace `PROVIDE(${serviceId}, ${preimage.toStringTruncated()}) <- PANIC`;
@@ -23570,7 +23636,7 @@ class Query {
23570
23636
  currentServiceId;
23571
23637
  partialState;
23572
23638
  index = host_call_handler_tryAsHostCallIndex(22);
23573
- gasCost = gas_tryAsSmallGas(10);
23639
+ basicGasCost = gas_tryAsSmallGas(10);
23574
23640
  tracedRegisters = host_call_handler_traceRegisters(IN_OUT_REG_1, IN_OUT_REG_2);
23575
23641
  constructor(currentServiceId, partialState) {
23576
23642
  this.currentServiceId = currentServiceId;
@@ -23636,7 +23702,7 @@ class Solicit {
23636
23702
  currentServiceId;
23637
23703
  partialState;
23638
23704
  index = host_call_handler_tryAsHostCallIndex(23);
23639
- gasCost = gas_tryAsSmallGas(10);
23705
+ basicGasCost = gas_tryAsSmallGas(10);
23640
23706
  tracedRegisters = host_call_handler_traceRegisters(solicit_IN_OUT_REG, 8);
23641
23707
  constructor(currentServiceId, partialState) {
23642
23708
  this.currentServiceId = currentServiceId;
@@ -23684,12 +23750,12 @@ class Solicit {
23684
23750
 
23685
23751
  const transfer_IN_OUT_REG = 7; // `d`
23686
23752
  const AMOUNT_REG = 8; // `a`
23687
- const ON_TRANSFER_GAS_REG = 9; // `l`
23753
+ const TRANSFER_GAS_FEE_REG = 9; // `l`
23688
23754
  const MEMO_START_REG = 10; // `o`
23689
23755
  /**
23690
23756
  * Transfer balance from one service account to another.
23691
23757
  *
23692
- * https://graypaper.fluffylabs.dev/#/7e6ff6a/373b00373b00?v=0.6.7
23758
+ * https://graypaper.fluffylabs.dev/#/ab2cdbd/373f00373f00?v=0.7.2
23693
23759
  */
23694
23760
  class Transfer {
23695
23761
  currentServiceId;
@@ -23701,38 +23767,50 @@ class Transfer {
23701
23767
  },
23702
23768
  }));
23703
23769
  /**
23704
- * `g = 10 + ω9`
23705
- * 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
23706
23778
  */
23707
- gasCost = (regs) => {
23708
- const gas = 10n + regs.get(ON_TRANSFER_GAS_REG);
23709
- return gas_tryAsGas(gas);
23710
- };
23711
- 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);
23712
23783
  constructor(currentServiceId, partialState) {
23713
23784
  this.currentServiceId = currentServiceId;
23714
23785
  this.partialState = partialState;
23715
23786
  }
23716
- async execute(_gas, regs, memory) {
23787
+ async execute(gas, regs, memory) {
23717
23788
  // `d`: destination
23718
23789
  const destination = getServiceId(regs.get(transfer_IN_OUT_REG));
23719
23790
  // `a`: amount
23720
23791
  const amount = regs.get(AMOUNT_REG);
23721
23792
  // `l`: gas
23722
- const onTransferGas = common_tryAsServiceGas(regs.get(ON_TRANSFER_GAS_REG));
23793
+ const transferGasFee = common_tryAsServiceGas(regs.get(TRANSFER_GAS_FEE_REG));
23723
23794
  // `o`: transfer memo
23724
23795
  const memoStart = regs.get(MEMO_START_REG);
23725
23796
  const memo = bytes_Bytes.zero(TRANSFER_MEMO_BYTES);
23726
23797
  const memoryReadResult = memory.loadInto(memo.raw, memoStart);
23727
23798
  // page fault while reading the memory.
23728
23799
  if (memoryReadResult.isError) {
23729
- logger_logger.trace `TRANSFER(${destination}, ${amount}, ${onTransferGas}, ${memo}) <- PANIC`;
23800
+ logger_logger.trace `TRANSFER(${destination}, ${amount}, ${transferGasFee}, ${memo}) <- PANIC`;
23730
23801
  return host_call_handler_PvmExecution.Panic;
23731
23802
  }
23732
- const transferResult = this.partialState.transfer(destination, amount, onTransferGas, memo);
23733
- 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)}`;
23734
23805
  // All good!
23735
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
+ }
23736
23814
  regs.set(transfer_IN_OUT_REG, results_HostCallResult.OK);
23737
23815
  return;
23738
23816
  }
@@ -23772,7 +23850,7 @@ class Upgrade {
23772
23850
  currentServiceId;
23773
23851
  partialState;
23774
23852
  index = host_call_handler_tryAsHostCallIndex(19);
23775
- gasCost = gas_tryAsSmallGas(10);
23853
+ basicGasCost = gas_tryAsSmallGas(10);
23776
23854
  tracedRegisters = host_call_handler_traceRegisters(upgrade_IN_OUT_REG, GAS_REG, ALLOWANCE_REG);
23777
23855
  constructor(currentServiceId, partialState) {
23778
23856
  this.currentServiceId = currentServiceId;
@@ -23815,7 +23893,7 @@ class Yield {
23815
23893
  currentServiceId;
23816
23894
  partialState;
23817
23895
  index = host_call_handler_tryAsHostCallIndex(25);
23818
- gasCost = gas_tryAsSmallGas(10);
23896
+ basicGasCost = gas_tryAsSmallGas(10);
23819
23897
  tracedRegisters = host_call_handler_traceRegisters(yield_IN_OUT_REG);
23820
23898
  constructor(currentServiceId, partialState) {
23821
23899
  this.currentServiceId = currentServiceId;
@@ -23851,7 +23929,7 @@ class fetch_Fetch {
23851
23929
  currentServiceId;
23852
23930
  fetch;
23853
23931
  index = tryAsHostCallIndex(1);
23854
- gasCost = tryAsSmallGas(10);
23932
+ basicGasCost = tryAsSmallGas(10);
23855
23933
  tracedRegisters = traceRegisters(fetch_IN_OUT_REG, 8, 9, 10, 11, 12);
23856
23934
  constructor(currentServiceId, fetch) {
23857
23935
  this.currentServiceId = currentServiceId;
@@ -24008,7 +24086,7 @@ class info_Info {
24008
24086
  currentServiceId;
24009
24087
  account;
24010
24088
  index = tryAsHostCallIndex(5);
24011
- gasCost = tryAsSmallGas(10);
24089
+ basicGasCost = tryAsSmallGas(10);
24012
24090
  tracedRegisters = traceRegisters(info_IN_OUT_REG, 8, OFFSET_REG, LEN_REG);
24013
24091
  constructor(currentServiceId, account) {
24014
24092
  this.currentServiceId = currentServiceId;
@@ -24074,6 +24152,7 @@ const codecServiceAccountInfoWithThresholdBalance = descriptors_codec.object({
24074
24152
 
24075
24153
 
24076
24154
 
24155
+
24077
24156
  const decoder = new TextDecoder("utf8");
24078
24157
  /**
24079
24158
  * Log message to the console
@@ -24083,7 +24162,7 @@ const decoder = new TextDecoder("utf8");
24083
24162
  class log_LogHostCall {
24084
24163
  currentServiceId;
24085
24164
  index = tryAsHostCallIndex(100);
24086
- gasCost = tryAsSmallGas(0);
24165
+ basicGasCost = tryAsSmallGas(0);
24087
24166
  // intentionally not tracing anything here, since the message will be printed anyway.
24088
24167
  tracedRegisters = traceRegisters();
24089
24168
  constructor(currentServiceId) {
@@ -24095,8 +24174,8 @@ class log_LogHostCall {
24095
24174
  const targetLength = regs.get(9);
24096
24175
  const msgStart = regs.get(10);
24097
24176
  const msgLength = regs.get(11);
24098
- const target = new Uint8Array(clampU64ToU32(targetLength));
24099
- const message = new Uint8Array(clampU64ToU32(msgLength));
24177
+ const target = safeAllocUint8Array(clampU64ToU32(targetLength));
24178
+ const message = safeAllocUint8Array(clampU64ToU32(msgLength));
24100
24179
  if (targetStart !== 0n) {
24101
24180
  memory.loadInto(target, targetStart);
24102
24181
  }
@@ -24115,6 +24194,7 @@ class log_LogHostCall {
24115
24194
 
24116
24195
 
24117
24196
 
24197
+
24118
24198
  const lookup_IN_OUT_REG = 7;
24119
24199
  /**
24120
24200
  * Lookup a preimage.
@@ -24125,7 +24205,7 @@ class lookup_Lookup {
24125
24205
  currentServiceId;
24126
24206
  account;
24127
24207
  index = tryAsHostCallIndex(2);
24128
- gasCost = tryAsSmallGas(10);
24208
+ basicGasCost = tryAsSmallGas(10);
24129
24209
  tracedRegisters = traceRegisters(lookup_IN_OUT_REG, 8, 9, 10, 11);
24130
24210
  constructor(currentServiceId, account) {
24131
24211
  this.currentServiceId = currentServiceId;
@@ -24157,7 +24237,7 @@ class lookup_Lookup {
24157
24237
  // NOTE [MaSo] this is ok to cast to number, because we are bounded by the
24158
24238
  // valueLength in both cases and valueLength is WC (4,000,000,000) + metadata
24159
24239
  // which is less than 2^32
24160
- 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));
24161
24241
  const memoryWriteResult = memory.storeFrom(destinationAddress, chunk);
24162
24242
  if (memoryWriteResult.isError) {
24163
24243
  return PvmExecution.Panic;
@@ -24178,6 +24258,7 @@ class lookup_Lookup {
24178
24258
 
24179
24259
 
24180
24260
 
24261
+
24181
24262
  const read_IN_OUT_REG = 7;
24182
24263
  /**
24183
24264
  * Read account storage.
@@ -24188,7 +24269,7 @@ class read_Read {
24188
24269
  currentServiceId;
24189
24270
  account;
24190
24271
  index = tryAsHostCallIndex(3);
24191
- gasCost = tryAsSmallGas(10);
24272
+ basicGasCost = tryAsSmallGas(10);
24192
24273
  tracedRegisters = traceRegisters(read_IN_OUT_REG, 8, 9, 10, 11, 12);
24193
24274
  constructor(currentServiceId, account) {
24194
24275
  this.currentServiceId = currentServiceId;
@@ -24205,7 +24286,7 @@ class read_Read {
24205
24286
  const destinationAddress = regs.get(10);
24206
24287
  const storageKeyLengthClamped = clampU64ToU32(storageKeyLength);
24207
24288
  // k
24208
- const rawKey = BytesBlob.blobFrom(new Uint8Array(storageKeyLengthClamped));
24289
+ const rawKey = BytesBlob.blobFrom(safeAllocUint8Array(storageKeyLengthClamped));
24209
24290
  const memoryReadResult = memory.loadInto(rawKey.raw, storageKeyStartAddress);
24210
24291
  if (memoryReadResult.isError) {
24211
24292
  logger.trace `READ(${serviceId}, ${rawKey}) <- PANIC`;
@@ -24223,7 +24304,7 @@ class read_Read {
24223
24304
  // NOTE [MaSo] this is ok to cast to number, because we are bounded by the
24224
24305
  // valueLength in both cases and valueLength is WC (4,000,000,000) + metadata
24225
24306
  // which is less than 2^32
24226
- 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));
24227
24308
  const memoryWriteResult = memory.storeFrom(destinationAddress, chunk);
24228
24309
  if (memoryWriteResult.isError) {
24229
24310
  logger.trace `READ(${serviceId}, ${rawKey}) <- PANIC`;
@@ -24258,7 +24339,7 @@ class write_Write {
24258
24339
  currentServiceId;
24259
24340
  account;
24260
24341
  index = tryAsHostCallIndex(4);
24261
- gasCost = tryAsSmallGas(10);
24342
+ basicGasCost = tryAsSmallGas(10);
24262
24343
  tracedRegisters = traceRegisters(write_IN_OUT_REG, 8, 9, 10);
24263
24344
  constructor(currentServiceId, account) {
24264
24345
  this.currentServiceId = currentServiceId;
@@ -24274,7 +24355,7 @@ class write_Write {
24274
24355
  // v_z
24275
24356
  const valueLength = regs.get(10);
24276
24357
  const storageKeyLengthClamped = clampU64ToU32(storageKeyLength);
24277
- const rawStorageKey = new Uint8Array(storageKeyLengthClamped);
24358
+ const rawStorageKey = safeAllocUint8Array(storageKeyLengthClamped);
24278
24359
  const keyLoadingResult = memory.loadInto(rawStorageKey, storageKeyStartAddress);
24279
24360
  if (keyLoadingResult.isError) {
24280
24361
  logger.trace `WRITE() <- PANIC`;
@@ -24283,7 +24364,7 @@ class write_Write {
24283
24364
  // k
24284
24365
  const storageKey = asOpaqueType(BytesBlob.blobFrom(rawStorageKey));
24285
24366
  const valueLengthClamped = clampU64ToU32(valueLength);
24286
- const value = new Uint8Array(valueLengthClamped);
24367
+ const value = safeAllocUint8Array(valueLengthClamped);
24287
24368
  const valueLoadingResult = memory.loadInto(value, valueStart);
24288
24369
  // Note [MaSo] this is ok to return bcs if valueLength is 0, then this panic won't happen
24289
24370
  if (valueLoadingResult.isError) {
@@ -25172,7 +25253,7 @@ function shuffling_fisherYatesShuffle(arr, entropy) {
25172
25253
  }
25173
25254
  function hashToNumberSequence(entropy, length) {
25174
25255
  const result = new Array(length);
25175
- const randomBytes = new Uint8Array(ENTROPY_BYTES + 4);
25256
+ const randomBytes = safeAllocUint8Array(ENTROPY_BYTES + 4);
25176
25257
  randomBytes.set(entropy.raw);
25177
25258
  for (let i = 0; i < length; i++) {
25178
25259
  randomBytes.set(u32AsLeBytes(tryAsU32(Math.floor(i / 8))), ENTROPY_BYTES);
@@ -26202,6 +26283,7 @@ class chain_stf_OnChain {
26202
26283
  authorization;
26203
26284
  // chapter 13: https://graypaper.fluffylabs.dev/#/68eaa1f/18b60118b601?v=0.6.4
26204
26285
  statistics;
26286
+ isReadyForNextEpoch = Promise.resolve(false);
26205
26287
  constructor(chainSpec, state, blocks, hasher) {
26206
26288
  this.chainSpec = chainSpec;
26207
26289
  this.state = state;
@@ -26220,6 +26302,14 @@ class chain_stf_OnChain {
26220
26302
  this.preimages = new Preimages(state);
26221
26303
  this.authorization = new Authorization(chainSpec, state);
26222
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
+ }
26223
26313
  async verifySeal(timeSlot, block) {
26224
26314
  const sealState = this.safrole.getSafroleSealState(timeSlot);
26225
26315
  return await this.safroleSeal.verifyHeaderSeal(block.header.view(), sealState);
@@ -26228,6 +26318,10 @@ class chain_stf_OnChain {
26228
26318
  const headerView = block.header.view();
26229
26319
  const header = block.header.materialize();
26230
26320
  const timeSlot = header.timeSlotIndex;
26321
+ // reset the epoch cache state
26322
+ if (headerView.epochMarker.view() !== null) {
26323
+ this.isReadyForNextEpoch = Promise.resolve(false);
26324
+ }
26231
26325
  // safrole seal
26232
26326
  let newEntropyHash;
26233
26327
  if (omitSealVerification) {
@@ -26692,6 +26786,7 @@ async function runWorkPackageTest(test, file) {
26692
26786
 
26693
26787
 
26694
26788
 
26789
+
26695
26790
  class MemoryChunkItem {
26696
26791
  static fromJson = {
26697
26792
  address: "number",
@@ -26749,10 +26844,10 @@ async function runPvmTest(testContent) {
26749
26844
  const endPageIndex = tryAsMemoryIndex(startPageIndex + page.length);
26750
26845
  const isWriteable = page["is-writable"];
26751
26846
  if (isWriteable) {
26752
- memoryBuilder.setWriteablePages(startPageIndex, endPageIndex, new Uint8Array(page.length));
26847
+ memoryBuilder.setWriteablePages(startPageIndex, endPageIndex, safeAllocUint8Array(page.length));
26753
26848
  }
26754
26849
  else {
26755
- memoryBuilder.setReadablePages(startPageIndex, endPageIndex, new Uint8Array(page.length));
26850
+ memoryBuilder.setReadablePages(startPageIndex, endPageIndex, safeAllocUint8Array(page.length));
26756
26851
  }
26757
26852
  }
26758
26853
  for (const memoryChunk of initialMemory) {
@@ -26804,7 +26899,7 @@ async function runPvmTest(testContent) {
26804
26899
  }, {});
26805
26900
  for (const [pageNumberAsString, memoryChunks] of Object.entries(expectedMemoryByPageNumber)) {
26806
26901
  const pageNumber = tryAsPageNumber(Number(pageNumberAsString));
26807
- const expectedPage = new Uint8Array(PAGE_SIZE);
26902
+ const expectedPage = safeAllocUint8Array(PAGE_SIZE);
26808
26903
  for (const memoryChunk of memoryChunks) {
26809
26904
  const pageIndex = memoryChunk.address % PAGE_SIZE;
26810
26905
  expectedPage.set(memoryChunk.contents, pageIndex);