diff --git a/solana/programs/gateway/src/axelar_auth_weighted.rs b/solana/programs/gateway/src/axelar_auth_weighted.rs index 5a27289..4347426 100644 --- a/solana/programs/gateway/src/axelar_auth_weighted.rs +++ b/solana/programs/gateway/src/axelar_auth_weighted.rs @@ -104,7 +104,7 @@ impl AxelarAuthWeighted { .ok_or(AxelarAuthWeightedError::EpochCalculationOverflow)? .into(); if elapsed >= self.previous_signers_retention.into() { - msg!("verifier set is too old"); + msg!("signing verifier set is too old"); return Err(AxelarAuthWeightedError::InvalidSignerSet); } @@ -125,10 +125,12 @@ impl AxelarAuthWeighted { ) -> Result { // signers must be sorted binary or alphabetically in lower case if new_verifier_set.is_empty() { + msg!("No signers in the new set"); return Err(AxelarAuthWeightedError::InvalidSignerSet); } if !matches!(new_verifier_set.sufficient_weight(), Some(true)) { + msg!("insufficient weight for the new verifier set"); return Err(AxelarAuthWeightedError::InvalidWeightThreshold); } diff --git a/solana/programs/gateway/src/instructions.rs b/solana/programs/gateway/src/instructions.rs index 9593a99..3eb448d 100644 --- a/solana/programs/gateway/src/instructions.rs +++ b/solana/programs/gateway/src/instructions.rs @@ -36,14 +36,15 @@ pub enum GatewayInstruction { /// Rotate signers for the Gateway Root Config PDA account. /// - /// 1. [] Gateway ExecuteData PDA account - /// 2. [] Verifier Setr Tracker PDA account (the one that signed the + /// 0. [] Gateway ExecuteData PDA account + /// 1. [] Verifier Setr Tracker PDA account (the one that signed the /// ExecuteData) - /// 3. [WRITE, SIGNER] new uninitialized VerifierSetTracker PDA account (the + /// 2. [WRITE, SIGNER] new uninitialized VerifierSetTracker PDA account (the /// one that needs to be initialized) - /// 4. [WRITE, SIGNER] Funding account for the new VerifierSetTracker PDA - /// 5. [] System Program account - /// 6. Optional: [SIGNER] `Operator` that's stored in the gateway config PDA. + /// 3. [WRITE, SIGNER] Funding account for the new VerifierSetTracker PDA + /// 4. [] System Program account + /// 5. Optional: [SIGNER] `Operator` that's stored in the gateway config + /// PDA. RotateSigners, /// Represents the `CallContract` Axelar event. diff --git a/solana/programs/gateway/src/processor/initialize_execute_data.rs b/solana/programs/gateway/src/processor/initialize_execute_data.rs index 00a99d8..0eee7f9 100644 --- a/solana/programs/gateway/src/processor/initialize_execute_data.rs +++ b/solana/programs/gateway/src/processor/initialize_execute_data.rs @@ -45,7 +45,10 @@ impl Processor { }; // Check: Execute Data account is not initialized. - execute_data_account.check_uninitialized_pda()?; + if let Err(err) = execute_data_account.check_uninitialized_pda() { + msg!("Execute Datat PDA already initialized"); + return Err(err); + } // Check: Execute Data PDA is correctly derived crate::assert_valid_execute_data_pda( &execute_data, diff --git a/solana/programs/gateway/tests/module/approve_messages.rs b/solana/programs/gateway/tests/module/approve_messages.rs index ff27134..e0ab619 100644 --- a/solana/programs/gateway/tests/module/approve_messages.rs +++ b/solana/programs/gateway/tests/module/approve_messages.rs @@ -569,9 +569,10 @@ async fn fail_if_signer_set_epoch_is_older_than_4() { .await; // We generate 4 new unique signer sets (not registered yet) - let new_signer_sets = (1..=MAX_ALLOWED_SIGNERS as u128) + let new_signer_sets = (0..MAX_ALLOWED_SIGNERS as u128) .map(|weight| make_signers(&[55u128, weight], 55 + weight as u64)) .collect::>(); + assert_eq!(MAX_ALLOWED_SIGNERS, new_signer_sets.len()); // Only the latest signer set is allowed to call "rotate signers" ix // to register the next latest signer set. We iterate over all signer sets, @@ -582,6 +583,7 @@ async fn fail_if_signer_set_epoch_is_older_than_4() { .tuple_windows::<(_, _)>() .enumerate() { + dbg!(idx); let new_epoch = U256::from((idx + 1) as u128); let root_pda_data = fixture .get_account::(&gateway_root_pda, &gmp_gateway::ID) @@ -596,6 +598,7 @@ async fn fail_if_signer_set_epoch_is_older_than_4() { &domain_separator, ) .await; + dbg!("rotatetd"); } // Now we have registered 5 sets in total (1 initial signer set + 4 that we diff --git a/solana/programs/gateway/tests/module/rotate_signers.rs b/solana/programs/gateway/tests/module/rotate_signers.rs index a460384..70ea807 100644 --- a/solana/programs/gateway/tests/module/rotate_signers.rs +++ b/solana/programs/gateway/tests/module/rotate_signers.rs @@ -2,13 +2,14 @@ use axelar_message_primitives::U256; use axelar_rkyv_encoding::types::{Payload, VerifierSet}; use gmp_gateway::commands::OwnedCommand; use gmp_gateway::instructions::GatewayInstruction; -use gmp_gateway::state::GatewayConfig; use solana_program_test::tokio; +use solana_sdk::compute_budget::ComputeBudgetInstruction; use solana_sdk::instruction::{AccountMeta, Instruction}; use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use test_fixtures::test_setup::{ - make_signers, SolanaAxelarIntegration, SolanaAxelarIntegrationMetadata, + make_signers, make_signers_with_quorum, SolanaAxelarIntegration, + SolanaAxelarIntegrationMetadata, }; use crate::{ @@ -23,7 +24,6 @@ fn payload_and_command(verifier_set: &VerifierSet) -> (Payload, [OwnedCommand; 1 } /// successfully process execute when there is 1 rotate signers commands -#[ignore] #[tokio::test] async fn successfully_rotates_signers() { // Setup @@ -35,6 +35,7 @@ async fn successfully_rotates_signers() { .. } = SolanaAxelarIntegration::builder() .initial_signer_weights(vec![11, 42, 33]) + .previous_signers_retention(2) .build() .setup() .await; @@ -76,7 +77,13 @@ async fn successfully_rotates_signers() { root_pda_data.auth_weighted.current_epoch(), new_epoch.clone() ); - // todo -- assert that the signer tracker pda has been initialized + // assert that the signer tracker pda has been initialized + let _ = fixture + .get_account::( + &new_signer_set.verifier_set_tracker(), + &gmp_gateway::ID, + ) + .await; // - test that both signer sets can sign new messages for signer_set in [new_signer_set, signers] { @@ -157,7 +164,6 @@ async fn cannot_invoke_rotate_signers_without_respecting_minimum_delay() { /// Ensure that we can use an old signer set to sign messages as long as the /// operator also signed the `rotate_signers` ix -#[ignore] #[tokio::test] async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_operator() { // Setup @@ -170,6 +176,7 @@ async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_ope .. } = SolanaAxelarIntegration::builder() .initial_signer_weights(vec![11, 22, 150]) + .previous_signers_retention(2) // the signer retention defines n amount of old signrs whose signatures are valid .build() .setup() .await; @@ -185,7 +192,7 @@ async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_ope .await; let newer_signer_set = make_signers(&[500, 200], 2); - let (payload, _command) = payload_and_command(&new_signer_set.verifier_set()); + let (payload, _command) = payload_and_command(&newer_signer_set.verifier_set()); // we stil use the initial signer set to sign the data (the `signers` variable) let (execute_data_pda, _, pda_execute_data) = fixture .init_rotate_signers_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) @@ -203,7 +210,10 @@ async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_ope .unwrap(); let tx = fixture .send_tx_with_custom_signers_with_metadata( - &[ix], + &[ + ComputeBudgetInstruction::set_compute_unit_limit(u32::MAX), + ix, + ], &[&operator, &fixture.payer.insecure_clone()], ) .await; @@ -227,12 +237,16 @@ async fn succeed_if_signer_set_signed_by_old_signer_set_and_submitted_by_the_ope root_pda_data.auth_weighted.current_epoch(), new_epoch.clone() ); - // todo -- assert verifier set pda + let _ = fixture + .get_account::( + &newer_signer_set.verifier_set_tracker(), + &gmp_gateway::ID, + ) + .await; } /// We use a different account in place of the expected operator to try and /// rotate signers - but an on-chain check rejects his attempts -#[ignore] #[tokio::test] async fn fail_if_provided_operator_is_not_the_real_operator_thats_stored_in_gateway_state() { // Setup @@ -244,6 +258,7 @@ async fn fail_if_provided_operator_is_not_the_real_operator_thats_stored_in_gate .. } = SolanaAxelarIntegration::builder() .initial_signer_weights(vec![11, 22, 150]) + .previous_signers_retention(100) .build() .setup() .await; @@ -279,7 +294,10 @@ async fn fail_if_provided_operator_is_not_the_real_operator_thats_stored_in_gate .unwrap(); let tx = fixture .send_tx_with_custom_signers_with_metadata( - &[ix], + &[ + ComputeBudgetInstruction::set_compute_unit_limit(u32::MAX), + ix, + ], &[&fake_operator, &fixture.payer.insecure_clone()], ) .await; @@ -296,7 +314,6 @@ async fn fail_if_provided_operator_is_not_the_real_operator_thats_stored_in_gate /// ensure that the operator still needs to use a valid signer set to to /// force-rotate the signers -#[ignore] #[tokio::test] async fn fail_if_operator_is_not_using_pre_registered_signer_set() { // Setup @@ -308,15 +325,16 @@ async fn fail_if_operator_is_not_using_pre_registered_signer_set() { .. } = SolanaAxelarIntegration::builder() .initial_signer_weights(vec![11, 22, 150]) + .previous_signers_retention(100) .build() .setup() .await; // generate a new random operator set to be used (do not register it) - let new_signer_set = make_signers(&[500, 200], 1); let random_signer_set = make_signers(&[11], 54); + let new_signer_set = make_signers(&[500, 200], 1); let (payload, ..) = payload_and_command(&new_signer_set.verifier_set()); - // using `new_signers` which is the cause of the failure + // using `initial_singers` to sign the message which is the cause of the failure let (execute_data_pda, _) = fixture .init_execute_data( &gateway_root_pda, @@ -338,7 +356,10 @@ async fn fail_if_operator_is_not_using_pre_registered_signer_set() { .unwrap(); let tx = fixture .send_tx_with_custom_signers_with_metadata( - &[ix], + &[ + ComputeBudgetInstruction::set_compute_unit_limit(u32::MAX), + ix, + ], &[&operator, &fixture.payer.insecure_clone()], ) .await; @@ -350,61 +371,74 @@ async fn fail_if_operator_is_not_using_pre_registered_signer_set() { .unwrap() .log_messages .into_iter() - .any(|msg| { msg.contains("EpochNotFound") })); + .any(|msg| { msg.contains("Invalid VerifierSetTracker PDA") })); } /// Ensure that the operator also need to explicitly sign the ix -#[ignore] #[tokio::test] async fn fail_if_operator_only_passed_but_not_actual_signer() { // Setup let SolanaAxelarIntegrationMetadata { mut fixture, - signers, gateway_root_pda, operator, domain_separator, + signers: initial_singers, .. } = SolanaAxelarIntegration::builder() .initial_signer_weights(vec![11, 22, 150]) + .previous_signers_retention(100) .build() .setup() .await; - // -- we set a new signer set to be the "latest" signer set + // generate a new random operator set to be used (do not register it) let new_signer_set = make_signers(&[500, 200], 1); - let (payload, command) = payload_and_command(&new_signer_set.verifier_set()); + fixture + .fully_rotate_signers( + &gateway_root_pda, + new_signer_set.verifier_set(), + &initial_singers, + &domain_separator, + ) + .await; + let random_signer_set = make_signers(&[11], 54); + let (payload, ..) = payload_and_command(&random_signer_set.verifier_set()); - // we stil use the initial signer set to sign the data (the `signers` variable) + // using `initial_singers` to sign the message which is the cause of the failure let (execute_data_pda, _) = fixture - .init_execute_data(&gateway_root_pda, payload, &signers, &domain_separator) + .init_execute_data( + &gateway_root_pda, + payload, + &initial_singers, + &domain_separator, + ) .await; - let rotate_signers_command_pda = fixture - .init_pending_gateway_commands(&gateway_root_pda, &command) - .await - .pop() - .unwrap(); // Action - let data = borsh::to_vec(&GatewayInstruction::RotateSigners).unwrap(); let accounts = vec![ AccountMeta::new(gateway_root_pda, false), - AccountMeta::new(execute_data_pda, false), - AccountMeta::new(rotate_signers_command_pda, false), - AccountMeta::new(operator.pubkey(), false), /* the flag being `false` is the cause of - * failure for the tx */ - AccountMeta::new_readonly(signers.verifier_set_tracker(), false), - AccountMeta::new(new_signer_set.verifier_set_tracker(), false), + AccountMeta::new_readonly(execute_data_pda, false), + AccountMeta::new_readonly(initial_singers.verifier_set_tracker(), false), + AccountMeta::new(random_signer_set.verifier_set_tracker(), false), AccountMeta::new(fixture.payer.pubkey(), true), AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new(operator.pubkey(), false), /* this is `false` because operator does not + * sign in + * this test */ ]; - let ix = Instruction { program_id: gmp_gateway::id(), accounts, - data, + data: borsh::to_vec(&GatewayInstruction::RotateSigners).unwrap(), }; let tx = fixture - .send_tx_with_custom_signers_with_metadata(&[ix], &[&fixture.payer.insecure_clone()]) + .send_tx_with_custom_signers_with_metadata( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(u32::MAX), + ix, + ], + &[&fixture.payer.insecure_clone()], + ) .await; // Assert @@ -416,9 +450,9 @@ async fn fail_if_operator_only_passed_but_not_actual_signer() { .into_iter() .any(|msg| { msg.contains("Proof is not signed by the latest signer set") })); } + /// disallow rotate signers if any other signer set besides the most recent /// epoch signed the proof -#[ignore] #[tokio::test] async fn fail_if_rotate_signers_signed_by_old_signer_set() { // Setup @@ -430,6 +464,7 @@ async fn fail_if_rotate_signers_signed_by_old_signer_set() { .. } = SolanaAxelarIntegration::builder() .initial_signer_weights(vec![11, 22, 150]) + .previous_signers_retention(100) .build() .setup() .await; @@ -465,9 +500,8 @@ async fn fail_if_rotate_signers_signed_by_old_signer_set() { /// `rotate_signer_set` is ignored if total weight is smaller than new /// command weight quorum (tx succeeds) -#[ignore] #[tokio::test] -async fn ignore_rotate_signers_if_total_weight_is_smaller_than_quorum() { +async fn fail_rotate_signers_if_total_weight_is_smaller_than_quorum() { // Setup let SolanaAxelarIntegrationMetadata { mut fixture, @@ -480,7 +514,7 @@ async fn ignore_rotate_signers_if_total_weight_is_smaller_than_quorum() { .build() .setup() .await; - let new_signer_set = make_signers(&[1, 1], 10); + let new_signer_set = make_signers_with_quorum(&[1, 1], u64::MAX, 10); // Action let (.., tx) = fixture @@ -492,16 +526,15 @@ async fn ignore_rotate_signers_if_total_weight_is_smaller_than_quorum() { ) .await; - assert!(tx.result.is_ok()); - let gateway = fixture - .get_account::(&gateway_root_pda, &gmp_gateway::ID) - .await; - let constant_epoch: U256 = 1u128.into(); - assert_eq!(gateway.auth_weighted.current_epoch(), constant_epoch); - // todo -- assert verifier set pda + assert!(tx.result.is_err()); + assert!(tx + .metadata + .unwrap() + .log_messages + .into_iter() + .any(|msg| { msg.contains("insufficient weight for the new verifier set") })); } -#[ignore] #[tokio::test] async fn fail_if_order_of_commands_is_not_the_same_as_order_of_accounts() { // Setup @@ -542,8 +575,7 @@ async fn fail_if_order_of_commands_is_not_the_same_as_order_of_accounts() { assert!(tx.result.is_err()); } -/// `rotate_signer_set` is ignored if new signer set len is 0 (tx succeeds) -#[ignore] +/// `rotate_signer_set` fails when new singner len is zero #[tokio::test] async fn fail_on_rotate_signers_if_new_ops_len_is_zero() { // Setup @@ -576,11 +608,11 @@ async fn fail_on_rotate_signers_if_new_ops_len_is_zero() { .await; // Assert - assert!(tx.result.is_ok()); - let gateway = fixture - .get_account::(&gateway_root_pda, &gmp_gateway::ID) - .await; - let constant_epoch: U256 = 1u128.into(); - assert_eq!(gateway.auth_weighted.current_epoch(), constant_epoch); - // todo -- assert verifier set pda + assert!(tx.result.is_err()); + assert!(tx + .metadata + .unwrap() + .log_messages + .into_iter() + .any(|msg| { msg.contains("InvalidSignerSet") })); }