Skip to content
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

Add Capella & Deneb light client support #4946

Merged
merged 86 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
388a7cd
rebase and add comment
GeemoCandama Feb 7, 2023
b47b4f2
conditional test
GeemoCandama Feb 8, 2023
df78885
test
GeemoCandama Feb 8, 2023
e124c99
optimistic chould be working now
GeemoCandama Feb 8, 2023
cfdeca0
finality should be working now
GeemoCandama Feb 8, 2023
e15b98f
try again
GeemoCandama Feb 8, 2023
6db8a41
try again
GeemoCandama Feb 8, 2023
1537947
clippy fix
GeemoCandama Feb 8, 2023
5dbde26
add lc bootstrap beacon api
GeemoCandama Feb 9, 2023
4611a9c
add lc optimistic/finality update to events
GeemoCandama Feb 9, 2023
d3da99f
fmt
GeemoCandama Feb 9, 2023
a2f6a8a
That error isn't occuring on my computer but I think this should fix it
GeemoCandama Feb 11, 2023
00179e4
Merge branch 'unstable' into light_client_beacon_api_1
jimmygchen Nov 16, 2023
d74e3b4
Add missing test file
jimmygchen Nov 16, 2023
ad3cc69
Update light client types to comply with Altair light client spec.
jimmygchen Nov 16, 2023
f062e77
Fix test compilation
jimmygchen Nov 16, 2023
7307756
Merge branch 'unstable' into light_client_beacon_api_1
jimmygchen Nov 16, 2023
bfd3fb7
Support deserializing light client structures for the Bellatrix fork
jimmygchen Nov 17, 2023
d90df3f
Move `get_light_client_bootstrap` logic to `BeaconChain`. `LightClien…
jimmygchen Nov 17, 2023
80ff555
Misc fixes.
jimmygchen Nov 17, 2023
75bd2ac
Add light client bootstrap API test and fix existing ones.
jimmygchen Nov 17, 2023
bd75152
Merge branch 'unstable' into light_client_beacon_api_1
jimmygchen Nov 17, 2023
e0d0ece
Fix test for `light-client-server` http api config.
jimmygchen Nov 17, 2023
161ece6
Appease clippy
jimmygchen Nov 17, 2023
885958e
Add Altair light client SSZ tests
jimmygchen Nov 21, 2023
fc30e04
Merge branch 'unstable' of https://github.com/sigp/lighthouse into li…
eserilev Nov 21, 2023
fe8e2e4
updates to light client header
eserilev Nov 21, 2023
ec1400f
light client header from signed beacon block
eserilev Nov 22, 2023
435da02
using options
eserilev Nov 24, 2023
73a581a
implement helper functions
eserilev Nov 25, 2023
5ff6f43
placeholder conversion from vec hash256 to exec branch
eserilev Nov 25, 2023
7aacc65
add deneb
eserilev Nov 25, 2023
3593f52
using fixed vector
eserilev Nov 25, 2023
65a5770
remove unwraps
eserilev Nov 26, 2023
fd1a691
by epoch
eserilev Nov 28, 2023
443d219
compute merkle proof
eserilev Nov 28, 2023
a9fa092
merkle proof
eserilev Nov 28, 2023
effeec1
update comments
eserilev Nov 28, 2023
f92a296
resolve merge conflicts
eserilev Nov 28, 2023
03c163f
linting
eserilev Nov 28, 2023
67df375
Merge branch 'unstable' into light-client-ssz-tests
jimmygchen Nov 28, 2023
16740c2
superstruct attempt
eserilev Dec 1, 2023
302a1cb
superstruct changes
eserilev Dec 3, 2023
79eeefd
lint
eserilev Dec 3, 2023
17adb6a
altair
eserilev Dec 3, 2023
5ab7681
update
eserilev Dec 3, 2023
74b0b6f
update
eserilev Dec 3, 2023
1879bfd
changes to light_client_optimistic_ and finality
eserilev Dec 3, 2023
9f0a2bc
merge unstable
eserilev Dec 12, 2023
1d10abe
refactor
eserilev Dec 12, 2023
22dc62f
resolved merge conflicts
eserilev Jan 31, 2024
8ba6ebe
Merge branch 'unstable' of https://github.com/sigp/lighthouse into ca…
eserilev Jan 31, 2024
b75d23b
block_to_light_client_header fork aware
eserilev Feb 1, 2024
5f75f25
fmt
eserilev Feb 1, 2024
96f89a2
comment fix
eserilev Feb 1, 2024
7a253bf
comment fix
eserilev Feb 1, 2024
5808f7c
include merge fork, update deserialize_by_fork, refactor
eserilev Feb 3, 2024
6663614
fmt
eserilev Feb 3, 2024
f442b5a
pass by ref to prevent clone
eserilev Feb 3, 2024
932f440
rename merkle proof fn
eserilev Feb 3, 2024
5556891
add FIXME
eserilev Feb 5, 2024
06da24a
LightClientHeader TestRandom
eserilev Feb 6, 2024
453b4d2
fix comments
eserilev Feb 6, 2024
d97d496
fork version deserialize
eserilev Feb 29, 2024
e6f6876
merge unstable
eserilev Feb 29, 2024
b23c913
move fn arguments, fork name calc
eserilev Mar 2, 2024
dde9274
use task executor
eserilev Mar 3, 2024
38cdd77
remove unneeded fns
eserilev Mar 5, 2024
c064c45
remove dead code
eserilev Mar 6, 2024
b964f3f
add manual ssz decoding/encoding and add ssz_tests_by_fork macro
eserilev Mar 7, 2024
9c9fbca
merge deneb types with tests
eserilev Mar 7, 2024
3a3616c
merge ssz tests, revert code deletion, cleanup
eserilev Mar 7, 2024
6ddfe7e
move chainspec
eserilev Mar 7, 2024
258f51b
update ssz tests
eserilev Mar 8, 2024
cb41899
fmt
eserilev Mar 8, 2024
57d90fe
light client ssz tests
eserilev Mar 11, 2024
534e808
change to superstruct
eserilev Mar 11, 2024
dd43c12
changes from feedback
eserilev Mar 12, 2024
aba1475
linting
eserilev Mar 12, 2024
6fb0270
Merge branch 'unstable' of https://github.com/sigp/lighthouse into ca…
eserilev Mar 12, 2024
b3f7b81
test fix
eserilev Mar 12, 2024
8a7b2bc
cleanup
eserilev Mar 12, 2024
d314d12
Remove unused `derive`.
jimmygchen Mar 13, 2024
9b31545
Merge branch 'unstable' of https://github.com/sigp/lighthouse into ca…
eserilev Mar 23, 2024
a7dd9ee
beta compiler fix
eserilev Mar 23, 2024
d9a0b48
merge
eserilev Mar 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1347,11 +1347,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
(parent_root, slot, sync_aggregate): LightClientProducerEvent<T::EthSpec>,
) -> Result<(), Error> {
self.light_client_server_cache.recompute_and_cache_updates(
&self.log,
self.store.clone(),
&parent_root,
slot,
&sync_aggregate,
&self.log,
&self.spec,
)
}

Expand Down Expand Up @@ -6635,13 +6636,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
&self,
block_root: &Hash256,
) -> Result<Option<(LightClientBootstrap<T::EthSpec>, ForkName)>, Error> {
let Some((state_root, slot)) = self
.get_blinded_block(block_root)?
.map(|block| (block.state_root(), block.slot()))
else {
let handle = self
.task_executor
.handle()
.ok_or(BeaconChainError::RuntimeShutdown)?;

let Some(block) = handle.block_on(async { self.get_block(block_root).await })? else {
return Ok(None);
};

let (state_root, slot) = (block.state_root(), block.slot());

let Some(mut state) = self.get_state(&state_root, Some(slot))? else {
return Ok(None);
};
Expand All @@ -6651,12 +6656,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.map_err(Error::InconsistentFork)?;

match fork_name {
ForkName::Altair | ForkName::Merge => {
LightClientBootstrap::from_beacon_state(&mut state)
ForkName::Altair | ForkName::Merge | ForkName::Capella | ForkName::Deneb => {
LightClientBootstrap::from_beacon_state(&mut state, &block, &self.spec)
.map(|bootstrap| Some((bootstrap, fork_name)))
.map_err(Error::LightClientError)
}
ForkName::Base | ForkName::Capella | ForkName::Deneb => Err(Error::UnsupportedFork),
ForkName::Base => Err(Error::UnsupportedFork),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions beacon_node/beacon_chain/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ easy_from_to!(StateAdvanceError, BeaconChainError);
easy_from_to!(BlockReplayError, BeaconChainError);
easy_from_to!(InconsistentFork, BeaconChainError);
easy_from_to!(AvailabilityCheckError, BeaconChainError);
easy_from_to!(LightClientError, BeaconChainError);

#[derive(Debug)]
pub enum BlockProductionError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientFinalityUpdate<T> {
// verify that enough time has passed for the block to have been propagated
let start_time = chain
.slot_clock
.start_of(rcv_finality_update.signature_slot)
.start_of(*rcv_finality_update.signature_slot())
.ok_or(Error::SigSlotStartIsNone)?;
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
// verify that enough time has passed for the block to have been propagated
let start_time = chain
.slot_clock
.start_of(rcv_optimistic_update.signature_slot)
.start_of(*rcv_optimistic_update.signature_slot())
.ok_or(Error::SigSlotStartIsNone)?;
let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0);
if seen_timestamp + chain.spec.maximum_gossip_clock_disparity()
Expand All @@ -65,10 +65,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
let head_block = &head.snapshot.beacon_block;
// check if we can process the optimistic update immediately
// otherwise queue
let canonical_root = rcv_optimistic_update
.attested_header
.beacon
.canonical_root();
let canonical_root = rcv_optimistic_update.get_canonical_root();

if canonical_root != head_block.message().parent_root() {
return Err(Error::UnknownBlockParentRoot(canonical_root));
Expand All @@ -84,7 +81,7 @@ impl<T: BeaconChainTypes> VerifiedLightClientOptimisticUpdate<T> {
return Err(Error::InvalidLightClientOptimisticUpdate);
}

let parent_root = rcv_optimistic_update.attested_header.beacon.parent_root;
let parent_root = rcv_optimistic_update.get_parent_root();
Ok(Self {
light_client_optimistic_update: rcv_optimistic_update,
parent_root,
Expand Down
67 changes: 31 additions & 36 deletions beacon_node/beacon_chain/src/light_client_server_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use types::light_client_update::{FinalizedRootProofLen, FINALIZED_ROOT_INDEX};
use types::non_zero_usize::new_non_zero_usize;
use types::{
BeaconBlockRef, BeaconState, ChainSpec, EthSpec, ForkName, Hash256, LightClientFinalityUpdate,
LightClientHeader, LightClientOptimisticUpdate, Slot, SyncAggregate,
LightClientOptimisticUpdate, Slot, SyncAggregate,
};

/// A prev block cache miss requires to re-generate the state of the post-parent block. Items in the
Expand Down Expand Up @@ -71,24 +71,26 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
/// results are cached either on disk or memory to be served via p2p and rest API
pub fn recompute_and_cache_updates(
&self,
log: &Logger,
store: BeaconStore<T>,
block_parent_root: &Hash256,
block_slot: Slot,
sync_aggregate: &SyncAggregate<T::EthSpec>,
log: &Logger,
chain_spec: &ChainSpec,
) -> Result<(), BeaconChainError> {
let _timer =
metrics::start_timer(&metrics::LIGHT_CLIENT_SERVER_CACHE_RECOMPUTE_UPDATES_TIMES);

let signature_slot = block_slot;
let attested_block_root = block_parent_root;

let attested_block = store.get_blinded_block(attested_block_root)?.ok_or(
BeaconChainError::DBInconsistent(format!(
"Block not available {:?}",
attested_block_root
)),
)?;
let attested_block =
store
.get_full_block(attested_block_root)?
.ok_or(BeaconChainError::DBInconsistent(format!(
"Block not available {:?}",
attested_block_root
)))?;

let cached_parts = self.get_or_compute_prev_block_cache(
store.clone(),
Expand All @@ -109,11 +111,12 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
};
if is_latest_optimistic {
// can create an optimistic update, that is more recent
*self.latest_optimistic_update.write() = Some(LightClientOptimisticUpdate {
attested_header: block_to_light_client_header(attested_block.message()),
sync_aggregate: sync_aggregate.clone(),
*self.latest_optimistic_update.write() = Some(LightClientOptimisticUpdate::new(
&attested_block,
sync_aggregate.clone(),
signature_slot,
});
chain_spec,
)?);
};

// Spec: Full nodes SHOULD provide the LightClientFinalityUpdate with the highest
Expand All @@ -127,17 +130,16 @@ impl<T: BeaconChainTypes> LightClientServerCache<T> {
if is_latest_finality & !cached_parts.finalized_block_root.is_zero() {
// Immediately after checkpoint sync the finalized block may not be available yet.
if let Some(finalized_block) =
store.get_blinded_block(&cached_parts.finalized_block_root)?
store.get_full_block(&cached_parts.finalized_block_root)?
{
*self.latest_finality_update.write() = Some(LightClientFinalityUpdate {
// TODO: may want to cache this result from latest_optimistic_update if producing a
// light_client header becomes expensive
attested_header: block_to_light_client_header(attested_block.message()),
finalized_header: block_to_light_client_header(finalized_block.message()),
finality_branch: cached_parts.finality_branch.clone(),
sync_aggregate: sync_aggregate.clone(),
*self.latest_finality_update.write() = Some(LightClientFinalityUpdate::new(
&attested_block,
&finalized_block,
cached_parts.finality_branch.clone(),
sync_aggregate.clone(),
signature_slot,
});
chain_spec,
)?);
} else {
debug!(
log,
Expand Down Expand Up @@ -214,7 +216,7 @@ impl LightClientCachedData {
}
}

// Implements spec priorization rules:
// Implements spec prioritization rules:
// > Full nodes SHOULD provide the LightClientFinalityUpdate with the highest attested_header.beacon.slot (if multiple, highest signature_slot)
//
// ref: https://github.com/ethereum/consensus-specs/blob/113c58f9bf9c08867f6f5f633c4d98e0364d612a/specs/altair/light-client/full-node.md#create_light_client_finality_update
Expand All @@ -223,14 +225,15 @@ fn is_latest_finality_update<T: EthSpec>(
attested_slot: Slot,
signature_slot: Slot,
) -> bool {
if attested_slot > prev.attested_header.beacon.slot {
let prev_slot = prev.get_attested_header_slot();
if attested_slot > prev_slot {
true
} else {
attested_slot == prev.attested_header.beacon.slot && signature_slot > prev.signature_slot
attested_slot == prev_slot && signature_slot > *prev.signature_slot()
}
}

// Implements spec priorization rules:
// Implements spec prioritization rules:
// > Full nodes SHOULD provide the LightClientOptimisticUpdate with the highest attested_header.beacon.slot (if multiple, highest signature_slot)
//
// ref: https://github.com/ethereum/consensus-specs/blob/113c58f9bf9c08867f6f5f633c4d98e0364d612a/specs/altair/light-client/full-node.md#create_light_client_optimistic_update
Expand All @@ -239,18 +242,10 @@ fn is_latest_optimistic_update<T: EthSpec>(
attested_slot: Slot,
signature_slot: Slot,
) -> bool {
if attested_slot > prev.attested_header.beacon.slot {
let prev_slot = prev.get_slot();
if attested_slot > prev_slot {
true
} else {
attested_slot == prev.attested_header.beacon.slot && signature_slot > prev.signature_slot
}
}

fn block_to_light_client_header<T: EthSpec>(
block: BeaconBlockRef<T, types::BlindedPayload<T>>,
) -> LightClientHeader {
// TODO: make fork aware
LightClientHeader {
beacon: block.block_header(),
attested_slot == prev_slot && signature_slot > *prev.signature_slot()
}
}
4 changes: 2 additions & 2 deletions beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2337,7 +2337,7 @@ pub fn serve<T: BeaconChainTypes>(

let fork_name = chain
.spec
.fork_name_at_slot::<T::EthSpec>(update.signature_slot);
.fork_name_at_slot::<T::EthSpec>(*update.signature_slot());
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.status(200)
Expand Down Expand Up @@ -2384,7 +2384,7 @@ pub fn serve<T: BeaconChainTypes>(

let fork_name = chain
.spec
.fork_name_at_slot::<T::EthSpec>(update.signature_slot);
.fork_name_at_slot::<T::EthSpec>(*update.signature_slot());
match accept_header {
Some(api_types::Accept::Ssz) => Response::builder()
.status(200)
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/http_api/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1717,7 +1717,7 @@ impl ApiTester {
};

let expected = block.slot();
assert_eq!(result.header.beacon.slot, expected);
assert_eq!(result.get_slot(), expected);

self
}
Expand Down
15 changes: 12 additions & 3 deletions beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,9 +590,18 @@ fn handle_rpc_response<T: EthSpec>(
SupportedProtocol::MetaDataV1 => Ok(Some(RPCResponse::MetaData(MetaData::V1(
MetaDataV1::from_ssz_bytes(decoded_buffer)?,
)))),
SupportedProtocol::LightClientBootstrapV1 => Ok(Some(RPCResponse::LightClientBootstrap(
LightClientBootstrap::from_ssz_bytes(decoded_buffer)?,
))),
SupportedProtocol::LightClientBootstrapV1 => match fork_name {
Some(fork_name) => Ok(Some(RPCResponse::LightClientBootstrap(Arc::new(
LightClientBootstrap::from_ssz_bytes(decoded_buffer, fork_name)?,
)))),
None => Err(RPCError::ErrorResponse(
RPCResponseErrorCode::InvalidRequest,
format!(
"No context bytes provided for {:?} response",
versioned_protocol
),
)),
},
// MetaData V2 responses have no context bytes, so behave similarly to V1 responses
SupportedProtocol::MetaDataV2 => Ok(Some(RPCResponse::MetaData(MetaData::V2(
MetaDataV2::from_ssz_bytes(decoded_buffer)?,
Expand Down
8 changes: 2 additions & 6 deletions beacon_node/lighthouse_network/src/rpc/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ pub enum RPCResponse<T: EthSpec> {
BlobsByRange(Arc<BlobSidecar<T>>),

/// A response to a get LIGHT_CLIENT_BOOTSTRAP request.
LightClientBootstrap(LightClientBootstrap<T>),
LightClientBootstrap(Arc<LightClientBootstrap<T>>),

/// A response to a get BLOBS_BY_ROOT request.
BlobsByRoot(Arc<BlobSidecar<T>>),
Expand Down Expand Up @@ -569,11 +569,7 @@ impl<T: EthSpec> std::fmt::Display for RPCResponse<T> {
RPCResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
RPCResponse::MetaData(metadata) => write!(f, "Metadata: {}", metadata.seq_number()),
RPCResponse::LightClientBootstrap(bootstrap) => {
write!(
f,
"LightClientBootstrap Slot: {}",
bootstrap.header.beacon.slot
)
write!(f, "LightClientBootstrap Slot: {}", bootstrap.get_slot())
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/lighthouse_network/src/service/api_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub enum Response<TSpec: EthSpec> {
/// A response to a get BLOBS_BY_ROOT request.
BlobsByRoot(Option<Arc<BlobSidecar<TSpec>>>),
/// A response to a LightClientUpdate request.
LightClientBootstrap(LightClientBootstrap<TSpec>),
LightClientBootstrap(Arc<LightClientBootstrap<TSpec>>),
}

impl<TSpec: EthSpec> std::convert::From<Response<TSpec>> for RPCCodedResponse<TSpec> {
Expand Down
26 changes: 20 additions & 6 deletions beacon_node/lighthouse_network/src/types/pubsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,17 +264,31 @@ impl<T: EthSpec> PubsubMessage<T> {
)))
}
GossipKind::LightClientFinalityUpdate => {
let light_client_finality_update =
LightClientFinalityUpdate::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?;
let light_client_finality_update = match fork_context.from_context_bytes(gossip_topic.fork_digest) {
Some(&fork_name) => {
LightClientFinalityUpdate::from_ssz_bytes(data, fork_name)
.map_err(|e| format!("{:?}", e))?
},
None => return Err(format!(
"light_client_finality_update topic invalid for given fork digest {:?}",
gossip_topic.fork_digest
)),
};
Ok(PubsubMessage::LightClientFinalityUpdate(Box::new(
light_client_finality_update,
)))
}
GossipKind::LightClientOptimisticUpdate => {
let light_client_optimistic_update =
LightClientOptimisticUpdate::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?;
let light_client_optimistic_update = match fork_context.from_context_bytes(gossip_topic.fork_digest) {
Some(&fork_name) => {
LightClientOptimisticUpdate::from_ssz_bytes(data, fork_name)
.map_err(|e| format!("{:?}", e))?
},
None => return Err(format!(
"light_client_optimistic_update topic invalid for given fork digest {:?}",
gossip_topic.fork_digest
)),
};
Ok(PubsubMessage::LightClientOptimisticUpdate(Box::new(
light_client_optimistic_update,
)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
match self.chain.get_light_client_bootstrap(&block_root) {
Ok(Some((bootstrap, _))) => self.send_response(
peer_id,
Response::LightClientBootstrap(bootstrap),
Response::LightClientBootstrap(Arc::new(bootstrap)),
request_id,
),
Ok(None) => self.send_error_response(
Expand Down
Loading