-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature gate: init program with
RevokePendingActivation
instruction (…
…#5510) * feature gate: init program * move to program folder * address latest feedback
- Loading branch information
Joe C
authored
Oct 18, 2023
1 parent
0ce6561
commit fe6a570
Showing
10 changed files
with
380 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# 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)! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
[package] | ||
name = "spl-feature-gate" | ||
version = "0.1.0" | ||
description = "Solana Program Library Feature Gate Program" | ||
authors = ["Solana Labs Maintainers <[email protected]>"] | ||
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.0" | ||
solana-program = "1.16.16" | ||
spl-program-error = { version = "0.3.0", path = "../../libraries/program-error" } | ||
|
||
[dev-dependencies] | ||
solana-program-test = "1.16.16" | ||
solana-sdk = "1.16.16" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "lib"] | ||
|
||
[package.metadata.docs.rs] | ||
targets = ["x86_64-unknown-linux-gnu"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
//! Program entrypoint | ||
use { | ||
crate::processor, | ||
solana_program::{ | ||
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey, | ||
}, | ||
}; | ||
|
||
entrypoint!(process_instruction); | ||
fn process_instruction( | ||
program_id: &Pubkey, | ||
accounts: &[AccountInfo], | ||
input: &[u8], | ||
) -> ProgramResult { | ||
processor::process(program_id, accounts, input) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
//! 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, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
//! 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<Self, ProgramError> { | ||
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<u8> { | ||
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
//! 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"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
//! 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::<ProgramError>(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) | ||
} | ||
} | ||
} |
Oops, something went wrong.