Skip to content
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

Add NP rewards with dashboard logs #978

Merged
merged 19 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 31 additions & 40 deletions rs/dre-canisters/trustworthy-node-metrics/add_np.py
Original file line number Diff line number Diff line change
@@ -1,60 +1,51 @@
import csv
import json
import subprocess

import time
# Define the file paths and constants
csv_file_path = "node_info_api.csv"
did_file_path = "rs/dre-canisters/trustworthy-node-metrics/src/trustworthy-node-metrics/trustworthy-node-metrics.did"
network = "ic"
canister_name = "trustworthy-node-metrics"

# Function to generate the dfx command for node_metadata
def generate_dfx_command(node_mappings):
nodes = [
f'record {{ node_id = principal "{row["node_id"]}"; node_provider_id = principal "{row["node_provider_id"]}"; node_provider_name = "{row["node_provider_name"]}"; }}'
for row in node_mappings
]
# Prepare the argument for the canister call
node_metadata_args = f"vec {{ {'; '.join(nodes)} ; }}"

# Function to generate the dfx command for a single node
def generate_dfx_command(node_data):
command = [
"dfx", "canister", "call", canister_name, "node_metadata",
f'({node_metadata_args})',
"dfx", "canister", "call", canister_name, "np_rewardable_backfill",
f'(record {{ node_provider_id = principal "{node_data["node_provider_id"]}"; region = "{node_data["region"]}"; node_type = "{node_data["node_type"]}"; count = {node_data["count"]}; }})',
"--candid", did_file_path,
"--network", network
]

return command

# Read the CSV file and filter out missing providers
def read_csv(csv_file_path):
node_mappings = []
with open(csv_file_path, mode='r') as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
if row['node_provider_id'] != "missing": # Ignore missing providers
node_mappings.append({
"node_id": row["node_id"],
"node_provider_id": row["node_provider_id"],
"node_provider_name": row["node_provider_name"]
})
# Read the .json file and extract node information
def read_json(json_file_path):
with open(json_file_path, mode='r') as file:
# Each line is a separate JSON object, so we use a list to store them
node_mappings = [json.loads(line) for line in file]
return node_mappings

# Execute the dfx command for each node
def execute_dfx_command(command):
try:
result = subprocess.run(command, capture_output=True, text=True)
print("Command executed successfully:", result.stdout)
if result.stderr:
print("Error:", result.stderr)
except Exception as e:
print("Failed to execute the dfx command:", str(e))

# Main execution
if __name__ == "__main__":
# Read the CSV and get node mappings
node_mappings = read_csv(csv_file_path)
# Read the JSON file and get node mappings
node_mappings = read_json(json_file_path)

if node_mappings:
# Generate the dfx command
dfx_command = generate_dfx_command(node_mappings)

# Execute the dfx command
try:
result = subprocess.run(dfx_command, capture_output=True, text=True)
print(result.stdout)
if result.stderr:
print("Error:", result.stderr)
except Exception as e:
print("Failed to execute the dfx command:", str(e))
# Loop through each node and execute a separate command
for node in node_mappings:
# Generate the dfx command for the current node
dfx_command = generate_dfx_command(node)

# Execute the dfx command for the current node
execute_dfx_command(dfx_command)
else:
print("No valid node mappings found.")
print("No valid node mappings found.")
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChartData, formatDateToUTC, generateChartData, LoadingIndicator, NodeMe
import { PeriodFilter } from './FilterBar';
import { Box, Grid } from '@mui/material';
import PerformanceChart from './PerformanceChart';
import { NodeRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeRewardsMultiplier } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { ExportTable } from './ExportTable';
import { GridColDef, GridRowsProp } from '@mui/x-data-grid';
import { Principal } from '@dfinity/principal';
Expand All @@ -16,7 +16,7 @@ export interface NodePerformanceChartProps {
}

