Use procedural macro to generate static public keys (bp #7219) (#7241)

automerge
This commit is contained in:
Justin Starry
2019-12-03 22:07:19 -05:00
committed by Grimes
parent 57dce86d5e
commit 35ae76532a
13 changed files with 298 additions and 167 deletions

View File

@ -33,7 +33,6 @@ generic-array = { version = "0.13.2", default-features = false, features = ["ser
hex = "0.4.0"
hmac = "0.7.0"
itertools = { version = "0.8.2" }
lazy_static = "1.4.0"
log = { version = "0.4.8" }
memmap = { version = "0.6.2", optional = true }
num-derive = { version = "0.3" }
@ -47,8 +46,9 @@ serde_derive = "1.0.102"
serde_json = { version = "1.0.41", optional = true }
sha2 = "0.8.0"
ed25519-dalek = { version = "1.0.0-pre.1", optional = true }
solana-logger = { path = "../logger", version = "0.21.1", optional = true }
solana-crate-features = { path = "../crate-features", version = "0.21.1", optional = true }
solana-logger = { path = "../logger", version = "0.21.1", optional = true }
solana-sdk-macro = { path = "macro" }
[dev-dependencies]
tiny-bip39 = "0.6.2"

18
sdk/macro/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "solana-sdk-macro"
version = "0.21.1"
description = "Solana SDK Macro"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
homepage = "https://solana.com/"
license = "Apache-2.0"
edition = "2018"
[lib]
proc-macro = true
[dependencies]
bs58 = "0.3.0"
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = ["full", "extra-traits"] }

73
sdk/macro/src/lib.rs Normal file
View File

@ -0,0 +1,73 @@
//! Convenience macro to declare a static public key and functions to interact with it
//!
//! Input: a single literal base58 string representation of a program's id
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use std::convert::TryFrom;
use syn::{
parse::{Parse, ParseStream, Result},
parse_macro_input, Expr, LitByte, LitStr,
};
struct Id(proc_macro2::TokenStream);
impl Parse for Id {
fn parse(input: ParseStream) -> Result<Self> {
let token_stream = if input.peek(syn::LitStr) {
let id_literal: LitStr = input.parse()?;
let id_vec = bs58::decode(id_literal.value())
.into_vec()
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 id"))?;
let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..]))
.map_err(|_| syn::Error::new_spanned(&id_literal, "id is not 32 bytes long"))?;
let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
quote! {
::solana_sdk::pubkey::Pubkey::new_from_array(
[#(#bytes,)*]
)
}
} else {
let expr: Expr = input.parse()?;
quote! { #expr }
};
if !input.is_empty() {
let stream: proc_macro2::TokenStream = input.parse()?;
return Err(syn::Error::new_spanned(stream, "unexpected token"));
}
Ok(Id(token_stream))
}
}
impl ToTokens for Id {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let id = &self.0;
tokens.extend(quote! {
pub static ID: ::solana_sdk::pubkey::Pubkey = #id;
pub fn check_id(id: &::solana_sdk::pubkey::Pubkey) -> bool {
id == &ID
}
pub fn id() -> ::solana_sdk::pubkey::Pubkey {
ID
}
#[cfg(test)]
#[test]
fn test_id() {
assert!(check_id(&id()));
}
});
}
}
#[proc_macro]
pub fn declare_id(input: TokenStream) -> TokenStream {
let id = parse_macro_input!(input as Id);
TokenStream::from(quote! {#id})
}

View File

@ -1,2 +1 @@
pub const BS58_STRING: &str = "BPFLoader1111111111111111111111111111111111";
crate::declare_id!(BS58_STRING);
crate::declare_id!("BPFLoader1111111111111111111111111111111111");

View File

@ -37,6 +37,10 @@ macro_rules! solana_entrypoint(
/// # Examples
///
/// ```
/// use std::str::FromStr;
/// # // wrapper is used so that the macro invocation occurs in the item position
/// # // rather than in the statement position which isn't allowed.
/// # mod item_wrapper {
/// use solana_sdk::account::KeyedAccount;
/// use solana_sdk::instruction::InstructionError;
/// use solana_sdk::pubkey::Pubkey;
@ -51,11 +55,46 @@ macro_rules! solana_entrypoint(
/// Ok(())
/// }
///
/// solana_sdk::declare_program!(
/// "My!!!11111111111111111111111111111111111111",
/// declare_program!(
/// "My11111111111111111111111111111111111111111",
/// solana_my_program,
/// my_process_instruction
/// );
///
/// # }
/// # use solana_sdk::pubkey::Pubkey;
/// # use item_wrapper::id;
/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
/// assert_eq!(id(), my_id);
/// ```
/// ```
/// use std::str::FromStr;
/// # // wrapper is used so that the macro invocation occurs in the item position
/// # // rather than in the statement position which isn't allowed.
/// # mod item_wrapper {
/// use solana_sdk::account::KeyedAccount;
/// use solana_sdk::instruction::InstructionError;
/// use solana_sdk::pubkey::Pubkey;
/// use solana_sdk::declare_program;
///
/// fn my_process_instruction(
/// program_id: &Pubkey,
/// keyed_accounts: &mut [KeyedAccount],
/// data: &[u8],
/// ) -> Result<(), InstructionError> {
/// // Process an instruction
/// Ok(())
/// }
///
/// declare_program!(
/// solana_sdk::system_program::ID,
/// solana_my_program,
/// my_process_instruction
/// );
/// # }
///
/// # use item_wrapper::id;
/// assert_eq!(id(), solana_sdk::system_program::ID);
/// ```
#[macro_export]
macro_rules! declare_program(

View File

@ -1,3 +1,6 @@
// Allows macro expansion of `use ::solana_sdk::*` to work within this crate
extern crate self as solana_sdk;
pub mod account;
pub mod account_utils;
pub mod bpf_loader;
@ -24,6 +27,29 @@ pub mod system_program;
pub mod sysvar;
pub mod timing;
/// Convenience macro to declare a static public key and functions to interact with it
///
/// Input: a single literal base58 string representation of a program's id
///
/// # Example
///
/// ```
/// # // wrapper is used so that the macro invocation occurs in the item position
/// # // rather than in the statement position which isn't allowed.
/// use std::str::FromStr;
/// use solana_sdk::{declare_id, pubkey::Pubkey};
///
/// # mod item_wrapper {
/// # use solana_sdk::declare_id;
/// declare_id!("My11111111111111111111111111111111111111111");
/// # }
/// # use item_wrapper::id;
///
/// let my_id = Pubkey::from_str("My11111111111111111111111111111111111111111").unwrap();
/// assert_eq!(id(), my_id);
/// ```
pub use solana_sdk_macro::declare_id;
// On-chain program specific modules
pub mod account_info;
pub mod entrypoint;
@ -52,5 +78,4 @@ pub mod transport;
#[macro_use]
extern crate serde_derive;
pub extern crate bs58;
pub extern crate lazy_static;
extern crate log as logger;

View File

@ -1,5 +1,4 @@
pub const BS58_STRING: &str = "MoveLdr111111111111111111111111111111111111";
crate::declare_id!(BS58_STRING);
crate::declare_id!("MoveLdr111111111111111111111111111111111111");
pub fn solana_move_loader_program() -> (String, crate::pubkey::Pubkey) {
("solana_move_loader_program".to_string(), id())

View File

@ -47,6 +47,10 @@ impl Pubkey {
)
}
pub const fn new_from_array(pubkey_array: [u8; 32]) -> Self {
Self(pubkey_array)
}
#[cfg(not(feature = "program"))]
pub fn new_rand() -> Self {
Self::new(&rand::random::<[u8; 32]>())
@ -104,62 +108,6 @@ pub fn read_pubkey_file(infile: &str) -> Result<Pubkey, Box<dyn error::Error>> {
Ok(Pubkey::from_str(&printable)?)
}
/// Convenience macro to declare a static Pubkey and functions to interact with it
///
/// bs58_string: bs58 string representation the program's id
///
/// # Examples
///
/// ```
/// solana_sdk::declare_id!("My!!!11111111111111111111111111111111111111");
/// ```
#[macro_export]
macro_rules!
declare_id(
($bs58_string:expr) => (
use std::str::FromStr;
$crate::lazy_static::lazy_static! {
static ref _PUBKEY: $crate::pubkey::Pubkey = {
match $crate::pubkey::Pubkey::from_str(&$bs58_string) {
Ok(pubkey) => pubkey,
Err(_) => {
let pubkey_vec = $crate::bs58::decode(&$bs58_string)
.into_vec()
.map_err(|e| panic!("Error: {}, {}", $bs58_string, e))
.unwrap();
let expected_len = std::mem::size_of::<$crate::pubkey::Pubkey>();
let len = pubkey_vec.len();
if len != expected_len {
panic!(
"Error: {}, decoded length {}, expected {}",
$bs58_string, len, expected_len);
} else {
panic!(
"Error: {}, not a valid string, cannot determine reason",
$bs58_string);
}
}
}
};
}
pub fn check_id(id: &$crate::pubkey::Pubkey) -> bool {
*id == *_PUBKEY
}
pub fn id() -> $crate::pubkey::Pubkey {
*_PUBKEY
}
#[cfg(test)]
#[test]
fn test_id() {
assert!(check_id(&id()));
}
)
);
#[cfg(test)]
mod tests {
use super::*;