Skip to content

Commit 988d99e

Browse files
Merge pull request #44 from cardano-foundation/fix/resolve_difference_in_epoch_384
feat: rewards calculation is now 100% matching the db sync ada pots a…
2 parents 9b17d2a + ac90b0f commit 988d99e

File tree

23 files changed

+163
-197
lines changed

23 files changed

+163
-197
lines changed

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# Cardano Rewards Calculation 🧮
22

33
<p align="left">
4-
<img alt="Tests" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/actions/workflows/tests.yaml/badge.svg?branch=main" />
5-
<img alt="Coverage" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/blob/gh-pages/badges/jacoco.svg?raw=true" />
4+
<a href="https://cardano-foundation.github.io/cf-java-rewards-calculation/coverage-report/"><img alt="Tests" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/actions/workflows/tests.yaml/badge.svg?branch=main" /></a>
5+
<a href="https://cardano-foundation.github.io/cf-java-rewards-calculation/coverage-report/"><img alt="Coverage" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/blob/gh-pages/badges/jacoco.svg?raw=true" /></a>
66
<img alt="Release" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/actions/workflows/release.yaml/badge.svg?branch=main" />
7+
<a href="https://central.sonatype.com/artifact/org.cardanofoundation/cf-rewards-calculation"><img alt="Publish" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/actions/workflows/publish.yaml/badge.svg?branch=main" /></a>
8+
<a href="https://cardano-foundation.github.io/cf-java-rewards-calculation/report-latest/treasury_calculation.html"><img alt="Report" src="https://github.com/cardano-foundation/cf-java-rewards-calculation/actions/workflows/report.yaml/badge.svg?branch=main" /></a>
79
<a href="https://conventionalcommits.org"><img alt="conventionalcommits" src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits" /></a>
810
<a href="https://opensource.org/licenses/MIT"><img alt="License" src="https://img.shields.io/badge/License-MIT-green.svg" /></a>
11+
<a href="https://discord.gg/4WVNHgQ7bP"><img alt="Discord" src="https://img.shields.io/discord/1022471509173882950?label=Discord"></a>
912
</p>
1013

