@feelyourprotocol/block 8141.0.0

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 (133) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +466 -0
  3. package/dist/cjs/block/block.d.ts +147 -0
  4. package/dist/cjs/block/block.d.ts.map +1 -0
  5. package/dist/cjs/block/block.js +415 -0
  6. package/dist/cjs/block/block.js.map +1 -0
  7. package/dist/cjs/block/constructors.d.ts +77 -0
  8. package/dist/cjs/block/constructors.d.ts.map +1 -0
  9. package/dist/cjs/block/constructors.js +298 -0
  10. package/dist/cjs/block/constructors.js.map +1 -0
  11. package/dist/cjs/block/index.d.ts +3 -0
  12. package/dist/cjs/block/index.d.ts.map +1 -0
  13. package/dist/cjs/block/index.js +19 -0
  14. package/dist/cjs/block/index.js.map +1 -0
  15. package/dist/cjs/consensus/clique.d.ts +52 -0
  16. package/dist/cjs/consensus/clique.d.ts.map +1 -0
  17. package/dist/cjs/consensus/clique.js +144 -0
  18. package/dist/cjs/consensus/clique.js.map +1 -0
  19. package/dist/cjs/consensus/ethash.d.ts +9 -0
  20. package/dist/cjs/consensus/ethash.d.ts.map +1 -0
  21. package/dist/cjs/consensus/ethash.js +13 -0
  22. package/dist/cjs/consensus/ethash.js.map +1 -0
  23. package/dist/cjs/consensus/index.d.ts +3 -0
  24. package/dist/cjs/consensus/index.d.ts.map +1 -0
  25. package/dist/cjs/consensus/index.js +29 -0
  26. package/dist/cjs/consensus/index.js.map +1 -0
  27. package/dist/cjs/from-beacon-payload.d.ts +36 -0
  28. package/dist/cjs/from-beacon-payload.d.ts.map +1 -0
  29. package/dist/cjs/from-beacon-payload.js +48 -0
  30. package/dist/cjs/from-beacon-payload.js.map +1 -0
  31. package/dist/cjs/header/constructors.d.ts +39 -0
  32. package/dist/cjs/header/constructors.d.ts.map +1 -0
  33. package/dist/cjs/header/constructors.js +127 -0
  34. package/dist/cjs/header/constructors.js.map +1 -0
  35. package/dist/cjs/header/header.d.ts +134 -0
  36. package/dist/cjs/header/header.d.ts.map +1 -0
  37. package/dist/cjs/header/header.js +699 -0
  38. package/dist/cjs/header/header.js.map +1 -0
  39. package/dist/cjs/header/index.d.ts +3 -0
  40. package/dist/cjs/header/index.d.ts.map +1 -0
  41. package/dist/cjs/header/index.js +19 -0
  42. package/dist/cjs/header/index.js.map +1 -0
  43. package/dist/cjs/helpers.d.ts +59 -0
  44. package/dist/cjs/helpers.d.ts.map +1 -0
  45. package/dist/cjs/helpers.js +172 -0
  46. package/dist/cjs/helpers.js.map +1 -0
  47. package/dist/cjs/index.d.ts +8 -0
  48. package/dist/cjs/index.d.ts.map +1 -0
  49. package/dist/cjs/index.js +31 -0
  50. package/dist/cjs/index.js.map +1 -0
  51. package/dist/cjs/package.json +3 -0
  52. package/dist/cjs/params.d.ts +3 -0
  53. package/dist/cjs/params.d.ts.map +1 -0
  54. package/dist/cjs/params.js +97 -0
  55. package/dist/cjs/params.js.map +1 -0
  56. package/dist/cjs/types.d.ts +228 -0
  57. package/dist/cjs/types.d.ts.map +1 -0
  58. package/dist/cjs/types.js +3 -0
  59. package/dist/cjs/types.js.map +1 -0
  60. package/dist/esm/block/block.d.ts +147 -0
  61. package/dist/esm/block/block.d.ts.map +1 -0
  62. package/dist/esm/block/block.js +411 -0
  63. package/dist/esm/block/block.js.map +1 -0
  64. package/dist/esm/block/constructors.d.ts +77 -0
  65. package/dist/esm/block/constructors.d.ts.map +1 -0
  66. package/dist/esm/block/constructors.js +286 -0
  67. package/dist/esm/block/constructors.js.map +1 -0
  68. package/dist/esm/block/index.d.ts +3 -0
  69. package/dist/esm/block/index.d.ts.map +1 -0
  70. package/dist/esm/block/index.js +3 -0
  71. package/dist/esm/block/index.js.map +1 -0
  72. package/dist/esm/consensus/clique.d.ts +52 -0
  73. package/dist/esm/consensus/clique.d.ts.map +1 -0
  74. package/dist/esm/consensus/clique.js +132 -0
  75. package/dist/esm/consensus/clique.js.map +1 -0
  76. package/dist/esm/consensus/ethash.d.ts +9 -0
  77. package/dist/esm/consensus/ethash.d.ts.map +1 -0
  78. package/dist/esm/consensus/ethash.js +10 -0
  79. package/dist/esm/consensus/ethash.js.map +1 -0
  80. package/dist/esm/consensus/index.d.ts +3 -0
  81. package/dist/esm/consensus/index.d.ts.map +1 -0
  82. package/dist/esm/consensus/index.js +3 -0
  83. package/dist/esm/consensus/index.js.map +1 -0
  84. package/dist/esm/from-beacon-payload.d.ts +36 -0
  85. package/dist/esm/from-beacon-payload.d.ts.map +1 -0
  86. package/dist/esm/from-beacon-payload.js +45 -0
  87. package/dist/esm/from-beacon-payload.js.map +1 -0
  88. package/dist/esm/header/constructors.d.ts +39 -0
  89. package/dist/esm/header/constructors.d.ts.map +1 -0
  90. package/dist/esm/header/constructors.js +120 -0
  91. package/dist/esm/header/constructors.js.map +1 -0
  92. package/dist/esm/header/header.d.ts +134 -0
  93. package/dist/esm/header/header.d.ts.map +1 -0
  94. package/dist/esm/header/header.js +695 -0
  95. package/dist/esm/header/header.js.map +1 -0
  96. package/dist/esm/header/index.d.ts +3 -0
  97. package/dist/esm/header/index.d.ts.map +1 -0
  98. package/dist/esm/header/index.js +3 -0
  99. package/dist/esm/header/index.js.map +1 -0
  100. package/dist/esm/helpers.d.ts +59 -0
  101. package/dist/esm/helpers.d.ts.map +1 -0
  102. package/dist/esm/helpers.js +161 -0
  103. package/dist/esm/helpers.js.map +1 -0
  104. package/dist/esm/index.d.ts +8 -0
  105. package/dist/esm/index.d.ts.map +1 -0
  106. package/dist/esm/index.js +8 -0
  107. package/dist/esm/index.js.map +1 -0
  108. package/dist/esm/package.json +3 -0
  109. package/dist/esm/params.d.ts +3 -0
  110. package/dist/esm/params.d.ts.map +1 -0
  111. package/dist/esm/params.js +94 -0
  112. package/dist/esm/params.js.map +1 -0
  113. package/dist/esm/types.d.ts +228 -0
  114. package/dist/esm/types.d.ts.map +1 -0
  115. package/dist/esm/types.js +2 -0
  116. package/dist/esm/types.js.map +1 -0
  117. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  118. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  119. package/package.json +75 -0
  120. package/src/block/block.ts +526 -0
  121. package/src/block/constructors.ts +407 -0
  122. package/src/block/index.ts +2 -0
  123. package/src/consensus/clique.ts +171 -0
  124. package/src/consensus/ethash.ts +11 -0
  125. package/src/consensus/index.ts +12 -0
  126. package/src/from-beacon-payload.ts +82 -0
  127. package/src/header/constructors.ts +169 -0
  128. package/src/header/header.ts +890 -0
  129. package/src/header/index.ts +2 -0
  130. package/src/helpers.ts +223 -0
  131. package/src/index.ts +13 -0
  132. package/src/params.ts +95 -0
  133. package/src/types.ts +254 -0
