diff --git a/.github/workflows/pull-request-feature-gate.yml b/.github/workflows/pull-request-feature-gate.yml deleted file mode 100644 index 12d41675239..00000000000 --- a/.github/workflows/pull-request-feature-gate.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Feature Gate Pull Request - -on: - pull_request: - paths: - - 'feature-gate/**' - - 'ci/*-version.sh' - - '.github/workflows/pull-request-feature-gate.yml' - push: - branches: [master] - paths: - - 'feature-gate/**' - - 'ci/*-version.sh' - - '.github/workflows/pull-request-feature-gate.yml' - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - cargo-test-sbf: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set env vars - run: | - source ci/rust-version.sh - echo "RUST_STABLE=$rust_stable" >> $GITHUB_ENV - source ci/solana-version.sh - echo "SOLANA_VERSION=$solana_version" >> $GITHUB_ENV - - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_STABLE }} - - - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - key: cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUST_STABLE}} - - - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/rustfilt - key: cargo-sbf-bins-${{ runner.os }} - - - uses: actions/cache@v3 - with: - path: ~/.cache/solana - key: solana-${{ env.SOLANA_VERSION }} - - - name: Install dependencies - run: | - ./ci/install-build-deps.sh - ./ci/install-program-deps.sh - echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH - - - name: Build and test - run: ./ci/cargo-test-sbf.sh feature-gate diff --git a/Cargo.lock b/Cargo.lock index d07be0d5615..7f1cefac379 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7022,17 +7022,6 @@ dependencies = [ "spl-token 4.0.1", ] -[[package]] -name = "spl-feature-gate" -version = "0.1.0" -dependencies = [ - "num_enum 0.7.2", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-program-error 0.4.1", -] - [[package]] name = "spl-feature-proposal" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 6f042b7c825..f07979180d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,6 @@ members = [ "examples/rust/sysvar", "examples/rust/transfer-lamports", "examples/rust/transfer-tokens", - "feature-gate/program", "feature-proposal/program", "feature-proposal/cli", "governance/addin-mock/program", diff --git a/feature-gate/README.md b/feature-gate/README.md deleted file mode 100644 index c0dbaf5787c..00000000000 --- a/feature-gate/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Feature Gate Program - -This program serves to manage new features on Solana. - -It serves one purpose: revoking features that are pending activation. - -More information & documentation will follow as this program matures, but you -can follow the discussions -[here](https://github.com/solana-labs/solana/issues/32780)! diff --git a/feature-gate/program/Cargo.toml b/feature-gate/program/Cargo.toml deleted file mode 100644 index 2f3a83ae349..00000000000 --- a/feature-gate/program/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "spl-feature-gate" -version = "0.1.0" -description = "Solana Program Library Feature Gate Program" -authors = ["Solana Labs Maintainers "] -repository = "https://github.com/solana-labs/solana-program-library" -license = "Apache-2.0" -edition = "2021" - -[features] -no-entrypoint = [] -test-sbf = [] - -[dependencies] -num_enum = "0.7.2" -solana-program = ">=1.18.2,<=2" -spl-program-error = { version = "0.4.0", path = "../../libraries/program-error" } - -[dev-dependencies] -solana-program-test = ">=1.18.2,<=2" -solana-sdk = ">=1.18.2,<=2" - -[lib] -crate-type = ["cdylib", "lib"] - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] diff --git a/feature-gate/program/src/entrypoint.rs b/feature-gate/program/src/entrypoint.rs deleted file mode 100644 index d3a0113d746..00000000000 --- a/feature-gate/program/src/entrypoint.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Program entrypoint - -use { - crate::processor, - solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey}, -}; - -solana_program::entrypoint!(process_instruction); -fn process_instruction( - program_id: &Pubkey, - accounts: &[AccountInfo], - input: &[u8], -) -> ProgramResult { - processor::process(program_id, accounts, input) -} diff --git a/feature-gate/program/src/error.rs b/feature-gate/program/src/error.rs deleted file mode 100644 index 731c61213a4..00000000000 --- a/feature-gate/program/src/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Program error types - -use spl_program_error::*; - -/// Program specific errors -#[spl_program_error] -pub enum FeatureGateError { - /// Operation overflowed - #[error("Operation overflowed")] - Overflow, - /// Feature already activated - #[error("Feature already activated")] - FeatureAlreadyActivated, -} diff --git a/feature-gate/program/src/instruction.rs b/feature-gate/program/src/instruction.rs deleted file mode 100644 index 4fa73a514e1..00000000000 --- a/feature-gate/program/src/instruction.rs +++ /dev/null @@ -1,77 +0,0 @@ -//! Program instructions - -use { - num_enum::{IntoPrimitive, TryFromPrimitive}, - solana_program::{ - instruction::{AccountMeta, Instruction}, - program_error::ProgramError, - pubkey::Pubkey, - }, -}; - -/// Feature Gate program instructions -#[derive(Clone, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] -#[repr(u8)] -pub enum FeatureGateInstruction { - /// Revoke a pending feature activation. - /// - /// A "pending" feature activation is a feature account that has been - /// allocated and assigned, but hasn't yet been updated by the runtime - /// with an `activation_slot`. - /// - /// Features that _have_ been activated by the runtime cannot be revoked. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[w+s]` Feature account - /// 1. `[w]` Destination (for rent lamports) - RevokePendingActivation, -} -impl FeatureGateInstruction { - /// Unpacks a byte buffer into a - /// [FeatureGateInstruction](enum.FeatureGateInstruction.html). - pub fn unpack(input: &[u8]) -> Result { - if input.len() != 1 { - return Err(ProgramError::InvalidInstructionData); - } - Self::try_from(input[0]).map_err(|_| ProgramError::InvalidInstructionData) - } - - /// Packs a [FeatureGateInstruction](enum.FeatureGateInstruction.html) into - /// a byte buffer. - pub fn pack(&self) -> Vec { - vec![self.to_owned().into()] - } -} - -/// Creates a 'RevokePendingActivation' instruction. -pub fn revoke_pending_activation(feature_id: &Pubkey, destination: &Pubkey) -> Instruction { - let accounts = vec![ - AccountMeta::new(*feature_id, true), - AccountMeta::new(*destination, false), - ]; - - let data = FeatureGateInstruction::RevokePendingActivation.pack(); - - Instruction { - program_id: crate::id(), - accounts, - data, - } -} - -#[cfg(test)] -mod test { - use super::*; - - fn test_pack_unpack(instruction: &FeatureGateInstruction) { - let packed = instruction.pack(); - let unpacked = FeatureGateInstruction::unpack(&packed).unwrap(); - assert_eq!(instruction, &unpacked); - } - - #[test] - fn test_pack_unpack_revoke_pending_activation() { - test_pack_unpack(&FeatureGateInstruction::RevokePendingActivation); - } -} diff --git a/feature-gate/program/src/lib.rs b/feature-gate/program/src/lib.rs deleted file mode 100644 index e6be6428a15..00000000000 --- a/feature-gate/program/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! Feature Gate program - -#![deny(missing_docs)] -#![cfg_attr(not(test), forbid(unsafe_code))] - -#[cfg(not(feature = "no-entrypoint"))] -mod entrypoint; -pub mod error; -pub mod instruction; -pub mod processor; - -// Export current SDK types for downstream users building with a different SDK -// version -pub use solana_program; - -solana_program::declare_id!("Feature111111111111111111111111111111111111"); diff --git a/feature-gate/program/src/processor.rs b/feature-gate/program/src/processor.rs deleted file mode 100644 index 574ad143071..00000000000 --- a/feature-gate/program/src/processor.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! Program state processor - -use { - crate::{error::FeatureGateError, instruction::FeatureGateInstruction}, - solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - feature::Feature, - msg, - program_error::ProgramError, - pubkey::Pubkey, - system_program, - }, -}; - -/// Processes a [RevokePendingActivation](enum.FeatureGateInstruction.html) -/// instruction. -pub fn process_revoke_pending_activation( - _program_id: &Pubkey, - accounts: &[AccountInfo], -) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - - let feature_info = next_account_info(account_info_iter)?; - let destination_info = next_account_info(account_info_iter)?; - - if !feature_info.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - - // This will also check the program ID - if Feature::from_account_info(feature_info)? - .activated_at - .is_some() - { - return Err(FeatureGateError::FeatureAlreadyActivated.into()); - } - - let new_destination_lamports = feature_info - .lamports() - .checked_add(destination_info.lamports()) - .ok_or::(FeatureGateError::Overflow.into())?; - - **feature_info.try_borrow_mut_lamports()? = 0; - **destination_info.try_borrow_mut_lamports()? = new_destination_lamports; - - feature_info.realloc(0, true)?; - feature_info.assign(&system_program::id()); - - Ok(()) -} - -/// Processes an [Instruction](enum.Instruction.html). -pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { - let instruction = FeatureGateInstruction::unpack(input)?; - match instruction { - FeatureGateInstruction::RevokePendingActivation => { - msg!("Instruction: RevokePendingActivation"); - process_revoke_pending_activation(program_id, accounts) - } - } -} diff --git a/feature-gate/program/tests/functional.rs b/feature-gate/program/tests/functional.rs deleted file mode 100644 index e586e7bc459..00000000000 --- a/feature-gate/program/tests/functional.rs +++ /dev/null @@ -1,146 +0,0 @@ -#![cfg(feature = "test-sbf")] - -use { - solana_program::instruction::InstructionError, - solana_program_test::{processor, tokio, ProgramTest, ProgramTestContext}, - solana_sdk::{ - account::Account as SolanaAccount, - feature::{activate_with_lamports, Feature}, - pubkey::Pubkey, - signature::{Keypair, Signer}, - transaction::{Transaction, TransactionError}, - }, - spl_feature_gate::{error::FeatureGateError, instruction::revoke_pending_activation}, -}; - -async fn setup_pending_feature( - context: &mut ProgramTestContext, - feature_keypair: &Keypair, - rent_lamports: u64, -) { - let transaction = Transaction::new_signed_with_payer( - &activate_with_lamports( - &feature_keypair.pubkey(), - &context.payer.pubkey(), - rent_lamports, - ), - Some(&context.payer.pubkey()), - &[&context.payer, feature_keypair], - context.last_blockhash, - ); - - context - .banks_client - .process_transaction(transaction) - .await - .unwrap(); -} - -#[tokio::test] -async fn test_revoke_pending_activation() { - let feature_keypair = Keypair::new(); - let destination = Pubkey::new_unique(); - let mock_active_feature_keypair = Keypair::new(); - - let mut program_test = ProgramTest::new( - "spl_feature_gate", - spl_feature_gate::id(), - processor!(spl_feature_gate::processor::process), - ); - - // Add a mock _active_ feature for testing later - program_test.add_account( - mock_active_feature_keypair.pubkey(), - SolanaAccount { - lamports: 500_000_000, - owner: spl_feature_gate::id(), - data: vec![ - 1, // `Some()` - 45, 0, 0, 0, 0, 0, 0, 0, // Random slot `u64` - ], - ..SolanaAccount::default() - }, - ); - - let mut context = program_test.start_with_context().await; - let rent = context.banks_client.get_rent().await.unwrap(); - let rent_lamports = rent.minimum_balance(Feature::size_of()); // For checking account balance later - - setup_pending_feature(&mut context, &feature_keypair, rent_lamports).await; - - // Fail: feature not signer - let mut revoke_ix = revoke_pending_activation(&feature_keypair.pubkey(), &destination); - revoke_ix.accounts[0].is_signer = false; - let transaction = Transaction::new_signed_with_payer( - &[revoke_ix], - Some(&context.payer.pubkey()), - &[&context.payer], - context.last_blockhash, - ); - let error = context - .banks_client - .process_transaction(transaction) - .await - .unwrap_err() - .unwrap(); - assert_eq!( - error, - TransactionError::InstructionError(0, InstructionError::MissingRequiredSignature) - ); - - // Fail: feature is already active - let transaction = Transaction::new_signed_with_payer( - &[revoke_pending_activation( - &mock_active_feature_keypair.pubkey(), - &destination, - )], - Some(&context.payer.pubkey()), - &[&context.payer, &mock_active_feature_keypair], - context.last_blockhash, - ); - let error = context - .banks_client - .process_transaction(transaction) - .await - .unwrap_err() - .unwrap(); - assert_eq!( - error, - TransactionError::InstructionError( - 0, - InstructionError::Custom(FeatureGateError::FeatureAlreadyActivated as u32) - ) - ); - - // Success: Revoke a feature activation - let transaction = Transaction::new_signed_with_payer( - &[revoke_pending_activation( - &feature_keypair.pubkey(), - &destination, - )], - Some(&context.payer.pubkey()), - &[&context.payer, &feature_keypair], - context.last_blockhash, - ); - - context - .banks_client - .process_transaction(transaction) - .await - .unwrap(); - - // Confirm feature account was closed and destination account received lamports - let feature_account = context - .banks_client - .get_account(feature_keypair.pubkey()) - .await - .unwrap(); - assert!(feature_account.is_none()); - let destination_account = context - .banks_client - .get_account(destination) - .await - .unwrap() - .unwrap(); - assert_eq!(destination_account.lamports, rent_lamports); -}