1114
This project is aims to target *multiple goals*. First of all, it tries to *re-implement the cardano ledger rules* for calculating
@@ -14,8 +17,6 @@ The second goal is to use this implementation to *[validate the rewards calculat
1417
The third goal is to *provide a library* that can be used in other project (such as [yaci-store](https://github.com/bloxbean/yaci-store)) to serve the rewards data *independently of [DB Sync](https://github.com/IntersectMBO/cardano-db-sync)*.
1518
Last but not least, this project could also be used to *understand the influence of protocol parameters* and the flow of ADA through an interactive report.
1619

17-
🚧️ The calculation is currently correct until the beginning of the Babbage era. 🚧️
18-
1920
## 🧪 Test Reports
2021

2122
To ensure the stability and reliability of this project, unit tests have been implemented. By clicking on the link below, you can access the detailed test report.
@@ -87,6 +88,19 @@ While the flowchart above shows the calculation of the Ada pots in general, ther
8788

8889
## 🚀 Getting Started
8990

91+
You can use this project as a library in your own project. The library is available through maven central,
92+
or you can clone the repository and run the tests or build the library yourself.
93+
94+
#### Maven
95+
96+
```xml
97+
<dependency>
98+
<groupId>org.cardanofoundation</groupId>
99+
<artifactId>cf-rewards-calculation</artifactId>
100+
<version>0.7.2</version>
101+
</dependency>
102+
```
103+
90104
Make sure to have Java 17 installed and run the following commands:
91105

92106
```
@@ -154,7 +168,8 @@ POSTGRES_DB=cexplorer
154168
155169
JSON_DATA_SOURCE_FOLDER=/path/to/your/rewards-calculation-test-data
156170
```
157-
`⚠️ The actual rewards data will also be fetched when setting DATA_FETCHER_SKIP_VALIDATION_DATA=false, but it is only used from the validator and not within the calculation itself.`
171+
> [!NOTE]
172+
>️ The actual rewards data will also be fetched when setting DATA_FETCHER_SKIP_VALIDATION_DATA=false, but it is only used from the validator and not within the calculation itself.`
158173
159174
#### Data Plotter
160175

@@ -171,10 +186,10 @@ JSON_DATA_SOURCE_FOLDER=/path/to/your/rewards-calculation-test-data
171186
```
172187

173188
## 🫡 Roadmap
174-
- [ ] Provide a library through maven central to use the calculation in other projects
175189
- [ ] Enhance reporting and add values for the other pots as well. Display the flow of Ada within an epoch
176-
- [ ] Find out the root cause of the difference between the actual rewards and the calculated rewards beginning with epoch 350
177190
- [ ] Add a `/docs` folder containing parsable Markdown files to explain MIR certificates and edge cases
191+
- [X] Provide a library through maven central to use the calculation in other projects
192+
- [X] Find out the root cause of the difference between the actual rewards and the calculated rewards beginning with epoch 350
178193
- [X] Include MIR certificates
179194
- [X] Calculate member and operator rewards
180195
- [X] Add deposits and utxo pot

calculation/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>org.cardanofoundation</groupId>
88
<artifactId>cf-rewards</artifactId>
9-
<version>0.7.1</version>
9+
<version>0.7.2</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public static EpochCalculationResult calculateEpochRewardPots(final int epoch, f
3030
final HashSet<String> lateDeregisteredAccounts,
3131
final HashSet<String> registeredAccountsSinceLastEpoch,
3232
final HashSet<String> registeredAccountsUntilNow,
33-
final HashSet<String> sharedPoolRewardAddressesWithoutReward) {
33+
final HashSet<String> sharedPoolRewardAddressesWithoutReward,
34+
final HashSet<String> deregisteredAccountsOnEpochBoundary) {
3435
final EpochCalculationResult epochCalculationResult = EpochCalculationResult.builder().epoch(epoch).build();
3536

3637
if (epoch < MAINNET_SHELLEY_START_EPOCH) {
@@ -84,9 +85,7 @@ public static EpochCalculationResult calculateEpochRewardPots(final int epoch, f
8485
// treasury (see: Pool Reap Transition, p.53, figure 40, shelley-ledger.pdf)
8586
if (retiredPools.size() > 0) {
8687
List<String> rewardAddressesOfRetiredPools = retiredPools.stream().map(PoolDeregistration::getRewardAddress).toList();
87-
List<String> deregisteredOwnerAccounts = deregisteredAccounts.stream()
88-
.filter(rewardAddressesOfRetiredPools::contains).toList();
89-
List<String> lateDeregisteredOwnerAccounts = lateDeregisteredAccounts.stream()
88+
List<String> deregisteredOwnerAccounts = deregisteredAccountsOnEpochBoundary.stream()
9089
.filter(rewardAddressesOfRetiredPools::contains).toList();
9190
List<String> ownerAccountsRegisteredInThePast = registeredAccountsUntilNow.stream()
9291
.filter(rewardAddressesOfRetiredPools::contains).toList();
@@ -97,7 +96,6 @@ public static EpochCalculationResult calculateEpochRewardPots(final int epoch, f
9796
for (PoolDeregistration retiredPool : retiredPools) {
9897
String rewardAddress = retiredPool.getRewardAddress();
9998
if (deregisteredOwnerAccounts.contains(rewardAddress) ||
100-
lateDeregisteredOwnerAccounts.contains(rewardAddress) ||
10199
!ownerAccountsRegisteredInThePast.contains(rewardAddress)) {
102100
// If the reward address has been unregistered, the deposit can not be returned
103101
// and will be added to the treasury instead (Pool Reap see: shelley-ledger.pdf p.53)

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,16 @@ public static PoolRewardCalculationResult calculatePoolRewardInEpoch(final Strin
211211
String rewardAddress = poolRewardCalculationResult.getRewardAddress();
212212

213213
if (!accountsRegisteredInThePast.contains(rewardAddress)) {
214-
log.debug(poolRewardCalculationResult.getRewardAddress() + " has never been registered. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
214+
log.info(poolRewardCalculationResult.getRewardAddress() + " has never been registered. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
215+
if (earnedEpoch >= MAINNET_VASIL_HARDFORK_EPOCH) {
216+
unspendableEarnedRewards = poolOperatorReward;
217+
}
215218
poolOperatorReward = BigInteger.ZERO;
216219
} else if (deregisteredAccounts.contains(rewardAddress)) {
217-
log.debug(poolRewardCalculationResult.getRewardAddress() + " has been deregistered. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
220+
log.info(poolRewardCalculationResult.getRewardAddress() + " has been deregistered. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
218221
poolOperatorReward = BigInteger.ZERO;
219222
} else if (lateDeregisteredAccounts.contains(rewardAddress)) {
220-
log.debug("[unregRU]: " + poolRewardCalculationResult.getRewardAddress() + " has been deregistered lately. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
223+
log.info("[unregRU]: " + poolRewardCalculationResult.getRewardAddress() + " has been deregistered lately. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
221224
unspendableEarnedRewards = poolOperatorReward;
222225
poolOperatorReward = BigInteger.ZERO;
223226
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ public static TreasuryCalculationResult calculateTreasuryInEpoch(int epoch, Prot
2020
List<PoolDeregistration> retiredPools,
2121
List<MirCertificate> mirCertificates,
2222
final HashSet<String> deregisteredAccounts,
23-
final HashSet<String> lateDeregisteredAccounts,
2423
final HashSet<String> registeredAccountsUntilNow,
2524
BigInteger unspendableEarnedRewards) {
2625
// The Shelley era and the ada pot system started on mainnet in epoch 208.
@@ -63,8 +62,6 @@ public static TreasuryCalculationResult calculateTreasuryInEpoch(int epoch, Prot
6362
List<String> rewardAddressesOfRetiredPools = retiredPools.stream().map(PoolDeregistration::getRewardAddress).toList();
6463
HashSet<String> deregisteredRewardAccounts = deregisteredAccounts.stream()
6564
.filter(rewardAddressesOfRetiredPools::contains).collect(Collectors.toCollection(HashSet::new));
66-
deregisteredRewardAccounts.addAll(lateDeregisteredAccounts.stream()
67-
.filter(rewardAddressesOfRetiredPools::contains).collect(Collectors.toSet()));
6865
List<String> ownerAccountsRegisteredInThePast = registeredAccountsUntilNow.stream()
6966
.filter(rewardAddressesOfRetiredPools::contains).toList();
7067

calculation/src/main/java/org/cardanofoundation/rewards/calculation/constants/RewardConstants.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class RewardConstants {
77
public static final BigInteger TOTAL_LOVELACE = new BigInteger("45000000000000000");
88
public static final BigInteger POOL_DEPOSIT_IN_LOVELACE = BigInteger.valueOf(500000000);
99
// https://developers.cardano.org/docs/operate-a-stake-pool/introduction-to-cardano/#slots-and-epochs
10-
public static final int EXPECTED_SLOTS_PER_EPOCH = 432000;
10+
public static final long EXPECTED_SLOTS_PER_EPOCH = 432000;
1111
public static final BigInteger MAINNET_SHELLEY_INITIAL_RESERVES = new BigInteger("13888022852926644");
1212
public static final BigInteger MAINNET_SHELLEY_INITIAL_TREASURY = new BigInteger("0");
1313
public static final BigInteger MAINNET_SHELLEY_INITIAL_UTXO = TOTAL_LOVELACE.subtract(MAINNET_SHELLEY_INITIAL_RESERVES);

calculation/src/main/java/org/cardanofoundation/rewards/calculation/util/BigNumberUtils.java

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
public class BigNumberUtils {
99

10+
public final static MathContext mathContext = new MathContext(20);
11+
1012
public static BigInteger add(BigInteger a, BigInteger b) {
1113
return a.add(b);
1214
}
@@ -20,21 +22,11 @@ public static BigInteger multiplyAndFloor(BigInteger a, BigDecimal b, BigDecimal
2022
return new BigDecimal(a).multiply(b).multiply(c).round(new MathContext(0, RoundingMode.FLOOR)).toBigInteger();
2123
}
2224

23-
public static BigInteger multiplyAndFloor(BigInteger a, double b, double c) {
24-
return new BigDecimal(a).multiply(BigDecimal.valueOf(b).multiply(BigDecimal.valueOf(c)))
25-
.round(new MathContext(0, RoundingMode.FLOOR)).toBigInteger();
26-
}
27-
2825
public static BigInteger multiplyAndFloor(BigInteger a, BigDecimal b) {
2926
return new BigDecimal(a).multiply(b)
3027
.round(new MathContext(0, RoundingMode.FLOOR)).toBigInteger();
3128
}
3229

33-
public static BigInteger multiplyAndFloor(BigInteger a, double b) {
34-
return new BigDecimal(a).multiply(BigDecimal.valueOf(b))
35-
.round(new MathContext(0, RoundingMode.FLOOR)).toBigInteger();
36-
}
37-
3830
public static BigInteger multiply(BigInteger a, BigInteger b, BigInteger c) {
3931
return a.multiply(b).multiply(c);
4032
}
@@ -136,43 +128,43 @@ public static BigDecimal multiply(double a, long b) {
136128
}
137129

138130
public static BigDecimal divide(BigDecimal a, BigDecimal b) {
139-
return a.divide(b, new MathContext(32));
131+
return a.divide(b, mathContext);
140132
}
141133

142134
public static BigDecimal divide(BigInteger a, BigDecimal b) {
143-
return new BigDecimal(a).divide(b, new MathContext(32));
135+
return new BigDecimal(a).divide(b, mathContext);
144136
}
145137

146138
public static BigDecimal divide(double a, BigInteger b) {
147-
return BigDecimal.valueOf(a).divide(new BigDecimal(b), new MathContext(32));
139+
return BigDecimal.valueOf(a).divide(new BigDecimal(b), mathContext);
148140
}
149141

150142
public static BigDecimal divide(BigInteger a, BigInteger b) {
151-
return new BigDecimal(a).divide(new BigDecimal(b), new MathContext(32));
143+
return new BigDecimal(a).divide(new BigDecimal(b), mathContext);
152144
}
153145

154146
public static BigDecimal divide(double a, BigDecimal b) {
155-
return BigDecimal.valueOf(a).divide(b, new MathContext(32));
147+
return BigDecimal.valueOf(a).divide(b, mathContext);
156148
}
157149

158150
public static BigDecimal divide(BigDecimal a, double b) {
159-
return a.divide(BigDecimal.valueOf(b), new MathContext(32));
151+
return a.divide(BigDecimal.valueOf(b), mathContext);
160152
}
161153

162154
public static BigDecimal divide(double a, double b) {
163-
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), new MathContext(32));
155+
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), mathContext);
164156
}
165157

166158
public static BigDecimal divide(long a, double b) {
167-
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), new MathContext(32));
159+
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), mathContext);
168160
}
169161

170162
public static BigDecimal divide(long a, long b) {
171-
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), new MathContext(32));
163+
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), mathContext);
172164
}
173165

174166
public static BigDecimal divide(double a, long b) {
175-
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), new MathContext(32));
167+
return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), mathContext);
176168
}
177169

178170
public static BigDecimal min(BigDecimal a, BigDecimal b) {

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>org.cardanofoundation</groupId>
66
<artifactId>cf-rewards</artifactId>
7-
<version>0.7.1</version>
7+
<version>0.7.2</version>
88
<name>cardano-reward-calculation</name>
99
<url>https://github.com/cardano-foundation/cf-java-rewards-calculation.git</url>
1010

release-please-config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"type": "xml",
1818
"path": "validation/pom.xml",
1919
"xpath": "//project/parent/version"
20-
}
20+
},
21+
"README.md"
2122
],
2223
"packages": {
2324
".":{}

report/treasury_calculation_result.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)