소스 검색

program init (#35)

* program init

* fix up ci

* fix up ci, take 2

* build-sbf job

* configure dependent jobs

* js tests: disable program loading

* ci: restore program builds in test_program

* review feedback
Joe C 8 달 전
부모
커밋
4414f51109
12개의 변경된 파일241개의 추가작업 그리고 16개의 파일을 삭제
  1. 92 2
      .github/workflows/main.yml
  2. 12 0
      Cargo.lock
  3. 20 1
      Cargo.toml
  4. 11 9
      interface/Cargo.toml
  5. 12 2
      interface/src/error.rs
  6. 8 0
      package.json
  7. 34 0
      program/Cargo.toml
  8. 19 0
      program/src/entrypoint.rs
  9. 5 0
      program/src/lib.rs
  10. 9 0
      program/src/processor.rs
  11. 8 2
      scripts/helpers/start-validator.mts
  12. 11 0
      scripts/rust.mts

+ 92 - 2
.github/workflows/main.yml

@@ -69,6 +69,30 @@ jobs:
 
       - name: Lint / Features
         run: pnpm interface:lint:features
+  
+  format_and_lint_program:
+    name: Format & Lint Program
+    runs-on: ubuntu-latest
+    steps:
+      - name: Git Checkout
+        uses: actions/checkout@v4
+
+      - name: Setup Environment
+        uses: ./.github/actions/setup
+        with:
+          toolchain: format, lint
+
+      - name: Format
+        run: pnpm program:format
+
+      - name: Lint / Clippy
+        run: pnpm program:lint:clippy
+
+      - name: Lint / Docs
+        run: pnpm program:lint:docs
+
+      - name: Lint / Features
+        run: pnpm program:lint:features
 
   wasm_interface:
     name: Build Interface in WASM
@@ -130,10 +154,40 @@ jobs:
           git status --porcelain
           test -z "$(git status --porcelain)"
 
+  build_program:
+    name: Build Program
+    runs-on: ubuntu-latest
+    needs: format_and_lint_program
+    steps:
+      - name: Git Checkout
+        uses: actions/checkout@v4
+
+      - name: Setup Environment
+        uses: ./.github/actions/setup
+        with:
+          cargo-cache-key: cargo-build-program
+          solana: true
+
+      - name: Build
+        run: pnpm program:build
+
+      - name: Upload Program Builds
+        uses: actions/upload-artifact@v4
+        with:
+          name: program-builds
+          path: ./target/deploy/*.so
+          if-no-files-found: error
+
+      - name: Save Program Builds For Client Jobs
+        uses: actions/cache/save@v4
+        with:
+          path: ./**/*.so
+          key: ${{ runner.os }}-builds-${{ github.sha }}
+
   test_client_js:
     name: Test Client JS
     runs-on: ubuntu-latest
-    needs: format_and_lint_client_js
+    needs: [format_and_lint_client_js, build_program]
     steps:
       - name: Git Checkout
         uses: actions/checkout@v4
@@ -143,13 +197,19 @@ jobs:
         with:
           solana: true
 
+      - name: Restore Program Builds
+        uses: actions/cache/restore@v4
+        with:
+          path: ./**/*.so
+          key: ${{ runner.os }}-builds-${{ github.sha }}
+
       - name: Test Client JS
         run: pnpm js:test
 
   test_client_rust:
     name: Test Client Rust
     runs-on: ubuntu-latest
-    needs: format_and_lint_client_rust
+    needs: [format_and_lint_client_rust, build_program]
     steps:
       - name: Git Checkout
         uses: actions/checkout@v4
@@ -161,5 +221,35 @@ jobs:
           toolchain: test
           solana: true
 
+      - name: Restore Program Builds
+        uses: actions/cache/restore@v4
+        with:
+          path: ./**/*.so
+          key: ${{ runner.os }}-builds-${{ github.sha }}
+
       - name: Test Client Rust
         run: pnpm rust:test
+  
+  test_program:
+    name: Test Program
+    runs-on: ubuntu-latest
+    needs: build_program
+    steps:
+      - name: Git Checkout
+        uses: actions/checkout@v4
+
+      - name: Setup Environment
+        uses: ./.github/actions/setup
+        with:
+          cargo-cache-key: cargo-program-tests
+          toolchain: test
+          solana: true
+
+      - name: Restore Program Builds
+        uses: actions/cache/restore@v4
+        with:
+          path: ./**/*.so
+          key: ${{ runner.os }}-builds-${{ github.sha }}
+
+      - name: Test
+        run: pnpm program:test

+ 12 - 0
Cargo.lock

@@ -4363,6 +4363,7 @@ dependencies = [
  "solana-frozen-abi-macro",
  "solana-instruction",
  "solana-logger",
+ "solana-msg",
  "solana-nonce",
  "solana-program",
  "solana-program-entrypoint",
@@ -4375,6 +4376,17 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "solana-system-program"
+version = "3.0.0"
+dependencies = [
+ "solana-account-info",
+ "solana-program-entrypoint",
+ "solana-program-error",
+ "solana-pubkey",
+ "solana-system-interface",
+]
+
 [[package]]
 name = "solana-sysvar-id"
 version = "2.1.12"

+ 20 - 1
Cargo.toml

@@ -1,6 +1,10 @@
 [workspace]
 resolver = "2"
-members = ["clients/rust", "interface"]
+members = [
+    "clients/rust",
+    "interface",
+    "program",
+]
 
 [workspace.package]
 authors = ["Anza Maintainers <maintainers@anza.xyz>"]
@@ -9,6 +13,21 @@ homepage = "https://anza.xyz/"
 license = "Apache-2.0"
 edition = "2021"
 
+[workspace.dependencies]
+solana-account-info = "2.1"
+solana-cpi = "2.1"
+solana-decode-error = "2.1"
+solana-frozen-abi = "2.1"
+solana-frozen-abi-macro = "2.1"
+solana-instruction = "2.1"
+solana-logger = "2.1"
+solana-nonce = "0.0.2"
+solana-msg = "2.1"
+solana-program = { version = "2.1", default-features = false }
+solana-program-entrypoint = "2.1"
+solana-program-error = "2.1"
+solana-pubkey = { version = "2.1", default-features = false }
+
 [workspace.metadata.cli]
 solana = "2.1.0"
 

+ 11 - 9
interface/Cargo.toml

@@ -19,11 +19,13 @@ num-traits = "0.2"
 serde = { version = "1.0.210", optional = true }
 serde_derive = { version = "1.0.210", optional = true }
 solana-decode-error = "^2.1"
-solana-frozen-abi = { version = "^2.1", features = ["frozen-abi"], optional = true }
-solana-frozen-abi-macro = { version = "^2.1", features = ["frozen-abi"], optional = true }
-solana-instruction = { version = "^2.1", features = ["bincode", "std"], optional = true }
-solana-logger = { version = "^2.1", optional = true }
-solana-pubkey = { version = "^2.1", default-features = false }
+solana-frozen-abi = { workspace = true, features = ["frozen-abi"], optional = true }
+solana-frozen-abi-macro = { workspace = true, features = ["frozen-abi"], optional = true }
+solana-instruction = { workspace = true, features = ["bincode", "std"], optional = true }
+solana-logger = { workspace = true, optional = true }
+solana-msg = { workspace = true }
+solana-program-error = { workspace = true }
+solana-pubkey = { workspace = true, default-features = false }
 
 [target.'cfg(target_arch = "wasm32")'.dependencies]
 js-sys = "0.3.72"
@@ -35,10 +37,10 @@ borsh = { version = "1.5.1", features = ["derive", "unstable__schema"] }
 solana-account-info = "^2.1"
 solana-cpi = "^2.1"
 solana-nonce = "^0.0.2"
-solana-program = { version = "^2.1", default-features = false }
-solana-program-entrypoint = "^2.1"
-solana-program-error = { version = "^2.1", features = ["borsh"] }
-solana-pubkey = { version = "^2.1", features = ["std"] }
+solana-program = { workspace = true, default-features = false }
+solana-program-entrypoint = { workspace = true }
+solana-program-error = { workspace = true, features = ["borsh"] }
+solana-pubkey = { workspace = true, features = ["std"] }
 solana-system-interface = { path = ".", features = ["bincode"] }
 static_assertions = "1.1.0"
 strum = "0.24"

+ 12 - 2
interface/src/error.rs

@@ -1,5 +1,9 @@
-use num_traits::{FromPrimitive, ToPrimitive};
-use solana_decode_error::DecodeError;
+use {
+    num_traits::{FromPrimitive, ToPrimitive},
+    solana_decode_error::DecodeError,
+    solana_msg::msg,
+    solana_program_error::PrintProgramError,
+};
 
 // Use strum when testing to ensure our FromPrimitive
 // impl is exhaustive
@@ -118,6 +122,12 @@ impl core::fmt::Display for SystemError {
     }
 }
 
+impl PrintProgramError for SystemError {
+    fn print<E>(&self) {
+        msg!(&self.to_string());
+    }
+}
+
 impl<T> DecodeError<T> for SystemError {
     fn type_of() -> &'static str {
         "SystemError"

+ 8 - 0
package.json

@@ -27,6 +27,14 @@
     "interface:publish": "tsx ./scripts/rust.mts publish interface",
     "interface:test": "tsx ./scripts/rust.mts test interface",
     "interface:wasm": "tsx ./scripts/rust.mts wasm interface",
+    "program:format": "tsx ./scripts/rust.mts format program",
+    "program:lint": "tsx ./scripts/rust.mts lint program",
+    "program:lint:clippy": "tsx ./scripts/rust.mts lint-clippy program",
+    "program:lint:docs": "tsx ./scripts/rust.mts lint-docs program",
+    "program:lint:features": "tsx ./scripts/rust.mts lint-features program",
+    "program:publish": "tsx ./scripts/rust.mts publish program",
+    "program:build": "tsx ./scripts/rust.mts build-sbf program",
+    "program:test": "tsx ./scripts/rust.mts test program",
     "template:upgrade": "tsx ./scripts/helpers/upgrade-template.ts"
   },
   "devDependencies": {

+ 34 - 0
program/Cargo.toml

@@ -0,0 +1,34 @@
+[package]
+name = "solana-system-program"
+version = "3.0.0"
+description = "Solana System Program"
+readme = "README.md"
+authors = { workspace = true }
+repository = { workspace = true }
+homepage = { workspace = true }
+license = { workspace = true }
+edition = { workspace = true }
+
+[package.metadata.solana]
+program-id = "11111111111111111111111111111111"
+
+[features]
+bpf-entrypoint = []
+
+[dependencies]
+solana-account-info = { workspace = true }
+solana-program-entrypoint = { workspace = true }
+solana-program-error = { workspace = true }
+solana-pubkey = { workspace = true }
+solana-system-interface = { path = "../interface" }
+
+[dev-dependencies]
+
+[lib]
+crate-type = ["cdylib"]
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = [
+    'cfg(target_os, values("solana"))',
+]

+ 19 - 0
program/src/entrypoint.rs

@@ -0,0 +1,19 @@
+//! Program entrypoint.
+
+use {
+    crate::processor,
+    solana_account_info::AccountInfo,
+    solana_program_error::{PrintProgramError, ProgramResult},
+    solana_pubkey::Pubkey,
+    solana_system_interface::error::SystemError,
+};
+
+solana_program_entrypoint::entrypoint!(process_instruction);
+fn process_instruction(
+    program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    processor::process(program_id, accounts, instruction_data)
+        .inspect_err(PrintProgramError::print::<SystemError>)
+}

+ 5 - 0
program/src/lib.rs

@@ -0,0 +1,5 @@
+//! Solana System Program.
+
+#[cfg(all(target_os = "solana", feature = "bpf-entrypoint"))]
+pub mod entrypoint;
+pub mod processor;

+ 9 - 0
program/src/processor.rs

@@ -0,0 +1,9 @@
+//! Program processor.
+
+use {
+    solana_account_info::AccountInfo, solana_program_error::ProgramResult, solana_pubkey::Pubkey,
+};
+
+pub fn process(_program_id: &Pubkey, _accounts: &[AccountInfo], _input: &[u8]) -> ProgramResult {
+    Ok(())
+}

+ 8 - 2
scripts/helpers/start-validator.mts

@@ -28,7 +28,10 @@ if (!restart && isValidatorRunning) {
 const verb = isValidatorRunning ? 'Restarting' : 'Starting';
 
 // Get programs and accounts.
-const programs = [...getPrograms(), ...getExternalPrograms()];
+// TODO: JOE C:
+// Disabled adding the BPF program until BPF implementation is complete.
+// Tests expect a valid System program to live at the corresponding address.
+const programs = [...getExternalPrograms()];
 const programPluralized = programs.length === 1 ? 'program' : 'programs';
 const accounts = [...getExternalAccounts()];
 const accountsPluralized = accounts.length === 1 ? 'account' : 'accounts';
@@ -97,7 +100,10 @@ try {
   process.exit();
 }
 
-function getPrograms() {
+// TODO: JOE C:
+// Disabled adding the BPF program until BPF implementation is complete.
+// Tests expect a valid System program to live at the corresponding address.
+function _getPrograms() {
   const binaryDir = path.join(workingDirectory, 'target', 'deploy');
   return getProgramFolders().map((folder) => {
     const cargo = getCargo(folder);

+ 11 - 0
scripts/rust.mts

@@ -13,6 +13,7 @@ import {
 } from './helpers/utils.mts';
 
 enum Command {
+    BuildSbf = 'build-sbf',
     Format = 'format',
     LintClippy = 'lint-clippy',
     LintDocs = 'lint-docs',
@@ -37,6 +38,13 @@ async function cargo(
     await $`cargo ${toolchain} ${command} --manifest-path ${manifestPath} ${cargoArgs} -- ${commandArgs}`;
 }
 
+async function buildSbf() {
+    return cargo(
+        'build-sbf',
+        ['--features', 'bpf-entrypoint'],
+    );
+}
+
 async function format() {
     return cargo(
         getToolchainArgument('format'),
@@ -126,6 +134,9 @@ async function publish() {
 
 
 switch (command) {
+    case Command.BuildSbf:
+        await buildSbf();
+        break;
     case Command.Format:
         await format();
         break;