-
Notifications
You must be signed in to change notification settings - Fork 830
feat: implement eip-7918 #4142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: implement eip-7918 #4142
Changes from all commits
8d876f9
b071c86
f2a1690
ddc2db4
2386290
271f3af
8cb08ff
acfa90b
211758e
3b7bfd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you also had this strange random (?) linter stuff !? |
||
return this.cache.hash | ||
} | ||
return this.keccakFunction(RLP.encode(this.raw())) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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', | ||
) | ||
}) | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just for informational purposes: is this AI or partly or not? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not matter who to praise for: these are really nicely readable tests! 😄 😂 |
||
}) | ||
|
||
describe('transaction validation tests', () => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be removed (I guess?). |
||
}, | ||
/** | ||
. * Beacon block root in the EVM | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please do not use the
n
notation for BigInts (compatibility reasons) + we have these predefined constants (likeBIGINT_1
) for various commonly used BigInt values, which gives a performance benefit and which you can use on various occasions in this PR.