Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ members = [
"cluster-type",
"commitment-config",
"compute-budget-interface",
"config-interface",
"cpi",
"decode-error",
"define-syscall",
Expand Down Expand Up @@ -222,6 +223,7 @@ solana-clock = { path = "clock", version = "2.2.1" }
solana-cluster-type = { path = "cluster-type", version = "2.2.1" }
solana-commitment-config = { path = "commitment-config", version = "2.2.1" }
solana-compute-budget-interface = { path = "compute-budget-interface", version = "2.2.1" }
solana-config-interface = { path = "config-interface", version = "1.0.0" }
solana-cpi = { path = "cpi", version = "2.2.1" }
solana-decode-error = { path = "decode-error", version = "2.2.1" }
solana-define-syscall = { path = "define-syscall", version = "2.2.1" }
Expand Down
50 changes: 50 additions & 0 deletions config-interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[package]
name = "solana-config-interface"
description = "Solana config program interface."
documentation = "https://docs.rs/solana-config-interface"
version = "1.0.0"
authors = { workspace = true }
repository = { workspace = true }
homepage = { workspace = true }
license = { workspace = true }
edition = { workspace = true }

[dependencies]
bincode = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
solana-account = { workspace = true, optional = true }
solana-instruction = { workspace = true, optional = true }
solana-pubkey = { workspace = true }
solana-sdk-ids = { workspace = true }
solana-short-vec = { workspace = true, optional = true }
solana-stake-interface = { workspace = true, optional = true, features = [
"bincode",
] }
solana-system-interface = { workspace = true, optional = true, features = [
"bincode",
] }

[features]
bincode = [
"dep:bincode",
"dep:solana-account",
"dep:solana-instruction",
"dep:solana-stake-interface",
"dep:solana-system-interface",
"serde",
]
serde = [
"dep:serde",
"dep:serde_derive",
"dep:solana-short-vec",
"solana-pubkey/serde",
]

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
all-features = true
rustdoc-args = ["--cfg=docsrs"]

[lints]
workspace = true
50 changes: 50 additions & 0 deletions config-interface/src/config_instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use {
crate::{id, ConfigKeys, ConfigState},
solana_instruction::{AccountMeta, Instruction},
solana_pubkey::Pubkey,
solana_system_interface::instruction as system_instruction,
};

fn initialize_account<T: ConfigState>(config_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*config_pubkey, true)];
let account_data = (ConfigKeys { keys: vec![] }, T::default());
Instruction::new_with_bincode(id(), &account_data, account_metas)
}

#[cfg(feature = "bincode")]
/// Create a new, empty configuration account
pub fn create_account<T: ConfigState>(
from_account_pubkey: &Pubkey,
config_account_pubkey: &Pubkey,
lamports: u64,
keys: Vec<(Pubkey, bool)>,
) -> Vec<Instruction> {
let space = T::max_space() + ConfigKeys::serialized_size(keys);
vec![
system_instruction::create_account(
from_account_pubkey,
config_account_pubkey,
lamports,
space,
&id(),
),
initialize_account::<T>(config_account_pubkey),
]
}

/// Store new data in a configuration account
pub fn store<T: ConfigState>(
config_account_pubkey: &Pubkey,
is_config_signer: bool,
keys: Vec<(Pubkey, bool)>,
data: &T,
) -> Instruction {
let mut account_metas = vec![AccountMeta::new(*config_account_pubkey, is_config_signer)];
for (signer_pubkey, _) in keys.iter().filter(|(_, is_signer)| *is_signer) {
if signer_pubkey != config_account_pubkey {
account_metas.push(AccountMeta::new(*signer_pubkey, true));
}
}
let account_data = (ConfigKeys { keys }, data);
Instruction::new_with_bincode(id(), &account_data, account_metas)
}
75 changes: 75 additions & 0 deletions config-interface/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![allow(clippy::arithmetic_side_effects)]
#[cfg(feature = "bincode")]
pub mod config_instruction;

use solana_pubkey::Pubkey;
pub use solana_sdk_ids::config::id;
#[cfg(feature = "bincode")]
#[allow(deprecated)]
use {
bincode::{deserialize, serialize, serialized_size},
solana_account::{Account, AccountSharedData},
solana_stake_interface::config::Config as StakeConfig,
};
#[cfg(feature = "serde")]
use {
serde_derive::{Deserialize, Serialize},
solana_short_vec as short_vec,
};

#[cfg(feature = "serde")]
pub trait ConfigState: serde::Serialize + Default {
/// Maximum space that the serialized representation will require
fn max_space() -> u64;
}

// TODO move ConfigState into `solana_program` to implement trait locally
#[cfg(feature = "bincode")]
#[allow(deprecated)]
impl ConfigState for StakeConfig {
fn max_space() -> u64 {
serialized_size(&StakeConfig::default()).unwrap()
}
}

/// A collection of keys to be stored in Config account data.
#[derive(Debug, Default)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct ConfigKeys {
// Each key tuple comprises a unique `Pubkey` identifier,
// and `bool` whether that key is a signer of the data
#[cfg_attr(feature = "serde", serde(with = "short_vec"))]
pub keys: Vec<(Pubkey, bool)>,
}

#[cfg(feature = "bincode")]
impl ConfigKeys {
pub fn serialized_size(keys: Vec<(Pubkey, bool)>) -> u64 {
serialized_size(&ConfigKeys { keys }).unwrap()
}
}

#[cfg(feature = "bincode")]
pub fn get_config_data(bytes: &[u8]) -> Result<&[u8], bincode::Error> {
deserialize::<ConfigKeys>(bytes)
.and_then(|keys| serialized_size(&keys))
.map(|offset| &bytes[offset as usize..])
}

#[cfg(feature = "bincode")]
// utility for pre-made Accounts
pub fn create_config_account<T: ConfigState>(
keys: Vec<(Pubkey, bool)>,
config_data: &T,
lamports: u64,
) -> AccountSharedData {
let mut data = serialize(&ConfigKeys { keys }).unwrap();
data.extend_from_slice(&serialize(config_data).unwrap());
AccountSharedData::from(Account {
lamports,
data,
owner: id(),
..Account::default()
})
}