@@ -0,0 +1,890 @@
1
+ import { Common, ConsensusAlgorithm, ConsensusType, Hardfork, Mainnet } from '@feelyourprotocol/common'
2
+ import { RLP } from '@feelyourprotocol/rlp'
3
+ import {
4
+ Address,
5
+ BIGINT_0,
6
+ BIGINT_1,
7
+ BIGINT_2,
8
+ BIGINT_7,
9
+ EthereumJSErrorWithoutCode,
10
+ KECCAK256_RLP,
11
+ KECCAK256_RLP_ARRAY,
12
+ SHA256_NULL,
13
+ TypeOutput,
14
+ bigIntToHex,
15
+ bigIntToUnpaddedBytes,
16
+ bytesToHex,
17
+ bytesToUtf8,
18
+ createZeroAddress,
19
+ equalsBytes,
20
+ hexToBytes,
21
+ toType,
22
+ } from '@feelyourprotocol/util'
23
+ import { keccak_256 } from '@noble/hashes/sha3.js'
24
+
25
+ import {
26
+ CLIQUE_EXTRA_SEAL,
27
+ CLIQUE_EXTRA_VANITY,
28
+ cliqueIsEpochTransition,
29
+ } from '../consensus/clique.ts'
30
+ import { computeBlobGasPrice } from '../helpers.ts'
31
+ import { paramsBlock } from '../params.ts'
32
+
33
+ import type { BlockHeaderBytes, BlockOptions, HeaderData, JSONHeader } from '../types.ts'
34
+
35
+ interface HeaderCache {
36
+ hash: Uint8Array | undefined
37
+ }
38
+
39
+ const DEFAULT_GAS_LIMIT = BigInt('0xffffffffffffff')
40
+
41
+ /**
42
+ * An object that represents the block header.
43
+ */
44
+ export class BlockHeader {
45
+ public readonly parentHash: Uint8Array
46
+ public readonly uncleHash: Uint8Array
47
+ public readonly coinbase: Address
48
+ public readonly stateRoot: Uint8Array
49
+ public readonly transactionsTrie: Uint8Array
50
+ public readonly receiptTrie: Uint8Array
51
+ public readonly logsBloom: Uint8Array
52
+ public readonly difficulty: bigint
53
+ public readonly number: bigint
54
+ public readonly gasLimit: bigint
55
+ public readonly gasUsed: bigint
56
+ public readonly timestamp: bigint
57
+ public readonly extraData: Uint8Array
58
+ public readonly mixHash: Uint8Array
59
+ public readonly nonce: Uint8Array
60
+ public readonly baseFeePerGas?: bigint
61
+ public readonly withdrawalsRoot?: Uint8Array
62
+ public readonly blobGasUsed?: bigint
63
+ public readonly excessBlobGas?: bigint
64
+ public readonly parentBeaconBlockRoot?: Uint8Array
65
+ public readonly requestsHash?: Uint8Array
66
+ public readonly blockAccessListHash?: Uint8Array
67
+ public readonly slotNumber?: bigint
68
+
69
+ public readonly common: Common
70
+
71
+ protected keccakFunction: (msg: Uint8Array) => Uint8Array
72
+
73
+ protected cache: HeaderCache = {
74
+ hash: undefined,
75
+ }
76
+
77
+ /**
78
+ * EIP-4399: After merge to PoS, `mixHash` supplanted as `prevRandao`
79
+ */
80
+ get prevRandao() {
81
+ if (!this.common.isActivatedEIP(4399)) {
82
+ const msg = this._errorMsg(
83
+ 'The prevRandao parameter can only be accessed when EIP-4399 is activated',
84
+ )
85
+ throw EthereumJSErrorWithoutCode(msg)
86
+ }
87
+ return this.mixHash
88
+ }
89
+
90
+ /**
91
+ * This constructor takes the values, validates them, assigns them and freezes the object.
92
+ *
93
+ * @deprecated Use the public static factory methods to assist in creating a Header object from
94
+ * varying data types. For a default empty header, use {@link createBlockHeader}.
95
+ *
96
+ */
97
+ constructor(headerData: HeaderData, opts: BlockOptions = {}) {
98
+ if (opts.common) {
99
+ this.common = opts.common.copy()
100
+ } else {
101
+ this.common = new Common({
102
+ chain: Mainnet, // default
103
+ })
104
+ }
105
+ this.common.updateParams(opts.params ?? paramsBlock)
106
+
107
+ this.keccakFunction = this.common.customCrypto.keccak256 ?? keccak_256
108
+
109
+ const skipValidateConsensusFormat = opts.skipConsensusFormatValidation ?? false
110
+
111
+ const defaults = {
112
+ parentHash: new Uint8Array(32),
113
+ uncleHash: KECCAK256_RLP_ARRAY,
114
+ coinbase: createZeroAddress(),
115
+ stateRoot: new Uint8Array(32),
116
+ transactionsTrie: KECCAK256_RLP,
117
+ receiptTrie: KECCAK256_RLP,
118
+ logsBloom: new Uint8Array(256),
119
+ difficulty: BIGINT_0,
120
+ number: BIGINT_0,
121
+ gasLimit: DEFAULT_GAS_LIMIT,
122
+ gasUsed: BIGINT_0,
123
+ timestamp: BIGINT_0,
124
+ extraData: new Uint8Array(0),
125
+ mixHash: new Uint8Array(32),
126
+ nonce: new Uint8Array(8),
127
+ }
128
+
129
+ const parentHash = toType(headerData.parentHash, TypeOutput.Uint8Array) ?? defaults.parentHash
130
+ const uncleHash = toType(headerData.uncleHash, TypeOutput.Uint8Array) ?? defaults.uncleHash
131
+ const coinbase = new Address(
132
+ toType(headerData.coinbase ?? defaults.coinbase, TypeOutput.Uint8Array),
133
+ )
134
+ const stateRoot = toType(headerData.stateRoot, TypeOutput.Uint8Array) ?? defaults.stateRoot
135
+ const transactionsTrie =
136
+ toType(headerData.transactionsTrie, TypeOutput.Uint8Array) ?? defaults.transactionsTrie
137
+ const receiptTrie =
138
+ toType(headerData.receiptTrie, TypeOutput.Uint8Array) ?? defaults.receiptTrie
139
+ const logsBloom = toType(headerData.logsBloom, TypeOutput.Uint8Array) ?? defaults.logsBloom
140
+ const difficulty = toType(headerData.difficulty, TypeOutput.BigInt) ?? defaults.difficulty
141
+ const number = toType(headerData.number, TypeOutput.BigInt) ?? defaults.number
142
+ const gasLimit = toType(headerData.gasLimit, TypeOutput.BigInt) ?? defaults.gasLimit
143
+ const gasUsed = toType(headerData.gasUsed, TypeOutput.BigInt) ?? defaults.gasUsed
144
+ const timestamp = toType(headerData.timestamp, TypeOutput.BigInt) ?? defaults.timestamp
145
+ const extraData = toType(headerData.extraData, TypeOutput.Uint8Array) ?? defaults.extraData
146
+ const mixHash = toType(headerData.mixHash, TypeOutput.Uint8Array) ?? defaults.mixHash
147
+ const nonce = toType(headerData.nonce, TypeOutput.Uint8Array) ?? defaults.nonce
148
+
149
+ const setHardfork = opts.setHardfork ?? false
150
+ if (setHardfork === true) {
151
+ this.common.setHardforkBy({
152
+ blockNumber: number,
153
+ timestamp,
154
+ })
155
+ }
156
+
157
+ // Hardfork defaults which couldn't be paired with earlier defaults
158
+ const hardforkDefaults = {
159
+ baseFeePerGas: this.common.isActivatedEIP(1559)
160
+ ? number === this.common.hardforkBlock(Hardfork.London)
161
+ ? this.common.param('initialBaseFee')
162
+ : BIGINT_7
163
+ : undefined,
164
+ withdrawalsRoot: this.common.isActivatedEIP(4895) ? KECCAK256_RLP : undefined,
165
+ blobGasUsed: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined,
166
+ excessBlobGas: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined,
167
+ parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? new Uint8Array(32) : undefined,
168
+ // Note: as of devnet-4 we stub the null SHA256 hash, but for devnet5 this will actually
169
+ // be the correct hash for empty requests.
170
+ requestsHash: this.common.isActivatedEIP(7685) ? SHA256_NULL : undefined,
171
+ blockAccessListHash: this.common.isActivatedEIP(7928) ? new Uint8Array(32) : undefined,
172
+ slotNumber: this.common.isActivatedEIP(7843) ? BIGINT_0 : undefined,
173
+ }
174
+
175
+ const baseFeePerGas =
176
+ toType(headerData.baseFeePerGas, TypeOutput.BigInt) ?? hardforkDefaults.baseFeePerGas
177
+ const withdrawalsRoot =
178
+ toType(headerData.withdrawalsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.withdrawalsRoot
179
+ const blobGasUsed =
180
+ toType(headerData.blobGasUsed, TypeOutput.BigInt) ?? hardforkDefaults.blobGasUsed
181
+ const excessBlobGas =
182
+ toType(headerData.excessBlobGas, TypeOutput.BigInt) ?? hardforkDefaults.excessBlobGas
183
+ const parentBeaconBlockRoot =
184
+ toType(headerData.parentBeaconBlockRoot, TypeOutput.Uint8Array) ??
185
+ hardforkDefaults.parentBeaconBlockRoot
186
+ const requestsHash =
187
+ toType(headerData.requestsHash, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsHash
188
+ const blockAccessListHash =
189
+ toType(headerData.blockAccessListHash, TypeOutput.Uint8Array) ??
190
+ hardforkDefaults.blockAccessListHash
191
+ const slotNumber =
192
+ toType(headerData.slotNumber, TypeOutput.BigInt) ?? hardforkDefaults.slotNumber
193
+
194
+ if (!this.common.isActivatedEIP(1559) && baseFeePerGas !== undefined) {
195
+ throw EthereumJSErrorWithoutCode(
196
+ 'A base fee for a block can only be set with EIP1559 being activated',
197
+ )
198
+ }
199
+
200
+ if (!this.common.isActivatedEIP(4895) && withdrawalsRoot !== undefined) {
201
+ throw EthereumJSErrorWithoutCode(
202
+ 'A withdrawalsRoot for a header can only be provided with EIP4895 being activated',
203
+ )
204
+ }
205
+
206
+ if (!this.common.isActivatedEIP(4844)) {
207
+ if (blobGasUsed !== undefined) {
208
+ throw EthereumJSErrorWithoutCode(
209
+ 'blob gas used can only be provided with EIP4844 activated',
210
+ )
211
+ }
212
+
213
+ if (excessBlobGas !== undefined) {
214
+ throw EthereumJSErrorWithoutCode(
215
+ 'excess blob gas can only be provided with EIP4844 activated',
216
+ )
217
+ }
218
+ }
219
+
220
+ if (!this.common.isActivatedEIP(4788) && parentBeaconBlockRoot !== undefined) {
221
+ throw EthereumJSErrorWithoutCode(
222
+ 'A parentBeaconBlockRoot for a header can only be provided with EIP4788 being activated',
223
+ )
224
+ }
225
+
226
+ if (!this.common.isActivatedEIP(7685) && requestsHash !== undefined) {
227
+ throw EthereumJSErrorWithoutCode('requestsHash can only be provided with EIP 7685 activated')
228
+ }
229
+
230
+ if (!this.common.isActivatedEIP(7928) && blockAccessListHash !== undefined) {
231
+ throw EthereumJSErrorWithoutCode(
232
+ 'blockAccessListHash can only be provided with EIP 7928 activated',
233
+ )
234
+ }
235
+
236
+ if (!this.common.isActivatedEIP(7843) && slotNumber !== undefined) {
237
+ throw EthereumJSErrorWithoutCode('slotNumber can only be provided with EIP 7843 activated')
238
+ }
239
+
240
+ this.parentHash = parentHash
241
+ this.uncleHash = uncleHash
242
+ this.coinbase = coinbase
243
+ this.stateRoot = stateRoot
244
+ this.transactionsTrie = transactionsTrie
245
+ this.receiptTrie = receiptTrie
246
+ this.logsBloom = logsBloom
247
+ this.difficulty = difficulty
248
+ this.number = number
249
+ this.gasLimit = gasLimit
250
+ this.gasUsed = gasUsed
251
+ this.timestamp = timestamp
252
+ this.extraData = extraData
253
+ this.mixHash = mixHash
254
+ this.nonce = nonce
255
+ this.baseFeePerGas = baseFeePerGas
256
+ this.withdrawalsRoot = withdrawalsRoot
257
+ this.blobGasUsed = blobGasUsed
258
+ this.excessBlobGas = excessBlobGas
259
+ this.parentBeaconBlockRoot = parentBeaconBlockRoot
260
+ this.requestsHash = requestsHash
261
+ this.blockAccessListHash = blockAccessListHash
262
+ this.slotNumber = slotNumber
263
+ this._genericFormatValidation()
264
+ this._validateDAOExtraData()
265
+
266
+ // Now we have set all the values of this Header, we possibly have set a dummy
267
+ // `difficulty` value (defaults to 0). If we have a `calcDifficultyFromHeader`
268
+ // block option parameter, we instead set difficulty to this value.
269
+ if (
270
+ opts.calcDifficultyFromHeader &&
271
+ this.common.consensusAlgorithm() === ConsensusAlgorithm.Ethash
272
+ ) {
273
+ this.difficulty = this.ethashCanonicalDifficulty(opts.calcDifficultyFromHeader)
274
+ }
275
+
276
+ // Validate consensus format after block is sealed (if applicable) so extraData checks will pass
277
+ if (skipValidateConsensusFormat === false) this._consensusFormatValidation()
278
+
279
+ const freeze = opts?.freeze ?? true
280
+ if (freeze) {
281
+ Object.freeze(this)
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Validates correct buffer lengths, throws if invalid.
287
+ */
288
+ protected _genericFormatValidation() {
289
+ const { parentHash, stateRoot, transactionsTrie, receiptTrie, mixHash, nonce } = this
290
+
291
+ if (parentHash.length !== 32) {
292
+ const msg = this._errorMsg(`parentHash must be 32 bytes, received ${parentHash.length} bytes`)
293
+ throw EthereumJSErrorWithoutCode(msg)
294
+ }
295
+ if (stateRoot.length !== 32) {
296
+ const msg = this._errorMsg(`stateRoot must be 32 bytes, received ${stateRoot.length} bytes`)
297
+ throw EthereumJSErrorWithoutCode(msg)
298
+ }
299
+ if (transactionsTrie.length !== 32) {
300
+ const msg = this._errorMsg(
301
+ `transactionsTrie must be 32 bytes, received ${transactionsTrie.length} bytes`,
302
+ )
303
+ throw EthereumJSErrorWithoutCode(msg)
304
+ }
305
+ if (receiptTrie.length !== 32) {
306
+ const msg = this._errorMsg(
307
+ `receiptTrie must be 32 bytes, received ${receiptTrie.length} bytes`,
308
+ )
309
+ throw EthereumJSErrorWithoutCode(msg)
310
+ }
311
+ if (mixHash.length !== 32) {
312
+ const msg = this._errorMsg(`mixHash must be 32 bytes, received ${mixHash.length} bytes`)
313
+ throw EthereumJSErrorWithoutCode(msg)
314
+ }
315
+
316
+ if (nonce.length !== 8) {
317
+ const msg = this._errorMsg(`nonce must be 8 bytes, received ${nonce.length} bytes`)
318
+ throw EthereumJSErrorWithoutCode(msg)
319
+ }
320
+
321
+ // check if the block used too much gas
322
+ if (this.gasUsed > this.gasLimit) {
323
+ const msg = this._errorMsg(
324
+ `Invalid block: too much gas used. Used: ${this.gasUsed}, gas limit: ${this.gasLimit}`,
325
+ )
326
+ throw EthereumJSErrorWithoutCode(msg)
327
+ }
328
+
329
+ // Validation for EIP-1559 blocks
330
+ if (this.common.isActivatedEIP(1559)) {
331
+ if (typeof this.baseFeePerGas !== 'bigint') {
332
+ const msg = this._errorMsg('EIP1559 block has no base fee field')
333
+ throw EthereumJSErrorWithoutCode(msg)
334
+ }
335
+ const londonHfBlock = this.common.hardforkBlock(Hardfork.London)
336
+ if (
337
+ typeof londonHfBlock === 'bigint' &&
338
+ londonHfBlock !== BIGINT_0 &&
339
+ this.number === londonHfBlock
340
+ ) {
341
+ const initialBaseFee = this.common.param('initialBaseFee')
342
+ if (this.baseFeePerGas !== initialBaseFee) {
343
+ const msg = this._errorMsg('Initial EIP1559 block does not have initial base fee')
344
+ throw EthereumJSErrorWithoutCode(msg)
345
+ }
346
+ }
347
+ }
348
+
349
+ if (this.common.isActivatedEIP(4895)) {
350
+ if (this.withdrawalsRoot === undefined) {
351
+ const msg = this._errorMsg('EIP4895 block has no withdrawalsRoot field')
352
+ throw EthereumJSErrorWithoutCode(msg)
353
+ }
354
+ if (this.withdrawalsRoot?.length !== 32) {
355
+ const msg = this._errorMsg(
356
+ `withdrawalsRoot must be 32 bytes, received ${this.withdrawalsRoot!.length} bytes`,
357
+ )
358
+ throw EthereumJSErrorWithoutCode(msg)
359
+ }
360
+ }
361
+
362
+ if (this.common.isActivatedEIP(4788)) {
363
+ if (this.parentBeaconBlockRoot === undefined) {
364
+ const msg = this._errorMsg('EIP4788 block has no parentBeaconBlockRoot field')
365
+ throw EthereumJSErrorWithoutCode(msg)
366
+ }
367
+ if (this.parentBeaconBlockRoot?.length !== 32) {
368
+ const msg = this._errorMsg(
369
+ `parentBeaconBlockRoot must be 32 bytes, received ${
370
+ this.parentBeaconBlockRoot!.length
371
+ } bytes`,
372
+ )
373
+ throw EthereumJSErrorWithoutCode(msg)
374
+ }
375
+ }
376
+
377
+ if (this.common.isActivatedEIP(7685)) {
378
+ if (this.requestsHash === undefined) {
379
+ const msg = this._errorMsg('EIP7685 block has no requestsHash field')
380
+ throw EthereumJSErrorWithoutCode(msg)
381
+ }
382
+ }
383
+
384
+ if (this.common.isActivatedEIP(7928)) {
385
+ if (this.blockAccessListHash === undefined) {
386
+ const msg = this._errorMsg('EIP7928 block has no blockAccessListHash field')
387
+ throw EthereumJSErrorWithoutCode(msg)
388
+ }
389
+ if (this.blockAccessListHash?.length !== 32) {
390
+ const msg = this._errorMsg(
391
+ `blockAccessListHash must be 32 bytes, received ${this.blockAccessListHash!.length} bytes`,
392
+ )
393
+ throw EthereumJSErrorWithoutCode(msg)
394
+ }
395
+ }
396
+
397
+ if (this.common.isActivatedEIP(7843)) {
398
+ if (this.slotNumber === undefined) {
399
+ const msg = this._errorMsg('EIP7843 block has no slotNumber field')
400
+ throw EthereumJSErrorWithoutCode(msg)
401
+ }
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Checks static parameters related to consensus algorithm
407
+ * @throws if any check fails
408
+ */
409
+ protected _consensusFormatValidation() {
410
+ const { nonce, uncleHash, difficulty, extraData, number } = this
411
+
412
+ // Consensus type dependent checks
413
+ if (this.common.consensusAlgorithm() === ConsensusAlgorithm.Ethash) {
414
+ // PoW/Ethash
415
+ if (number > BIGINT_0 && this.extraData.length > this.common.param('maxExtraDataSize')) {
416
+ // Check length of data on all post-genesis blocks
417
+ const msg = this._errorMsg('invalid amount of extra data')
418
+ throw EthereumJSErrorWithoutCode(msg)
419
+ }
420
+ }
421
+ if (this.common.consensusAlgorithm() === ConsensusAlgorithm.Clique) {
422
+ // PoA/Clique
423
+ const minLength = CLIQUE_EXTRA_VANITY + CLIQUE_EXTRA_SEAL
424
+ if (!cliqueIsEpochTransition(this)) {
425
+ // ExtraData length on epoch transition
426
+ if (this.extraData.length !== minLength) {
427
+ const msg = this._errorMsg(
428
+ `extraData must be ${minLength} bytes on non-epoch transition blocks, received ${this.extraData.length} bytes`,
429
+ )
430
+ throw EthereumJSErrorWithoutCode(msg)
431
+ }
432
+ } else {
433
+ const signerLength = this.extraData.length - minLength
434
+ if (signerLength % 20 !== 0) {
435
+ const msg = this._errorMsg(
436
+ `invalid signer list length in extraData, received signer length of ${signerLength} (not divisible by 20)`,
437
+ )
438
+ throw EthereumJSErrorWithoutCode(msg)
439
+ }
440
+ // coinbase (beneficiary) on epoch transition
441
+ if (!this.coinbase.isZero()) {
442
+ const msg = this._errorMsg(
443
+ `coinbase must be filled with zeros on epoch transition blocks, received ${this.coinbase}`,
444
+ )
445
+ throw EthereumJSErrorWithoutCode(msg)
446
+ }
447
+ }
448
+ // MixHash format
449
+ if (!equalsBytes(this.mixHash, new Uint8Array(32))) {
450
+ const msg = this._errorMsg(`mixHash must be filled with zeros, received ${this.mixHash}`)
451
+ throw EthereumJSErrorWithoutCode(msg)
452
+ }
453
+ }
454
+ // Validation for PoS blocks (EIP-3675)
455
+ if (this.common.consensusType() === ConsensusType.ProofOfStake) {
456
+ let error = false
457
+ let errorMsg = ''
458
+
459
+ if (!equalsBytes(uncleHash, KECCAK256_RLP_ARRAY)) {
460
+ errorMsg += `, uncleHash: ${bytesToHex(uncleHash)} (expected: ${bytesToHex(
461
+ KECCAK256_RLP_ARRAY,
462
+ )})`
463
+ error = true
464
+ }
465
+ if (number !== BIGINT_0) {
466
+ // Skip difficulty, nonce, and extraData check for PoS genesis block as genesis block may have non-zero difficulty (if TD is > 0)
467
+ if (difficulty !== BIGINT_0) {
468
+ errorMsg += `, difficulty: ${difficulty} (expected: 0)`
469
+ error = true
470
+ }
471
+ if (extraData.length > 32) {
472
+ errorMsg += `, extraData: ${bytesToHex(
473
+ extraData,
474
+ )} (cannot exceed 32 bytes length, received ${extraData.length} bytes)`
475
+ error = true
476
+ }
477
+ if (!equalsBytes(nonce, new Uint8Array(8))) {
478
+ errorMsg += `, nonce: ${bytesToHex(nonce)} (expected: ${bytesToHex(new Uint8Array(8))})`
479
+ error = true
480
+ }
481
+ }
482
+ if (error) {
483
+ const msg = this._errorMsg(`Invalid PoS block: ${errorMsg}`)
484
+ throw EthereumJSErrorWithoutCode(msg)
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Validates if the block gasLimit remains in the boundaries set by the protocol.
491
+ * Throws if out of bounds.
492
+ *
493
+ * @param parentBlockHeader - the header from the parent `Block` of this header
494
+ */
495
+ validateGasLimit(parentBlockHeader: BlockHeader) {
496
+ let parentGasLimit = parentBlockHeader.gasLimit
497
+ // EIP-1559: assume double the parent gas limit on fork block
498
+ // to adopt to the new gas target centered logic
499
+ const londonHardforkBlock = this.common.hardforkBlock(Hardfork.London)
500
+ if (
501
+ typeof londonHardforkBlock === 'bigint' &&
502
+ londonHardforkBlock !== BIGINT_0 &&
503
+ this.number === londonHardforkBlock
504
+ ) {
505
+ const elasticity = this.common.param('elasticityMultiplier')
506
+ parentGasLimit = parentGasLimit * elasticity
507
+ }
508
+ const gasLimit = this.gasLimit
509
+
510
+ const a = parentGasLimit / this.common.param('gasLimitBoundDivisor')
511
+ const maxGasLimit = parentGasLimit + a
512
+ const minGasLimit = parentGasLimit - a
513
+
514
+ if (gasLimit >= maxGasLimit) {
515
+ const msg = this._errorMsg(
516
+ `gas limit increased too much. Gas limit: ${gasLimit}, max gas limit: ${maxGasLimit}`,
517
+ )
518
+ throw EthereumJSErrorWithoutCode(msg)
519
+ }
520
+
521
+ if (gasLimit <= minGasLimit) {
522
+ const msg = this._errorMsg(
523
+ `gas limit decreased too much. Gas limit: ${gasLimit}, min gas limit: ${minGasLimit}`,
524
+ )
525
+ throw EthereumJSErrorWithoutCode(msg)
526
+ }
527
+
528
+ if (gasLimit < this.common.param('minGasLimit')) {
529
+ const msg = this._errorMsg(
530
+ `gas limit decreased below minimum gas limit. Gas limit: ${gasLimit}, minimum gas limit: ${this.common.param(
531
+ 'minGasLimit',
532
+ )}`,
533
+ )
534
+ throw EthereumJSErrorWithoutCode(msg)
535
+ }
536
+ }
537
+
538
+ /**
539
+ * Calculates the base fee for a potential next block
540
+ */
541
+ public calcNextBaseFee(): bigint {
542
+ if (!this.common.isActivatedEIP(1559)) {
543
+ const msg = this._errorMsg(
544
+ 'calcNextBaseFee() can only be called with EIP1559 being activated',
545
+ )
546
+ throw EthereumJSErrorWithoutCode(msg)
547
+ }
548
+ let nextBaseFee: bigint
549
+ const elasticity = this.common.param('elasticityMultiplier')
550
+ const parentGasTarget = this.gasLimit / elasticity
551
+
552
+ if (parentGasTarget === this.gasUsed) {
553
+ nextBaseFee = this.baseFeePerGas!
554
+ } else if (this.gasUsed > parentGasTarget) {
555
+ const gasUsedDelta = this.gasUsed - parentGasTarget
556
+ const baseFeeMaxChangeDenominator = this.common.param('baseFeeMaxChangeDenominator')
557
+
558
+ const calculatedDelta =
559
+ (this.baseFeePerGas! * gasUsedDelta) / parentGasTarget / baseFeeMaxChangeDenominator
560
+ nextBaseFee = (calculatedDelta > BIGINT_1 ? calculatedDelta : BIGINT_1) + this.baseFeePerGas!
561
+ } else {
562
+ const gasUsedDelta = parentGasTarget - this.gasUsed
563
+ const baseFeeMaxChangeDenominator = this.common.param('baseFeeMaxChangeDenominator')
564
+
565
+ const calculatedDelta =
566
+ (this.baseFeePerGas! * gasUsedDelta) / parentGasTarget / baseFeeMaxChangeDenominator
567
+ nextBaseFee =
568
+ this.baseFeePerGas! - calculatedDelta > BIGINT_0
569
+ ? this.baseFeePerGas! - calculatedDelta
570
+ : BIGINT_0
571
+ }
572
+ return nextBaseFee
573
+ }
574
+
575
+ /**
576
+ * Returns the price per unit of blob gas for a blob transaction in the current/pending block
577
+ * @returns the price in gwei per unit of blob gas spent
578
+ */
579
+ getBlobGasPrice(): bigint {
580
+ if (this.excessBlobGas === undefined) {
581
+ throw EthereumJSErrorWithoutCode('header must have excessBlobGas field populated')
582
+ }
583
+ return computeBlobGasPrice(this.excessBlobGas, this.common)
584
+ }
585
+
586
+ /**
587
+ * Returns the total fee for blob gas spent for including blobs in block.
588
+ *
589
+ * @param numBlobs number of blobs in the transaction/block
590
+ * @returns the total blob gas fee for numBlobs blobs
591
+ */
592
+ calcDataFee(numBlobs: number): bigint {
593
+ const blobGasPerBlob = this.common.param('blobGasPerBlob')
594
+ const blobGasUsed = blobGasPerBlob * BigInt(numBlobs)
595
+
596
+ const blobGasPrice = this.getBlobGasPrice()
597
+ return blobGasUsed * blobGasPrice
598
+ }
599
+
600
+ /**
601
+ * Calculates the excess blob gas for next (hopefully) post EIP 4844 block.
602
+ */
603
+ public calcNextExcessBlobGas(childCommon: Common): bigint {
604
+ const excessBlobGas = this.excessBlobGas ?? BIGINT_0
605
+ const blobGasUsed = this.blobGasUsed ?? BIGINT_0
606
+
607
+ const { targetBlobGasPerBlock: targetPerBlock, maxBlobGasPerBlock: maxPerBlock } =
608
+ childCommon.getBlobGasSchedule()
609
+
610
+ // Early exit (strictly < per spec)
611
+ if (excessBlobGas + blobGasUsed < targetPerBlock) {
612
+ return BIGINT_0
613
+ }
614
+
615
+ // EIP-7918 reserve price check
616
+ if (childCommon.isActivatedEIP(7918)) {
617
+ const blobBaseCost = childCommon.param('blobBaseCost')
618
+ const gasPerBlob = childCommon.param('blobGasPerBlob')
619
+ const baseFee = this.baseFeePerGas ?? BIGINT_0
620
+ const blobFee = computeBlobGasPrice(excessBlobGas, childCommon)
621
+
622
+ if (blobBaseCost * baseFee > gasPerBlob * blobFee) {
623
+ const increase = (blobGasUsed * (maxPerBlock - targetPerBlock)) / maxPerBlock
624
+ return excessBlobGas + increase
625
+ }
626
+ }
627
+
628
+ // Original 4844 path
629
+ return excessBlobGas + blobGasUsed - targetPerBlock
630
+ }
631
+
632
+ /**
633
+ * Calculate the blob gas price of the block built on top of this one
634
+ * @returns The blob gas price
635
+ */
636
+ public calcNextBlobGasPrice(childCommon: Common): bigint {
637
+ return computeBlobGasPrice(this.calcNextExcessBlobGas(childCommon), childCommon)
638
+ }
639
+
640
+ /**
641
+ * Returns a Uint8Array Array of the raw Bytes in this header, in order.
642
+ */
643
+ raw(): BlockHeaderBytes {
644
+ const rawItems = [
645
+ this.parentHash,
646
+ this.uncleHash,
647
+ this.coinbase.bytes,
648
+ this.stateRoot,
649
+ this.transactionsTrie,
650
+ this.receiptTrie,
651
+ this.logsBloom,
652
+ bigIntToUnpaddedBytes(this.difficulty),
653
+ bigIntToUnpaddedBytes(this.number),
654
+ bigIntToUnpaddedBytes(this.gasLimit),
655
+ bigIntToUnpaddedBytes(this.gasUsed),
656
+ bigIntToUnpaddedBytes(this.timestamp ?? BIGINT_0),
657
+ this.extraData,
658
+ this.mixHash,
659
+ this.nonce,
660
+ ]
661
+
662
+ if (this.common.isActivatedEIP(1559)) {
663
+ rawItems.push(bigIntToUnpaddedBytes(this.baseFeePerGas!))
664
+ }
665
+
666
+ if (this.common.isActivatedEIP(4895)) {
667
+ rawItems.push(this.withdrawalsRoot!)
668
+ }
669
+
670
+ if (this.common.isActivatedEIP(4844)) {
671
+ rawItems.push(bigIntToUnpaddedBytes(this.blobGasUsed!))
672
+ rawItems.push(bigIntToUnpaddedBytes(this.excessBlobGas!))
673
+ }
674
+ if (this.common.isActivatedEIP(4788)) {
675
+ rawItems.push(this.parentBeaconBlockRoot!)
676
+ }
677
+ if (this.common.isActivatedEIP(7685)) {
678
+ rawItems.push(this.requestsHash!)
679
+ }
680
+
681
+ if (this.common.isActivatedEIP(7928)) {
682
+ rawItems.push(this.blockAccessListHash!)
683
+ }
684
+ if (this.common.isActivatedEIP(7843)) {
685
+ rawItems.push(bigIntToUnpaddedBytes(this.slotNumber!))
686
+ }
687
+ return rawItems
688
+ }
689
+
690
+ /**
691
+ * Returns the hash of the block header.
692
+ */
693
+ hash(): Uint8Array {
694
+ if (Object.isFrozen(this)) {
695
+ this.cache.hash ??= this.keccakFunction(RLP.encode(this.raw())) as Uint8Array
696
+ return this.cache.hash
697
+ }
698
+ return this.keccakFunction(RLP.encode(this.raw()))
699
+ }
700
+
701
+ /**
702
+ * Checks if the block header is a genesis header.
703
+ */
704
+ isGenesis(): boolean {
705
+ return this.number === BIGINT_0
706
+ }
707
+
708
+ /**
709
+ * Returns the canonical difficulty for this block.
710
+ *
711
+ * @param parentBlockHeader - the header from the parent `Block` of this header
712
+ */
713
+ ethashCanonicalDifficulty(parentBlockHeader: BlockHeader): bigint {
714
+ if (this.common.consensusType() !== ConsensusType.ProofOfWork) {
715
+ const msg = this._errorMsg('difficulty calculation is only supported on PoW chains')
716
+ throw EthereumJSErrorWithoutCode(msg)
717
+ }
718
+ if (this.common.consensusAlgorithm() !== ConsensusAlgorithm.Ethash) {
719
+ const msg = this._errorMsg(
720
+ 'difficulty calculation currently only supports the ethash algorithm',
721
+ )
722
+ throw EthereumJSErrorWithoutCode(msg)
723
+ }
724
+ const blockTs = this.timestamp
725
+ const { timestamp: parentTs, difficulty: parentDif } = parentBlockHeader
726
+ const minimumDifficulty = this.common.param('minimumDifficulty')
727
+ const offset = parentDif / this.common.param('difficultyBoundDivisor')
728
+ let num = this.number
729
+
730
+ // We use a ! here as TS cannot follow this hardfork-dependent logic, but it always gets assigned
731
+ let dif!: bigint
732
+
733
+ if (this.common.gteHardfork(Hardfork.Byzantium)) {
734
+ // max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99) (EIP100)
735
+ const uncleAddend = equalsBytes(parentBlockHeader.uncleHash, KECCAK256_RLP_ARRAY) ? 1 : 2
736
+ let a = BigInt(uncleAddend) - (blockTs - parentTs) / BigInt(9)
737
+ const cutoff = BigInt(-99)
738
+ // MAX(cutoff, a)
739
+ if (cutoff > a) {
740
+ a = cutoff
741
+ }
742
+ dif = parentDif + offset * a
743
+ }
744
+
745
+ if (this.common.gteHardfork(Hardfork.Byzantium)) {
746
+ // Get delay as parameter from common
747
+ num = num - this.common.param('difficultyBombDelay')
748
+ if (num < BIGINT_0) {
749
+ num = BIGINT_0
750
+ }
751
+ } else if (this.common.gteHardfork(Hardfork.Homestead)) {
752
+ // 1 - (block_timestamp - parent_timestamp) // 10
753
+ let a = BIGINT_1 - (blockTs - parentTs) / BigInt(10)
754
+ const cutoff = BigInt(-99)
755
+ // MAX(cutoff, a)
756
+ if (cutoff > a) {
757
+ a = cutoff
758
+ }
759
+ dif = parentDif + offset * a
760
+ } else {
761
+ // pre-homestead
762
+ if (parentTs + this.common.param('durationLimit') > blockTs) {
763
+ dif = offset + parentDif
764
+ } else {
765
+ dif = parentDif - offset
766
+ }
767
+ }
768
+
769
+ const exp = num / BigInt(100000) - BIGINT_2
770
+ if (exp >= 0) {
771
+ dif = dif + BIGINT_2 ** exp
772
+ }
773
+
774
+ if (dif < minimumDifficulty) {
775
+ dif = minimumDifficulty
776
+ }
777
+
778
+ return dif
779
+ }
780
+
781
+ /**
782
+ * Returns the rlp encoding of the block header.
783
+ */
784
+ serialize(): Uint8Array {
785
+ return RLP.encode(this.raw())
786
+ }
787
+
788
+ /**
789
+ * Returns the block header in JSON format.
790
+ */
791
+ toJSON(): JSONHeader {
792
+ const withdrawalAttr = this.withdrawalsRoot
793
+ ? { withdrawalsRoot: bytesToHex(this.withdrawalsRoot) }
794
+ : {}
795
+ const JSONDict: JSONHeader = {
796
+ parentHash: bytesToHex(this.parentHash),
797
+ uncleHash: bytesToHex(this.uncleHash),
798
+ coinbase: this.coinbase.toString(),
799
+ stateRoot: bytesToHex(this.stateRoot),
800
+ transactionsTrie: bytesToHex(this.transactionsTrie),
801
+ ...withdrawalAttr,
802
+ receiptTrie: bytesToHex(this.receiptTrie),
803
+ logsBloom: bytesToHex(this.logsBloom),
804
+ difficulty: bigIntToHex(this.difficulty),
805
+ number: bigIntToHex(this.number),
806
+ gasLimit: bigIntToHex(this.gasLimit),
807
+ gasUsed: bigIntToHex(this.gasUsed),
808
+ timestamp: bigIntToHex(this.timestamp),
809
+ extraData: bytesToHex(this.extraData),
810
+ mixHash: bytesToHex(this.mixHash),
811
+ nonce: bytesToHex(this.nonce),
812
+ }
813
+ if (this.common.isActivatedEIP(1559)) {
814
+ JSONDict.baseFeePerGas = bigIntToHex(this.baseFeePerGas!)
815
+ }
816
+ if (this.common.isActivatedEIP(4844)) {
817
+ JSONDict.blobGasUsed = bigIntToHex(this.blobGasUsed!)
818
+ JSONDict.excessBlobGas = bigIntToHex(this.excessBlobGas!)
819
+ }
820
+ if (this.common.isActivatedEIP(4788)) {
821
+ JSONDict.parentBeaconBlockRoot = bytesToHex(this.parentBeaconBlockRoot!)
822
+ }
823
+ if (this.common.isActivatedEIP(7685)) {
824
+ JSONDict.requestsHash = bytesToHex(this.requestsHash!)
825
+ }
826
+ if (this.common.isActivatedEIP(7928)) {
827
+ JSONDict.blockAccessListHash = bytesToHex(this.blockAccessListHash!)
828
+ }
829
+ if (this.common.isActivatedEIP(7843)) {
830
+ JSONDict.slotNumber = bigIntToHex(this.slotNumber!)
831
+ }
832
+ return JSONDict
833
+ }
834
+
835
+ /**
836
+ * Validates extra data is DAO_ExtraData for DAO_ForceExtraDataRange blocks after DAO
837
+ * activation block (see: https://blog.slock.it/hard-fork-specification-24b889e70703)
838
+ */
839
+ protected _validateDAOExtraData() {
840
+ if (!this.common.hardforkIsActiveOnBlock(Hardfork.Dao, this.number)) {
841
+ return
842
+ }
843
+ const DAOActivationBlock = this.common.hardforkBlock(Hardfork.Dao)
844
+ if (DAOActivationBlock === null || this.number < DAOActivationBlock) {
845
+ return
846
+ }
847
+ const DAO_ExtraData = hexToBytes('0x64616f2d686172642d666f726b')
848
+ const DAO_ForceExtraDataRange = BigInt(9)
849
+ const drift = this.number - DAOActivationBlock
850
+ if (drift <= DAO_ForceExtraDataRange && !equalsBytes(this.extraData, DAO_ExtraData)) {
851
+ const msg = this._errorMsg(
852
+ `extraData should be 'dao-hard-fork', got ${bytesToUtf8(this.extraData)} (hex: ${bytesToHex(
853
+ this.extraData,
854
+ )})`,
855
+ )
856
+ throw EthereumJSErrorWithoutCode(msg)
857
+ }
858
+ }
859
+
860
+ /**
861
+ * Return a compact error string representation of the object
862
+ */
863
+ public errorStr() {
864
+ let hash = ''
865
+ try {
866
+ hash = bytesToHex(this.hash())
867
+ } catch {
868
+ hash = 'error'
869
+ }
870
+ let hf = ''
871
+ try {
872
+ hf = this.common.hardfork()
873
+ } catch {
874
+ hf = 'error'
875
+ }
876
+ let errorStr = `block header number=${this.number} hash=${hash} `
877
+ errorStr += `hf=${hf} baseFeePerGas=${this.baseFeePerGas ?? 'none'}`
878
+ return errorStr
879
+ }
880
+
881
+ /**
882
+ * Helper function to create an annotated error message
883
+ *
884
+ * @param msg Base error message
885
+ * @hidden
886
+ */
887
+ protected _errorMsg(msg: string) {
888
+ return `${msg} (${this.errorStr()})`
889
+ }
890
+ }