Skip to content

Commit

Permalink
feat: use HashSet to improve the performance
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianbormann committed Feb 26, 2024
1 parent 312bfb5 commit 60ea7d2
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

import static org.cardanofoundation.rewards.calculation.PoolRewardsCalculation.calculatePoolRewardInEpoch;
import static org.cardanofoundation.rewards.calculation.constants.RewardConstants.*;
Expand All @@ -17,17 +19,17 @@
@Slf4j
public class EpochCalculation {

public static EpochCalculationResult calculateEpochRewardPots(int epoch, AdaPots adaPotsForPreviousEpoch,
ProtocolParameters protocolParameters, Epoch epochInfo,
List<PoolDeregistration> retiredPools,
List<String> deregisteredAccounts,
List<MirCertificate> mirCertificates,
List<String> poolsThatProducedBlocksInEpoch,
List<PoolHistory> poolHistories,
List<String> lateDeregisteredAccounts,
List<String> accountsRegisteredInThePast,
List<String> sharedPoolRewardAddressesWithoutReward) {
EpochCalculationResult epochCalculationResult = EpochCalculationResult.builder().epoch(epoch).build();
public static EpochCalculationResult calculateEpochRewardPots(final int epoch, final AdaPots adaPotsForPreviousEpoch,
final ProtocolParameters protocolParameters, final Epoch epochInfo,
final List<PoolDeregistration> retiredPools,
final HashSet<String> deregisteredAccounts,
final List<MirCertificate> mirCertificates,
final List<String> poolsThatProducedBlocksInEpoch,
final List<PoolHistory> poolHistories,
final HashSet<String> lateDeregisteredAccounts,
final HashSet<String> accountsRegisteredInThePast,
final HashSet<String> sharedPoolRewardAddressesWithoutReward) {
final EpochCalculationResult epochCalculationResult = EpochCalculationResult.builder().epoch(epoch).build();

double treasuryGrowthRate = 0.2;
double monetaryExpandRate = 0.003;
Expand All @@ -53,15 +55,16 @@ public static EpochCalculationResult calculateEpochRewardPots(int epoch, AdaPots
}
}

BigInteger reserveInPreviousEpoch = adaPotsForPreviousEpoch.getReserves();
BigInteger treasuryInPreviousEpoch = adaPotsForPreviousEpoch.getTreasury();
final int blocksInEpoch = totalBlocksInEpoch;
final BigInteger reserveInPreviousEpoch = adaPotsForPreviousEpoch.getReserves();
final BigInteger treasuryInPreviousEpoch = adaPotsForPreviousEpoch.getTreasury();

BigInteger rewardPot = TreasuryCalculation.calculateTotalRewardPotWithEta(
final BigInteger rewardPot = TreasuryCalculation.calculateTotalRewardPotWithEta(
monetaryExpandRate, totalBlocksInEpoch, decentralizationParameter, reserveInPreviousEpoch, totalFeesForCurrentEpoch);

BigInteger treasuryCut = multiplyAndFloor(rewardPot, treasuryGrowthRate);
final BigInteger treasuryCut = multiplyAndFloor(rewardPot, treasuryGrowthRate);
BigInteger treasuryForCurrentEpoch = treasuryInPreviousEpoch.add(treasuryCut);
BigInteger stakePoolRewardsPot = rewardPot.subtract(treasuryCut);
final BigInteger stakePoolRewardsPot = rewardPot.subtract(treasuryCut);

// The sum of all the refunds attached to unregistered reward accounts are added to the
// treasury (see: Pool Reap Transition, p.53, figure 40, shely-ledger.pdf)
Expand All @@ -86,14 +89,13 @@ public static EpochCalculationResult calculateEpochRewardPots(int epoch, AdaPots
treasuryForCurrentEpoch = treasuryForCurrentEpoch.subtract(treasuryWithdrawals);

BigInteger totalDistributedRewards = BigInteger.ZERO;
BigInteger adaInCirculation = TOTAL_LOVELACE.subtract(reserveInPreviousEpoch);
List<PoolRewardCalculationResult> PoolRewardCalculationResults = new ArrayList<>();

int processedPools = 0;
final BigInteger adaInCirculation = TOTAL_LOVELACE.subtract(reserveInPreviousEpoch);
final List<PoolRewardCalculationResult> poolRewardCalculationResults = new ArrayList<>();
BigInteger unspendableEarnedRewards = BigInteger.ZERO;

int i = 1;
for (String poolId : poolsThatProducedBlocksInEpoch) {
log.info("[" + processedPools + "/" + poolsThatProducedBlocksInEpoch.size() + "] Processing pool: " + poolId);
log.info("[" + i + " / " + poolsThatProducedBlocksInEpoch.size() + "] Processing pool: " + poolId);
PoolHistory poolHistory = poolHistories.stream().filter(history -> history.getPoolId().equals(poolId)).findFirst().orElse(null);

PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculationResult
Expand All @@ -105,17 +107,16 @@ public static EpochCalculationResult calculateEpochRewardPots(int epoch, AdaPots
activeStakeInEpoch = epochInfo.getActiveStake();
}

if (epoch > 212 && epoch < 255) {
totalBlocksInEpoch = epochInfo.getNonOBFTBlockCount();
}

// Step 10 a: Check if pool reward address or member stake addresses have been unregistered before
List<String> stakeAddresses = new ArrayList<>();
final HashSet<String> stakeAddresses = new HashSet<>();
stakeAddresses.add(poolHistory.getRewardAddress());
stakeAddresses.addAll(poolHistory.getDelegators().stream().map(Delegator::getStakeAddress).toList());

List<String> latestAccountDeregistrations = deregisteredAccounts.stream()
.filter(stakeAddresses::contains).toList();
final HashSet<String> delegatorAccountDeregistrations = deregisteredAccounts.stream()
.filter(stakeAddresses::contains).collect(Collectors.toCollection(HashSet::new));

final HashSet<String> lateDeregisteredDelegators = lateDeregisteredAccounts.stream()
.filter(stakeAddresses::contains).collect(Collectors.toCollection(HashSet::new));

// There was a different behavior in the previous version of the node
// If a pool reward address had been used for multiple pools,
Expand All @@ -128,16 +129,16 @@ public static EpochCalculationResult calculateEpochRewardPots(int epoch, AdaPots
}

poolRewardCalculationResult = calculatePoolRewardInEpoch(poolId, poolHistory,
totalBlocksInEpoch, protocolParameters,
blocksInEpoch, protocolParameters,
adaInCirculation, activeStakeInEpoch, stakePoolRewardsPot,
poolHistory.getOwnerActiveStake(), poolHistory.getOwners(),
latestAccountDeregistrations, ignoreLeaderReward, lateDeregisteredAccounts, accountsRegisteredInThePast);
delegatorAccountDeregistrations, ignoreLeaderReward, lateDeregisteredDelegators, accountsRegisteredInThePast);
}

PoolRewardCalculationResults.add(poolRewardCalculationResult);
totalDistributedRewards = add(totalDistributedRewards, poolRewardCalculationResult.getDistributedPoolReward());
unspendableEarnedRewards = unspendableEarnedRewards.add(poolRewardCalculationResult.getUnspendableEarnedRewards());
processedPools++;
poolRewardCalculationResults.add(poolRewardCalculationResult);
i++;
}

BigInteger calculatedReserve = subtract(reserveInPreviousEpoch, subtract(rewardPot, totalFeesForCurrentEpoch));
Expand All @@ -163,7 +164,7 @@ public static EpochCalculationResult calculateEpochRewardPots(int epoch, AdaPots
epochCalculationResult.setTotalRewardsPot(rewardPot);
epochCalculationResult.setReserves(calculatedReserve);
epochCalculationResult.setTreasury(treasuryForCurrentEpoch);
epochCalculationResult.setPoolRewardCalculationResults(PoolRewardCalculationResults);
epochCalculationResult.setPoolRewardCalculationResults(poolRewardCalculationResults);
epochCalculationResult.setTotalPoolRewardsPot(stakePoolRewardsPot);
epochCalculationResult.setTotalAdaInCirculation(adaInCirculation);
epochCalculationResult.setTotalUndistributedRewards(undistributedRewards);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,28 +124,26 @@ public static BigInteger calculateMemberReward(BigInteger poolReward, double mar
relativeMemberStake), relativeStakeOfPool));
}

