Rust BPF programs depend on Solana SDK (#5819)

This commit is contained in:
Jack May
2019-09-06 09:20:14 -07:00
committed by GitHub
parent 1f9fde5f7b
commit e5f902369c
40 changed files with 187 additions and 146 deletions

119
sdk/src/entrypoint.rs Normal file
View File

@ -0,0 +1,119 @@
//! @brief Solana Rust-based BPF program entrypoint and its parameter types
extern crate alloc;
use alloc::vec::Vec;
use core::mem::size_of;
use core::slice::{from_raw_parts, from_raw_parts_mut};
/// Public key
pub type SolPubkey = [u8; 32];
/// Keyed Account
pub struct SolKeyedAccount<'a> {
/// Public key of the account
pub key: &'a SolPubkey,
/// Public key of the account
pub is_signer: bool,
/// Number of lamports owned by this account
pub lamports: &'a mut u64,
/// On-chain data within this account
pub data: &'a mut [u8],
/// Program that owns this account
pub owner: &'a SolPubkey,
}
/// Information about the state of the cluster immediately before the program
/// started executing the current instruction
pub struct SolClusterInfo<'a> {
/// program_id of the currently executing program
pub program_id: &'a SolPubkey,
}
/// Declare entrypoint of the program.
///
/// Deserialize the program input parameters and call
/// a user defined entrypoint. Users must call
/// this function otherwise an entrypoint for
/// their program will not be created.
#[macro_export]
macro_rules! entrypoint {
($process_instruction:ident) => {
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> bool {
unsafe {
if let Ok((mut kas, info, data)) = $crate::entrypoint::deserialize(input) {
$process_instruction(&mut kas, &info, &data)
} else {
false
}
}
}
};
}
/// Deserialize the input parameters
#[allow(clippy::type_complexity)]
pub unsafe fn deserialize<'a>(
input: *mut u8,
) -> Result<(Vec<SolKeyedAccount<'a>>, SolClusterInfo<'a>, &'a [u8]), ()> {
let mut offset: usize = 0;
// Number of KeyedAccounts present
#[allow(clippy::cast_ptr_alignment)]
let num_ka = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
// KeyedAccounts
let mut kas = Vec::with_capacity(num_ka);
for _ in 0..num_ka {
let is_signer = {
#[allow(clippy::cast_ptr_alignment)]
let is_signer_val = *(input.add(offset) as *const u64);
(is_signer_val != 0)
};
offset += size_of::<u64>();
let key: &SolPubkey = &*(input.add(offset) as *const [u8; size_of::<SolPubkey>()]);
offset += size_of::<SolPubkey>();
#[allow(clippy::cast_ptr_alignment)]
let lamports = &mut *(input.add(offset) as *mut u64);
offset += size_of::<u64>();
#[allow(clippy::cast_ptr_alignment)]
let data_length = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let data = { from_raw_parts_mut(input.add(offset), data_length) };
offset += data_length;
let owner: &SolPubkey = &*(input.add(offset) as *const [u8; size_of::<SolPubkey>()]);
offset += size_of::<SolPubkey>();
kas.push(SolKeyedAccount {
key,
is_signer,
lamports,
data,
owner,
});
}
// Instruction data
#[allow(clippy::cast_ptr_alignment)]
let data_length = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let data = { from_raw_parts(input.add(offset), data_length) };
offset += data_length;
// Program Id
let program_id: &SolPubkey = &*(input.add(offset) as *const [u8; size_of::<SolPubkey>()]);
let info = SolClusterInfo { program_id };
Ok((kas, info, data))
}

View File

@ -1,30 +1,48 @@
pub mod account;
pub mod account_utils;
pub mod bpf_loader;
pub mod client;
pub mod fee_calculator;
pub mod genesis_block;
pub mod hash;
pub mod inflation;
pub mod instruction;
pub mod instruction_processor_utils;
pub mod loader_instruction;
pub mod message;
pub mod native_loader;
pub mod packet;
pub mod poh_config;
#[macro_use]
extern crate cfg_if;
pub mod pubkey;
pub mod rent;
pub mod rpc_port;
pub mod short_vec;
pub mod signature;
pub mod system_instruction;
pub mod system_program;
pub mod system_transaction;
pub mod sysvar;
pub mod timing;
pub mod transaction;
pub mod transport;
// On-chain program modules
cfg_if! {
if #[cfg(feature = "program")] {
pub mod entrypoint;
pub mod log;
pub mod program_test;
}
}
// Kitchen sink modules
cfg_if! {
if #[cfg(feature = "kitchen_sink")] {
pub mod account;
pub mod account_utils;
pub mod bpf_loader;
pub mod client;
pub mod fee_calculator;
pub mod genesis_block;
pub mod hash;
pub mod inflation;
pub mod instruction;
pub mod instruction_processor_utils;
pub mod loader_instruction;
pub mod message;
pub mod native_loader;
pub mod packet;
pub mod poh_config;
pub mod rent;
pub mod rpc_port;
pub mod short_vec;
pub mod signature;
pub mod system_instruction;
pub mod system_program;
pub mod system_transaction;
pub mod sysvar;
pub mod timing;
pub mod transaction;
pub mod transport;
}
}
#[macro_use]
extern crate serde_derive;

