From c9b8977226bce6c845c6ed3074fc326d93abb9e3 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 18 Mar 2022 10:27:51 -0500 Subject: [PATCH] Add crate docs for solana-program (#23363) * Add crate docs for solana-program * Rework solana-program docs for pr feedback * Clarify log module docs * Remove address lookup table program from solana-program docs --- sdk/program/src/lib.rs | 552 ++++++++++++++++++++++++++++++++++ sdk/program/src/log.rs | 89 ++++-- sdk/program/src/sysvar/mod.rs | 7 + 3 files changed, 625 insertions(+), 23 deletions(-) diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index e87d65af71..a50ecf70da 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -1,3 +1,555 @@ +//! The base library for all Solana on-chain Rust programs. +//! +//! All Solana Rust programs that run on-chain will link to this crate, which +//! acts as a standard library for Solana programs. Solana programs also link to +//! the [Rust standard library][std], though it is [modified][sstd] for the +//! Solana runtime environment. While off-chain programs that interact with the +//! Solana network _can_ link to this crate, they typically instead use the +//! [`solana-sdk`] crate, which reexports all modules from `solana-program`. +//! +//! [std]: https://doc.rust-lang.org/stable/std/ +//! [sstd]: https://docs.solana.com/developing/on-chain-programs/developing-rust#restrictions +//! [`solana-sdk`]: https://docs.rs/solana-sdk/latest/solana_sdk/ +//! +//! This library defines +//! +//! - macros for declaring the [program entrypoint][pe], +//! - [core data types][cdt], +//! - [logging] macros, +//! - [serialization] methods, +//! - methods for [cross-program instruction execution][cpi], +//! - program IDs and instruction constructors for the system program and other +//! [native programs][np], +//! - [sysvar] accessors. +//! +//! [pe]: #defining-a-solana-program +//! [cdt]: #core-data-types +//! [logging]: crate::log +//! [serialization]: #serialization +//! [np]: #native-programs +//! [cpi]: #cross-program-instruction-execution +//! [sysvar]: #sysvars +//! +//! Idiomatic examples of `solana-program` usage can be found in +//! [the Solana Program Library][spl]. +//! +//! [spl]: https://github.com/solana-labs/solana-program-library +//! +//! # Defining a solana program +//! +//! Solana program crates have some unique properties compared to typical Rust +//! programs: +//! +//! - They are often compiled for both on-chain use and off-chain use. This is +//! primarily because off-chain clients may need access to data types +//! defined by the on-chain program. +//! - They do not define a `main` function, but instead define their entrypoint +//! with the [`entrypoint!`] macro. +//! - They are compiled as the ["cdylib"] crate type for dynamic loading +//! by the Solana runtime. +//! - They run in a constrained VM environment, and while they do have access to +//! the [Rust standard library][std], many features of the standard library, +//! particularly related to OS services, will fail at runtime, will silently +//! do nothing, or are not defined. See the [restrictions to the Rust standard +//! library][sstd] in the Solana documentation for more. +//! +//! [std]: https://doc.rust-lang.org/std/index.html +//! ["cdylib"]: https://doc.rust-lang.org/reference/linkage.html +//! +//! Because multiple crates that are linked together cannot all define +//! program entrypoints (see the [`entrypoint!`] documentation) a common +//! convention is to use a [Cargo feature] called `no-entrypoint` to allow +//! the program entrypoint to be disabled. +//! +//! [Cargo feature]: https://doc.rust-lang.org/cargo/reference/features.html +//! +//! The skeleton of a Solana program typically looks like: +//! +//! ``` +//! #[cfg(not(feature = "no-entrypoint"))] +//! pub mod entrypoint { +//! use solana_program::{ +//! account_info::AccountInfo, +//! entrypoint, +//! entrypoint::ProgramResult, +//! pubkey::Pubkey, +//! }; +//! +//! entrypoint!(process_instruction); +//! +//! pub fn process_instruction( +//! program_id: &Pubkey, +//! accounts: &[AccountInfo], +//! instruction_data: &[u8], +//! ) -> ProgramResult { +//! // Decode and dispatch instructions here. +//! todo!() +//! } +//! } +//! +//! // Additional code goes here. +//! ``` +//! +//! With a `Cargo.toml` file that contains +//! +//! ```toml +//! [lib] +//! crate-type = ["cdylib", "rlib"] +//! +//! [features] +//! no-entrypoint = [] +//! ``` +//! +//! Note that a Solana program must specify its crate-type as "cdylib", and +//! "cdylib" crates will automatically be discovered and built by the `cargo +//! build-bpf` command. Solana programs also often have crate-type "rlib" so +//! they can be linked to other Rust crates. +//! +//! # On-chain vs. off-chain compilation targets +//! +//! Solana programs run on the [rbpf] VM, which implements a variant of the +//! [eBPF] instruction set. Because this crate can be compiled for both on-chain +//! and off-chain execution, the environments of which are significantly +//! different, it extensively uses [conditional compilation][cc] to tailor its +//! implementation to the environment. The `cfg` predicate used for identifying +//! compilation for on-chain programs is `target_arch = "bpf"`, as in this +//! example from the `solana-program` codebase that logs a message via a +//! syscall when run on-chain, and via a library call when offchain: +//! +//! [rbpf]: https://github.com/solana-labs/rbpf +//! [eBPF]: https://ebpf.io/ +//! [cc]: https://doc.rust-lang.org/reference/conditional-compilation.html +//! +//! ``` +//! pub fn sol_log(message: &str) { +//! #[cfg(target_arch = "bpf")] +//! unsafe { +//! sol_log_(message.as_ptr(), message.len() as u64); +//! } +//! +//! #[cfg(not(target_arch = "bpf"))] +//! program_stubs::sol_log(message); +//! } +//! # mod program_stubs { +//! # pub(crate) fn sol_log(message: &str) { } +//! # } +//! ``` +//! +//! This `cfg` pattern is suitable as well for user code that needs to work both +//! on-chain and off-chain. +//! +//! `solana-program` and `solana-sdk` were previously a single crate. Because of +//! this history, and because of the dual-usage of `solana-program` for two +//! different environments, it contains some features that are not available to +//! on-chain programs at compile-time. It also contains some on-chain features +//! that will fail in off-chain scenarios at runtime. This distinction is not +//! well-reflected in the documentation. +//! +//! For a more complete description of Solana's implementation of eBPF and its +//! limitations, see the main Solana documentation for [on-chain programs][ocp]. +//! +//! [ocp]: https://docs.solana.com/developing/on-chain-programs/overview +//! +//! # Core data types +//! +//! - [`Pubkey`] — The address of a [Solana account][acc]. Some account +//! addresses are [ed25519] public keys, with corresponding secret keys that +//! are managed off-chain. Often though account addresses do not have +//! corresponding secret keys, as with [_program derived addresses_][pdas], or +//! the secret key is not relevant to the operation of a program, and may have +//! even been disposed of. As running Solana programs can not safely create or +//! manage secret keys, the full [`Keypair`] is not defined in +//! `solana-program` but in `solana-sdk`. +//! - [`Hash`] — A [SHA-256] hash. Used to uniquely identify blocks, and +//! also for general purpose hashing. +//! - [`AccountInfo`] — A description of a single Solana account. All accounts +//! that might be accessed by a program invocation are provided to the program +//! entrypoint as `AccountInfo`. +//! - [`Instruction`] — A directive telling the runtime to execute a program, +//! passing it a set of accounts and program-specific data. +//! - [`ProgramError`] and [`ProgramResult`] — The error type that all programs +//! must return, reported to the runtime as a `u64`. +//! - [`Sol`] — The Solana native token type, with conversions to and from +//! [_lamports_], the smallest fractional unit of SOL, in the [`native_token`] +//! module. +//! +//! [acc]: https://docs.solana.com/developing/programming-model/accounts +//! [`Pubkey`]: pubkey::Pubkey +//! [`Hash`]: hash::Hash +//! [`Instruction`]: instruction::Instruction +//! [`AccountInfo`]: account_info::AccountInfo +//! [`ProgramError`]: program_error::ProgramError +//! [`ProgramResult`]: entrypoint::ProgramResult +//! [ed25519]: https://ed25519.cr.yp.to/ +//! [`Keypair`]: https://docs.rs/solana-sdk/latest/solana_sdk/signer/keypair/struct.Keypair.html +//! [SHA-256]: https://en.wikipedia.org/wiki/SHA-2 +//! [`Sol`]: native_token::Sol +//! [_lamports_]: https://docs.solana.com/introduction#what-are-sols +//! +//! # Serialization +//! +//! Within the Solana runtime, programs, and network, at least three different +//! serialization formats are used, and `solana-program` provides access to +//! those needed by programs. +//! +//! In user-written Solana program code, serialization is primarily used for +//! accessing [`AccountInfo`] data and [`Instruction`] data, both of which are +//! program-specific binary data. Every program is free to decide their own +//! serialization format, but data recieved from other sources — +//! [sysvars][sysvar] for example — must be deserialized using the methods +//! indicated by the documentation for that data or data type. +//! +//! [`AccountInfo`]: account_info::AccountInfo +//! [`Instruction`]: instruction::Instruction +//! +//! The three serialization formats in use in Solana are: +//! +//! - __[Borsh]__, a compact and well-specified format developed by the [NEAR] +//! project, suitable for use in protocol definitions and for archival storage. +//! It has a [Rust implementation][brust] and a [JavaScript implementation][bjs] +//! and is recommended for all purposes. +//! +//! Users need to import the [`borsh`] crate themselves — it is not +//! re-exported by `solana-program`, though this crate provides several useful +//! utilities in its [`borsh` module][borshmod] that are not available in the +//! `borsh` library. +//! +//! The [`Instruction::new_with_borsh`] function creates an `Instruction` by +//! serializing a value with borsh. +//! +//! [Borsh]: https://borsh.io/ +//! [NEAR]: https://near.org/ +//! [brust]: https://docs.rs/borsh +//! [bjs]: https://github.com/near/borsh-js +//! [`borsh`]: https://docs.rs/borsh +//! [borshmod]: crate::borsh +//! [`Instruction::new_with_borsh`]: instruction::Instruction::new_with_borsh +//! +//! - __[Bincode]__, a compact serialization format that implements the [Serde] +//! Rust APIs. As it does not have a specification nor a JavaScript +//! implementation, and uses more CPU than borsh, it is not recommend for new +//! code. +//! +//! Many system program and native program instructions are serialized with +//! bincode, and it is used for other purposes in the runtime. In these cases +//! Rust programmers are generally not directly exposed to the encoding format +//! as it is hidden behind APIs. +//! +//! The [`Instruction::new_with_bincode`] function creates an `Instruction` by +//! serializing a value with bincode. +//! +//! [Bincode]: https://docs.rs/bincode +//! [Serde]: https://serde.rs/ +//! [`Instruction::new_with_bincode`]: instruction::Instruction::new_with_bincode +//! +//! - __[`Pack`]__, a Solana-specific serialization API that is used by many +//! older programs in the [Solana Program Library][spl] to define their +//! account format. It is difficult to implement and does not define a +//! language-independent serialization format. It is not generally recommended +//! for new code. +//! +//! [`Pack`]: program_pack::Pack +//! +//! Developers should carefully consider the CPU cost of serialization, balanced +//! against the need for correctness and ease of use: off-the-shelf +//! serialization formats tend to be more expensive than carefully hand-written +//! application-specific formats; but application-specific formats are more +//! difficult to ensure the correctness of, and to provide multi-language +//! implementations for. It is not uncommon for programs to pack and unpack +//! their data with hand-written code. +//! +//! # Cross-program instruction execution +//! +//! Solana programs may call other programs, termed [_cross-program +//! invocation_][cpi] (CPI), with the [`invoke`] and [`invoke_signed`] +//! functions. When calling another program the caller must provide the +//! [`Instruction`] to be invoked, as well as the [`AccountInfo`] for every +//! account required by the instruction. Because the only way for a program to +//! acquire `AccountInfo` values is by receiving them from the runtime at the +//! [program entrypoint][entrypoint!], any account required by the callee +//! program must transitively be required by the caller program, and provided by +//! _its_ caller. +//! +//! [`invoke`]: program::invoke +//! [`invoke_signed`]: program::invoke_signed +//! [cpi]: https://docs.solana.com/developing/programming-model/calling-between-programs +//! +//! A simple example of transferring lamports via CPI: +//! +//! ``` +//! use solana_program::{ +//! account_info::{next_account_info, AccountInfo}, +//! entrypoint, +//! entrypoint::ProgramResult, +//! program::invoke, +//! pubkey::Pubkey, +//! system_instruction, +//! system_program, +//! }; +//! +//! entrypoint!(process_instruction); +//! +//! fn process_instruction( +//! program_id: &Pubkey, +//! accounts: &[AccountInfo], +//! instruction_data: &[u8], +//! ) -> ProgramResult { +//! let account_info_iter = &mut accounts.iter(); +//! +//! let payer = next_account_info(account_info_iter)?; +//! let recipient = next_account_info(account_info_iter)?; +//! // The system program is a required account to invoke a system +//! // instruction, even though we don't use it directly. +//! let system_account = next_account_info(account_info_iter)?; +//! +//! assert!(payer.is_writable); +//! assert!(payer.is_signer); +//! assert!(recipient.is_writable); +//! assert!(system_program::check_id(system_account.key)); +//! +//! let lamports = 1000000; +//! +//! invoke( +//! &system_instruction::transfer(payer.key, recipient.key, lamports), +//! &[payer.clone(), recipient.clone(), system_account.clone()], +//! ) +//! } +//! ``` +//! +//! Solana also includes a mechinasm to let programs control and sign for +//! accounts without needing to protect a corresponding secret key, called +//! [_program derived addresses_][pdas]. PDAs are derived with the +//! [`Pubkey::find_program_address`] function. With a PDA, a program can call +//! `invoke_signed` to call another program while virtually "signing" for the +//! PDA. +//! +//! [pdas]: https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses +//! [`Pubkey::find_program_address`]: pubkey::Pubkey::find_program_address +//! +//! A simple example of creating an account for a PDA: +//! +//! ``` +//! use solana_program::{ +//! account_info::{next_account_info, AccountInfo}, +//! entrypoint, +//! entrypoint::ProgramResult, +//! program::invoke_signed, +//! pubkey::Pubkey, +//! system_instruction, +//! system_program, +//! }; +//! +//! entrypoint!(process_instruction); +//! +//! fn process_instruction( +//! program_id: &Pubkey, +//! accounts: &[AccountInfo], +//! instruction_data: &[u8], +//! ) -> ProgramResult { +//! let account_info_iter = &mut accounts.iter(); +//! let payer = next_account_info(account_info_iter)?; +//! let vault_pda = next_account_info(account_info_iter)?; +//! let system_program = next_account_info(account_info_iter)?; +//! +//! assert!(payer.is_writable); +//! assert!(payer.is_signer); +//! assert!(vault_pda.is_writable); +//! assert_eq!(vault_pda.owner, &system_program::ID); +//! assert!(system_program::check_id(system_program.key)); +//! +//! let vault_bump_seed = instruction_data[0]; +//! let vault_seeds = &[b"vault", payer.key.as_ref(), &[vault_bump_seed]]; +//! let expected_vault_pda = Pubkey::create_program_address(vault_seeds, program_id)?; +//! +//! assert_eq!(vault_pda.key, &expected_vault_pda); +//! +//! let lamports = 10000000; +//! let vault_size = 16; +//! +//! invoke_signed( +//! &system_instruction::create_account( +//! &payer.key, +//! &vault_pda.key, +//! lamports, +//! vault_size, +//! &program_id, +//! ), +//! &[ +//! payer.clone(), +//! vault_pda.clone(), +//! ], +//! &[ +//! &[ +//! b"vault", +//! payer.key.as_ref(), +//! &[vault_bump_seed], +//! ], +//! ] +//! )?; +//! Ok(()) +//! } +//! ``` +//! +//! # Native programs +//! +//! Some solana programs are [_native programs_][np2], running native machine +//! code that is distributed with the runtime, with well-known program IDs. +//! +//! [np2]: https://docs.solana.com/developing/runtime-facilities/programs +//! +//! Some native programs can be [invoked][cpi] by other programs, but some can +//! only be executed as "top-level" instructions included by off-chain clients +//! in a [`Transaction`]. +//! +//! [`Transaction`]: https://docs.rs/solana-sdk/latest/solana_sdk/transaction/struct.Transaction.html +//! +//! This crate defines the program IDs for most native programs. Even though +//! some native programs cannot be invoked by other programs, a Solana program +//! may need access to their program IDs. For example, a program may need to +//! verify that an ed25519 signature verification instruction was included in +//! the same transaction as its own instruction. For many native programs, this +//! crate also defines enums that represent the instructions they process, and +//! constructors for building the instructions. +//! +//! Locations of program IDs and instruction constructors are noted in the list +//! below, as well as whether they are invokable by other programs. +//! +//! While some native programs have been active since the genesis block, others +//! are activated dynamically after a specific [slot], and some are not yet +//! active. This documentation does not distinguish which native programs are +//! active on any particular network. The `solana feature status` CLI command +//! can help in determining active features. +//! +//! [slot]: https://docs.solana.com/terminology#slot +//! +//! Native programs important to Solana program authors include: +//! +//! - __System Program__: Creates new accounts, allocates account data, assigns +//! accounts to owning programs, transfers lamports from System Program owned +//! accounts and pays transaction fees. +//! - ID: [`solana_program::system_program`] +//! - Instruction: [`solana_program::system_instruction`] +//! - Invokable by programs? yes +//! +//! - __Compute Budget Program__: Requests additional CPU or memory resources +//! for a transaction. This program does nothing when called from another +//! program. +//! - ID: [`solana_sdk::compute_budget`](https://docs.rs/solana-sdk/latest/solana_sdk/compute_budget/index.html) +//! - Instruction: [`solana_sdk::compute_budget`](https://docs.rs/solana-sdk/latest/solana_sdk/compute_budget/index.html) +//! - Invokable by programs? no +//! +//! - __ed25519 Program__: Verifies an ed25519 signature. +//! - ID: [`solana_program::ed25519_program`] +//! - Instruction: [`solana_sdk::ed25519_instruction`](https://docs.rs/solana-sdk/latest/solana_sdk/ed25519_instruction/index.html) +//! - Invokable by programs? no +//! +//! - __secp256k1 Program__: Verifies secp256k1 public key recovery operations. +//! - ID: [`solana_program::secp256k1_program`] +//! - Instruction: [`solana_sdk::secp256k1_instruction`](https://docs.rs/solana-sdk/latest/solana_sdk/secp256k1_instruction/index.html) +//! - Invokable by programs? no +//! +//! - __BPF Loader__: Deploys, and executes immutable programs on the chain. +//! - ID: [`solana_program::bpf_loader`] +//! - Instruction: [`solana_program::loader_instruction`] +//! - Invokable by programs? yes +//! +//! - __Upgradable BPF Loader__: Deploys, upgrades, and executes upgradable +//! programs on the chain. +//! - ID: [`solana_program::bpf_loader_upgradeable`] +//! - Instruction: [`solana_program::loader_upgradeable_instruction`] +//! - Invokable by programs? yes +//! +//! - __Deprecated BPF Loader__: Deploys, and executes immutable programs on the +//! chain. +//! - ID: [`solana_program::bpf_loader_deprecated`] +//! - Instruction: [`solana_program::loader_instruction`] +//! - Invokable by programs? yes +//! +//! [lut]: https://docs.solana.com/proposals/transactions-v2 +//! +//! # Sysvars +//! +//! Sysvars are special accounts that contain dynamically-updating data about +//! the network cluster, the blockchain history, and the executing transaction. +//! +//! The program IDs for sysvars are defined in the [`sysvar`] module, and simple +//! sysvars implement the [`Sysvar::get`] method, which loads a sysvar directly +//! from the runtime, as in this example that logs the `clock` sysvar: +//! +//! [`Sysvar::get`]: sysvar::Sysvar::get +//! +//! ``` +//! use solana_program::{ +//! account_info::AccountInfo, +//! clock, +//! entrypoint::ProgramResult, +//! msg, +//! pubkey::Pubkey, +//! sysvar::Sysvar, +//! }; +//! +//! fn process_instruction( +//! program_id: &Pubkey, +//! accounts: &[AccountInfo], +//! instruction_data: &[u8], +//! ) -> ProgramResult { +//! let clock = clock::Clock::get()?; +//! msg!("clock: {:#?}", clock); +//! Ok(()) +//! } +//! ``` +//! +//! Since Solana sysvars are accounts, if the `AccountInfo` is provided to the +//! program, then the program can deserialize the sysvar with +//! [`Sysvar::from_account_info`] to access its data, as in this example that +//! again logs the [`clock`][clk] sysvar. +//! +//! [`Sysvar::from_account_info`]: sysvar::Sysvar::from_account_info +//! [clk]: sysvar::clock +//! +//! ``` +//! use solana_program::{ +//! account_info::{next_account_info, AccountInfo}, +//! clock, +//! entrypoint::ProgramResult, +//! msg, +//! pubkey::Pubkey, +//! sysvar::Sysvar, +//! }; +//! +//! fn process_instruction( +//! program_id: &Pubkey, +//! accounts: &[AccountInfo], +//! instruction_data: &[u8], +//! ) -> ProgramResult { +//! let account_info_iter = &mut accounts.iter(); +//! let clock_account = next_account_info(account_info_iter)?; +//! let clock = clock::Clock::from_account_info(&clock_account)?; +//! msg!("clock: {:#?}", clock); +//! Ok(()) +//! } +//! ``` +//! +//! When possible, programs should prefer to call `Sysvar::get` instead of +//! deserializing with `Sysvar::from_account_info`, as the latter imposes extra +//! overhead of deserialization while also requiring the sysvar account address +//! be passed to the program, wasting the limited space available to +//! transactions. Deserializing sysvars that can instead be retrieved with +//! `Sysvar::get` should be only be considered for compatibility with older +//! programs that pass around sysvar accounts. +//! +//! Some sysvars are too large to deserialize within a program, and +//! `Sysvar::from_account_info` returns an error. Some sysvars are too large +//! to deserialize within a program, and attempting to will exhaust the +//! program's compute budget. Some sysvars do not implement `Sysvar::get` and +//! return an error. Some sysvars have custom deserializers that do not +//! implement the `Sysvar` trait. These cases are documented in the modules for +//! individual sysvars. +//! +//! For more details see the Solana [documentation on sysvars][sysvardoc]. +//! +//! [sysvardoc]: https://docs.solana.com/developing/runtime-facilities/sysvars + #![allow(incomplete_features)] #![cfg_attr(RUSTC_WITH_SPECIALIZATION, feature(specialization))] #![cfg_attr(RUSTC_NEEDS_PROC_MACRO_HYGIENE, feature(proc_macro_hygiene))] diff --git a/sdk/program/src/log.rs b/sdk/program/src/log.rs index 4655c1b1fd..e3a9342ecf 100644 --- a/sdk/program/src/log.rs +++ b/sdk/program/src/log.rs @@ -1,4 +1,37 @@ -//! Solana Rust-based BPF program logging +//! Logging utilities for Rust-based Solana programs. +//! +//! Logging is the main mechanism for getting debugging information out of +//! running Solana programs, and there are several functions available for doing +//! so efficiently, depending on the type of data being logged. +//! +//! The most common way to emit logs is through the [`msg!`] macro, which logs +//! simple strings, as well as [formatted strings][fs]. +//! +//! [`msg!`]: msg +//! [fs]: https://doc.rust-lang.org/std/fmt/ +//! +//! Logs can be viewed in multiple ways: +//! +//! - The `solana logs` command displays logs for all transactions executed on a +//! network. Note though that transactions that fail during pre-flight +//! simulation are not displayed here. +//! - When submitting transactions via [`RpcClient`], if Rust's own logging is +//! active then the `solana_client` crate logs at the "debug" level any logs +//! for transactions that failed during simulation. If using [`env_logger`] +//! these logs can be activated by setting `RUST_LOG=solana_client=debug`. +//! - Logs can be retrieved from a finalized transaction by calling +//! [`RpcClient::get_transaction`]. +//! - Block explorers may display logs. +//! +//! [`RpcClient`]: https://docs.rs/solana-client/latest/solana_client/rpc_client/struct.RpcClient.html +//! [`env_logger`]: https://docs.rs/env_logger +//! [`RpcClient::get_transaction`]: https://docs.rs/solana-client/latest/solana_client/rpc_client/struct.RpcClient.html#method.get_transaction +//! +//! While most logging functions are defined in this module, [`Pubkey`]s can +//! also be efficiently logged with the [`Pubkey::log`] function. +//! +//! [`Pubkey`]: crate::pubkey::Pubkey +//! [`Pubkey::log`]: crate::pubkey::Pubkey::log use crate::account_info::AccountInfo; @@ -19,14 +52,33 @@ macro_rules! info { }; } -/// Print a message to the log +/// Print a message to the log. /// -/// Fast form: -/// 1. Single string: `msg!("hi")` +/// Supports simple strings as well as Rust [format strings][fs]. When passed a +/// single expression it will be passed directly to [`sol_log`]. The expression +/// must have type `&str`, and is typically used for logging static strings. +/// When passed something other than an expression, particularly +/// a sequence of expressions, the tokens will be passed through the +/// [`format!`] macro before being logged with `sol_log`. /// -/// The generic form incurs a very large runtime overhead so it should be used with care: -/// 3. Generalized format string: `msg!("Hello {}: 1, 2, {}", "World", 3)` +/// [fs]: https://doc.rust-lang.org/std/fmt/ +/// [`format!`]: https://doc.rust-lang.org/std/fmt/fn.format.html /// +/// Note that Rust's formatting machinery is relatively CPU-intensive +/// for constrained environments like the Solana VM. +/// +/// # Examples +/// +/// ``` +/// use solana_program::msg; +/// +/// // The fast form +/// msg!("verifying multisig"); +/// +/// // With formatting +/// let err = "not enough signers"; +/// msg!("multisig failed: {}", err); +/// ``` #[macro_export] macro_rules! msg { ($msg:expr) => { @@ -35,9 +87,7 @@ macro_rules! msg { ($($arg:tt)*) => ($crate::log::sol_log(&format!($($arg)*))); } -/// Print a string to the log -/// -/// @param message - Message to print +/// Print a string to the log. #[inline] pub fn sol_log(message: &str) { #[cfg(target_arch = "bpf")] @@ -54,10 +104,7 @@ extern "C" { fn sol_log_(message: *const u8, len: u64); } -/// Print 64-bit values represented as hexadecimal to the log -/// -/// @param argx - integer arguments to print - +/// Print 64-bit values represented as hexadecimal to the log. #[inline] pub fn sol_log_64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) { #[cfg(target_arch = "bpf")] @@ -74,9 +121,7 @@ extern "C" { fn sol_log_64_(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64); } -/// Print some slices as base64 -/// -/// @param data - The slices to print +/// Print some slices as base64. pub fn sol_log_data(data: &[&[u8]]) { #[cfg(target_arch = "bpf")] { @@ -91,9 +136,7 @@ pub fn sol_log_data(data: &[&[u8]]) { crate::program_stubs::sol_log_data(data); } -/// Print the hexadecimal representation of a slice -/// -/// @param slice - The array to print +/// Print the hexadecimal representation of a slice. #[allow(dead_code)] pub fn sol_log_slice(slice: &[u8]) { for (i, s) in slice.iter().enumerate() { @@ -101,10 +144,10 @@ pub fn sol_log_slice(slice: &[u8]) { } } -/// Print the hexadecimal representation of the program's input parameters +/// Print the hexadecimal representation of the program's input parameters. /// -/// @param ka - A pointer to an array of `AccountInfo` to print -/// @param data - A pointer to the instruction data to print +/// - `accounts` - A slice of [`AccountInfo`]. +/// - `data` - The instruction data. #[allow(dead_code)] pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) { for (i, account) in accounts.iter().enumerate() { @@ -125,7 +168,7 @@ pub fn sol_log_params(accounts: &[AccountInfo], data: &[u8]) { sol_log_slice(data); } -/// Print the remaining compute units the program may consume +/// Print the remaining compute units available to the program. #[inline] pub fn sol_log_compute_units() { #[cfg(target_arch = "bpf")] diff --git a/sdk/program/src/sysvar/mod.rs b/sdk/program/src/sysvar/mod.rs index cabb9e785e..6cf1a3a64e 100644 --- a/sdk/program/src/sysvar/mod.rs +++ b/sdk/program/src/sysvar/mod.rs @@ -101,6 +101,13 @@ pub trait Sysvar: fn size_of() -> usize { bincode::serialized_size(&Self::default()).unwrap() as usize } + + /// Deserializes a sysvar from its `AccountInfo`. + /// + /// # Errors + /// + /// If `account_info` does not have the same ID as the sysvar + /// this function returns [`ProgramError::InvalidArgument`]. fn from_account_info(account_info: &AccountInfo) -> Result { if !Self::check_id(account_info.unsigned_key()) { return Err(ProgramError::InvalidArgument);