- docs/analysis/base-fee-floor.md: the base-fee floors (100 gwei per gas, 10,000 gwei per pgas), B_p 120,000 and S_p 30,000 pgas, the calibrated v1 pgas table (intrinsic 300, modexp 10 + 1 per 10 bytes) from the measured 44 cycles per EVM gas and 9 cycles per pgas, with the arithmetic and a stated price assumption; spec 05 section 5.10. The parameters are implemented on the node fork branch testnet-params (vendor, not in this repository). - docs/testnet/README.md: igneum-testnet-1 (chain id 4462, ports 268xx, frozen genesis 2026-10-05T00:00:00Z with its message and hash, mainnet finality window, every switch from genesis, no override file) and the reset policy. Every value proposed, for the morning sign-off. - docs/plans/history-rewrite.md: G14, the exact git-filter-repo pass, the dry run on a throwaway mirror clone (0 identity hits, 0 secrets, every stamp +0000, 312 commits), what breaks and the order for the morning. - G13: app/igneum-app/src/inputs.rs and igneum-ota-sign sign-inputs / verify-inputs; push-inputs.sh signs payload-inputs.json with the OTA key and pins the node commit (packaging/windows/node-source.pin); windows.yml verifies the signature with the embedded key, the zip, every file and the pin before building and uploads the verified record; fetch-ci-artifacts.sh signs the update manifest only with --sign-manifest <run-id> after re-verifying that run's inputs. test-inputs-signing.sh (16 cases) and tools/ci/check-workflow-shell.mjs. - site: testnet terms on the download section, wallet.html (wallet_addEthereumChain, chain ids 4462/4463, IGN, 18 decimals), the litepaper's app paragraph (MetaMask and the coming Igneum Wallet, no hardware wallet) and the miner fee sentence (no protocol fee; optional 1% in the miner software, off with one flag). node site/build.mjs and the link check pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
264 lines
15 KiB
YAML
264 lines
15 KiB
YAML
# Windows one-click app, built on GitHub's Windows runners so no PC is needed (4 October 2026).
|
|
#
|
|
# parse: every .ps1 under the Windows folders through the Windows PowerShell 5.1 parser (powershell.exe, the PowerShell
|
|
# on the PCs; 5.1 rejects "$name: text" and that class broke two launchers on 4 October), PSScriptAnalyzer as
|
|
# warnings, and a parenthesis check of every .bat/.cmd (the bare ")" class of 3 October). Required: build
|
|
# needs it.
|
|
# build: the engine (app/igneum-app, cargo on the MSVC target, so one fewer input), the window host exactly as
|
|
# app\windows\BUILD-APP.bat does it (MSVC, WebView2 SDK from NuGet, static loader, host.rc with the coin icon),
|
|
# the payload with packaging/windows/make-payload.sh in Git Bash, the installer with build-installer.ps1
|
|
# (Inno Setup, rcedit), a smoke run of both exes (--version, --help), the launcher's DRY_RUN, then the installer,
|
|
# the payload zip and the host as artifacts (90 days).
|
|
#
|
|
# Inputs that are not in git (igneumd.exe, igneum-miner.exe from the node fork; the prebuilt GPU workers with NVIDIA's
|
|
# NVRTC DLLs) come from payload-inputs.zip on the downloads host, published by packaging/windows/push-inputs.sh on the
|
|
# Mac; the DL_TOKEN repository secret is the path token (gh secret set DL_TOKEN < ~/.config/igneum/dl-token).
|
|
# The zip is trusted only through payload-inputs.json and its detached Ed25519 signature, made on the Mac with the
|
|
# OTA key: the step "payload inputs" verifies the signature with the public key compiled into the app
|
|
# (igneum-ota-sign verify-inputs embedded, built by the engine step), checks the zip's sha256 and every unpacked
|
|
# file against the manifest, and checks the manifest's node commit against packaging/windows/node-source.pin in
|
|
# this checkout, all before anything is built from them (review round 4, R4.5.2, ledger G13). The verified
|
|
# manifest, its signature and the runner's record go up as the igneum-windows-inputs artifact, which
|
|
# packaging/windows/fetch-ci-artifacts.sh re-verifies on the Mac before it will sign an update manifest.
|
|
name: windows-ci
|
|
on:
|
|
push:
|
|
branches: [master]
|
|
paths:
|
|
- 'app/**'
|
|
- 'packaging/windows/**'
|
|
- 'packaging/mac/packaged-config.sh'
|
|
- 'proto-cuda/windows-app/**'
|
|
- 'proto-cuda/windows-miner/**'
|
|
- 'proto-cuda/windows-node/**'
|
|
- 'proto-cuda/nvrtc/**'
|
|
- 'proto-cuda/build.bat'
|
|
- 'proto-opencl/**'
|
|
- 'proving/windows-wsl2/**'
|
|
- 'relay/clients/**'
|
|
- 'relay/playbooks/**'
|
|
- 'brand/icons/**'
|
|
- 'tools/ci/windows/**'
|
|
- '.github/workflows/windows.yml'
|
|
workflow_dispatch:
|
|
|
|
concurrency:
|
|
group: windows-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
parse:
|
|
name: PowerShell 5.1 parse, PSScriptAnalyzer, batch parentheses
|
|
runs-on: windows-latest
|
|
timeout-minutes: 15
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Windows PowerShell 5.1 parse of every .ps1 (with the negative self-test)
|
|
shell: powershell
|
|
run: |
|
|
$PSVersionTable.PSVersion.ToString()
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\ci\windows\check-ps51.ps1
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
- name: parentheses in every .bat and .cmd (with the negative self-test)
|
|
shell: powershell
|
|
run: |
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\ci\windows\check-bat.ps1
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
- name: PSScriptAnalyzer (warnings only, never fails the job)
|
|
shell: powershell
|
|
continue-on-error: true
|
|
run: |
|
|
try {
|
|
if (-not (Get-Module -ListAvailable PSScriptAnalyzer)) {
|
|
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser | Out-Null
|
|
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
|
|
Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser -AllowClobber
|
|
}
|
|
Import-Module PSScriptAnalyzer
|
|
$folders = @('proto-cuda/windows-app', 'proto-cuda/windows-miner', 'proto-cuda/windows-node', 'proving/windows-wsl2', 'relay/clients', 'relay/playbooks', 'packaging/windows', 'tools/ci/windows')
|
|
$total = 0
|
|
foreach ($f in $folders) {
|
|
$results = Invoke-ScriptAnalyzer -Path $f -Recurse -Severity Warning, Error -ExcludeRule PSAvoidUsingWriteHost, PSUseShouldProcessForStateChangingFunctions, PSUseSingularNouns, PSAvoidUsingPositionalParameters
|
|
foreach ($r in $results) {
|
|
$total += 1
|
|
$file = ($r.ScriptPath -replace '\\', '/')
|
|
Write-Host ("::warning file={0},line={1}::{2}: {3}" -f $file, $r.Line, $r.RuleName, $r.Message)
|
|
}
|
|
}
|
|
Write-Host "PSScriptAnalyzer: $total warnings (informational)"
|
|
} catch {
|
|
Write-Host "::warning::PSScriptAnalyzer could not run: $_"
|
|
}
|
|
|
|
build:
|
|
name: engine, window host, payload, installer, smoke run
|
|
needs: parse
|
|
runs-on: windows-latest
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: versions
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
v="$(sed -n 's/^version = "\(.*\)"/\1/p' app/igneum-app/Cargo.toml | head -1)"
|
|
echo "APP_VERSION=$v" >> "$GITHUB_ENV"
|
|
echo "app version $v"
|
|
rustc --version; cargo --version
|
|
git --version; bash --version | head -1; perl --version | sed -n 2p; 7z 2>/dev/null | head -2 | tail -1 || true
|
|
|
|
- name: engine (app/igneum-app, cargo build --release on the MSVC target)
|
|
shell: bash
|
|
working-directory: app/igneum-app
|
|
run: |
|
|
set -euo pipefail
|
|
cargo build --release --locked
|
|
ls -la target/release/igneum-app.exe
|
|
|
|
- name: payload inputs (payload-inputs.zip from the downloads host, signature, hashes and node commit verified)
|
|
shell: bash
|
|
env:
|
|
DL_TOKEN: ${{ secrets.DL_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "${DL_TOKEN:-}" ]; then
|
|
echo "::error::the DL_TOKEN repository secret is not set. On the Mac: tr -d '[:space:]' < ~/.config/igneum/dl-token | gh secret set DL_TOKEN --repo igneum-network/igneum"
|
|
exit 1
|
|
fi
|
|
base="https://dl.igneum.network/dl/$DL_TOKEN"
|
|
signer="app/igneum-app/target/release/igneum-ota-sign.exe"
|
|
[ -x "$signer" ] || { echo "::error::$signer was not built by the engine step"; exit 1; }
|
|
pin="packaging/windows/node-source.pin"
|
|
[ -s "$pin" ] || { echo "::error::$pin is missing: push-inputs.sh writes it, commit it with the inputs push"; exit 1; }
|
|
mkdir -p build/inputs "$HOME/.config/igneum"
|
|
printf '%s' "$DL_TOKEN" > "$HOME/.config/igneum/dl-token" # make-payload.sh reads it for the update manifest URL
|
|
curl -fsSL --retry 3 -o build/payload-inputs.json "$base/payload-inputs.json"
|
|
curl -fsSL --retry 3 -o build/payload-inputs.json.sig "$base/payload-inputs.json.sig"
|
|
curl -fsSL --retry 3 -o build/payload-inputs.zip "$base/payload-inputs.zip"
|
|
echo "inputs manifest:"; cat build/payload-inputs.json
|
|
# 1. the signature (the key compiled into the app), the zip's sha256 and size, the pinned node commit: all before unpacking
|
|
"$signer" verify-inputs embedded build/payload-inputs.json build/payload-inputs.json.sig --zip build/payload-inputs.zip --node-commit "$pin"
|
|
7z x -y -bso0 -bsp0 -obuild/inputs-unpacked build/payload-inputs.zip
|
|
mv build/inputs-unpacked/payload-inputs/* build/inputs/
|
|
# 2. every unpacked file by sha256 and size, and nothing in the folder the manifest does not name
|
|
"$signer" verify-inputs embedded build/payload-inputs.json build/payload-inputs.json.sig --dir build/inputs
|
|
echo "inputs:"; ls -la build/inputs
|
|
for f in igneumd.exe igneum-miner.exe; do [ -f "build/inputs/$f" ] || { echo "::error::payload-inputs.zip has no $f"; exit 1; }; done
|
|
# 3. the runner's record for fetch-ci-artifacts.sh, which re-verifies the signature and the pin on the Mac
|
|
fp="$("$signer" embedded | sed -n 2p)"
|
|
node_commit="$(jq -r .node_source_commit build/payload-inputs.json)"
|
|
zip_sha="$(jq -r .zip.sha256 build/payload-inputs.json)"
|
|
mkdir -p build/inputs-artifact
|
|
cp build/payload-inputs.json build/payload-inputs.json.sig build/inputs-artifact/
|
|
printf '{ "run_id": "%s", "run_attempt": "%s", "head_sha": "%s", "key_fingerprint": "%s", "node_commit": "%s", "zip_sha256": "%s", "verified_at": "%s" }\n' \
|
|
"$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" "$GITHUB_SHA" "$fp" "$node_commit" "$zip_sha" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > build/inputs-artifact/inputs-verified.json
|
|
cat build/inputs-artifact/inputs-verified.json
|
|
|
|
- name: window host (app\windows\BUILD-APP.bat, exactly as on the PC)
|
|
shell: cmd
|
|
working-directory: app\windows
|
|
run: call BUILD-APP.bat < nul
|
|
|
|
- name: payload (packaging/windows/make-payload.sh, as on the Mac, in Git Bash)
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
export IGNEUM_WIN_RELEASE="$PWD/build/inputs"
|
|
export IGNEUM_WORKERS_DIR="$PWD/build/inputs"
|
|
export IGNEUM_APP_EXE="$PWD/app/igneum-app/target/release/igneum-app.exe"
|
|
packaging/windows/make-payload.sh "$PWD/packaging/windows/dist/igneum-windows-app.zip"
|
|
test -f "packaging/windows/igneum-windows-app/Igneum Miner.exe" || { echo "::error::the window host did not land in the payload"; exit 1; }
|
|
|
|
- name: installer (packaging/windows/build-installer.ps1 in Windows PowerShell 5.1, Inno Setup, rcedit)
|
|
shell: powershell
|
|
working-directory: packaging\windows
|
|
run: |
|
|
$iscc = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
|
|
if (-not (Test-Path $iscc)) { choco install innosetup -y --no-progress | Out-Null }
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\build-installer.ps1 -NoWinget -Version $env:APP_VERSION
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
Get-ChildItem dist | Format-Table Name, Length
|
|
|
|
- name: smoke run (igneum-app.exe --version, Igneum Miner.exe --version and --help)
|
|
shell: powershell
|
|
run: |
|
|
$payload = Resolve-Path 'packaging\windows\igneum-windows-app'
|
|
function Run-Capture([string]$exe, [string]$flag) {
|
|
$out = Join-Path $env:RUNNER_TEMP ('smoke-' + [IO.Path]::GetRandomFileName() + '.txt')
|
|
$p = Start-Process -FilePath $exe -ArgumentList $flag -Wait -NoNewWindow -PassThru -RedirectStandardOutput $out
|
|
$text = if (Test-Path $out) { (Get-Content $out -Raw) } else { '' }
|
|
Write-Host ("{0} {1} -> exit {2}: {3}" -f (Split-Path -Leaf $exe), $flag, $p.ExitCode, $text.Trim())
|
|
if ($p.ExitCode -ne 0) { throw "$exe $flag exited $($p.ExitCode)" }
|
|
return $text
|
|
}
|
|
$v = $env:APP_VERSION
|
|
$a = Run-Capture (Join-Path $payload 'igneum-app.exe') '--version'
|
|
if ($a -notmatch "igneum-app $([regex]::Escape($v))") { throw "igneum-app --version did not print 'igneum-app $v'" }
|
|
$b = Run-Capture (Join-Path $payload 'Igneum Miner.exe') '--version'
|
|
if ($b -notmatch "Igneum Miner $([regex]::Escape($v))") { throw "Igneum Miner.exe --version did not print 'Igneum Miner $v' (version.h and Cargo.toml differ?)" }
|
|
$c = Run-Capture (Join-Path $payload 'Igneum Miner.exe') '--help'
|
|
if ($c -notmatch 'Usage') { throw 'Igneum Miner.exe --help did not print the usage line' }
|
|
$info = (Get-Item (Join-Path $payload 'Igneum Miner.exe')).VersionInfo
|
|
Write-Host ("host version block: {0} {1} {2}" -f $info.ProductName, $info.ProductVersion, $info.FileDescription)
|
|
if ($info.ProductName -ne 'Igneum Miner') { throw 'the host exe carries no Igneum Miner version block (host.rc)' }
|
|
|
|
- name: launcher dry run (proto-cuda/windows-app, DRY_RUN=1, Windows PowerShell 5.1 via the .ps1 and the .bat)
|
|
shell: powershell
|
|
run: |
|
|
$stage = Join-Path $env:RUNNER_TEMP 'launcher'
|
|
New-Item -ItemType Directory -Force -Path $stage | Out-Null
|
|
Copy-Item -Path 'proto-cuda\windows-app\*' -Destination $stage -Recurse -Force
|
|
foreach ($f in @('igneumd.exe', 'igneum-miner.exe', 'igneum-worker-cuda.exe', 'igneum-worker-opencl.exe')) {
|
|
if (Test-Path "build\inputs\$f") { Copy-Item "build\inputs\$f" $stage }
|
|
}
|
|
Get-ChildItem 'build\inputs' -Filter 'nvrtc*.dll' | Copy-Item -Destination $stage
|
|
$env:DRY_RUN = '1'
|
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $stage 'start-igneum.ps1')
|
|
if ($LASTEXITCODE -ne 0) { throw "start-igneum.ps1 DRY_RUN=1 exited $LASTEXITCODE" }
|
|
$bat = cmd /c "cd /d `"$stage`" && START-IGNEUM.bat < nul 2>&1" | Out-String
|
|
Write-Host $bat
|
|
if ($bat -notmatch 'dry run done') { throw 'START-IGNEUM.bat with DRY_RUN=1 did not reach the end of the plan' }
|
|
|
|
- name: sizes
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
{
|
|
echo "## Windows build $APP_VERSION"
|
|
echo
|
|
echo "| file | bytes |"
|
|
echo "|---|---:|"
|
|
for f in packaging/windows/dist/Igneum-Miner-Setup-*.exe packaging/windows/dist/igneum-windows-app.zip "app/windows/dist/Igneum Miner.exe" app/igneum-app/target/release/igneum-app.exe; do
|
|
printf '| %s | %s |\n' "$(basename "$f")" "$(stat -c %s "$f")"
|
|
done
|
|
echo
|
|
echo "inputs (signature, hashes and node commit verified): $(tr -d '\n' < build/payload-inputs.json | head -c 400)"
|
|
echo
|
|
echo "verified: $(cat build/inputs-artifact/inputs-verified.json)"
|
|
} | tee -a "$GITHUB_STEP_SUMMARY"
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: igneum-windows-installer
|
|
path: packaging/windows/dist/Igneum-Miner-Setup-*.exe
|
|
retention-days: 90
|
|
if-no-files-found: error
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: igneum-windows-payload
|
|
path: packaging/windows/dist/igneum-windows-app.zip
|
|
retention-days: 90
|
|
if-no-files-found: error
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: igneum-windows-host
|
|
path: app/windows/dist/Igneum Miner.exe
|
|
retention-days: 90
|
|
if-no-files-found: error
|
|
- uses: actions/upload-artifact@v4
|
|
with:
|
|
name: igneum-windows-inputs
|
|
path: build/inputs-artifact/
|
|
retention-days: 90
|
|
if-no-files-found: error
|