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

Baseline bug fix (FIP-0081) #1557

Merged
merged 27 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
102ee19
initial updates to integrate baseline bug fix
kkarrancsu Jun 21, 2024
7b582ad
Merge branch 'filecoin-project:master' into fip0081
kkarrancsu Jun 24, 2024
6da9f6a
moving ramp duration to be returned by call to total_power rather tha…
kkarrancsu Jun 24, 2024
e76fd96
updated to use correct variable name
kkarrancsu Jun 25, 2024
ad45428
changing type to i64 so that the ramp can be preplanned
kkarrancsu Jun 25, 2024
f4f11cd
updated state to store epoch at which upgrade will happen, and the du…
kkarrancsu Jun 25, 2024
63b7ecd
fixed syntax and type errors indicated by running make check
kkarrancsu Jun 26, 2024
adf43f0
Merge branch 'master' into fip0081
kkarrancsu Jun 26, 2024
6825568
fixed make check errors
kkarrancsu Jun 26, 2024
2d245ca
fixing rustfmt errors
kkarrancsu Jul 2, 2024
60d63d3
fixing additional rustfmt errors
kkarrancsu Jul 2, 2024
c19558e
fixed integration test errors
kkarrancsu Jul 5, 2024
1bd140e
Merge branch 'master' into fip0081
lemmih Sep 9, 2024
a140b68
write three tests, fix bug in pledge calculation
lemmih Sep 9, 2024
22cc3c8
remove unnecessary TODO items
lemmih Sep 10, 2024
330ef9d
remote yet another unnecessary TODO item
lemmih Sep 20, 2024
06ff09c
document the two new fields in power state
lemmih Sep 20, 2024
bceeb2c
test pre-ramp, test early on-ramp
lemmih Sep 20, 2024
417f9e1
check first step on ramp
lemmih Sep 20, 2024
37c442d
use if-then-else rather than min/max
lemmih Sep 20, 2024
252a358
avoid a division when merging baseline and simple pledges
lemmih Sep 24, 2024
8c969b4
validate pledges 1 epoch around FIP0081 activation
lemmih Sep 24, 2024
e1836c0
rename gamme constant, reword comments
lemmih Sep 24, 2024
7f09f47
test more FIP0081 cases
lemmih Sep 24, 2024
1611afe
move tests to separate file
lemmih Sep 25, 2024
5c361a5
Merge branch 'master' into fip0081
lemmih Sep 25, 2024
c5b2658
correct test comments
lemmih Sep 26, 2024
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
2 changes: 2 additions & 0 deletions actors/market/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,7 @@ pub mod power {
pub quality_adj_power: StoragePower,
pub pledge_collateral: TokenAmount,
pub quality_adj_power_smoothed: FilterEstimate,
pub ramp_start_epoch: i64,
pub ramp_duration_epochs: u64,
}
}
2 changes: 2 additions & 0 deletions actors/market/tests/harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,6 +1150,8 @@ pub fn expect_query_network_info(rt: &MockRuntime) {
quality_adj_power: power.clone(),
pledge_collateral: TokenAmount::default(),
quality_adj_power_smoothed: FilterEstimate::new(reward.atto().clone(), BigInt::zero()),
ramp_start_epoch: 0,
ramp_duration_epochs: 0,
};
let current_reward = ThisEpochRewardReturn {
this_epoch_baseline_power: power,
Expand Down
2 changes: 2 additions & 0 deletions actors/miner/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ pub mod power {
pub quality_adj_power: StoragePower,
pub pledge_collateral: TokenAmount,
pub quality_adj_power_smoothed: FilterEstimate,
pub ramp_start_epoch: i64,
pub ramp_duration_epochs: u64,
}
#[derive(Serialize_tuple, Deserialize_tuple)]
pub struct EnrollCronEventParams {
Expand Down
18 changes: 18 additions & 0 deletions actors/miner/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,8 @@ impl Actor {
network_baseline: rew.this_epoch_baseline_power,
circulating_supply,
epoch_reward: rew.this_epoch_reward_smoothed,
epochs_since_ramp_start: rt.curr_epoch() - pwr.ramp_start_epoch,
ramp_duration_epochs: pwr.ramp_duration_epochs,
};

/*
Expand Down Expand Up @@ -1908,6 +1910,8 @@ impl Actor {
network_baseline: rew.this_epoch_baseline_power,
circulating_supply,
epoch_reward: rew.this_epoch_reward_smoothed,
epochs_since_ramp_start: rt.curr_epoch() - pwr.ramp_start_epoch,
ramp_duration_epochs: pwr.ramp_duration_epochs,
};
activate_new_sector_infos(
rt,
Expand Down Expand Up @@ -1981,6 +1985,8 @@ impl Actor {
network_baseline: params.reward_baseline_power,
circulating_supply: rt.total_fil_circ_supply(),
epoch_reward: params.reward_smoothed,
epochs_since_ramp_start: 0,
ramp_duration_epochs: 0,
};
activate_new_sector_infos(
rt,
Expand Down Expand Up @@ -2093,6 +2099,8 @@ impl Actor {
network_baseline: rew.this_epoch_baseline_power,
circulating_supply,
epoch_reward: rew.this_epoch_reward_smoothed,
epochs_since_ramp_start: rt.curr_epoch() - pwr.ramp_start_epoch,
ramp_duration_epochs: pwr.ramp_duration_epochs,
};

let sector_day_reward = expected_reward_for_power(
Expand All @@ -2115,6 +2123,8 @@ impl Actor {
&pledge_inputs.epoch_reward,
&pledge_inputs.network_qap,
&pledge_inputs.circulating_supply,
pledge_inputs.epochs_since_ramp_start,
pledge_inputs.ramp_duration_epochs,
);

let sectors_to_add = valid_sectors
Expand Down Expand Up @@ -4116,6 +4126,8 @@ where
network_baseline: rew.this_epoch_baseline_power,
circulating_supply,
epoch_reward: rew.this_epoch_reward_smoothed,
epochs_since_ramp_start: rt.curr_epoch() - pow.ramp_start_epoch,
ramp_duration_epochs: pow.ramp_duration_epochs,
};
let mut power_delta = PowerPair::zero();
let mut pledge_delta = TokenAmount::zero();
Expand Down Expand Up @@ -4307,6 +4319,8 @@ fn update_existing_sector_info(
&pledge_inputs.epoch_reward,
&pledge_inputs.network_qap,
&pledge_inputs.circulating_supply,
pledge_inputs.epochs_since_ramp_start,
pledge_inputs.ramp_duration_epochs,
),
);
new_sector_info
Expand Down Expand Up @@ -5546,6 +5560,8 @@ fn activate_new_sector_infos(
&pledge_inputs.epoch_reward,
&pledge_inputs.network_qap,
&pledge_inputs.circulating_supply,
pledge_inputs.epochs_since_ramp_start,
pledge_inputs.ramp_duration_epochs,
);

deposit_to_unlock += pci.pre_commit_deposit.clone();
Expand Down Expand Up @@ -5958,6 +5974,8 @@ struct NetworkPledgeInputs {
pub network_baseline: StoragePower,
pub circulating_supply: TokenAmount,
pub epoch_reward: FilterEstimate,
pub epochs_since_ramp_start: i64,
pub ramp_duration_epochs: u64,
}

// Note: probably better to push this one level down into state
Expand Down
40 changes: 37 additions & 3 deletions actors/miner/src/monies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub const TERMINATION_LIFETIME_CAP: ChainEpoch = 140;
// Multiplier of whole per-winner rewards for a consensus fault penalty.
const CONSENSUS_FAULT_FACTOR: u64 = 5;

const FIXED_POINT_FACTOR: u64 = 1000; // 3 decimal places
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: there's lots of other fixed point calculations floating around actors code so change this to something more precise either ONE_THOUSAND or GAMMA_FIXED_POINT_FACTOR

Copy link
Contributor

Choose a reason for hiding this comment

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

Done.


/// The projected block reward a sector would earn over some period.
/// Also known as "BR(t)".
/// BR(t) = ProjectedRewardFraction(t) * SectorQualityAdjustedPower
Expand Down Expand Up @@ -255,6 +257,8 @@ pub fn initial_pledge_for_power(
reward_estimate: &FilterEstimate,
network_qa_power_estimate: &FilterEstimate,
circulating_supply: &TokenAmount,
epochs_since_ramp_start: i64,
ramp_duration_epochs: u64,
) -> TokenAmount {
let ip_base = expected_reward_for_power_clamped_at_atto_fil(
reward_estimate,
Expand All @@ -267,10 +271,40 @@ pub fn initial_pledge_for_power(
let lock_target_denom = LOCK_TARGET_FACTOR_DENOM;
let pledge_share_num = qa_power;
let network_qa_power = network_qa_power_estimate.estimate();
let pledge_share_denom = cmp::max(cmp::max(&network_qa_power, baseline_power), qa_power);

anorth marked this conversation as resolved.
Show resolved Hide resolved
// Once FIP-0081 has fully activated, additional pledge will be 70% baseline
// pledge + 30% simple pledge.
const FIP_0081_ACTIVATION_PERMILLE: i64 = 300;
// Gamma is the share of pledge coming from the baseline formulation, with 1-gamma coming from simple pledge.
// Gamma ranges from 1.0 before the ramp to 0.7 at the end.
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
// gamma = 1000 - 300 * (epochs_since_ramp_start / ramp_duration_epochs).max(0).min(1)
let skew = if epochs_since_ramp_start < 0 {
// No skew before ramp start
0
} else if ramp_duration_epochs == 0 || epochs_since_ramp_start >= ramp_duration_epochs as i64 {
// 100% skew after ramp end
FIP_0081_ACTIVATION_PERMILLE as u64
} else {
((epochs_since_ramp_start * FIP_0081_ACTIVATION_PERMILLE) / ramp_duration_epochs as i64)
as u64
};
let gamma = FIXED_POINT_FACTOR - skew;

let additional_ip_num = lock_target_num * pledge_share_num;
let additional_ip_denom = pledge_share_denom * lock_target_denom;
let additional_ip = additional_ip_num.div_floor(&additional_ip_denom);

let pledge_share_denom_baseline =
cmp::max(cmp::max(&network_qa_power, baseline_power), qa_power);
let pledge_share_denom_simple = cmp::max(&network_qa_power, qa_power);

let additional_ip_denom_baseline = pledge_share_denom_baseline * lock_target_denom;
let additional_ip_baseline = additional_ip_num.div_floor(&additional_ip_denom_baseline);
let additional_ip_denom_simple = pledge_share_denom_simple * lock_target_denom;
let additional_ip_simple = additional_ip_num.div_floor(&additional_ip_denom_simple);

// convex combination of simple and baseline pledge
let additional_ip = (additional_ip_baseline * gamma
+ additional_ip_simple * (FIXED_POINT_FACTOR - gamma))
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
/ FIXED_POINT_FACTOR;
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved

let nominal_pledge = ip_base + TokenAmount::from_atto(additional_ip);
let pledge_cap = TokenAmount::from_atto(INITIAL_PLEDGE_MAX_PER_BYTE.atto() * qa_power);
Expand Down
3 changes: 3 additions & 0 deletions actors/miner/tests/aggregate_prove_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ fn valid_precommits_then_aggregate_provecommit() {
&actor.epoch_reward_smooth,
&actor.epoch_qa_power_smooth,
&rt.total_fil_circ_supply(),
// NOTE: zero inputs here preserve the original pledge functionality
0,
0,
);
let ten_sectors_initial_pledge = BigInt::from(10i32) * expected_initial_pledge.clone();
assert_eq!(ten_sectors_initial_pledge, st.initial_pledge);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,122 @@ fn initial_pledge_clamped_at_one_attofil() {
&reward_estimate(),
&power_estimate(),
&circulating_supply(),
// NOTE: setting this to zero preserves the original pledge definition (before baseline bug fix)
ZenGround0 marked this conversation as resolved.
Show resolved Hide resolved
// so these inputs configure the function to return the original pledge
0,
0,
);
assert_eq!(TokenAmount::from_atto(1), initial_pledge);
}

// Pre-ramp where 'baseline power' dominates
#[test]
fn initial_pledge_pre_ramp_negative() {
let initial_pledge = initial_pledge_for_power(
&qa_sector_power(),
&StoragePower::from(1u64 << 37),
&reward_estimate(),
&power_estimate(),
&TokenAmount::from_whole(1),
-100,
100,
);
assert_eq!(
TokenAmount::from_atto(1) + TokenAmount::from_whole(1500).div_floor(10000),
initial_pledge
);
}

// Pre-ramp where 'baseline power' dominates
#[test]
fn initial_pledge_pre_ramp() {
let initial_pledge = initial_pledge_for_power(
&qa_sector_power(),
&StoragePower::from(1u64 << 37),
&reward_estimate(),
&power_estimate(),
&TokenAmount::from_whole(1),
Copy link
Member

Choose a reason for hiding this comment

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

I found these a bit hard to mentally verify. Could you please expand them, e.g. by using named vals for the parameters, and spell out in words the expected result. I suggest not using the consts defined for the "clamping" test – mixing and matching those with inline values couples the tests together and makes it hard to trace what the values are intended for. You can probably combine all these new cases into a single test for re-use.

E.g. "With a fixed reward of zero and circulating supply of 1, a sector contributing 1/2 the current network QAP requires pledge of 1/2 * 30% of circulating supply => 15% of one token."

Please add tests for:

  • Another point part-way through the ramp, to demonstrate it's really linear
  • Before the ramp start, i.e. epochs_since_ramp_start is negative
  • Ramp start and duration of zero (i.e. no ramp for all new networks)
  • Ramp duration of zero, and the epochs -1, 0, and 1 around that ramp (i.e. step change)

Copy link
Contributor

Choose a reason for hiding this comment

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

I only have a rudimentary understanding of the reasons behind pledges and FIP0081. My understanding is limited to this: We previously used equation A to calculate initial pledges, and now we're using 70% A + 30% B. If you want a more detailed explanation of the rules, we'll have to pull in one of the authors of FIP0081.

Another point part-way through the ramp, to demonstrate it's really linear

Done.

Before the ramp start, i.e. epochs_since_ramp_start is negative

Done.

Ramp start and duration of zero (i.e. no ramp for all new networks)

Already there.

Ramp duration of zero, and the epochs -1, 0, and 1 around that ramp (i.e. step change)

Done.

Copy link
Member

Choose a reason for hiding this comment

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

Ramp start and duration of zero (i.e. no ramp for all new networks)

The existing test is not exercising the pledge calculation properly, it's just testing a boundary condition where there is zero reward and supply. Please add a test for (ramp, duration) = (0, 0) and with reward/supply inputs similar to the other tests you added.

Ramp duration of zero, and the epochs -1, 0, and 1 around that ramp (i.e. step change)

I don't see this test added. To be concrete, I'm looking to exercise where epochs_since_ramp start differs by zero and +/- 1 from the ramp duration.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ramp start and duration of zero (i.e. no ramp for all new networks)

The existing test is not exercising the pledge calculation properly, it's just testing a boundary condition where there is zero reward and supply. Please add a test for (ramp, duration) = (0, 0) and with reward/supply inputs similar to the other tests you added.

Done.

Ramp duration of zero, and the epochs -1, 0, and 1 around that ramp (i.e. step change)

I don't see this test added. To be concrete, I'm looking to exercise where epochs_since_ramp start differs by zero and +/- 1 from the ramp duration.

Added a test with epochs_since_ramp={-1,0,1}.

0,
100,
);
assert_eq!(
TokenAmount::from_atto(1) + TokenAmount::from_whole(1500).div_floor(10000),
initial_pledge
);
}

// On-ramp where 'baseline power' (85%).
#[test]
fn initial_pledge_on_ramp_mid() {
let initial_pledge = initial_pledge_for_power(
&qa_sector_power(),
&StoragePower::from(1u64 << 37),
&reward_estimate(),
&power_estimate(),
&TokenAmount::from_whole(1),
50,
100,
);
assert_eq!(
TokenAmount::from_atto(1) + TokenAmount::from_whole(1725).div_floor(10000),
initial_pledge
);
}

// Post-ramp where 'baseline power' has reduced effect (97%).
#[test]
fn initial_pledge_on_ramp_early() {
let initial_pledge = initial_pledge_for_power(
&qa_sector_power(),
&StoragePower::from(1u64 << 37),
&reward_estimate(),
&power_estimate(),
&TokenAmount::from_whole(1),
10,
100,
);
assert_eq!(
TokenAmount::from_atto(1) + TokenAmount::from_whole(1545).div_floor(10000),
initial_pledge
);
}

// Post-ramp, first epoch, pledge should be 97% 'baseline' + 3% simple.
#[test]
fn initial_pledge_on_ramp_step() {
let initial_pledge = initial_pledge_for_power(
&qa_sector_power(),
&StoragePower::from(1u64 << 37),
&reward_estimate(),
&power_estimate(),
&TokenAmount::from_whole(1),
1,
10,
);
assert_eq!(
TokenAmount::from_atto(1) + TokenAmount::from_whole(1545).div_floor(10000),
initial_pledge
);
}

// Post-ramp where 'baseline power' has reduced effect (70%).
#[test]
fn initial_pledge_post_ramp() {
let initial_pledge = initial_pledge_for_power(
&qa_sector_power(),
&StoragePower::from(1u64 << 37),
&reward_estimate(),
&power_estimate(),
&TokenAmount::from_whole(1),
500,
100,
);
assert_eq!(
TokenAmount::from_atto(1) + TokenAmount::from_whole(1950).div_floor(10000),
initial_pledge
);
}

#[test]
fn precommit_deposit_is_clamped_at_one_attofil() {
let precommit_deposit =
Expand Down
4 changes: 4 additions & 0 deletions actors/miner/tests/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ impl ActorHarness {
quality_adj_power: self.network_qa_power.clone(),
pledge_collateral: self.network_pledge.clone(),
quality_adj_power_smoothed: self.epoch_qa_power_smooth.clone(),
ramp_start_epoch: 0,
ramp_duration_epochs: 0,
};
let current_reward = ThisEpochRewardReturn {
this_epoch_baseline_power: self.baseline_power.clone(),
Expand Down Expand Up @@ -2335,6 +2337,8 @@ impl ActorHarness {
&self.epoch_reward_smooth,
&self.epoch_qa_power_smooth,
&rt.total_fil_circ_supply(),
0,
0,
)
}

Expand Down
2 changes: 2 additions & 0 deletions actors/power/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ impl Actor {
quality_adj_power: st.this_epoch_quality_adj_power,
pledge_collateral: st.this_epoch_pledge_collateral,
quality_adj_power_smoothed: st.this_epoch_qa_power_smoothed,
ramp_start_epoch: st.ramp_start_epoch,
ramp_duration_epochs: st.ramp_duration_epochs,
})
}

Expand Down
9 changes: 9 additions & 0 deletions actors/power/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ pub struct State {
/// Number of miners having proven the minimum consensus power.
pub miner_above_min_power_count: i64,

/// FIP0081 changed pledge calculations, moving from ruleset A to ruleset B.
/// This change is spread over several epochs to avoid sharp jumps in pledge
/// amounts. At `ramp_start_epoch`, we use the old ruleset. At
/// `ramp_start_epoch + ramp_duration_epochs`, we use 70% old rules + 30%
/// new rules. See FIP0081 for more details.
pub ramp_start_epoch: i64,
/// Number of epochs over which the new pledge calculation is ramped up.
pub ramp_duration_epochs: u64,
anorth marked this conversation as resolved.
Show resolved Hide resolved

/// A queue of events to be triggered by cron, indexed by epoch.
pub cron_event_queue: Cid, // Multimap, (HAMT[ChainEpoch]AMT[CronEvent]

Expand Down
2 changes: 2 additions & 0 deletions actors/power/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ pub struct CurrentTotalPowerReturn {
pub quality_adj_power: StoragePower,
pub pledge_collateral: TokenAmount,
pub quality_adj_power_smoothed: FilterEstimate,
pub ramp_start_epoch: i64,
pub ramp_duration_epochs: u64,
}

#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone, Eq, PartialEq)]
Expand Down
Loading