Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement multi-era time #155

Merged
merged 1 commit into from
Feb 19, 2022
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: 1 addition & 1 deletion src/mapper/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl EventWriter {

pub fn compute_timestamp(&self, slot: u64) -> Option<u64> {
match &self.utils.time {
Some(provider) => provider.slot_to_wallclock(slot).ok(),
Some(provider) => provider.slot_to_wallclock(slot).into(),
_ => None,
}
}
Expand Down
18 changes: 16 additions & 2 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
model::Event,
utils::{
bech32::{Bech32Config, Bech32Provider},
time::{NaiveConfig as TimeConfig, NaiveProvider as NaiveTime},
time::NaiveProvider as NaiveTime,
},
};

Expand Down Expand Up @@ -50,6 +50,10 @@ impl SwallowResult for Result<(), Error> {
/// values.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ChainWellKnownInfo {
pub byron_slot_length: u32,
pub byron_known_slot: u64,
pub byron_known_hash: String,
pub byron_known_time: u64,
pub shelley_slot_length: u32,
pub shelley_known_slot: u64,
pub shelley_known_hash: String,
Expand All @@ -61,6 +65,11 @@ impl ChainWellKnownInfo {
/// Hardcoded values for mainnet
pub fn mainnet() -> Self {
ChainWellKnownInfo {
byron_slot_length: 20,
byron_known_slot: 0,
byron_known_time: 1506203091,
byron_known_hash: "f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f"
.to_string(),
shelley_slot_length: 1,
shelley_known_slot: 4492800,
shelley_known_hash: "aa83acbf5904c0edfe4d79b3689d3d00fcfc553cf360fd2229b98d464c28e9de"
Expand All @@ -73,6 +82,11 @@ impl ChainWellKnownInfo {
/// Hardcoded values for testnet
pub fn testnet() -> Self {
ChainWellKnownInfo {
byron_slot_length: 20,
byron_known_slot: 1031,
byron_known_time: 1564020236,
byron_known_hash: "388a82f053603f3552717d61644a353188f2d5500f4c6354cc1ad27a36a7ea91"
.to_string(),
shelley_slot_length: 1,
shelley_known_slot: 1598400,
shelley_known_hash: "02b1c561715da9e540411123a6135ee319b02f60b9a11a603d3305556c04329f"
Expand Down Expand Up @@ -111,7 +125,7 @@ impl Utils {
// TODO: refactor this using the builder pattern
pub fn new(well_known: ChainWellKnownInfo, cursor: Option<cursor::Provider>) -> Self {
Self {
time: NaiveTime::new(TimeConfig::from_well_known(&well_known)).into(),
time: NaiveTime::new(well_known.clone()).into(),
bech32: Bech32Provider::new(Bech32Config::from_well_known(&well_known)),
cursor,
well_known,
Expand Down
111 changes: 52 additions & 59 deletions src/utils/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,12 @@
//!
//! Common operations to deal with blockchain time and wallclock conversions

use serde::Deserialize;

use crate::{utils::ChainWellKnownInfo, Error};
use crate::utils::ChainWellKnownInfo;

/// Abstraction available to stages to deal with blockchain time conversions
pub(crate) trait TimeProvider {
/// Maps between slots and wallclock
fn slot_to_wallclock(&self, slot: u64) -> Result<u64, Error>;
}

#[derive(Deserialize, Clone)]
pub struct NaiveConfig {
pub slot_length: u32,
pub start_slot: u64,
pub start_timestamp: u64,
}

impl NaiveConfig {
pub(crate) fn from_well_known(info: &ChainWellKnownInfo) -> Self {
Self {
slot_length: info.shelley_slot_length,
start_slot: info.shelley_known_slot,
start_timestamp: info.shelley_known_time,
}
}
fn slot_to_wallclock(&self, slot: u64) -> u64;
}

/// A naive, standalone implementation of a time provider
Expand All @@ -36,82 +17,94 @@ impl NaiveConfig {
/// logic from a well-known configured point in the chain, assuming homogeneous
/// slot length from that point forward.
#[derive(Clone)]
pub(crate) struct NaiveProvider(NaiveConfig);
pub(crate) struct NaiveProvider(ChainWellKnownInfo);

impl NaiveProvider {
pub fn new(config: NaiveConfig) -> Self {
pub fn new(config: ChainWellKnownInfo) -> Self {
NaiveProvider(config)
}
}

#[inline]
fn compute_linear_timestamp(
known_slot: u64,
known_time: u64,
slot_length: u64,
query_slot: u64,
) -> u64 {
known_time + (query_slot - known_slot) * slot_length
}

impl TimeProvider for NaiveProvider {
fn slot_to_wallclock(&self, slot: u64) -> Result<u64, Error> {
fn slot_to_wallclock(&self, slot: u64) -> u64 {
let NaiveProvider(config) = self;

if slot < config.start_slot {
return Err(
"naive time provider can't compute wallclock for slots prior to start_slot".into(),
);
if slot < config.shelley_known_slot {
compute_linear_timestamp(
config.byron_known_slot,
config.byron_known_time,
config.byron_slot_length as u64,
slot,
)
} else {
compute_linear_timestamp(
config.shelley_known_slot,
config.shelley_known_time,
config.shelley_slot_length as u64,
slot,
)
}

let total_delta_secs = (slot - config.start_slot) * config.slot_length as u64;

let out = config.start_timestamp + total_delta_secs;

Ok(out)
}
}

#[cfg(test)]
mod tests {
use super::*;

fn shelley_mainnet() -> NaiveConfig {
NaiveConfig {
slot_length: 1,
start_slot: 4492800,
start_timestamp: 1596059091,
}
}

fn shelley_testnet() -> NaiveConfig {
NaiveConfig {
slot_length: 1,
start_slot: 1598400,
start_timestamp: 1595967616,
}
}

fn assert_slot_matches_timestamp(provider: &NaiveProvider, slot: u64, ts: u64) {
let wallclock = provider
.slot_to_wallclock(slot)
.expect("unable to compute wallclock");
let wallclock = provider.slot_to_wallclock(slot);

assert_eq!(wallclock, ts);
}

#[test]
fn naive_provider_matches_mainnet_values() {
let provider = NaiveProvider::new(shelley_mainnet());
let provider = NaiveProvider::new(ChainWellKnownInfo::mainnet());

// Byron start, value copied from:
// https://explorer.cardano.org/en/block?id=f0f7892b5c333cffc4b3c4344de48af4cc63f55e44936196f365a9ef2244134f
assert_slot_matches_timestamp(&provider, 0, 1506203091);

// Byron middle, value copied from:
// https://explorer.cardano.org/en/block?id=c1b57d58761af4dc3c6bdcb3542170cec6db3c81e551cd68012774d1c38129a3
assert_slot_matches_timestamp(&provider, 2160007, 1549403231);

// value copied from:
// Shelley start, value copied from:
// https://explorer.cardano.org/en/block?id=aa83acbf5904c0edfe4d79b3689d3d00fcfc553cf360fd2229b98d464c28e9de
assert_slot_matches_timestamp(&provider, 4492800, 1596059091);

// value copied from:
// Shelly middle, value copied from:
// https://explorer.cardano.org/en/block?id=ca60833847d0e70a1adfa6b7f485766003cf7d96d28d481c20d4390f91b76d68
assert_slot_matches_timestamp(&provider, 51580240, 1643146531);
}

#[test]
fn naive_provider_matches_testnet_values() {
let provider = NaiveProvider::new(shelley_testnet());
let provider = NaiveProvider::new(ChainWellKnownInfo::testnet());

// Byron start, value copied from:
// https://explorer.cardano-testnet.iohkdev.io/en/block?id=388a82f053603f3552717d61644a353188f2d5500f4c6354cc1ad27a36a7ea91
assert_slot_matches_timestamp(&provider, 1031, 1564020236);

// Byron middle, value copied from:
// https://explorer.cardano-testnet.iohkdev.io/en/block?id=66102c0b80e1eebc9cddf9cab43c1bf912e4f1963d6f3b8ff948952f8409e779
assert_slot_matches_timestamp(&provider, 561595, 1575231516);

// value copied from:
// Shelley start, value copied from:
// https://explorer.cardano-testnet.iohkdev.io/en/block?id=02b1c561715da9e540411123a6135ee319b02f60b9a11a603d3305556c04329f
assert_slot_matches_timestamp(&provider, 1598400, 1595967616);

// value copied from:
// Shelley middle, value copied from:
// https://explorer.cardano-testnet.iohkdev.io/en/block?id=26a1b5a649309c0c8dd48f3069d9adea5a27edf5171dfb941b708acaf2d76dcd
assert_slot_matches_timestamp(&provider, 48783593, 1643152809);
}
Expand Down