generator-impl-v8-rolling.go generator-impl-v10-rolling.go
+-- 21 lines: package rewards---------------------------------------------------------------------------------------------------------------------
	"github.com/rocket-pool/smartnode/v2/shared/keys"
	sharedtypes "github.com/rocket-pool/smartnode/v2/shared/types"
	merkletree "github.com/wealdtech/go-merkletree"
	"github.com/wealdtech/go-merkletree/keccak256"
)

// Implementation for tree generator ruleset v8 with rolling record support                                                                       
type treeGeneratorImpl_v8_rolling struct {                                                                                                        
	networkState         *state.NetworkState
	rewardsFile          *RewardsFile_v3
	elSnapshotHeader     *types.Header
	logger               *slog.Logger
	rp                   *rocketpool.RocketPool
	cfg                  *config.SmartNodeConfig
+-- 11 lines: bc                   beacon.IBeaconClient-------------------------------------------------------------------------------------------
	beaconConfig         beacon.Eth2Config
	rollingRecord        *RollingRecord
	nodeDetails          map[common.Address]*NodeSmoothingDetails
}

// Create a new tree generator
func newTreeGeneratorImpl_v8_rolling(logger *slog.Logger, index uint64, startTime time.Time, endTime time.Time, consensusBlock uint64, elSnapshotHeader *types.Header, intervalsPassed uint64, state *state.NetworkState, rollingRecord *RollingRecord) *treeGeneratorImpl_v8_rolling {
	return &treeGeneratorImpl_v8_rolling{                                                                                                     
		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,
+-- 24 lines: InvalidNetworkNodes: map[common.Address]uint64{},-----------------------------------------------------------------------------------
		networkState:      state,
		rollingRecord:     rollingRecord,
	}
}

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

func (r *treeGeneratorImpl_v8_rolling) 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
+-- 57 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_rolling) 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{
+-- 33 lines: 0: true,----------------------------------------------------------------------------------------------------------------------------
	}

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

// Generates a merkle tree from the provided rewards map
func (r *treeGeneratorImpl_v8_rolling) generateMerkleTree() error {                                                                               
	// Generate the leaf data for each claimer
	totalData := make([][]byte, 0, len(r.rewardsFile.ClaimerRewards))
	for address, rewardsForClaimer := range r.rewardsFile.ClaimerRewards {
		// Ignore claimers that didn't receive any rewards
		if rewardsForClaimer.CollateralRpl.Cmp(common.Big0) == 0 && rewardsForClaimer.OracleDaoRpl.Cmp(common.Big0) == 0 && rewardsForClaimer.SmoothingPoolEth.Cmp(common.Big0) == 0 {
			continue
+-- 56 lines: }-----------------------------------------------------------------------------------------------------------------------------------
	r.rewardsFile.MerkleTree = tree
	r.rewardsFile.MerkleRoot = common.BytesToHash(tree.Root()).Hex()
	return nil
}

// Calculates the per-network distribution amounts and the total reward amounts
func (r *treeGeneratorImpl_v8_rolling) 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_rolling) 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_rolling) 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
			}
			if !validNetwork {
				r.rewardsFile.InvalidNetworkNodes[address] = network
+--  3 lines: network = 0-------------------------------------------------------------------------------------------------------------------------
			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]
+-- 27 lines: if !exists {------------------------------------------------------------------------------------------------------------------------
	r.logger.Info("Calculated true Protocol DAO rewards to account for truncation", slog.String(keys.AmountKey, getRewardsString(&pDaoRewards.Int)))

	return nil
}

