|
| 1 | +# Rust Toolchain Pinning for Reproducible Builds |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | + |
| 5 | +Previously, all GitHub Actions workflows used `dtolnay/rust-toolchain@stable`, which floats to whatever the latest stable Rust release is at the moment of CI execution. This caused several critical issues: |
| 6 | + |
| 7 | +### Issues with Floating Toolchain |
| 8 | + |
| 9 | +1. **Non-Reproducible Builds**: Two builds a week apart could produce different WASM bytecode |
| 10 | +2. **Hash Manifest Breakage**: SHA-256 manifest produced by `release-hash.yml` would change unexpectedly |
| 11 | +3. **Bump Night Volatility**: Changes to wasm-encoder or soroban-sdk re-exports after Rust releases would alter output |
| 12 | +4. **Broken Reproducibility Promise**: Issue #8 promised reproducible builds, but floating toolchain violated this |
| 13 | + |
| 14 | +### Example Failure Scenario |
| 15 | + |
| 16 | +``` |
| 17 | +Week 1 (Rust 1.94.0): |
| 18 | + cargo build --release → apexchainx_calculator.wasm |
| 19 | + SHA-256: abc123... |
| 20 | +
|
| 21 | +Week 2 (Rust 1.94.1 released): |
| 22 | + cargo build --release → apexchainx_calculator.wasm |
| 23 | + SHA-256: def456... ← DIFFERENT! |
| 24 | +``` |
| 25 | + |
| 26 | +This breaks: |
| 27 | +- Deployment verification |
| 28 | +- Audit trail integrity |
| 29 | +- Reproducible build guarantees |
| 30 | +- CI/CD stability |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Solution |
| 35 | + |
| 36 | +Pin the Rust toolchain to a specific version across all workflows and local development. |
| 37 | + |
| 38 | +### Changes Made |
| 39 | + |
| 40 | +#### 1. Created `rust-toolchain.toml` at Repository Root |
| 41 | + |
| 42 | +```toml |
| 43 | +[toolchain] |
| 44 | +channel = "1.94.1" |
| 45 | +``` |
| 46 | + |
| 47 | +**Purpose**: |
| 48 | +- Cargo and rustup automatically respect this file |
| 49 | +- Ensures local development matches CI environment |
| 50 | +- Single source of truth for Rust version |
| 51 | + |
| 52 | +**Benefits**: |
| 53 | +- Developers automatically use correct version when running `cargo build` |
| 54 | +- No manual configuration needed per developer |
| 55 | +- Prevents "works on my machine" issues |
| 56 | + |
| 57 | +#### 2. Updated GitHub Actions Workflows |
| 58 | + |
| 59 | +**Changed from:** |
| 60 | +```yaml |
| 61 | +uses: dtolnay/rust-toolchain@stable |
| 62 | +``` |
| 63 | +
|
| 64 | +**Changed to:** |
| 65 | +```yaml |
| 66 | +uses: dtolnay/rust-toolchain@1.94.1 |
| 67 | +``` |
| 68 | +
|
| 69 | +**Files Modified:** |
| 70 | +- `.github/workflows/ci.yml` |
| 71 | +- `.github/workflows/release-hash.yml` |
| 72 | +- `.github/workflows/security.yml` |
| 73 | + |
| 74 | +--- |
| 75 | + |
| 76 | +## Why Rust 1.94.1? |
| 77 | + |
| 78 | +1. **Current Stable**: Latest stable release as of implementation (March 2026) |
| 79 | +2. **Soroban SDK Compatibility**: Compatible with soroban-sdk v21.0.0 |
| 80 | +3. **Tested & Verified**: Already in use on development systems |
| 81 | +4. **Recent Features**: Includes modern Rust features and optimizations |
| 82 | + |
| 83 | +--- |
| 84 | + |
| 85 | +## Reproducibility Guarantees |
| 86 | + |
| 87 | +With this change, the following are now **guaranteed**: |
| 88 | + |
| 89 | +### ✅ Deterministic WASM Bytecode |
| 90 | +Same source code + same toolchain = identical WASM output |
| 91 | + |
| 92 | +### ✅ Stable SHA-256 Manifests |
| 93 | +`release-hash.yml` will produce consistent hashes across builds: |
| 94 | +```bash |
| 95 | +# Build on Monday |
| 96 | +sha256sum apexchainx_calculator.wasm |
| 97 | +# abc123def456... |
| 98 | +
|
| 99 | +# Build on Friday (same code) |
| 100 | +sha256sum apexchainx_calculator.wasm |
| 101 | +# abc123def456... ← IDENTICAL |
| 102 | +``` |
| 103 | + |
| 104 | +### ✅ CI/Local Parity |
| 105 | +CI builds match local developer builds exactly |
| 106 | + |
| 107 | +### ✅ Audit Trail Integrity |
| 108 | +Historical builds remain reproducible for security audits |
| 109 | + |
| 110 | +--- |
| 111 | + |
| 112 | +## Maintenance & Upgrades |
| 113 | + |
| 114 | +### When to Update Rust Version |
| 115 | + |
| 116 | +Update the pinned version when: |
| 117 | +1. **Security patches**: Critical Rust security vulnerabilities |
| 118 | +2. **Soroban SDK requirements**: New SDK version requires newer Rust |
| 119 | +3. **Desired features**: Team decides to adopt new Rust features |
| 120 | +4. **Scheduled maintenance**: Quarterly or semi-annual updates |
| 121 | + |
| 122 | +### How to Update |
| 123 | + |
| 124 | +1. **Test locally first:** |
| 125 | + ```bash |
| 126 | + # Update rust-toolchain.toml |
| 127 | + channel = "1.95.0" |
| 128 | + |
| 129 | + # Build and test |
| 130 | + cargo build --release --target wasm32-unknown-unknown |
| 131 | + cargo test |
| 132 | + ``` |
| 133 | + |
| 134 | +2. **Update all workflows:** |
| 135 | + ```yaml |
| 136 | + uses: dtolnay/rust-toolchain@1.95.0 |
| 137 | + ``` |
| 138 | + |
| 139 | +3. **Verify reproducibility:** |
| 140 | + ```bash |
| 141 | + # Build twice and compare hashes |
| 142 | + cargo clean |
| 143 | + cargo build --release --target wasm32-unknown-unknown |
| 144 | + sha256sum target/wasm32-unknown-unknown/release/*.wasm > hash1.txt |
| 145 | + |
| 146 | + cargo clean |
| 147 | + cargo build --release --target wasm32-unknown-unknown |
| 148 | + sha256sum target/wasm32-unknown-unknown/release/*.wasm > hash2.txt |
| 149 | + |
| 150 | + diff hash1.txt hash2.txt # Should be identical |
| 151 | + ``` |
| 152 | + |
| 153 | +4. **Document the change:** |
| 154 | + - Update CHANGELOG.md |
| 155 | + - Note any breaking changes or new features enabled |
| 156 | + - Update this document with new version rationale |
| 157 | + |
| 158 | +### Version Update Checklist |
| 159 | + |
| 160 | +- [ ] Test new Rust version locally |
| 161 | +- [ ] Verify WASM builds successfully |
| 162 | +- [ ] Run full test suite |
| 163 | +- [ ] Update `rust-toolchain.toml` |
| 164 | +- [ ] Update all three workflow files |
| 165 | +- [ ] Verify SHA-256 reproducibility |
| 166 | +- [ ] Update documentation |
| 167 | +- [ ] Create PR with clear rationale |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## Verification |
| 172 | + |
| 173 | +### Verify Local Toolchain |
| 174 | + |
| 175 | +```bash |
| 176 | +# Check current Rust version |
| 177 | +rustc --version |
| 178 | +# Should output: rustc 1.94.1 (e408947bf 2026-03-25) |
| 179 | +
|
| 180 | +# Cargo respects rust-toolchain.toml automatically |
| 181 | +cargo --version |
| 182 | +``` |
| 183 | + |
| 184 | +### Verify Reproducible Builds |
| 185 | + |
| 186 | +```bash |
| 187 | +# Build WASM twice and compare |
| 188 | +cargo clean |
| 189 | +cargo build --release --manifest-path apexchainx_calculator/Cargo.toml --target wasm32-unknown-unknown |
| 190 | +sha256sum apexchainx_calculator/target/wasm32-unknown-unknown/release/apexchainx_calculator.wasm |
| 191 | +
|
| 192 | +cargo clean |
| 193 | +cargo build --release --manifest-path apexchainx_calculator/Cargo.toml --target wasm32-unknown-unknown |
| 194 | +sha256sum apexchainx_calculator/target/wasm32-unknown-unknown/release/apexchainx_calculator.wasm |
| 195 | +
|
| 196 | +# SHA-256 hashes MUST match |
| 197 | +``` |
| 198 | + |
| 199 | +### Verify CI Alignment |
| 200 | + |
| 201 | +```bash |
| 202 | +# Check workflow files |
| 203 | +grep "rust-toolchain@" .github/workflows/*.yml |
| 204 | +
|
| 205 | +# All should show: dtolnay/rust-toolchain@1.94.1 |
| 206 | +``` |
| 207 | + |
| 208 | +--- |
| 209 | + |
| 210 | +## Impact Analysis |
| 211 | + |
| 212 | +### Before Fix |
| 213 | + |
| 214 | +| Aspect | Status | |
| 215 | +|--------|--------| |
| 216 | +| Build reproducibility | ❌ Not guaranteed | |
| 217 | +| SHA-256 stability | ❌ Changes over time | |
| 218 | +| CI/local parity | ❌ May differ | |
| 219 | +| Audit trail | ❌ Unreliable | |
| 220 | +| Deployment verification | ❌ Inconsistent | |
| 221 | + |
| 222 | +### After Fix |
| 223 | + |
| 224 | +| Aspect | Status | |
| 225 | +|--------|--------| |
| 226 | +| Build reproducibility | ✅ Guaranteed | |
| 227 | +| SHA-256 stability | ✅ Stable | |
| 228 | +| CI/local parity | ✅ Identical | |
| 229 | +| Audit trail | ✅ Reliable | |
| 230 | +| Deployment verification | ✅ Consistent | |
| 231 | + |
| 232 | +--- |
| 233 | + |
| 234 | +## Related Issues |
| 235 | + |
| 236 | +- **Issue #8**: Reproducibility promise - Now fulfilled |
| 237 | +- **Release Hash Workflow**: SHA-256 manifests now stable |
| 238 | +- **Security Audits**: Historical builds now reproducible |
| 239 | + |
| 240 | +--- |
| 241 | + |
| 242 | +## Developer Experience |
| 243 | + |
| 244 | +### No Action Required |
| 245 | + |
| 246 | +Developers using `cargo` automatically get the correct Rust version: |
| 247 | + |
| 248 | +```bash |
| 249 | +# When you clone the repo and run cargo |
| 250 | +git clone <repo> |
| 251 | +cd ApexChainx-Contracts |
| 252 | +
|
| 253 | +# cargo automatically reads rust-toolchain.toml |
| 254 | +cargo build |
| 255 | +# rustup will download 1.94.1 if needed |
| 256 | +``` |
| 257 | + |
| 258 | +### Manual Installation (if needed) |
| 259 | + |
| 260 | +If you need to manually install Rust 1.94.1: |
| 261 | + |
| 262 | +```bash |
| 263 | +rustup install 1.94.1 |
| 264 | +rustup default 1.94.1 |
| 265 | +``` |
| 266 | + |
| 267 | +--- |
| 268 | + |
| 269 | +## Rollout Plan |
| 270 | + |
| 271 | +1. **Immediate**: Pin to 1.94.1 (current stable) |
| 272 | +2. **Verification**: Run CI and verify all workflows pass |
| 273 | +3. **Documentation**: Update team on new reproducibility guarantees |
| 274 | +4. **Monitoring**: Watch for any unexpected issues |
| 275 | +5. **Future**: Schedule periodic Rust version reviews (quarterly) |
| 276 | + |
| 277 | +--- |
| 278 | + |
| 279 | +## Conclusion |
| 280 | + |
| 281 | +This change ensures: |
| 282 | +- ✅ Reproducible builds across all environments |
| 283 | +- ✅ Stable SHA-256 hash manifests |
| 284 | +- ✅ CI/local development parity |
| 285 | +- ✅ Fulfillment of reproducibility promises |
| 286 | +- ✅ Reliable audit trail for security |
| 287 | + |
| 288 | +The pinned toolchain eliminates a source of non-determinism and provides the foundation for trustworthy, verifiable builds. |
| 289 | + |
| 290 | +--- |
| 291 | + |
| 292 | +**Last Updated**: 2026-06-21 |
| 293 | +**Rust Version**: 1.94.1 |
| 294 | +**Next Review**: 2026-09-21 (Quarterly) |
0 commit comments