Skip to content

Commit 0dce642

Browse files
feat: implement eip-7918 (#4142)
* feat: implement eip-7918 (draft) * chore: fix type errors * chore: fix implementation * chore: adjust naming * test: fix test * test: fix test * chore: fix test case * chore: fix test case * chore: reset submodule * chore: address review --------- Co-authored-by: Holger Drewes <[email protected]>
1 parent 89bc05b commit 0dce642

File tree

6 files changed

+136
-11
lines changed

6 files changed

+136
-11
lines changed

packages/block/src/header/header.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -560,15 +560,32 @@ export class BlockHeader {
560560
* Calculates the excess blob gas for next (hopefully) post EIP 4844 block.
561561
*/
562562
public calcNextExcessBlobGas(childCommon: Common): bigint {
563-
// The validation of the fields and 4844 activation is already taken care in BlockHeader constructor
564-
const targetGasConsumed = (this.excessBlobGas ?? BIGINT_0) + (this.blobGasUsed ?? BIGINT_0)
565-
const targetBlobGasPerBlock = childCommon.param('targetBlobGasPerBlock')
563+
const excessBlobGas = this.excessBlobGas ?? BIGINT_0
564+
const blobGasUsed = this.blobGasUsed ?? BIGINT_0
566565

567-
if (targetGasConsumed <= targetBlobGasPerBlock) {
568-
return BIGINT_0
569-
} else {
570-
return targetGasConsumed - targetBlobGasPerBlock
566+
const targetPerBlock = childCommon.param('targetBlobGasPerBlock')
567+
const maxPerBlock = childCommon.param('maxBlobGasPerBlock')
568+
569+
// Early exit (strictly < per spec)
570+
if (excessBlobGas + blobGasUsed < targetPerBlock) {
571+
return 0n
572+
}
573+
574+
// EIP-7918 reserve price check
575+
if (childCommon.isActivatedEIP(7918)) {
576+
const blobBaseCost = childCommon.param('blobBaseCost')
577+
const gasPerBlob = childCommon.param('blobGasPerBlob')
578+
const baseFee = this.baseFeePerGas ?? 0n
579+
const blobFee = this.getBlobGasPrice()
580+
581+
if (blobBaseCost * baseFee > gasPerBlob * blobFee) {
582+
const increase = (blobGasUsed * (maxPerBlock - targetPerBlock)) / maxPerBlock
583+
return excessBlobGas + increase
584+
}
571585
}
586+
587+
// Original 4844 path
588+
return excessBlobGas + blobGasUsed - targetPerBlock
572589
}
573590

574591
/**
@@ -635,9 +652,7 @@ export class BlockHeader {
635652
*/
636653
hash(): Uint8Array {
637654
if (Object.isFrozen(this)) {
638-
if (!this.cache.hash) {
639-
this.cache.hash = this.keccakFunction(RLP.encode(this.raw())) as Uint8Array
640-
}
655+
this.cache.hash ??= this.keccakFunction(RLP.encode(this.raw())) as Uint8Array
641656
return this.cache.hash
642657
}
643658
return this.keccakFunction(RLP.encode(this.raw()))

packages/block/src/params.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ export const paramsBlock: ParamsDict = {
7474
blobGasPriceUpdateFraction: 3338477, // The denominator used in the exponential when calculating a blob gas price
7575
// gasPrices
7676
minBlobGas: 1, // The minimum fee per blob gas
77+
blobBaseCost: 8192, // EIP-7918: Blob base fee bounded by execution cost (2^13)
7778
},
7879
/**
7980
* Delaying Difficulty Bomb to mid-September 2022

packages/block/test/eip4844block.spec.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,105 @@ describe('blob gas tests', () => {
161161
const nextBlobGas = highGasHeader.calcNextBlobGasPrice(common)
162162
assert.strictEqual(nextBlobGas, BigInt(7)) // TODO verify that this is correct
163163
})
164+
165+
describe('EIP-7918: Blob base fee bounded by execution cost', () => {
166+
const osakaCommon = createCommonFromGethGenesis(eip4844GethGenesis, {
167+
chain: 'customChain',
168+
hardfork: Hardfork.Cancun,
169+
params: paramsBlock,
170+
customCrypto: { kzg },
171+
eips: [7918],
172+
})
173+
174+
it('applies reserve price when exec cost dominates', () => {
175+
const highBaseFee = 1_000_000_000_000_000n
176+
const target = osakaCommon.param('targetBlobGasPerBlock')
177+
const max = osakaCommon.param('maxBlobGasPerBlock')
178+
const BLOB_BASE_COST = osakaCommon.param('blobBaseCost')
179+
const GAS_PER_BLOB = osakaCommon.param('blobGasPerBlob')
180+
181+
const header = createBlockHeader(
182+
{
183+
number: 1,
184+
baseFeePerGas: highBaseFee,
185+
excessBlobGas: 0n,
186+
blobGasUsed: target,
187+
},
188+
{ common: osakaCommon, skipConsensusFormatValidation: true },
189+
)
190+
191+
assert.isTrue(BLOB_BASE_COST * highBaseFee > GAS_PER_BLOB * header.getBlobGasPrice())
192+
193+
const got = header.calcNextExcessBlobGas(osakaCommon)
194+
const expected = (target * (max - target)) / max
195+
assert.strictEqual(got, expected)
196+
})
197+
198+
it('should use original EIP-4844 logic when reserve price condition is not met', () => {
199+
// Create a header with low base fee and high blob gas price
200+
const lowBaseFee = 1n // Very low base fee (1 wei)
201+
202+
// Set excessBlobGas to a high value to get high blob gas price
203+
const highExcessBlobGas = 1000000000n
204+
const header = createBlockHeader(
205+
{
206+
number: 1,
207+
baseFeePerGas: lowBaseFee,
208+
excessBlobGas: highExcessBlobGas,
209+
blobGasUsed: blobGasPerBlob * 2n, // 2 blobs used
210+
},
211+
{ common: osakaCommon, skipConsensusFormatValidation: true },
212+
)
213+
214+
const excessBlobGas = header.calcNextExcessBlobGas(osakaCommon)
215+
216+
// Should use original EIP-4844 logic
217+
const blobBaseCost = osakaCommon.param('blobBaseCost')
218+
const currentBlobGasPrice = header.getBlobGasPrice()
219+
220+
// Check that reserve price condition is not met
221+
assert.isTrue(
222+
blobBaseCost * lowBaseFee <= blobGasPerBlob * currentBlobGasPrice,
223+
'reserve price condition should not be met',
224+
)
225+
226+
const targetGasConsumed = highExcessBlobGas + blobGasPerBlob * 2n
227+
const targetBlobGasPerBlock = osakaCommon.param('targetBlobGasPerBlock')
228+
const expectedExcessBlobGas = targetGasConsumed - targetBlobGasPerBlock
229+
230+
assert.strictEqual(
231+
excessBlobGas,
232+
expectedExcessBlobGas,
233+
'should use original EIP-4844 logic when reserve price condition is not met',
234+
)
235+
})
236+
237+
it('should not apply EIP-7918 logic when EIP is not activated', () => {
238+
// Use Cancun hardfork where EIP-7918 is not activated
239+
const header = createBlockHeader(
240+
{
241+
number: 1,
242+
baseFeePerGas: 1000000000n,
243+
excessBlobGas: 1000000n,
244+
blobGasUsed: blobGasPerBlob,
245+
},
246+
{ common, skipConsensusFormatValidation: true },
247+
)
248+
249+
const excessBlobGas = header.calcNextExcessBlobGas(common)
250+
251+
// Should use original EIP-4844 logic since EIP-7918 is not activated
252+
const targetGasConsumed = 1000000n + blobGasPerBlob
253+
const targetBlobGasPerBlock = common.param('targetBlobGasPerBlock')
254+
const expectedExcessBlobGas = targetGasConsumed - targetBlobGasPerBlock
255+
256+
assert.strictEqual(
257+
excessBlobGas,
258+
expectedExcessBlobGas,
259+
'should use original EIP-4844 logic when EIP-7918 is not activated',
260+
)
261+
})
262+
})
164263
})
165264

166265
describe('transaction validation tests', () => {

packages/common/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ The following EIPs are currently supported:
364364
- [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) - BLOBBASEFEE opcode (Cancun)
365365
- [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) - Increase calldata cost (Prague)
366366
- [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) - General purpose execution layer requests (Prague)
367+
- [EIP-7918](https://eips.ethereum.org/EIPS/eip-7918) - Blob base fee bounded by execution cost (Osaka)
367368
- [EIP-7691](https://eips.ethereum.org/EIPS/eip-7691) - Blob throughput increase (Prague)
368369
- [EIP-7692](https://eips.ethereum.org/EIPS/eip-7692) - EVM Object Format (EOF) v1 (`experimental`)
369370
- [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) - Set EOA account code (Prague)

packages/common/src/eips.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,15 @@ export const eipsDict: EIPsDict = {
442442
minimumHardfork: Hardfork.Paris,
443443
requiredEIPs: [4844],
444444
},
445+
/**
446+
* Description : Blob base fee bounded by execution cost
447+
* URL : https://eips.ethereum.org/EIPS/eip-7918
448+
* Status : Last Call
449+
*/
450+
7918: {
451+
minimumHardfork: Hardfork.Paris,
452+
requiredEIPs: [4844],
453+
},
445454
/**
446455
* Description : EVM Object Format (EOFv1) Meta
447456
* URL : https://github.com/ethereum/EIPs/blob/4153e95befd0264082de3c4c2fe3a85cc74d3152/EIPS/eip-7692.md

packages/common/src/hardforks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ export const hardforksDict: HardforksDict = {
166166
* Status : Draft
167167
*/
168168
osaka: {
169-
eips: [7594, 7823, 7825, 7883, 7939, 7951],
169+
eips: [7594, 7823, 7825, 7883, 7939, 7951, 7918],
170170
},
171171
/**
172172
* Description: Next feature hardfork after osaka, internally used for verkle testing/implementation (incomplete/experimental)

0 commit comments

Comments
 (0)