proto-cuda/windows-node/: START-NODE.bat and start-node.ps1 (igneumd --devnet with --addpeer to the Mac, status line every 30 s through igneum-miner watch, keep-awake, random-delay restart, Ctrl+C), BUILD-NODE.bat and build-node.ps1 (builds on the PC from the src.zip snapshot; winget for Rustup, LLVM and protoc; MSVC default toolset), ALLOW-FIREWALL.bat and allow-firewall.ps1, README.txt, cross-build.sh (the mingw-w64 recipe that built igneumd.exe in 8 min 25 s; the exe ships with the three mingw runtime DLLs) and make-package.sh. WINDOWS-MINER.md gains "Run your own node"; bench-log records the cross-compile and the two-peer sync test on the Mac (ports 27000 and 27010, live node untouched). The mining launcher's NODE_HOST=auto edit stays uncommitted for the agent that owns start-mining.ps1. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
212 lines
12 KiB
PowerShell
212 lines
12 KiB
PowerShell
# Builds igneumd.exe (the Igneum node) on this PC from the source snapshot in src.zip. Started by BUILD-NODE.bat.
|
|
# 3 October 2026.
|
|
#
|
|
# Steps:
|
|
# 1. Unpacks src.zip into src\ (src\vendor\igneum-node is the node fork, src\igneum-pow the lottery-hash crate the fork
|
|
# depends on by relative path; the layout must stay as it is). Nothing is cloned: the snapshot is a `git archive`
|
|
# of the fork's HEAD made on the Mac, so no GitHub access is needed and the repository stays private.
|
|
# 2. Makes sure the tools are present, installing the missing ones with winget (silent):
|
|
# Rust Rustlang.Rustup (stable, MSVC host). cargo lands in %USERPROFILE%\.cargo\bin.
|
|
# LLVM LLVM.LLVM, for libclang.dll: the rocksdb crate generates its bindings with bindgen (LIBCLANG_PATH).
|
|
# protoc Google.Protobuf, or a direct download of the protoc release zip: the gRPC and p2p crates run protoc
|
|
# from their build scripts (prost-build, no vendored protoc).
|
|
# MSVC the toolset of the installed Visual Studio, through vcvarsall x64 (the default 14.5x is fine for
|
|
# Rust and for the C and C++ crates: rocksdb, zstd, lz4, bzip2, zlib, secp256k1, mimalloc). The CUDA
|
|
# 14.30 trick of the mining launcher is not used here.
|
|
# 3. Runs `cargo build --release -p kaspad --features igneum-pow` in src\vendor\igneum-node (the package keeps its
|
|
# upstream name kaspad; the binary is igneumd.exe; the feature selects the real lottery-hash engine, without it the
|
|
# node would check blocks with the kHeavyHash stub and reject every devnet block). A progress line prints every
|
|
# minute: elapsed time, crates compiled, the crate being compiled.
|
|
# 4. Copies target\release\igneumd.exe next to BUILD-NODE.bat (and igneum-miner.exe if it was missing).
|
|
#
|
|
# Time: approximate 15 to 25 minutes on a Ryzen 7 9800X3D for the first build (the rocksdb C++ sources and about 500
|
|
# Rust crates; thin LTO at the end), under a minute for a rebuild with nothing changed. Windows Defender's real-time
|
|
# scan of target\ can add a few minutes. Extract the zip to a short path (C:\igneum-node) so no build path reaches the
|
|
# 260-character limit.
|
|
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
$ErrorActionPreference = 'Continue'
|
|
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
Set-Location $root
|
|
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
$buildLog = Join-Path $root "build-$stamp.log"
|
|
$started = Get-Date
|
|
$vcvars = if ($env:VCVARS) { $env:VCVARS } else { 'C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat' }
|
|
|
|
function Log([string]$msg) {
|
|
$line = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') $msg"
|
|
Write-Host $line
|
|
Add-Content -Path $buildLog -Value $line
|
|
}
|
|
|
|
function Refresh-Path {
|
|
$m = [Environment]::GetEnvironmentVariable('PATH', 'Machine')
|
|
$u = [Environment]::GetEnvironmentVariable('PATH', 'User')
|
|
$env:PATH = "$env:PATH;$m;$u"
|
|
}
|
|
|
|
function Invoke-Winget([string]$id) {
|
|
if (-not (Get-Command winget.exe -ErrorAction SilentlyContinue)) { Log "winget is not available, cannot install $id by itself"; return }
|
|
Log "installing $id with winget (silent)"
|
|
$out = & winget.exe install --id $id -e --silent --accept-package-agreements --accept-source-agreements 2>&1
|
|
$out | ForEach-Object { Add-Content -Path $buildLog -Value " winget: $_" }
|
|
Refresh-Path
|
|
}
|
|
|
|
Log "igneum node build on $env:COMPUTERNAME (run $stamp), folder $root"
|
|
if ($root.Length -gt 80) { Log "WARNING: this folder path is $($root.Length) characters long; cargo's build paths may pass the 260-character limit. C:\igneum-node is a safer place." }
|
|
|
|
# ---- 1. the source -----------------------------------------------------------------------------------------------------
|
|
|
|
$src = Join-Path $root 'src'
|
|
$nodeDir = Join-Path $src 'vendor\igneum-node'
|
|
$srcZip = Join-Path $root 'src.zip'
|
|
if (-not (Test-Path (Join-Path $nodeDir 'Cargo.toml'))) {
|
|
if (-not (Test-Path $srcZip)) { Log 'neither src\vendor\igneum-node nor src.zip is here. Extract the whole package again.'; exit 1 }
|
|
Log "unpacking src.zip into $src"
|
|
try { Expand-Archive -Path $srcZip -DestinationPath $src -Force } catch { Log "could not unpack src.zip: $_"; exit 1 }
|
|
}
|
|
if (-not (Test-Path (Join-Path $src 'igneum-pow\Cargo.toml'))) { Log 'src\igneum-pow is missing (the fork depends on it by relative path); the snapshot is incomplete.'; exit 1 }
|
|
$snapshotNote = Join-Path $src 'SNAPSHOT.txt'
|
|
if (Test-Path $snapshotNote) { Get-Content $snapshotNote | ForEach-Object { Log "source: $_" } }
|
|
|
|
# ---- 2. tools -------------------------------------------------------------------------------------------------------------
|
|
|
|
# Rust
|
|
$cargoBin = Join-Path $env:USERPROFILE '.cargo\bin'
|
|
if (Test-Path $cargoBin) { $env:PATH = "$cargoBin;$env:PATH" }
|
|
if (-not (Get-Command cargo.exe -ErrorAction SilentlyContinue)) {
|
|
Invoke-Winget 'Rustlang.Rustup'
|
|
if (Test-Path $cargoBin) { $env:PATH = "$cargoBin;$env:PATH" }
|
|
}
|
|
if (-not (Get-Command cargo.exe -ErrorAction SilentlyContinue)) { Log 'cargo is still not found. Install Rust from https://rustup.rs (default options) and run BUILD-NODE.bat again.'; exit 1 }
|
|
$toolchains = @(& rustup.exe toolchain list 2>&1)
|
|
if (-not ($toolchains | Where-Object { $_ -match '^stable' })) {
|
|
Log 'no stable toolchain yet: rustup toolchain install stable'
|
|
& rustup.exe toolchain install stable --profile minimal 2>&1 | ForEach-Object { Add-Content -Path $buildLog -Value " rustup: $_" }
|
|
& rustup.exe default stable 2>&1 | ForEach-Object { Add-Content -Path $buildLog -Value " rustup: $_" }
|
|
}
|
|
$active = (& rustup.exe show active-toolchain 2>&1 | Select-Object -First 1)
|
|
if ($active -notmatch 'msvc') { Log "the active toolchain is '$active'; the MSVC host toolchain is the tested one (rustup default stable-msvc)" }
|
|
Log "rust: $(& rustc.exe --version 2>&1) / $(& cargo.exe --version 2>&1) ($active)"
|
|
|
|
# LLVM (libclang for bindgen)
|
|
$libclang = $null
|
|
foreach ($d in @($env:LIBCLANG_PATH, 'C:\Program Files\LLVM\bin', 'C:\Program Files\LLVM\lib')) {
|
|
if ($d -and (Test-Path (Join-Path $d 'libclang.dll'))) { $libclang = $d; break }
|
|
}
|
|
if (-not $libclang) {
|
|
Invoke-Winget 'LLVM.LLVM'
|
|
foreach ($d in @('C:\Program Files\LLVM\bin', 'C:\Program Files\LLVM\lib')) { if (Test-Path (Join-Path $d 'libclang.dll')) { $libclang = $d; break } }
|
|
}
|
|
if (-not $libclang) { Log 'libclang.dll not found (LLVM). Install LLVM from https://releases.llvm.org (the Windows installer) and run BUILD-NODE.bat again.'; exit 1 }
|
|
$env:LIBCLANG_PATH = $libclang
|
|
Log "libclang: $libclang"
|
|
|
|
# protoc
|
|
$protoc = $null
|
|
$cmd = Get-Command protoc.exe -ErrorAction SilentlyContinue
|
|
if ($cmd) { $protoc = $cmd.Source }
|
|
$localProtoc = Join-Path $root 'tools\protoc\bin\protoc.exe'
|
|
if (-not $protoc -and (Test-Path $localProtoc)) { $protoc = $localProtoc }
|
|
if (-not $protoc) {
|
|
Invoke-Winget 'Google.Protobuf'
|
|
$cmd = Get-Command protoc.exe -ErrorAction SilentlyContinue
|
|
if ($cmd) { $protoc = $cmd.Source }
|
|
}
|
|
if (-not $protoc) {
|
|
# Fallback: the release zip from the protobuf project (public), unpacked under tools\protoc.
|
|
$ver = '33.1'
|
|
$url = "https://github.com/protocolbuffers/protobuf/releases/download/v$ver/protoc-$ver-win64.zip"
|
|
$zip = Join-Path $root 'tools\protoc.zip'
|
|
Log "protoc not found: downloading $url"
|
|
try {
|
|
New-Item -ItemType Directory -Force -Path (Join-Path $root 'tools') | Out-Null
|
|
Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
|
|
Expand-Archive -Path $zip -DestinationPath (Join-Path $root 'tools\protoc') -Force
|
|
} catch { Log "download failed: $_" }
|
|
if (Test-Path $localProtoc) { $protoc = $localProtoc }
|
|
}
|
|
if (-not $protoc) { Log 'protoc is still missing. Install it (winget install Google.Protobuf, or the protoc-*-win64.zip from the protobuf releases page, unpacked to tools\protoc) and run BUILD-NODE.bat again.'; exit 1 }
|
|
$env:PROTOC = $protoc
|
|
Log "protoc: $protoc ($(& $protoc --version 2>&1))"
|
|
|
|
# MSVC: import the toolset environment (default version). rustc and the cc crate find MSVC through vswhere by themselves,
|
|
# so a missing vcvarsall is a warning, not a stop.
|
|
function Find-VcVarsAll {
|
|
if ($vcvars -and (Test-Path $vcvars)) { return $vcvars }
|
|
foreach ($pf in @('C:\Program Files', 'C:\Program Files (x86)')) {
|
|
$hits = Get-ChildItem -Path (Join-Path $pf 'Microsoft Visual Studio') -Recurse -Filter 'vcvarsall.bat' -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FullName -match '\\VC\\Auxiliary\\Build\\vcvarsall\.bat$' } | Sort-Object FullName -Descending
|
|
if ($hits) { return $hits[0].FullName }
|
|
}
|
|
return $null
|
|
}
|
|
$bat = Find-VcVarsAll
|
|
if ($bat) {
|
|
Log "importing the MSVC environment: `"$bat`" x64"
|
|
$out = cmd /s /c "`"$bat`" x64 >nul 2>&1 && set" 2>$null
|
|
foreach ($l in @($out)) {
|
|
$i = $l.IndexOf('=')
|
|
if ($i -gt 0) { [Environment]::SetEnvironmentVariable($l.Substring(0, $i), $l.Substring($i + 1), 'Process') }
|
|
}
|
|
}
|
|
$cl = Get-Command cl.exe -ErrorAction SilentlyContinue
|
|
if ($cl) { Log "MSVC: $($cl.Source)" } else { Log 'cl.exe is not on PATH (no vcvarsall found); cargo will look for MSVC by itself. If the build stops with "linker link.exe not found", install the "Desktop development with C++" workload in the Visual Studio Installer.' }
|
|
|
|
# ---- 3. the build ------------------------------------------------------------------------------------------------------
|
|
|
|
$needMiner = -not (Test-Path (Join-Path $root 'igneum-miner.exe'))
|
|
$cargoArgs = @('build', '--release', '-p', 'kaspad', '--features', 'igneum-pow')
|
|
if ($needMiner) { $cargoArgs += @('-p', 'igneum-miner') }
|
|
$cargoOut = Join-Path $root "build-$stamp.cargo.log"
|
|
Log "cargo $($cargoArgs -join ' ') in $nodeDir (cargo's own output: $cargoOut)"
|
|
Log 'this takes about 15 to 25 minutes the first time; a line prints every minute'
|
|
$t0 = Get-Date
|
|
$proc = Start-Process -FilePath (Get-Command cargo.exe).Source -ArgumentList $cargoArgs -WorkingDirectory $nodeDir -RedirectStandardOutput ($cargoOut + '.out') -RedirectStandardError $cargoOut -NoNewWindow -PassThru
|
|
if ($proc) { $null = $proc.Handle }
|
|
|
|
function Get-CargoProgress {
|
|
# cargo prints " Compiling name vX.Y.Z" lines to stderr; count them and show the last one.
|
|
try {
|
|
$fs = New-Object IO.FileStream($cargoOut, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite)
|
|
try { $sr = New-Object IO.StreamReader($fs); $text = $sr.ReadToEnd(); $sr.Close() } finally { $fs.Close() }
|
|
} catch { return 'waiting for cargo' }
|
|
$lines = @($text -split "`n" | ForEach-Object { $_.TrimEnd("`r") })
|
|
$compiling = @($lines | Where-Object { $_ -match '^\s*Compiling ' })
|
|
$last = if ($compiling.Count -gt 0) { ($compiling[-1] -replace '^\s*Compiling ', '') -replace ' \(.*\)$', '' } else { 'resolving and downloading crates' }
|
|
$errors = @($lines | Where-Object { $_ -match '^error' }).Count
|
|
$txt = "$($compiling.Count) crates compiled, now $last"
|
|
if ($errors -gt 0) { $txt += ", $errors error line(s) so far" }
|
|
return $txt
|
|
}
|
|
|
|
$lastLine = Get-Date
|
|
while (-not $proc.HasExited) {
|
|
Start-Sleep -Seconds 5
|
|
if (((Get-Date) - $lastLine).TotalSeconds -ge 60) {
|
|
$lastLine = Get-Date
|
|
Log (" {0:N0} min: {1}" -f ((Get-Date) - $t0).TotalMinutes, (Get-CargoProgress))
|
|
}
|
|
}
|
|
$took = (Get-Date) - $t0
|
|
$rc = $proc.ExitCode
|
|
Log ("cargo finished with exit code {0} after {1} ({2})" -f $rc, $took.ToString('hh\:mm\:ss'), (Get-CargoProgress))
|
|
|
|
# ---- 4. the result ----------------------------------------------------------------------------------------------------------
|
|
|
|
$built = Join-Path $nodeDir 'target\release\igneumd.exe'
|
|
if ($rc -ne 0 -or -not (Test-Path $built)) {
|
|
Log "the build FAILED. The last error lines from $cargoOut :"
|
|
try { Get-Content $cargoOut -ErrorAction SilentlyContinue | Where-Object { $_ -match '^(error|warning: unused| = note: )|LNK|link\.exe|protoc|libclang|bindgen' } | Select-Object -Last 15 | ForEach-Object { Log " $_" } } catch { }
|
|
Log 'Common causes: no MSVC (install the Desktop development with C++ workload), protoc or libclang not found (see the lines above), a path over 260 characters (move the folder to C:\igneum-node), Defender holding a file (run it again).'
|
|
exit 1
|
|
}
|
|
Copy-Item $built (Join-Path $root 'igneumd.exe') -Force
|
|
$size = (Get-Item (Join-Path $root 'igneumd.exe')).Length
|
|
Log ("igneumd.exe copied next to BUILD-NODE.bat ({0:N1} MB)" -f ($size / 1MB))
|
|
if ($needMiner) {
|
|
$m = Join-Path $nodeDir 'target\release\igneum-miner.exe'
|
|
if (Test-Path $m) { Copy-Item $m (Join-Path $root 'igneum-miner.exe') -Force; Log 'igneum-miner.exe copied too (the status reader of START-NODE.bat)' }
|
|
}
|
|
Log ("done in {0}. Now run START-NODE.bat." -f ((Get-Date) - $started).ToString('hh\:mm\:ss'))
|