pinocchio-development
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePinocchio Development Guide
Pinocchio开发指南
Build blazing-fast Solana programs with Pinocchio - a zero-dependency, zero-copy framework that delivers 88-95% compute unit reduction and 40% smaller binaries compared to traditional approaches.
使用Pinocchio构建极速Solana程序——这是一款零依赖、零拷贝框架,与传统方案相比,可减少88-95%的计算单元(CU),并生成缩小40%的二进制文件。
Overview
概述
Pinocchio is Anza's minimalist Rust library for writing Solana programs without the heavyweight crate. It treats incoming transaction data as a single byte slice, reading it in-place via zero-copy techniques.
solana-programPinocchio是Anza推出的极简Rust库,用于编写Solana程序,无需依赖庞大的 crate。它将传入的交易数据视为单个字节切片,通过零拷贝技术就地读取数据。
solana-programPerformance Comparison
性能对比
| Metric | Anchor | Native (solana-program) | Pinocchio |
|---|---|---|---|
| Token Transfer CU | ~6,000 | ~4,500 | ~600-800 |
| Binary Size | Large | Medium | Small (-40%) |
| Heap Allocation | Required | Required | Optional |
| Dependencies | Many | Several | Zero* |
*Only Solana SDK types for on-chain execution
| 指标 | Anchor | 原生(solana-program) | Pinocchio |
|---|---|---|---|
| 代币转账CU | ~6,000 | ~4,500 | ~600-800 |
| 二进制文件大小 | 大 | 中 | 小(缩小40%) |
| 堆内存分配 | 必需 | 必需 | 可选 |
| 依赖项 | 众多 | 若干 | 零* |
仅依赖Solana SDK类型用于链上执行
When to Use Pinocchio
何时使用Pinocchio
Use Pinocchio When:
- Building high-throughput programs (DEXs, orderbooks, games)
- Compute units are a bottleneck
- Binary size matters (program deployment costs)
- You need maximum control over memory
- Building infrastructure (tokens, vaults, escrows)
Consider Anchor Instead When:
- Rapid prototyping / MVPs
- Team unfamiliar with low-level Rust
- Complex account relationships
- Need extensive ecosystem tooling
- Audit timeline is tight (more auditors know Anchor)
推荐使用Pinocchio的场景:
- 构建高吞吐量程序(去中心化交易所、订单簿、游戏)
- 计算单元成为性能瓶颈
- 关注二进制文件大小(程序部署成本)
- 需要对内存进行最大程度的控制
- 构建基础设施类程序(代币、金库、托管)
推荐使用Anchor的场景:
- 快速原型开发/最小可行产品(MVP)
- 团队不熟悉底层Rust开发
- 存在复杂的账户关系
- 需要丰富的生态工具支持
- 审计时间紧张(更多审计人员熟悉Anchor)
Quick Start
快速开始
1. Project Setup
1. 项目设置
toml
undefinedtoml
undefinedCargo.toml
Cargo.toml
[package]
name = "my-program"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
[features]
default = []
bpf-entrypoint = []
[dependencies]
pinocchio = "0.10"
pinocchio-system = "0.4" # System Program CPI helpers
pinocchio-token = "0.4" # Token Program CPI helpers
bytemuck = { version = "1.14", features = ["derive"] }
[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
opt-level = 3
undefined[package]
name = "my-program"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
[features]
default = []
bpf-entrypoint = []
[dependencies]
pinocchio = "0.10"
pinocchio-system = "0.4" # System Program CPI 工具
pinocchio-token = "0.4" # Token Program CPI 工具
bytemuck = { version = "1.14", features = ["derive"] }
[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
opt-level = 3
undefined2. Basic Program Structure
2. 基础程序结构
rust
use pinocchio::{
account_info::AccountInfo,
entrypoint,
program_error::ProgramError,
pubkey::Pubkey,
ProgramResult,
};
// Declare entrypoint
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
// Route instructions by discriminator (first byte)
match instruction_data.first() {
Some(0) => initialize(accounts, &instruction_data[1..]),
Some(1) => execute(accounts, &instruction_data[1..]),
_ => Err(ProgramError::InvalidInstructionData),
}
}rust
use pinocchio::{
account_info::AccountInfo,
entrypoint,
program_error::ProgramError,
pubkey::Pubkey,
ProgramResult,
};
// 声明入口点
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
// 通过 discriminator(第一个字节)路由指令
match instruction_data.first() {
Some(0) => initialize(accounts, &instruction_data[1..]),
Some(1) => execute(accounts, &instruction_data[1..]),
_ => Err(ProgramError::InvalidInstructionData),
}
}3. Account Definition with Bytemuck
3. 使用Bytemuck定义账户
rust
use bytemuck::{Pod, Zeroable};
// Single-byte discriminator for account type
pub const VAULT_DISCRIMINATOR: u8 = 1;
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Vault {
pub discriminator: u8,
pub owner: [u8; 32], // Pubkey as bytes
pub balance: u64,
pub bump: u8,
pub _padding: [u8; 6], // Align to 8 bytes
}
impl Vault {
pub const LEN: usize = std::mem::size_of::<Self>();
pub fn from_account(account: &AccountInfo) -> Result<&Self, ProgramError> {
let data = account.try_borrow_data()?;
if data.len() < Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
if data[0] != VAULT_DISCRIMINATOR {
return Err(ProgramError::InvalidAccountData);
}
Ok(bytemuck::from_bytes(&data[..Self::LEN]))
}
pub fn from_account_mut(account: &AccountInfo) -> Result<&mut Self, ProgramError> {
let mut data = account.try_borrow_mut_data()?;
if data.len() < Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
Ok(bytemuck::from_bytes_mut(&mut data[..Self::LEN]))
}
}rust
use bytemuck::{Pod, Zeroable};
// 账户类型的单字节 discriminator
pub const VAULT_DISCRIMINATOR: u8 = 1;
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Vault {
pub discriminator: u8,
pub owner: [u8; 32], // Pubkey 字节形式
pub balance: u64,
pub bump: u8,
pub _padding: [u8; 6], // 对齐到8字节
}
impl Vault {
pub const LEN: usize = std::mem::size_of::<Self>();
pub fn from_account(account: &AccountInfo) -> Result<&Self, ProgramError> {
let data = account.try_borrow_data()?;
if data.len() < Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
if data[0] != VAULT_DISCRIMINATOR {
return Err(ProgramError::InvalidAccountData);
}
Ok(bytemuck::from_bytes(&data[..Self::LEN]))
}
pub fn from_account_mut(account: &AccountInfo) -> Result<&mut Self, ProgramError> {
let mut data = account.try_borrow_mut_data()?;
if data.len() < Self::LEN {
return Err(ProgramError::InvalidAccountData);
}
Ok(bytemuck::from_bytes_mut(&mut data[..Self::LEN]))
}
}Instructions
指令开发
Step 1: Define Account Validation
步骤1:定义账户验证逻辑
Create a struct to hold validated accounts:
rust
pub struct InitializeAccounts<'a> {
pub vault: &'a AccountInfo,
pub owner: &'a AccountInfo,
pub system_program: &'a AccountInfo,
}
impl<'a> InitializeAccounts<'a> {
pub fn parse(accounts: &'a [AccountInfo]) -> Result<Self, ProgramError> {
let [vault, owner, system_program, ..] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// Validate owner is signer
if !owner.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
// Validate system program
if system_program.key() != &pinocchio_system::ID {
return Err(ProgramError::IncorrectProgramId);
}
Ok(Self {
vault,
owner,
system_program,
})
}
}创建结构体存储已验证的账户:
rust
pub struct InitializeAccounts<'a> {
pub vault: &'a AccountInfo,
pub owner: &'a AccountInfo,
pub system_program: &'a AccountInfo,
}
impl<'a> InitializeAccounts<'a> {
pub fn parse(accounts: &'a [AccountInfo]) -> Result<Self, ProgramError> {
let [vault, owner, system_program, ..] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// 验证owner是签名者
if !owner.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
// 验证系统程序
if system_program.key() != &pinocchio_system::ID {
return Err(ProgramError::IncorrectProgramId);
}
Ok(Self {
vault,
owner,
system_program,
})
}
}Step 2: Implement Instruction Handler
步骤2:实现指令处理函数
rust
use pinocchio_system::instructions::CreateAccount;
pub fn initialize(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let ctx = InitializeAccounts::parse(accounts)?;
// Derive PDA
let (pda, bump) = Pubkey::find_program_address(
&[b"vault", ctx.owner.key().as_ref()],
&crate::ID,
);
// Verify PDA matches
if ctx.vault.key() != &pda {
return Err(ProgramError::InvalidSeeds);
}
// Create account via CPI
let space = Vault::LEN as u64;
let rent = pinocchio::sysvar::rent::Rent::get()?;
let lamports = rent.minimum_balance(space as usize);
CreateAccount {
from: ctx.owner,
to: ctx.vault,
lamports,
space,
owner: &crate::ID,
}
.invoke_signed(&[&[b"vault", ctx.owner.key().as_ref(), &[bump]]])?;
// Initialize account data
let vault = Vault::from_account_mut(ctx.vault)?;
vault.discriminator = VAULT_DISCRIMINATOR;
vault.owner = ctx.owner.key().to_bytes();
vault.balance = 0;
vault.bump = bump;
Ok(())
}rust
use pinocchio_system::instructions::CreateAccount;
pub fn initialize(accounts: &[AccountInfo], data: &[u8]) -> ProgramResult {
let ctx = InitializeAccounts::parse(accounts)?;
// 推导PDA
let (pda, bump) = Pubkey::find_program_address(
&[b"vault", ctx.owner.key().as_ref()],
&crate::ID,
);
// 验证PDA匹配
if ctx.vault.key() != &pda {
return Err(ProgramError::InvalidSeeds);
}
// 通过CPI创建账户
let space = Vault::LEN as u64;
let rent = pinocchio::sysvar::rent::Rent::get()?;
let lamports = rent.minimum_balance(space as usize);
CreateAccount {
from: ctx.owner,
to: ctx.vault,
lamports,
space,
owner: &crate::ID,
}
.invoke_signed(&[&[b"vault", ctx.owner.key().as_ref(), &[bump]]])?;
// 初始化账户数据
let vault = Vault::from_account_mut(ctx.vault)?;
vault.discriminator = VAULT_DISCRIMINATOR;
vault.owner = ctx.owner.key().to_bytes();
vault.balance = 0;
vault.bump = bump;
Ok(())
}Entrypoint Options
入口点选项
Pinocchio provides three entrypoint macros with different trade-offs:
Pinocchio提供三个入口点宏,各有不同的权衡:
1. Standard Entrypoint (Recommended for most cases)
1. 标准入口点(大多数场景推荐)
rust
use pinocchio::entrypoint;
entrypoint!(process_instruction);- Sets up heap allocator
- Configures panic handler
- Deserializes accounts automatically
rust
use pinocchio::entrypoint;
entrypoint!(process_instruction);- 配置堆内存分配器
- 设置panic处理程序
- 自动反序列化账户
2. Lazy Entrypoint (Best for single-instruction programs)
2. 延迟入口点(单指令程序最佳选择)
rust
use pinocchio::lazy_entrypoint;
lazy_entrypoint!(process_instruction);
pub fn process_instruction(mut context: InstructionContext) -> ProgramResult {
// Accounts parsed on-demand
let account = context.next_account()?;
let data = context.instruction_data();
Ok(())
}- Defers parsing until needed
- Best CU savings for simple programs
- 80-87% CU reduction in memo program benchmarks
rust
use pinocchio::lazy_entrypoint;
lazy_entrypoint!(process_instruction);
pub fn process_instruction(mut context: InstructionContext) -> ProgramResult {
// 按需解析账户
let account = context.next_account()?;
let data = context.instruction_data();
Ok(())
}- 延迟解析直到需要时
- 为简单程序带来最大的计算单元节省
- 在备忘录程序基准测试中减少80-87%的计算单元
3. No Allocator (Maximum optimization)
3. 无分配器(极致优化)
rust
use pinocchio::{entrypoint, no_allocator};
no_allocator!();
entrypoint!(process_instruction);- Disables heap entirely
- Cannot use ,
String,VecBox - Best for statically-sized operations
rust
use pinocchio::{entrypoint, no_allocator};
no_allocator!();
entrypoint!(process_instruction);- 完全禁用堆内存
- 无法使用、
String、Vec等类型Box - 最适合静态大小的操作
CPI Patterns
CPI模式
System Program CPI
系统程序CPI
rust
use pinocchio_system::instructions::{CreateAccount, Transfer};
// Create account
CreateAccount {
from: payer,
to: new_account,
lamports: rent_lamports,
space: account_size,
owner: &program_id,
}.invoke()?;
// Transfer SOL
Transfer {
from: source,
to: destination,
lamports: amount,
}.invoke()?;
// Transfer with PDA signer
Transfer {
from: pda_account,
to: destination,
lamports: amount,
}.invoke_signed(&[&[b"vault", owner.as_ref(), &[bump]]])?;rust
use pinocchio_system::instructions::{CreateAccount, Transfer};
// 创建账户
CreateAccount {
from: payer,
to: new_account,
lamports: rent_lamports,
space: account_size,
owner: &program_id,
}.invoke()?;
// 转账SOL
Transfer {
from: source,
to: destination,
lamports: amount,
}.invoke()?;
// 使用PDA签名者转账
Transfer {
from: pda_account,
to: destination,
lamports: amount,
}.invoke_signed(&[&[b"vault", owner.as_ref(), &[bump]]])?;Token Program CPI
代币程序CPI
rust
use pinocchio_token::instructions::{Transfer, MintTo, Burn};
// Transfer tokens
Transfer {
source: from_token_account,
destination: to_token_account,
authority: owner,
amount: token_amount,
}.invoke()?;
// Mint tokens (with PDA authority)
MintTo {
mint: mint_account,
token_account: destination,
authority: mint_authority_pda,
amount: mint_amount,
}.invoke_signed(&[&[b"mint_auth", &[bump]]])?;rust
use pinocchio_token::instructions::{Transfer, MintTo, Burn};
// 转账代币
Transfer {
source: from_token_account,
destination: to_token_account,
authority: owner,
amount: token_amount,
}.invoke()?;
// 铸造代币(使用PDA权限)
MintTo {
mint: mint_account,
token_account: destination,
authority: mint_authority_pda,
amount: mint_amount,
}.invoke_signed(&[&[b"mint_auth", &[bump]]])?;Custom CPI (Third-party programs)
自定义CPI(第三方程序)
rust
use pinocchio::{
instruction::{AccountMeta, Instruction},
program::invoke,
};
// Build instruction manually
let accounts = vec![
AccountMeta::new(*account1.key(), false),
AccountMeta::new_readonly(*account2.key(), true),
];
let ix = Instruction {
program_id: &external_program_id,
accounts: &accounts,
data: &instruction_data,
};
invoke(&ix, &[account1, account2])?;rust
use pinocchio::{
instruction::{AccountMeta, Instruction},
program::invoke,
};
// 手动构建指令
let accounts = vec![
AccountMeta::new(*account1.key(), false),
AccountMeta::new_readonly(*account2.key(), true),
];
let ix = Instruction {
program_id: &external_program_id,
accounts: &accounts,
data: &instruction_data,
};
invoke(&ix, &[account1, account2])?;Account Validation Patterns
账户验证模式
Pattern 1: TryFrom Trait
模式1:TryFrom trait
rust
pub struct DepositAccounts<'a> {
pub vault: &'a AccountInfo,
pub owner: &'a AccountInfo,
pub system_program: &'a AccountInfo,
}
impl<'a> TryFrom<&'a [AccountInfo]> for DepositAccounts<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let [vault, owner, system_program, ..] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// Validations
require!(owner.is_signer(), ProgramError::MissingRequiredSignature);
require!(vault.is_writable(), ProgramError::InvalidAccountData);
Ok(Self { vault, owner, system_program })
}
}
// Usage
let ctx = DepositAccounts::try_from(accounts)?;rust
pub struct DepositAccounts<'a> {
pub vault: &'a AccountInfo,
pub owner: &'a AccountInfo,
pub system_program: &'a AccountInfo,
}
impl<'a> TryFrom<&'a [AccountInfo]> for DepositAccounts<'a> {
type Error = ProgramError;
fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
let [vault, owner, system_program, ..] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
// 验证逻辑
require!(owner.is_signer(), ProgramError::MissingRequiredSignature);
require!(vault.is_writable(), ProgramError::InvalidAccountData);
Ok(Self { vault, owner, system_program })
}
}
// 使用方式
let ctx = DepositAccounts::try_from(accounts)?;Pattern 2: Builder Pattern
模式2:构建器模式
rust
pub struct AccountValidator<'a> {
account: &'a AccountInfo,
}
impl<'a> AccountValidator<'a> {
pub fn new(account: &'a AccountInfo) -> Self {
Self { account }
}
pub fn is_signer(self) -> Result<Self, ProgramError> {
if !self.account.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
Ok(self)
}
pub fn is_writable(self) -> Result<Self, ProgramError> {
if !self.account.is_writable() {
return Err(ProgramError::InvalidAccountData);
}
Ok(self)
}
pub fn has_owner(self, owner: &Pubkey) -> Result<Self, ProgramError> {
if self.account.owner() != owner {
return Err(ProgramError::IllegalOwner);
}
Ok(self)
}
pub fn build(self) -> &'a AccountInfo {
self.account
}
}
// Usage
let owner = AccountValidator::new(&accounts[0])
.is_signer()?
.is_writable()?
.build();rust
pub struct AccountValidator<'a> {
account: &'a AccountInfo,
}
impl<'a> AccountValidator<'a> {
pub fn new(account: &'a AccountInfo) -> Self {
Self { account }
}
pub fn is_signer(self) -> Result<Self, ProgramError> {
if !self.account.is_signer() {
return Err(ProgramError::MissingRequiredSignature);
}
Ok(self)
}
pub fn is_writable(self) -> Result<Self, ProgramError> {
if !self.account.is_writable() {
return Err(ProgramError::InvalidAccountData);
}
Ok(self)
}
pub fn has_owner(self, owner: &Pubkey) -> Result<Self, ProgramError> {
if self.account.owner() != owner {
return Err(ProgramError::IllegalOwner);
}
Ok(self)
}
pub fn build(self) -> &'a AccountInfo {
self.account
}
}
// 使用方式
let owner = AccountValidator::new(&accounts[0])
.is_signer()?
.is_writable()?
.build();Pattern 3: Macro-based Validation
模式3:基于宏的验证
rust
macro_rules! require {
($cond:expr, $err:expr) => {
if !$cond {
return Err($err);
}
};
}
macro_rules! require_signer {
($account:expr) => {
require!($account.is_signer(), ProgramError::MissingRequiredSignature)
};
}
macro_rules! require_writable {
($account:expr) => {
require!($account.is_writable(), ProgramError::InvalidAccountData)
};
}rust
macro_rules! require {
($cond:expr, $err:expr) => {
if !$cond {
return Err($err);
}
};
}
macro_rules! require_signer {
($account:expr) => {
require!($account.is_signer(), ProgramError::MissingRequiredSignature)
};
}
macro_rules! require_writable {
($account:expr) => {
require!($account.is_writable(), ProgramError::InvalidAccountData)
};
}PDA Operations
PDA操作
Deriving PDAs
推导PDA
rust
use pinocchio::pubkey::Pubkey;
// Find PDA with bump
let (pda, bump) = Pubkey::find_program_address(
&[b"vault", user.key().as_ref()],
program_id,
);
// Create PDA with known bump (cheaper)
let pda = Pubkey::create_program_address(
&[b"vault", user.key().as_ref(), &[bump]],
program_id,
)?;rust
use pinocchio::pubkey::Pubkey;
// 查找带bump值的PDA
let (pda, bump) = Pubkey::find_program_address(
&[b"vault", user.key().as_ref()],
program_id,
);
// 使用已知bump值创建PDA(成本更低)
let pda = Pubkey::create_program_address(
&[b"vault", user.key().as_ref(), &[bump]],
program_id,
)?;PDA Signing for CPI
用于CPI的PDA签名
rust
// Single seed set
let signer_seeds = &[b"vault", owner.as_ref(), &[bump]];
Transfer {
from: vault_pda,
to: destination,
lamports: amount,
}.invoke_signed(&[signer_seeds])?;
// Multiple PDA signers
let signer1 = &[b"vault", owner.as_ref(), &[bump1]];
let signer2 = &[b"authority", &[bump2]];
invoke_signed(&ix, &accounts, &[signer1, signer2])?;rust
// 单种子集
let signer_seeds = &[b"vault", owner.as_ref(), &[bump]];
Transfer {
from: vault_pda,
to: destination,
lamports: amount,
}.invoke_signed(&[signer_seeds])?;
// 多个PDA签名者
let signer1 = &[b"vault", owner.as_ref(), &[bump1]];
let signer2 = &[b"authority", &[bump2]];
invoke_signed(&ix, &accounts, &[signer1, signer2])?;Data Serialization
数据序列化
Fixed-Size with Bytemuck (Recommended)
固定大小数据使用Bytemuck(推荐)
rust
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct GameState {
pub discriminator: u8,
pub player: [u8; 32],
pub score: u64,
pub level: u8,
pub _padding: [u8; 6],
}
// Zero-copy read
let state: &GameState = bytemuck::from_bytes(&data);
// Zero-copy write
let state: &mut GameState = bytemuck::from_bytes_mut(&mut data);rust
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct GameState {
pub discriminator: u8,
pub player: [u8; 32],
pub score: u64,
pub level: u8,
pub _padding: [u8; 6],
}
// 零拷贝读取
let state: &GameState = bytemuck::from_bytes(&data);
// 零拷贝写入
let state: &mut GameState = bytemuck::from_bytes_mut(&mut data);Variable-Size with Borsh
可变大小数据使用Borsh
rust
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Metadata {
pub name: String,
pub symbol: String,
pub uri: String,
}
// Deserialize (allocates)
let metadata = Metadata::try_from_slice(data)?;
// Serialize
let mut buffer = Vec::new();
metadata.serialize(&mut buffer)?;rust
use borsh::{BorshDeserialize, BorshSerialize};
#[derive(BorshSerialize, BorshDeserialize)]
pub struct Metadata {
pub name: String,
pub symbol: String,
pub uri: String,
}
// 反序列化(会分配内存)
let metadata = Metadata::try_from_slice(data)?;
// 序列化
let mut buffer = Vec::new();
metadata.serialize(&mut buffer)?;Manual Parsing (Maximum control)
手动解析(最大控制权)
rust
pub fn parse_u64(data: &[u8]) -> Result<u64, ProgramError> {
if data.len() < 8 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(u64::from_le_bytes(data[..8].try_into().unwrap()))
}
pub fn parse_pubkey(data: &[u8]) -> Result<Pubkey, ProgramError> {
if data.len() < 32 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(Pubkey::new_from_array(data[..32].try_into().unwrap()))
}rust
pub fn parse_u64(data: &[u8]) -> Result<u64, ProgramError> {
if data.len() < 8 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(u64::from_le_bytes(data[..8].try_into().unwrap()))
}
pub fn parse_pubkey(data: &[u8]) -> Result<Pubkey, ProgramError> {
if data.len() < 32 {
return Err(ProgramError::InvalidInstructionData);
}
Ok(Pubkey::new_from_array(data[..32].try_into().unwrap()))
}IDL Generation with Shank
使用Shank生成IDL
Since Pinocchio doesn't auto-generate IDLs, use Shank:
rust
use shank::{ShankAccount, ShankInstruction};
#[derive(ShankAccount)]
pub struct Vault {
pub owner: Pubkey,
pub balance: u64,
}
#[derive(ShankInstruction)]
pub enum ProgramInstruction {
#[account(0, writable, signer, name = "vault")]
#[account(1, signer, name = "owner")]
#[account(2, name = "system_program")]
Initialize,
#[account(0, writable, name = "vault")]
#[account(1, signer, name = "owner")]
Deposit { amount: u64 },
}Generate IDL:
bash
shank idl -o idl.json -p src/lib.rs由于Pinocchio不会自动生成IDL,可使用Shank工具:
rust
use shank::{ShankAccount, ShankInstruction};
#[derive(ShankAccount)]
pub struct Vault {
pub owner: Pubkey,
pub balance: u64,
}
#[derive(ShankInstruction)]
pub enum ProgramInstruction {
#[account(0, writable, signer, name = "vault")]
#[account(1, signer, name = "owner")]
#[account(2, name = "system_program")]
Initialize,
#[account(0, writable, name = "vault")]
#[account(1, signer, name = "owner")]
Deposit { amount: u64 },
}生成IDL:
bash
shank idl -o idl.json -p src/lib.rsGuidelines
开发指南
- Always use single-byte discriminators for instructions and accounts
- Prefer bytemuck over Borsh for fixed-size data
- Use for single-instruction programs
lazy_entrypoint! - Validate all accounts before processing
- Use for PDA-owned account operations
invoke_signed - Add padding to align structs to 8 bytes
- Test with or Bankrun
solana-program-test
- 始终为指令和账户使用单字节discriminator
- 对于固定大小数据,优先使用bytemuck而非Borsh
- 单指令程序使用
lazy_entrypoint! - 处理前验证所有账户
- PDA所属账户操作使用
invoke_signed - 添加填充使结构体对齐到8字节
- 使用或Bankrun进行测试
solana-program-test
Files in This Skill
本技能包含的文件
pinocchio-development/
├── SKILL.md # This file
├── scripts/
│ ├── scaffold-program.sh # Project generator
│ └── benchmark-cu.sh # CU benchmarking
├── resources/
│ ├── account-patterns.md # Validation patterns
│ ├── cpi-reference.md # CPI quick reference
│ ├── optimization-checklist.md # Performance tips
│ └── anchor-comparison.md # Side-by-side comparison
├── examples/
│ ├── counter/ # Basic counter program
│ ├── vault/ # PDA vault with deposits
│ ├── token-operations/ # Token minting/transfers
│ └── transfer-hook/ # Token-2022 hook
├── templates/
│ └── program-template.rs # Starter template
└── docs/
├── migration-from-anchor.md # Anchor migration guide
└── edge-cases.md # Gotchas and solutionspinocchio-development/
├── SKILL.md # 本文档
├── scripts/
│ ├── scaffold-program.sh # 项目生成脚本
│ └── benchmark-cu.sh # 计算单元基准测试脚本
├── resources/
│ ├── account-patterns.md # 验证模式文档
│ ├── cpi-reference.md # CPI速查手册
│ ├── optimization-checklist.md # 性能优化技巧
│ └── anchor-comparison.md # 对比Anchor的文档
├── examples/
│ ├── counter/ # 基础计数器程序
│ ├── vault/ # 带存款功能的PDA金库
│ ├── token-operations/ # 代币铸造/转账程序
│ └── transfer-hook/ # Token-2022钩子程序
├── templates/
│ └── program-template.rs # 程序模板
└── docs/
├── migration-from-anchor.md # Anchor迁移指南
└── edge-cases.md # 注意事项与解决方案Performance Benchmarks (2025)
性能基准测试(2025年)
Latest benchmarks demonstrate Pinocchio's efficiency:
| Program | Anchor CU | Pinocchio CU | Reduction |
|---|---|---|---|
| Token Transfer | ~6,000 | ~600-800 | 88-95% |
| Memo Program | ~650 | ~108 | 83% |
| Counter | ~800 | ~104 | 87% |
Assembly implementation: 104 CU, Pinocchio: 108 CU, Basic Anchor: 649 CU
最新基准测试展示了Pinocchio的效率:
| 程序 | Anchor计算单元 | Pinocchio计算单元 | 减少比例 |
|---|---|---|---|
| 代币转账 | ~6,000 | ~600-800 | 88-95% |
| 备忘录程序 | ~650 | ~108 | 83% |
| 计数器 | ~800 | ~104 | 87% |
汇编实现:104 CU,Pinocchio:108 CU,基础Anchor:649 CU
SDK Roadmap (Anza Plans)
SDK路线图(Anza计划)
The Anza team has announced plans for SDK v3:
Anza团队已宣布SDK v3的计划:
Coming Improvements
即将到来的改进
- Unified Base Types: Reusable types across Anchor and Pinocchio
- New Serialization Library: Zero-copy, simpler enums, variable-length types
- ATA Program Optimization: Pinocchio-optimized Associated Token Account
- Token22 Optimization: Full Token Extensions support with minimal CU usage
- 统一基础类型:Anchor与Pinocchio之间可复用的类型
- 新序列化库:零拷贝、更简洁的枚举、可变长度类型
- ATA程序优化:Pinocchio优化的关联代币账户
- Token22优化:完整支持代币扩展,同时最小化计算单元使用
Integration Progress
集成进展
- Pinocchio types are being integrated into the core Solana SDK
- Improved interoperability between Anchor and Pinocchio programs
- Pinocchio类型正被整合到核心Solana SDK中
- 提升Anchor与Pinocchio程序之间的互操作性
Notes
注意事项
- Pinocchio is unaudited - use with caution in production
- Version 0.10.x is current (latest: )
pinocchio = "0.10" - and
pinocchio-system = "0.4"for CPI helperspinocchio-token = "0.4" - Token-2022 support via is under active development
pinocchio-token - For client generation, use Codama with your Shank-generated IDL
- Maintained by Anza (Solana Agave client developers)
- Pinocchio未经过审计——生产环境使用请谨慎
- 当前版本为0.10.x(最新版本:)
pinocchio = "0.10" - CPI工具使用和
pinocchio-system = "0.4"pinocchio-token = "0.4" - 对Token-2022的支持正在积极开发中
pinocchio-token - 客户端生成可使用Codama配合Shank生成的IDL
- 由Anza(Solana Agave客户端开发者)维护