diff --git a/client/beefy/src/lib.rs b/client/beefy/src/lib.rs index 9b2bf383df8ef..56c9c360d3d13 100644 --- a/client/beefy/src/lib.rs +++ b/client/beefy/src/lib.rs @@ -24,7 +24,7 @@ use prometheus::Registry; use sc_client_api::{Backend, BlockchainEvents, Finalizer}; use sc_network_gossip::{GossipEngine, Network as GossipNetwork}; -use sp_api::ProvideRuntimeApi; +use sp_api::{NumberFor, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; use sp_keystore::SyncCryptoStorePtr; use sp_runtime::traits::Block; @@ -111,7 +111,7 @@ where B: Block, BE: Backend, C: Client, - C::Api: BeefyApi, + C::Api: BeefyApi>, N: GossipNetwork + Clone + Send + 'static, { /// BEEFY client @@ -142,7 +142,7 @@ where B: Block, BE: Backend, C: Client, - C::Api: BeefyApi, + C::Api: BeefyApi>, N: GossipNetwork + Clone + Send + 'static, { let BeefyParams { diff --git a/client/beefy/src/worker.rs b/client/beefy/src/worker.rs index 0c7d8d4ffdc9c..c574d5ba1f892 100644 --- a/client/beefy/src/worker.rs +++ b/client/beefy/src/worker.rs @@ -100,7 +100,7 @@ where B: Block + Codec, BE: Backend, C: Client, - C::Api: BeefyApi, + C::Api: BeefyApi>, { /// Return a new BEEFY worker instance. /// @@ -146,10 +146,20 @@ where B: Block, BE: Backend, C: Client, - C::Api: BeefyApi, + C::Api: BeefyApi>, { - /// Return `true`, if we should vote on block `number` - fn should_vote_on(&self, number: NumberFor) -> bool { + /// Return `true`, if we should vote on block `header` + fn should_vote_on(&self, header: &B::Header) -> bool { + let number = *header.number(); + let at = BlockId::hash(header.hash()); + let session_boundary = self.client.runtime_api().get_session_boundary(&at).ok(); + let session_start = if let Some(session_boundary) = session_boundary { + session_boundary + } else { + warn!(target: "beefy", "🥩 Could not retrieve session boundary- won't vote for: {:?}", number); + return false + }; + let best_beefy_block = if let Some(block) = self.best_beefy_block { block } else { @@ -157,7 +167,12 @@ where return false }; - let target = vote_target(self.best_grandpa_block, best_beefy_block, self.min_block_delta); + let target = vote_target( + self.best_grandpa_block, + best_beefy_block, + session_start, + self.min_block_delta, + ); trace!(target: "beefy", "🥩 should_vote_on: #{:?}, next_block_to_vote_on: #{:?}", number, target); @@ -258,7 +273,7 @@ where } } - if self.should_vote_on(*notification.header.number()) { + if self.should_vote_on(¬ification.header) { let (validators, validator_set_id) = if let Some(rounds) = &self.rounds { (rounds.validators(), rounds.validator_set_id()) } else { @@ -461,13 +476,18 @@ where } /// Calculate next block number to vote on -fn vote_target(best_grandpa: N, best_beefy: N, min_delta: u32) -> N +fn vote_target(best_grandpa: N, best_beefy: N, session_start: N, min_delta: u32) -> N where N: AtLeast32Bit + Copy + Debug, { + // if the session_start as a mandatory block does not have a beefy justification yet, + // we vote on it + let m: u32 = if session_start <= best_beefy { 1 } else { 0 }; + let diff = best_grandpa.saturating_sub(best_beefy); - let diff = diff.saturated_into::(); - let target = best_beefy + min_delta.max(diff.next_power_of_two()).into(); + let diff = diff.saturated_into::() / 2; + let target = (session_start * (1u32 - m).into()) + + ((best_beefy + min_delta.max(diff.next_power_of_two().into()).into()) * m.into()); trace!( target: "beefy", @@ -485,90 +505,69 @@ mod tests { use super::vote_target; #[test] - fn vote_on_min_block_delta() { - let t = vote_target(1u32, 0, 4); - assert_eq!(4, t); - let t = vote_target(2u32, 0, 4); - assert_eq!(4, t); - let t = vote_target(3u32, 0, 4); - assert_eq!(4, t); - let t = vote_target(4u32, 0, 4); - assert_eq!(4, t); - - let t = vote_target(4u32, 4, 4); - assert_eq!(8, t); - - let t = vote_target(10u32, 10, 4); - assert_eq!(14, t); - let t = vote_target(11u32, 10, 4); - assert_eq!(14, t); - let t = vote_target(12u32, 10, 4); - assert_eq!(14, t); - let t = vote_target(13u32, 10, 4); - assert_eq!(14, t); - - let t = vote_target(10u32, 10, 8); - assert_eq!(18, t); - let t = vote_target(11u32, 10, 8); - assert_eq!(18, t); - let t = vote_target(12u32, 10, 8); - assert_eq!(18, t); - let t = vote_target(13u32, 10, 8); - assert_eq!(18, t); + fn vote_on_mandatory_block() { + let t = vote_target(1008u32, 1000, 1001, 4); + assert_eq!(1001, t); + + let t = vote_target(1016u32, 1000, 1008, 4); + assert_eq!(1008, t); + + let t = vote_target(105u32, 100, 101, 4); + assert_eq!(101, t); } #[test] fn vote_on_power_of_two() { - let t = vote_target(1008u32, 1000, 4); + let t = vote_target(1008u32, 1000, 999, 4); + assert_eq!(1004, t); + + let t = vote_target(1016u32, 1000, 999, 4); assert_eq!(1008, t); - let t = vote_target(1016u32, 1000, 4); + let t = vote_target(1032u32, 1000, 999, 4); assert_eq!(1016, t); - let t = vote_target(1032u32, 1000, 4); + let t = vote_target(1064u32, 1000, 999, 4); assert_eq!(1032, t); - let t = vote_target(1064u32, 1000, 4); + let t = vote_target(1128u32, 1000, 999, 4); assert_eq!(1064, t); - let t = vote_target(1128u32, 1000, 4); + let t = vote_target(1256u32, 1000, 999, 4); assert_eq!(1128, t); - let t = vote_target(1256u32, 1000, 4); + let t = vote_target(1512u32, 1000, 999, 4); assert_eq!(1256, t); - let t = vote_target(1512u32, 1000, 4); - assert_eq!(1512, t); - - let t = vote_target(1024u32, 0, 4); - assert_eq!(1024, t); + let t = vote_target(1024u32, 0, 0, 4); + assert_eq!(512, t); } #[test] fn vote_on_target_block() { - let t = vote_target(1008u32, 1002, 4); - assert_eq!(1010, t); - let t = vote_target(1010u32, 1002, 4); - assert_eq!(1010, t); - - let t = vote_target(1016u32, 1006, 4); - assert_eq!(1022, t); - let t = vote_target(1022u32, 1006, 4); - assert_eq!(1022, t); - - let t = vote_target(1032u32, 1012, 4); - assert_eq!(1044, t); - let t = vote_target(1044u32, 1012, 4); - assert_eq!(1044, t); - - let t = vote_target(1064u32, 1014, 4); - assert_eq!(1078, t); - let t = vote_target(1078u32, 1014, 4); - assert_eq!(1078, t); - - let t = vote_target(1128u32, 1008, 4); - assert_eq!(1136, t); - let t = vote_target(1136u32, 1008, 4); - assert_eq!(1136, t); + let t = vote_target(1008u32, 1002, 1001, 4); + assert_eq!(1006, t); + let t = vote_target(1010u32, 1002, 1001, 4); + assert_eq!(1006, t); + + let t = vote_target(1016u32, 1006, 1004, 4); + assert_eq!(1014, t); + let t = vote_target(1022u32, 1006, 1004, 4); + assert_eq!(1014, t); + + let t = vote_target(1032u32, 1012, 1010, 4); + assert_eq!(1028, t); + let t = vote_target(1044u32, 1012, 1010, 4); + assert_eq!(1028, t); + + let t = vote_target(1064u32, 1014, 1012, 4); + assert_eq!(1046, t); + let t = vote_target(1078u32, 1014, 1012, 4); + assert_eq!(1046, t); + + let t = vote_target(1128u32, 1008, 1004, 4); + assert_eq!(1072, t); + let t = vote_target(1136u32, 1008, 1004, 4); + assert_eq!(1072, t); } } diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index 8dbdd66f3559b..7ce07b75997e8 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -156,10 +156,12 @@ pub struct VoteMessage { sp_api::decl_runtime_apis! { /// API necessary for BEEFY voters. - pub trait BeefyApi + pub trait BeefyApi { /// Return the current active BEEFY validator set fn validator_set() -> Option>; + /// Returns the block number at which the current session began + fn get_session_boundary() -> BlockNumber; } }