diff --git a/README.md b/README.md index 1a693af3..46584365 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,13 @@ Limestone

- Create Solana programs with no dependencies attached. + Create Solana programs with no dependencies* attached.

+

@@ -23,25 +24,20 @@ There are no dependencies on me

-## Overview - -Pinocchio is a zero-dependency library to create Solana programs in Rust. It takes advantage of the way SVM loaders serialize the program input parameters into a byte array that is then passed to the program's entrypoint to define zero-copy types to read the input. Since the communication between a program and SVM loader — either at the first time the program is called or when one program invokes the instructions of another program — is done via a byte array, a program can define its own types. This completely eliminates the dependency on the `solana-program` crate, which in turn mitigates dependency issues by having a crate specifically designed to create on-chain programs. +

+* It includes only dependencies to Solana SDK types — i.e., no external dependencies. +

-As a result, Pinocchio can be used as a replacement for [`solana-program`](https://crates.io/crates/solana-program) to write on-chain programs, which are optimized in terms of both compute units consumption and binary size. +## Overview -The library defines: -* program entrypoint -* core data types -* logging macros -* `syscall` functions -* access to system accounts (`sysvars`) -* cross-program invocation +Pinocchio is a *no-external* dependencies library to create Solana programs in Rust. The only dependencies are types from the Solana SDK specifically designed for on-chain programs. This mitigates dependency issues and offers an efficient zero-copy library to write programs, optimized in terms of both compute units consumption and binary size. ## Features -* Zero dependencies and `no_std` crate -* Efficient `entrypoint!` macro – no copies or allocations -* Improved CU consumption of cross-program invocations +* `no_std` crate +* no-external dependencies +* efficient `program_entrypoint!` macro – no copies or allocations +* lightweight `lazy_program_entrypoint` providing more control over how the input is parsed ## Getting started @@ -57,21 +53,21 @@ This will add `pinocchio` as a dependency to your project. A Solana program needs to define an entrypoint, which will be called by the runtime to begin the program execution. The `entrypoint!` macro emits the common boilerplate to set up the program entrypoint. The macro will also set up [global allocator](https://doc.rust-lang.org/stable/core/alloc/trait.GlobalAlloc.html) and [custom panic hook](https://github.com/anza-xyz/rust/blob/2830febbc59d44bdd7ad2c3b81731f1d08b96eba/library/std/src/sys/pal/sbf/mod.rs#L49) using the [default_allocator!](https://docs.rs/pinocchio/latest/pinocchio/macro.default_allocator.html) and [default_panic_handler!](https://docs.rs/pinocchio/latest/pinocchio/macro.default_panic_handler.html) macros. -The [`entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.entrypoint.html) is a convenience macro that invokes three other macros to set all symbols required for a program execution: +The [`entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.entrypoint.html) is a convenience macro that invokes three other macros to set all components required for a program execution: * [`program_entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.program_entrypoint.html): declares the program entrypoint * [`default_allocator!`](https://docs.rs/pinocchio/latest/pinocchio/macro.default_allocator.html): declares the default (bump) global allocator * [`default_panic_handler!`](https://docs.rs/pinocchio/latest/pinocchio/macro.default_panic_handler.html): declares the default panic handler -If all dependencies are `no_std`, you should append [`nostd_panic_handler!`](https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html) to declare a rust runtime panic handler. There's no need to do this if any dependency is `std` since rust compiler will emit std panic handler. +When all dependencies are `no_std`, you should use [`nostd_panic_handler!`](https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html) instead of `default_panic_handler!` to declare a rust runtime panic handler. There's no need to do this when any dependency is `std` since rust compiler will emit a panic handler. To use the `entrypoint!` macro, use the following in your entrypoint definition: ```rust use pinocchio::{ account::AccountView, + Address, entrypoint, - ProgramResult, - Address + ProgramResult }; entrypoint!(process_instruction); @@ -91,17 +87,17 @@ The information from the input is parsed into their own entities: * `accounts`: the accounts received * `instruction_data`: data for the instruction -`pinocchio` also offers variations of the program entrypoint (`lazy_program_allocator`) and global allocator (`no_allocator`). In order to use these, the program needs to specify the program entrypoint, global allocator and panic handler individually. The `entrypoint!` macro is equivalent to writing: +`pinocchio` also offers variations of the program entrypoint (`lazy_program_entrypoint`) and global allocator (`no_allocator`). In order to use these, the program needs to specify the program entrypoint, global allocator and panic handler individually. The `entrypoint!` macro is equivalent to writing: ```rust program_entrypoint!(process_instruction); default_allocator!(); default_panic_handler!(); ``` -Any of these macros can be replaced by other implementations and `pinocchio` offers a couple of variants for this. +Any of these macros can be replaced by alternative implementations. 📌 [`lazy_program_entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.lazy_program_entrypoint.html) -The `entrypoint!` macro looks similar to the "standard" one found in `solana-program`. It parses the whole input and provides the `program_id`, `accounts` and `instruction_data` separately. This consumes compute units before the program begins its execution. In some cases, it is beneficial for a program to have more control when the input parsing is happening, even whether the parsing is needed or not — this is the purpose of the [`lazy_program_entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.lazy_program_entrypoint.html) macro. This macro only wraps the program input and provides methods to parse the input on demand. +The `entrypoint!` macro looks similar to the "standard" one found in [`solana-program-entrypoint`](https://docs.rs/solana-program-entrypoint/latest/solana_program_entrypoint/macro.entrypoint.html). It parses the whole input and provides the `program_id`, `accounts` and `instruction_data` separately. This consumes compute units before the program begins its execution. In some cases, it is beneficial for a program to have more control when the input parsing is happening, even whether the parsing is needed or not — this is the purpose of the [`lazy_program_entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.lazy_program_entrypoint.html) macro. This macro only wraps the program input and provides methods to parse the input on demand. The `lazy_entrypoint` is suitable for programs that have a single or very few instructions, since it requires the program to handle the parsing, which can become complex as the number of instructions increases. For *larger* programs, the [`program_entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.program_entrypoint.html) will likely be easier and more efficient to use. @@ -166,7 +162,21 @@ pub fn process_instruction( > ⚠️ **Note:** > The `no_allocator!` macro can also be used in combination with the `lazy_program_entrypoint!`. -## Crate feature: `alloc` +Since the `no_allocator!` macro does not allocate memory, the `32kb` memory region reserved for the heap remains unused. To take advantage of this, the `no_allocator!` macro emits an `allocate_unchecked` helper function that allows you to manually reserve memory for a type at compile time. + +```rust +/// static allocation: +/// - 0 is the offset when the type will be allocated +/// - `allocate_unchecked` returns a mutable reference to the allocated type +let lamports = allocate_unchecked::(0); +*lamports = 1_000_000_000; +``` + +Note that it is the developer's responsibility to ensure that types do not overlap in memory — the `offset + ` of different types must not overlap. + +## Crate features + +### `alloc` The `alloc` feature is enabled by default and it uses the [`alloc`](https://doc.rust-lang.org/alloc/) crate. This provides access to dynamic memory allocation in combination with the `default_allocator`, e.g., required to use `String` and `Vec` in a program. Helpers that need to allocate memory, such as fetching `SlotHashes` sysvar data, are also available. @@ -179,9 +189,17 @@ pinocchio = { version = "0.10.0", default-features = false } > ⚠️ **Note:** > The `default_allocator` macro is not available when disabling the `alloc` feature. +### `cpi` + +The `cpi` feature enables the cross-program invocation helpers, as well as types to define instructions and signer information. + +``` +pinocchio = { version = "0.10.0", features = ["cpi"] } +``` + ## Advance entrypoint configuration -The symbols emitted by the entrypoint macros — program entrypoint, global allocator and default panic handler — can only be defined once globally. If the program crate is also intended to be used as a library, it is common practice to define a Cargo [feature](https://doc.rust-lang.org/cargo/reference/features.html) in your program crate to conditionally enable the module that includes the `entrypoint!` macro invocation. The convention is to name the feature `bpf-entrypoint`. +The components emitted by the entrypoint macros — program entrypoint, global allocator and default panic handler — can only be defined once globally. If the program crate is also intended to be used as a library, it is common practice to define a Cargo [feature](https://doc.rust-lang.org/cargo/reference/features.html) in your program crate to conditionally enable the module that includes the `entrypoint!` macro invocation. The convention is to name the feature `bpf-entrypoint`. ```rust #[cfg(feature = "bpf-entrypoint")] diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index a6cf88fd..778fbe86 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -8,13 +8,17 @@ readme = "./README.md" repository = { workspace = true } rust-version = { workspace = true } +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + [lib] crate-type = ["rlib"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(target_os, values("solana"))', - 'cfg(target_feature, values("static-syscalls"))', ] } [features] diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 7aa54f4d..0cddda12 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -1,18 +1,10 @@ -//! # Pinocchio +//! # A library to create Solana programs in Rust //! -//! Pinocchio is a "no-external" dependencies library to create Solana programs -//! in Rust, which means that the only dependencies are from the Solana SDK. This -//! reduces the chance of dependency conflicts when compiling the program. -//! -//! It takes advantage of the way SVM loaders serialize the program input -//! parameters into a byte array that is then passed to the program's entrypoint -//! to use zero-copy types to read the input - these types are defined in an efficient -//! way taking into consideration that they will be used in on-chain programs. -//! -//! It is intended to be used by on-chain programs only; for off-chain programs, -//! use instead [`solana-sdk`] crates. -//! -//! [`solana-sdk`]: https://docs.rs/solana-sdk/latest/solana_sdk/ +//! Pinocchio is a *no-external* dependencies library to create Solana programs +//! in Rust. The only dependencies are types from the Solana SDK specifically +//! designed for on-chain programs. This mitigates dependency issues and offers +//! an efficient zero-copy library to write programs, optimized in terms of both +//! compute units consumption and binary size. //! //! ## Defining the program entrypoint //! @@ -23,17 +15,23 @@ //! and [panic handler](https://doc.rust-lang.org/nomicon/panic-handler.html) using //! the [`default_allocator!`] and [`default_panic_handler!`] macros. //! -//! The [`entrypoint!`] is a convenience macro that invokes three other macros to set -//! all symbols required for a program execution: +//! The [`entrypoint!`](https://docs.rs/pinocchio/latest/pinocchio/macro.entrypoint.html) +//! is a convenience macro that invokes three other macros to set all components +//! required for a program execution: //! //! * [`program_entrypoint!`]: declares the program entrypoint //! * [`default_allocator!`]: declares the default (bump) global allocator //! * [`default_panic_handler!`]: declares the default panic handler //! +//! When all dependencies are `no_std`, you should use [`nostd_panic_handler!`](https://docs.rs/pinocchio/latest/pinocchio/macro.nostd_panic_handler.html) +//! instead of `default_panic_handler!` to declare a rust runtime panic handler. +//! There's no need to do this when any dependency is `std` since rust compiler will +//! emit a panic handler. +//! //! To use the `entrypoint!` macro, use the following in your entrypoint definition: //! ```ignore //! use pinocchio::{ -//! AccountView, +//! account::AccountView, //! Address, //! entrypoint, //! ProgramResult @@ -56,18 +54,16 @@ //! * `accounts`: the accounts received //! * `instruction_data`: data for the instruction //! -//! Pinocchio also offers variations of the program entrypoint -//! ([`lazy_program_entrypoint`]) and global allocator ([`no_allocator`]). In -//! order to use these, the program needs to specify the program entrypoint, -//! global allocator and panic handler individually. The [`entrypoint!`] macro -//! is equivalent to writing: +//! `pinocchio` also offers variations of the program entrypoint (`lazy_program_entrypoint`) +//! and global allocator (`no_allocator`). In order to use these, the program needs to +//! specify the program entrypoint, global allocator and panic handler individually. The +//! `entrypoint!` macro is equivalent to writing: //! ```ignore //! program_entrypoint!(process_instruction); //! default_allocator!(); //! default_panic_handler!(); //! ``` -//! Any of these macros can be replaced by other implementations and Pinocchio -//! offers a couple of variants for this. +//! Any of these macros can be replaced by alternative implementations. //! //! ### [`lazy_program_entrypoint!`] //! @@ -160,16 +156,44 @@ //! 💡 The [`no_allocator!`] macro can also be used in combination with the //! [`lazy_program_entrypoint!`]. //! -//! ## `std` crate feature +//! Since the `no_allocator!` macro does not allocate memory, the `32kb` memory region +//! reserved for the heap remains unused. To take advantage of this, the `no_allocator!` +//! macro emits an `allocate_unchecked` helper function that allows you to manually +//! reserve memory for a type at compile time. +//! ```ignore +//! /// static allocation: +//! /// - 0 is the offset when the type will be allocated +//! /// - `allocate_unchecked` returns a mutable reference to the allocated type +//! let lamports = allocate_unchecked::(0); +//! *lamports = 1_000_000_000; +//! ``` +//! +//! Note that it is the developer's responsibility to ensure that types do not overlap +//! in memory - the `offset + ` of different types must not overlap. +//! +//! ## Crate features +//! +//! ### `alloc` +//! +//! The `alloc` feature is enabled by default and it uses the [`alloc`](https://doc.rust-lang.org/alloc/) +//! crate. This provides access to dynamic memory allocation in combination with the +//! [`default_allocator!`], e.g., required to use `String` and `Vec` in a program. +//! Helpers that need to allocate memory, such as fetching [`crate::sysvars::slot_hashes::SlotHashes::fetch`] +//! sysvar data, are also available. +//! +//! When no allocation is needed or desired, the feature can be disabled: +//! +//! ```ignore +//! pinocchio = { version = "0.10.0", default-features = false } +//! ``` +//! +//! ### `cpi` +//! +//! The `cpi` feature enables the cross-program invocation helpers, as well as types +//! to define instructions and signer information. //! -//! By default, Pinocchio is a `no_std` crate. This means that it does not use any -//! code from the standard (`std`) library. While this does not affect how Pinocchio -//! is used, there is a one particular apparent difference. Helpers that need to -//! allocate memory, such as fetching `SlotHashes` sysvar, are not available. To -//! enable these helpers, the `alloc` feature must be enabled when adding Pinocchio -//! as a dependency: //! ```ignore -//! pinocchio = { version = "0.10.0", features = ["alloc"] } +//! pinocchio = { version = "0.10.0", features = ["cpi"] } //! ``` //! //! ## Advanced entrypoint configuration @@ -185,7 +209,7 @@ //! #[cfg(feature = "bpf-entrypoint")] //! mod entrypoint { //! use pinocchio::{ -//! AccountView, +//! account::AccountView, //! Address, //! entrypoint, //! ProgramResult