From 41dce3a101b9cac2c5aa5f814dfc9d70bd031ef3 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Sun, 1 Mar 2026 17:38:40 +0000 Subject: [PATCH 1/4] feat: allow foresters to register at any time by disabling registration phase time check --- programs/registry/src/protocol_config/state.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/programs/registry/src/protocol_config/state.rs b/programs/registry/src/protocol_config/state.rs index eaaa41b2b1..cad725fad1 100644 --- a/programs/registry/src/protocol_config/state.rs +++ b/programs/registry/src/protocol_config/state.rs @@ -184,12 +184,16 @@ impl ProtocolConfig { /// In the last part of the active phase the registration phase starts. /// Returns end slot of the registration phase/start slot of the next active phase. + /// Registration phase time check is temporarily disabled to allow + /// foresters to register for the current epoch at any time. pub fn is_registration_phase(&self, slot: u64) -> Result { let latest_register_epoch = self.get_latest_register_epoch(slot)?; + /* let latest_register_epoch_progress = self.get_latest_register_epoch_progress(slot)?; if latest_register_epoch_progress >= self.registration_phase_length { return err!(RegistryError::NotInRegistrationPeriod); - } + } */ + Ok((latest_register_epoch) * self.active_phase_length + self.genesis_slot + self.registration_phase_length) From 4e66adc34358d1debed5331eafa3753a22d6ddb2 Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Sun, 1 Mar 2026 17:49:55 +0000 Subject: [PATCH 2/4] format --- programs/registry/src/protocol_config/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programs/registry/src/protocol_config/state.rs b/programs/registry/src/protocol_config/state.rs index cad725fad1..d3094498a8 100644 --- a/programs/registry/src/protocol_config/state.rs +++ b/programs/registry/src/protocol_config/state.rs @@ -188,12 +188,12 @@ impl ProtocolConfig { /// foresters to register for the current epoch at any time. pub fn is_registration_phase(&self, slot: u64) -> Result { let latest_register_epoch = self.get_latest_register_epoch(slot)?; - /* + /* let latest_register_epoch_progress = self.get_latest_register_epoch_progress(slot)?; if latest_register_epoch_progress >= self.registration_phase_length { return err!(RegistryError::NotInRegistrationPeriod); } */ - + Ok((latest_register_epoch) * self.active_phase_length + self.genesis_slot + self.registration_phase_length) From 901320e2b2f7c801a9b94880f425f694acce285b Mon Sep 17 00:00:00 2001 From: Sergey Timoshin Date: Sun, 1 Mar 2026 19:09:09 +0000 Subject: [PATCH 3/4] format --- .../csdk-anchor-full-derived-test/tests/integration_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs index 9b40b900e5..2c3e82972a 100644 --- a/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs +++ b/sdk-tests/csdk-anchor-full-derived-test/tests/integration_tests.rs @@ -3863,7 +3863,7 @@ async fn test_d9_edge_many_literals() { #[tokio::test] async fn test_d9_edge_mixed() { use csdk_anchor_full_derived_test::d9_seeds::{ - edge_cases::{AB, SEED_123, _UNDERSCORE_CONST}, + edge_cases::{_UNDERSCORE_CONST, AB, SEED_123}, D9EdgeMixedParams, }; From 4fd014b9bb1680ae7172cf7a6d39cc79277fa81c Mon Sep 17 00:00:00 2001 From: ananas Date: Mon, 2 Mar 2026 18:13:46 +0000 Subject: [PATCH 4/4] test: add register any time functional test --- .../tests/test_register_anytime.rs | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 program-tests/registry-test/tests/test_register_anytime.rs diff --git a/program-tests/registry-test/tests/test_register_anytime.rs b/program-tests/registry-test/tests/test_register_anytime.rs new file mode 100644 index 0000000000..0971c61819 --- /dev/null +++ b/program-tests/registry-test/tests/test_register_anytime.rs @@ -0,0 +1,93 @@ +use forester_utils::forester_epoch::Epoch; +use light_program_test::{ + program_test::{LightProgramTest, TestRpc}, + ProgramTestConfig, +}; +use light_registry::{ + protocol_config::state::{ProtocolConfig, ProtocolConfigPda}, + ForesterConfig, ForesterEpochPda, +}; +use light_test_utils::{ + assert_epoch::{assert_epoch_pda, assert_registered_forester_pda}, + register_test_forester, Rpc, +}; +use serial_test::serial; +use solana_sdk::{signature::Keypair, signer::Signer}; + +/// Verifies that foresters can register for an epoch outside the original +/// registration window (slots 0-99 with default config). Previously this +/// would fail with `NotInRegistrationPeriod`. +#[serial] +#[tokio::test] +async fn test_forester_register_outside_registration_phase() { + let config = ProgramTestConfig { + protocol_config: ProtocolConfig::default(), + with_prover: false, + with_forester: false, + ..Default::default() + }; + let mut rpc = LightProgramTest::new(config).await.unwrap(); + rpc.indexer = None; + let env = rpc.test_accounts.clone(); + + let forester_keypair = Keypair::new(); + rpc.airdrop_lamports(&forester_keypair.pubkey(), 1_000_000_000) + .await + .unwrap(); + + // Register the base forester PDA. + register_test_forester( + &mut rpc, + &env.protocol.governance_authority, + &forester_keypair.pubkey(), + ForesterConfig { fee: 1 }, + ) + .await + .unwrap(); + + let protocol_config = rpc + .get_anchor_account::(&env.protocol.governance_authority_pda) + .await + .unwrap() + .unwrap() + .config; + + // Warp to slot 500 -- well past the old registration window (0-99). + // With ProtocolConfig::default() the active phase starts at slot 100. + rpc.warp_to_slot(500).unwrap(); + + // Register for epoch 0 with explicit epoch to bypass client-side + // auto-detection (which would skip if not in registration phase). + let registered_epoch = Epoch::register( + &mut rpc, + &protocol_config, + &forester_keypair, + &forester_keypair.pubkey(), + Some(0), + ) + .await + .unwrap(); + + assert!( + registered_epoch.is_some(), + "Forester should be able to register outside the original registration window" + ); + let registered_epoch = registered_epoch.unwrap(); + + let forester_epoch_pda = rpc + .get_anchor_account::(®istered_epoch.forester_epoch_pda) + .await + .unwrap() + .unwrap(); + assert_eq!(forester_epoch_pda.epoch, 0); + assert!(forester_epoch_pda.total_epoch_weight.is_none()); + + assert_epoch_pda(&mut rpc, 0, 1).await; + assert_registered_forester_pda( + &mut rpc, + ®istered_epoch.forester_epoch_pda, + &forester_keypair.pubkey(), + 0, + ) + .await; +}