-
Notifications
You must be signed in to change notification settings - Fork 200
feat: nv29 base #7599
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
feat: nv29 base #7599
Changes from all commits
39c6d2f
3f827f9
1c39046
7dbcbb5
389957f
09e2115
3bb81f3
7f7cf8a
1542331
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -444,6 +444,75 @@ async fn initial_pledge_collateral_matches_the_sector_pledge() { | |
| ); | ||
| } | ||
|
|
||
| /// Puts nv29 at [`FIXTURE_EPOCH`], because no network schedules Solstice yet. | ||
| fn solstice_at_fixture_epoch(mut config: ChainConfig) -> ChainConfig { | ||
| config | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you might want to sort this collection, just in case - the ordering there is important. |
||
| .height_infos | ||
| .get_mut(&Height::Solstice) | ||
| .expect("every network lists Solstice") | ||
| .epoch = FIXTURE_EPOCH; | ||
| config | ||
| } | ||
|
|
||
| async fn initial_pledge_collateral( | ||
| config: ChainConfig, | ||
| epoch: ChainEpoch, | ||
| ) -> Result<TokenAmount, ServerError> { | ||
| let (ctx, _) = ctx_at(config, epoch, &Default::default()); | ||
| // Default seal proof is `Invalid`, so that field has to be set; the rest go unread. | ||
| let pre_commit = SectorPreCommitInfo::from(fil_actor_miner_state::v18::SectorPreCommitInfo { | ||
| seal_proof: RegisteredSealProofV4::StackedDRG32GiBV1P1, | ||
| expiration: epoch + 1_000, | ||
| ..Default::default() | ||
| }); | ||
| StateMinerInitialPledgeCollateral::handle( | ||
| ctx, | ||
| (Address::new_id(1000), pre_commit, ApiTipsetKey(None)), | ||
| &Default::default(), | ||
| ) | ||
| .await | ||
| } | ||
|
|
||
| /// From NV29 a pre-commit no longer describes a pledge, so the collateral RPC refuses. | ||
| #[rstest] | ||
| #[case::mainnet(ChainConfig::mainnet())] | ||
| #[case::calibnet(ChainConfig::calibnet())] | ||
| #[case::butterflynet(ChainConfig::butterflynet())] | ||
| #[case::devnet(ChainConfig::devnet())] | ||
| #[tokio::test] | ||
| async fn initial_pledge_collateral_is_retired_from_nv29(#[case] config: ChainConfig) { | ||
| let config = solstice_at_fixture_epoch(config); | ||
| let activation = first_epoch_of(&config, Height::Solstice); | ||
|
|
||
| let error = initial_pledge_collateral(config, activation) | ||
| .await | ||
| .unwrap_err(); | ||
|
|
||
| assert!( | ||
| error | ||
| .to_string() | ||
| .contains("unsupported from network version 29"), | ||
| "{error}" | ||
| ); | ||
| } | ||
|
|
||
| #[rstest] | ||
| #[case::mainnet(ChainConfig::mainnet())] | ||
| #[case::calibnet(ChainConfig::calibnet())] | ||
| #[case::butterflynet(ChainConfig::butterflynet())] | ||
| #[case::devnet(ChainConfig::devnet())] | ||
| #[tokio::test] | ||
| async fn initial_pledge_collateral_answers_until_nv29(#[case] config: ChainConfig) { | ||
| let config = solstice_at_fixture_epoch(config); | ||
| let activation = first_epoch_of(&config, Height::Solstice); | ||
|
|
||
| let pledge = initial_pledge_collateral(config, activation - 1) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| assert!(pledge.is_positive()); | ||
| } | ||
|
|
||
| #[rstest] | ||
| #[case::no_power_actor(Address::POWER_ACTOR)] | ||
| #[case::no_reward_actor(Address::REWARD_ACTOR)] | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| // Copyright 2019-2026 ChainSafe Systems | ||
| // SPDX-License-Identifier: Apache-2.0, MIT | ||
|
|
||
| //! Market actor migration for FIP-0118: re-encodes the state without | ||
| //! `pending_deal_allocation_ids`. | ||
|
|
||
| use crate::state_migration::common::{ | ||
| ActorMigration, ActorMigrationInput, ActorMigrationOutput, TypeMigration, TypeMigrator, | ||
| }; | ||
| use crate::utils::db::CborStoreExt as _; | ||
| use cid::Cid; | ||
| use fil_actor_market_state::v18::State as MarketStateOld; | ||
| use fil_actor_market_state::v19::State as MarketStateNew; | ||
| use fvm_ipld_blockstore::Blockstore; | ||
|
|
||
| pub struct MarketMigrator { | ||
| pub new_code_cid: Cid, | ||
| } | ||
|
|
||
| impl<BS: Blockstore> ActorMigration<BS> for MarketMigrator { | ||
| fn migrate_state( | ||
| &self, | ||
| store: &BS, | ||
| input: ActorMigrationInput, | ||
| ) -> anyhow::Result<Option<ActorMigrationOutput>> { | ||
| let in_state: MarketStateOld = store.get_cbor_required(&input.head)?; | ||
| let out_state: MarketStateNew = TypeMigrator::migrate_type(in_state, store)?; | ||
| let new_head = store.put_cbor_default(&out_state)?; | ||
| Ok(Some(ActorMigrationOutput { | ||
| new_code_cid: self.new_code_cid, | ||
| new_head, | ||
| })) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use crate::db::MemoryDB; | ||
| use crate::utils::cid::CidCborExt as _; | ||
| use fvm_ipld_encoding::CborStore as _; | ||
| use fvm_shared4::econ::TokenAmount; | ||
|
|
||
| // The dropped field sits mid-tuple, so a shifted field would show up as a wrong value | ||
| // rather than a missing one. | ||
| #[test] | ||
| fn drops_pending_deal_allocation_ids_and_keeps_every_other_field() { | ||
| let store = MemoryDB::default(); | ||
| let distinct_cid = |tag: u64| store.put_cbor_default(&tag).unwrap(); | ||
| let in_state = MarketStateOld { | ||
| proposals: distinct_cid(1), | ||
| states: distinct_cid(2), | ||
| pending_proposals: distinct_cid(3), | ||
| escrow_table: distinct_cid(4), | ||
| locked_table: distinct_cid(5), | ||
| next_id: 1234, | ||
| deal_ops_by_epoch: distinct_cid(6), | ||
| last_cron: 5678, | ||
| total_client_locked_collateral: TokenAmount::from_atto(11), | ||
| total_provider_locked_collateral: TokenAmount::from_atto(22), | ||
| total_client_storage_fee: TokenAmount::from_atto(33), | ||
| pending_deal_allocation_ids: distinct_cid(7), | ||
| provider_sectors: distinct_cid(8), | ||
| }; | ||
| let head = store.put_cbor_default(&in_state).unwrap(); | ||
| let new_code_cid = Cid::from_cbor_blake2b256(&"market v19 code").unwrap(); | ||
|
|
||
| let output = MarketMigrator { new_code_cid } | ||
| .migrate_state(&store, ActorMigrationInput::for_head(head)) | ||
| .unwrap() | ||
| .unwrap(); | ||
|
|
||
| assert_eq!(output.new_code_cid, new_code_cid); | ||
| let out_state: MarketStateNew = store.get_cbor_required(&output.new_head).unwrap(); | ||
| let expected = MarketStateNew { | ||
| proposals: in_state.proposals, | ||
| states: in_state.states, | ||
| pending_proposals: in_state.pending_proposals, | ||
| escrow_table: in_state.escrow_table, | ||
| locked_table: in_state.locked_table, | ||
| next_id: in_state.next_id, | ||
| deal_ops_by_epoch: in_state.deal_ops_by_epoch, | ||
| last_cron: in_state.last_cron, | ||
| total_client_locked_collateral: in_state.total_client_locked_collateral.clone(), | ||
| total_provider_locked_collateral: in_state.total_provider_locked_collateral.clone(), | ||
| total_client_storage_fee: in_state.total_client_storage_fee.clone(), | ||
| provider_sectors: in_state.provider_sectors, | ||
| }; | ||
| // `State` has no `PartialEq`. | ||
| assert_eq!(format!("{out_state:?}"), format!("{expected:?}")); | ||
|
Comment on lines
+89
to
+90
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's annoying - can we derive it? not a blocker but a general good practice for the APIs is to have those derived (even if Refer to https://rust-lang.github.io/api-guidelines/interoperability.html
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will add this in the |
||
| assert_eq!(store.put_cbor_default(&out_state).unwrap(), output.new_head); | ||
| // The v19 tuple is one field shorter, so it no longer decodes as v18. | ||
| assert!(store.get_cbor::<MarketStateOld>(&output.new_head).is_err()); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will this fail the RPC calibnet tests once NV29 activates on calibnet? Not sure what policy we put there, but should be that if both nodes fail with similar errors then it's okay.