Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basics/realloc/steel #319

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions basics/realloc/steel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[workspace]
members = ["api","program"]
resolver = "2"

[workspace.dependencies]
solana-program = "1.18.17"
steel = "2.1"
bytemuck = "1.14"
num_enum = "0.7"
13 changes: 13 additions & 0 deletions basics/realloc/steel/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "realloc-steel-api"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]

[dependencies]
solana-program.workspace = true
steel.workspace = true
bytemuck.workspace = true
num_enum.workspace = true
41 changes: 41 additions & 0 deletions basics/realloc/steel/api/src/instruction/create.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::state::AddressInfo;
use steel::*;

use super::SteelInstruction;

instruction!(SteelInstruction, CreateAddressInfo);
/// create address info
#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
#[repr(C, packed)]
pub struct CreateAddressInfo {
pub address_info: AddressInfo,
}

impl CreateAddressInfo {
pub fn process(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
let address_info_data = Self::try_from_bytes(data)?.address_info;

let [payer, address_info_account, system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};

payer.is_signer()?;
address_info_account.is_signer()?;
address_info_account.is_empty()?;
system_program.is_program(&system_program::ID)?;

create_account::<AddressInfo>(
address_info_account,
system_program,
payer,
&crate::ID,
&[],
)?;

let address_info = address_info_account.as_account_mut::<AddressInfo>(&crate::ID)?;

*address_info = address_info_data;

Ok(())
}
}
58 changes: 58 additions & 0 deletions basics/realloc/steel/api/src/instruction/extend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::state::*;
use steel::*;
use sysvar::rent::Rent;

use super::SteelInstruction;

instruction!(SteelInstruction, ExtendAddressInfo);
/// extend address info
#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
#[repr(C, packed)]
pub struct ExtendAddressInfo {
pub address_info: EnhancedAddressInfoExtender,
}

impl ExtendAddressInfo {
pub fn process(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Steel uses zero_copy types, so the sizes are fixed, however we can move from one account
// type to another, e.g AddressInfo -> ExtendedAddressInfo

let [payer, address_info_account, _system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};

// we collect the extended data
//
let address_info_extended_data = Self::try_from_bytes(data)?.address_info;

// We get pay for the extra space we need before allocating the space
//
let account_span = 8 + std::mem::size_of::<ExtendedAddressInfo>(); // 8 byte Account Discriminator + sizeof ExtendedAddressInfo
let lamports_required = (Rent::get()?).minimum_balance(account_span);
let diff = lamports_required.saturating_sub(address_info_account.lamports());
address_info_account.collect(diff, payer)?;

// we reallocate new space to accomodate the new data
//
address_info_account.realloc(account_span, false)?; // no zero init

// we set the discriminator to the `ExtendedAccountInfo`, so Steel can deserialize the account as such.
//
{
let mut data = address_info_account.data.borrow_mut();
data[0] = ExtendedAddressInfo::discriminator();
}

// now we reset the account discriminator, we can deserialise as `ExtendedAddressInfo`
//
let extended_address_info =
address_info_account.as_account_mut::<ExtendedAddressInfo>(&crate::ID)?;

// set the extended address info
//
extended_address_info.state = address_info_extended_data.state;
extended_address_info.zip = address_info_extended_data.zip;

Ok(())
}
}
19 changes: 19 additions & 0 deletions basics/realloc/steel/api/src/instruction/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pub mod create;
pub use create::*;

pub mod extend;
pub use extend::*;

pub mod zero_init;
pub use zero_init::*;

use steel::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]

pub enum SteelInstruction {
CreateAddressInfo = 0,
ExtendAddressInfo = 1,
ZeroInit = 2,
}
56 changes: 56 additions & 0 deletions basics/realloc/steel/api/src/instruction/zero_init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use crate::state::*;
use steel::*;
use sysvar::rent::Rent;

use super::SteelInstruction;

instruction!(SteelInstruction, ZeroInit);
/// work info
#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
#[repr(C)]
pub struct ZeroInit {
pub work_info: WorkInfo,
}

impl ZeroInit {
pub fn process(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
// Steel uses zero_copy types, so the sizes are fixed, however we can move from one account
// type to another, e.g ExtendedAddressInfo -> WorkInfo

let [payer, address_info_account, _system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};

// we collect the extended data
//
let work_info_data = Self::try_from_bytes(data)?.work_info;

// We get pay for the extra space we need before allocating the space
//
let account_span = 8 + std::mem::size_of::<WorkInfo>(); // 8 byte Account Discriminator + sizeof WorkInfo
let lamports_required = (Rent::get()?).minimum_balance(account_span);
let diff = lamports_required.saturating_sub(address_info_account.lamports());
address_info_account.collect(diff, payer)?;

// we reallocate new space to accomodate the new data
//
address_info_account.realloc(account_span, true)?; // zero init

// we set the discriminator to the `WorkInfo`, so Steel can deserialize the account as such.
//
{
let mut data = address_info_account.data.borrow_mut();
data[0] = WorkInfo::discriminator();
}

// now we reset the account discriminator, we can deserialise as `WorkInfo``
//
let work_info = address_info_account.as_account_mut::<WorkInfo>(&crate::ID)?;

// set the extended address info
//
*work_info = work_info_data;

Ok(())
}
}
11 changes: 11 additions & 0 deletions basics/realloc/steel/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod instruction;
mod state;

use steel::*;

declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35");

pub mod prelude {
pub use crate::instruction::*;
pub use crate::state::*;
}
48 changes: 48 additions & 0 deletions basics/realloc/steel/api/src/state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use steel::*;

#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
pub enum SteelAccount {
AddressInfo,
ExtendedAddressInfo,
WorkInfo,
}

account!(SteelAccount, AddressInfo);
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct AddressInfo {
pub name: [u8; 48],
pub house_number: u8,
pub street: [u8; 48],
pub city: [u8; 48],
}

account!(SteelAccount, ExtendedAddressInfo);
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct ExtendedAddressInfo {
pub name: [u8; 48],
pub house_number: u8,
pub street: [u8; 48],
pub city: [u8; 48],
pub state: [u8; 48],
pub zip: u32,
}

account!(SteelAccount, WorkInfo);
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct WorkInfo {
pub name: [u8; 48],
pub position: [u8; 48],
pub company: [u8; 48],
pub years_employed: u8,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
pub struct EnhancedAddressInfoExtender {
pub state: [u8; 48],
pub zip: u32,
}
8 changes: 8 additions & 0 deletions basics/realloc/steel/cicd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash

# This script is for quick building & deploying of the program.
# It also serves as a reference for the commands used for building & deploying Solana programs.
# Run this bad boy with "bash cicd.sh" or "./cicd.sh"

cargo build-sbf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
solana program deploy ./program/target/so/program.so
23 changes: 23 additions & 0 deletions basics/realloc/steel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"scripts": {
"test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts",
"build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test",
"build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so",
"deploy": "solana program deploy ./program/target/so/program.so"
},
"dependencies": {
"@solana/web3.js": "^1.95.4"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.1",
"@types/mocha": "^10.0.9",
"@types/node": "^22.8.6",
"borsh": "0.7.0",
"chai": "^4.3.4",
"mocha": "^10.8.2",
"solana-bankrun": "^0.4.0",
"ts-mocha": "^10.0.0",
"typescript": "^5.6.3"
}
}
Loading
Loading