Payload envelope db operations (#8717)

Adds support for payload envelopes in the db. This is the minimum we'll need to store and fetch payloads.


  


Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>
This commit is contained in:
Eitan Seri-Levi
2026-02-02 21:46:10 -08:00
committed by GitHub
parent 99e957ad35
commit ed7354d460
7 changed files with 108 additions and 3 deletions

View File

@@ -1296,6 +1296,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok(self.store.get_blinded_block(block_root)?)
}
pub fn get_payload_envelope(
&self,
block_root: &Hash256,
) -> Result<Option<SignedExecutionPayloadEnvelope<T::EthSpec>>, Error> {
Ok(self.store.get_payload_envelope(block_root)?)
}
/// Return the status of a block as it progresses through the various caches of the beacon
/// chain. Used by sync to learn the status of a block and prevent repeated downloads /
/// processing attempts.

View File

@@ -773,6 +773,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
StoreOp::DeleteBlock(block_root),
StoreOp::DeleteExecutionPayload(block_root),
StoreOp::DeleteBlobs(block_root),
StoreOp::DeletePayloadEnvelope(block_root),
StoreOp::DeleteSyncCommitteeBranch(block_root),
]
})

View File

@@ -106,8 +106,8 @@ fn check_db_columns() {
let current_columns: Vec<&'static str> = DBColumn::iter().map(|c| c.as_str()).collect();
let expected_columns = vec![
"bma", "blk", "blb", "bdc", "bdi", "ste", "hsd", "hsn", "bsn", "bsd", "bss", "bs3", "bcs",
"bst", "exp", "bch", "opo", "etc", "frk", "pkc", "brp", "bsx", "bsr", "bbx", "bbr", "bhr",
"brm", "dht", "cus", "otb", "bhs", "olc", "lcu", "scb", "scm", "dmy",
"bst", "exp", "pay", "bch", "opo", "etc", "frk", "pkc", "brp", "bsx", "bsr", "bbx", "bbr",
"bhr", "brm", "dht", "cus", "otb", "bhs", "olc", "lcu", "scb", "scm", "dmy",
];
assert_eq!(expected_columns, current_columns);
}

View File

