Browse Source

custom instruction data

jpcaulfi 3 years ago
parent
commit
2cd8290fa6

+ 6 - 5
README.md

@@ -40,16 +40,17 @@ Each example contains two folders:
 
 How to build & run:
 - Before running anything in any folder make sure you pull in the dependencies with `yarn install`.
-- `native` - Use `cicd.sh` to build & deploy the program. Run `npm run test` to test it.
+- `native` - Use `cicd.sh` to build & deploy the program. Run `yarn run test` to test it.
 - `anchor` - Use `anchor build && anchor deploy` to build & deploy the program. Run `anchor run test` to test it.
 
 ## To-Do:
 **Got something you want to see here? Add it to the list. Or better yet, write one & create a PR!*
 - ### Program Basics
-- [] 1. Processing an instruction
-- [] 2. Custom instruction data
-- [] 3. Recommended program layout
-- [] 4. Custom instruction data expanded
+- [x] 1. Hello Solana
+- [x] 2. Custom instruction data
+- [] 3. Instruction accounts
+- [] 4. Recommended program layout
+- [] 5. Custom instruction data expanded
 - ### Accounts
 - [] 1. Creating a system account
 - [] 2. Modifying an account's data

+ 5 - 0
program-basics/custom-instruction-data/README.md

@@ -0,0 +1,5 @@
+# Custom Instruction Data
+
+OK, couple new things here. We want to make use of that data field in our transaction instruction.
+
+**For native**, we need to add `borsh` and `borsh-derive` so we can mark a struct as serializable to/from **BPF format**.

+ 14 - 0
program-basics/custom-instruction-data/anchor/Anchor.toml

@@ -0,0 +1,14 @@
+[features]
+seeds = false
+[programs.localnet]
+custom_instruction_data = "DgoL5J44aspizyUs9fcnpGEUJjWTLJRCfx8eYtUMYczf"
+
+[registry]
+url = "https://anchor.projectserum.com"
+
+[provider]
+cluster = "localnet"
+wallet = "~/.config/solana/id.json"
+
+[scripts]
+test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

+ 13 - 0
program-basics/custom-instruction-data/anchor/Cargo.toml

@@ -0,0 +1,13 @@
+[workspace]
+members = [
+    "programs/*"
+]
+
+[profile.release]
+overflow-checks = true
+lto = "fat"
+codegen-units = 1
+[profile.release.build-override]
+opt-level = 3
+incremental = false
+codegen-units = 1

+ 14 - 0
program-basics/custom-instruction-data/anchor/package.json

@@ -0,0 +1,14 @@
+{
+    "dependencies": {
+        "@project-serum/anchor": "^0.24.2"
+    },
+    "devDependencies": {
+        "@types/bn.js": "^5.1.0",
+        "@types/chai": "^4.3.0",
+        "@types/mocha": "^9.0.0",
+        "chai": "^4.3.4",
+        "mocha": "^9.0.3",
+        "ts-mocha": "^10.0.0",
+        "typescript": "^4.3.5"
+    }
+}

+ 19 - 0
program-basics/custom-instruction-data/anchor/programs/custom-instruction-data/Cargo.toml

@@ -0,0 +1,19 @@
+[package]
+name = "custom-instruction-data"
+version = "0.1.0"
+description = "Created with Anchor"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "lib"]
+name = "custom_instruction_data"
+
+[features]
+no-entrypoint = []
+no-idl = []
+no-log-ix-name = []
+cpi = ["no-entrypoint"]
+default = []
+
+[dependencies]
+anchor-lang = "0.24.2"

+ 2 - 0
program-basics/custom-instruction-data/anchor/programs/custom-instruction-data/Xargo.toml

@@ -0,0 +1,2 @@
+[target.bpfel-unknown-unknown.dependencies.std]
+features = []

+ 29 - 0
program-basics/custom-instruction-data/anchor/programs/custom-instruction-data/src/lib.rs

@@ -0,0 +1,29 @@
+use anchor_lang::prelude::*;
+
+declare_id!("DgoL5J44aspizyUs9fcnpGEUJjWTLJRCfx8eYtUMYczf");
+
+#[program]
+pub mod custom_instruction_data {
+    use super::*;
+
+    // With Anchor, we just put instruction data in the function signature!
+    //
+    pub fn go_to_park(
+        ctx: Context<Park>,
+        name: String,
+        height: u32,
+    ) -> Result<()> {
+        
+        msg!("Welcome to the park, {}!", name);
+        if height > 5 {
+            msg!("You are tall enough to ride this ride. Congratulations.");
+        } else {
+            msg!("You are NOT tall enough to ride this ride. Sorry mate.");
+        };
+
+        Ok(())
+    }
+}
+
+#[derive(Accounts)]
+pub struct Park {}

+ 23 - 0
program-basics/custom-instruction-data/anchor/tests/test.ts

@@ -0,0 +1,23 @@
+import * as anchor from "@project-serum/anchor";
+import { CustomInstructionData } from "../target/types/custom_instruction_data";
+
+
+describe("custom-instruction-data", () => {
+
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
+  const program = anchor.workspace.CustomInstructionData as anchor.Program<CustomInstructionData>;
+
+  it("Go to the park!", async () => {
+    
+    // Again, Anchor makes it super simple.
+    //
+    await program.methods.goToPark("Jimmy", 3)
+    .accounts({})
+    .rpc();
+    await program.methods.goToPark("Mary", 10)
+    .accounts({})
+    .rpc();
+
+  });
+});

