Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT]: SIMD 0072 #9

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
37 changes: 36 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ bpf-entrypoint = []
test-sbf = []

[dependencies]
num_enum = "0.7.2"
bincode = "1.3.3"
bytemuck = { version = "1.16.0", features = ["derive"] }
shank = "0.4.2"
solana-program = "2.0.1"
spl-program-error = "0.5.0"
Expand All @@ -25,6 +26,7 @@ spl-program-error = "0.5.0"
mollusk-svm = { version = "0.0.10-solana-2.0", features = ["fuzz-fd"] }
mollusk-svm-bencher = "0.0.10-solana-2.0"
solana-sdk = "2.0.1"
test-case = "3.3.1"

[lib]
crate-type = ["cdylib", "lib"]
Expand Down
12 changes: 12 additions & 0 deletions program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,16 @@ pub enum FeatureGateError {
/// Feature already activated
#[error("Feature already activated")]
FeatureAlreadyActivated,
/// Incorrect staged features address
#[error("Incorrect staged features address")]
IncorrectStagedFeaturesAddress,
/// Incorrect validator support signal address
#[error("Incorrect validator support signal address")]
IncorrectValidatorSupportSignalAddress,
/// Feature already staged for activation
#[error("Feature already staged for activation")]
FeatureAlreadyStaged,
/// Feature stage is full
#[error("Feature stage is full")]
FeatureStageFull,
}
176 changes: 169 additions & 7 deletions program/src/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Program instructions

use {
num_enum::{IntoPrimitive, TryFromPrimitive},
crate::state::FeatureBitMask,
shank::ShankInstruction,
solana_program::{
incinerator,
Expand All @@ -14,7 +14,7 @@ use {

/// Feature Gate program instructions
#[rustfmt::skip]
#[derive(Clone, Debug, PartialEq, IntoPrimitive, ShankInstruction, TryFromPrimitive)]
#[derive(Clone, Debug, PartialEq, ShankInstruction)]
#[repr(u8)]
pub enum FeatureGateInstruction {
/// Revoke a pending feature activation.
Expand Down Expand Up @@ -51,25 +51,122 @@ pub enum FeatureGateInstruction {
description = "The system program"
)]
RevokePendingActivation,
/// Add a feature to the set of features staged for activation at the end of
/// the next epoch.
///
/// Features submitted to this instruction during epoch N-1 will be staged
/// for activation at the end of epoch N. This instruction can only be
/// invoked by the designated staging authority.
///
/// This instruction expects the staged features account to either exist or
/// have been allocated enough space and owned by the Feature Gate program,
/// in order to initialize state. If the account is not yet initialized, it
/// will be initialized before the new feature is added.
///
/// Accounts expected by this instruction:
///
/// 0. `[ ]` Feature account
/// 1. `[w]` Staged features account
/// 2. `[s]` Staging authority
#[account(
0,
name = "feature",
description = "The feature account to stage"
)]
#[account(
1,
writable,
name = "staged_features",
description = "The staged features account"
)]
#[account(
2,
signer,
name = "staging_authority",
description = "The staging authority"
)]
StageFeatureForActivation,
/// Signal stake support for staged features.
///
/// This instruction will lookup the provided vote account's total stake
/// for the current epoch, then interpret the provided bitmask, then use
/// their stake amount to update the stake in support for the features in
/// the staged features account.
///
/// This instruction expects the validator support signal account to either
/// exist or have been allocated enough space and owned by the Feature Gate
/// program, in order to initialize state. If the account is not yet
/// initialized, it will be initialized, and the validator's stake will be
/// added to each supported feature.
///
/// If the validator support signal account already contains the validator's
/// last submitted bitmask for the current epoch, the validator's previous
/// bitmask will be used to deduct stake support before adding the new stake
/// support, to account for changes in the validator's support signal.
///
/// Accounts expected by this instruction:
///
/// 0. `[w]` Staged features account
/// 1. `[w]` Validator support signal account
/// 2. `[ ]` Vote account
/// 3. `[s]` Authorized voter account
#[account(
0,
writable,
name = "staged_features",
description = "The staged features account"
)]
#[account(
1,
writable,
name = "validator_support_signal",
description = "The validator support signal account"
)]
#[account(
2,
name = "vote_account",
description = "The vote account"
)]
#[account(
3,
signer,
name = "authorized_voter",
description = "authorized voter account"
)]
SignalSupportForStagedFeatures {
/// The bitmask of features supported.
signal: FeatureBitMask,
},
}

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);
match input.split_first() {
Some((&0, _)) => Ok(Self::RevokePendingActivation),
Some((&1, _)) => Ok(Self::StageFeatureForActivation),
Some((&2, rest)) if rest.len() == 1 => {
let signal = FeatureBitMask(rest[0]);
Ok(Self::SignalSupportForStagedFeatures { signal })
}
_ => 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()]
match self {
Self::RevokePendingActivation => vec![0],
Self::StageFeatureForActivation => vec![1],
Self::SignalSupportForStagedFeatures { signal } => vec![2, signal.into()],
}
}
}

