Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@

- [#7446](https://github.com/ChainSafe/forest/pull/7446): Fixed a panic condition on `ChainNotify` when a client closes a connection just after subscription.

- [#7413](https://github.com/ChainSafe/forest/issues/7413): Block validation on quicknet now rejects headers whose beacon entries do not cover every epoch since the parent tipset

## Forest v0.35.0 "Shravan"

Non-mandatory release for all node operators. It includes some fixes and improvements, notably around state-related RPC. Note that this release contains breaking changes, so please read the changelog carefully before upgrading.
Expand Down
6 changes: 4 additions & 2 deletions src/beacon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub use drand::*;
#[cfg(test)]
pub mod mock_beacon;
#[cfg(test)]
mod tests {
mod drand;
pub mod tests {
// `pub` so that helpers such as `drand::new_beacon_quicknet` can be shared with
// tests in other modules.
pub mod drand;
}
2 changes: 1 addition & 1 deletion src/beacon/tests/drand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn new_beacon_mainnet() -> DrandBeacon {
)
}

fn new_beacon_quicknet() -> DrandBeacon {
pub fn new_beacon_quicknet() -> DrandBeacon {
DrandBeacon::new(
1598306400,
30,
Expand Down
130 changes: 129 additions & 1 deletion src/blocks/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,27 @@ impl RawBlockHeader {
));
}

// ref: https://github.com/filecoin-project/lotus/blob/27abf0f16a7f2a83305910f3c2a1844764d20b75/chain/beacon/beacon.go#L95
if curr_beacon.network().is_unchained() {
Comment thread
EclesioMeloJunior marked this conversation as resolved.
for (idx, beacon_entry) in self.beacon_entries.iter().enumerate() {
let lookup_epoch = parent_epoch + 1 + idx as i64;

let expected_round =
curr_beacon.max_beacon_round_for_epoch(network_version, lookup_epoch);
if beacon_entry.round() != expected_round {
return Err(Error::Validation(
format!(
"expected max round for epoch {} to be {}, got: {}",
lookup_epoch,
expected_round,
beacon_entry.round(),
)
.into(),
));
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if !curr_beacon
.verify_entries(&self.beacon_entries, prev_entry)
.map_err(|e| Error::Validation(format!("{e:#}").into()))?
Expand Down Expand Up @@ -352,7 +373,10 @@ impl<'de> Deserialize<'de> for CachingBlockHeader {
#[cfg(test)]
mod tests {
use super::*;
use crate::beacon::{BeaconEntry, BeaconPoint, BeaconSchedule, mock_beacon::MockBeacon};
use crate::beacon::{
BeaconEntry, BeaconPoint, BeaconSchedule, mock_beacon::MockBeacon,
tests::drand::new_beacon_quicknet,
};
use crate::blocks::{CachingBlockHeader, Error};
use crate::shim::clock::ChainEpoch;
use crate::shim::{address::Address, version::NetworkVersion};
Expand All @@ -361,6 +385,7 @@ mod tests {
use crate::utils::multihash::MultihashCode;
use cid::Cid;
use fvm_ipld_encoding::{DAG_CBOR, to_vec};
use rstest::{fixture, rstest};

impl quickcheck::Arbitrary for CachingBlockHeader {
fn arbitrary(g: &mut quickcheck::Gen) -> Self {
Expand Down Expand Up @@ -432,4 +457,107 @@ mod tests {
*FILECOIN_GENESIS_CID
);
}

#[fixture]
#[once]
fn schedule() -> BeaconSchedule {
BeaconSchedule(vec![BeaconPoint::new(0, new_beacon_quicknet())])
}

#[derive(Debug)]
struct BeaconEntriesCase {
parent_epoch: ChainEpoch,
prev_round: u64,
epoch: ChainEpoch,
rounds: Vec<u64>,
accepted: bool,
}

#[rstest]
#[case::no_null_round(BeaconEntriesCase {
parent_epoch: 6216199,
prev_round: 30662992,
epoch: 6216200,
rounds: vec![30663002],
accepted: true,
})]
#[case::null_round_both_entries(BeaconEntriesCase {
parent_epoch: 6216198,
prev_round: 30662982,
epoch: 6216200,
rounds: vec![30662992, 30663002],
accepted: true,
})]
#[case::null_round_invalid_entry(BeaconEntriesCase {
parent_epoch: 6216198,
prev_round: 30662982,
epoch: 6216200,
rounds: vec![30662990, 30663002],
accepted: false,
})]
#[case::null_round_missing_null_entry(BeaconEntriesCase {
parent_epoch: 6216198,
prev_round: 30662982,
epoch: 6216200,
rounds: vec![30663002],
accepted: false,
})]
#[case::null_round_missing_own_entry(BeaconEntriesCase {
parent_epoch: 6216198,
prev_round: 30662982,
epoch: 6216200,
rounds: vec![30662992],
accepted: false,
})]
Comment thread
EclesioMeloJunior marked this conversation as resolved.
#[case::extra_trailing_entry(BeaconEntriesCase {
parent_epoch: 6216199,
prev_round: 30662992,
epoch: 6216200,
rounds: vec![30663002, 30663002],
accepted: false,
})]
#[case::null_round_extra_trailing_entry(BeaconEntriesCase {
Comment thread
EclesioMeloJunior marked this conversation as resolved.
parent_epoch: 6216198,
prev_round: 30662982,
epoch: 6216200,
rounds: vec![30662992, 30663002, 30663002],
accepted: false,
})]
#[tokio::test]
async fn validate_beacon_entries_on_quicknet(
schedule: &BeaconSchedule,
#[case] case: BeaconEntriesCase,
) {
let BeaconEntriesCase {
parent_epoch,
prev_round,
epoch,
rounds,
accepted,
} = case;

let (_, curr_beacon) = schedule.beacon_for_epoch(epoch).unwrap();

let mut beacon_entries = Vec::with_capacity(rounds.len());
for round in rounds {
beacon_entries.push(curr_beacon.entry(round).await.unwrap());
}

let prev_entry = BeaconEntry::new(prev_round, vec![]);
let header = RawBlockHeader {
miner_address: Address::new_id(0),
epoch,
beacon_entries,
..Default::default()
};

let result =
header.validate_block_drand(NetworkVersion::V22, schedule, parent_epoch, &prev_entry);

assert_eq!(
result.is_ok(),
accepted,
"epoch {epoch}, parent {parent_epoch}: {result:?}"
);
}
}
Loading