From 1d677ddb55c88e8aa137fb678953436e98a6eb2c Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 11 Mar 2022 18:24:33 -0500 Subject: [PATCH 1/2] feat(BlockRecord): add new fields --- src/model.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/model.rs b/src/model.rs index 3b499032..d845c66e 100644 --- a/src/model.rs +++ b/src/model.rs @@ -175,6 +175,8 @@ pub enum StakeCredential { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct BlockRecord { pub era: Era, + pub epoch: u64, + pub slot_in_epoch: u64, pub body_size: usize, pub issuer_vkey: String, pub tx_count: usize, From 597cafe93d4c9f96f924b729634f1290a3484507 Mon Sep 17 00:00:00 2001 From: Santiago Carmuega Date: Fri, 25 Mar 2022 09:26:06 -0300 Subject: [PATCH 2/2] Implement epoch math --- src/mapper/byron.rs | 4 ++ src/mapper/map.rs | 9 +++++ src/model.rs | 4 +- src/utils/mod.rs | 6 +++ src/utils/time.rs | 97 ++++++++++++++++++++++++++++++++++++++------- 5 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/mapper/byron.rs b/src/mapper/byron.rs index 6d0357f4..168146c1 100644 --- a/src/mapper/byron.rs +++ b/src/mapper/byron.rs @@ -150,6 +150,8 @@ impl EventWriter { hash: hash.to_hex(), number: source.header.consensus_data.2[0], slot: source.header.consensus_data.0.to_abs_slot(), + epoch: Some(source.header.consensus_data.0.epoch), + epoch_slot: Some(source.header.consensus_data.0.slot), previous_hash: source.header.prev_block.to_hex(), cbor_hex: match self.config.include_block_cbor { true => hex::encode(cbor).into(), @@ -201,6 +203,8 @@ impl EventWriter { tx_count: 0, number: source.header.consensus_data.difficulty[0], slot: source.header.to_abs_slot(), + epoch: Some(source.header.consensus_data.epoch_id), + epoch_slot: Some(0), previous_hash: source.header.prev_block.to_hex(), cbor_hex: match self.config.include_block_cbor { true => hex::encode(cbor).into(), diff --git a/src/mapper/map.rs b/src/mapper/map.rs index e1fb6819..982be2ec 100644 --- a/src/mapper/map.rs +++ b/src/mapper/map.rs @@ -16,6 +16,7 @@ use crate::model::{ StakeCredential, TransactionRecord, TxInputRecord, TxOutputRecord, }; +use crate::utils::time::TimeProvider; use crate::Error; use super::EventWriter; @@ -359,6 +360,12 @@ impl EventWriter { cbor: &[u8], era: Era, ) -> Result { + let relative_epoch = self + .utils + .time + .as_ref() + .map(|time| time.absolute_slot_to_relative(source.header.header_body.slot)); + Ok(BlockRecord { era, body_size: source.header.header_body.block_body_size as usize, @@ -367,6 +374,8 @@ impl EventWriter { hash: hex::encode(hash), number: source.header.header_body.block_number, slot: source.header.header_body.slot, + epoch: relative_epoch.map(|(epoch, _)| epoch), + epoch_slot: relative_epoch.map(|(_, epoch_slot)| epoch_slot), previous_hash: hex::encode(source.header.header_body.prev_hash), cbor_hex: match self.config.include_block_cbor { true => hex::encode(cbor).into(), diff --git a/src/model.rs b/src/model.rs index 625adc30..aaf1115c 100644 --- a/src/model.rs +++ b/src/model.rs @@ -175,8 +175,8 @@ pub enum StakeCredential { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct BlockRecord { pub era: Era, - pub epoch: u64, - pub slot_in_epoch: u64, + pub epoch: Option, + pub epoch_slot: Option, pub body_size: usize, pub issuer_vkey: String, pub tx_count: usize, diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 95ce21c2..e662be83 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -52,10 +52,12 @@ impl SwallowResult for Result<(), Error> { /// values. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ChainWellKnownInfo { + pub byron_epoch_length: u32, pub byron_slot_length: u32, pub byron_known_slot: u64, pub byron_known_hash: String, pub byron_known_time: u64, + pub shelley_epoch_length: u32, pub shelley_slot_length: u32, pub shelley_known_slot: u64, pub shelley_known_hash: String, @@ -67,11 +69,13 @@ impl ChainWellKnownInfo { /// Hardcoded values for mainnet pub fn mainnet() -> Self { ChainWellKnownInfo { + byron_epoch_length: 432000, byron_slot_length: 20, byron_known_slot: 0, byron_known_time: 1506203091, byron_known_hash: "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f" .to_string(), + shelley_epoch_length: 432000, shelley_slot_length: 1, shelley_known_slot: 4492800, shelley_known_hash: "aa83acbf5904c0edfe4d79b3689d3d00fcfc553cf360fd2229b98d464c28e9de" @@ -84,11 +88,13 @@ impl ChainWellKnownInfo { /// Hardcoded values for testnet pub fn testnet() -> Self { ChainWellKnownInfo { + byron_epoch_length: 432000, byron_slot_length: 20, byron_known_slot: 0, byron_known_time: 1564010416, byron_known_hash: "8f8602837f7c6f8b8867dd1cbc1842cf51a27eaed2c70ef48325d00f8efb320f" .to_string(), + shelley_epoch_length: 432000, shelley_slot_length: 1, shelley_known_slot: 1598400, shelley_known_hash: "02b1c561715da9e540411123a6135ee319b02f60b9a11a603d3305556c04329f" diff --git a/src/utils/time.rs b/src/utils/time.rs index 638dc452..da2bef4d 100644 --- a/src/utils/time.rs +++ b/src/utils/time.rs @@ -8,6 +8,7 @@ use crate::utils::ChainWellKnownInfo; pub(crate) trait TimeProvider { /// Maps between slots and wallclock fn slot_to_wallclock(&self, slot: u64) -> u64; + fn absolute_slot_to_relative(&self, slot: u64) -> (u64, u64); } /// A naive, standalone implementation of a time provider @@ -17,11 +18,33 @@ pub(crate) trait TimeProvider { /// logic from a well-known configured point in the chain, assuming homogeneous /// slot length from that point forward. #[derive(Clone)] -pub(crate) struct NaiveProvider(ChainWellKnownInfo); +pub(crate) struct NaiveProvider { + config: ChainWellKnownInfo, + shelley_start_epoch: u64, +} impl NaiveProvider { pub fn new(config: ChainWellKnownInfo) -> Self { - NaiveProvider(config) + assert!( + config.byron_epoch_length > 0, + "byron epoch length needs to be greater than zero" + ); + + assert!( + config.shelley_epoch_length > 0, + "shelley epoch length needs to be greater than zero" + ); + + let (shelley_start_epoch, _) = compute_era_epoch( + config.shelley_known_slot, + config.byron_slot_length as u64, + config.byron_epoch_length as u64, + ); + + NaiveProvider { + config, + shelley_start_epoch, + } } } @@ -35,9 +58,17 @@ fn compute_linear_timestamp( known_time + (query_slot - known_slot) * slot_length } +#[inline] +fn compute_era_epoch(era_slot: u64, era_slot_length: u64, era_epoch_length: u64) -> (u64, u64) { + let epoch = (era_slot * era_slot_length) / era_epoch_length; + let reminder = era_slot % era_epoch_length; + + (epoch, reminder) +} + impl TimeProvider for NaiveProvider { fn slot_to_wallclock(&self, slot: u64) -> u64 { - let NaiveProvider(config) = self; + let NaiveProvider { config, .. } = self; if slot < config.shelley_known_slot { compute_linear_timestamp( @@ -55,16 +86,50 @@ impl TimeProvider for NaiveProvider { ) } } + + fn absolute_slot_to_relative(&self, slot: u64) -> (u64, u64) { + let NaiveProvider { + config, + shelley_start_epoch, + } = self; + + if slot < config.shelley_known_slot { + compute_era_epoch( + slot, + config.byron_slot_length as u64, + config.byron_epoch_length as u64, + ) + } else { + let era_slot = slot - config.shelley_known_slot; + + let (era_epoch, reminder) = compute_era_epoch( + era_slot, + config.shelley_slot_length as u64, + config.shelley_epoch_length as u64, + ); + + (shelley_start_epoch + era_epoch, reminder) + } + } } #[cfg(test)] mod tests { use super::*; - fn assert_slot_matches_timestamp(provider: &NaiveProvider, slot: u64, ts: u64) { + fn assert_slot_matches_timestamp( + provider: &NaiveProvider, + slot: u64, + expected_ts: u64, + expected_epoch: u64, + expected_epoch_slot: u64, + ) { let wallclock = provider.slot_to_wallclock(slot); + assert_eq!(wallclock, expected_ts); - assert_eq!(wallclock, ts); + let (epoch, epoch_slot) = provider.absolute_slot_to_relative(slot); + assert_eq!(epoch, expected_epoch); + assert_eq!(epoch_slot, expected_epoch_slot); } #[test] @@ -73,19 +138,23 @@ mod tests { // Byron start, value copied from: // https://explorer.cardano.org/en/block?id=f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f - assert_slot_matches_timestamp(&provider, 0, 1506203091); + assert_slot_matches_timestamp(&provider, 0, 1506203091, 0, 0); // Byron middle, value copied from: // https://explorer.cardano.org/en/block?id=c1b57d58761af4dc3c6bdcb3542170cec6db3c81e551cd68012774d1c38129a3 - assert_slot_matches_timestamp(&provider, 2160007, 1549403231); + assert_slot_matches_timestamp(&provider, 2160007, 1549403231, 100, 7); // Shelley start, value copied from: // https://explorer.cardano.org/en/block?id=aa83acbf5904c0edfe4d79b3689d3d00fcfc553cf360fd2229b98d464c28e9de - assert_slot_matches_timestamp(&provider, 4492800, 1596059091); + assert_slot_matches_timestamp(&provider, 4492800, 1596059091, 208, 0); // Shelly middle, value copied from: // https://explorer.cardano.org/en/block?id=ca60833847d0e70a1adfa6b7f485766003cf7d96d28d481c20d4390f91b76d68 - assert_slot_matches_timestamp(&provider, 51580240, 1643146531); + assert_slot_matches_timestamp(&provider, 51580240, 1643146531, 316, 431440); + + // Shelly middle, value copied from: + // https://explorer.cardano.org/en/block?id=ec07c6f74f344062db5340480e5b364aac8bb40768d184c1b1491e05c5bec4c4 + assert_slot_matches_timestamp(&provider, 54605026, 1646171317, 324, 226); } #[test] @@ -94,22 +163,22 @@ mod tests { // Byron origin, value copied from: // https://explorer.cardano-testnet.iohkdev.io/en/block?id=8f8602837f7c6f8b8867dd1cbc1842cf51a27eaed2c70ef48325d00f8efb320f - assert_slot_matches_timestamp(&provider, 0, 1564010416); + assert_slot_matches_timestamp(&provider, 0, 1564010416, 0, 0); // Byron start, value copied from: // https://explorer.cardano-testnet.iohkdev.io/en/block?id=388a82f053603f3552717d61644a353188f2d5500f4c6354cc1ad27a36a7ea91 - assert_slot_matches_timestamp(&provider, 1031, 1564031036); + assert_slot_matches_timestamp(&provider, 1031, 1564031036, 0, 1031); // Byron middle, value copied from: // https://explorer.cardano-testnet.iohkdev.io/en/block?id=66102c0b80e1eebc9cddf9cab43c1bf912e4f1963d6f3b8ff948952f8409e779 - assert_slot_matches_timestamp(&provider, 561595, 1575242316); + assert_slot_matches_timestamp(&provider, 561595, 1575242316, 25, 129595); // Shelley start, value copied from: // https://explorer.cardano-testnet.iohkdev.io/en/block?id=02b1c561715da9e540411123a6135ee319b02f60b9a11a603d3305556c04329f - assert_slot_matches_timestamp(&provider, 1598400, 1595967616); + assert_slot_matches_timestamp(&provider, 1598400, 1595967616, 74, 0); // Shelley middle, value copied from: // https://explorer.cardano-testnet.iohkdev.io/en/block?id=26a1b5a649309c0c8dd48f3069d9adea5a27edf5171dfb941b708acaf2d76dcd - assert_slot_matches_timestamp(&provider, 48783593, 1643152809); + assert_slot_matches_timestamp(&provider, 48783593, 1643152809, 183, 97193); } }