@@ -745,6 +745,32 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
.map_err(|e| e.into())
}
pub fn get_payload_envelope(
&self,
block_root: &Hash256,
) -> Result<Option<SignedExecutionPayloadEnvelope<E>>, Error> {
let key = block_root.as_slice();
match self
.hot_db
.get_bytes(SignedExecutionPayloadEnvelope::<E>::db_column(), key)?
{
Some(bytes) => {
let envelope = SignedExecutionPayloadEnvelope::from_ssz_bytes(&bytes)?;
Ok(Some(envelope))
}
None => Ok(None),
}
}
/// Check if the payload envelope for a block exists on disk.
pub fn payload_envelope_exists(&self, block_root: &Hash256) -> Result<bool, Error> {
self.hot_db.key_exists(
SignedExecutionPayloadEnvelope::<E>::db_column(),
block_root.as_slice(),
)
}
/// Load the execution payload for a block from disk.
/// This method deserializes with the proper fork.
pub fn get_execution_payload(
@@ -1027,6 +1053,33 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
}
}
// TODO(gloas) we should store the execution payload separately like we do for blocks.
/// Prepare a signed execution payload envelope for storage in the database.
pub fn payload_envelope_as_kv_store_ops(
&self,
key: &Hash256,
payload: &SignedExecutionPayloadEnvelope<E>,
ops: &mut Vec<KeyValueStoreOp>,
) {
ops.push(KeyValueStoreOp::PutKeyValue(
SignedExecutionPayloadEnvelope::<E>::db_column(),
key.as_slice().into(),
payload.as_ssz_bytes(),
));
}
pub fn put_payload_envelope(
&self,
block_root: &Hash256,
payload_envelope: SignedExecutionPayloadEnvelope<E>,
) -> Result<(), Error> {
self.hot_db.put_bytes(
SignedExecutionPayloadEnvelope::<E>::db_column(),
block_root.as_slice(),
&payload_envelope.as_ssz_bytes(),
)
}
/// Store a state in the store.
pub fn put_state(&self, state_root: &Hash256, state: &BeaconState<E>) -> Result<(), Error> {
let mut ops: Vec<KeyValueStoreOp> = Vec::new();
@@ -1283,6 +1336,14 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
);
}
StoreOp::PutPayloadEnvelope(block_root, payload_envelope) => {
self.payload_envelope_as_kv_store_ops(
&block_root,
&payload_envelope,
&mut key_value_batch,
);
}
StoreOp::PutStateSummary(state_root, summary) => {
key_value_batch.push(summary.as_kv_store_op(state_root));
}
@@ -1309,6 +1370,13 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
}
}
StoreOp::DeletePayloadEnvelope(block_root) => {
key_value_batch.push(KeyValueStoreOp::DeleteKey(
SignedExecutionPayloadEnvelope::<E>::db_column(),
block_root.as_slice().to_vec(),
))
}
StoreOp::DeleteState(state_root, slot) => {
// Delete the hot state summary.
key_value_batch.push(KeyValueStoreOp::DeleteKey(
@@ -1528,6 +1596,8 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
StoreOp::PutDataColumns(_, _) => (),
StoreOp::PutPayloadEnvelope(_, _) => (),
StoreOp::PutState(_, _) => (),
StoreOp::PutStateSummary(_, _) => (),
@@ -1536,6 +1606,8 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
guard.delete_block(&block_root);
}
StoreOp::DeletePayloadEnvelope(_) => (),
StoreOp::DeleteState(_, _) => (),
StoreOp::DeleteBlobs(_) => (),

View File

@@ -1 +1,2 @@
pub mod execution_payload;
mod signed_execution_payload_envelope;

View File

@@ -0,0 +1,18 @@
use ssz::{Decode, Encode};
use types::{EthSpec, SignedExecutionPayloadEnvelope};
use crate::{DBColumn, Error, StoreItem};
impl<E: EthSpec> StoreItem for SignedExecutionPayloadEnvelope<E> {
fn db_column() -> DBColumn {
DBColumn::PayloadEnvelope
}
fn as_store_bytes(&self) -> Vec<u8> {
self.as_ssz_bytes()
}
fn from_store_bytes(bytes: &[u8]) -> Result<Self, Error> {
Ok(Self::from_ssz_bytes(bytes)?)
}
}

View File

@@ -234,12 +234,14 @@ pub enum StoreOp<'a, E: EthSpec> {
PutState(Hash256, &'a BeaconState<E>),
PutBlobs(Hash256, BlobSidecarList<E>),
PutDataColumns(Hash256, DataColumnSidecarList<E>),
PutPayloadEnvelope(Hash256, Arc<SignedExecutionPayloadEnvelope<E>>),
PutStateSummary(Hash256, HotStateSummary),
DeleteBlock(Hash256),
DeleteBlobs(Hash256),
DeleteDataColumns(Hash256, Vec<ColumnIndex>, ForkName),
DeleteState(Hash256, Option<Slot>),
DeleteExecutionPayload(Hash256),
DeletePayloadEnvelope(Hash256),
DeleteSyncCommitteeBranch(Hash256),
KeyValueOp(KeyValueStoreOp),
}
@@ -310,6 +312,9 @@ pub enum DBColumn {
/// Execution payloads for blocks more recent than the finalized checkpoint.
#[strum(serialize = "exp")]
ExecPayload,
/// Post-gloas execution payload envelopes.
#[strum(serialize = "pay")]
PayloadEnvelope,
/// For persisting in-memory state to the database.
#[strum(serialize = "bch")]
BeaconChain,
@@ -421,7 +426,8 @@ impl DBColumn {
| Self::BeaconRestorePoint
| Self::DhtEnrs
| Self::CustodyContext
| Self::OptimisticTransitionBlock => 32,
| Self::OptimisticTransitionBlock
| Self::PayloadEnvelope => 32,
Self::BeaconBlockRoots
| Self::BeaconDataColumnCustodyInfo
| Self::BeaconBlockRootsChunked