export const NodePerformanceChart: React.FC<NodePerformanceChartProps> = ({ node, periodFilter }) => {
const [performanceData, setPerformanceData] = useState<NodeRewards | null>(null);
const [performanceData, setPerformanceData] = useState<NodeRewardsMultiplier | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
Expand All @@ -36,7 +36,7 @@ export const NodePerformanceChart: React.FC<NodePerformanceChartProps> = ({ node
}

const performanceDailyData: ChartData[] = generateChartData(periodFilter, performanceData.daily_node_metrics);
const failureRateAvg = Math.round(performanceData.rewards_computation.failure_rate * 100);
const failureRateAvg = Math.round(performanceData.rewards_multiplier.failure_rate * 100);

const rows: GridRowsProp = performanceData.daily_node_metrics.map((data, index) => {
return {
Expand All @@ -62,7 +62,7 @@ export const NodePerformanceChart: React.FC<NodePerformanceChartProps> = ({ node
return (
<>
<Grid item xs={12} md={6}>
<NodeMetricsStats stats={performanceData.rewards_computation} />
<NodeMetricsStats stats={performanceData.rewards_multiplier} />
</Grid>
<Grid item xs={12} md={6}>
<Box sx={boxStyleWidget('right')}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const NodeProviderChart: React.FC<NodeProviderChartProps> = ({ provider,
const providerNodeMetrics = providerRewards.nodes_rewards;

const highFailureRateChart = providerNodeMetrics
.sort((a, b) => b.rewards_computation.failure_rate - a.rewards_computation.failure_rate)
.sort((a, b) => b.rewards_multiplier.failure_rate - a.rewards_multiplier.failure_rate)
.slice(0, 3)
.flatMap(nodeMetrics => {
const chartData = generateChartData(periodFilter, nodeMetrics.daily_node_metrics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const NodeProviderPage: React.FC<NodeProviderPageProps> = ({ nodeMetadata
</Grid>
<Grid item xs={12}>
<Typography variant="h6" component="div" >
Provider Rewards
Last Rewarding Period
</Typography>
<Divider/>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useEffect, useState } from 'react';
import { getLatestRewardRange, LoadingIndicator, setNodeProviderRewardsData } from '../utils/utils';
import { Box, Grid } from '@mui/material';
import { Box, Grid, Typography } from '@mui/material';
import { Principal } from '@dfinity/principal';
import { NodeProviderRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { WidgetNumber } from './Widgets';
import { boxStyleWidget } from '../Styles';
import { ExportTable } from './ExportTable';
import { GridColDef, GridRowsProp } from '@mui/x-data-grid';

export interface NodeProviderRewardsChartProps {
provider: string;
Expand Down Expand Up @@ -36,6 +38,21 @@ export const NodeProviderRewardsChart: React.FC<NodeProviderRewardsChartProps> =
return <p>No latestNodeRewards</p>;
}
const distribution_date = new Date(Number(latestProviderRewards.ts_distribution) * 1000);
const rows: GridRowsProp = latestProviderRewards.computation_log.map((data, index) => {
return {
id: index,
col0: index,
col1: data.reason,
col2: data.operation,
col3: data.result
};
});
const colDef: GridColDef[] = [
{ field: 'col0', headerName: 'Step', width: 100},
{ field: 'col1', headerName: 'Description', width: 1500},
{ field: 'col2', headerName: 'Operation', width: 500 },
{ field: 'col3', headerName: 'Result', width: 200 },
];

return (
<>
Expand All @@ -49,10 +66,16 @@ export const NodeProviderRewardsChart: React.FC<NodeProviderRewardsChartProps> =
</Grid>
<Grid item xs={12} md={6}>
<Box sx={boxStyleWidget('right')}>
<WidgetNumber value={Math.round(Number(latestProviderRewards.rewards_xdr) / Number(latestProviderRewards.xdr_conversion_rate)).toString()} title="Expected Rewards ICP (Excluding never assigned nodes)" sxValue={{ color: '#FFCC00' }} />
<WidgetNumber value={Math.round(Number(latestProviderRewards.rewards_xdr) / Number(latestProviderRewards.xdr_conversion_rate)).toString()} title="Expected Rewards ICP" sxValue={{ color: '#FFCC00' }} />
<WidgetNumber value={Math.round(Number(rewards_xdr_old[0]) / 100000000).toString()} title="Last Rewards ICP Received" sxValue={{ color: '#FFCC00' }} />
</Box>
</Grid>
<Grid item xs={12} md={12}>
<Typography variant="body1" gutterBottom>
Computation Log
</Typography>
<ExportTable colDef={colDef} rows={rows}/>
</Grid>

</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { ChartData, generateChartData, getLatestRewardRange, LoadingIndicator, NodeMetricsStats, NodePerformanceStats, setNodeRewardsData } from '../utils/utils';
import { Grid, Typography } from '@mui/material';
import PerformanceChart from './PerformanceChart';
import { NodeRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeRewardsMultiplier } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import RewardsInfo, { LinearReductionChart } from './RewardsInfo';
import { Principal } from '@dfinity/principal';
import { ExportTable } from './ExportTable';
Expand All @@ -14,7 +14,7 @@ export interface NodeRewardsChartProps {

export const NodeRewardsChart: React.FC<NodeRewardsChartProps> = ({ node }) => {
const latestRewardRange = getLatestRewardRange();
const [latestNodeRewards, setLatestNodeRewards] = useState<NodeRewards | null>(null);
const [latestNodeRewards, setLatestNodeRewards] = useState<NodeRewardsMultiplier | null>(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
Expand All @@ -33,10 +33,10 @@ export const NodeRewardsChart: React.FC<NodeRewardsChartProps> = ({ node }) => {
}

const rewardsDailyData: ChartData[] = generateChartData(latestRewardRange, latestNodeRewards.daily_node_metrics);
const failureRateAvg = Math.round((latestNodeRewards.rewards_computation.failure_rate) * 100)
const rewardsMultiplier = Math.round((latestNodeRewards.rewards_computation.rewards_multiplier) * 100);
const failureRateAvg = Math.round((latestNodeRewards.rewards_multiplier.failure_rate) * 100)
const rewardsMultiplier = Math.round((latestNodeRewards.rewards_multiplier.rewards_multiplier) * 100);
const rewardsReduction = 100 - rewardsMultiplier;
const rows: GridRowsProp = latestNodeRewards.rewards_computation.computation_log.map((data, index) => {
const rows: GridRowsProp = latestNodeRewards.rewards_multiplier.computation_log.map((data, index) => {
return {
id: index,
col0: index,
Expand All @@ -55,7 +55,7 @@ export const NodeRewardsChart: React.FC<NodeRewardsChartProps> = ({ node }) => {
return (
<>
<Grid item xs={12} md={6}>
<NodeMetricsStats stats={latestNodeRewards.rewards_computation} />
<NodeMetricsStats stats={latestNodeRewards.rewards_multiplier} />
</Grid>
<Grid item xs={12} md={6}>
<NodePerformanceStats
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import { SxProps, Theme } from '@mui/material';
import { NodeRewards } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';
import { NodeRewardsMultiplier } from '../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did';

interface RewardTableProps {
nodeRewards: NodeRewards[],
nodeRewards: NodeRewardsMultiplier[],
sx?: SxProps<Theme>;
}

Expand All @@ -35,7 +35,7 @@ const RewardTable: React.FC<RewardTableProps> = ({ nodeRewards }) => {
{nodeMetrics.node_id.toText()}
</TableCell>
<TableCell component="th" scope="row">
{nodeMetrics.rewards_computation.failure_rate * 100}%
{nodeMetrics.rewards_multiplier.failure_rate * 100}%
</TableCell>
</TableRow>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { Principal } from "@dfinity/principal";
import { trustworthy_node_metrics } from "../../../declarations/trustworthy-node-metrics";
import { DailyNodeMetrics, NodeRewardsArgs, NodeRewards, NodeProviderRewardsArgs, NodeProviderRewards } from "../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did";
import { DailyNodeMetrics, NodeRewardsArgs, NodeRewardsMultiplier, NodeProviderRewardsArgs, NodeProviderRewards } from "../../../declarations/trustworthy-node-metrics/trustworthy-node-metrics.did";
import { PeriodFilter } from "../components/FilterBar";
import { Box, CircularProgress } from "@mui/material";
import { WidgetNumber } from '../components/Widgets';
Expand Down Expand Up @@ -130,7 +130,7 @@ export const getLatestRewardRange = () => {
export const setNodeRewardsData = async (
periodFilter: PeriodFilter,
node_id: Principal,
setNodeRewards: React.Dispatch<React.SetStateAction<NodeRewards | null>>,
setNodeRewards: React.Dispatch<React.SetStateAction<NodeRewardsMultiplier | null>>,
setIsLoading: React.Dispatch<React.SetStateAction<boolean>>) => {
try {
setIsLoading(true);
Expand Down Expand Up @@ -186,7 +186,7 @@ export const LoadingIndicator: React.FC = () => (
</Box>
);

export const NodeMetricsStats: React.FC<{ stats: NodeRewards['rewards_computation'] | null }> = ({ stats }) => (
export const NodeMetricsStats: React.FC<{ stats: NodeRewardsMultiplier['rewards_multiplier'] | null }> = ({ stats }) => (
<Box sx={boxStyleWidget('left')}>
<WidgetNumber value={stats ? stats.days_assigned.toString() : "0"} title="Days Assigned" />
<WidgetNumber value={stats ? stats.days_unassigned.toString() : "0"} title="Days Unassigned" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,30 @@ impl Storable for MonthlyNodeProviderRewardsStored {
};
}

#[derive(Debug, Deserialize, Serialize, CandidType, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct NodeProviderRewardableKey {
pub node_provider_id: Principal,
pub region: String,
pub node_type: String,
}

const MAX_VALUE_SIZE_REWARDABLE_NODES: u32 = 300;

impl Storable for NodeProviderRewardableKey {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
Cow::Owned(Encode!(self).unwrap())
}

fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
Decode!(bytes.as_ref(), Self).unwrap()
}

const BOUND: Bound = Bound::Bounded {
max_size: MAX_VALUE_SIZE_REWARDABLE_NODES,
is_fixed_size: false,
};
}

#[derive(Debug, Deserialize, Serialize, CandidType, Clone)]
pub struct NodeMetricsStored {
pub subnet_assigned: Principal,
Expand Down Expand Up @@ -68,7 +92,7 @@ pub struct NodeRewardRatesStored {
pub rewards_rates: NodeRewardRates,
}

const MAX_VALUE_SIZE_BYTES_REWARD_RATES: u32 = 133;
const MAX_VALUE_SIZE_BYTES_REWARD_RATES: u32 = 200;

impl Storable for NodeRewardRatesStored {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
Expand Down Expand Up @@ -116,7 +140,7 @@ pub struct NodeMetadataStoredV2 {
pub node_type: String,
}

const MAX_VALUE_SIZE_BYTES_NODE_METADATA: u32 = 204;
const MAX_VALUE_SIZE_BYTES_NODE_METADATA: u32 = 400;

impl Storable for NodeMetadataStoredV2 {
fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
Expand Down Expand Up @@ -212,7 +236,7 @@ impl DailyNodeMetrics {
}

#[derive(Debug, Deserialize, CandidType)]
pub struct RewardMultiplierResult {
pub struct RewardsMultiplier {
pub days_assigned: u64,
pub days_unassigned: u64,
pub rewards_multiplier: f64,
Expand All @@ -225,11 +249,17 @@ pub struct RewardMultiplierResult {
}

#[derive(Debug, Deserialize, CandidType)]
pub struct NodeRewards {
pub struct NodeRewardsMultiplier {
pub node_id: Principal,
pub daily_node_metrics: Vec<DailyNodeMetrics>,
pub node_rate: NodeRewardRate,
pub rewards_computation: RewardMultiplierResult,
pub rewards_multiplier: RewardsMultiplier,
}

pub struct NodeProviderRewardsComputation {
pub rewards_xdr: u64,
pub rewards_xdr_no_reduction: u64,
pub computation_log: Vec<OperationExecutorLog>,
}

#[derive(Debug, Deserialize, CandidType)]
Expand All @@ -240,7 +270,8 @@ pub struct NodeProviderRewards {
pub rewards_xdr_old: Option<u64>,
pub ts_distribution: u64,
pub xdr_conversion_rate: Option<u64>,
pub nodes_rewards: Vec<NodeRewards>,
pub nodes_rewards: Vec<NodeRewardsMultiplier>,
pub computation_log: Vec<OperationExecutorLog>,
}

#[derive(Debug, Deserialize, CandidType)]
Expand Down
Loading
Loading