93
sdk/src/log.rs Normal file
View File

@ -0,0 +1,93 @@
//! @brief Solana Rust-based BPF program logging
use crate::entrypoint::{SolKeyedAccount, SolPubkey};
/// Prints a string
/// There are two forms and are fast
/// 1. Single string
/// 2. 5 integers
#[macro_export]
macro_rules! info {
($msg:expr) => {
$crate::log::sol_log($msg)
};
($arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr, $arg5:expr) => {
$crate::log::sol_log_64(
$arg1 as u64,
$arg2 as u64,
$arg3 as u64,
$arg4 as u64,
$arg5 as u64,
)
}; // `format!()` is not supported yet, Issue #3099
// `format!()` incurs a very large runtime overhead so it should be used with care
// ($($arg:tt)*) => ($crate::log::sol_log(&format!($($arg)*)));
}
/// Prints a string to stdout
///
/// @param message - Message to print
pub fn sol_log(message: &str) {
unsafe {
sol_log_(message.as_ptr(), message.len() as u64);
}
}
extern "C" {
fn sol_log_(message: *const u8, length: u64);
}
/// Prints 64 bit values represented as hexadecimal to stdout
///
/// @param argx - integer arguments to print
pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
unsafe {
sol_log_64_(arg1, arg2, arg3, arg4, arg5);
}
}
extern "C" {
fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64);
}
/// Prints the hexadecimal representation of a public key
///
/// @param - key The public key to print
#[allow(dead_code)]
pub fn sol_log_key(key: &SolPubkey) {
for (i, k) in key.iter().enumerate() {
sol_log_64(0, 0, 0, i as u64, u64::from(*k));
}
}
/// Prints the hexadecimal representation of a slice
///
/// @param slice - The array to print
#[allow(dead_code)]
pub fn sol_log_slice(slice: &[u8]) {
for (i, s) in slice.iter().enumerate() {
sol_log_64(0, 0, 0, i as u64, u64::from(*s));
}
}
/// Prints the hexadecimal representation of the program's input parameters
///
/// @param ka - A pointer to an array of `SolKeyedAccounts` to print
/// @param data - A pointer to the instruction data to print
#[allow(dead_code)]
pub fn sol_log_params(ka: &[SolKeyedAccount], data: &[u8]) {
for (i, k) in ka.iter().enumerate() {
sol_log("SolKeyedAccount");
sol_log_64(0, 0, 0, 0, i as u64);
sol_log("- Is signer");
sol_log_64(0, 0, 0, 0, k.is_signer as u64);
sol_log("- Key");
sol_log_key(&k.key);
sol_log("- Lamports");
sol_log_64(0, 0, 0, 0, *k.lamports);
sol_log("- AccountData");
sol_log_slice(k.data);
sol_log("- Owner");
sol_log_key(&k.owner);
}
sol_log("Instruction data");
sol_log_slice(data);
}

13
sdk/src/program_test.rs Normal file
View File

@ -0,0 +1,13 @@
//! @brief Solana Rust-based BPF program utility functions and types
#[no_mangle]
pub unsafe fn sol_log_(message: *const u8, length: u64) {
let slice = std::slice::from_raw_parts(message, length as usize);
let string = std::str::from_utf8(&slice).unwrap();
std::println!("{}", string);
}
#[no_mangle]
pub fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) {
std::println!("{} {} {} {} {}", arg1, arg2, arg3, arg4, arg5);
}

View File

@ -1,10 +1,7 @@
use std::convert::TryFrom;
use std::error;
use std::fmt;
use std::fs::{self, File};
use std::io::Write;
use std::mem;
use std::path::Path;
use std::str::FromStr;
pub use bs58;
@ -50,6 +47,7 @@ impl Pubkey {
)
}
#[cfg(not(feature = "program"))]
pub fn new_rand() -> Self {
Self::new(&rand::random::<[u8; 32]>())
}
@ -73,21 +71,25 @@ impl fmt::Display for Pubkey {
}
}
#[cfg(not(feature = "program"))]
pub fn write_pubkey(outfile: &str, pubkey: Pubkey) -> Result<(), Box<dyn error::Error>> {
use std::io::Write;
let printable = format!("{}", pubkey);
let serialized = serde_json::to_string(&printable)?;
if let Some(outdir) = Path::new(&outfile).parent() {
fs::create_dir_all(outdir)?;
if let Some(outdir) = std::path::Path::new(&outfile).parent() {
std::fs::create_dir_all(outdir)?;
}
let mut f = File::create(outfile)?;
let mut f = std::fs::File::create(outfile)?;
f.write_all(&serialized.clone().into_bytes())?;
Ok(())
}
#[cfg(not(feature = "program"))]
pub fn read_pubkey(infile: &str) -> Result<Pubkey, Box<dyn error::Error>> {
let f = File::open(infile.to_string())?;
let f = std::fs::File::open(infile.to_string())?;
let printable: String = serde_json::from_reader(f)?;
Ok(Pubkey::from_str(&printable)?)
}