+ 10 - 0
program-basics/custom-instruction-data/anchor/tsconfig.json

@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "types": ["mocha", "chai"],
+    "typeRoots": ["./node_modules/@types"],
+    "lib": ["es2015"],
+    "module": "commonjs",
+    "target": "es6",
+    "esModuleInterop": true
+  }
+}

+ 8 - 0
program-basics/custom-instruction-data/native/cicd.sh

@@ -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-bpf --manifest-path=./program/Cargo.toml --bpf-out-dir=./program/target/so
+solana program deploy ./program/target/so/program.so

+ 18 - 0
program-basics/custom-instruction-data/native/package.json

@@ -0,0 +1,18 @@
+{
+  "scripts": {
+    "test": "yarn run ts-mocha -p ./tsconfig.json -t 1000000 ./tests/test.ts"
+  },
+  "dependencies": {
+    "@solana/web3.js": "^1.47.3",
+    "fs": "^0.0.1-security"
+  },
+  "devDependencies": {
+    "@types/bn.js": "^5.1.0",
+    "@types/chai": "^4.3.1",
+    "@types/mocha": "^9.1.1",
+    "chai": "^4.3.4",
+    "mocha": "^9.0.3",
+    "ts-mocha": "^10.0.0",
+    "typescript": "^4.3.5"
+  }
+}

+ 12 - 0
program-basics/custom-instruction-data/native/program/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "program"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+borsh = "0.9.3"
+borsh-derive = "0.9.1"
+solana-program = "1.10.12"
+
+[lib]
+crate-type = ["cdylib", "lib"]

+ 39 - 0
program-basics/custom-instruction-data/native/program/src/lib.rs

@@ -0,0 +1,39 @@
+use borsh::{BorshDeserialize, BorshSerialize};
+use solana_program::{
+    account_info::AccountInfo, 
+    entrypoint, 
+    entrypoint::ProgramResult, 
+    msg, 
+    pubkey::Pubkey,
+};
+
+
+entrypoint!(process_instruction);
+
+
+fn process_instruction(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+
+    // Attempt to serialize the BPF format to our struct
+    //  using Borsh
+    //
+    let instruction_data_object = InstructionData::try_from_slice(&instruction_data)?;
+
+    msg!("Welcome to the park, {}!", instruction_data_object.name);
+    if instruction_data_object.height > 5 {
+        msg!("You are tall enough to ride this ride. Congratulations.");
+    } else {
+        msg!("You are NOT tall enough to ride this ride. Sorry mate.");
+    };
+
+    Ok(())
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Debug)]
+pub struct InstructionData {
+    name: String,
+    height: u32,
+}

+ 83 - 0
program-basics/custom-instruction-data/native/tests/test.ts

@@ -0,0 +1,83 @@
+import {
+    Connection,
+    Keypair,
+    sendAndConfirmTransaction,
+    Transaction,
+    TransactionInstruction,
+} from '@solana/web3.js';
+import * as borsh from "borsh";
+import { Buffer } from "buffer";
+
+
+function createKeypairFromFile(path: string): Keypair {
+    return Keypair.fromSecretKey(
+        Buffer.from(JSON.parse(require('fs').readFileSync(path, "utf-8")))
+    )
+};
+
+
+describe("custom-instruction-data", () => {
+
+    const connection = new Connection(`http://localhost:8899`, 'confirmed');
+    const payer = createKeypairFromFile(require('os').homedir() + '/.config/solana/id.json');
+    const program = createKeypairFromFile('./program/target/so/program-keypair.json');
+
+    class Assignable {
+        constructor(properties) {
+            Object.keys(properties).map((key) => {
+                return (this[key] = properties[key]);
+            });
+        };
+    };
+
+    class InstructionData extends Assignable {};
+
+    const InstructionDataSchema = new Map([
+        [
+            InstructionData, {
+                kind: 'struct',
+                fields: [
+                    ['name', 'string'],
+                    ['height', 'u32'],
+                ]
+            }
+        ]
+    ]);
+  
+    it("Go to the park!", async () => {
+
+        const jimmy = new InstructionData({
+            name: "Jimmy",
+            height: 3
+        });
+
+        const mary = new InstructionData({
+            name: "Mary",
+            height: 10
+        });
+
+        function toBuffer(obj: InstructionData): Buffer {
+            return Buffer.from(borsh.serialize(InstructionDataSchema, obj));
+        }
+
+        let ix1 = new TransactionInstruction({
+            keys: [
+                {pubkey: payer.publicKey, isSigner: true, isWritable: true}
+            ],
+            programId: program.publicKey,
+            data: toBuffer(jimmy),
+        });
+
+        let ix2 = new TransactionInstruction({
+            ...ix1,
+            data: toBuffer(mary),
+        });
+
+        await sendAndConfirmTransaction(
+            connection, 
+            new Transaction().add(ix1).add(ix2),
+            [payer]
+        );
+    });
+  });
+  

+ 10 - 0
program-basics/custom-instruction-data/native/tsconfig.json

@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "types": ["mocha", "chai"],
+    "typeRoots": ["./node_modules/@types"],
+    "lib": ["es2015"],
+    "module": "commonjs",
+    "target": "es6",
+    "esModuleInterop": true
+  }
+}