Skip to content

Commit 9f1554c

Browse files
committed
chore: add csv data plotter
1 parent 44bca89 commit 9f1554c

File tree

8 files changed

+394
-3
lines changed

8 files changed

+394
-3
lines changed

calculation/src/main/java/org/cardanofoundation/rewards/calculation/EpochCalculation.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public static EpochCalculationResult calculateEpochRewardPots(final int epoch,
8080

8181
// The sum of all the refunds attached to unregistered reward accounts are added to the
8282
// treasury (see: Pool Reap Transition, p.53, figure 40, shelley-ledger.pdf)
83+
BigInteger unclaimedRefunds = BigInteger.ZERO;
8384
if (rewardAddressesOfRetiredPools.size() > 0) {
8485
List<String> deregisteredOwnerAccounts = deregisteredAccountsOnEpochBoundary.stream()
8586
.filter(rewardAddressesOfRetiredPools::contains).toList();
@@ -95,6 +96,7 @@ public static EpochCalculationResult calculateEpochRewardPots(final int epoch,
9596
// If the reward address has been unregistered, the deposit can not be returned
9697
// and will be added to the treasury instead (Pool Reap see: shelley-ledger.pdf p.53)
9798
treasuryForCurrentEpoch = treasuryForCurrentEpoch.add(POOL_DEPOSIT_IN_LOVELACE);
99+
unclaimedRefunds = unclaimedRefunds.add(POOL_DEPOSIT_IN_LOVELACE);
98100
}
99101
}
100102
}
@@ -185,6 +187,7 @@ public static EpochCalculationResult calculateEpochRewardPots(final int epoch,
185187
.totalRewardPot(rewardPot)
186188
.treasuryWithdrawals(treasuryWithdrawals)
187189
.unspendableEarnedRewards(unspendableEarnedRewards)
190+
.unclaimedRefunds(unclaimedRefunds)
188191
.build();
189192

190193
epochCalculationResult.setTotalDistributedRewards(totalDistributedRewards);

calculation/src/main/java/org/cardanofoundation/rewards/calculation/TreasuryCalculation.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public static TreasuryCalculationResult calculateTreasuryInEpoch(int epoch, Prot
3131
.totalRewardPot(BigInteger.ZERO)
3232
.treasuryWithdrawals(BigInteger.ZERO)
3333
.unspendableEarnedRewards(BigInteger.ZERO)
34+
.unclaimedRefunds(BigInteger.ZERO)
3435
.build();
3536
}
3637

@@ -58,13 +59,14 @@ public static TreasuryCalculationResult calculateTreasuryInEpoch(int epoch, Prot
5859
final BigInteger treasuryCut = multiplyAndFloor(totalRewardPot, treasuryGrowthRate);
5960
BigInteger treasuryForCurrentEpoch = treasuryInPreviousEpoch.add(treasuryCut);
6061

62+
BigInteger unclaimedRefunds = BigInteger.ZERO;
6163
if (rewardAddressesOfRetiredPools.size() > 0) {
6264
HashSet<String> deregisteredRewardAccounts = deregisteredAccounts.stream()
6365
.filter(rewardAddressesOfRetiredPools::contains).collect(Collectors.toCollection(HashSet::new));
6466
List<String> ownerAccountsRegisteredInThePast = registeredAccountsUntilNow.stream()
6567
.filter(rewardAddressesOfRetiredPools::contains).toList();
6668

67-
BigInteger unclaimedRefunds = calculateUnclaimedRefundsForRetiredPools(rewardAddressesOfRetiredPools, deregisteredRewardAccounts, ownerAccountsRegisteredInThePast);
69+
unclaimedRefunds = calculateUnclaimedRefundsForRetiredPools(rewardAddressesOfRetiredPools, deregisteredRewardAccounts, ownerAccountsRegisteredInThePast);
6870
treasuryForCurrentEpoch = treasuryForCurrentEpoch.add(unclaimedRefunds);
6971
}
7072

@@ -84,6 +86,7 @@ public static TreasuryCalculationResult calculateTreasuryInEpoch(int epoch, Prot
8486
.totalRewardPot(totalRewardPot)
8587
.treasuryWithdrawals(treasuryWithdrawals)
8688
.unspendableEarnedRewards(unspendableEarnedRewards)
89+
.unclaimedRefunds(unclaimedRefunds)
8790
.build();
8891
}
8992

calculation/src/main/java/org/cardanofoundation/rewards/calculation/domain/TreasuryCalculationResult.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ public class TreasuryCalculationResult {
1515
BigInteger totalRewardPot;
1616
BigInteger treasuryWithdrawals;
1717
BigInteger unspendableEarnedRewards;
18+
BigInteger unclaimedRefunds;
1819
}

report/statistics.csv

Lines changed: 272 additions & 0 deletions
Large diffs are not rendered by default.

validation/src/main/java/org/cardanofoundation/rewards/RewardsApplication.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.cardanofoundation.rewards.validation.data.fetcher.DbSyncDataFetcher;
44
import org.cardanofoundation.rewards.validation.data.fetcher.KoiosDataFetcher;
5+
import org.cardanofoundation.rewards.validation.data.plotter.CsvDataPlotter;
56
import org.cardanofoundation.rewards.validation.data.plotter.JsonDataPlotter;
67
import org.slf4j.Logger;
78
import org.slf4j.LoggerFactory;
@@ -46,6 +47,9 @@ public class RewardsApplication implements ApplicationRunner {
4647
@Autowired
4748
private JsonDataPlotter jsonDataPlotter;
4849

50+
@Autowired
51+
private CsvDataPlotter csvDataPlotter;
52+
4953
@Autowired
5054
private KoiosDataFetcher koiosDataFetcher;
5155

@@ -89,7 +93,11 @@ public void run(ApplicationArguments args) throws Exception {
8993
}
9094
}
9195
} else if (runMode.equals("plot")) {
92-
jsonDataPlotter.plot(startEpoch, endEpoch);
96+
if (activeProfiles.contains("csv")) {
97+
csvDataPlotter.plot(startEpoch, endEpoch);
98+
} else {
99+
jsonDataPlotter.plot(startEpoch, endEpoch);
100+
}
93101
} else if (!runMode.equals("test")) {
94102
logger.warn("Unknown run mode: " + runMode);
95103
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.cardanofoundation.rewards.validation.data.plotter;
2+
3+
import org.cardanofoundation.rewards.calculation.domain.EpochCalculationResult;
4+
import org.cardanofoundation.rewards.calculation.domain.PoolState;
5+
import org.cardanofoundation.rewards.validation.EpochValidation;
6+
import org.cardanofoundation.rewards.validation.data.provider.JsonDataProvider;
7+
import org.cardanofoundation.rewards.validation.domain.EpochValidationInput;
8+
import org.cardanofoundation.rewards.validation.domain.TreasuryValidationResult;
9+
import org.cardanofoundation.rewards.validation.util.CsvConverter;
10+
import org.slf4j.Logger;
11+
import org.slf4j.LoggerFactory;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.stereotype.Service;
14+
15+
import java.util.*;
16+
import java.util.stream.Collectors;
17+
18+
@Service
19+
public class CsvDataPlotter implements DataPlotter {
20+
private static final Logger logger = LoggerFactory.getLogger(JsonDataPlotter.class);
21+
22+
@Autowired
23+
private JsonDataProvider jsonDataProvider;
24+
25+
@Override
26+
public void plot(int epochStart, int epochEnd) {
27+
if (epochStart > epochEnd) {
28+
throw new IllegalArgumentException("epochStart must be less than or equal to epochEnd");
29+
}
30+
31+
Map<Integer, TreasuryValidationResult> epochTreasuryValidationResultMap = new LinkedHashMap<>();
32+
33+
List<HashMap<String, String>> data = new ArrayList<>();
34+
for (int epoch = epochStart; epoch < epochEnd; epoch++) {
35+
HashMap<String, String> row = new HashMap<>();
36+
boolean detailedValidation = false;
37+
38+
EpochValidationInput epochValidationInput = jsonDataProvider.getEpochValidationInput(epoch);
39+
HashSet<String> poolIds = epochValidationInput.getPoolStates().stream().map(PoolState::getPoolId).collect(Collectors.toCollection(HashSet::new));
40+
EpochCalculationResult epochCalculationResult = EpochValidation.calculateEpochRewardPots(epoch,
41+
jsonDataProvider, detailedValidation);
42+
43+
row.put("epoch", String.valueOf(epoch));
44+
row.put("reserves", String.valueOf(epochCalculationResult.getReserves()));
45+
row.put("treasury", String.valueOf(epochCalculationResult.getTreasury()));
46+
row.put("epoch_fees", String.valueOf(epochValidationInput.getFees()));
47+
row.put("unspendable_earned_rewards", String.valueOf(epochCalculationResult.getTreasuryCalculationResult().getUnspendableEarnedRewards()));
48+
row.put("undistributed_rewards", String.valueOf(epochCalculationResult.getTotalUndistributedRewards()));
49+
row.put("unclaimed_refunds", String.valueOf(epochCalculationResult.getTreasuryCalculationResult().getUnclaimedRefunds()));
50+
row.put("treasury_withdrawals", String.valueOf(epochCalculationResult.getTreasuryCalculationResult().getTreasuryWithdrawals()));
51+
row.put("active_epoch_stake", String.valueOf(epochValidationInput.getActiveStake()));
52+
row.put("block_count", String.valueOf(epochValidationInput.getBlockCount()));
53+
row.put("unique_pools_with_blocks", String.valueOf(poolIds.size()));
54+
data.add(row);
55+
}
56+
57+
try {
58+
CsvConverter.writeObjectToCsvFile(data,
59+
"./report/statistics.csv");
60+
} catch (Exception e) {
61+
logger.error(e.getMessage());
62+
logger.warn("Failed to write statistics to csv file");
63+
}
64+
}
65+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.cardanofoundation.rewards.validation.util;
2+
3+
import java.io.BufferedWriter;
4+
import java.io.FileWriter;
5+
import java.io.IOException;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
9+
public class CsvConverter {
10+
11+
public static void writeObjectToCsvFile(List<HashMap<String, String>> data, String filePath) throws IOException {
12+
BufferedWriter writer = new BufferedWriter(new FileWriter(filePath));
13+
14+
if (data.size() == 0) {
15+
return;
16+
}
17+
18+
List<String> keys = data.get(0).keySet().stream().toList();
19+
20+
for (String key : keys) {
21+
writer.write(key + ",");
22+
}
23+
24+
writer.write("\n");
25+
26+
for (HashMap<String, String> row : data) {
27+
for (String key : keys) {
28+
if (row.get(key) == null) {
29+
writer.write(",");
30+
continue;
31+
}
32+
writer.write(row.get(key) + ",");
33+
}
34+
writer.write("\n");
35+
}
36+
37+
writer.close();
38+
}
39+
}

validation/src/main/resources/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ DATA_FETCHER_START_EPOCH=208
88
DATA_FETCHER_END_EPOCH=230
99
DATA_FETCHER_SKIP_VALIDATION_DATA=false
1010

11-
# Possible values are: ci, json, db-sync, koios
11+
# Possible values are: ci, json, db-sync, koios, csv
1212
SPRING_PROFILES_ACTIVE=ci json
1313

1414
# The following values are required for the dbsync profile

0 commit comments

Comments
 (0)