// Calculates the ETH rewards for the given interval
func (r *treeGeneratorImpl_v8_rolling) 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 {
+-- 27 lines: return nil--------------------------------------------------------------------------------------------------------------------------
		return err
	}

	// Update the rewards maps
	for nodeAddress, nodeInfo := range r.nodeDetails {
		if nodeInfo.SmoothingPoolEth.Cmp(common.Big0) > 0 {
			rewardsForClaimer, exists := r.rewardsFile.ClaimerRewards[nodeAddress]                                                    
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------
			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[nodeAddress] = rewardsForClaimer                                                     
			}
			rewardsForClaimer.SmoothingPoolEth.Add(&rewardsForClaimer.SmoothingPoolEth.Int, nodeInfo.SmoothingPoolEth)

			// Add minipool rewards to the JSON
			for _, minipoolInfo := range nodeInfo.Minipools {
				successfulAttestations := uint64(minipoolInfo.AttestationCount)
+-- 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_rolling) calculateNodeRewards() (*big.Int, *big.Int, error) {                                                       
	// Get the list of cheaters
	cheaters := r.getCheaters()

	// Get the latest scores from the rolling record
	minipools, totalScore, attestationCount := r.rollingRecord.GetScores(cheaters)

+-- 55 lines: If there weren't any successful attestations, everything goes to the pool stakers---------------------------------------------------
	r.logger.Info("Adjusted Pool Staker ETH to account for truncation", slog.String(keys.AmountKey, truePoolStakerAmount.String()))

	return truePoolStakerAmount, totalEthForMinipools, nil
}

// Validates that the provided network is legal
func (r *treeGeneratorImpl_v8_rolling) 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 EL header for the given interval's start block
func (r *treeGeneratorImpl_v8_rolling) getStartBlocksForInterval(context context.Context) (*types.Header, error) {                                
	// Get the Beacon block for the start slot of the record
	r.rewardsFile.ConsensusStartBlock = r.rollingRecord.StartSlot
	r.rewardsFile.MinipoolPerformanceFile.ConsensusStartBlock = r.rollingRecord.StartSlot
	beaconBlock, exists, err := r.bc.GetBeaconBlock(context, fmt.Sprint(r.rollingRecord.StartSlot))
	if err != nil {
		return nil, fmt.Errorf("error verifying block from previous interval: %w", err)
+-- 12 lines: }-----------------------------------------------------------------------------------------------------------------------------------
	}

	return startElHeader, nil
}

