Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit fe6a570

Browse files
author
Joe C
authored
feature gate: init program with RevokePendingActivation instruction (#5510)
* feature gate: init program * move to program folder * address latest feedback
1 parent 0ce6561 commit fe6a570

File tree

10 files changed

+380
-0
lines changed

10 files changed

+380
-0
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ members = [
1313
"examples/rust/sysvar",
1414
"examples/rust/transfer-lamports",
1515
"examples/rust/transfer-tokens",
16+
"feature-gate/program",
1617
"feature-proposal/program",
1718
"feature-proposal/cli",
1819
"governance/addin-mock/program",

feature-gate/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Feature Gate Program
2+
3+
This program serves to manage new features on Solana.
4+
5+
It serves one purpose: revoking features that are pending activation.
6+
7+
More information & documentation will follow as this program matures, but you
8+
can follow the discussions
9+
[here](https://github.com/solana-labs/solana/issues/32780)!

feature-gate/program/Cargo.toml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[package]
2+
name = "spl-feature-gate"
3+
version = "0.1.0"
4+
description = "Solana Program Library Feature Gate Program"
5+
authors = ["Solana Labs Maintainers <[email protected]>"]
6+
repository = "https://github.com/solana-labs/solana-program-library"
7+
license = "Apache-2.0"
8+
edition = "2021"
9+
10+
[features]
11+
no-entrypoint = []
12+
test-sbf = []
13+
14+
[dependencies]
15+
num_enum = "0.7.0"
16+
solana-program = "1.16.16"
17+
spl-program-error = { version = "0.3.0", path = "../../libraries/program-error" }
18+
19+
[dev-dependencies]
20+
solana-program-test = "1.16.16"
21+
solana-sdk = "1.16.16"
22+
23+
[lib]
24+
crate-type = ["cdylib", "lib"]
25+
26+
[package.metadata.docs.rs]
27+
targets = ["x86_64-unknown-linux-gnu"]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//! Program entrypoint
2+
3+
use {
4+
crate::processor,
5+
solana_program::{
6+
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, pubkey::Pubkey,
7+
},
8+
};
9+
10+
entrypoint!(process_instruction);
11+
fn process_instruction(
12+
program_id: &Pubkey,
13+
accounts: &[AccountInfo],
14+
input: &[u8],
15+
) -> ProgramResult {
16+
processor::process(program_id, accounts, input)
17+
}

feature-gate/program/src/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Program error types
2+
3+
use spl_program_error::*;
4+
5+
/// Program specific errors
6+
#[spl_program_error]
7+
pub enum FeatureGateError {
8+
/// Operation overflowed
9+
#[error("Operation overflowed")]
10+
Overflow,
11+
/// Feature already activated
12+
#[error("Feature already activated")]
13+
FeatureAlreadyActivated,
14+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//! Program instructions
2+
3+
use {
4+
num_enum::{IntoPrimitive, TryFromPrimitive},
5+
solana_program::{
6+
instruction::{AccountMeta, Instruction},
7+
program_error::ProgramError,
8+
pubkey::Pubkey,
9+
},
10+
};
11+
12+
/// Feature Gate program instructions
13+
#[derive(Clone, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
14+
#[repr(u8)]
15+
pub enum FeatureGateInstruction {
16+
/// Revoke a pending feature activation.
17+
///
18+
/// A "pending" feature activation is a feature account that has been
19+
/// allocated and assigned, but hasn't yet been updated by the runtime
20+
/// with an `activation_slot`.
21+
///
22+
/// Features that _have_ been activated by the runtime cannot be revoked.
23+
///
24+
/// Accounts expected by this instruction:
25+
///
26+
/// 0. `[w+s]` Feature account
27+
/// 1. `[w]` Destination (for rent lamports)
28+
RevokePendingActivation,
29+
}
30+
impl FeatureGateInstruction {
31+
/// Unpacks a byte buffer into a
32+
/// [FeatureGateInstruction](enum.FeatureGateInstruction.html).
33+
pub fn unpack(input: &[u8]) -> Result<Self, ProgramError> {
34+
if input.len() != 1 {
35+
return Err(ProgramError::InvalidInstructionData);
36+
}
37+
Self::try_from(input[0]).map_err(|_| ProgramError::InvalidInstructionData)
38+
}
39+
40+
/// Packs a [FeatureGateInstruction](enum.FeatureGateInstruction.html) into
41+
/// a byte buffer.
42+
pub fn pack(&self) -> Vec<u8> {
43+
vec![self.to_owned().into()]
44+
}
45+
}
46+
47+
/// Creates a 'RevokePendingActivation' instruction.
48+
pub fn revoke_pending_activation(feature_id: &Pubkey, destination: &Pubkey) -> Instruction {
49+
let accounts = vec![
50+
AccountMeta::new(*feature_id, true),
51+
AccountMeta::new(*destination, false),
52+
];
53+
54+
let data = FeatureGateInstruction::RevokePendingActivation.pack();
55+
56+
Instruction {
57+
program_id: crate::id(),
58+
accounts,
59+
data,
60+
}
61+
}
62+
63+
#[cfg(test)]
64+
mod test {
65+
use super::*;
66+
67+
fn test_pack_unpack(instruction: &FeatureGateInstruction) {
68+
let packed = instruction.pack();
69+
let unpacked = FeatureGateInstruction::unpack(&packed).unwrap();
70+
assert_eq!(instruction, &unpacked);
71+
}
72+
73+
#[test]
74+
fn test_pack_unpack_revoke_pending_activation() {
75+
test_pack_unpack(&FeatureGateInstruction::RevokePendingActivation);
76+
}
77+
}

feature-gate/program/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! Feature Gate program
2+
3+
#![deny(missing_docs)]
4+
#![cfg_attr(not(test), forbid(unsafe_code))]
5+
6+
#[cfg(not(feature = "no-entrypoint"))]
7+
mod entrypoint;
8+
pub mod error;
9+
pub mod instruction;
10+
pub mod processor;
11+
12+
// Export current SDK types for downstream users building with a different SDK
13+
// version
14+
pub use solana_program;
15+
16+
solana_program::declare_id!("Feature111111111111111111111111111111111111");

feature-gate/program/src/processor.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//! Program state processor
2+
3+
use {
4+
crate::{error::FeatureGateError, instruction::FeatureGateInstruction},
5+
solana_program::{
6+
account_info::{next_account_info, AccountInfo},
7+
entrypoint::ProgramResult,
8+
feature::Feature,
9+
msg,
10+
program_error::ProgramError,
11+
pubkey::Pubkey,
12+
system_program,
13+
},
14+
};
15+
16+
/// Processes a [RevokePendingActivation](enum.FeatureGateInstruction.html)
17+
/// instruction.
18+
pub fn process_revoke_pending_activation(
19+
_program_id: &Pubkey,
20+
accounts: &[AccountInfo],
21+
) -> ProgramResult {
22+
let account_info_iter = &mut accounts.iter();
23+
24+
let feature_info = next_account_info(account_info_iter)?;
25+
let destination_info = next_account_info(account_info_iter)?;
26+
27+
if !feature_info.is_signer {
28+
return Err(ProgramError::MissingRequiredSignature);
29+
}
30+
31+
// This will also check the program ID
32+
if Feature::from_account_info(feature_info)?
33+
.activated_at
34+
.is_some()
35+
{
36+
return Err(FeatureGateError::FeatureAlreadyActivated.into());
37+
}
38+
39+
let new_destination_lamports = feature_info
40+
.lamports()
41+
.checked_add(destination_info.lamports())
42+
.ok_or::<ProgramError>(FeatureGateError::Overflow.into())?;
43+
44+
**feature_info.try_borrow_mut_lamports()? = 0;
45+
**destination_info.try_borrow_mut_lamports()? = new_destination_lamports;
46+
47+
feature_info.realloc(0, true)?;
48+
feature_info.assign(&system_program::id());
49+
50+
Ok(())
51+
}
52+
53+
/// Processes an [Instruction](enum.Instruction.html).
54+
pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
55+
let instruction = FeatureGateInstruction::unpack(input)?;
56+
match instruction {
57+
FeatureGateInstruction::RevokePendingActivation => {
58+
msg!("Instruction: RevokePendingActivation");
59+
process_revoke_pending_activation(program_id, accounts)
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)