public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String poolId, PoolHistory poolHistoryCurrentEpoch,
int totalBlocksInEpoch, ProtocolParameters protocolParameters,
BigInteger adaInCirculation, BigInteger activeStakeInEpoch, BigInteger stakePoolRewardsPot,
BigInteger totalActiveStakeOfOwners, List<String> poolOwnerStakeAddresses,
List<String> deregisteredAccounts, boolean ignoreLeaderReward,
List<String> lateDeregisteredAccounts,
List<String> accountsRegisteredInThePast) {
// Step 1: Get Pool information of current epoch
// Example: https://api.koios.rest/api/v0/pool_history?_pool_bech32=pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt&_epoch_no=210
PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculationResult.builder()
public static PoolRewardCalculationResult calculatePoolRewardInEpoch(final String poolId, final PoolHistory poolHistoryCurrentEpoch,
final int totalBlocksInEpoch, final ProtocolParameters protocolParameters,
final BigInteger adaInCirculation, final BigInteger activeStakeInEpoch, BigInteger stakePoolRewardsPot,
final BigInteger totalActiveStakeOfOwners, final List<String> poolOwnerStakeAddresses,
final HashSet<String> deregisteredAccounts, final boolean ignoreLeaderReward,
final HashSet<String> lateDeregisteredAccounts,
final HashSet<String> accountsRegisteredInThePast) {
final PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculationResult.builder()
.epoch(poolHistoryCurrentEpoch.getEpoch())
.poolId(poolId)
.poolReward(BigInteger.ZERO)
.distributedPoolReward(BigInteger.ZERO)
.unspendableEarnedRewards(BigInteger.ZERO)
.build();

BigInteger poolStake = poolHistoryCurrentEpoch.getActiveStake();
BigInteger poolPledge = poolHistoryCurrentEpoch.getPledge();
double poolMargin = poolHistoryCurrentEpoch.getMargin();
BigInteger poolFixedCost = poolHistoryCurrentEpoch.getFixedCost();
int blocksPoolHasMinted = poolHistoryCurrentEpoch.getBlockCount();
final BigInteger poolStake = poolHistoryCurrentEpoch.getActiveStake();
final BigInteger poolPledge = poolHistoryCurrentEpoch.getPledge();
final double poolMargin = poolHistoryCurrentEpoch.getMargin();
final BigInteger poolFixedCost = poolHistoryCurrentEpoch.getFixedCost();
final int blocksPoolHasMinted = poolHistoryCurrentEpoch.getBlockCount();

poolRewardCalculationResult.setPoolMargin(poolMargin);
poolRewardCalculationResult.setPoolCost(poolFixedCost);
Expand All @@ -159,8 +157,8 @@ public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String pool
int optimalPoolCount = protocolParameters.getOptimalPoolCount();
double influenceParam = protocolParameters.getPoolOwnerInfluence();

// Step 5: Calculate apparent pool performance
BigDecimal apparentPoolPerformance =
// Calculate apparent pool performance
final BigDecimal apparentPoolPerformance =
PoolRewardsCalculation.calculateApparentPoolPerformance(poolStake, activeStakeInEpoch,
blocksPoolHasMinted, totalBlocksInEpoch, decentralizationParameter);
poolRewardCalculationResult.setApparentPoolPerformance(apparentPoolPerformance);
Expand All @@ -173,11 +171,11 @@ public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String pool
return poolRewardCalculationResult;
}

BigDecimal relativeStakeOfPoolOwner = divide(poolPledge, adaInCirculation);
BigDecimal relativePoolStake = divide(poolStake, adaInCirculation);
final BigDecimal relativeStakeOfPoolOwner = divide(poolPledge, adaInCirculation);
final BigDecimal relativePoolStake = divide(poolStake, adaInCirculation);

// Step 8: Calculate optimal pool reward
BigInteger optimalPoolReward =
final BigInteger optimalPoolReward =
PoolRewardsCalculation.calculateOptimalPoolReward(
stakePoolRewardsPot,
optimalPoolCount,
Expand All @@ -187,7 +185,7 @@ public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String pool
poolRewardCalculationResult.setOptimalPoolReward(optimalPoolReward);

// Step 9: Calculate pool reward as optimal pool reward * apparent pool performance
BigInteger poolReward = PoolRewardsCalculation.calculatePoolReward(optimalPoolReward, apparentPoolPerformance);
final BigInteger poolReward = PoolRewardsCalculation.calculatePoolReward(optimalPoolReward, apparentPoolPerformance);
poolRewardCalculationResult.setPoolReward(poolReward);

// Step 10: Calculate pool operator reward
Expand All @@ -214,37 +212,37 @@ public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String pool
log.info("[reward address of multiple pools] Pool " + poolId + " has been ignored. Operator would have received " + poolOperatorReward + " but will not receive any rewards.");
}

poolRewardCalculationResult.setOperatorReward(poolOperatorReward);
poolRewardCalculationResult.setDistributedPoolReward(poolOperatorReward);
// Step 11: Calculate pool member reward
List<Reward> memberRewards = new ArrayList<>();
BigInteger poolMemberRewards = BigInteger.ZERO;
final List<Reward> memberRewards = new ArrayList<>();
for (Delegator delegator : poolHistoryCurrentEpoch.getDelegators()) {
String stakeAddress = delegator.getStakeAddress();
if (delegator.getStakeAddress().equals(poolHistoryCurrentEpoch.getRewardAddress()) ||
poolOwnerStakeAddresses.contains(delegator.getStakeAddress())) {
final String stakeAddress = delegator.getStakeAddress();
if (stakeAddress.equals(poolHistoryCurrentEpoch.getRewardAddress()) ||
poolOwnerStakeAddresses.contains(stakeAddress)) {
continue;
}

BigInteger memberReward = PoolRewardsCalculation.calculateMemberReward(poolReward, poolMargin,
poolFixedCost, divide(delegator.getActiveStake(), adaInCirculation), relativePoolStake);

if (deregisteredAccounts.contains(stakeAddress)) {
log.info("Delegator " + delegator.getStakeAddress() + " has been deregistered. Delegator would have received " + memberReward + " but will not receive any rewards.");
log.info("Delegator " + stakeAddress + " has been deregistered. Delegator would have received " + memberReward + " but will not receive any rewards.");
memberReward = BigInteger.ZERO;
} else if (lateDeregisteredAccounts.contains(stakeAddress)) {
log.info("[unregRU]: " + delegator.getStakeAddress() + " has been deregistered lately. Delegator would have received " + memberReward + " but will not receive any rewards.");
log.info("[unregRU]: " + stakeAddress + " has been deregistered lately. Delegator would have received " + memberReward + " but will not receive any rewards.");
unspendableEarnedRewards = unspendableEarnedRewards.add(memberReward);
memberReward = BigInteger.ZERO;
}

memberRewards.add(Reward.builder()
.amount(memberReward)
.stakeAddress(delegator.getStakeAddress())
.stakeAddress(stakeAddress)
.build());

poolRewardCalculationResult.setDistributedPoolReward(
add(poolRewardCalculationResult.getDistributedPoolReward(), memberReward));
poolMemberRewards = poolMemberRewards.add(memberReward);
}
poolRewardCalculationResult.setDistributedPoolReward(poolOperatorReward.add(poolMemberRewards));
poolRewardCalculationResult.setOperatorReward(poolOperatorReward);
poolRewardCalculationResult.setMemberRewards(memberRewards);
poolRewardCalculationResult.setUnspendableEarnedRewards(unspendableEarnedRewards);
return poolRewardCalculationResult;
Expand Down

0 comments on commit 60ea7d2

Please sign in to comment.