Browse Source

add basics/realloc/steel (#241)

Co-authored-by: Ayush <ayush-15@live.com>
Perelyn 9 months ago
parent
commit
30d45c737c

+ 2 - 0
basics/realloc/steel/.gitignore

@@ -0,0 +1,2 @@
+target
+test-ledger

+ 21 - 0
basics/realloc/steel/Cargo.toml

@@ -0,0 +1,21 @@
+[workspace]
+resolver = "2"
+members = ["api", "program"]
+
+[workspace.package]
+version = "0.1.0"
+edition = "2021"
+license = "Apache-2.0"
+homepage = ""
+documentation = ""
+repository = ""
+readme = "./README.md"
+keywords = ["solana"]
+
+[workspace.dependencies]
+realloc-api = { path = "./api", version = "0.1.0" }
+bytemuck = "1.14"
+num_enum = "0.7"
+solana-program = "1.18"
+steel = "2.0"
+thiserror = "1.0"

+ 28 - 0
basics/realloc/steel/README.md

@@ -0,0 +1,28 @@
+# Realloc
+
+**Realloc** is a ...
+        
+## API
+- [`Consts`](api/src/consts.rs) – Program constants.
+- [`Error`](api/src/error.rs) – Custom program errors.
+- [`Event`](api/src/event.rs) – Custom program events.
+- [`Instruction`](api/src/instruction.rs) – Declared instructions.
+
+## Instructions
+- [`Add`](program/src/add.rs) – Add ...
+- [`Initialize`](program/src/initialize.rs) – Initialize ...
+
+## State
+- [`Counter`](api/src/state/counter.rs) – Counter ...
+
+## Get started
+
+Compile your program:
+```sh
+steel build
+```
+
+Run unit and integration tests:
+```sh
+steel test
+```

+ 18 - 0
basics/realloc/steel/api/Cargo.toml

@@ -0,0 +1,18 @@
+[package]
+name = "realloc-api"
+description = "API for interacting with the Realloc program"
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+documentation.workspace = true
+repository.workspace = true
+readme.workspace = true
+keywords.workspace = true
+
+[dependencies]
+bytemuck.workspace = true
+num_enum.workspace = true
+solana-program.workspace = true
+steel.workspace = true
+thiserror.workspace = true

+ 4 - 0
basics/realloc/steel/api/src/consts.rs

@@ -0,0 +1,4 @@
+/// Seed of the address_info account PDA.
+pub const ADDRESS_INFO: &[u8] = b"address_info";
+
+pub const MAX_STR_LEN: usize = 32;

+ 10 - 0
basics/realloc/steel/api/src/error.rs

@@ -0,0 +1,10 @@
+use steel::*;
+
+#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)]
+#[repr(u32)]
+pub enum ReallocError {
+    #[error("This is a dummy error")]
+    Dummy = 0,
+}
+
+error!(ReallocError);

+ 21 - 0
basics/realloc/steel/api/src/instruction.rs

@@ -0,0 +1,21 @@
+use steel::*;
+
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)]
+pub enum ReallocInstruction {
+    Initialize = 0,
+    Add = 1
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Pod, Zeroable)]
+pub struct Initialize {}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Pod, Zeroable)]
+pub struct Add {
+    pub amount: [u8; 8]
+}
+
+instruction!(ReallocInstruction, Initialize);
+instruction!(ReallocInstruction, Add);

+ 20 - 0
basics/realloc/steel/api/src/lib.rs

@@ -0,0 +1,20 @@
+pub mod consts;
+pub mod error;
+pub mod instruction;
+pub mod sdk;
+pub mod state;
+pub mod utils;
+
+pub mod prelude {
+    pub use crate::consts::*;
+    pub use crate::error::*;
+    pub use crate::instruction::*;
+    pub use crate::sdk::*;
+    pub use crate::state::*;
+    pub use crate::utils::*;
+}
+
+use steel::*;
+
+// TODO Set program id
+declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35");

+ 29 - 0
basics/realloc/steel/api/src/sdk.rs

@@ -0,0 +1,29 @@
+use steel::*;
+
+use crate::prelude::*;
+
+pub fn initialize(signer: Pubkey) -> Instruction {
+    Instruction {
+        program_id: crate::ID,
+        accounts: vec![
+            AccountMeta::new(signer, true),
+            AccountMeta::new(counter_pda().0, false),
+            AccountMeta::new_readonly(system_program::ID, false),
+        ],
+        data: Initialize {}.to_bytes()
+    }
+}
+
+pub fn add(signer: Pubkey, amount: u64) -> Instruction {
+    Instruction {
+        program_id: crate::ID,
+        accounts: vec![
+            AccountMeta::new(signer, true),
+            AccountMeta::new(counter_pda().0, false),
+        ],
+        data: Add {
+            amount: amount.to_le_bytes(),
+        }
+        .to_bytes(),
+    }
+}

