diff --git a/Cargo.lock b/Cargo.lock index 33bf1644a402a7..0414bbfb78dead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8450,6 +8450,7 @@ dependencies = [ "solana-sdk", "solana-sdk-ids", "solana-sdk-macro", + "solana-secp256k1-program", "solana-secp256k1-recover", "solana-secp256r1-program", "solana-seed-derivable", @@ -8486,6 +8487,36 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "solana-secp256k1-program" +version = "2.2.0" +dependencies = [ + "anyhow", + "bincode", + "digest 0.10.7", + "hex", + "libsecp256k1", + "rand 0.7.3", + "serde", + "serde_derive", + "sha3", + "solana-account-info", + "solana-feature-set", + "solana-hash", + "solana-instruction", + "solana-keccak-hasher", + "solana-keypair", + "solana-logger", + "solana-msg", + "solana-precompile-error", + "solana-program-error", + "solana-sdk", + "solana-sdk-ids", + "solana-secp256k1-program", + "solana-signer", + "solana-sysvar", +] + [[package]] name = "solana-secp256k1-recover" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index f8a008d6474944..056f379dad6d54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -150,6 +150,7 @@ members = [ "sdk/reserved-account-keys", "sdk/sanitize", "sdk/sdk-ids", + "sdk/secp256k1-program", "sdk/secp256r1-program", "sdk/seed-derivable", "sdk/seed-phrase", @@ -537,6 +538,7 @@ solana-runtime-transaction = { path = "runtime-transaction", version = "=2.2.0" solana-sdk = { path = "sdk", version = "=2.2.0" } solana-sdk-ids = { path = "sdk/sdk-ids", version = "=2.2.0" } solana-sdk-macro = { path = "sdk/macro", version = "=2.2.0" } +solana-secp256k1-program = { path = "sdk/secp256k1-program", version = "=2.2.0" } solana-secp256k1-recover = { path = "curves/secp256k1-recover", version = "=2.2.0", default-features = false } solana-send-transaction-service = { path = "send-transaction-service", version = "=2.2.0" } solana-short-vec = { path = "sdk/short-vec", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 6f4f4ab57c76e8..12ebfe25a73ea2 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -7161,6 +7161,7 @@ dependencies = [ "solana-sanitize", "solana-sdk-ids", "solana-sdk-macro", + "solana-secp256k1-program", "solana-secp256k1-recover", "solana-secp256r1-program", "solana-seed-derivable", @@ -7194,6 +7195,22 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "solana-secp256k1-program" +version = "2.2.0" +dependencies = [ + "bincode", + "digest 0.10.7", + "libsecp256k1 0.6.0", + "serde", + "serde_derive", + "sha3", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", +] + [[package]] name = "solana-secp256k1-recover" version = "2.2.0" diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index ada8f036b57acd..485a8d37e455a9 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -41,6 +41,7 @@ full = [ "dep:solana-precompile-error", "dep:solana-presigner", "dep:solana-quic-definitions", + "dep:solana-secp256k1-program", "dep:solana-seed-derivable", "dep:solana-seed-phrase", "dep:solana-signer", @@ -134,6 +135,7 @@ solana-reward-info = { workspace = true, features = ["serde"] } solana-sanitize = { workspace = true } solana-sdk-ids = { workspace = true } solana-sdk-macro = { workspace = true } +solana-secp256k1-program = { workspace = true, optional = true, features = ["bincode"] } solana-secp256k1-recover = { workspace = true } solana-secp256r1-program = { workspace = true, default-features = false } solana-seed-derivable = { workspace = true, optional = true } diff --git a/sdk/secp256k1-program/Cargo.toml b/sdk/secp256k1-program/Cargo.toml new file mode 100644 index 00000000000000..b22156f9322327 --- /dev/null +++ b/sdk/secp256k1-program/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "solana-secp256k1-program" +description = "Instructions for the Solana Secp256k1 native program." +documentation = "https://docs.rs/solana-secp256k1-program" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[dependencies] +bincode = { workspace = true, optional = true } +digest = { workspace = true } +libsecp256k1 = { workspace = true, features = ["hmac"] } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } +sha3 = { workspace = true } +solana-feature-set = { workspace = true, optional = true } +solana-instruction = { workspace = true, features = ["std"], optional = true } +solana-precompile-error = { workspace = true, optional = true } +solana-sdk-ids = { workspace = true, optional = true } + +[dev-dependencies] +anyhow = { workspace = true } +hex = { workspace = true } +rand0-7 = { workspace = true } +solana-account-info = { workspace = true } +solana-hash = { workspace = true } +solana-keccak-hasher = { workspace = true } +solana-keypair = { workspace = true } +solana-logger = { workspace = true } +solana-msg = { workspace = true } +solana-program-error = { workspace = true } +solana-sdk = { path = ".." } +solana-secp256k1-program = { path = ".", features = ["dev-context-only-utils"] } +solana-signer = { workspace = true } +solana-sysvar = { workspace = true } + +[features] +bincode = [ + "dep:bincode", + "dep:solana-feature-set", + "dep:solana-instruction", + "dep:solana-precompile-error", + "dep:solana-sdk-ids", + "serde", +] +dev-context-only-utils = ["bincode"] +serde = ["dep:serde", "dep:serde_derive"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] diff --git a/sdk/src/secp256k1_instruction.rs b/sdk/secp256k1-program/src/lib.rs similarity index 92% rename from sdk/src/secp256k1_instruction.rs rename to sdk/secp256k1-program/src/lib.rs index 958511d7febce5..cb491f033e028c 100644 --- a/sdk/src/secp256k1_instruction.rs +++ b/sdk/secp256k1-program/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] //! Instructions for the [secp256k1 native program][np]. //! //! [np]: https://docs.solanalabs.com/runtime/programs#secp256k1-program @@ -20,15 +21,15 @@ //! secp256k1 key recovery algorithm. Ethereum address can be created for //! secp256k1 public keys with the [`construct_eth_pubkey`] function. //! -//! [`keccak`]: crate::keccak +//! [`keccak`]: https://docs.rs/solana-sdk/latest/solana_sdk/keccak/index.html //! //! This instruction does not directly allow for key recovery as in Ethereum's //! [`ecrecover`] precompile. For that Solana provides the [`secp256k1_recover`] //! syscall. //! //! [secp256k1]: https://en.bitcoin.it/wiki/Secp256k1 -//! [`secp256k1_program`]: solana_program::secp256k1_program -//! [`secp256k1_recover`]: solana_program::secp256k1_recover +//! [`secp256k1_program`]: https://docs.rs/solana-program/latest/solana_program/secp256k1_program/index.html +//! [`secp256k1_recover`]: https://docs.rs/solana-secp256k1-recover //! [`ecrecover`]: https://docs.soliditylang.org/en/v0.8.14/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions //! //! Use cases for the secp256k1 instruction include: @@ -58,8 +59,8 @@ //! of one or more additional instructions, as long as those instructions are in //! the same transaction. //! -//! [`load_instruction_at_checked`]: crate::sysvar::instructions::load_instruction_at_checked -//! [`get_instruction_relative`]: crate::sysvar::instructions::get_instruction_relative +//! [`load_instruction_at_checked`]: https://docs.rs/solana-program/latest/solana_program/sysvar/instructions/fn.load_instruction_at_checked.html +//! [`get_instruction_relative`]: https://docs.rs/solana-program/latest/solana_program/sysvar/instructions/fn.get_instruction_relative.html //! //! Correct use of this program involves multiple steps, in client code and //! program code: @@ -85,7 +86,7 @@ //! - Check that the public keys and messages are the expected values per //! the program's requirements. //! -//! [`secp256k1_program::ID`]: crate::secp256k1_program::ID +//! [`secp256k1_program::ID`]: https://docs.rs/solana-program/latest/solana_program/secp256k1_program/constant.ID.html //! //! The signature, message, or Ethereum addresses may reside in the secp256k1 //! instruction data itself as additional data, their bytes following the bytes @@ -174,13 +175,13 @@ //! [`get_instruction_relative`] functions. Both of these functions check their //! sysvar argument to ensure it is the known instruction sysvar. //! -//! [is]: crate::sysvar::instructions +//! [is]: https://docs.rs/solana-program/latest/solana_program/sysvar/instructions/index.html //! //! Programs should _always_ verify that the secp256k1 program ID loaded through //! the instructions sysvar has the same value as in the [`secp256k1_program`] //! module. Again this prevents imposter programs. //! -//! [`secp256k1_program`]: crate::secp256k1_program +//! [`secp256k1_program`]: https://docs.rs/solana-program/latest/solana_program/secp256k1_program/index.html //! //! # Errors //! @@ -207,7 +208,7 @@ //! //! ```no_run //! mod secp256k1_defs { -//! use solana_program::program_error::ProgramError; +//! use solana_program_error::ProgramError; //! use std::iter::Iterator; //! //! pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; @@ -273,7 +274,7 @@ //! //! ```no_run //! # mod secp256k1_defs { -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_error::ProgramError; //! # use std::iter::Iterator; //! # //! # pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; @@ -321,14 +322,11 @@ //! # })) //! # } //! # } -//! use solana_program::{ -//! account_info::{next_account_info, AccountInfo}, -//! entrypoint::ProgramResult, -//! msg, -//! program_error::ProgramError, -//! secp256k1_program, -//! sysvar, -//! }; +//! use solana_account_info::{next_account_info, AccountInfo}; +//! use solana_msg::msg; +//! use solana_program_error::{ProgramError, ProgramResult}; +//! use solana_sdk_ids::secp256k1_program; +//! use solana_sysvar::instructions; //! //! /// An Ethereum address corresponding to a secp256k1 secret key that is //! /// authorized to sign our messages. @@ -349,14 +347,14 @@ //! //! // The instructions sysvar gives access to the instructions in the transaction. //! let instructions_sysvar_account = next_account_info(account_info_iter)?; -//! assert!(sysvar::instructions::check_id( +//! assert!(solana_sdk_ids::sysvar::instructions::check_id( //! instructions_sysvar_account.key //! )); //! //! // Load the secp256k1 instruction. //! // `new_secp256k1_instruction` generates an instruction that must be at index 0. //! let secp256k1_instr = -//! sysvar::instructions::load_instruction_at_checked(0, instructions_sysvar_account)?; +//! instructions::load_instruction_at_checked(0, instructions_sysvar_account)?; //! //! // Verify it is a secp256k1 instruction. //! // This is security-critical - what if the transaction uses an imposter secp256k1 program? @@ -419,14 +417,11 @@ //! ```no_run //! # use solana_sdk::example_mocks::solana_rpc_client; //! use anyhow::Result; +//! use solana_instruction::{AccountMeta, Instruction}; +//! use solana_keypair::Keypair; //! use solana_rpc_client::rpc_client::RpcClient; -//! use solana_sdk::{ -//! instruction::{AccountMeta, Instruction}, -//! secp256k1_instruction, -//! signature::{Keypair, Signer}, -//! sysvar, -//! transaction::Transaction, -//! }; +//! use solana_signer::Signer; +//! use solana_sdk::transaction::Transaction; //! //! fn demo_secp256k1_verify_basic( //! payer_keypair: &Keypair, @@ -438,13 +433,13 @@ //! // `secp256k_instruction::verify` (the secp256k1 program), this message is //! // keccak-hashed before signing. //! let msg = b"hello world"; -//! let secp256k1_instr = secp256k1_instruction::new_secp256k1_instruction(&secp256k1_secret_key, msg); +//! let secp256k1_instr = solana_secp256k1_program::new_secp256k1_instruction(&secp256k1_secret_key, msg); //! //! let program_instr = Instruction::new_with_bytes( //! program_keypair.pubkey(), //! &[], //! vec![ -//! AccountMeta::new_readonly(sysvar::instructions::ID, false) +//! AccountMeta::new_readonly(solana_sdk_ids::sysvar::instructions::ID, false) //! ], //! ); //! @@ -486,7 +481,7 @@ //! //! ```no_run //! # mod secp256k1_defs { -//! # use solana_program::program_error::ProgramError; +//! # use solana_program_error::ProgramError; //! # use std::iter::Iterator; //! # //! # pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; @@ -534,14 +529,11 @@ //! # })) //! # } //! # } -//! use solana_program::{ -//! account_info::{next_account_info, AccountInfo}, -//! entrypoint::ProgramResult, -//! msg, -//! program_error::ProgramError, -//! secp256k1_program, -//! sysvar, -//! }; +//! use solana_account_info::{next_account_info, AccountInfo}; +//! use solana_program_error::{ProgramError, ProgramResult}; +//! use solana_msg::msg; +//! use solana_sdk_ids::secp256k1_program; +//! use solana_sysvar::instructions; //! //! /// A struct to hold the values specified in the `SecpSignatureOffsets` struct. //! struct SecpSignature { @@ -561,15 +553,15 @@ //! ) -> Result, ProgramError> { //! let mut sigs = vec![]; //! for offsets in secp256k1_defs::iter_signature_offsets(secp256k1_instr_data)? { -//! let signature_instr = sysvar::instructions::load_instruction_at_checked( +//! let signature_instr = instructions::load_instruction_at_checked( //! offsets.signature_instruction_index as usize, //! instructions_sysvar_account, //! )?; -//! let eth_address_instr = sysvar::instructions::load_instruction_at_checked( +//! let eth_address_instr = instructions::load_instruction_at_checked( //! offsets.eth_address_instruction_index as usize, //! instructions_sysvar_account, //! )?; -//! let message_instr = sysvar::instructions::load_instruction_at_checked( +//! let message_instr = instructions::load_instruction_at_checked( //! offsets.message_instruction_index as usize, //! instructions_sysvar_account, //! )?; @@ -606,12 +598,12 @@ //! let account_info_iter = &mut accounts.iter(); //! //! let instructions_sysvar_account = next_account_info(account_info_iter)?; -//! assert!(sysvar::instructions::check_id( +//! assert!(solana_sdk_ids::sysvar::instructions::check_id( //! instructions_sysvar_account.key //! )); //! //! let secp256k1_instr = -//! sysvar::instructions::get_instruction_relative(-1, instructions_sysvar_account)?; +//! instructions::get_instruction_relative(-1, instructions_sysvar_account)?; //! //! assert!(secp256k1_program::check_id(&secp256k1_instr.program_id)); //! @@ -635,18 +627,16 @@ //! ```no_run //! # use solana_sdk::example_mocks::solana_rpc_client; //! use anyhow::Result; +//! use solana_instruction::{AccountMeta, Instruction}; //! use solana_rpc_client::rpc_client::RpcClient; -//! use solana_sdk::{ -//! instruction::{AccountMeta, Instruction}, -//! keccak, -//! secp256k1_instruction::{ -//! self, SecpSignatureOffsets, HASHED_PUBKEY_SERIALIZED_SIZE, -//! SIGNATURE_OFFSETS_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE, -//! }, -//! signature::{Keypair, Signer}, -//! sysvar, -//! transaction::Transaction, +//! use solana_secp256k1_program::{ +//! construct_eth_pubkey, SecpSignatureOffsets, HASHED_PUBKEY_SERIALIZED_SIZE, +//! SIGNATURE_OFFSETS_SERIALIZED_SIZE, SIGNATURE_SERIALIZED_SIZE, //! }; +//! use solana_signer::Signer; +//! use solana_keypair::Keypair; +//! use solana_sysvar::instructions; +//! use solana_sdk::transaction::Transaction; //! //! /// A struct to hold the values specified in the `SecpSignatureOffsets` struct. //! struct SecpSignature { @@ -736,7 +726,7 @@ //! let secret_key = libsecp256k1::SecretKey::random(&mut rand0_7::thread_rng()); //! let message = format!("hello world {}", idx).into_bytes(); //! let message_hash = { -//! let mut hasher = keccak::Hasher::default(); +//! let mut hasher = solana_keccak_hasher::Hasher::default(); //! hasher.hash(&message); //! hasher.result() //! }; @@ -746,7 +736,7 @@ //! let recovery_id = recovery_id.serialize(); //! //! let public_key = libsecp256k1::PublicKey::from_secret_key(&secret_key); -//! let eth_address = secp256k1_instruction::construct_eth_pubkey(&public_key); +//! let eth_address = construct_eth_pubkey(&public_key); //! //! signatures.push(SecpSignature { //! signature, @@ -758,7 +748,7 @@ //! //! let secp256k1_instr_data = make_secp256k1_instruction_data(&signatures, 0)?; //! let secp256k1_instr = Instruction::new_with_bytes( -//! solana_sdk::secp256k1_program::ID, +//! solana_sdk_ids::secp256k1_program::ID, //! &secp256k1_instr_data, //! vec![], //! ); @@ -767,7 +757,7 @@ //! program_keypair.pubkey(), //! &[], //! vec![ -//! AccountMeta::new_readonly(sysvar::instructions::ID, false) +//! AccountMeta::new_readonly(solana_sdk_ids::sysvar::instructions::ID, false) //! ], //! ); //! @@ -785,15 +775,11 @@ //! } //! ``` -#![cfg(feature = "full")] - -use { - digest::Digest, - serde_derive::{Deserialize, Serialize}, - solana_feature_set::FeatureSet, - solana_instruction::Instruction, - solana_precompile_error::PrecompileError, -}; +use digest::Digest; +#[cfg(feature = "serde")] +use serde_derive::{Deserialize, Serialize}; +#[cfg(feature = "bincode")] +use {solana_instruction::Instruction, solana_precompile_error::PrecompileError}; pub const HASHED_PUBKEY_SERIALIZED_SIZE: usize = 20; pub const SIGNATURE_SERIALIZED_SIZE: usize = 64; @@ -805,7 +791,8 @@ pub const DATA_START: usize = SIGNATURE_OFFSETS_SERIALIZED_SIZE + 1; /// See the [module documentation][md] for a complete description. /// /// [md]: self -#[derive(Default, Serialize, Deserialize, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] +#[derive(Default, Debug, Eq, PartialEq)] pub struct SecpSignatureOffsets { /// Offset to 64-byte signature plus 1-byte recovery ID. pub signature_offset: u16, @@ -838,7 +825,8 @@ pub struct SecpSignatureOffsets { /// /// `message_arr` is hashed with the [`keccak`] hash function prior to signing. /// -/// [`keccak`]: crate::keccak +/// [`keccak`]: https://docs.rs/solana-sdk/latest/solana_sdk/keccak/index.html +#[cfg(feature = "bincode")] pub fn new_secp256k1_instruction( priv_key: &libsecp256k1::SecretKey, message_arr: &[u8], @@ -893,7 +881,7 @@ pub fn new_secp256k1_instruction( bincode::serialize_into(writer, &offsets).unwrap(); Instruction { - program_id: solana_sdk::secp256k1_program::id(), + program_id: solana_sdk_ids::secp256k1_program::id(), accounts: vec![], data: instruction_data, } @@ -922,10 +910,11 @@ pub fn construct_eth_pubkey( /// disable a few minor additional checks that were activated on chain /// subsequent to the addition of the secp256k1 native program. For many /// purposes passing `FeatureSet::all_enabled()` is reasonable. +#[cfg(feature = "bincode")] pub fn verify( data: &[u8], instruction_datas: &[&[u8]], - _feature_set: &FeatureSet, + _feature_set: &solana_feature_set::FeatureSet, ) -> Result<(), PrecompileError> { if data.is_empty() { return Err(PrecompileError::InvalidInstructionDataSize); @@ -1007,6 +996,7 @@ pub fn verify( Ok(()) } +#[cfg(feature = "bincode")] fn get_data_slice<'a>( instruction_datas: &'a [&[u8]], instruction_index: u8, @@ -1031,16 +1021,13 @@ fn get_data_slice<'a>( pub mod test { use { super::*, - crate::{ - hash::Hash, - keccak, - secp256k1_instruction::{ - new_secp256k1_instruction, SecpSignatureOffsets, SIGNATURE_OFFSETS_SERIALIZED_SIZE, - }, - signature::{Keypair, Signer}, - transaction::Transaction, - }, rand0_7::{thread_rng, Rng}, + solana_feature_set::FeatureSet, + solana_hash::Hash, + solana_keccak_hasher as keccak, + solana_keypair::Keypair, + solana_sdk::transaction::Transaction, + solana_signer::Signer, }; fn test_case( diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index b774a97957eab4..99ab1d07c99dab 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -93,7 +93,6 @@ pub mod reward_type { pub use solana_reward_info::RewardType; } pub mod rpc_port; -pub mod secp256k1_instruction; pub mod shred_version; pub mod signature; pub mod signer; @@ -185,6 +184,9 @@ pub use solana_sdk_macro::declare_deprecated_id; pub use solana_sdk_macro::declare_id; /// Convenience macro to define multiple static public keys. pub use solana_sdk_macro::pubkeys; +#[deprecated(since = "2.2.0", note = "Use `solana-secp256k1-program` crate instead")] +#[cfg(feature = "full")] +pub use solana_secp256k1_program as secp256k1_instruction; #[deprecated(since = "2.1.0", note = "Use `solana-secp256k1-recover` crate instead")] pub use solana_secp256k1_recover as secp256k1_recover; #[deprecated(since = "2.2.0", note = "Use `solana-serde` crate instead")] diff --git a/svm/examples/Cargo.lock b/svm/examples/Cargo.lock index 7063039b4739bd..5fcf4a90f3c653 100644 --- a/svm/examples/Cargo.lock +++ b/svm/examples/Cargo.lock @@ -6496,6 +6496,7 @@ dependencies = [ "solana-sanitize", "solana-sdk-ids", "solana-sdk-macro", + "solana-secp256k1-program", "solana-secp256k1-recover", "solana-secp256r1-program", "solana-seed-derivable", @@ -6529,6 +6530,22 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "solana-secp256k1-program" +version = "2.2.0" +dependencies = [ + "bincode", + "digest 0.10.7", + "libsecp256k1", + "serde", + "serde_derive", + "sha3", + "solana-feature-set", + "solana-instruction", + "solana-precompile-error", + "solana-sdk-ids", +] + [[package]] name = "solana-secp256k1-recover" version = "2.2.0"