diff --git a/Cargo.lock b/Cargo.lock index f755aabb..7bf38d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6612,8 +6612,11 @@ dependencies = [ "num-derive", "num-traits", "serial_test", - "solana-program", - "solana-sdk", + "solana-decode-error", + "solana-msg", + "solana-program-error", + "solana-sha256-hasher", + "solana-sysvar", "spl-program-error-derive", "thiserror 2.0.11", ] diff --git a/program-error/Cargo.toml b/program-error/Cargo.toml index 9ae7911d..e7811761 100644 --- a/program-error/Cargo.toml +++ b/program-error/Cargo.toml @@ -10,14 +10,17 @@ edition = "2021" [dependencies] num-derive = "0.4" num-traits = "0.2" -solana-program = "2.2.1" +solana-decode-error = "2.2.1" +solana-msg = "2.2.1" +solana-program-error = "2.2.1" spl-program-error-derive = { version = "0.4.1", path = "./derive" } thiserror = "2.0" [dev-dependencies] lazy_static = "1.5" serial_test = "3.2" -solana-sdk = "2.1.0" +solana-sha256-hasher = "2.2.1" +solana-sysvar = "2.2.1" [lib] crate-type = ["cdylib", "lib"] diff --git a/program-error/README.md b/program-error/README.md index 5ada5934..7cf74d2d 100644 --- a/program-error/README.md +++ b/program-error/README.md @@ -2,9 +2,9 @@ Macros for implementing error-based traits on enums. -- `#[derive(IntoProgramError)]`: automatically derives the trait `From for solana_program::program_error::ProgramError`. -- `#[derive(DecodeError)]`: automatically derives the trait `solana_program::decode_error::DecodeError`. -- `#[derive(PrintProgramError)]`: automatically derives the trait `solana_program::program_error::PrintProgramError`. +- `#[derive(IntoProgramError)]`: automatically derives the trait `From for solana_program_error::ProgramError`. +- `#[derive(DecodeError)]`: automatically derives the trait `solana_decode_error::DecodeError`. +- `#[derive(PrintProgramError)]`: automatically derives the trait `solana_program_error::PrintProgramError`. - `#[spl_program_error]`: Automatically derives all below traits: - `Clone` - `Debug` @@ -18,7 +18,7 @@ Macros for implementing error-based traits on enums. ### `#[derive(IntoProgramError)]` -This derive macro automatically derives the trait `From for solana_program::program_error::ProgramError`. +This derive macro automatically derives the trait `From for solana_program_error::ProgramError`. Your enum must implement the following traits in order for this macro to work: @@ -48,7 +48,7 @@ pub enum ExampleError { ### `#[derive(DecodeError)]` -This derive macro automatically derives the trait `solana_program::decode_error::DecodeError`. +This derive macro automatically derives the trait `solana_decode_error::DecodeError`. Your enum must implement the following traits in order for this macro to work: @@ -86,7 +86,7 @@ pub enum ExampleError { ### `#[derive(PrintProgramError)]` -This derive macro automatically derives the trait `solana_program::program_error::PrintProgramError`. +This derive macro automatically derives the trait `solana_program_error::PrintProgramError`. Your enum must implement the following traits in order for this macro to work: @@ -149,10 +149,10 @@ It also imports the required crates so you don't have to in your program: Just annotate your enum... ```rust -use solana_program_error_derive::*; +use spl_program_error_derive::*; /// Example error -#[solana_program_error] +#[spl_program_error] pub enum ExampleError { /// Mint has no mint authority #[error("Mint has no mint authority")] @@ -261,29 +261,29 @@ impl ::core::cmp::PartialEq for ExampleError { __self_tag == __arg1_tag } } -impl From for solana_program::program_error::ProgramError { +impl From for solana_program_error::ProgramError { fn from(e: ExampleError) -> Self { - solana_program::program_error::ProgramError::Custom(e as u32) + solana_program_error::ProgramError::Custom(e as u32) } } -impl solana_program::decode_error::DecodeError for ExampleError { +impl solana_decode_error::DecodeError for ExampleError { fn type_of() -> &'static str { "ExampleError" } } -impl solana_program::program_error::PrintProgramError for ExampleError { +impl solana_program_error::PrintProgramError for ExampleError { fn print(&self) where - E: 'static + std::error::Error + solana_program::decode_error::DecodeError - + solana_program::program_error::PrintProgramError + E: 'static + std::error::Error + solana_decode_error::DecodeError + + solana_program_error::PrintProgramError + num_traits::FromPrimitive, { match self { ExampleError::MintHasNoMintAuthority => { - ::solana_program::log::sol_log("Mint has no mint authority") + ::solana_msg::sol_log("Mint has no mint authority") } ExampleError::IncorrectMintAuthority => { - ::solana_program::log::sol_log( + ::solana_msg::sol_log( "Incorrect mint authority has signed the instruction", ) } diff --git a/program-error/derive/src/lib.rs b/program-error/derive/src/lib.rs index 026b0eb7..555006a9 100644 --- a/program-error/derive/src/lib.rs +++ b/program-error/derive/src/lib.rs @@ -23,7 +23,7 @@ use { syn::{parse_macro_input, ItemEnum}, }; -/// Derive macro to add `Into` +/// Derive macro to add `Into` /// trait #[proc_macro_derive(IntoProgramError)] pub fn into_program_error(input: TokenStream) -> TokenStream { @@ -33,14 +33,14 @@ pub fn into_program_error(input: TokenStream) -> TokenStream { .into() } -/// Derive macro to add `solana_program::decode_error::DecodeError` trait +/// Derive macro to add `solana_decode_error::DecodeError` trait #[proc_macro_derive(DecodeError)] pub fn decode_error(input: TokenStream) -> TokenStream { let ItemEnum { ident, .. } = parse_macro_input!(input as ItemEnum); MacroType::DecodeError { ident }.generate_tokens().into() } -/// Derive macro to add `solana_program::program_error::PrintProgramError` trait +/// Derive macro to add `solana_program_error::PrintProgramError` trait #[proc_macro_derive(PrintProgramError)] pub fn print_program_error(input: TokenStream) -> TokenStream { let ItemEnum { @@ -60,9 +60,9 @@ pub fn print_program_error(input: TokenStream) -> TokenStream { /// - `PartialEq` /// - `thiserror::Error` /// - `num_derive::FromPrimitive` -/// - `Into` -/// - `solana_program::decode_error::DecodeError` -/// - `solana_program::program_error::PrintProgramError` +/// - `Into` +/// - `solana_decode_error::DecodeError` +/// - `solana_program_error::PrintProgramError` /// /// Optionally, you can add `hash_error_code_start: u32` argument to create /// a unique `u32` _starting_ error codes from the names of the enum variants. diff --git a/program-error/derive/src/macro_impl.rs b/program-error/derive/src/macro_impl.rs index 1f90cd55..538a174b 100644 --- a/program-error/derive/src/macro_impl.rs +++ b/program-error/derive/src/macro_impl.rs @@ -1,7 +1,7 @@ //! The actual token generator for the macro use { - crate::parser::{SolanaProgram, SplProgramErrorArgs}, + crate::parser::{SolanaDecodeError, SolanaProgramError, SplProgramErrorArgs}, proc_macro2::Span, quote::quote, sha2::{Digest, Sha256}, @@ -36,36 +36,42 @@ pub enum MacroType { impl MacroType { /// Generates the corresponding tokens based on variant selection pub fn generate_tokens(&mut self) -> proc_macro2::TokenStream { - let default_solana_program = SolanaProgram::default(); + let default_solana_program_error = SolanaProgramError::default(); + let default_solana_decode_error = SolanaDecodeError::default(); match self { - Self::IntoProgramError { ident } => into_program_error(ident, &default_solana_program), - Self::DecodeError { ident } => decode_error(ident, &default_solana_program), - Self::PrintProgramError { ident, variants } => { - print_program_error(ident, variants, &default_solana_program) + Self::IntoProgramError { ident } => { + into_program_error(ident, &default_solana_program_error) } + Self::DecodeError { ident } => decode_error(ident, &default_solana_decode_error), + Self::PrintProgramError { ident, variants } => print_program_error( + ident, + variants, + &default_solana_program_error, + &default_solana_decode_error, + ), Self::SplProgramError { args, item_enum } => spl_program_error(args, item_enum), } } } /// Builds the implementation of -/// `Into` More specifically, -/// implements `From for solana_program::program_error::ProgramError` -pub fn into_program_error(ident: &Ident, import: &SolanaProgram) -> proc_macro2::TokenStream { +/// `Into` More specifically, +/// implements `From for solana_program_error::ProgramError` +pub fn into_program_error(ident: &Ident, import: &SolanaProgramError) -> proc_macro2::TokenStream { let this_impl = quote! { - impl From<#ident> for #import::program_error::ProgramError { + impl From<#ident> for #import::ProgramError { fn from(e: #ident) -> Self { - #import::program_error::ProgramError::Custom(e as u32) + #import::ProgramError::Custom(e as u32) } } }; import.wrap(this_impl) } -/// Builds the implementation of `solana_program::decode_error::DecodeError` -pub fn decode_error(ident: &Ident, import: &SolanaProgram) -> proc_macro2::TokenStream { +/// Builds the implementation of `solana_decode_error::DecodeError` +pub fn decode_error(ident: &Ident, import: &SolanaDecodeError) -> proc_macro2::TokenStream { let this_impl = quote! { - impl #import::decode_error::DecodeError for #ident { + impl #import::DecodeError for #ident { fn type_of() -> &'static str { stringify!(#ident) } @@ -75,11 +81,12 @@ pub fn decode_error(ident: &Ident, import: &SolanaProgram) -> proc_macro2::Token } /// Builds the implementation of -/// `solana_program::program_error::PrintProgramError` +/// `solana_program_error::PrintProgramError` pub fn print_program_error( ident: &Ident, variants: &Punctuated, - import: &SolanaProgram, + program_error_import: &SolanaProgramError, + decode_error_import: &SolanaDecodeError, ) -> proc_macro2::TokenStream { let ppe_match_arms = variants.iter().map(|variant| { let variant_ident = &variant.ident; @@ -87,18 +94,18 @@ pub fn print_program_error( .unwrap_or_else(|| String::from("Unknown custom program error")); quote! { #ident::#variant_ident => { - #import::msg!(#error_msg) + ::solana_msg::msg!(#error_msg) } } }); let this_impl = quote! { - impl #import::program_error::PrintProgramError for #ident { + impl #program_error_import::PrintProgramError for #ident { fn print(&self) where E: 'static + std::error::Error - + #import::decode_error::DecodeError - + #import::program_error::PrintProgramError + + #decode_error_import::DecodeError + + #program_error_import::PrintProgramError + num_traits::FromPrimitive, { match self { @@ -107,7 +114,7 @@ pub fn print_program_error( } } }; - import.wrap(this_impl) + program_error_import.wrap(decode_error_import.wrap(this_impl)) } /// Helper to parse out the string literal from the `#[error(..)]` attribute @@ -135,9 +142,14 @@ pub fn spl_program_error( let ident = &item_enum.ident; let variants = &item_enum.variants; - let into_program_error = into_program_error(ident, &args.import); - let decode_error = decode_error(ident, &args.import); - let print_program_error = print_program_error(ident, variants, &args.import); + let into_program_error = into_program_error(ident, &args.program_error_import); + let decode_error = decode_error(ident, &args.decode_error_import); + let print_program_error = print_program_error( + ident, + variants, + &args.program_error_import, + &args.decode_error_import, + ); quote! { #[repr(u32)] diff --git a/program-error/derive/src/parser.rs b/program-error/derive/src/parser.rs index 7e01cb72..a22864e2 100644 --- a/program-error/derive/src/parser.rs +++ b/program-error/derive/src/parser.rs @@ -12,37 +12,68 @@ use { /// Possible arguments to the `#[spl_program_error]` attribute pub struct SplProgramErrorArgs { - /// Whether to hash the error codes using `solana_program::hash` + /// Whether to hash the error codes using sha-256 /// or to use the default error code assigned by `num_traits`. pub hash_error_code_start: Option, - /// Crate to use for solana_program - pub import: SolanaProgram, + /// Crate to use for solana_program_error + pub program_error_import: SolanaProgramError, + /// Crate to use for solana_decode_error + pub decode_error_import: SolanaDecodeError, } -/// Struct representing the path to a `solana_program` crate, which may be -/// renamed or otherwise. -pub struct SolanaProgram { +/// Struct representing the path to a `solana_program_error` crate, which may +/// be renamed or otherwise. +pub struct SolanaProgramError { import: Ident, explicit: bool, } -impl quote::ToTokens for SolanaProgram { +impl quote::ToTokens for SolanaProgramError { fn to_tokens(&self, tokens: &mut TokenStream) { self.import.to_tokens(tokens); } } -impl SolanaProgram { +impl SolanaProgramError { pub fn wrap(&self, output: TokenStream) -> TokenStream { if self.explicit { output } else { - anon_const_trick(output) + program_error_anon_const_trick(output) } } } -impl Default for SolanaProgram { +impl Default for SolanaProgramError { fn default() -> Self { Self { - import: Ident::new("_solana_program", Span::call_site()), + import: Ident::new("_solana_program_error", Span::call_site()), + explicit: false, + } + } +} + +/// Struct representing the path to a `solana_decode_error` crate, which may +/// be renamed or otherwise. +pub struct SolanaDecodeError { + import: Ident, + explicit: bool, +} +impl quote::ToTokens for SolanaDecodeError { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.import.to_tokens(tokens); + } +} +impl SolanaDecodeError { + pub fn wrap(&self, output: TokenStream) -> TokenStream { + if self.explicit { + output + } else { + decode_error_anon_const_trick(output) + } + } +} +impl Default for SolanaDecodeError { + fn default() -> Self { + Self { + import: Ident::new("_solana_decode_error", Span::call_site()), explicit: false, } } @@ -51,14 +82,21 @@ impl Default for SolanaProgram { impl Parse for SplProgramErrorArgs { fn parse(input: ParseStream) -> syn::Result { let mut hash_error_code_start = None; - let mut import = None; + let mut program_error_import = None; + let mut decode_error_import = None; while !input.is_empty() { match SplProgramErrorArgParser::parse(input)? { SplProgramErrorArgParser::HashErrorCodes { value, .. } => { hash_error_code_start = Some(value.base10_parse::()?); } - SplProgramErrorArgParser::SolanaProgramCrate { value, .. } => { - import = Some(SolanaProgram { + SplProgramErrorArgParser::SolanaProgramErrorCrate { value, .. } => { + program_error_import = Some(SolanaProgramError { + import: value.parse()?, + explicit: true, + }); + } + SplProgramErrorArgParser::SolanaDecodeErrorCrate { value, .. } => { + decode_error_import = Some(SolanaDecodeError { import: value.parse()?, explicit: true, }); @@ -67,7 +105,8 @@ impl Parse for SplProgramErrorArgs { } Ok(Self { hash_error_code_start, - import: import.unwrap_or(SolanaProgram::default()), + program_error_import: program_error_import.unwrap_or(SolanaProgramError::default()), + decode_error_import: decode_error_import.unwrap_or(SolanaDecodeError::default()), }) } } @@ -80,7 +119,12 @@ enum SplProgramErrorArgParser { value: LitInt, _comma: Option, }, - SolanaProgramCrate { + SolanaProgramErrorCrate { + _equals_sign: Token![=], + value: LitStr, + _comma: Option, + }, + SolanaDecodeErrorCrate { _equals_sign: Token![=], value: LitStr, _comma: Option, @@ -101,44 +145,65 @@ impl Parse for SplProgramErrorArgParser { _comma, }) } - "solana_program" => { + "solana_program_error" => { + let _equals_sign = input.parse::()?; + let value = input.parse::()?; + let _comma: Option = input.parse().unwrap_or(None); + Ok(Self::SolanaProgramErrorCrate { + _equals_sign, + value, + _comma, + }) + } + "solana_decode_error" => { let _equals_sign = input.parse::()?; let value = input.parse::()?; let _comma: Option = input.parse().unwrap_or(None); - Ok(Self::SolanaProgramCrate { + Ok(Self::SolanaDecodeErrorCrate { _equals_sign, value, _comma, }) } - _ => Err(input.error("Expected argument 'hash_error_code_start' or 'solana_program'")), + _ => Err(input.error("Expected argument 'hash_error_code_start', 'solana_program_error', or 'solana_decode_error'")), } } } // Within `exp`, you can bring things into scope with `extern crate`. // -// We don't want to assume that `solana_program::` is in scope - the user may -// have imported it under a different name, or may have imported it in a +// We don't want to assume that `solana_program_error::` is in scope - the user +// may have imported it under a different name, or may have imported it in a // non-toplevel module (common when putting impls behind a feature gate). // -// Solution: let's just generate `extern crate solana_program as -// _solana_program` and then refer to `_solana_program` in the derived code. -// However, macros are not allowed to produce `extern crate` statements at the -// toplevel. +// Solution: let's just generate `extern crate solana_program_error as +// _solana_program_error` and then refer to `_solana_program_error` in the +// derived code. However, macros are not allowed to produce `extern crate` +// statements at the toplevel. // -// Solution: let's generate `mod _impl_foo` and import solana_program within -// that. However, now we lose access to private members of the surrounding -// module. This is a problem if, for example, we're deriving for a newtype, -// where the inner type is defined in the same module, but not exported. +// Solution: let's generate `mod _impl_foo` and import solana_program_error +// within that. However, now we lose access to private members of the +// surrounding module. This is a problem if, for example, we're deriving for a +// newtype, where the inner type is defined in the same module, +// but not exported. // -// Solution: use the anonymous const trick. For some reason, `extern crate` +// Solution: use the anonymous const trick. For some reason, `extern crate` // statements are allowed here, but everything from the surrounding module is in -// scope. This trick is taken from serde and num_traits. -fn anon_const_trick(exp: TokenStream) -> TokenStream { +// scope. This trick is taken from serde and num_traits. +fn program_error_anon_const_trick(exp: TokenStream) -> TokenStream { + quote! { + const _: () = { + extern crate solana_program_error as _solana_program_error; + #exp + }; + } +} + +// Same thing, but for solana_decode_error +fn decode_error_anon_const_trick(exp: TokenStream) -> TokenStream { quote! { const _: () = { - extern crate solana_program as _solana_program; + extern crate solana_decode_error as _solana_decode_error; #exp }; } diff --git a/program-error/src/lib.rs b/program-error/src/lib.rs index 739995dd..b44f9ae2 100644 --- a/program-error/src/lib.rs +++ b/program-error/src/lib.rs @@ -9,7 +9,7 @@ extern crate self as spl_program_error; // Make these available downstream for the macro to work without // additional imports pub use { - num_derive, num_traits, solana_program, + num_derive, num_traits, solana_decode_error, solana_msg, solana_program_error, spl_program_error_derive::{ spl_program_error, DecodeError, IntoProgramError, PrintProgramError, }, diff --git a/program-error/tests/bench.rs b/program-error/tests/bench.rs index 40f01bb7..53c97661 100644 --- a/program-error/tests/bench.rs +++ b/program-error/tests/bench.rs @@ -12,32 +12,32 @@ pub enum ExampleError { IncorrectMintAuthority, } -impl From for solana_program::program_error::ProgramError { +impl From for solana_program_error::ProgramError { fn from(e: ExampleError) -> Self { - solana_program::program_error::ProgramError::Custom(e as u32) + solana_program_error::ProgramError::Custom(e as u32) } } -impl solana_program::decode_error::DecodeError for ExampleError { +impl solana_decode_error::DecodeError for ExampleError { fn type_of() -> &'static str { "ExampleError" } } -impl solana_program::program_error::PrintProgramError for ExampleError { +impl solana_program_error::PrintProgramError for ExampleError { fn print(&self) where E: 'static + std::error::Error - + solana_program::decode_error::DecodeError - + solana_program::program_error::PrintProgramError + + solana_decode_error::DecodeError + + solana_program_error::PrintProgramError + num_traits::FromPrimitive, { match self { ExampleError::MintHasNoMintAuthority => { - solana_program::msg!("Mint has no mint authority") + solana_msg::msg!("Mint has no mint authority") } ExampleError::IncorrectMintAuthority => { - solana_program::msg!("Incorrect mint authority has signed the instruction") + solana_msg::msg!("Incorrect mint authority has signed the instruction") } } } diff --git a/program-error/tests/mod.rs b/program-error/tests/mod.rs index 41358775..d9f15873 100644 --- a/program-error/tests/mod.rs +++ b/program-error/tests/mod.rs @@ -9,10 +9,8 @@ mod tests { use { super::*, serial_test::serial, - solana_program::{ - decode_error::DecodeError, - program_error::{PrintProgramError, ProgramError}, - }, + solana_decode_error::DecodeError, + solana_program_error::{PrintProgramError, ProgramError}, std::sync::{Arc, RwLock}, }; @@ -24,7 +22,7 @@ mod tests { *EXPECTED_DATA.write().unwrap() = expected_data; } pub struct SyscallStubs {} - impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { + impl solana_sysvar::program_stubs::SyscallStubs for SyscallStubs { fn sol_log(&self, message: &str) { assert_eq!( message, @@ -73,7 +71,7 @@ mod tests { static ONCE: Once = Once::new(); ONCE.call_once(|| { - solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); + solana_sysvar::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); }); // `Into` assert_eq!( @@ -112,7 +110,7 @@ mod tests { static ONCE: Once = Once::new(); ONCE.call_once(|| { - solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); + solana_sysvar::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); }); // `Into` assert_eq!( diff --git a/program-error/tests/spl.rs b/program-error/tests/spl.rs index d772d24f..4e86e170 100644 --- a/program-error/tests/spl.rs +++ b/program-error/tests/spl.rs @@ -42,7 +42,7 @@ fn test_library_error_codes() { fn get_error_code_check(hash_input: &str) -> u32 { let mut nonce: u32 = 0; loop { - let hash = solana_program::hash::hashv(&[hash_input.as_bytes(), &nonce.to_le_bytes()]); + let hash = solana_sha256_hasher::hashv(&[hash_input.as_bytes(), &nonce.to_le_bytes()]); let mut bytes = [0u8; 4]; bytes.copy_from_slice(&hash.to_bytes()[13..17]); let error_code = u32::from_le_bytes(bytes); @@ -73,9 +73,9 @@ fn test_library_error_codes() { ); } -/// Example error with solana_program crate set -#[spl_program_error(solana_program = "solana_program")] -enum ExampleSolanaProgramCrateError { +/// Example error with solana_program_error crate set +#[spl_program_error(solana_program_error = "solana_program_error")] +enum ExampleSolanaProgramError { /// This is a very informative error #[error("This is a very informative error")] VeryInformativeError, @@ -86,6 +86,23 @@ enum ExampleSolanaProgramCrateError { /// Tests that all macros compile #[test] -fn test_macros_compile_with_solana_program_crate() { - let _ = ExampleSolanaProgramCrateError::VeryInformativeError; +fn test_macros_compile_with_solana_program_error_crate() { + let _ = ExampleSolanaProgramError::VeryInformativeError; +} + +/// Example error with solana_decode_error crate set +#[spl_program_error(solana_decode_error = "solana_decode_error")] +enum ExampleSolanaDecodeError { + /// This is a very informative error + #[error("This is a very informative error")] + VeryInformativeError, + /// This is a super important error + #[error("This is a super important error")] + SuperImportantError, +} + +/// Tests that all macros compile +#[test] +fn test_macros_compile_with_solana_decode_error_crate() { + let _ = ExampleSolanaDecodeError::VeryInformativeError; }