Skip to content

Commit

Permalink
Merge pull request #8 from cardano-foundation/feat/slot-to-epoch-conv…
Browse files Browse the repository at this point in the history
…ersion

Slot to Epoch, and date to slot conversion
  • Loading branch information
nemo83 authored Jul 4, 2024
2 parents 5ba51e6 + 8be7302 commit b98d226
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 158 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@
<version>${junit-jupiter-engine.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit-jupiter-engine.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import org.cardanofoundation.conversions.converters.TimeConversions;

public record CardanoConverters(
ConversionsConfig conversionsConfig,
GenesisConfig genesisConfig,
EpochConversions epoch,
SlotConversions slot,
TimeConversions time,
EraConversions era) {}
ConversionsConfig conversionsConfig,
GenesisConfig genesisConfig,
EpochConversions epoch,
SlotConversions slot,
TimeConversions time,
EraConversions era) {}
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,11 @@ public static CardanoConverters createConverters(

return new CardanoConverters(
conversionsConfig,
genesisConfig,
epochConversions,
slotConversions,
timeConversions,
eraConversions
);
genesisConfig,
epochConversions,
slotConversions,
timeConversions,
eraConversions);
}

private static String getGenesisEraClasspathLink(EraType era, NetworkType networkType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
import org.cardanofoundation.conversions.domain.EraType;

/**
* Data sources:
* - https://cips.cardano.org/cips/cip59/feature-table.md.html -
* - https://cardanosolutions.github.io/kupo/#section/Rollbacks-and-chain-forks/How-Kupo-deals-with-rollbacks
* Data sources: - https://cips.cardano.org/cips/cip59/feature-table.md.html - -
* https://cardanosolutions.github.io/kupo/#section/Rollbacks-and-chain-forks/How-Kupo-deals-with-rollbacks
*/
@RequiredArgsConstructor
public class EraHistory {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,64 +1,62 @@
package org.cardanofoundation.conversions.converters;

import java.time.LocalDateTime;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.cardanofoundation.conversions.ConversionRuntimeException;
import org.cardanofoundation.conversions.GenesisConfig;
import org.cardanofoundation.conversions.domain.EraHistoryItem;
import org.cardanofoundation.conversions.domain.EraType;

import java.time.LocalDateTime;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
public class EraConversions {

private final GenesisConfig genesisConfig;
private final SlotConversions slotConversions;

public long firstRealSlot(EraType eraType) {
return getEraHistoryItem(eraType).firstRealSlotNo();
}
private final GenesisConfig genesisConfig;
private final SlotConversions slotConversions;

public long firstTheoreticalSlot(EraType eraType) {
return getEraHistoryItem(eraType).firstTheoreticalSlotNo();
}
public long firstRealSlot(EraType eraType) {
return getEraHistoryItem(eraType).firstRealSlotNo();
}

public Optional<Long> lastTheoreticalSlot(EraType eraType) {
return getEraHistoryItem(eraType).lastTheoreticalSlotNo();
}
public long firstTheoreticalSlot(EraType eraType) {
return getEraHistoryItem(eraType).firstTheoreticalSlotNo();
}

public Optional<Long> lastRealSlot(EraType eraType) {
return getEraHistoryItem(eraType).lastRealSlotNo();
}
public Optional<Long> lastTheoreticalSlot(EraType eraType) {
return getEraHistoryItem(eraType).lastTheoreticalSlotNo();
}

public LocalDateTime firstRealEraTime(EraType eraType) {
var absoluteSlot = firstRealSlot(eraType);
public Optional<Long> lastRealSlot(EraType eraType) {
return getEraHistoryItem(eraType).lastRealSlotNo();
}

return slotConversions.slotToTime(absoluteSlot);
}
public LocalDateTime firstRealEraTime(EraType eraType) {
var absoluteSlot = firstRealSlot(eraType);

public LocalDateTime firstTheoreticalEraTime(EraType eraType) {
var absoluteSlot = firstTheoreticalSlot(eraType);
return slotConversions.slotToTime(absoluteSlot);
}

return slotConversions.slotToTime(absoluteSlot);
}
public LocalDateTime firstTheoreticalEraTime(EraType eraType) {
var absoluteSlot = firstTheoreticalSlot(eraType);

public Optional<LocalDateTime> lastRealEraTime(EraType eraType) {
return lastRealSlot(eraType)
.map(slotConversions::slotToTime);
}
return slotConversions.slotToTime(absoluteSlot);
}

public Optional<LocalDateTime> lastTheoreticalEraTime(EraType eraType) {
return lastTheoreticalSlot(eraType)
.map(slotConversions::slotToTime);
}
public Optional<LocalDateTime> lastRealEraTime(EraType eraType) {
return lastRealSlot(eraType).map(slotConversions::slotToTime);
}

private EraHistoryItem getEraHistoryItem(EraType eraType) {
return genesisConfig.getEraHistory()
.findFirstByEra(eraType)
.orElseThrow(() -> new ConversionRuntimeException("Era details not found!, era: " + eraType));
}
public Optional<LocalDateTime> lastTheoreticalEraTime(EraType eraType) {
return lastTheoreticalSlot(eraType).map(slotConversions::slotToTime);
}

private EraHistoryItem getEraHistoryItem(EraType eraType) {
return genesisConfig
.getEraHistory()
.findFirstByEra(eraType)
.orElseThrow(
() -> new ConversionRuntimeException("Era details not found!, era: " + eraType));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,26 @@ public LocalDateTime slotToTime(long absoluteSlot) {
// for now post byron we have 1 slot = 1 second
return genesisConfig.blockTime(EraType.Shelley, absoluteSlot);
}

/**
* Computes the epoch a slot falls in.
*
* @param absoluteSlot the slot number to convert
* @return the Era number the slot falls in
*/
public Long slotToEpoch(long absoluteSlot) {
if (absoluteSlot < 0L) {
throw new IllegalArgumentException("absoluteSlot cannot be negative");
}

long firstShelleySlot = genesisConfig.firstShelleySlot();

if (absoluteSlot < firstShelleySlot) {
return absoluteSlot / genesisConfig.slotsPerEpoch(EraType.Byron);
} else {
var shelleyRelativeSlot = absoluteSlot - firstShelleySlot;
var numFullEpochs = shelleyRelativeSlot / genesisConfig.getShelleyEpochLength();
return genesisConfig.firstShelleyEpochNo() + numFullEpochs;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import lombok.RequiredArgsConstructor;
import org.cardanofoundation.conversions.GenesisConfig;
import org.cardanofoundation.conversions.domain.EraType;
Expand Down Expand Up @@ -55,4 +56,24 @@ int utcTimeToEpochNo(EraType era, LocalDateTime utcTime) {
return (int)
Math.ceil((double) (diffDurationSeconds / slotsPerEpoch / byronSlotsLengthSeconds));
}

/**
* @param utcTime the time to convert into a slot
* @return the slot corresponding the time passed as input (this might not coincide with a block)
*/
public Long toSlot(LocalDateTime utcTime) {

if (utcTime.isBefore(genesisConfig.getStartTime())) {
throw new IllegalArgumentException("Required that falls before start of the blockchain");
} else if (utcTime.isBefore(genesisConfig.getShelleyStartTime())) {
var secondsSinceByronBegin =
ChronoUnit.SECONDS.between(genesisConfig.getStartTime(), utcTime);
return secondsSinceByronBegin / genesisConfig.slotDuration(Byron).getSeconds();
} else {
var secondsSinceShelleyBegin =
ChronoUnit.SECONDS.between(genesisConfig.getShelleyStartTime(), utcTime);
return genesisConfig.firstShelleySlot()
+ secondsSinceShelleyBegin / genesisConfig.slotDuration(Shelley).getSeconds();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import lombok.extern.slf4j.Slf4j;
import org.cardanofoundation.conversions.ClasspathConversionsFactory;
import org.cardanofoundation.conversions.GenesisConfig;
import org.cardanofoundation.conversions.domain.EraType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand All @@ -35,8 +34,7 @@ public void testIntraByronFork() {
assertThat(epochConversions.epochToAbsoluteSlot(176, START)).isEqualTo(slot);
}


@Test
@Test
public void testConvertByronEpochToSlot() {
assertThat(epochConversions.epochToAbsoluteSlot(207, START)).isEqualTo(4471200);
assertThat(epochConversions.epochToAbsoluteSlot(207, END)).isEqualTo(4492799L);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,81 +1,79 @@
package org.cardanofoundation.conversions.converters;

import org.cardanofoundation.conversions.ClasspathConversionsFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;

import static org.assertj.core.api.Assertions.assertThat;
import static org.cardanofoundation.conversions.domain.EraType.Babbage;
import static org.cardanofoundation.conversions.domain.EraType.Shelley;
import static org.cardanofoundation.conversions.domain.NetworkType.MAINNET;

class EraConversionsMainNetTest {
private EraConversions eraConversions;

@BeforeEach
public void setup() {
var converters = ClasspathConversionsFactory.createConverters(MAINNET);
eraConversions = converters.era();
}

@Test
void firstRealSlotShelley() {
var absoluteSlot = eraConversions.firstRealSlot(Shelley);
assertThat(absoluteSlot).isEqualTo(4492800L);
}

@Test
void firstRealSlotBabbage() {
var absoluteSlot = eraConversions.firstRealSlot(Babbage);
assertThat(absoluteSlot).isEqualTo(72316896L);
}

@Test
void firstTheoreticalSlotShelley() {
var absoluteSlot = eraConversions.firstTheoreticalSlot(Shelley);
assertThat(absoluteSlot).isEqualTo(4492800L);
}

@Test
void firstTheoreticalSlotBabbage() {
var absoluteSlot = eraConversions.firstTheoreticalSlot(Babbage);
assertThat(absoluteSlot).isEqualTo(72316800L);
}

@Test
void lastRealSlotBabbage() {
assertThat(eraConversions.lastRealEraTime(Babbage)).isEmpty();
}

@Test
void lastTheoreticalSlotShelley() {
var absoluteSlot = eraConversions.lastTheoreticalSlot(Shelley).orElseThrow();
assertThat(absoluteSlot).isEqualTo(16588799L);
}

@Test
void firstRealEraTimeShelley() {
var time = eraConversions.firstRealEraTime(Shelley);
assertThat(time).isEqualTo(LocalDateTime.of(2020, 7, 29, 21, 44, 51));
}

@Test
void lastRealEraTimeShelley() {
var time = eraConversions.lastRealEraTime(Shelley).orElseThrow();
assertThat(time).isEqualTo(LocalDateTime.of(2020, 12, 16, 21, 43, 48));
}

@Test
void firstRealEraTimeBabbage() {
var time = eraConversions.firstRealEraTime(Babbage);
assertThat(time).isEqualTo(LocalDateTime.of(2022, 9, 22, 21, 46, 27));
}

@Test
void lastRealEraTimeBabbage() {
assertThat(eraConversions.lastRealEraTime(Babbage)).isEmpty();
}
import java.time.LocalDateTime;
import org.cardanofoundation.conversions.ClasspathConversionsFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

}
class EraConversionsMainNetTest {
private EraConversions eraConversions;

@BeforeEach
public void setup() {
var converters = ClasspathConversionsFactory.createConverters(MAINNET);
eraConversions = converters.era();
}

@Test
void firstRealSlotShelley() {
var absoluteSlot = eraConversions.firstRealSlot(Shelley);
assertThat(absoluteSlot).isEqualTo(4492800L);
}

@Test
void firstRealSlotBabbage() {
var absoluteSlot = eraConversions.firstRealSlot(Babbage);
assertThat(absoluteSlot).isEqualTo(72316896L);
}

@Test
void firstTheoreticalSlotShelley() {
var absoluteSlot = eraConversions.firstTheoreticalSlot(Shelley);
assertThat(absoluteSlot).isEqualTo(4492800L);
}

@Test
void firstTheoreticalSlotBabbage() {
var absoluteSlot = eraConversions.firstTheoreticalSlot(Babbage);
assertThat(absoluteSlot).isEqualTo(72316800L);
}

@Test
void lastRealSlotBabbage() {
assertThat(eraConversions.lastRealEraTime(Babbage)).isEmpty();
}

@Test
void lastTheoreticalSlotShelley() {
var absoluteSlot = eraConversions.lastTheoreticalSlot(Shelley).orElseThrow();
assertThat(absoluteSlot).isEqualTo(16588799L);
}

@Test
void firstRealEraTimeShelley() {
var time = eraConversions.firstRealEraTime(Shelley);
assertThat(time).isEqualTo(LocalDateTime.of(2020, 7, 29, 21, 44, 51));
}

@Test
void lastRealEraTimeShelley() {
var time = eraConversions.lastRealEraTime(Shelley).orElseThrow();
assertThat(time).isEqualTo(LocalDateTime.of(2020, 12, 16, 21, 43, 48));
}

@Test
void firstRealEraTimeBabbage() {
var time = eraConversions.firstRealEraTime(Babbage);
assertThat(time).isEqualTo(LocalDateTime.of(2022, 9, 22, 21, 46, 27));
}

@Test
void lastRealEraTimeBabbage() {
assertThat(eraConversions.lastRealEraTime(Babbage)).isEmpty();
}
}
Loading

0 comments on commit b98d226

Please sign in to comment.