Skip to content

Commit c69a7bb

Browse files
committed
feat: Added pinocchio counter program
1 parent 8735834 commit c69a7bb

File tree

17 files changed

+654
-1
lines changed

17 files changed

+654
-1
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ members = [
1010
"basics/counter/native/program",
1111
"basics/counter/anchor/programs/counter_anchor",
1212
"basics/counter/mpl-stack",
13+
"basics/counter/pinocchio/program",
1314
"basics/create-account/native/program",
1415
"basics/create-account/anchor/programs/create-system-account",
1516
"basics/cross-program-invocation/anchor/programs/*",
@@ -35,7 +36,7 @@ members = [
3536
"tokens/token-2022/non-transferable/native/program",
3637
"tokens/token-2022/default-account-state/native/program",
3738
"tokens/token-2022/transfer-fee/native/program",
38-
"tokens/token-2022/multiple-extensions/native/program",
39+
"tokens/token-2022/multiple-extensions/native/program", "basics/counter/pinocchio", "basics/counter/pinocchio/pinocchio-counter",
3940
]
4041
resolver = "2"
4142

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"scripts": {
3+
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./test/counter.test.ts",
4+
"build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test",
5+
"build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so",
6+
"deploy": "solana program deploy ./program/target/so/hello_solana_program_pinocchio.so"
7+
},
8+
"dependencies": {
9+
"@solana/web3.js": "^1.47.3"
10+
},
11+
"devDependencies": {
12+
"@types/bn.js": "^5.1.0",
13+
"@types/chai": "^4.3.1",
14+
"@types/mocha": "^9.1.1",
15+
"@types/node": "^22.15.2",
16+
"chai": "^4.3.4",
17+
"mocha": "^9.0.3",
18+
"solana-bankrun": "^0.3.0",
19+
"ts-mocha": "^10.0.0",
20+
"typescript": "^4.3.5"
21+
}
22+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "pinocchio-counter"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
crate-type = ["cdylib", "lib"]
8+
9+
[dependencies]
10+
pinocchio = "0.9.2"
11+
pinocchio-log = "0.5.1"
12+
pinocchio-pubkey = "0.3.0"
13+
bytemuck = {version = "1.23.2", features = ["derive"]}
14+
pinocchio-system = "0.3.0"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use bytemuck::{Pod, Zeroable};
2+
use pinocchio::{account_info::AccountInfo, msg, program_error::ProgramError};
3+
4+
pub fn load<T>(account: &AccountInfo) -> Result<&mut T, ProgramError>
5+
where
6+
T: Pod + Zeroable,
7+
{
8+
let data = unsafe { account.borrow_mut_data_unchecked() };
9+
10+
bytemuck::try_from_bytes_mut::<T>(data).map_err(|_| ProgramError::InvalidAccountData)
11+
}
12+
13+
pub fn require(condition: bool, err: ProgramError, msg: &'static str) -> Result<(), ProgramError> {
14+
if !condition {
15+
msg!(msg);
16+
return Err(err);
17+
}
18+
Ok(())
19+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::{load, require, states::Counter};
2+
use {
3+
pinocchio::{
4+
account_info::AccountInfo,
5+
instruction::{Seed, Signer},
6+
program_error::ProgramError,
7+
pubkey::{find_program_address, pubkey_eq, Pubkey},
8+
sysvars::{rent::Rent, Sysvar},
9+
ProgramResult,
10+
},
11+
pinocchio_log::log,
12+
pinocchio_system::instructions::CreateAccount,
13+
};
14+
pub fn process_create_counter(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
15+
log!("Counter Instruction: CreateCounter");
16+
validate(program_id, accounts)?;
17+
18+
if let [creator, counter_pda, _system_program] = accounts {
19+
let counter_seeds = &[Counter::PREFIX];
20+
21+
let (expected_counter, bump) = find_program_address(counter_seeds, program_id);
22+
23+
require(
24+
pubkey_eq(&expected_counter, counter_pda.key()),
25+
ProgramError::IncorrectProgramId,
26+
"Validation Error: Seed constraints violated",
27+
)?;
28+
let curve_bump: &[u8] = &[bump];
29+
let seeds = [Seed::from(Counter::PREFIX), Seed::from(curve_bump)];
30+
let signer = Signer::from(&seeds);
31+
32+
CreateAccount {
33+
from: creator,
34+
lamports: Rent::get()?.minimum_balance(Counter::SIZE),
35+
owner: program_id,
36+
space: Counter::SIZE as u64,
37+
to: counter_pda,
38+
}
39+
.invoke_signed(&[signer])?;
40+
41+
let counter = load::<Counter>(counter_pda)?;
42+
43+
counter.count = 0;
44+
45+
Ok(())
46+
} else {
47+
Err(ProgramError::NotEnoughAccountKeys)
48+
}
49+
}
50+
pub fn validate(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
51+
if let [creator, counter_pda, _system_program] = accounts {
52+
require(
53+
creator.is_signer(),
54+
ProgramError::MissingRequiredSignature,
55+
"Validation Error: Creator must be a signer",
56+
)?;
57+
58+
require(
59+
counter_pda.is_writable(),
60+
ProgramError::InvalidAccountData,
61+
"Validation Error: Counter program Writable priviledge escalated",
62+
)?;
63+
64+
Ok(())
65+
} else {
66+
Err(ProgramError::NotEnoughAccountKeys)
67+
}
68+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use crate::{load, require, states::Counter};
2+
use {
3+
pinocchio::{
4+
account_info::AccountInfo,
5+
program_error::ProgramError,
6+
pubkey::{find_program_address, pubkey_eq, Pubkey},
7+
ProgramResult,
8+
},
9+
pinocchio_log::log,
10+
};
11+
12+
pub fn process_increment_counter(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
13+
log!("Counter Instruction: IncrementCounter");
14+
validate(program_id, accounts)?;
15+
16+
if let [_user, counter_pda] = accounts {
17+
let counter = load::<Counter>(counter_pda)?;
18+
19+
counter.count += 1;
20+
Ok(())
21+
} else {
22+
Err(ProgramError::NotEnoughAccountKeys)
23+
}
24+
}
25+
26+
pub fn validate(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
27+
if let [user, counter_pda] = accounts {
28+
require(
29+
user.is_signer(),
30+
ProgramError::MissingRequiredSignature,
31+
"Validation Error: User must be a signer",
32+
)?;
33+
34+
let counter_seeds = &[Counter::PREFIX];
35+
36+
let (expected_counter, _) = find_program_address(counter_seeds, program_id);
37+
38+
require(
39+
pubkey_eq(&expected_counter, counter_pda.key()),
40+
ProgramError::IncorrectProgramId,
41+
"Validation Error: Seed constraints violated",
42+
)?;
43+
44+
require(
45+
counter_pda.data_len() == Counter::SIZE,
46+
ProgramError::UninitializedAccount,
47+
"Validation Error: Counter isn't initialized yet",
48+
)?;
49+
50+
require(
51+
counter_pda.is_writable(),
52+
ProgramError::InvalidAccountData,
53+
"Validation Error: Counter program Writable priviledge escalated",
54+
)?;
55+
56+
Ok(())
57+
} else {
58+
Err(ProgramError::NotEnoughAccountKeys)
59+
}
60+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use pinocchio::program_error::ProgramError;
2+
pub mod create_counter;
3+
pub mod increment_counter;
4+
#[repr(u8)]
5+
pub enum CounterInstructions {
6+
CreateCounter,
7+
Increment,
8+
}
9+
10+
impl TryFrom<&u8> for CounterInstructions {
11+
type Error = ProgramError;
12+
13+
fn try_from(value: &u8) -> Result<Self, Self::Error> {
14+
match *value {
15+
0 => Ok(CounterInstructions::CreateCounter),
16+
1 => Ok(CounterInstructions::Increment),
17+
_ => Err(ProgramError::InvalidInstructionData),
18+
}
19+
}
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#![no_std]
2+
use pinocchio::{no_allocator, nostd_panic_handler, program_entrypoint};
3+
use pinocchio_pubkey::declare_id;
4+
5+
pub mod helpers;
6+
pub mod instructions;
7+
pub mod processor;
8+
pub mod states;
9+
pub use helpers::*;
10+
use processor::*;
11+
12+
program_entrypoint!(process_instructions);
13+
nostd_panic_handler!();
14+
no_allocator!();
15+
16+
declare_id!("8TpdLD58VBWsdzxRi2yRcmKJD9UcE2GuUrBwsyCwpbUN");
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use crate::instructions::{
2+
create_counter::process_create_counter, increment_counter::process_increment_counter,
3+
CounterInstructions,
4+
};
5+
use pinocchio::{
6+
account_info::AccountInfo, program_error::ProgramError, pubkey::Pubkey, ProgramResult,
7+
};
8+
9+
pub fn process_instructions(
10+
program_id: &Pubkey,
11+
accounts: &[AccountInfo],
12+
ix_data: &[u8],
13+
) -> ProgramResult {
14+
let (disc, _) = ix_data
15+
.split_first()
16+
.ok_or(ProgramError::InvalidInstructionData)?;
17+
18+
match CounterInstructions::try_from(disc)? {
19+
CounterInstructions::CreateCounter => process_create_counter(program_id, accounts)?,
20+
CounterInstructions::Increment => process_increment_counter(program_id, accounts)?,
21+
}
22+
Ok(())
23+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use bytemuck::{Pod, Zeroable};
2+
3+
#[repr(C)]
4+
#[derive(Clone, Copy, Zeroable, Pod)]
5+
pub struct Counter {
6+
pub count: u64,
7+
}
8+
9+
impl Counter {
10+
pub const PREFIX: &[u8] = b"counter";
11+
pub const SIZE: usize = core::mem::size_of::<Self>();
12+
}

0 commit comments

Comments
 (0)