+ 27 - 0
basics/realloc/steel/api/src/state/address_info.rs

@@ -0,0 +1,27 @@
+use crate::consts::*;
+use crate::utils::*;
+use steel::*;
+
+use super::ReallocAccount;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
+pub struct AddressInfo {
+    pub name: [u8; MAX_STR_LEN],
+    pub house_number: u8,
+    pub street: [u8; MAX_STR_LEN],
+    pub city: [u8; MAX_STR_LEN],
+}
+
+impl AddressInfo {
+    pub fn new(name: &str, house_number: u8, street: &str, city: &str) -> Self {
+        AddressInfo {
+            name: str_to_bytes(name),
+            house_number,
+            street: str_to_bytes(street),
+            city: str_to_bytes(city),
+        }
+    }
+}
+
+account!(ReallocAccount, AddressInfo);

+ 43 - 0
basics/realloc/steel/api/src/state/enchanced_address_info.rs

@@ -0,0 +1,43 @@
+use crate::consts::*;
+use crate::state::AddressInfo;
+use crate::utils::*;
+use steel::*;
+
+use super::ReallocAccount;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
+pub struct EnhancedAddressInfoExtender {
+    pub state: [u8; MAX_STR_LEN],
+    pub zip: u32,
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
+pub struct EnhancedAddressInfo {
+    pub zip: u32,
+    pub house_number: u8,
+
+    pub name: [u8; MAX_STR_LEN],
+    pub street: [u8; MAX_STR_LEN],
+    pub city: [u8; MAX_STR_LEN],
+    pub state: [u8; MAX_STR_LEN],
+
+    pub _padding: [u8; 3],
+}
+
+impl EnhancedAddressInfo {
+    pub fn from_address_info(address_info: AddressInfo, state: &str, zip: u32) -> Self {
+        EnhancedAddressInfo {
+            name: address_info.name,
+            house_number: address_info.house_number,
+            street: address_info.street,
+            city: address_info.city,
+            state: str_to_bytes(state),
+            zip,
+            _padding: [0u8; 3],
+        }
+    }
+}
+
+account!(ReallocAccount, EnhancedAddressInfo);

+ 25 - 0
basics/realloc/steel/api/src/state/mod.rs

@@ -0,0 +1,25 @@
+mod address_info;
+mod enchanced_address_info;
+mod work_info;
+
+pub use address_info::*;
+pub use enchanced_address_info::*;
+pub use work_info::*;
+
+use steel::*;
+
+use crate::consts::*;
+
+#[repr(u8)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
+pub enum ReallocAccount {
+    AddressInfo = 0,
+    EnhancedAddressInfo = 1,
+    EnhancedAddressInfoExtender = 2,
+    WorkInfo = 3,
+}
+
+/// Fetch PDA of the counter account.
+pub fn counter_pda() -> (Pubkey, u8) {
+    Pubkey::find_program_address(&[COUNTER], &crate::id())
+}

+ 27 - 0
basics/realloc/steel/api/src/state/work_info.rs

@@ -0,0 +1,27 @@
+use crate::consts::*;
+use crate::utils::*;
+use steel::*;
+
+use super::ReallocAccount;
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
+pub struct WorkInfo {
+    pub name: [u8; MAX_STR_LEN],
+    pub position: [u8; MAX_STR_LEN],
+    pub company: [u8; MAX_STR_LEN],
+    pub years_employed: u8,
+}
+
+impl WorkInfo {
+    pub fn new(name: &str, position: &str, company: &str, years_employed: u8) -> Self {
+        WorkInfo {
+            name: str_to_bytes(name),
+            position: str_to_bytes(position),
+            company: str_to_bytes(company),
+            years_employed,
+        }
+    }
+}
+
+account!(ReallocAccount, WorkInfo);

+ 5 - 0
basics/realloc/steel/api/src/utils.rs

@@ -0,0 +1,5 @@
+pub fn str_to_bytes(name: &str) -> [u8; MAX_STR_LEN] {
+    let mut name_bytes = [0u8; MAX_STR_LEN];
+    name_bytes[..name.len()].copy_from_slice(name.as_bytes());
+    name_bytes
+}

+ 26 - 0
basics/realloc/steel/program/Cargo.toml

@@ -0,0 +1,26 @@
+[package]
+name = "realloc-program"
+description = ""
+version.workspace = true
+edition.workspace = true
+license.workspace = true
+homepage.workspace = true
+documentation.workspace = true
+repository.workspace = true
+readme.workspace = true
+keywords.workspace = true
+
+[lib]
+crate-type = ["cdylib", "lib"]
+
+[dependencies]
+realloc-api.workspace = true
+solana-program.workspace = true
+steel.workspace = true
+
+[dev-dependencies]
+base64 = "0.21"
+rand = "0.8.5"
+solana-program-test = "1.18"
+solana-sdk = "1.18"
+tokio = { version = "1.35", features = ["full"] }

+ 22 - 0
basics/realloc/steel/program/src/add.rs

@@ -0,0 +1,22 @@
+use realloc_api::prelude::*;
+use steel::*;
+
+pub fn process_add(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
+    // Parse args.
+    let args = Add::try_from_bytes(data)?;
+	let amount = u64::from_le_bytes(args.amount);
+
+    // Load accounts.
+    let [signer_info, counter_info] = accounts else {
+        return Err(ProgramError::NotEnoughAccountKeys);        
+    };
+    signer_info.is_signer()?;
+	let counter = counter_info
+		.as_account_mut::<Counter>(&realloc_api::ID)?
+		.assert_mut(|c| c.value < 100)?;
+
+    // Update state 
+	counter.value += amount;
+
+    Ok(())
+}

+ 28 - 0
basics/realloc/steel/program/src/initialize.rs

@@ -0,0 +1,28 @@
+use realloc_api::prelude::*;
+use steel::*;
+
+pub fn process_initialize(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult {
+    // Load accounts.
+    let [signer_info, counter_info, system_program] = accounts else {
+        return Err(ProgramError::NotEnoughAccountKeys);        
+    };
+    signer_info.is_signer()?;
+    counter_info.is_empty()?.is_writable()?.has_seeds(
+        &[COUNTER],
+        &realloc_api::ID
+    )?;
+    system_program.is_program(&system_program::ID)?;
+
+    // Initialize counter.
+    create_account::<Counter>(
+        counter_info,
+        system_program,
+        signer_info,
+        &realloc_api::ID,
+        &[COUNTER],
+    )?;
+    let counter = counter_info.as_account_mut::<Counter>(&realloc_api::ID)?;
+    counter.value = 0;
+
+    Ok(())
+}

+ 25 - 0
basics/realloc/steel/program/src/lib.rs

@@ -0,0 +1,25 @@
+mod add;
+mod initialize;
+
+use add::*;
+use initialize::*;
+        
+use realloc_api::prelude::*;
+use steel::*;
+
+pub fn process_instruction(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    data: &[u8],
+) -> ProgramResult {
+    let (ix, data) = parse_instruction(&realloc_api::ID, program_id, data)?;
+
+    match ix {
+        ReallocInstruction::Initialize => process_initialize(accounts, data)?,
+        ReallocInstruction::Add => process_add(accounts, data)?,
+    }
+
+    Ok(())
+}
+
+entrypoint!(process_instruction);

+ 46 - 0
basics/realloc/steel/program/tests/test.rs

@@ -0,0 +1,46 @@
+use realloc_api::prelude::*;
+use solana_program::hash::Hash;
+use solana_program_test::{processor, BanksClient, ProgramTest};
+use solana_sdk::{signature::Keypair, signer::Signer, transaction::Transaction};
+use steel::*;
+
+async fn setup() -> (BanksClient, Keypair, Hash) {
+    let mut program_test = ProgramTest::new(
+        "realloc_program",
+        realloc_api::ID,
+        processor!(realloc_program::process_instruction),
+    );
+    program_test.prefer_bpf(true);
+    program_test.start().await
+}
+
+#[tokio::test]
+async fn run_test() {
+    // Setup test
+    let (mut banks, payer, blockhash) = setup().await;
+
+    // Submit initialize transaction.
+    let ix = initialize(payer.pubkey());
+    let tx = Transaction::new_signed_with_payer(&[ix], Some(&payer.pubkey()), &[&payer], blockhash);
+    let res = banks.process_transaction(tx).await;
+    assert!(res.is_ok());
+
+    // Verify counter was initialized.
+    let counter_address = counter_pda().0;
+    let counter_account = banks.get_account(counter_address).await.unwrap().unwrap();
+    let counter = Counter::try_from_bytes(&counter_account.data).unwrap();
+    assert_eq!(counter_account.owner, realloc_api::ID);
+    assert_eq!(counter.value, 0);
+
+    // Submit add transaction.
+    let ix = add(payer.pubkey(), 42);
+    let tx = Transaction::new_signed_with_payer(&[ix], Some(&payer.pubkey()), &[&payer], blockhash);
+    let res = banks.process_transaction(tx).await;
+    assert!(res.is_ok());
+
+    // Verify counter was incremented.
+    let counter_account = banks.get_account(counter_address).await.unwrap().unwrap();
+    let counter = Counter::try_from_bytes(&counter_account.data).unwrap();
+    assert_eq!(counter.value, 42);
+}
+