/// Creates a 'RevokePendingActivation' instruction.
/// Creates a [RevokePendingActivation](enum.FeatureGateInstruction.html)
/// instruction.
pub fn revoke_pending_activation(feature_id: &Pubkey) -> Instruction {
let accounts = vec![
AccountMeta::new(*feature_id, true),
Expand All @@ -86,6 +183,53 @@ pub fn revoke_pending_activation(feature_id: &Pubkey) -> Instruction {
}
}

/// Creates a [StageFeatureForActivation](enum.FeatureGateInstruction.html)
/// instruction.
pub fn stage_feature_for_activation(
feature_id: &Pubkey,
staged_features_address: &Pubkey,
staging_authority: &Pubkey,
) -> Instruction {
let accounts = vec![
AccountMeta::new_readonly(*feature_id, false),
AccountMeta::new(*staged_features_address, false),
AccountMeta::new_readonly(*staging_authority, true),
];

let data = FeatureGateInstruction::StageFeatureForActivation.pack();

Instruction {
program_id: crate::id(),
accounts,
data,
}
}

/// Creates a [SignalSupportForStagedFeatures](enum.FeatureGateInstruction.html)
/// instruction.
pub fn signal_support_for_staged_features(
staged_features_address: &Pubkey,
validator_support_signal_address: &Pubkey,
vote_account_address: &Pubkey,
authorized_voter_address: &Pubkey,
signal: FeatureBitMask,
) -> Instruction {
let accounts = vec![
AccountMeta::new(*staged_features_address, false),
AccountMeta::new(*validator_support_signal_address, false),
AccountMeta::new_readonly(*vote_account_address, false),
AccountMeta::new_readonly(*authorized_voter_address, true),
];

let data = FeatureGateInstruction::SignalSupportForStagedFeatures { signal }.pack();

Instruction {
program_id: crate::id(),
accounts,
data,
}
}

#[cfg(test)]
mod test {
use super::*;
Expand All @@ -100,4 +244,22 @@ mod test {
fn test_pack_unpack_revoke_pending_activation() {
test_pack_unpack(&FeatureGateInstruction::RevokePendingActivation);
}

#[test]
fn test_pack_unpack_stage_feature_for_activation() {
test_pack_unpack(&FeatureGateInstruction::StageFeatureForActivation);
}

#[test]
fn test_pack_unpack_signal_support_for_staged_features() {
test_pack_unpack(&FeatureGateInstruction::SignalSupportForStagedFeatures {
signal: FeatureBitMask(0),
});
test_pack_unpack(&FeatureGateInstruction::SignalSupportForStagedFeatures {
signal: FeatureBitMask(1),
});
test_pack_unpack(&FeatureGateInstruction::SignalSupportForStagedFeatures {
signal: FeatureBitMask(255),
});
}
}
1 change: 1 addition & 0 deletions program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ mod entrypoint;
pub mod error;
pub mod instruction;
pub mod processor;
pub mod state;

solana_program::declare_id!("Feature111111111111111111111111111111111111");
Loading