use scale::Encode;
use subxt::Metadata;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsensusType {
Aura,
Babe,
Unknown,
}
const AURA_PALLET: &str = "Aura";
const BABE_PALLET: &str = "Babe";
pub fn aura_current_slot_key() -> Vec<u8> {
let pallet_hash = sp_core::twox_128(b"Aura");
let storage_hash = sp_core::twox_128(b"CurrentSlot");
[pallet_hash.as_slice(), storage_hash.as_slice()].concat()
}
pub fn babe_current_slot_key() -> Vec<u8> {
let pallet_hash = sp_core::twox_128(b"Babe");
let storage_hash = sp_core::twox_128(b"CurrentSlot");
[pallet_hash.as_slice(), storage_hash.as_slice()].concat()
}
pub fn detect_consensus_type(metadata: &Metadata) -> ConsensusType {
if metadata.pallet_by_name(AURA_PALLET).is_some() {
ConsensusType::Aura
} else if metadata.pallet_by_name(BABE_PALLET).is_some() {
ConsensusType::Babe
} else {
ConsensusType::Unknown
}
}
pub fn calculate_next_slot(current_timestamp_ms: u64, slot_duration_ms: u64) -> u64 {
assert!(slot_duration_ms > 0, "Slot duration cannot be zero");
let next_timestamp = current_timestamp_ms.saturating_add(slot_duration_ms);
next_timestamp / slot_duration_ms
}
pub fn encode_aura_slot(slot: u64) -> Vec<u8> {
slot.encode()
}
pub fn encode_babe_predigest(slot: u64, authority_index: u32) -> Vec<u8> {
const SECONDARY_PLAIN_INDEX: u8 = 2;
let mut encoded = vec![SECONDARY_PLAIN_INDEX];
encoded.extend(authority_index.encode());
encoded.extend(slot.encode());
encoded
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn calculate_next_slot_works_correctly() {
assert_eq!(calculate_next_slot(12_000, 6_000), 3);
assert_eq!(calculate_next_slot(0, 6_000), 1);
assert_eq!(calculate_next_slot(5_999, 6_000), 1);
assert_eq!(calculate_next_slot(6_000, 6_000), 2);
}
#[test]
fn calculate_next_slot_with_parachain_duration() {
const PARA_SLOT_DURATION: u64 = 12_000;
assert_eq!(calculate_next_slot(0, PARA_SLOT_DURATION), 1);
assert_eq!(calculate_next_slot(12_000, PARA_SLOT_DURATION), 2);
assert_eq!(calculate_next_slot(24_000, PARA_SLOT_DURATION), 3);
}
#[test]
fn calculate_next_slot_saturates_on_overflow() {
let large_timestamp = u64::MAX - 1000;
let result = calculate_next_slot(large_timestamp, 6_000);
assert_eq!(result, u64::MAX / 6_000);
}
#[test]
#[should_panic(expected = "Slot duration cannot be zero")]
fn calculate_next_slot_panics_on_zero_duration() {
calculate_next_slot(12_000, 0);
}
#[test]
fn encode_aura_slot_produces_u64_le() {
let slot: u64 = 12345;
let encoded = encode_aura_slot(slot);
assert_eq!(encoded.len(), 8);
assert_eq!(encoded, slot.to_le_bytes());
}
#[test]
fn encode_babe_predigest_produces_correct_format() {
let slot: u64 = 295033271;
let authority_index: u32 = 0;
let encoded = encode_babe_predigest(slot, authority_index);
assert_eq!(encoded.len(), 13);
assert_eq!(encoded[0], 2);
assert_eq!(&encoded[1..5], &authority_index.to_le_bytes());
assert_eq!(&encoded[5..13], &slot.to_le_bytes());
}
#[test]
fn encode_babe_predigest_with_authority_index() {
let slot: u64 = 100;
let authority_index: u32 = 5;
let encoded = encode_babe_predigest(slot, authority_index);
assert_eq!(&encoded[1..5], &5u32.to_le_bytes());
}
}