generator-impl-v8.go generator-impl-v10.go
+-- 22 lines: package rewards---------------------------------------------------------------------------------------------------------------------
	"github.com/rocket-pool/smartnode/v2/shared/config"
	"github.com/rocket-pool/smartnode/v2/shared/keys"
	sharedtypes "github.com/rocket-pool/smartnode/v2/shared/types"
	"golang.org/x/sync/errgroup"
)

var six = big.NewInt(6)                                                                                                                           
                                                                                                                                                  
// Implementation for tree generator ruleset v8                                                                                                   
type treeGeneratorImpl_v8 struct {                                                                                                                
	networkState           *state.NetworkState
	rewardsFile            *RewardsFile_v3
	elSnapshotHeader       *types.Header
	logger                 *slog.Logger
	rp                     *rocketpool.RocketPool
	cfg                    *config.SmartNodeConfig
+-- 14 lines: bc                     beacon.IBeaconClient-----------------------------------------------------------------------------------------
	totalAttestationScore  *big.Int
	successfulAttestations uint64
	genesisTime            time.Time
}

// Create a new tree generator
func newTreeGeneratorImpl_v8(logger *slog.Logger, index uint64, startTime time.Time, endTime time.Time, consensusBlock uint64, elSnapshotHeader *types.Header, intervalsPassed uint64, state *state.NetworkState) *treeGeneratorImpl_v8 {
	return &treeGeneratorImpl_v8{                                                                                                             
		rewardsFile: &RewardsFile_v3{
			RewardsFileHeader: &sharedtypes.RewardsFileHeader{
				RewardsFileVersion:  3,
				RulesetVersion:      8,                                                                                           
				Index:               index,
				StartTime:           startTime.UTC(),
				EndTime:             endTime.UTC(),
				ConsensusEndBlock:   consensusBlock,
				ExecutionEndBlock:   elSnapshotHeader.Number.Uint64(),
				IntervalsPassed:     intervalsPassed,
+-- 25 lines: InvalidNetworkNodes: map[common.Address]uint64{},-----------------------------------------------------------------------------------
		totalAttestationScore: big.NewInt(0),
		networkState:          state,
	}
}

// Get the version of the ruleset used by this generator
func (r *treeGeneratorImpl_v8) getRulesetVersion() uint64 {                                                                                       
	return r.rewardsFile.RulesetVersion
}

func (r *treeGeneratorImpl_v8) generateTree(context context.Context, rp *rocketpool.RocketPool, cfg *config.SmartNodeConfig, bc beacon.IBeaconClient) (sharedtypes.IRewardsFile, error) {
	r.logger.Info("Started rewards tree generation.", slog.Uint64(keys.RulesetKey, r.rewardsFile.RulesetVersion))

	// Provision some struct params
	r.rp = rp
	r.cfg = cfg
	r.bc = bc
+-- 58 lines: r.validNetworkCache = map[uint64]bool{----------------------------------------------------------------------------------------------

	return r.rewardsFile, nil
}

// Quickly calculates an approximate of the staker's share of the smoothing pool balance without processing Beacon performance
// Used for approximate returns in the rETH ratio update
func (r *treeGeneratorImpl_v8) approximateStakerShareOfSmoothingPool(context context.Context, rp *rocketpool.RocketPool, cfg *config.SmartNodeConfig, bc beacon.IBeaconClient) (*big.Int, error) {
	r.logger.Info("Approximating rewards tree.", slog.Uint64(keys.RulesetKey, r.rewardsFile.RulesetVersion))

	r.rp = rp
	r.cfg = cfg
	r.bc = bc
	r.validNetworkCache = map[uint64]bool{
+-- 34 lines: 0: true,----------------------------------------------------------------------------------------------------------------------------
	}

	return &r.rewardsFile.TotalRewards.PoolStakerSmoothingPoolEth.Int, nil
}

// Calculates the per-network distribution amounts and the total reward amounts
func (r *treeGeneratorImpl_v8) updateNetworksAndTotals() {                                                                                        
	// Get the highest network index with valid rewards
	highestNetworkIndex := uint64(0)
	for network := range r.rewardsFile.NetworkRewards {
		if network > highestNetworkIndex {
			highestNetworkIndex = network
		}
+-- 10 lines: }-----------------------------------------------------------------------------------------------------------------------------------
			}
			r.rewardsFile.NetworkRewards[network] = rewardsForNetwork
		}
	}
}

