diff --git a/.github/.env b/.github/.env index 7a485c4a..cfe5c133 100644 --- a/.github/.env +++ b/.github/.env @@ -3,3 +3,4 @@ NODE_VERSION=20.x PROGRAMS=["programs/escrow", "programs/adversarial_program/margin_withdraw_cpi"] RUST_VERSION=1.78.0 SOLANA_VERSION=1.17.34 +VERIFY_BUILD_VERSION=0.2.11 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d4176dba..2ca7f310 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @richardwu @febo @samuelvanderwaal +* @richardwu @nftechie @leantOnSol diff --git a/.github/file-filters.yml b/.github/file-filters.yml index e7246e46..20839b5f 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -5,7 +5,7 @@ # Programs. programs_common: &programs_common - - ".github/workflows/build-programs.yml" + - ".github/workflows/build-verify-programs.yml" - ".github/workflows/test-programs.yml" - ".github/workflows/main.yml" - ".github/file-filters.yml" diff --git a/.github/workflows/build-programs.yml b/.github/workflows/build-verify-programs.yml similarity index 55% rename from .github/workflows/build-programs.yml rename to .github/workflows/build-verify-programs.yml index 2d9c32f0..e5f54624 100644 --- a/.github/workflows/build-programs.yml +++ b/.github/workflows/build-verify-programs.yml @@ -1,4 +1,4 @@ -name: Build Programs +name: Build Verify Programs on: workflow_call: @@ -26,6 +26,11 @@ on: default: 20.x required: true type: string + verify: + description: Verify build version + default: 0.2.11 + required: true + type: string env: CACHE: true @@ -33,7 +38,7 @@ env: RPC: ${{ secrets.SOLANA_MAINNET_RPC_URL }} jobs: - build_programs: + build_verify_programs: name: Build runs-on: ubuntu-latest steps: @@ -41,9 +46,10 @@ jobs: uses: actions/checkout@v4 - name: Setup SSH agent - uses: tensor-foundation/actions/ssh-agent@v1 + uses: tensor-foundation/actions/ssh-agent@v2 with: - key: ${{ secrets.TOOLBOX_KEY }} + key: | + ${{ secrets.TOOLBOX_KEY }} - name: Load environment variables run: cat .github/.env >> $GITHUB_ENV @@ -54,7 +60,7 @@ jobs: toolchain: ${{ inputs.rust || env.RUST_VERSION }} - name: Install Solana - uses: tensor-foundation/actions/install-solana@v2.1 + uses: tensor-foundation/actions/install-solana@v3 with: version: ${{ inputs.solana || env.SOLANA_VERSION }} cache: ${{ env.CACHE }} @@ -72,10 +78,29 @@ jobs: with: folder: "." key: programs + + - name: Build adversarial program + working-directory: ./programs/adversarial_program + run: cargo-build-sbf + + - name: Dump mainnet binaries + run: pnpm programs:dump_binaries:mainnet + + - name: Cache solana-verify + uses: actions/cache@v4 + id: cache-verify + with: + path: /home/runner/.cargo-install/solana-verify + key: solana-verify-${{ inputs.verify || env.VERIFY_BUILD_VERSION }}-${{ runner.os }} + + - name: Install verify build + if: steps.cache-verify.outputs.cache-hit != 'true' + run: cargo install solana-verify --force --root /home/runner/.cargo-install/solana-verify --version ${{ inputs.verify || env.VERIFY_BUILD_VERSION }} --locked - - name: Build programs + - name: Verify build program shell: bash - run: pnpm programs:build + run: | + /home/runner/.cargo-install/solana-verify/bin/solana-verify build --library-name escrow_program | tee >(tail -n 1 > build_hash.txt) - name: Upload programs build uses: actions/upload-artifact@v4 @@ -83,6 +108,8 @@ jobs: name: programs-build # First wildcard ensures exported paths are consistently under the programs folder. path: | - ./target*/deploy/*.so - ./target*/deploy/*.json - if-no-files-found: error + ./targe*/deploy/*.so + build_hash.txt + ./targe*/deploy/*.json + !./targe*/deploy/*keypair*.json + if-no-files-found: error \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32f02668..f54f7404 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -134,7 +134,7 @@ jobs: command: clippy args: --all-targets --all-features --no-deps -- -D warnings - build_programs: + build_verify_programs: name: Programs needs: [changes, lint_js, lint_rust] if: | @@ -142,7 +142,7 @@ jobs: (needs.lint_js.result == 'success' || needs.lint_js.result == 'skipped') && (needs.lint_rust.result == 'success' || needs.lint_rust.result == 'skipped') && needs.changes.outputs.any == 'true' - uses: ./.github/workflows/build-programs.yml + uses: ./.github/workflows/build-verify-programs.yml secrets: inherit test_programs: @@ -161,7 +161,7 @@ jobs: generate_clients: name: Generate Clients if: ${{ needs.changes.outputs.any == 'true' }} - needs: [changes, build_programs] + needs: [changes, build_verify_programs] runs-on: ubuntu-latest steps: - name: Git checkout diff --git a/.github/workflows/publish-js-client.yml b/.github/workflows/publish-js-client.yml index 3003de52..d7fca658 100644 --- a/.github/workflows/publish-js-client.yml +++ b/.github/workflows/publish-js-client.yml @@ -3,26 +3,13 @@ name: Publish JS Client on: workflow_dispatch: inputs: - bump: - description: Version bump - required: true - default: patch - type: choice - options: - - patch - - minor - - major - - prerelease - - prepatch - - preminor - - premajor tag: - description: NPM Tag (and preid for pre-releases) + description: NPM Tag required: true type: string default: latest - create_release: - description: Create a GitHub release + dry_run: + description: Dry run required: true type: boolean default: true @@ -31,19 +18,22 @@ env: CACHE: true jobs: - build_programs: + build_verify_programs: name: Programs - uses: ./.github/workflows/build-programs.yml + if: github.ref == 'refs/heads/main' || inputs.dry_run == true + uses: ./.github/workflows/build-verify-programs.yml secrets: inherit test_js: name: JS client - needs: build_programs + if: github.ref == 'refs/heads/main' || inputs.dry_run == true + needs: build_verify_programs uses: ./.github/workflows/test-js-client.yml secrets: inherit publish_js: name: JS client / Publish + if: github.ref == 'refs/heads/main' || inputs.dry_run == true runs-on: ubuntu-latest needs: test_js permissions: @@ -71,17 +61,11 @@ jobs: - name: Build working-directory: ./clients/js run: pnpm build - - - name: Bump - id: bump + + - name: Get version + id: get_version working-directory: ./clients/js - run: | - if [ "${{ startsWith(inputs.bump, 'pre') }}" == "true" ]; then - pnpm version ${{ inputs.bump }} --preid ${{ inputs.tag }} --no-git-tag-version - else - pnpm version ${{ inputs.bump }} --no-git-tag-version - fi - echo "new_version=$(pnpm pkg get version | sed 's/"//g')" >> $GITHUB_OUTPUT + run: echo "version=$(pnpm pkg get version | sed 's/"//g')" >> $GITHUB_OUTPUT - name: Set publishing config run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" @@ -90,40 +74,4 @@ jobs: - name: Publish working-directory: ./clients/js - run: pnpm publish --no-git-checks --tag ${{ inputs.tag }} - - - name: Commit and tag new version - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Deploy JS client v${{ steps.bump.outputs.new_version }} - tagging_message: js@v${{ steps.bump.outputs.new_version }} - - - name: Create GitHub release - if: github.event.inputs.create_release == 'true' - uses: ncipollo/release-action@v1 - with: - tag: js@v${{ steps.bump.outputs.new_version }} - - deploy_js_docs: - name: JS client / Deploy docs - runs-on: ubuntu-latest - needs: publish_js - environment: - name: js-client-documentation - url: ${{ steps.deploy.outputs.url }} - steps: - - name: Git checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.ref }} - - - name: Load environment variables - run: cat .github/.env >> $GITHUB_ENV - - - name: Deploy to Vercel - id: deploy - env: - VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} - VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} - working-directory: ./clients/js - run: echo "url=$(vercel deploy --prod --token=${{ secrets.VERCEL_TOKEN }})" | tee $GITHUB_OUTPUT + run: pnpm publish --no-git-checks --tag ${{ inputs.tag }} ${{ inputs.dry_run && '--dry-run' || '' }} \ No newline at end of file diff --git a/.github/workflows/publish-rust-client.yml b/.github/workflows/publish-rust-client.yml index ff678f57..ceee50f1 100644 --- a/.github/workflows/publish-rust-client.yml +++ b/.github/workflows/publish-rust-client.yml @@ -31,9 +31,9 @@ env: CACHE: true jobs: - build_programs: + build_verify_programs: name: Programs - uses: ./.github/workflows/build-programs.yml + uses: ./.github/workflows/build-verify-programs.yml secrets: inherit build_rust_client: @@ -43,7 +43,7 @@ jobs: test_rust_client: name: Rust Client - needs: [build_programs, build_rust_client] + needs: [build_verify_programs, build_rust_client] uses: ./.github/workflows/test-rust-client.yml secrets: inherit diff --git a/package.json b/package.json index f0eaf3b6..ffe2f7df 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "programs:build:artifacts": "zx ./scripts/program/build.mjs artifacts", "programs:build:devnet": "zx ./scripts/program/build.mjs devnet", "programs:build:mainnet": "zx ./scripts/program/build.mjs mainnet", + "programs:dump_binaries:devnet": "zx ./scripts/program/dump_devnet_external.mjs && zx ./scripts/program/dump_devnet_others.mjs", + "programs:dump_binaries:mainnet": "zx ./scripts/program/dump_mainnet_external.mjs && zx ./scripts/program/dump_mainnet_others.mjs", "programs:test": "zx ./scripts/program/test.mjs", "programs:clean": "zx ./scripts/program/clean.mjs", "programs:format": "zx ./scripts/program/format.mjs", diff --git a/scripts/program/build.mjs b/scripts/program/build.mjs index 32ddba65..e3a40348 100644 --- a/scripts/program/build.mjs +++ b/scripts/program/build.mjs @@ -1,20 +1,17 @@ #!/usr/bin/env zx import "zx/globals"; -import { getProgramFolders, workingDirectory } from "../utils.mjs"; +import { workingDirectory, getProgramFolders } from "../utils.mjs"; -const whereToFetchBinariesFrom = argv._.filter( - (a) => a !== path.basename(__filename) -)[0]; +const whereToFetchBinariesFrom = argv._.filter(a => a !== path.basename(__filename))[0]; // Save external programs binaries to the output directory. -import "./dump_mainnet_external.mjs"; +if(whereToFetchBinariesFrom === "artifacts" || whereToFetchBinariesFrom === "mainnet") await import("./dump_mainnet_external.mjs"); +else await import("./dump_devnet_external.mjs"); -// Fetch "Offchain"ProgramAddresse from artifacts/mainnet/devnet -if (whereToFetchBinariesFrom === "artifacts") - await import("../fetch-external-binaries.mjs"); -else if (whereToFetchBinariesFrom === "mainnet") - await import("./dump_mainnet_others.mjs"); -else await import("./dump_devnet.mjs"); +// Fetch binaries (potentially offchain) from artifacts/mainnet/devnet +if(whereToFetchBinariesFrom === "artifacts") await import("../fetch-external-binaries.mjs"); +else if(whereToFetchBinariesFrom === "mainnet") await import("./dump_mainnet_others.mjs"); +else await import("./dump_devnet_others.mjs"); // Build the programs. for (const folder of getProgramFolders()) { diff --git a/scripts/program/dump_devnet_external.mjs b/scripts/program/dump_devnet_external.mjs new file mode 100644 index 00000000..f8b33295 --- /dev/null +++ b/scripts/program/dump_devnet_external.mjs @@ -0,0 +1,22 @@ +#!/usr/bin/env zx +import "zx/globals"; +import { + getExternalAccountAddresses, + getExternalProgramAddresses, + getExternalProgramOutputDir, +} from "../utils.mjs"; +import { dump } from "./dump.mjs"; + +// Get input from environment variables. +const rpc = "https://api.devnet.solana.com"; +const outputDir = getExternalProgramOutputDir(); +// Ensure we have some external programs/accounts to dump. +const programs = getExternalProgramAddresses(); +const accounts = getExternalAccountAddresses(); +const addresses = [ + ...programs.map((program) => `${program}.so`), + ...accounts.map((account) => `${account}.json`), +].flat(); + +echo(`Dumping external accounts to '${outputDir}':`); +await dump(rpc, outputDir, addresses); \ No newline at end of file diff --git a/scripts/program/dump_devnet.mjs b/scripts/program/dump_devnet_others.mjs similarity index 71% rename from scripts/program/dump_devnet.mjs rename to scripts/program/dump_devnet_others.mjs index 6fbde2f6..ba4844b5 100644 --- a/scripts/program/dump_devnet.mjs +++ b/scripts/program/dump_devnet_others.mjs @@ -2,16 +2,18 @@ import "zx/globals"; import { getExternalProgramOutputDir, - getOffchainProgramAddresses, + getOffchainProgramAddresses } from "../utils.mjs"; -import { dump } from "./dump.mjs"; +import { dump } from "./dump.mjs" // Get input from environment variables. const rpc = "https://api.devnet.solana.com"; const outputDir = getExternalProgramOutputDir(); // Ensure we have some external programs/accounts to dump. const programs = getOffchainProgramAddresses(); -const addresses = [...programs.map((program) => `${program}.so`)].flat(); +const addresses = [ + ...programs.map((program) => `${program}.so`), +].flat(); echo(`Dumping external accounts from devnet to '${outputDir}':`); -await dump(rpc, outputDir, addresses); +await dump(rpc, outputDir, addresses); \ No newline at end of file diff --git a/scripts/program/dump_mainnet_external.mjs b/scripts/program/dump_mainnet_external.mjs index 2cb2a103..8b065abc 100644 --- a/scripts/program/dump_mainnet_external.mjs +++ b/scripts/program/dump_mainnet_external.mjs @@ -5,7 +5,7 @@ import { getExternalProgramAddresses, getExternalProgramOutputDir, } from "../utils.mjs"; -import { dump } from "./dump.mjs"; +import { dump } from "./dump.mjs"; // Get input from environment variables. const rpc = process.env.RPC ?? "https://api.mainnet-beta.solana.com"; @@ -19,4 +19,4 @@ const addresses = [ ].flat(); echo(`Dumping external accounts to '${outputDir}':`); -await dump(rpc, outputDir, addresses); +await dump(rpc, outputDir, addresses); \ No newline at end of file diff --git a/scripts/program/dump_mainnet_others.mjs b/scripts/program/dump_mainnet_others.mjs index 4c8d004b..29cc0a10 100644 --- a/scripts/program/dump_mainnet_others.mjs +++ b/scripts/program/dump_mainnet_others.mjs @@ -2,16 +2,18 @@ import "zx/globals"; import { getExternalProgramOutputDir, - getOffchainProgramAddresses, + getOffchainProgramAddresses } from "../utils.mjs"; -import { dump } from "./dump.mjs"; +import { dump } from "./dump.mjs" // Get input from environment variables. const rpc = process.env.RPC ?? "https://api.mainnet-beta.solana.com"; const outputDir = getExternalProgramOutputDir(); // Ensure we have some external programs/accounts to dump. const programs = getOffchainProgramAddresses(); -const addresses = [...programs.map((program) => `${program}.so`)].flat(); +const addresses = [ + ...programs.map((program) => `${program}.so`), +].flat(); -echo(`Dumping external accounts from devnet to '${outputDir}':`); -await dump(rpc, outputDir, addresses); +echo(`Dumping external accounts from mainnet to '${outputDir}':`); +await dump(rpc, outputDir, addresses); \ No newline at end of file