diff --git a/build-verified/action.yaml b/build-verified/action.yaml index 2c5c1db..c9a6a49 100644 --- a/build-verified/action.yaml +++ b/build-verified/action.yaml @@ -8,6 +8,11 @@ inputs: description: "Features to enable during build" required: false default: "" + base-image: + description: "Override solana-verify's --base-image. Pass the same value + to verify-build to keep the verify-PDA aligned." + required: false + default: "" runs: using: "composite" @@ -20,10 +25,18 @@ runs: - name: Build Verified shell: bash + env: + FEATURES_INPUT: ${{ inputs.features }} + BASE_IMAGE: ${{ inputs.base-image }} + PROGRAM: ${{ inputs.program }} run: | - FEATURES="${{ inputs.features }}" - if [ ! -z "$FEATURES" ]; then - FEATURES="--features $FEATURES" + FEATURES="" + if [ -n "$FEATURES_INPUT" ]; then + FEATURES="--features $FEATURES_INPUT" + fi + BASE_IMAGE_ARG="" + if [ -n "$BASE_IMAGE" ]; then + BASE_IMAGE_ARG="--base-image $BASE_IMAGE" fi # Run solana-verify directly without Docker - ~/.cargo/bin/solana-verify build --library-name ${{ inputs.program }} -- $FEATURES + ~/.cargo/bin/solana-verify build $BASE_IMAGE_ARG --library-name "$PROGRAM" -- $FEATURES diff --git a/readme.md b/readme.md index b97fec8..bf2cd00 100644 --- a/readme.md +++ b/readme.md @@ -328,7 +328,7 @@ These actions use the [program-metadata](https://github.com/solana-program/progr - `build-anchor`: Specialized Anchor program builder - `program-upgrade`: Handles the exteding of the program account in case the program is getting bigger and either sets the buffer or skips that in case of squads deploy - `idl-upload`: Either sets the Anchor IDL buffer or skips that in case of squads deploy -- `verify-build`: Verifies on-chain programs match source using solana-verify andthe osec api +- `verify-build`: Writes the signer's verify PDA and queues a remote build with `solana-verify remote submit-job` (mainnet only), recording the hash before deploy so the buffer is resolvable via the osec api; with squads also exports the program-authority PDA tx (`pda_tx`) for the multisig ### Squads buffer-only release diff --git a/verify-build/action.yaml b/verify-build/action.yaml index 53e2841..11ea0a8 100644 --- a/verify-build/action.yaml +++ b/verify-build/action.yaml @@ -40,6 +40,16 @@ inputs: vault-address: description: "The Squads vault address (required if use-squads is true)" required: false + base-image: + description: "Override solana-verify's --base-image. Must match + build-verified to keep the verify-PDA aligned with the built .so." + required: false + default: "" + features: + description: "Comma-separated cargo features. Must match build-verified + to keep the verify-PDA aligned with the built .so." + required: false + default: "" runs: using: "composite" @@ -54,62 +64,119 @@ runs: - name: Debug Environment shell: bash + env: + EVENT_NETWORK: ${{ github.event.inputs.network }} + EVENT_VERIFY: ${{ github.event.inputs.verify }} + HAS_KEYPAIR: ${{ inputs.keypair != '' }} run: | - echo "Network: ${{ github.event.inputs.network }}" - echo "Verify: ${{ github.event.inputs.verify }}" - echo "Has keypair: ${{ inputs.keypair != '' }}" + echo "Network: $EVENT_NETWORK" + echo "Verify: $EVENT_VERIFY" + echo "Has keypair: $HAS_KEYPAIR" + # Writes the signer's verify PDA and queues a remote build. Runs for both squads and non-squads so the built hash is recorded before deploy and the buffer is resolvable via /resolve-hash + # In the squads flow this is separate from the program authority PDA tx exported below which the multisig executes itself - name: Verify Build shell: bash - if: inputs.use-squads == 'false' + env: + SKIP_BUILD: ${{ inputs.skip-build }} + SKIP_PROMPT: ${{ inputs.skip-prompt }} + MOUNT_PATH: ${{ inputs.mount-path }} + COMMIT_HASH: ${{ inputs.commit-hash }} + NETWORK: ${{ inputs.network }} + BASE_IMAGE: ${{ inputs.base-image }} + FEATURES_INPUT: ${{ inputs.features }} + PROGRAM_ID: ${{ inputs.program-id }} + RPC_URL: ${{ inputs.rpc-url }} + PROGRAM: ${{ inputs.program }} + REPO_URL: ${{ inputs.repo-url }} run: | ARGS="" - if [ "${{ inputs.skip-build }}" = "true" ]; then + if [ "$SKIP_BUILD" = "true" ]; then ARGS="$ARGS --skip-build" fi - if [ "${{ inputs.skip-prompt }}" = "true" ]; then + if [ "$SKIP_PROMPT" = "true" ]; then ARGS="$ARGS --skip-prompt" fi - if [ -n "${{ inputs.mount-path }}" ]; then - ARGS="$ARGS --mount-path ${{ inputs.mount-path }}" + if [ -n "$MOUNT_PATH" ]; then + ARGS="$ARGS --mount-path $MOUNT_PATH" fi - if [ -n "${{ inputs.commit-hash }}" ]; then - ARGS="$ARGS --commit-hash ${{ inputs.commit-hash }}" + if [ -n "$COMMIT_HASH" ]; then + ARGS="$ARGS --commit-hash $COMMIT_HASH" fi - if [ "${{ inputs.network }}" = "mainnet" ]; then - ARGS="$ARGS --remote" + if [ -n "$BASE_IMAGE" ]; then + ARGS="$ARGS --base-image $BASE_IMAGE" fi + CARGO_ARGS="" + if [ -n "$FEATURES_INPUT" ]; then + CARGO_ARGS="-- --features $FEATURES_INPUT" + fi + + # With --skip-build this only uploads the PDA, the remote worker does the actual build solana-verify verify-from-repo \ - --program-id ${{ inputs.program-id }} \ - --url ${{ inputs.rpc-url }} \ - --library-name ${{ inputs.program }} \ + --program-id "$PROGRAM_ID" \ + --url "$RPC_URL" \ + --library-name "$PROGRAM" \ -k ./deploy-keypair.json \ $ARGS \ - ${{ inputs.repo-url }} + "$REPO_URL" \ + $CARGO_ARGS + + # Queue the remote build. Replaces the deprecated --remote flag and is mainnet only + if [ "$NETWORK" = "mainnet" ]; then + UPLOADER=$(solana-keygen pubkey ./deploy-keypair.json) + echo "Queuing remote verify job for program $PROGRAM_ID (uploader $UPLOADER)" + solana-verify remote submit-job \ + --program-id "$PROGRAM_ID" \ + --uploader "$UPLOADER" \ + --url "$RPC_URL" + else + echo "Network is '$NETWORK', not mainnet; skipping remote submit-job (remote verification is mainnet-only)." + fi - name: Export PDA Transaction id: export-pda if: inputs.use-squads == 'true' shell: bash + env: + BASE_IMAGE: ${{ inputs.base-image }} + FEATURES_INPUT: ${{ inputs.features }} + REPO_URL: ${{ inputs.repo-url }} + PROGRAM_ID: ${{ inputs.program-id }} + VAULT_ADDRESS: ${{ inputs.vault-address }} + COMMIT_HASH: ${{ inputs.commit-hash }} + RPC_URL: ${{ inputs.rpc-url }} + PROGRAM: ${{ inputs.program }} run: | echo "=== Exporting PDA Transaction ===" + BASE_IMAGE_ARG="" + if [ -n "$BASE_IMAGE" ]; then + BASE_IMAGE_ARG="--base-image $BASE_IMAGE" + fi + + CARGO_ARGS="" + if [ -n "$FEATURES_INPUT" ]; then + CARGO_ARGS="-- --features $FEATURES_INPUT" + fi + # Get the last line which should be the base64 transaction TX=$(solana-verify export-pda-tx \ - ${{ inputs.repo-url }} \ - --program-id ${{ inputs.program-id }} \ - --uploader ${{ inputs.vault-address }} \ - --commit-hash ${{ inputs.commit-hash }} \ - --url ${{ inputs.rpc-url }} \ - --library-name ${{ inputs.program }} \ - --encoding base64 | tail -n 1) - + "$REPO_URL" \ + --program-id "$PROGRAM_ID" \ + --uploader "$VAULT_ADDRESS" \ + --commit-hash "$COMMIT_HASH" \ + --url "$RPC_URL" \ + --library-name "$PROGRAM" \ + $BASE_IMAGE_ARG \ + --encoding base64 \ + $CARGO_ARGS | tail -n 1) + # Verify we got a base64 string if [[ ! "$TX" =~ ^[A-Za-z0-9+/=]*$ ]]; then echo "Error: Invalid base64 transaction" diff --git a/write-program-buffer/action.yaml b/write-program-buffer/action.yaml index 940844b..f5a52a9 100644 --- a/write-program-buffer/action.yaml +++ b/write-program-buffer/action.yaml @@ -83,13 +83,32 @@ runs: if: steps.check-program.outputs.exists == 'true' shell: bash run: | + MIN_EXTEND_SIZE=10240 + MAX_PERMITTED_DATA_LENGTH=10485760 + PROGRAMDATA_METADATA_SIZE=45 + MAX_PROGRAM_SIZE=$((MAX_PERMITTED_DATA_LENGTH - PROGRAMDATA_METADATA_SIZE)) + REQUIRED_SIZE=$(wc -c < ./target/deploy/${{ inputs.program }}.so) CURRENT_SIZE="${{ steps.check-program.outputs.data_len }}" echo "Required size: $REQUIRED_SIZE" echo "Current size: $CURRENT_SIZE" + if [ "$REQUIRED_SIZE" -gt "$MAX_PROGRAM_SIZE" ]; then + echo "Error: program size of $REQUIRED_SIZE bytes exceeds the maximum program size of $MAX_PROGRAM_SIZE bytes" + exit 1 + fi + if [ "$REQUIRED_SIZE" -gt "$CURRENT_SIZE" ]; then + HEADROOM=$((MAX_PROGRAM_SIZE - CURRENT_SIZE)) + if [ "$HEADROOM" -lt "$MIN_EXTEND_SIZE" ]; then + echo "Only $HEADROOM bytes of headroom to the maximum account size, the SIMD-0431 minimum does not apply" + MIN_EXTEND_SIZE=$HEADROOM + fi EXTEND_SIZE=$((REQUIRED_SIZE - CURRENT_SIZE)) + if [ "$EXTEND_SIZE" -lt "$MIN_EXTEND_SIZE" ]; then + echo "Extend size of $EXTEND_SIZE bytes is below the SIMD-0431 minimum, extending by $MIN_EXTEND_SIZE bytes instead" + EXTEND_SIZE=$MIN_EXTEND_SIZE + fi echo "Program needs to be extended by $EXTEND_SIZE bytes" solana program extend ${{ inputs.program-id }} $EXTEND_SIZE \ --keypair ./deploy-keypair.json \