Skip to content

Commit

Permalink
fix: get correct registration state in case of late re-registrations
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianbormann committed Mar 4, 2024
1 parent 5603d5f commit d5dc65e
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 21 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ jobs:
with:
release-type: simple
extra-files: |
pom.xml
pom.xml
validation/pom.xml
calculation/pom.xml
2 changes: 1 addition & 1 deletion calculation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.cardanofoundation</groupId>
<artifactId>cf-rewards</artifactId>
<version>0.5.0-SNAPSHOT</version>
<version>0.6.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public static BigInteger calculateLeaderReward(BigInteger poolReward, double mar
return poolReward;
}

return add(new BigDecimal(poolCost).toBigInteger(), floor(multiply(subtract(poolReward, poolCost),
return add(poolCost, floor(multiply(subtract(poolReward, poolCost),
add(margin, multiply((1 - margin), divide(relativeOwnerStake, relativeStakeOfPool))))));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.cardanofoundation.rewards.calculation;

import org.cardanofoundation.rewards.calculation.domain.*;
import org.cardanofoundation.rewards.calculation.enums.AccountUpdateAction;
import org.cardanofoundation.rewards.calculation.enums.MirPot;

import java.math.BigDecimal;
Expand Down Expand Up @@ -127,7 +126,7 @@ private static BigDecimal calculateEta(int totalBlocksInEpochByPools, BigDecimal
// decentralizationParameter is the proportion of blocks that are expected to be produced by stake pools
// instead of the OBFT (Ouroboros Byzantine Fault Tolerance) nodes. It was introduced close before the Shelley era:
// https://github.com/input-output-hk/cardano-ledger/commit/c4f10d286faadcec9e4437411bce9c6c3b6e51c2
BigDecimal expectedBlocksInNonOBFTSlots = new BigDecimal(EXPECTED_SLOT_PER_EPOCH )
BigDecimal expectedBlocksInNonOBFTSlots = new BigDecimal(EXPECTED_SLOTS_PER_EPOCH)
.multiply(activeSlotsCoeff).multiply(BigDecimal.ONE.subtract(decentralizationParameter));

// eta is the ratio between the number of blocks that have been produced during the epoch, and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class RewardConstants {
public static final BigInteger TOTAL_LOVELACE = new BigInteger("45000000000000000");
public static final BigInteger POOL_DEPOSIT_IN_LOVELACE = BigInteger.valueOf(500000000);
// https://developers.cardano.org/docs/operate-a-stake-pool/introduction-to-cardano/#slots-and-epochs
public static final int EXPECTED_SLOT_PER_EPOCH = 432000;
public static final int EXPECTED_SLOTS_PER_EPOCH = 432000;
public static final BigInteger MAINNET_SHELLEY_INITIAL_RESERVES = new BigInteger("13888022852926644");
public static final BigInteger MAINNET_SHELLEY_INITIAL_TREASURY = new BigInteger("0");
public static final BigInteger MAINNET_SHELLEY_INITIAL_UTXO = TOTAL_LOVELACE.subtract(MAINNET_SHELLEY_INITIAL_RESERVES);
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cardanofoundation</groupId>
<artifactId>cf-rewards</artifactId>
<version>0.5.0-SNAPSHOT</version>
<version>0.6.1</version>
<name>cardano-reward-calculation</name>
<modules>
<module>calculation</module>
Expand Down
2 changes: 1 addition & 1 deletion report/treasury_calculation_result.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion validation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.cardanofoundation</groupId>
<artifactId>cf-rewards</artifactId>
<version>0.5.0-SNAPSHOT</version>
<version>0.6.1</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,16 @@ public static EpochCalculationResult calculateEpochRewardPots(int epoch, DataPro
List<PoolBlock> blocksMadeByPoolsInEpoch = dataProvider.getBlocksMadeByPoolsInEpoch(epoch - 2);
List<String> poolIds = blocksMadeByPoolsInEpoch.stream().map(PoolBlock::getPoolId).distinct().toList();
List<PoolHistory> poolHistories = dataProvider.getHistoryOfAllPoolsInEpoch(epoch - 2, blocksMadeByPoolsInEpoch);
HashSet<String> deregisteredAccounts = dataProvider.getDeregisteredAccountsInEpoch(epoch - 1, RANDOMNESS_STABILISATION_WINDOW);
HashSet<String> lateDeregisteredAccounts = dataProvider.getLateAccountDeregistrationsInEpoch(epoch - 1, RANDOMNESS_STABILISATION_WINDOW);

HashSet<String> deregisteredAccounts;
HashSet<String> lateDeregisteredAccounts = new HashSet<>();
if (epoch < MAINNET_VASIL_HARDFORK_EPOCH) {
deregisteredAccounts = dataProvider.getDeregisteredAccountsInEpoch(epoch - 1, RANDOMNESS_STABILISATION_WINDOW);
lateDeregisteredAccounts = dataProvider.getLateAccountDeregistrationsInEpoch(epoch - 1, RANDOMNESS_STABILISATION_WINDOW);
} else {
deregisteredAccounts = dataProvider.getDeregisteredAccountsInEpoch(epoch - 1, EXPECTED_SLOTS_PER_EPOCH);
}

HashSet<String> sharedPoolRewardAddressesWithoutReward = new HashSet<>();
if (epoch - 2 < MAINNET_ALLEGRA_HARDFORK_EPOCH) {
sharedPoolRewardAddressesWithoutReward = dataProvider.findSharedPoolRewardAddressWithoutReward(epoch - 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import org.cardanofoundation.rewards.calculation.domain.*;
import org.cardanofoundation.rewards.validation.data.provider.DataProvider;
import org.cardanofoundation.rewards.validation.domain.PoolReward;
import org.cardanofoundation.rewards.validation.entity.jpa.projection.TotalPoolRewards;

import java.math.BigDecimal;
import java.math.BigInteger;
Expand Down Expand Up @@ -124,8 +123,15 @@ public static PoolRewardCalculationResult computePoolRewardInEpoch(String poolId

PoolHistory poolHistoryCurrentEpoch = dataProvider.getPoolHistory(poolId, epoch);

HashSet<String> accountDeregistrations = dataProvider.getDeregisteredAccountsInEpoch(epoch + 1, RANDOMNESS_STABILISATION_WINDOW);
HashSet<String> lateAccountDeregistrations = dataProvider.getLateAccountDeregistrationsInEpoch(epoch + 1, RANDOMNESS_STABILISATION_WINDOW);
HashSet<String> accountDeregistrations;
HashSet<String> lateAccountDeregistrations = new HashSet<>();
if (epoch < MAINNET_VASIL_HARDFORK_EPOCH) {
accountDeregistrations = dataProvider.getDeregisteredAccountsInEpoch(epoch + 1, RANDOMNESS_STABILISATION_WINDOW);
lateAccountDeregistrations = dataProvider.getLateAccountDeregistrationsInEpoch(epoch + 1, RANDOMNESS_STABILISATION_WINDOW);
} else {
accountDeregistrations = dataProvider.getDeregisteredAccountsInEpoch(epoch + 1, EXPECTED_SLOTS_PER_EPOCH);
}

HashSet<String> sharedPoolRewardAddressesWithoutReward = dataProvider.findSharedPoolRewardAddressWithoutReward(epoch);

HashSet<String> rewardAddresses = new HashSet<>();
Expand Down Expand Up @@ -158,6 +164,7 @@ public static boolean poolRewardIsValid(PoolRewardCalculationResult poolRewardCa
.toList();

if (actualPoolReward.equals(BigInteger.ZERO)) {
log.info("Pool reward is zero for pool " + poolId + " but calculated pool reward is " + poolRewardCalculationResult.getPoolReward().longValue() + " Lovelace");
return poolRewardCalculationResult.getPoolReward().equals(BigInteger.ZERO);
}

Expand All @@ -183,7 +190,7 @@ public static boolean poolRewardIsValid(PoolRewardCalculationResult poolRewardCa
totalDifference = totalDifference.add(difference);

if (difference.compareTo(BigInteger.ZERO) > 0) {
log.info("[" + rewardIndex + "] The difference between expected member " + reward.getStakeAddress() + " reward and actual member reward is : " + reward.getAmount().longValue() + " Lovelace");
log.info("[" + rewardIndex + "] The difference between expected member " + reward.getStakeAddress() + " reward and actual member reward is : " + difference.longValue() + " Lovelace");
}
}
rewardIndex++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import java.util.List;
import java.util.stream.Collectors;

import static org.cardanofoundation.rewards.calculation.constants.RewardConstants.RANDOMNESS_STABILISATION_WINDOW;
import static org.cardanofoundation.rewards.calculation.constants.RewardConstants.*;
import static org.cardanofoundation.rewards.validation.enums.DataType.*;
import static org.cardanofoundation.rewards.validation.util.JsonConverter.writeObjectToJsonFile;

Expand Down Expand Up @@ -227,7 +227,7 @@ private void fetchHistoryOfAllPoolsInEpoch(int epoch, boolean override) {
}
}

private void fetchDeregisteredAccountsInEpoch(int epoch, boolean override) {
private void fetchDeregisteredAccountsInEpoch(int epoch, boolean override, boolean mainnet) {
String filePath = String.format("%s/%s/epoch%d.json", sourceFolder, ACCOUNT_DEREGISTRATION.resourceFolderName, epoch);
File outputFile = new File(filePath);

Expand All @@ -236,7 +236,12 @@ private void fetchDeregisteredAccountsInEpoch(int epoch, boolean override) {
return;
}

HashSet<String> deregisteredAccountsInEpoch = dbSyncDataProvider.getDeregisteredAccountsInEpoch(epoch, RANDOMNESS_STABILISATION_WINDOW);
long stabilityWindow = RANDOMNESS_STABILISATION_WINDOW;
if (mainnet && epoch >= MAINNET_VASIL_HARDFORK_EPOCH) {
stabilityWindow = EXPECTED_SLOTS_PER_EPOCH;
}

HashSet<String> deregisteredAccountsInEpoch = dbSyncDataProvider.getDeregisteredAccountsInEpoch(epoch, stabilityWindow);
if (deregisteredAccountsInEpoch == null) {
logger.error("Failed to fetch deregistered accounts for epoch " + epoch);
return;
Expand Down Expand Up @@ -315,7 +320,11 @@ private void fetchSharedPoolRewardAddressWithoutReward(int epoch, boolean overri
}
}

private void fetchLateAccountDeregistrationsInEpoch(int epoch, boolean override) {
private void fetchLateAccountDeregistrationsInEpoch(int epoch, boolean override, boolean mainnet) {
if (mainnet && epoch >= MAINNET_VASIL_HARDFORK_EPOCH) {
return;
}

String filePath = String.format("%s/%s/epoch%d.json", sourceFolder, LATE_DEREGISTRATIONS.resourceFolderName, epoch);
File outputFile = new File(filePath);

Expand Down Expand Up @@ -344,8 +353,8 @@ public void fetch(int epoch, boolean override, boolean skipValidationData) {
fetchProtocolParameters(epoch, override);
fetchRetiredPoolsInEpoch(epoch, override);
fetchHistoryOfAllPoolsInEpoch(epoch, override);
fetchDeregisteredAccountsInEpoch(epoch, override);
fetchLateAccountDeregistrationsInEpoch(epoch, override);
fetchDeregisteredAccountsInEpoch(epoch, override, true);
fetchLateAccountDeregistrationsInEpoch(epoch, override, true);
fetchSharedPoolRewardAddressWithoutReward(epoch, override);
fetchMirCertificatesInEpoch(epoch, override);
fetchStakeAddressesWithRegistrationsUntilEpoch(epoch, override);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ public void testCalculateEpochRewardsForEpoch385() {
testCalculateEpochPots(385, dbSyncDataProvider, false);
}

@Test
@EnabledIf(expression = "#{environment.acceptsProfiles('db-sync')}", loadContext = true, reason = "DB Sync data provider must be available for this test")
public void testCalculateEpochRewardsForEpoch415() {
testCalculateEpochPots(415, dbSyncDataProvider, false);
}

@Test
@EnabledIf(expression = "#{environment.acceptsProfiles('db-sync')}", loadContext = true, reason = "DB Sync data provider must be available for this test")
public void testCalculateEpochRewardsForEpoch350() {
testCalculateEpochPots(350, dbSyncDataProvider, true);
}

@Test
@EnabledIf(expression = "#{environment.acceptsProfiles('db-sync')}", loadContext = true, reason = "DB Sync data provider must be available for this test")
public void testCalculateEpochRewardsForEpoch384() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ void calculateNorthPoolRewardInEpoch217() {
Test_calculatePoolReward(poolId, epoch, DataProviderType.JSON);
}

@Test
@EnabledIf(expression = "#{environment.acceptsProfiles('db-sync')}", loadContext = true, reason = "DB Sync data provider must be available for this test")
void calculateBinancePool59RewardInEpoch413() {
String poolId = "pool1wfvreaqszfzxe5w2swwpqpy7e4jax9nyky0x855xqswkzq4v7zf";
int epoch = 413;
Test_calculatePoolReward(poolId, epoch, DataProviderType.DB_SYNC);
}

@Test
@EnabledIf(expression = "#{environment.acceptsProfiles('db-sync')}", loadContext = true, reason = "DB Sync data provider must be available for this test")
void calculateSTKH1PoolRewardInEpoch363() {
Expand Down

0 comments on commit d5dc65e

Please sign in to comment.