Skip to content

Commit

Permalink
feat: Add epoch and epoch slot values to Block record (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
rvcas authored Mar 25, 2022
1 parent 4e7e4aa commit 10c0ad4
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 14 deletions.
4 changes: 4 additions & 0 deletions src/mapper/byron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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(),
Expand Down
9 changes: 9 additions & 0 deletions src/mapper/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::model::{
StakeCredential, TransactionRecord, TxInputRecord, TxOutputRecord,
};

use crate::utils::time::TimeProvider;
use crate::Error;

use super::EventWriter;
Expand Down Expand Up @@ -359,6 +360,12 @@ impl EventWriter {
cbor: &[u8],
era: Era,
) -> Result<BlockRecord, Error> {
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,
Expand All @@ -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(),
Expand Down
2 changes: 2 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ pub enum StakeCredential {
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockRecord {
pub era: Era,
pub epoch: Option<u64>,
pub epoch_slot: Option<u64>,
pub body_size: usize,
pub issuer_vkey: String,
pub tx_count: usize,
Expand Down
6 changes: 6 additions & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"
Expand All @@ -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"
Expand Down
97 changes: 83 additions & 14 deletions src/utils/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
}
}
}

Expand All @@ -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(
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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);
}
}

0 comments on commit 10c0ad4

Please sign in to comment.