diff --git a/packages/block/src/header/header.ts b/packages/block/src/header/header.ts index 477f9b77790..d8daa1c2359 100644 --- a/packages/block/src/header/header.ts +++ b/packages/block/src/header/header.ts @@ -560,15 +560,32 @@ export class BlockHeader { * Calculates the excess blob gas for next (hopefully) post EIP 4844 block. */ public calcNextExcessBlobGas(childCommon: Common): bigint { - // The validation of the fields and 4844 activation is already taken care in BlockHeader constructor - const targetGasConsumed = (this.excessBlobGas ?? BIGINT_0) + (this.blobGasUsed ?? BIGINT_0) - const targetBlobGasPerBlock = childCommon.param('targetBlobGasPerBlock') + const excessBlobGas = this.excessBlobGas ?? 0n + const blobGasUsed = this.blobGasUsed ?? 0n - if (targetGasConsumed <= targetBlobGasPerBlock) { - return BIGINT_0 - } else { - return targetGasConsumed - targetBlobGasPerBlock + const targetPerBlock = childCommon.param('targetBlobGasPerBlock') + const maxPerBlock = childCommon.param('maxBlobGasPerBlock') + + // Early exit (strictly < per spec) + if (excessBlobGas + blobGasUsed < targetPerBlock) { + return 0n + } + + // EIP-7918 reserve price check + if (childCommon.isActivatedEIP(7918)) { + const blobBaseCost = childCommon.param('blobBaseCost') + const gasPerBlob = childCommon.param('blobGasPerBlob') + const baseFee = this.baseFeePerGas ?? 0n + const blobFee = this.getBlobGasPrice() + + if (blobBaseCost * baseFee > gasPerBlob * blobFee) { + const increase = (blobGasUsed * (maxPerBlock - targetPerBlock)) / maxPerBlock + return excessBlobGas + increase + } } + + // Original 4844 path + return excessBlobGas + blobGasUsed - targetPerBlock } /** @@ -635,9 +652,7 @@ export class BlockHeader { */ hash(): Uint8Array { if (Object.isFrozen(this)) { - if (!this.cache.hash) { - this.cache.hash = this.keccakFunction(RLP.encode(this.raw())) as Uint8Array - } + this.cache.hash ??= this.keccakFunction(RLP.encode(this.raw())) as Uint8Array return this.cache.hash } return this.keccakFunction(RLP.encode(this.raw())) diff --git a/packages/block/src/params.ts b/packages/block/src/params.ts index de4be648b8c..6191536d177 100644 --- a/packages/block/src/params.ts +++ b/packages/block/src/params.ts @@ -74,6 +74,7 @@ export const paramsBlock: ParamsDict = { blobGasPriceUpdateFraction: 3338477, // The denominator used in the exponential when calculating a blob gas price // gasPrices minBlobGas: 1, // The minimum fee per blob gas + blobBaseCost: 8192, // EIP-7918: Blob base fee bounded by execution cost (2^13) }, /** * Delaying Difficulty Bomb to mid-September 2022 diff --git a/packages/block/test/eip4844block.spec.ts b/packages/block/test/eip4844block.spec.ts index 3d890c81cd6..8ad492d0783 100644 --- a/packages/block/test/eip4844block.spec.ts +++ b/packages/block/test/eip4844block.spec.ts @@ -161,6 +161,105 @@ describe('blob gas tests', () => { const nextBlobGas = highGasHeader.calcNextBlobGasPrice(common) assert.strictEqual(nextBlobGas, BigInt(7)) // TODO verify that this is correct }) + + describe('EIP-7918: Blob base fee bounded by execution cost', () => { + const osakaCommon = createCommonFromGethGenesis(eip4844GethGenesis, { + chain: 'customChain', + hardfork: Hardfork.Cancun, + params: paramsBlock, + customCrypto: { kzg }, + eips: [7918], + }) + + it('applies reserve price when exec cost dominates', () => { + const highBaseFee = 1_000_000_000_000_000n + const target = osakaCommon.param('targetBlobGasPerBlock') + const max = osakaCommon.param('maxBlobGasPerBlock') + const BLOB_BASE_COST = osakaCommon.param('blobBaseCost') + const GAS_PER_BLOB = osakaCommon.param('blobGasPerBlob') + + const header = createBlockHeader( + { + number: 1, + baseFeePerGas: highBaseFee, + excessBlobGas: 0n, + blobGasUsed: target, + }, + { common: osakaCommon, skipConsensusFormatValidation: true }, + ) + + assert.isTrue(BLOB_BASE_COST * highBaseFee > GAS_PER_BLOB * header.getBlobGasPrice()) + + const got = header.calcNextExcessBlobGas(osakaCommon) + const expected = (target * (max - target)) / max + assert.strictEqual(got, expected) + }) + + it('should use original EIP-4844 logic when reserve price condition is not met', () => { + // Create a header with low base fee and high blob gas price + const lowBaseFee = 1n // Very low base fee (1 wei) + + // Set excessBlobGas to a high value to get high blob gas price + const highExcessBlobGas = 1000000000n + const header = createBlockHeader( + { + number: 1, + baseFeePerGas: lowBaseFee, + excessBlobGas: highExcessBlobGas, + blobGasUsed: blobGasPerBlob * 2n, // 2 blobs used + }, + { common: osakaCommon, skipConsensusFormatValidation: true }, + ) + + const excessBlobGas = header.calcNextExcessBlobGas(osakaCommon) + + // Should use original EIP-4844 logic + const blobBaseCost = osakaCommon.param('blobBaseCost') + const currentBlobGasPrice = header.getBlobGasPrice() + + // Check that reserve price condition is not met + assert.isTrue( + blobBaseCost * lowBaseFee <= blobGasPerBlob * currentBlobGasPrice, + 'reserve price condition should not be met', + ) + + const targetGasConsumed = highExcessBlobGas + blobGasPerBlob * 2n + const targetBlobGasPerBlock = osakaCommon.param('targetBlobGasPerBlock') + const expectedExcessBlobGas = targetGasConsumed - targetBlobGasPerBlock + + assert.strictEqual( + excessBlobGas, + expectedExcessBlobGas, + 'should use original EIP-4844 logic when reserve price condition is not met', + ) + }) + + it('should not apply EIP-7918 logic when EIP is not activated', () => { + // Use Cancun hardfork where EIP-7918 is not activated + const header = createBlockHeader( + { + number: 1, + baseFeePerGas: 1000000000n, + excessBlobGas: 1000000n, + blobGasUsed: blobGasPerBlob, + }, + { common, skipConsensusFormatValidation: true }, + ) + + const excessBlobGas = header.calcNextExcessBlobGas(common) + + // Should use original EIP-4844 logic since EIP-7918 is not activated + const targetGasConsumed = 1000000n + blobGasPerBlob + const targetBlobGasPerBlock = common.param('targetBlobGasPerBlock') + const expectedExcessBlobGas = targetGasConsumed - targetBlobGasPerBlock + + assert.strictEqual( + excessBlobGas, + expectedExcessBlobGas, + 'should use original EIP-4844 logic when EIP-7918 is not activated', + ) + }) + }) }) describe('transaction validation tests', () => { diff --git a/packages/common/README.md b/packages/common/README.md index 5ac44b0a4aa..f5e543f2bca 100644 --- a/packages/common/README.md +++ b/packages/common/README.md @@ -364,6 +364,7 @@ The following EIPs are currently supported: - [EIP-7516](https://eips.ethereum.org/EIPS/eip-7516) - BLOBBASEFEE opcode (Cancun) - [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) - Increase calldata cost (Prague) - [EIP-7685](https://eips.ethereum.org/EIPS/eip-7685) - General purpose execution layer requests (Prague) +- [EIP-7918](https://eips.ethereum.org/EIPS/eip-7918) - Blob base fee bounded by execution cost (Osaka) - [EIP-7691](https://eips.ethereum.org/EIPS/eip-7691) - Blob throughput increase (Prague) - [EIP-7692](https://eips.ethereum.org/EIPS/eip-7692) - EVM Object Format (EOF) v1 (`experimental`) - [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) - Set EOA account code (Prague) diff --git a/packages/common/src/eips.ts b/packages/common/src/eips.ts index d2abacef501..f79999ab87d 100644 --- a/packages/common/src/eips.ts +++ b/packages/common/src/eips.ts @@ -442,6 +442,15 @@ export const eipsDict: EIPsDict = { minimumHardfork: Hardfork.Paris, requiredEIPs: [4844], }, + /** + * Description : Blob base fee bounded by execution cost + * URL : https://eips.ethereum.org/EIPS/eip-7918 + * Status : Last Call + */ + 7918: { + minimumHardfork: Hardfork.Paris, + requiredEIPs: [4844], + }, /** * Description : EVM Object Format (EOFv1) Meta * URL : https://github.com/ethereum/EIPs/blob/4153e95befd0264082de3c4c2fe3a85cc74d3152/EIPS/eip-7692.md diff --git a/packages/common/src/hardforks.ts b/packages/common/src/hardforks.ts index 6651c6577ed..948fe63f9bf 100644 --- a/packages/common/src/hardforks.ts +++ b/packages/common/src/hardforks.ts @@ -166,7 +166,7 @@ export const hardforksDict: HardforksDict = { * Status : Draft */ osaka: { - eips: [7594, 7823, 7825, 7883, 7939, 7951], + eips: [7594, 7823, 7825, 7883, 7939, 7951, 7918], }, /** * Description: Next feature hardfork after osaka, internally used for verkle testing/implementation (incomplete/experimental) diff --git a/packages/vm/src/params.ts b/packages/vm/src/params.ts index 5a6a4f05991..30d56f85532 100644 --- a/packages/vm/src/params.ts +++ b/packages/vm/src/params.ts @@ -62,6 +62,7 @@ export const paramsVM: ParamsDict = { blobGasPriceUpdateFraction: 3338477, // The denominator used in the exponential when calculating a blob gas price // gasPrices minBlobGas: 1, // The minimum fee per blob gas + blobBaseCost: 8192, // EIP-7918: Blob base fee bounded by execution cost (2^13) }, /** . * Beacon block root in the EVM