Skip to content

Commit

Permalink
refactor price handling/usage
Browse files Browse the repository at this point in the history
  • Loading branch information
andymck committed Dec 2, 2024
1 parent 9140e49 commit 0efc7dc
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 63 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions iot_verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ futures = { workspace = true }
futures-util = { workspace = true }
prost = { workspace = true }
chrono = { workspace = true }
helium-lib = { workspace = true }
helium-proto = { workspace = true }
helium-crypto = { workspace = true, features = ["sqlx-postgres"] }
async-trait = { workspace = true }
Expand Down
35 changes: 19 additions & 16 deletions iot_verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,29 @@ pub mod telemetry;
pub mod tx_scaler;
pub mod witness_updater;

use rust_decimal::{prelude::ToPrimitive, Decimal};
use rust_decimal_macros::dec;
use rust_decimal::Decimal;
pub use settings::Settings;

pub const IOT_SUB_DAO_ONCHAIN_ADDRESS: &str = "Gm9xDCJawDEKDrrQW6haw94gABaYzQwCq4ZQU8h8bd22";

pub struct PriceConverter;

impl PriceConverter {
pub fn hnt_bones_to_pricer_format(hnt_bone_price: Decimal) -> u64 {
(hnt_bone_price * dec!(1_0000_0000) * dec!(1_0000_0000))
.to_u64()
.unwrap_or_default()
}

pub fn pricer_format_to_hnt_bones(hnt_price: u64) -> Decimal {
Decimal::from(hnt_price) / dec!(1_0000_0000) / dec!(1_0000_0000)
}
#[derive(Clone, Debug)]
pub struct HntPrice {
pub hnt_price_in_bones: u64,
pub hnt_price: Decimal,
pub price_per_hnt_bone: Decimal,
pub decimals: u8,
}

pub fn pricer_format_to_hnt(hnt_price: u64) -> Decimal {
Decimal::from(hnt_price) / dec!(1_0000_0000)
impl HntPrice {
pub fn new(hnt_price_in_bones: u64, decimals: u8) -> Self {
let hnt_price =
Decimal::from(hnt_price_in_bones) / Decimal::from(10_u64.pow(decimals as u32));
let price_per_hnt_bone = hnt_price / Decimal::from(10_u64.pow(decimals as u32));
Self {
hnt_price_in_bones,
hnt_price,
price_per_hnt_bone,
decimals,
}
}
}
65 changes: 33 additions & 32 deletions iot_verifier/src/reward_share.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::poc_report::ReportType as PocReportType;
use crate::{poc_report::ReportType as PocReportType, HntPrice};
use chrono::{DateTime, Utc};
use file_store::{iot_packet::IotValidPacket, iot_valid_poc::IotPoc, traits::TimestampEncode};
use futures::stream::TryStreamExt;
Expand Down Expand Up @@ -274,7 +274,7 @@ impl GatewayShares {
pub async fn calculate_rewards_per_share(
&self,
reward_info: &ResolvedSubDaoEpochRewardInfo,
hnt_bone_price: Decimal,
hnt_price: HntPrice,
) -> anyhow::Result<(Decimal, Decimal, Decimal)> {
// the total number of shares for beacons, witnesses and data transfer
// dc shares here is the sum of all spent data transfer DC this epoch
Expand All @@ -288,7 +288,8 @@ impl GatewayShares {
// up to a max cap of total_dc_transfer_rewards
// if the dc transfer rewards is less than total_dc_transfer_rewards
// then the remainer will be added to the POC rewards allocation
let total_dc_transfer_rewards_used = dc_to_hnt_bones(total_dc_shares, hnt_bone_price);
let total_dc_transfer_rewards_used =
dc_to_hnt_bones(total_dc_shares, hnt_price.price_per_hnt_bone);
let (dc_transfer_rewards_unused, total_dc_transfer_rewards_capped) =
normalize_dc_transfer_rewards(
total_dc_transfer_rewards_used,
Expand Down Expand Up @@ -428,7 +429,7 @@ async fn aggregate_dc_shares(
#[cfg(test)]
mod test {
use super::*;
use crate::{reward_share, PriceConverter};
use crate::{reward_share, HntPrice};
use chrono::Duration;

pub const EPOCH_ADDRESS: &str = "112E7TxoNHV46M6tiPA8N1MkeMeQxc9ztb4JQLXBVAAUfq1kJLoF";
Expand Down Expand Up @@ -519,7 +520,7 @@ mod test {
// total epoch dc rewards amount
// this results in a significant redistribution of dc rewards to POC
async fn test_reward_share_calculation_fixed_dc_spend_with_transfer_distribution() {
let hnt_bone_price = dec!(0.000000000359);
let hnt_price = HntPrice::new(3590000, 8);

let gw1: PublicKeyBinary = "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6"
.parse()
Expand Down Expand Up @@ -558,7 +559,8 @@ mod test {
let total_dc_spend =
gw1_dc_spend + gw2_dc_spend + gw3_dc_spend + gw4_dc_spend + gw5_dc_spend + gw6_dc_spend;
println!("total dc spend: {total_dc_spend}");
let total_used_data_transfer_tokens = dc_to_hnt_bones(total_dc_spend, hnt_bone_price);
let total_used_data_transfer_tokens =
dc_to_hnt_bones(total_dc_spend, hnt_price.price_per_hnt_bone);
println!("total data transfer rewards for dc spent: {total_used_data_transfer_tokens}");
let total_unused_data_transfer_tokens =
total_data_transfer_tokens_for_period - total_used_data_transfer_tokens;
Expand Down Expand Up @@ -593,7 +595,7 @@ mod test {
let gw_shares = GatewayShares::new(shares).unwrap();
let (beacon_rewards_per_share, witness_rewards_per_share, dc_transfer_rewards_per_share) =
gw_shares
.calculate_rewards_per_share(&reward_info, hnt_bone_price)
.calculate_rewards_per_share(&reward_info, hnt_price.clone())
.await
.unwrap();

Expand Down Expand Up @@ -659,23 +661,23 @@ mod test {

// assert the expected data transfer rewards amounts per gateway
// using the dc_to_hnt_bones helper function
let gw1_expected_dc_rewards = dc_to_hnt_bones(gw1_dc_spend, hnt_bone_price)
let gw1_expected_dc_rewards = dc_to_hnt_bones(gw1_dc_spend, hnt_price.price_per_hnt_bone)
.to_u64()
.unwrap();
assert_eq!(gw1_expected_dc_rewards.to_u64().unwrap(), 13_983_286);
let gw2_expected_dc_rewards = dc_to_hnt_bones(gw2_dc_spend, hnt_bone_price)
let gw2_expected_dc_rewards = dc_to_hnt_bones(gw2_dc_spend, hnt_price.price_per_hnt_bone)
.to_u64()
.unwrap();
assert_eq!(gw2_expected_dc_rewards.to_u64().unwrap(), 139_275_766);
let gw3_expected_dc_rewards = dc_to_hnt_bones(gw3_dc_spend, hnt_bone_price)
let gw3_expected_dc_rewards = dc_to_hnt_bones(gw3_dc_spend, hnt_price.price_per_hnt_bone)
.to_u64()
.unwrap();
assert_eq!(gw3_expected_dc_rewards.to_u64().unwrap(), 139_275_766);
let gw5_expected_dc_rewards = dc_to_hnt_bones(gw5_dc_spend, hnt_bone_price)
let gw5_expected_dc_rewards = dc_to_hnt_bones(gw5_dc_spend, hnt_price.price_per_hnt_bone)
.to_u64()
.unwrap();
assert_eq!(gw5_expected_dc_rewards.to_u64().unwrap(), 0);
let gw6_expected_dc_rewards = dc_to_hnt_bones(gw6_dc_spend, hnt_bone_price)
let gw6_expected_dc_rewards = dc_to_hnt_bones(gw6_dc_spend, hnt_price.price_per_hnt_bone)
.to_u64()
.unwrap();
assert_eq!(gw6_expected_dc_rewards.to_u64().unwrap(), 1_392_757_660);
Expand Down Expand Up @@ -732,7 +734,8 @@ mod test {
#[tokio::test]
// test reward distribution where there is zero transfer of dc rewards to poc
async fn test_reward_share_calculation_without_data_transfer_distribution() {
let hnt_bone_price = dec!(0.000000000359);
let hnt_price = HntPrice::new(3590000, 8);

let gw1: PublicKeyBinary = "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6"
.parse()
.expect("failed gw1 parse");
Expand Down Expand Up @@ -762,8 +765,10 @@ mod test {
// distribute this amount of dc across the gateways
// this results in zero unallocated dc rewards being
// available to distributed to POC
let total_dc_to_spend =
hnt_bones_to_dc(total_data_transfer_tokens_for_period, hnt_bone_price);
let total_dc_to_spend = hnt_bones_to_dc(
total_data_transfer_tokens_for_period,
hnt_price.price_per_hnt_bone,
);
println!("total dc value of scheduled data transfer tokens: {total_dc_to_spend}");

// generate the rewards map
Expand Down Expand Up @@ -810,7 +815,7 @@ mod test {
let gw_shares = GatewayShares::new(shares).unwrap();
let (beacon_rewards_per_share, witness_rewards_per_share, dc_transfer_rewards_per_share) =
gw_shares
.calculate_rewards_per_share(&reward_info, hnt_bone_price)
.calculate_rewards_per_share(&reward_info, hnt_price)
.await
.unwrap();

Expand Down Expand Up @@ -928,7 +933,8 @@ mod test {
#[tokio::test]
// test reward distribution where there is transfer of dc rewards to poc
async fn test_reward_share_calculation_with_data_transfer_distribution() {
let hnt_bone_price = dec!(0.000000000359);
let hnt_price = HntPrice::new(3590000, 8);

let gw1: PublicKeyBinary = "112NqN2WWMwtK29PMzRby62fDydBJfsCLkCAf392stdok48ovNT6"
.parse()
.expect("failed gw1 parse");
Expand Down Expand Up @@ -956,8 +962,10 @@ mod test {
// get the expected total amount of dc we need to spend
// spread *some* of this across the gateways and then confirm
// the unallocated rewards go to poc
let total_dc_to_spend =
hnt_bones_to_dc(total_data_transfer_tokens_for_period, hnt_bone_price);
let total_dc_to_spend = hnt_bones_to_dc(
total_data_transfer_tokens_for_period,
hnt_price.price_per_hnt_bone,
);
println!("total_dc_to_spend: {total_dc_to_spend}");

// generate the rewards map
Expand Down Expand Up @@ -998,7 +1006,7 @@ mod test {
let gw_shares = GatewayShares::new(shares).unwrap();
let (beacon_rewards_per_share, witness_rewards_per_share, dc_transfer_rewards_per_share) =
gw_shares
.calculate_rewards_per_share(&reward_info, hnt_bone_price)
.calculate_rewards_per_share(&reward_info, hnt_price)
.await
.unwrap();

Expand Down Expand Up @@ -1133,18 +1141,11 @@ mod test {
let hnt_dollar_price = dec!(1.0);
let hnt_dollar_bone_price = dec!(0.00000001);
let hnt_price_from_pricer = 100000000_u64;
let pricer_decimals = 8;
let hnt_price = HntPrice::new(hnt_price_from_pricer, pricer_decimals);

assert_eq!(
hnt_dollar_bone_price,
PriceConverter::pricer_format_to_hnt_bones(hnt_price_from_pricer)
);
assert_eq!(
hnt_price_from_pricer,
PriceConverter::hnt_bones_to_pricer_format(hnt_dollar_bone_price)
);
assert_eq!(
hnt_dollar_price,
PriceConverter::pricer_format_to_hnt(hnt_price_from_pricer)
);
assert_eq!(hnt_dollar_bone_price, hnt_price.price_per_hnt_bone);
assert_eq!(hnt_price_from_pricer, hnt_price.hnt_price_in_bones);
assert_eq!(hnt_dollar_price, hnt_price.hnt_price);
}
}
22 changes: 14 additions & 8 deletions iot_verifier/src/rewarder.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::{
reward_share::{self, GatewayShares},
telemetry, PriceConverter, IOT_SUB_DAO_ONCHAIN_ADDRESS,
telemetry, HntPrice, IOT_SUB_DAO_ONCHAIN_ADDRESS,
};
use chrono::{DateTime, TimeZone, Utc};
use db_store::meta;
use file_store::{file_sink, traits::TimestampEncode};
use futures::future::LocalBoxFuture;
use helium_lib::token::Token;
use helium_proto::{
reward_manifest::RewardData::IotRewardData,
services::poc_lora::{
Expand Down Expand Up @@ -143,19 +144,24 @@ where
.price(&helium_proto::BlockchainTokenTypeV1::Hnt)
.await?;

let hnt_bone_price = PriceConverter::pricer_format_to_hnt_bones(hnt_price);
let hnt_price = HntPrice::new(hnt_price, Token::Hnt.decimals());

tracing::info!(
"Rewarding for epoch {} period: {} to {} with hnt bone price: {}",
reward_info.epoch,
reward_info.epoch_period.start,
reward_info.epoch_period.end,
hnt_bone_price
hnt_price.price_per_hnt_bone
);

// process rewards for poc and dc
let poc_dc_shares =
reward_poc_and_dc(&self.pool, &self.rewards_sink, &reward_info, hnt_bone_price).await?;
let poc_dc_shares = reward_poc_and_dc(
&self.pool,
&self.rewards_sink,
&reward_info,
hnt_price.clone(),
)
.await?;

// process rewards for the operational fund

Expand Down Expand Up @@ -197,7 +203,7 @@ where
written_files,
reward_data: Some(IotRewardData(reward_data)),
epoch: reward_info.epoch,
price: hnt_price,
price: hnt_price.hnt_price_in_bones,
},
[],
)
Expand Down Expand Up @@ -256,14 +262,14 @@ pub async fn reward_poc_and_dc(
pool: &Pool<Postgres>,
rewards_sink: &file_sink::FileSinkClient<proto::IotRewardShare>,
reward_info: &ResolvedSubDaoEpochRewardInfo,
hnt_bone_price: Decimal,
hnt_price: HntPrice,
) -> anyhow::Result<RewardPocDcDataPoints> {
let reward_shares =
reward_share::aggregate_reward_shares(pool, &reward_info.epoch_period).await?;
let gateway_shares = GatewayShares::new(reward_shares)?;
let (beacon_rewards_per_share, witness_rewards_per_share, dc_transfer_rewards_per_share) =
gateway_shares
.calculate_rewards_per_share(reward_info, hnt_bone_price)
.calculate_rewards_per_share(reward_info, hnt_price)
.await?;

// get the total poc and dc rewards for the period
Expand Down
11 changes: 4 additions & 7 deletions iot_verifier/tests/integrations/rewarder_poc_dc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use helium_proto::services::poc_lora::{
use iot_verifier::{
poc_report::ReportType,
reward_share::{self, GatewayDCShare, GatewayPocShare},
rewarder,
rewarder, HntPrice,
};
use prost::Message;
use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy};
Expand All @@ -26,6 +26,8 @@ async fn test_poc_and_dc_rewards(pool: PgPool) -> anyhow::Result<()> {

let reward_info = default_rewards_info(89_041_095_890_411, Duration::hours(24));

let hnt_price = HntPrice::new(1, 8);

// seed all the things
let mut txn = pool.clone().begin().await?;
seed_pocs(reward_info.epoch_period.start, &mut txn).await?;
Expand All @@ -34,12 +36,7 @@ async fn test_poc_and_dc_rewards(pool: PgPool) -> anyhow::Result<()> {

// run rewards for poc and dc
let (_, rewards) = tokio::join!(
rewarder::reward_poc_and_dc(
&pool,
&iot_rewards_client,
&reward_info,
dec!(0.0000000000000001)
),
rewarder::reward_poc_and_dc(&pool, &iot_rewards_client, &reward_info, hnt_price),
receive_expected_rewards(&mut iot_rewards)
);
if let Ok((gateway_rewards, unallocated_poc_reward)) = rewards {
Expand Down

0 comments on commit 0efc7dc

Please sign in to comment.