func (r *treeGeneratorImpl_v8) calculateNodeRplRewards(                                                                                           
	collateralRewards *big.Int,
	nodeEffectiveStake *big.Int,
	totalEffectiveRplStake *big.Int,
	nodeWeight *big.Int,
	totalNodeWeight *big.Int,
) *big.Int {
+-- 36 lines: if nodeEffectiveStake.Sign() <= 0 || nodeWeight.Sign() <= 0 {-----------------------------------------------------------------------

	// Add them together
	return rpip30Rewards.Add(rpip30Rewards, oldRewards)
}

// Calculates the RPL rewards for the given interval
func (r *treeGeneratorImpl_v8) calculateRplRewards() error {                                                                                      
	pendingRewards := r.networkState.NetworkDetails.PendingRPLRewards
	r.logger.Info("Calculated Pending RPL rewards", slog.String(keys.AmountKey, getRewardsString(pendingRewards)))
	if pendingRewards.Cmp(common.Big0) == 0 {
		return fmt.Errorf("there are no pending RPL rewards, so this interval cannot be used for rewards submission")
	}

+-- 43 lines: Get baseline Protocol DAO rewards---------------------------------------------------------------------------------------------------
				totalNodeEffectiveStake,
				nodeWeights[nodeDetails.NodeAddress],
				totalNodeWeight,
			)

			// If there are pending rewards, add it to the map
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
			if nodeRplRewards.Sign() == 1 {
				rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[nodeDetails.NodeAddress]                                
				if !exists {
					// Get the network the rewards should go to
					network := r.networkState.NodeDetails[i].RewardNetwork.Uint64()
					validNetwork, err := r.validateNetwork(network)
					if err != nil {
						return err
+--  6 lines: }-----------------------------------------------------------------------------------------------------------------------------------
					rewardsForClaimer = &ClaimerRewardsInfo_v3{
						RewardNetwork:    network,
						CollateralRpl:    sharedtypes.NewQuotedBigInt(0),
						OracleDaoRpl:     sharedtypes.NewQuotedBigInt(0),
						SmoothingPoolEth: sharedtypes.NewQuotedBigInt(0),
					}
					r.rewardsFile.ClaimerRewards[nodeDetails.NodeAddress] = rewardsForClaimer                                 
				}
				rewardsForClaimer.CollateralRpl.Add(&rewardsForClaimer.CollateralRpl.Int, nodeRplRewards)

				// Add the rewards to the running total for the specified network
				rewardsForNetwork, exists := r.rewardsFile.NetworkRewards[rewardsForClaimer.RewardNetwork]
				if !exists {
+-- 63 lines: rewardsForNetwork = &sharedtypes.NetworkRewardsInfo{--------------------------------------------------------------------------------

		// Calculate the oDAO rewards for the node: (participation time) * (total oDAO rewards) / (total participation time)
		individualOdaoRewards := big.NewInt(0)
		individualOdaoRewards.Mul(trueODaoNodeTimes[address], totalODaoRewards)
		individualOdaoRewards.Div(individualOdaoRewards, totalODaoNodeTime)

		rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[address]                                                                
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
		if !exists {
			// Get the network the rewards should go to
			network := r.networkState.NodeDetailsByAddress[address].RewardNetwork.Uint64()
			validNetwork, err := r.validateNetwork(network)
			if err != nil {
				return err
+--  6 lines: }-----------------------------------------------------------------------------------------------------------------------------------
			rewardsForClaimer = &ClaimerRewardsInfo_v3{
				RewardNetwork:    network,
				CollateralRpl:    sharedtypes.NewQuotedBigInt(0),
				OracleDaoRpl:     sharedtypes.NewQuotedBigInt(0),
				SmoothingPoolEth: sharedtypes.NewQuotedBigInt(0),
			}
			r.rewardsFile.ClaimerRewards[address] = rewardsForClaimer                                                                 

		}
		rewardsForClaimer.OracleDaoRpl.Add(&rewardsForClaimer.OracleDaoRpl.Int, individualOdaoRewards)

		// Add the rewards to the running total for the specified network
		rewardsForNetwork, exists := r.rewardsFile.NetworkRewards[rewardsForClaimer.RewardNetwork]
+-- 30 lines: if !exists {------------------------------------------------------------------------------------------------------------------------
	r.logger.Info("Calculated total node weight,", slog.String(keys.TotalNodeWeightKey, totalNodeWeight.String()))

	return nil
}

// Calculates the ETH rewards for the given interval
func (r *treeGeneratorImpl_v8) calculateEthRewards(context context.Context, checkBeaconPerformance bool) error {                                  
	// Get the Smoothing Pool contract's balance
	r.smoothingPoolBalance = r.networkState.NetworkDetails.SmoothingPoolBalance
	r.logger.Info("Retrieved Smoothing Pool balance", slog.String(keys.AmountKey, getRewardsString(r.smoothingPoolBalance)))

	// Ignore the ETH calculation if there are no rewards
	if r.smoothingPoolBalance.Cmp(common.Big0) == 0 {
+-- 79 lines: return nil--------------------------------------------------------------------------------------------------------------------------
		return err
	}

	// Update the rewards maps
	for _, nodeInfo := range r.nodeDetails {
		if nodeInfo.IsEligible && nodeInfo.SmoothingPoolEth.Cmp(common.Big0) > 0 {
			rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[nodeInfo.Address]                                               
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
			if !exists {
				network := nodeInfo.RewardsNetwork
				validNetwork, err := r.validateNetwork(network)
				if err != nil {
					return err
				}
+--  5 lines: if !validNetwork {------------------------------------------------------------------------------------------------------------------
				rewardsForClaimer = &ClaimerRewardsInfo_v3{
					RewardNetwork:    network,
					CollateralRpl:    sharedtypes.NewQuotedBigInt(0),
					OracleDaoRpl:     sharedtypes.NewQuotedBigInt(0),
					SmoothingPoolEth: sharedtypes.NewQuotedBigInt(0),
				}
				r.rewardsFile.ClaimerRewards[nodeInfo.Address] = rewardsForClaimer                                                
			}
			rewardsForClaimer.SmoothingPoolEth.Add(&rewardsForClaimer.SmoothingPoolEth.Int, nodeInfo.SmoothingPoolEth)

			// Add minipool rewards to the JSON
			for _, minipoolInfo := range nodeInfo.Minipools {
				successfulAttestations := uint64(len(minipoolInfo.CompletedAttestations))
+-- 35 lines: missingAttestations := uint64(len(minipoolInfo.MissingAttestationSlots))------------------------------------------------------------
	r.rewardsFile.TotalRewards.NodeOperatorSmoothingPoolEth.Int = *nodeOpEth
	r.rewardsFile.TotalRewards.TotalSmoothingPoolEth.Int = *r.smoothingPoolBalance
	return nil
}

// Calculate the distribution of Smoothing Pool ETH to each node
func (r *treeGeneratorImpl_v8) calculateNodeRewards() (*big.Int, *big.Int, error) {                                                               
	// If there weren't any successful attestations, everything goes to the pool stakers
	if r.totalAttestationScore.Cmp(common.Big0) == 0 || r.successfulAttestations == 0 {
		r.logger.Warn("Sending the whole smoothing pool balance to the pool stakers.", slog.String(keys.TotalScoreKey, r.totalAttestationScore.String()), slog.Uint64(keys.SuccessfulAttestationsKey, r.successfulAttestations))
		return r.smoothingPoolBalance, big.NewInt(0), nil
	}

+-- 43 lines: totalEthForMinipools := big.NewInt(0)-----------------------------------------------------------------------------------------------
	r.logger.Info("Adjusted Pool Staker ETH to account for truncation", slog.String(keys.AmountKey, truePoolStakerAmount.String()))

	return truePoolStakerAmount, totalEthForMinipools, nil
}

// Get all of the duties for a range of epochs
func (r *treeGeneratorImpl_v8) processAttestationsForInterval(context context.Context) error {                                                    
	startEpoch := r.rewardsFile.ConsensusStartBlock / r.beaconConfig.SlotsPerEpoch
	endEpoch := r.rewardsFile.ConsensusEndBlock / r.beaconConfig.SlotsPerEpoch

	// Determine the validator indices of each minipool
	err := r.createMinipoolIndexMap()
	if err != nil {
+-- 30 lines: return err--------------------------------------------------------------------------------------------------------------------------

	r.logger.Info("Finished participation check", slog.Duration(keys.TotalElapsedKey, time.Since(reportStartTime)))
	return nil
}

// Process an epoch, optionally getting the duties for all eligible minipools in it and checking each one's attestation performance
func (r *treeGeneratorImpl_v8) processEpoch(context context.Context, getDuties bool, epoch uint64) error {                                        

	// Get the committee info and attestation records for this epoch
	var committeeData beacon.Committees
	attestationsPerSlot := make([][]beacon.AttestationInfo, r.slotsPerEpoch)
	var wg errgroup.Group

+-- 45 lines: if getDuties {----------------------------------------------------------------------------------------------------------------------

	return nil

}

// Handle all of the attestations in the given slot
func (r *treeGeneratorImpl_v8) checkDutiesForSlot(attestations []beacon.AttestationInfo, slot uint64) error {                                     

	one := eth.EthToWei(1)
	validatorReq := eth.EthToWei(32)

	// Go through the attestations for the block
	for _, attestation := range attestations {
+-- 51 lines: Get the RP committees for this attestation's slot and index-------------------------------------------------------------------------

	return nil

}

// Maps out the attestaion duties for the given epoch
func (r *treeGeneratorImpl_v8) getDutiesForEpoch(committees beacon.Committees) error {                                                            

	// Crawl the committees
	for idx := 0; idx < committees.Count(); idx++ {
		slotIndex := committees.Slot(idx)
		if slotIndex < r.rewardsFile.ConsensusStartBlock || slotIndex > r.rewardsFile.ConsensusEndBlock {
			// Ignore slots that are out of bounds
+-- 51 lines: continue----------------------------------------------------------------------------------------------------------------------------

	return nil

}

// Maps all minipools to their validator indices and creates a map of indices to minipool info
func (r *treeGeneratorImpl_v8) createMinipoolIndexMap() error {                                                                                   

	// Get the status for all uncached minipool validators and add them to the cache
	r.validatorIndexMap = map[string]*MinipoolInfo{}
	for _, details := range r.nodeDetails {
		if details.IsEligible {
			for _, minipoolInfo := range details.Minipools {
+-- 41 lines: status, exists := r.networkState.ValidatorDetails[minipoolInfo.ValidatorPubkey]-----------------------------------------------------

	return nil

}

// Get the details for every node that was opted into the Smoothing Pool for at least some portion of this interval
func (r *treeGeneratorImpl_v8) getSmoothingPoolNodeDetails() error {                                                                              

	farFutureTime := time.Unix(1000000000000000000, 0) // Far into the future
	farPastTime := time.Unix(0, 0)

	// For each NO, get their opt-in status and time of last change in batches
	r.logger.Info("Getting details of nodes for Smoothing Pool calculation...")
+-- 75 lines: nodeCount := uint64(len(r.networkState.NodeDetails))--------------------------------------------------------------------------------

	return nil

}

// Validates that the provided network is legal
func (r *treeGeneratorImpl_v8) validateNetwork(network uint64) (bool, error) {                                                                    
	valid, exists := r.validNetworkCache[network]
	if !exists {
		// Make the oDAO settings binding
		oMgr, err := oracle.NewOracleDaoManager(r.rp)
		if err != nil {
			return false, fmt.Errorf("error creating oDAO manager binding: %w", err)
+-- 12 lines: }-----------------------------------------------------------------------------------------------------------------------------------
	}

	return valid, nil
}

// Gets the start blocks for the given interval
func (r *treeGeneratorImpl_v8) getStartBlocksForInterval(context context.Context, previousIntervalEvent rewards.RewardsEvent) (*types.Header, error) {
	// Sanity check to confirm the BN can access the block from the previous interval
	_, exists, err := r.bc.GetBeaconBlock(context, previousIntervalEvent.ConsensusBlock.String())
	if err != nil {
		return nil, fmt.Errorf("error verifying block from previous interval: %w", err)
	}
	if !exists {
+-- 41 lines: return nil, fmt.Errorf("couldn't retrieve CL block from previous interval (slot %d); this likely means you checkpoint sync'd your Beacon Node and it has not backfilled to the previous interval yet so it cannot be used for tree generation", previousIntervalEvent.ConsensusBlock.Uint64())
	}

	return startElHeader, nil
}

// Get the bond and node fee of a minipool for the specified time
func (r *treeGeneratorImpl_v8) getMinipoolBondAndNodeFee(details *rpstate.NativeMinipoolDetails, blockTime time.Time) (*big.Int, *big.Int) {      
	currentBond := details.NodeDepositBalance
	currentFee := details.NodeFee
	previousBond := details.LastBondReductionPrevValue
	previousFee := details.LastBondReductionPrevNodeFee

	var reductionTimeBig *big.Int = details.LastBondReductionTime
+-- 17 lines: if reductionTimeBig.Cmp(common.Big0) == 0 {-----------------------------------------------------------------------------------------
+-- 22 lines: package rewards---------------------------------------------------------------------------------------------------------------------
	"github.com/rocket-pool/smartnode/v2/shared/config"
	"github.com/rocket-pool/smartnode/v2/shared/keys"
	sharedtypes "github.com/rocket-pool/smartnode/v2/shared/types"
	"golang.org/x/sync/errgroup"
)

// Implementation for tree generator ruleset v10                                                                                                  
type treeGeneratorImpl_v10 struct {                                                                                                               
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
	networkState           *state.NetworkState
	rewardsFile            *RewardsFile_v3
	elSnapshotHeader       *types.Header
	logger                 *slog.Logger
	rp                     *rocketpool.RocketPool
	cfg                    *config.SmartNodeConfig
+-- 14 lines: bc                     beacon.IBeaconClient-----------------------------------------------------------------------------------------
	totalAttestationScore  *big.Int
	successfulAttestations uint64
	genesisTime            time.Time
}

// Create a new tree generator
func newTreeGeneratorImpl_v10(logger *slog.Logger, index uint64, startTime time.Time, endTime time.Time, consensusBlock uint64, elSnapshotHeader *types.Header, intervalsPassed uint64, state *state.NetworkState) *treeGeneratorImpl_v10 {
	return &treeGeneratorImpl_v10{                                                                                                            
		rewardsFile: &RewardsFile_v3{
			RewardsFileHeader: &sharedtypes.RewardsFileHeader{
				RewardsFileVersion:  3,
				RulesetVersion:      10,                                                                                          
				Index:               index,
				StartTime:           startTime.UTC(),
				EndTime:             endTime.UTC(),
				ConsensusEndBlock:   consensusBlock,
				ExecutionEndBlock:   elSnapshotHeader.Number.Uint64(),
				IntervalsPassed:     intervalsPassed,
+-- 25 lines: InvalidNetworkNodes: map[common.Address]uint64{},-----------------------------------------------------------------------------------
		totalAttestationScore: big.NewInt(0),
		networkState:          state,
	}
}

// Get the version of the ruleset used by this generator
func (r *treeGeneratorImpl_v10) getRulesetVersion() uint64 {                                                                                      
	return r.rewardsFile.RulesetVersion
}

func (r *treeGeneratorImpl_v10) generateTree(context context.Context, rp *rocketpool.RocketPool, cfg *config.SmartNodeConfig, bc beacon.IBeaconClient) (sharedtypes.IRewardsFile, error) {
	r.logger.Info("Started rewards tree generation.", slog.Uint64(keys.RulesetKey, r.rewardsFile.RulesetVersion))

	// Provision some struct params
	r.rp = rp
	r.cfg = cfg
	r.bc = bc
+-- 58 lines: r.validNetworkCache = map[uint64]bool{----------------------------------------------------------------------------------------------

	return r.rewardsFile, nil
}

// Quickly calculates an approximate of the staker's share of the smoothing pool balance without processing Beacon performance
// Used for approximate returns in the rETH ratio update
func (r *treeGeneratorImpl_v10) approximateStakerShareOfSmoothingPool(context context.Context, rp *rocketpool.RocketPool, cfg *config.SmartNodeConfig, bc beacon.IBeaconClient) (*big.Int, error) {
	r.logger.Info("Approximating rewards tree.", slog.Uint64(keys.RulesetKey, r.rewardsFile.RulesetVersion))

	r.rp = rp
	r.cfg = cfg
	r.bc = bc
	r.validNetworkCache = map[uint64]bool{
+-- 34 lines: 0: true,----------------------------------------------------------------------------------------------------------------------------
	}

	return &r.rewardsFile.TotalRewards.PoolStakerSmoothingPoolEth.Int, nil
}

// Calculates the per-network distribution amounts and the total reward amounts
func (r *treeGeneratorImpl_v10) updateNetworksAndTotals() {                                                                                       
	// Get the highest network index with valid rewards
	highestNetworkIndex := uint64(0)
	for network := range r.rewardsFile.NetworkRewards {
		if network > highestNetworkIndex {
			highestNetworkIndex = network
		}
+-- 10 lines: }-----------------------------------------------------------------------------------------------------------------------------------
			}
			r.rewardsFile.NetworkRewards[network] = rewardsForNetwork
		}
	}
}

func (r *treeGeneratorImpl_v10) calculateNodeRplRewards(                                                                                          
	collateralRewards *big.Int,
	nodeEffectiveStake *big.Int,
	totalEffectiveRplStake *big.Int,
	nodeWeight *big.Int,
	totalNodeWeight *big.Int,
) *big.Int {
+-- 36 lines: if nodeEffectiveStake.Sign() <= 0 || nodeWeight.Sign() <= 0 {-----------------------------------------------------------------------

	// Add them together
	return rpip30Rewards.Add(rpip30Rewards, oldRewards)
}

// Calculates the RPL rewards for the given interval
func (r *treeGeneratorImpl_v10) calculateRplRewards() error {                                                                                     
	pendingRewards := r.networkState.NetworkDetails.PendingRPLRewards
	r.logger.Info("Calculated Pending RPL rewards", slog.String(keys.AmountKey, getRewardsString(pendingRewards)))
	if pendingRewards.Cmp(common.Big0) == 0 {
		return fmt.Errorf("there are no pending RPL rewards, so this interval cannot be used for rewards submission")
	}

+-- 43 lines: Get baseline Protocol DAO rewards---------------------------------------------------------------------------------------------------
				totalNodeEffectiveStake,
				nodeWeights[nodeDetails.NodeAddress],
				totalNodeWeight,
			)

			// If there are pending rewards, add it to the map
			claimer := nodeDetails.NodeAddress                                                                                        
			if nodeDetails.IsRplWithdrawalAddressSet && nodeDetails.PrimaryWithdrawalAddress != nodeDetails.NodeAddress {             
				// Add it to the RPL withdrawal address if set                                                                    
				claimer = nodeDetails.RplWithdrawalAddress                                                                        
			}                                                                                                                         
			if nodeRplRewards.Sign() == 1 {
				rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[claimer]                                                
				if !exists {
					// Get the network the rewards should go to
					network := r.networkState.NodeDetails[i].RewardNetwork.Uint64()
					validNetwork, err := r.validateNetwork(network)
					if err != nil {
						return err
+--  6 lines: }-----------------------------------------------------------------------------------------------------------------------------------
					rewardsForClaimer = &ClaimerRewardsInfo_v3{
						RewardNetwork:    network,
						CollateralRpl:    sharedtypes.NewQuotedBigInt(0),
						OracleDaoRpl:     sharedtypes.NewQuotedBigInt(0),
						SmoothingPoolEth: sharedtypes.NewQuotedBigInt(0),
					}
					r.rewardsFile.ClaimerRewards[claimer] = rewardsForClaimer                                                 
				}
				rewardsForClaimer.CollateralRpl.Add(&rewardsForClaimer.CollateralRpl.Int, nodeRplRewards)

				// Add the rewards to the running total for the specified network
				rewardsForNetwork, exists := r.rewardsFile.NetworkRewards[rewardsForClaimer.RewardNetwork]
				if !exists {
+-- 63 lines: rewardsForNetwork = &sharedtypes.NetworkRewardsInfo{--------------------------------------------------------------------------------

		// Calculate the oDAO rewards for the node: (participation time) * (total oDAO rewards) / (total participation time)
		individualOdaoRewards := big.NewInt(0)
		individualOdaoRewards.Mul(trueODaoNodeTimes[address], totalODaoRewards)
		individualOdaoRewards.Div(individualOdaoRewards, totalODaoNodeTime)

		claimer := address                                                                                                                
		node := r.networkState.NodeDetailsByAddress[address]                                                                              
		if node.IsRplWithdrawalAddressSet && node.PrimaryWithdrawalAddress != node.NodeAddress {                                          
			// Add it to the RPL withdrawal address if set                                                                            
			claimer = node.RplWithdrawalAddress                                                                                       
		}                                                                                                                                 
		rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[claimer]                                                                
		if !exists {
			// Get the network the rewards should go to
			network := r.networkState.NodeDetailsByAddress[address].RewardNetwork.Uint64()
			validNetwork, err := r.validateNetwork(network)
			if err != nil {
				return err
+--  6 lines: }-----------------------------------------------------------------------------------------------------------------------------------
			rewardsForClaimer = &ClaimerRewardsInfo_v3{
				RewardNetwork:    network,
				CollateralRpl:    sharedtypes.NewQuotedBigInt(0),
				OracleDaoRpl:     sharedtypes.NewQuotedBigInt(0),
				SmoothingPoolEth: sharedtypes.NewQuotedBigInt(0),
			}
			r.rewardsFile.ClaimerRewards[claimer] = rewardsForClaimer                                                                 

		}
		rewardsForClaimer.OracleDaoRpl.Add(&rewardsForClaimer.OracleDaoRpl.Int, individualOdaoRewards)

		// Add the rewards to the running total for the specified network
		rewardsForNetwork, exists := r.rewardsFile.NetworkRewards[rewardsForClaimer.RewardNetwork]
+-- 30 lines: if !exists {------------------------------------------------------------------------------------------------------------------------
	r.logger.Info("Calculated total node weight,", slog.String(keys.TotalNodeWeightKey, totalNodeWeight.String()))

	return nil
}

// Calculates the ETH rewards for the given interval
func (r *treeGeneratorImpl_v10) calculateEthRewards(context context.Context, checkBeaconPerformance bool) error {                                 
	// Get the Smoothing Pool contract's balance
	r.smoothingPoolBalance = r.networkState.NetworkDetails.SmoothingPoolBalance
	r.logger.Info("Retrieved Smoothing Pool balance", slog.String(keys.AmountKey, getRewardsString(r.smoothingPoolBalance)))

	// Ignore the ETH calculation if there are no rewards
	if r.smoothingPoolBalance.Cmp(common.Big0) == 0 {
+-- 79 lines: return nil--------------------------------------------------------------------------------------------------------------------------
		return err
	}

	// Update the rewards maps
	for _, nodeInfo := range r.nodeDetails {
		if nodeInfo.IsEligible && nodeInfo.SmoothingPoolEth.Cmp(common.Big0) > 0 {
			claimer := nodeInfo.Address                                                                                               
			node := r.networkState.NodeDetailsByAddress[nodeInfo.Address]                                                             
			if node.IsRplWithdrawalAddressSet && node.PrimaryWithdrawalAddress != node.NodeAddress {                                  
				// Add the ETH to the primary withdrawal address if the RPL withdrawal address is set                             
				claimer = node.PrimaryWithdrawalAddress                                                                           
			}                                                                                                                         
                                                                                                                                                  
			rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[claimer]                                                        
			if !exists {
				network := nodeInfo.RewardsNetwork
				validNetwork, err := r.validateNetwork(network)
				if err != nil {
					return err
				}
+--  5 lines: if !validNetwork {------------------------------------------------------------------------------------------------------------------
				rewardsForClaimer = &ClaimerRewardsInfo_v3{
					RewardNetwork:    network,
					CollateralRpl:    sharedtypes.NewQuotedBigInt(0),
					OracleDaoRpl:     sharedtypes.NewQuotedBigInt(0),
					SmoothingPoolEth: sharedtypes.NewQuotedBigInt(0),
				}
				r.rewardsFile.ClaimerRewards[claimer] = rewardsForClaimer                                                         
			}
			rewardsForClaimer.SmoothingPoolEth.Add(&rewardsForClaimer.SmoothingPoolEth.Int, nodeInfo.SmoothingPoolEth)

			// Add minipool rewards to the JSON
			for _, minipoolInfo := range nodeInfo.Minipools {
				successfulAttestations := uint64(len(minipoolInfo.CompletedAttestations))
+-- 35 lines: missingAttestations := uint64(len(minipoolInfo.MissingAttestationSlots))------------------------------------------------------------
	r.rewardsFile.TotalRewards.NodeOperatorSmoothingPoolEth.Int = *nodeOpEth
	r.rewardsFile.TotalRewards.TotalSmoothingPoolEth.Int = *r.smoothingPoolBalance
	return nil
}

// Calculate the distribution of Smoothing Pool ETH to each node
func (r *treeGeneratorImpl_v10) calculateNodeRewards() (*big.Int, *big.Int, error) {                                                              
	// If there weren't any successful attestations, everything goes to the pool stakers
	if r.totalAttestationScore.Cmp(common.Big0) == 0 || r.successfulAttestations == 0 {
		r.logger.Warn("Sending the whole smoothing pool balance to the pool stakers.", slog.String(keys.TotalScoreKey, r.totalAttestationScore.String()), slog.Uint64(keys.SuccessfulAttestationsKey, r.successfulAttestations))
		return r.smoothingPoolBalance, big.NewInt(0), nil
	}

+-- 43 lines: totalEthForMinipools := big.NewInt(0)-----------------------------------------------------------------------------------------------
	r.logger.Info("Adjusted Pool Staker ETH to account for truncation", slog.String(keys.AmountKey, truePoolStakerAmount.String()))

	return truePoolStakerAmount, totalEthForMinipools, nil
}

// Get all of the duties for a range of epochs
func (r *treeGeneratorImpl_v10) processAttestationsForInterval(context context.Context) error {                                                   
	startEpoch := r.rewardsFile.ConsensusStartBlock / r.beaconConfig.SlotsPerEpoch
	endEpoch := r.rewardsFile.ConsensusEndBlock / r.beaconConfig.SlotsPerEpoch

	// Determine the validator indices of each minipool
	err := r.createMinipoolIndexMap()
	if err != nil {
+-- 30 lines: return err--------------------------------------------------------------------------------------------------------------------------

	r.logger.Info("Finished participation check", slog.Duration(keys.TotalElapsedKey, time.Since(reportStartTime)))
	return nil
}

// Process an epoch, optionally getting the duties for all eligible minipools in it and checking each one's attestation performance
func (r *treeGeneratorImpl_v10) processEpoch(context context.Context, getDuties bool, epoch uint64) error {                                       

	// Get the committee info and attestation records for this epoch
	var committeeData beacon.Committees
	attestationsPerSlot := make([][]beacon.AttestationInfo, r.slotsPerEpoch)
	var wg errgroup.Group

+-- 45 lines: if getDuties {----------------------------------------------------------------------------------------------------------------------

	return nil

}

// Handle all of the attestations in the given slot
func (r *treeGeneratorImpl_v10) checkDutiesForSlot(attestations []beacon.AttestationInfo, slot uint64) error {                                    

	one := eth.EthToWei(1)
	validatorReq := eth.EthToWei(32)

	// Go through the attestations for the block
	for _, attestation := range attestations {
+-- 51 lines: Get the RP committees for this attestation's slot and index-------------------------------------------------------------------------

	return nil

}

// Maps out the attestaion duties for the given epoch
func (r *treeGeneratorImpl_v10) getDutiesForEpoch(committees beacon.Committees) error {                                                           

	// Crawl the committees
	for idx := 0; idx < committees.Count(); idx++ {
		slotIndex := committees.Slot(idx)
		if slotIndex < r.rewardsFile.ConsensusStartBlock || slotIndex > r.rewardsFile.ConsensusEndBlock {
			// Ignore slots that are out of bounds
+-- 51 lines: continue----------------------------------------------------------------------------------------------------------------------------

	return nil

}

// Maps all minipools to their validator indices and creates a map of indices to minipool info
func (r *treeGeneratorImpl_v10) createMinipoolIndexMap() error {                                                                                  

	// Get the status for all uncached minipool validators and add them to the cache
	r.validatorIndexMap = map[string]*MinipoolInfo{}
	for _, details := range r.nodeDetails {
		if details.IsEligible {
			for _, minipoolInfo := range details.Minipools {
+-- 41 lines: status, exists := r.networkState.ValidatorDetails[minipoolInfo.ValidatorPubkey]-----------------------------------------------------

	return nil

}

// Get the details for every node that was opted into the Smoothing Pool for at least some portion of this interval
func (r *treeGeneratorImpl_v10) getSmoothingPoolNodeDetails() error {                                                                             

	farFutureTime := time.Unix(1000000000000000000, 0) // Far into the future
	farPastTime := time.Unix(0, 0)

	// For each NO, get their opt-in status and time of last change in batches
	r.logger.Info("Getting details of nodes for Smoothing Pool calculation...")
+-- 75 lines: nodeCount := uint64(len(r.networkState.NodeDetails))--------------------------------------------------------------------------------

	return nil

}

// Validates that the provided network is legal
func (r *treeGeneratorImpl_v10) validateNetwork(network uint64) (bool, error) {                                                                   
	valid, exists := r.validNetworkCache[network]
	if !exists {
		// Make the oDAO settings binding
		oMgr, err := oracle.NewOracleDaoManager(r.rp)
		if err != nil {
			return false, fmt.Errorf("error creating oDAO manager binding: %w", err)
+-- 12 lines: }-----------------------------------------------------------------------------------------------------------------------------------
	}

	return valid, nil
}

// Gets the start blocks for the given interval
func (r *treeGeneratorImpl_v10) getStartBlocksForInterval(context context.Context, previousIntervalEvent rewards.RewardsEvent) (*types.Header, error) {
	// Sanity check to confirm the BN can access the block from the previous interval
	_, exists, err := r.bc.GetBeaconBlock(context, previousIntervalEvent.ConsensusBlock.String())
	if err != nil {
		return nil, fmt.Errorf("error verifying block from previous interval: %w", err)
	}
	if !exists {
+-- 41 lines: return nil, fmt.Errorf("couldn't retrieve CL block from previous interval (slot %d); this likely means you checkpoint sync'd your Beacon Node and it has not backfilled to the previous interval yet so it cannot be used for tree generation", previousIntervalEvent.ConsensusBlock.Uint64())
	}

	return startElHeader, nil
}

// Get the bond and node fee of a minipool for the specified time
func (r *treeGeneratorImpl_v10) getMinipoolBondAndNodeFee(details *rpstate.NativeMinipoolDetails, blockTime time.Time) (*big.Int, *big.Int) {     
	currentBond := details.NodeDepositBalance
	currentFee := details.NodeFee
	previousBond := details.LastBondReductionPrevValue
	previousFee := details.LastBondReductionPrevNodeFee

	var reductionTimeBig *big.Int = details.LastBondReductionTime
+-- 17 lines: if reductionTimeBig.Cmp(common.Big0) == 0 {-----------------------------------------------------------------------------------------