Skip to content

feat: implement node rewards calculation and API endpoint #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 45 commits into
base: development
Choose a base branch
from

Conversation

0oM4R
Copy link

@0oM4R 0oM4R commented Jun 12, 2025

Description

This PR introduces a new API endpoint to calculate real-time node rewards based on node resources and uptime reports.

Please note that the network usage is not included
And please consider those concerns, comment

Changes

  • New Endpoint:
    GET /nodes/{node_id}/rewards
    Returns: Rewards

  • New Handler:
    getNodeRewardsHandler – handles input validation and response logic.

New Reward Logic:
Added a pkg/server/rewards.go file with the logic to:
- Get uptime reports for the current month
- Calculate uptime percentage
- Apply reward formula
- Return reward values (or zeroes if uptime is too low)

Swagger Update:
API documentation updated with the new endpoint and response schema.

Tests

Add unit tests, create a fake node, and submit some reports
image

Related Issues

Checklist

  • Tests included
  • Build pass
  • Documentation
  • Code format and docstring

@Eslam-Nawara Eslam-Nawara force-pushed the development_rewards branch from 10c4a79 to 6646f65 Compare June 29, 2025 12:26

// Use precise floating point comparison
const delta = 1e-9 // Very small acceptable difference
assert.InDelta(t, expected.FarmerReward, got.FarmerReward, delta)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all this values are rounded no?

@0oM4R 0oM4R requested a review from Eslam-Nawara July 8, 2025 10:39
Comment on lines 145 to 149
assert.Equal(t, expected.FarmerReward, got.FarmerReward)
assert.Equal(t, expected.TfReward, got.TfReward)
assert.Equal(t, expected.FpReward, got.FpReward)
assert.Equal(t, expected.Total, got.Total)
assert.Equal(t, expected.UpTimePercentage, got.UpTimePercentage)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just use assert.Equal(t, expected, got)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just use assert.Equal(t, expected, got)

Is that enough, or should we use something like deep equal?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert.Equal does the same job

@0oM4R 0oM4R requested a review from Eslam-Nawara July 14, 2025 15:30
}

// calculateCapacityReward calculates the reward for a node based on its capacity and uptime, during a specific period.
func CalculateCapacityReward(capacity db.Resources, reports []db.UptimeReport, periodStart, periodEnd time.Time) (Reward, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function is not used outside the server pkg, so no need to be exported

Comment on lines 145 to 149
assert.Equal(t, expected.FarmerReward, got.FarmerReward)
assert.Equal(t, expected.TfReward, got.TfReward)
assert.Equal(t, expected.FpReward, got.FpReward)
assert.Equal(t, expected.Total, got.Total)
assert.Equal(t, expected.UpTimePercentage, got.UpTimePercentage)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert.Equal does the same job

return Reward{}, ErrReportsNotInAscOrder
}

upTimePercentage := calculatePercentage(periodEnd.Sub(periodStart), downtimeSinceLastReportTimestamp(reports[len(reports)-1].Timestamp, periodEnd))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

percentage of what?


upTimePercentage := calculatePercentage(periodEnd.Sub(periodStart), downtimeSinceLastReportTimestamp(reports[len(reports)-1].Timestamp, periodEnd))

return computeCapacityRewardWithUptime(capacity, upTimePercentage)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculate instead of compute

Comment on lines +72 to +86
// calculateBaseCapacityReward calculates the base reward from node capacity without applying uptime.
func calculateBaseCapacityReward(capacity db.Resources) float64 {
mruReward := bytesToGB(capacity.MRU) * MemoryRewardPerGB
sruReward := bytesToTB(capacity.SRU) * SsdRewardPerTB
hruReward := bytesToTB(capacity.HRU) * HddRewardPerTB

return mruReward + sruReward + hruReward
}

// calculateTotalReward calculates the total reward based on node capacity and uptime percentage.
// It first calculates the base capacity reward and then applies the uptime percentage.
func calculateTotalReward(capacity db.Resources, upTimePercentage float64) float64 {
baseReward := calculateBaseCapacityReward(capacity)
return baseReward * (upTimePercentage / 100)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need two functions for this, the logic of calculateBaseCapacityReward can be moved to calculate totalReward instead

Comment on lines +30 to +38
FirstPeriodStartTimestamp int64 = 1522501000

// Uptime events are supposed to happen every 40 minutes.
// Here we set this to one hour (3600 sec) to allow some room.
UptimeEventsInterval = 3600

// The duration of a standard period, as used by the minting payouts, in seconds.
// Calculated as: 24 hours * 60 minutes * 60 seconds * (365*3 + 366*2) / 60 periods
StandardPeriodDuration int64 = 24 * 60 * 60 * (365*3 + 366*2) / 60
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we using those consts outside the pkg? if no we should not export them

Comment on lines +128 to +129
periodStart := referenceTime.Unix() - periodOffset
return time.Unix(periodStart, 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use referenceTime.Add(-time.Duration(periodOffset)) instead

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants