-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
feat: add 4844 header fields and consensus checks #3972
Conversation
failing proptest cases, needs an encoding / decoding fix - working on it |
Codecov Report
... and 44 files with indirect coverage changes
Flags with carried forward coverage won't be shown. Click here to find out more.
|
crates/primitives/src/header.rs
Outdated
// but withdrawals root is present. | ||
if let Some(ref base_fee) = self.base_fee_per_gas { | ||
U256::from(*base_fee).encode(out); | ||
} else if self.withdrawals_root.is_some() { | ||
} else if self.withdrawals_root.is_some() || | ||
self.blob_gas_used.is_some() || | ||
self.excess_blob_gas.is_some() | ||
{ | ||
out.put_u8(EMPTY_STRING_CODE); | ||
} | ||
|
||
// Encode withdrawals root. Put empty string if withdrawals root is missing, | ||
// but blob gas used is present. | ||
if let Some(ref root) = self.withdrawals_root { | ||
root.encode(out); | ||
} else if self.blob_gas_used.is_some() || self.excess_blob_gas.is_some() { | ||
out.put_u8(EMPTY_STRING_CODE); | ||
} | ||
|
||
// Encode blob gas used. Put empty string if blob gas used is missing, | ||
// but excess blob gas is present. | ||
if let Some(ref blob_gas_used) = self.blob_gas_used { | ||
U256::from(*blob_gas_used).encode(out); | ||
} else if self.excess_blob_gas.is_some() { | ||
out.put_u8(EMPTY_STRING_CODE); | ||
} | ||
|
||
// Encode excess blob gas. If new fields are added, the above pattern will need to be | ||
// repeated and placeholders added. Otherwise, it's impossible to tell _which_ fields | ||
// are missing. This is mainly relevant for contrived cases where a header is created | ||
// at random, for example: | ||
// * A header is created with a withdrawals root, but no base fee. Shanghai blocks are |
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.
we should think of how to make this less horrible, since it's possible there will be even more header extensions in the future
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.
maybe different header types? like we do with transactions?
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.
ah, this wouldn't work because we don't have a tx type byte like we do for transactions - we'd need to know in advance (before decoding) which type of header it is
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.
this looks pretty good already, less horrible than expected actually.
we should add a few checks:
- consensus sanity checks
- rlp checks with some reference rlp data
if chain_spec.fork(Hardfork::Cancun).active_at_timestamp(header.timestamp) { | ||
let blob_gas_used = header.blob_gas_used.ok_or(ConsensusError::BlobGasUsedMissing)?; |
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.
I'd like to extract these into a standalone function
we also need to check the full block downloaders whether we need to update the checks there
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.
just realized our full block client enforces that the header has a withdrawals root, which is not true for pre-shanghai. but we can only perform these checks properly if we have the Arc<ChainSpec>
, so it will need to be added
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.
so yes they need to be updated
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.
^ nevermind, I misread the code and this is completely wrong.
We currently only validate the body w.r.t the header, and do no other checks:
reth/crates/interfaces/src/p2p/full_block.rs
Lines 316 to 331 in be8797d
let withdrawals = block.withdrawals.as_deref().unwrap_or(&[]); | |
if let Some(header_withdrawals_root) = header.withdrawals_root { | |
let withdrawals_root = reth_primitives::proofs::calculate_withdrawals_root(withdrawals); | |
if withdrawals_root != header_withdrawals_root { | |
return Err(ConsensusError::BodyWithdrawalsRootDiff { | |
got: withdrawals_root, | |
expected: header_withdrawals_root, | |
}) | |
} | |
return Ok(()) | |
} | |
if !withdrawals.is_empty() { | |
return Err(ConsensusError::WithdrawalsRootUnexpected) | |
} |
so, if there is no withdrawals root, but withdrawals exist in the body, an error is returned. And vice versa for non-empty withdrawals roots.
I'm not sure what we would check here, we perform header validation in the tree, so maybe we can keep the checks as-is.
crates/primitives/src/blobfee.rs
Outdated
if excess_blob_gas < TARGET_DATA_GAS_PER_BLOCK { | ||
return 0 | ||
} | ||
|
||
excess_blob_gas - TARGET_DATA_GAS_PER_BLOCK |
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.
this is just saturating_sub, right?
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.
right, not sure how i missed this
// Encode excess blob gas. If new fields are added, the above pattern will need to be | ||
// repeated and placeholders added. Otherwise, it's impossible to tell _which_ fields | ||
// are missing. This is mainly relevant for contrived cases where a header is created | ||
// at random, for example: | ||
// * A header is created with a withdrawals root, but no base fee. Shanghai blocks are |
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.
yeah this is a bit horrible, but still managable
// TODO: add header fields to the rpc header | ||
blob_gas_used: _, | ||
excess_blob_gas: _, |
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.
will add an issue for this
will find some reference RLP data, may have to generate it using geth code |
Added RLP tests from devnet-7 and EF tests |
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.
lgtm
This adds EIP-4844 header fields:
blob_gas_used
excess_blob_gas
And their according consensus header validation checks. The method
calculate_excess_blob_gas
is added which is specified in the EIP-4844 header extension section.RLP encoding and decoding are updated in a similar way to the withdrawals root.
Fixes #3941