// Detect and flag any cheaters
func (r *treeGeneratorImpl_v8_rolling) getCheaters() map[common.Address]bool {                                                                    
	cheatingNodes := map[common.Address]bool{}
	three := big.NewInt(3)

	for _, nd := range r.networkState.NodeDetails {
		for _, mpd := range r.networkState.MinipoolDetailsByNode[nd.NodeAddress] {
			if mpd.PenaltyCount.Cmp(three) >= 0 {
+--  9 lines: If any minipool has 3+ penalties, ban the entire node-------------------------------------------------------------------------------
+-- 21 lines: package rewards---------------------------------------------------------------------------------------------------------------------
	"github.com/rocket-pool/smartnode/v2/shared/keys"
	sharedtypes "github.com/rocket-pool/smartnode/v2/shared/types"
	merkletree "github.com/wealdtech/go-merkletree"
	"github.com/wealdtech/go-merkletree/keccak256"
)

// Implementation for tree generator ruleset v10 with rolling record support                                                                      
type treeGeneratorImpl_v10_rolling struct {                                                                                                       
	networkState         *state.NetworkState
	rewardsFile          *RewardsFile_v3
	elSnapshotHeader     *types.Header
	logger               *slog.Logger
	rp                   *rocketpool.RocketPool
	cfg                  *config.SmartNodeConfig
+-- 11 lines: bc                   beacon.IBeaconClient-------------------------------------------------------------------------------------------
	beaconConfig         beacon.Eth2Config
	rollingRecord        *RollingRecord
	nodeDetails          map[common.Address]*NodeSmoothingDetails
}

// Create a new tree generator
func newTreeGeneratorImpl_v10_rolling(logger *slog.Logger, index uint64, startTime time.Time, endTime time.Time, consensusBlock uint64, elSnapshotHeader *types.Header, intervalsPassed uint64, state *state.NetworkState, rollingRecord *RollingRecord) *treeGeneratorImpl_v10_rolling {
	return &treeGeneratorImpl_v10_rolling{                                                                                                    
		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,
+-- 24 lines: InvalidNetworkNodes: map[common.Address]uint64{},-----------------------------------------------------------------------------------
		networkState:      state,
		rollingRecord:     rollingRecord,
	}
}

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

func (r *treeGeneratorImpl_v10_rolling) 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
+-- 57 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_rolling) 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{
+-- 33 lines: 0: true,----------------------------------------------------------------------------------------------------------------------------
	}

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

// Generates a merkle tree from the provided rewards map
func (r *treeGeneratorImpl_v10_rolling) generateMerkleTree() error {                                                                              
	// Generate the leaf data for each claimer
	totalData := make([][]byte, 0, len(r.rewardsFile.ClaimerRewards))
	for address, rewardsForClaimer := range r.rewardsFile.ClaimerRewards {
		// Ignore claimers that didn't receive any rewards
		if rewardsForClaimer.CollateralRpl.Cmp(common.Big0) == 0 && rewardsForClaimer.OracleDaoRpl.Cmp(common.Big0) == 0 && rewardsForClaimer.SmoothingPoolEth.Cmp(common.Big0) == 0 {
			continue
+-- 56 lines: }-----------------------------------------------------------------------------------------------------------------------------------
	r.rewardsFile.MerkleTree = tree
	r.rewardsFile.MerkleRoot = common.BytesToHash(tree.Root()).Hex()
	return nil
}

// Calculates the per-network distribution amounts and the total reward amounts
func (r *treeGeneratorImpl_v10_rolling) 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_rolling) 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_rolling) 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 := node.RewardNetwork.Uint64()                                                                                    
			validNetwork, err := r.validateNetwork(network)
			if err != nil {
				return err
			}
			if !validNetwork {
				r.rewardsFile.InvalidNetworkNodes[address] = network
+--  3 lines: network = 0-------------------------------------------------------------------------------------------------------------------------
			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]
+-- 27 lines: if !exists {------------------------------------------------------------------------------------------------------------------------
	r.logger.Info("Calculated true Protocol DAO rewards to account for truncation", slog.String(keys.AmountKey, getRewardsString(&pDaoRewards.Int)))

	return nil
}

// Calculates the ETH rewards for the given interval
func (r *treeGeneratorImpl_v10_rolling) 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 {
+-- 27 lines: return nil--------------------------------------------------------------------------------------------------------------------------
		return err
	}

	// Update the rewards maps
	for nodeAddress, nodeInfo := range r.nodeDetails {
		if nodeInfo.SmoothingPoolEth.Cmp(common.Big0) > 0 {
			claimer := nodeAddress                                                                                                    
			node := r.networkState.NodeDetailsByAddress[nodeAddress]                                                                  
			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(minipoolInfo.AttestationCount)
+-- 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_rolling) calculateNodeRewards() (*big.Int, *big.Int, error) {                                                      
	// Get the list of cheaters
	cheaters := r.getCheaters()

	// Get the latest scores from the rolling record
	minipools, totalScore, attestationCount := r.rollingRecord.GetScores(cheaters)

+-- 55 lines: If there weren't any successful attestations, everything goes to the pool stakers---------------------------------------------------
	r.logger.Info("Adjusted Pool Staker ETH to account for truncation", slog.String(keys.AmountKey, truePoolStakerAmount.String()))

	return truePoolStakerAmount, totalEthForMinipools, nil
}

// Validates that the provided network is legal
func (r *treeGeneratorImpl_v10_rolling) 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 EL header for the given interval's start block
func (r *treeGeneratorImpl_v10_rolling) getStartBlocksForInterval(context context.Context) (*types.Header, error) {                               
	// Get the Beacon block for the start slot of the record
	r.rewardsFile.ConsensusStartBlock = r.rollingRecord.StartSlot
	r.rewardsFile.MinipoolPerformanceFile.ConsensusStartBlock = r.rollingRecord.StartSlot
	beaconBlock, exists, err := r.bc.GetBeaconBlock(context, fmt.Sprint(r.rollingRecord.StartSlot))
	if err != nil {
		return nil, fmt.Errorf("error verifying block from previous interval: %w", err)
+-- 12 lines: }-----------------------------------------------------------------------------------------------------------------------------------
	}

	return startElHeader, nil
}

// Detect and flag any cheaters
func (r *treeGeneratorImpl_v10_rolling) getCheaters() map[common.Address]bool {                                                                   
	cheatingNodes := map[common.Address]bool{}
	three := big.NewInt(3)

	for _, nd := range r.networkState.NodeDetails {
		for _, mpd := range r.networkState.MinipoolDetailsByNode[nd.NodeAddress] {
			if mpd.PenaltyCount.Cmp(three) >= 0 {
+--  9 lines: If any minipool has 3+ penalties, ban the entire node-------------------------------------------------------------------------------