diff --git a/Cargo.lock b/Cargo.lock index ccf04d80caa385..437cbcb797baca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7738,6 +7738,21 @@ dependencies = [ "solana-decode-error", ] +[[package]] +name = "solana-precompiles" +version = "2.2.0" +dependencies = [ + "lazy_static", + "solana-ed25519-program", + "solana-feature-set", + "solana-precompile-error", + "solana-program", + "solana-pubkey", + "solana-sdk-ids", + "solana-secp256k1-program", + "solana-secp256r1-program", +] + [[package]] name = "solana-presigner" version = "2.2.0" @@ -8456,6 +8471,7 @@ dependencies = [ "solana-native-token", "solana-packet", "solana-precompile-error", + "solana-precompiles", "solana-presigner", "solana-program", "solana-program-memory", diff --git a/Cargo.toml b/Cargo.toml index 2281583906b67d..f1f845d8bb0e63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -138,6 +138,7 @@ members = [ "sdk/package-metadata-macro", "sdk/packet", "sdk/precompile-error", + "sdk/precompiles", "sdk/presigner", "sdk/program", "sdk/program-entrypoint", @@ -498,6 +499,7 @@ solana-perf = { path = "perf", version = "=2.2.0" } solana-poh = { path = "poh", version = "=2.2.0" } solana-poseidon = { path = "poseidon", version = "=2.2.0" } solana-precompile-error = { path = "sdk/precompile-error", version = "=2.2.0" } +solana-precompiles = { path = "sdk/precompiles", version = "=2.2.0" } solana-presigner = { path = "sdk/presigner", version = "=2.2.0" } solana-program = { path = "sdk/program", version = "=2.2.0", default-features = false } solana-program-error = { path = "sdk/program-error", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 16b10b0b451bbd..9d1f426bf64bdb 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -6081,6 +6081,21 @@ dependencies = [ "solana-decode-error", ] +[[package]] +name = "solana-precompiles" +version = "2.2.0" +dependencies = [ + "lazy_static", + "solana-ed25519-program", + "solana-feature-set", + "solana-precompile-error", + "solana-program", + "solana-pubkey", + "solana-sdk-ids", + "solana-secp256k1-program", + "solana-secp256r1-program", +] + [[package]] name = "solana-presigner" version = "2.2.0" @@ -7167,6 +7182,7 @@ dependencies = [ "solana-native-token", "solana-packet", "solana-precompile-error", + "solana-precompiles", "solana-presigner", "solana-program", "solana-program-memory", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 1cc7507ce308d5..7ee6a5e8c9a037 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -40,6 +40,7 @@ full = [ "dep:solana-compute-budget-interface", "dep:solana-keypair", "dep:solana-precompile-error", + "dep:solana-precompiles", "dep:solana-presigner", "dep:solana-quic-definitions", "dep:solana-secp256k1-program", @@ -75,7 +76,7 @@ frozen-abi = [ "solana-transaction-error/frozen-abi" ] # Enables the "vendored" feature of openssl inside of secp256r1-program -openssl-vendored = ["solana-secp256r1-program/openssl-vendored"] +openssl-vendored = ["solana-precompiles/openssl-vendored"] [dependencies] bincode = { workspace = true } @@ -135,6 +136,7 @@ solana-keypair = { workspace = true, optional = true, features = ["seed-derivabl solana-native-token = { workspace = true } solana-packet = { workspace = true, features = ["bincode", "serde"] } solana-precompile-error = { workspace = true, optional = true } +solana-precompiles = { workspace = true, optional = true } solana-presigner = { workspace = true, optional = true } solana-program = { workspace = true } solana-program-memory = { workspace = true } diff --git a/sdk/precompiles/Cargo.toml b/sdk/precompiles/Cargo.toml new file mode 100644 index 00000000000000..c2cf3a946cc316 --- /dev/null +++ b/sdk/precompiles/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "solana-precompiles" +description = "Solana precompiled programs." +documentation = "https://docs.rs/solana-precompiles" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +lazy_static = { workspace = true } +solana-ed25519-program = { workspace = true } +solana-feature-set = { workspace = true } +solana-precompile-error = { workspace = true } +solana-program = { workspace = true, default-features = false } +solana-pubkey = { workspace = true } +solana-sdk-ids = { workspace = true } +solana-secp256k1-program = { workspace = true, features = ["bincode"] } +solana-secp256r1-program = { workspace = true, default-features = false } + +[features] +# Enables the "vendored" feature of openssl inside of secp256r1-program +openssl-vendored = ["solana-secp256r1-program/openssl-vendored"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[lints] +workspace = true diff --git a/sdk/precompiles/src/lib.rs b/sdk/precompiles/src/lib.rs new file mode 100644 index 00000000000000..eee49df3adc4e0 --- /dev/null +++ b/sdk/precompiles/src/lib.rs @@ -0,0 +1,115 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +use { + lazy_static::lazy_static, solana_feature_set::FeatureSet, + solana_precompile_error::PrecompileError, solana_program::instruction::CompiledInstruction, + solana_pubkey::Pubkey, +}; + +/// All precompiled programs must implement the `Verify` function +pub type Verify = fn(&[u8], &[&[u8]], &FeatureSet) -> std::result::Result<(), PrecompileError>; + +/// Information on a precompiled program +pub struct Precompile { + /// Program id + pub program_id: Pubkey, + /// Feature to enable on, `None` indicates always enabled + pub feature: Option, + /// Verification function + pub verify_fn: Verify, +} +impl Precompile { + /// Creates a new `Precompile` + pub fn new(program_id: Pubkey, feature: Option, verify_fn: Verify) -> Self { + Precompile { + program_id, + feature, + verify_fn, + } + } + /// Check if a program id is this precompiled program + pub fn check_id(&self, program_id: &Pubkey, is_enabled: F) -> bool + where + F: Fn(&Pubkey) -> bool, + { + self.feature + .map_or(true, |ref feature_id| is_enabled(feature_id)) + && self.program_id == *program_id + } + /// Verify this precompiled program + pub fn verify( + &self, + data: &[u8], + instruction_datas: &[&[u8]], + feature_set: &FeatureSet, + ) -> std::result::Result<(), PrecompileError> { + (self.verify_fn)(data, instruction_datas, feature_set) + } +} + +lazy_static! { + /// The list of all precompiled programs + static ref PRECOMPILES: Vec = vec![ + Precompile::new( + solana_sdk_ids::secp256k1_program::id(), + None, // always enabled + solana_secp256k1_program::verify, + ), + Precompile::new( + solana_sdk_ids::ed25519_program::id(), + None, // always enabled + solana_ed25519_program::verify, + ), + Precompile::new( + solana_sdk_ids::secp256r1_program::id(), + Some(solana_feature_set::enable_secp256r1_precompile::id()), + solana_secp256r1_program::verify, + ) + ]; +} + +/// Check if a program is a precompiled program +pub fn is_precompile(program_id: &Pubkey, is_enabled: F) -> bool +where + F: Fn(&Pubkey) -> bool, +{ + PRECOMPILES + .iter() + .any(|precompile| precompile.check_id(program_id, |feature_id| is_enabled(feature_id))) +} + +/// Find an enabled precompiled program +pub fn get_precompile(program_id: &Pubkey, is_enabled: F) -> Option<&Precompile> +where + F: Fn(&Pubkey) -> bool, +{ + PRECOMPILES + .iter() + .find(|precompile| precompile.check_id(program_id, |feature_id| is_enabled(feature_id))) +} + +pub fn get_precompiles<'a>() -> &'a [Precompile] { + &PRECOMPILES +} + +/// Check that a program is precompiled and if so verify it +pub fn verify_if_precompile( + program_id: &Pubkey, + precompile_instruction: &CompiledInstruction, + all_instructions: &[CompiledInstruction], + feature_set: &FeatureSet, +) -> Result<(), PrecompileError> { + for precompile in PRECOMPILES.iter() { + if precompile.check_id(program_id, |feature_id| feature_set.is_active(feature_id)) { + let instruction_datas: Vec<_> = all_instructions + .iter() + .map(|instruction| instruction.data.as_ref()) + .collect(); + return precompile.verify( + &precompile_instruction.data, + &instruction_datas, + feature_set, + ); + } + } + Ok(()) +} diff --git a/sdk/src/precompiles.rs b/sdk/src/precompiles.rs index 5b40397bb75301..5fbbc33d6d833d 100644 --- a/sdk/src/precompiles.rs +++ b/sdk/src/precompiles.rs @@ -4,117 +4,7 @@ #[deprecated(since = "2.1.0", note = "Use `solana-precompile-error` crate instead.")] pub use solana_precompile_error::PrecompileError; -use { - lazy_static::lazy_static, solana_feature_set::FeatureSet, - solana_program::instruction::CompiledInstruction, solana_pubkey::Pubkey, - solana_secp256r1_program as secp256r1_program, +#[deprecated(since = "2.2.0", note = "Use `solana-precompiles` crate instead.")] +pub use solana_precompiles::{ + get_precompile, get_precompiles, is_precompile, verify_if_precompile, Precompile, Verify, }; - -/// All precompiled programs must implement the `Verify` function -pub type Verify = fn(&[u8], &[&[u8]], &FeatureSet) -> std::result::Result<(), PrecompileError>; - -/// Information on a precompiled program -pub struct Precompile { - /// Program id - pub program_id: Pubkey, - /// Feature to enable on, `None` indicates always enabled - pub feature: Option, - /// Verification function - pub verify_fn: Verify, -} -impl Precompile { - /// Creates a new `Precompile` - pub fn new(program_id: Pubkey, feature: Option, verify_fn: Verify) -> Self { - Precompile { - program_id, - feature, - verify_fn, - } - } - /// Check if a program id is this precompiled program - pub fn check_id(&self, program_id: &Pubkey, is_enabled: F) -> bool - where - F: Fn(&Pubkey) -> bool, - { - self.feature - .map_or(true, |ref feature_id| is_enabled(feature_id)) - && self.program_id == *program_id - } - /// Verify this precompiled program - pub fn verify( - &self, - data: &[u8], - instruction_datas: &[&[u8]], - feature_set: &FeatureSet, - ) -> std::result::Result<(), PrecompileError> { - (self.verify_fn)(data, instruction_datas, feature_set) - } -} - -lazy_static! { - /// The list of all precompiled programs - static ref PRECOMPILES: Vec = vec![ - Precompile::new( - crate::secp256k1_program::id(), - None, // always enabled - crate::secp256k1_instruction::verify, - ), - Precompile::new( - crate::ed25519_program::id(), - None, // always enabled - crate::ed25519_instruction::verify, - ), - Precompile::new( - secp256r1_program::id(), - Some(solana_feature_set::enable_secp256r1_precompile::id()), - secp256r1_program::verify, - ) - ]; -} - -/// Check if a program is a precompiled program -pub fn is_precompile(program_id: &Pubkey, is_enabled: F) -> bool -where - F: Fn(&Pubkey) -> bool, -{ - PRECOMPILES - .iter() - .any(|precompile| precompile.check_id(program_id, |feature_id| is_enabled(feature_id))) -} - -/// Find an enabled precompiled program -pub fn get_precompile(program_id: &Pubkey, is_enabled: F) -> Option<&Precompile> -where - F: Fn(&Pubkey) -> bool, -{ - PRECOMPILES - .iter() - .find(|precompile| precompile.check_id(program_id, |feature_id| is_enabled(feature_id))) -} - -pub fn get_precompiles<'a>() -> &'a [Precompile] { - &PRECOMPILES -} - -/// Check that a program is precompiled and if so verify it -pub fn verify_if_precompile( - program_id: &Pubkey, - precompile_instruction: &CompiledInstruction, - all_instructions: &[CompiledInstruction], - feature_set: &FeatureSet, -) -> Result<(), PrecompileError> { - for precompile in PRECOMPILES.iter() { - if precompile.check_id(program_id, |feature_id| feature_set.is_active(feature_id)) { - let instruction_datas: Vec<_> = all_instructions - .iter() - .map(|instruction| instruction.data.as_ref()) - .collect(); - return precompile.verify( - &precompile_instruction.data, - &instruction_datas, - feature_set, - ); - } - } - Ok(()) -} diff --git a/svm/examples/Cargo.lock b/svm/examples/Cargo.lock index 595b57fe4ea38e..dec2a6067b65bf 100644 --- a/svm/examples/Cargo.lock +++ b/svm/examples/Cargo.lock @@ -5901,6 +5901,21 @@ dependencies = [ "solana-decode-error", ] +[[package]] +name = "solana-precompiles" +version = "2.2.0" +dependencies = [ + "lazy_static", + "solana-ed25519-program", + "solana-feature-set", + "solana-precompile-error", + "solana-program", + "solana-pubkey", + "solana-sdk-ids", + "solana-secp256k1-program", + "solana-secp256r1-program", +] + [[package]] name = "solana-presigner" version = "2.2.0" @@ -6502,6 +6517,7 @@ dependencies = [ "solana-native-token", "solana-packet", "solana-precompile-error", + "solana-precompiles", "solana-presigner", "solana-program", "solana-program-memory",