Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1861a0132a | |||
| 5df7f79a2e | |||
| a72e5b7089 | |||
| 57711318a7 | |||
| 799443b248 | |||
| e92ac468a6 | |||
| 6d08c83f04 | |||
| 523f6794d5 | |||
| 1dce5dd2db | |||
| 95a6faa538 | |||
| 83258c713e | |||
| 3ff48d589f | |||
| 5af66b8cf4 | |||
| 2407970566 | |||
| 241fc68495 | |||
| 64b9d37472 | |||
| bb6e53f3f6 | |||
| 247db74393 | |||
| 4309fadf21 | |||
| 9bc2f7be11 | |||
| 6205018dd1 | |||
| ef984d86db | |||
| 3218ed9200 | |||
| bd8183d642 | |||
| 5c3f4c17fc |
@@ -0,0 +1,36 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ARG TRIANGLES_VERSION=v5.7.6
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git build-essential libtool autotools-dev automake pkg-config \
|
||||
libssl-dev libevent-dev bsdmainutils libboost-all-dev \
|
||||
libminiupnpc-dev libzmq3-dev tor curl wget ca-certificates && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build BerkeleyDB 4.8
|
||||
RUN cd /tmp && \
|
||||
curl -sL -o db-4.8.30.NC.tar.gz http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz && \
|
||||
tar xzf db-4.8.30.NC.tar.gz && \
|
||||
cd db-4.8.30.NC/build_unix && \
|
||||
curl -sL -o ../dist/config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD' && \
|
||||
curl -sL -o ../dist/config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD' && \
|
||||
chmod +x ../dist/config.guess ../dist/config.sub && \
|
||||
../dist/configure --enable-cxx --disable-shared --with-pic --prefix=/usr/local && \
|
||||
make -j$(nproc) && \
|
||||
make install && \
|
||||
ldconfig && \
|
||||
cd / && rm -rf /tmp/db-4.8.30.NC*
|
||||
|
||||
# Build triangles
|
||||
RUN cd /tmp && \
|
||||
git clone -b ${TRIANGLES_VERSION} https://github.com/SamiAhmed7777/triangles_v5.git && \
|
||||
cd triangles_v5/src && \
|
||||
make -f makefile.unix -j$(nproc) USE_UPNP=1 && \
|
||||
strip trianglesd && \
|
||||
mkdir -p /usr/local/bin && \
|
||||
cp trianglesd /usr/local/bin/ && \
|
||||
chmod +x /usr/local/bin/trianglesd && \
|
||||
cd / && rm -rf /tmp/triangles_v5
|
||||
|
||||
CMD ["/bin/bash"]
|
||||
@@ -0,0 +1,53 @@
|
||||
# TRI-PI GitHub Actions
|
||||
|
||||
## Automated Build Pipeline
|
||||
|
||||
This repository uses GitHub Actions to automatically rebuild TRI-PI whenever a new version of triangles_v5 is released.
|
||||
|
||||
### Workflows
|
||||
|
||||
#### `build-arm64.yml` - Automatic ARM64 Builder
|
||||
|
||||
**Triggers:**
|
||||
1. **Manual dispatch** - Run builds on demand via GitHub UI
|
||||
2. **Scheduled checks** - Every 6 hours, checks for new triangles_v5 releases
|
||||
3. **Auto-build** - When a new version is detected, builds automatically
|
||||
|
||||
**What it does:**
|
||||
1. Checks for new triangles_v5 releases
|
||||
2. Builds trianglesd from source in ARM64 Docker container
|
||||
3. Creates release package (tar.gz)
|
||||
4. Updates VERSION file and documentation
|
||||
5. Commits and pushes to main branch
|
||||
6. Creates GitHub Release with binaries
|
||||
|
||||
**Build environment:**
|
||||
- Ubuntu 24.04 ARM64 (via QEMU emulation)
|
||||
- BerkeleyDB 4.8 (built from source)
|
||||
- Boost 1.83, OpenSSL 3.0
|
||||
- Full Tor integration
|
||||
|
||||
### Manual Trigger
|
||||
|
||||
To manually build a specific version:
|
||||
|
||||
1. Go to **Actions** tab
|
||||
2. Select **Build TRI-PI ARM64** workflow
|
||||
3. Click **Run workflow**
|
||||
4. Optionally enter a version tag (for example `v5.8.1`)
|
||||
5. Click **Run workflow**
|
||||
|
||||
If no version is supplied, the workflow builds the latest published `triangles_v5` release.
|
||||
|
||||
### Automatic Updates
|
||||
|
||||
The workflow runs every 6 hours to check for new releases. When detected:
|
||||
- Builds automatically
|
||||
- Creates release
|
||||
- Updates repo
|
||||
|
||||
No manual intervention needed!
|
||||
|
||||
### Version Tracking
|
||||
|
||||
Current version is stored in `VERSION` at repo root and updated by the ARM64 build workflow to match the upstream `triangles_v5` tag it actually built.
|
||||
@@ -0,0 +1,180 @@
|
||||
name: Build TRI-PI ARM64
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [new-release]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "triangles_v5 tag to build (example: v5.8.1). Leave empty to use latest upstream release."
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-arm64:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout tri-pi repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Resolve target triangles version
|
||||
id: get-version
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
VERSION=""
|
||||
|
||||
if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
|
||||
VERSION="${{ github.event.client_payload.version }}"
|
||||
elif [ -n "${{ github.event.inputs.version || '' }}" ]; then
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION=$(curl -fsSL https://api.github.com/repos/SamiAhmed7777/triangles_v5/releases/latest | jq -r .tag_name)
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then
|
||||
echo "Failed to resolve triangles_v5 release version" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "🔍 Building triangles $VERSION for ARM64"
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build triangles_v5 ARM64
|
||||
run: |
|
||||
VERSION="${{ steps.get-version.outputs.version }}"
|
||||
echo "Building triangles $VERSION for ARM64..."
|
||||
|
||||
docker run --rm --platform linux/arm64 \
|
||||
-e VERSION="$VERSION" \
|
||||
-v $PWD:/workspace \
|
||||
-w /workspace \
|
||||
ubuntu:24.04 bash -c '
|
||||
set -e
|
||||
|
||||
echo "Building triangles $VERSION for ARM64..."
|
||||
|
||||
# Install dependencies
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq \
|
||||
git build-essential libtool autotools-dev automake pkg-config \
|
||||
libssl-dev libevent-dev bsdmainutils libboost-all-dev \
|
||||
libminiupnpc-dev libzmq3-dev tor curl wget ca-certificates jq
|
||||
|
||||
# Build BerkeleyDB 4.8
|
||||
cd /tmp
|
||||
curl -sL -o db-4.8.30.NC.tar.gz http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz
|
||||
tar xzf db-4.8.30.NC.tar.gz
|
||||
cd db-4.8.30.NC/build_unix
|
||||
|
||||
# Update config scripts for ARM64
|
||||
curl -sL -o ../dist/config.guess "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD"
|
||||
curl -sL -o ../dist/config.sub "http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD"
|
||||
chmod +x ../dist/config.guess ../dist/config.sub
|
||||
|
||||
../dist/configure --enable-cxx --disable-shared --with-pic --prefix=/usr/local
|
||||
make -j$(nproc)
|
||||
make install
|
||||
ldconfig
|
||||
|
||||
# Clone and build triangles
|
||||
cd /tmp
|
||||
git clone --branch $VERSION --depth 1 https://github.com/SamiAhmed7777/triangles_v5.git
|
||||
cd triangles_v5/src
|
||||
|
||||
make -f makefile.unix clean 2>/dev/null || true
|
||||
make -f makefile.unix -j$(nproc) USE_UPNP=1
|
||||
strip trianglesd
|
||||
|
||||
# Copy to workspace
|
||||
mkdir -p /workspace/bin
|
||||
cp trianglesd /workspace/bin/
|
||||
chmod +x /workspace/bin/trianglesd
|
||||
|
||||
echo "Build complete!"
|
||||
file /workspace/bin/trianglesd
|
||||
/workspace/bin/trianglesd --version || true
|
||||
'
|
||||
|
||||
- name: Create release package
|
||||
run: |
|
||||
VERSION="${{ steps.get-version.outputs.version }}"
|
||||
PACKAGE_DIR="tri-pi-${VERSION}-arm64"
|
||||
|
||||
mkdir -p releases
|
||||
tar czf "releases/${PACKAGE_DIR}.tar.gz" \
|
||||
bin/ docs/ install.sh README.md VERSION BOOTSTRAP.md
|
||||
|
||||
cd releases
|
||||
sha256sum "${PACKAGE_DIR}.tar.gz" > "${PACKAGE_DIR}.tar.gz.sha256"
|
||||
|
||||
echo "📦 Package created:"
|
||||
ls -lh "${PACKAGE_DIR}.tar.gz"*
|
||||
|
||||
- name: Update repo version markers
|
||||
run: |
|
||||
VERSION="${{ steps.get-version.outputs.version }}"
|
||||
printf '%s\n' "$VERSION" > VERSION
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
VERSION="${{ steps.get-version.outputs.version }}"
|
||||
|
||||
git config user.name "TRI-PI Builder"
|
||||
git config user.email "bot@tri-pi.build"
|
||||
|
||||
git add VERSION bin/trianglesd releases/
|
||||
git commit -m "Auto-build: TRI-PI ${VERSION} ARM64 release - Native ARM64 binary, GitHub Actions QEMU" || echo "No changes to commit"
|
||||
|
||||
git push origin main || echo "Already up to date"
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
tag_name: ${{ steps.get-version.outputs.version }}
|
||||
name: TRI-PI ${{ steps.get-version.outputs.version }} ARM64
|
||||
body: |
|
||||
# TRI-PI ${{ steps.get-version.outputs.version }} ARM64
|
||||
|
||||
Native ARM64 release built from [triangles_v5 ${{ steps.get-version.outputs.version }}](https://github.com/SamiAhmed7777/triangles_v5/releases/tag/${{ steps.get-version.outputs.version }})
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
wget https://github.com/SamiAhmed7777/tri-pi/releases/download/${{ steps.get-version.outputs.version }}/tri-pi-${{ steps.get-version.outputs.version }}-arm64.tar.gz
|
||||
tar xzf tri-pi-${{ steps.get-version.outputs.version }}-arm64.tar.gz
|
||||
cd tri-pi-*/
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
## What's Included
|
||||
|
||||
- Native ARM64 trianglesd binary
|
||||
- One-command installer
|
||||
- Complete build documentation
|
||||
- Tor integration
|
||||
- Updated seed nodes
|
||||
|
||||
Built for: Raspberry Pi 4/5 (64-bit), ARM64 servers
|
||||
files: |
|
||||
releases/tri-pi-${{ steps.get-version.outputs.version }}-arm64.tar.gz
|
||||
releases/tri-pi-${{ steps.get-version.outputs.version }}-arm64.tar.gz.sha256
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,68 @@
|
||||
name: Watch Upstream triangles_v5
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Check for new releases every 6 hours
|
||||
- cron: '0 */6 * * *'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check-upstream:
|
||||
name: Check for New Releases
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check for new upstream releases
|
||||
id: check
|
||||
run: |
|
||||
# Get latest release from triangles_v5
|
||||
LATEST=$(curl -s https://api.github.com/repos/SamiAhmed7777/triangles_v5/releases/latest | jq -r .tag_name)
|
||||
|
||||
# Get our latest release
|
||||
CURRENT=$(curl -s https://api.github.com/repos/SamiAhmed7777/tri-pi/releases/latest | jq -r .tag_name)
|
||||
|
||||
echo "Upstream latest: $LATEST"
|
||||
echo "Our latest: $CURRENT"
|
||||
|
||||
if [ "$LATEST" != "$CURRENT" ] && [ "$LATEST" != "null" ]; then
|
||||
echo "New version detected: $LATEST"
|
||||
echo "new_version=$LATEST" >> $GITHUB_OUTPUT
|
||||
echo "needs_build=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "No new version"
|
||||
echo "needs_build=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Trigger ARM64 build
|
||||
if: steps.check.outputs.needs_build == 'true'
|
||||
uses: peter-evans/repository-dispatch@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repository: ${{ github.repository }}
|
||||
event-type: new-release
|
||||
client-payload: '{"version": "${{ steps.check.outputs.new_version }}"}'
|
||||
|
||||
- name: Create issue if build triggered
|
||||
if: steps.check.outputs.needs_build == 'true'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: `New upstream release detected: ${{ steps.check.outputs.new_version }}`,
|
||||
body: `Automated build triggered for triangles_v5 ${{ steps.check.outputs.new_version }}
|
||||
|
||||
Check the [Actions tab](https://github.com/${{ github.repository }}/actions) to monitor build progress.
|
||||
|
||||
Once complete, the new release will be available at:
|
||||
https://github.com/${{ github.repository }}/releases
|
||||
|
||||
---
|
||||
*This issue was created automatically by the upstream watcher.*`,
|
||||
labels: ['automated-build']
|
||||
})
|
||||
+21
@@ -1 +1,22 @@
|
||||
# Generated build artifacts
|
||||
build/.qemu-work/
|
||||
build/.debug-mount/
|
||||
build/output/
|
||||
build/release/
|
||||
build/triangles-src/
|
||||
|
||||
# Generated logs and reports
|
||||
build/*.log
|
||||
build/ARM64-BUILD-SUCCESS.md
|
||||
build/RELEASE_NOTES.md
|
||||
|
||||
# Local helper scripts generated during experiments
|
||||
build/compile-arm64-final.sh
|
||||
build/compile-arm64-growpart.sh
|
||||
build/compile-arm64-ultra-clean.sh
|
||||
build/cross-compile-arm64.sh
|
||||
build/cross-compile-static.sh
|
||||
build/test-daemon-network.sh
|
||||
build/test-daemon-runtime.sh
|
||||
build/test-daemon-simple.sh
|
||||
build/test-daemon-sync.sh
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
## Blockchain Bootstrap
|
||||
|
||||
Fast-sync your Triangles node with a pre-synced blockchain snapshot instead of syncing from genesis (which takes days/weeks on a Pi).
|
||||
|
||||
### During Installation
|
||||
|
||||
Both `install.sh` and `bootstrap.sh` offer automatic bootstrap download. Choose option 1 when prompted.
|
||||
|
||||
### Manual Bootstrap
|
||||
|
||||
If the automatic download failed (e.g., server unreachable from your network), transfer the snapshot manually:
|
||||
|
||||
```bash
|
||||
# On a machine that can reach the bootstrap server:
|
||||
curl -O http://194.233.88.206:8085/triangles-bootstrap.tar.gz
|
||||
|
||||
# Transfer to your Pi:
|
||||
scp triangles-bootstrap.tar.gz pi@your-pi:/tmp/
|
||||
|
||||
# On the Pi:
|
||||
sudo systemctl stop triangles
|
||||
cd /root/.triangles
|
||||
tar xzf /tmp/triangles-bootstrap.tar.gz
|
||||
sudo systemctl start triangles
|
||||
```
|
||||
|
||||
### What's in the Snapshot
|
||||
|
||||
**Included:**
|
||||
- `blk*.dat` — Blockchain data
|
||||
- `txleveldb/` — Transaction/block index
|
||||
- `database/` — BerkeleyDB environment
|
||||
- `peers.dat` — Known peer cache
|
||||
|
||||
**Not included (generated fresh):**
|
||||
- `wallet.dat` — Created on first run
|
||||
- `onion/` — Tor hidden service keys
|
||||
- `tor_data/` — Tor state
|
||||
- `debug.log` — Runtime log
|
||||
|
||||
### After Bootstrap
|
||||
|
||||
Your node will:
|
||||
1. Load the snapshot blockchain
|
||||
2. Fast-import and verify blocks
|
||||
3. Sync remaining blocks from peers
|
||||
4. Generate a fresh wallet and Tor identity
|
||||
|
||||
Watch progress:
|
||||
```bash
|
||||
tail -f /root/.triangles/debug.log # See FastImport progress
|
||||
trianglesd -datadir=/root/.triangles getinfo # Check block height
|
||||
```
|
||||
|
||||
### Snapshot Updates
|
||||
|
||||
The bootstrap server updates automatically every Sunday at 4:00 AM UTC. The snapshot is always within a week of current.
|
||||
|
||||
### Security
|
||||
|
||||
- Wallet is **never** included — always generated locally
|
||||
- Blockchain data is verified against network consensus during import
|
||||
- Use only the official bootstrap URL or transfer from a trusted source
|
||||
@@ -1,75 +0,0 @@
|
||||
# tri-pi Project Notes
|
||||
|
||||
`tri-pi` is the appliance wrapper around a separate `triangles` source checkout.
|
||||
|
||||
This repo should own:
|
||||
|
||||
- Raspberry Pi provisioning
|
||||
- Tor hidden service setup
|
||||
- local status UI and API
|
||||
- systemd integration
|
||||
- cross-build and QEMU validation glue
|
||||
|
||||
This repo should not own:
|
||||
|
||||
- Triangles protocol code
|
||||
- wallet UI code
|
||||
- consensus changes
|
||||
|
||||
## Repository Boundary
|
||||
|
||||
Recommended local layout:
|
||||
|
||||
```text
|
||||
e:/repos/
|
||||
├── triangles/
|
||||
└── tri-pi/
|
||||
```
|
||||
|
||||
Use `triangles` for building `trianglesd` itself.
|
||||
Use `tri-pi` for packaging and operating that daemon on Raspberry Pi hardware.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
- `backend/` — Python status API server (real RPC to `trianglesd`, systemctl health, Tor hostname)
|
||||
- `frontend/` — Vanilla JS dashboard (auto-refresh, responsive grid)
|
||||
- `build/` — ARM cross-build (`build-arm.sh`) and QEMU smoke test (`qemu-smoke.sh`)
|
||||
- `setup/` — Pi installer (`install.sh`), Tor setup (`configure-tor.sh`), config generator
|
||||
- `services/` — systemd units for `trianglesd` and `tripi-backend`
|
||||
- `tor/` — torrc template for v3 hidden service
|
||||
- `config/` — `triangles.conf` template and backend env example
|
||||
- `docs/` — architecture and development notes
|
||||
|
||||
## Architectural Direction
|
||||
|
||||
The backend/frontend split is inspired by `velxio`, but the product scope is much smaller:
|
||||
|
||||
- no browser IDE
|
||||
- no board simulator
|
||||
- no multi-user platform features
|
||||
- yes to reproducible local runtime orchestration
|
||||
- yes to QEMU-backed validation of the Pi appliance
|
||||
|
||||
## Install & Deploy Flow
|
||||
|
||||
1. Cross-compile: `./build/build-arm.sh` (needs `../triangles` source checkout)
|
||||
2. Install on Pi: `TRIANGLESD_BIN=build/output/trianglesd sudo ./setup/install.sh`
|
||||
3. Validate: `sudo ./build/qemu-smoke.sh` (uses systemd-nspawn + qemu-user-static)
|
||||
|
||||
## Key Paths (on Pi)
|
||||
|
||||
| What | Path |
|
||||
|---------------------|---------------------------------------|
|
||||
| App prefix | `/opt/tri-pi/` |
|
||||
| Daemon config | `/etc/triangles/triangles.conf` |
|
||||
| Daemon data | `/var/lib/triangles/` |
|
||||
| Backend env | `/etc/default/tripi-backend` |
|
||||
| Tor hidden service | `/var/lib/tor/tri-pi/` |
|
||||
| Tor hostname file | `/var/lib/tor/tri-pi/hostname` |
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Run QEMU smoke test on a real ARM64 host to validate end-to-end.
|
||||
2. Decide whether releases ship prebuilt ARM binaries or build on device.
|
||||
3. Add first-boot provisioning hooks in `image/` for SD card images.
|
||||
4. Consider adding a lightweight health-check endpoint the dashboard can use for uptime monitoring.
|
||||
@@ -1,89 +1,138 @@
|
||||
# tri-pi
|
||||
# TRI-PI — Triangles Node for ARM64
|
||||
|
||||
`tri-pi` is a Raspberry Pi appliance for running a Triangles (TRI) relay node over Tor.
|
||||
[](https://github.com/SamiAhmed7777/tri-pi/releases/latest)
|
||||
[](https://github.com/SamiAhmed7777/tri-pi/actions)
|
||||
[](https://github.com/SamiAhmed7777/tri-pi)
|
||||
|
||||
Each Pi acts as a full network node, strengthening the Triangles network by:
|
||||
- Relaying transactions across the network
|
||||
- Relaying blocks to help other nodes sync
|
||||
- Accepting incoming connections via Tor hidden service
|
||||
- Operating entirely over Tor for privacy and decentralization
|
||||
|
||||
It is a separate project from the Triangles source tree. The daemon itself lives in a sibling checkout such as `../triangles`, while `tri-pi` owns:
|
||||
|
||||
- Pi provisioning
|
||||
- Tor hidden service setup
|
||||
- systemd units
|
||||
- health/status APIs
|
||||
- a lightweight local dashboard
|
||||
- cross-build and QEMU-based test helpers
|
||||
|
||||
## Repository Layout
|
||||
|
||||
```text
|
||||
tri-pi/
|
||||
├── backend/ # Local API and status collector
|
||||
├── build/ # Cross-build and QEMU helper scripts
|
||||
├── config/ # triangles.conf and backend env templates
|
||||
├── docs/ # Architecture and workflow notes
|
||||
├── frontend/ # Local status dashboard
|
||||
├── image/ # Pi image customization placeholders
|
||||
├── services/ # systemd unit files
|
||||
├── setup/ # Provisioning scripts run on the Pi
|
||||
└── tor/ # torrc templates and Tor helper material
|
||||
```
|
||||
|
||||
## Relationship To `triangles`
|
||||
|
||||
`tri-pi` does not vendor or modify the daemon source by default. It expects a separate checkout:
|
||||
|
||||
```text
|
||||
e:/repos/
|
||||
├── triangles/
|
||||
└── tri-pi/
|
||||
```
|
||||
|
||||
That keeps the concerns clean:
|
||||
|
||||
- `triangles`: wallet/node source, native builds, protocol changes
|
||||
- `tri-pi`: appliance packaging, deployment, monitoring, Tor, Pi UX
|
||||
|
||||
## Current Scope
|
||||
|
||||
This scaffold includes:
|
||||
|
||||
- a minimal Python backend that exposes node status
|
||||
- a static dashboard that polls the backend
|
||||
- Pi install/build script placeholders
|
||||
- systemd unit templates
|
||||
- Tor and daemon config templates
|
||||
|
||||
It does not yet include:
|
||||
|
||||
- a production-ready installer
|
||||
- real RPC authentication management
|
||||
- image generation
|
||||
- CI workflows
|
||||
- automatic wallet unlock
|
||||
Run a [Triangles (TRI)](https://github.com/SamiAhmed7777/triangles_v5) cryptocurrency node on Raspberry Pi 4/5 or any ARM64 server. Includes Tor integration, systemd service, and optional blockchain bootstrap.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Run the local backend:
|
||||
### One-liner install
|
||||
|
||||
```bash
|
||||
python backend/app.py
|
||||
curl -sSL https://raw.githubusercontent.com/SamiAhmed7777/tri-pi/main/bootstrap.sh | sudo bash
|
||||
```
|
||||
|
||||
Then open:
|
||||
Downloads the latest release, installs dependencies, creates a systemd service, and offers blockchain bootstrap. Ready in ~60 seconds.
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8080/
|
||||
### Manual install
|
||||
|
||||
```bash
|
||||
# Download latest release
|
||||
wget https://github.com/SamiAhmed7777/tri-pi/releases/latest/download/tri-pi-$(curl -fsSL https://api.github.com/repos/SamiAhmed7777/triangles_v5/releases/latest | jq -r .tag_name | sed 's/^v//')-arm64.tar.gz
|
||||
tar xzf tri-pi-*.tar.gz
|
||||
|
||||
# Install
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
By default the backend serves the frontend from `frontend/` and reports placeholder node data until a real `trianglesd` RPC endpoint is configured.
|
||||
## What's Included
|
||||
|
||||
## Next Steps
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `bin/trianglesd` | Native ARM64 binary (stripped, ~5MB) |
|
||||
| `install.sh` | Interactive installer with bootstrap option |
|
||||
| `bootstrap.sh` | One-liner installer (fetches latest release) |
|
||||
| `BOOTSTRAP.md` | Blockchain bootstrap guide |
|
||||
| `docs/BUILD.md` | Build-from-source instructions |
|
||||
|
||||
1. Wire `backend/app.py` to the real Triangles RPC surface.
|
||||
2. Flesh out `setup/install.sh` for Raspberry Pi OS Bookworm.
|
||||
3. Add QEMU-based integration tests under `build/`.
|
||||
4. Add release packaging for prebuilt ARM artifacts.
|
||||
## After Installation
|
||||
|
||||
```bash
|
||||
# Start the node
|
||||
sudo systemctl start triangles
|
||||
|
||||
# Check status
|
||||
trianglesd -datadir=/root/.triangles getinfo
|
||||
|
||||
# Watch sync progress
|
||||
watch -n5 'trianglesd -datadir=/root/.triangles getinfo | grep blocks'
|
||||
|
||||
# View logs
|
||||
journalctl -u triangles -f
|
||||
tail -f /root/.triangles/debug.log
|
||||
|
||||
# View Tor onion address
|
||||
cat /root/.triangles/onion/hostname
|
||||
```
|
||||
|
||||
### Common commands
|
||||
|
||||
```bash
|
||||
trianglesd -datadir=/root/.triangles getblockcount # Current block height
|
||||
trianglesd -datadir=/root/.triangles getpeerinfo # Connected peers
|
||||
trianglesd -datadir=/root/.triangles getstakinginfo # Staking status
|
||||
trianglesd -datadir=/root/.triangles getnewaddress # New address
|
||||
trianglesd -datadir=/root/.triangles getbalance # Wallet balance
|
||||
```
|
||||
|
||||
> **Tip:** Create an alias to save typing:
|
||||
> ```bash
|
||||
> echo 'alias tri="trianglesd -datadir=/root/.triangles"' >> ~/.bashrc && source ~/.bashrc
|
||||
> tri getinfo
|
||||
> ```
|
||||
|
||||
## System Requirements
|
||||
|
||||
| | Minimum | Recommended |
|
||||
|---|---------|-------------|
|
||||
| **Platform** | Raspberry Pi 4 (64-bit) | Raspberry Pi 5 or ARM64 VPS |
|
||||
| **OS** | Ubuntu 24.04, Pi OS 64-bit | Ubuntu 24.04 LTS |
|
||||
| **RAM** | 2 GB | 4 GB |
|
||||
| **Storage** | 5 GB free | 10 GB free |
|
||||
| **Network** | Internet (Tor supported) | Wired ethernet |
|
||||
|
||||
## Blockchain Bootstrap
|
||||
|
||||
Syncing from genesis takes days/weeks on a Pi. The installer offers a pre-synced blockchain download (~1.3 GB) that gets you running in minutes.
|
||||
|
||||
If bootstrap fails during install (server unreachable from your network), you can manually transfer:
|
||||
|
||||
```bash
|
||||
# From a machine that can reach the bootstrap server:
|
||||
curl -O https://bootstrap.cryptographic-triangles.org/tri-bootstrap.tar.gz
|
||||
scp tri-bootstrap.tar.gz pi@your-pi:/tmp/
|
||||
|
||||
# On the Pi:
|
||||
sudo systemctl stop triangles
|
||||
cd /root/.triangles
|
||||
tar xzf /tmp/tri-bootstrap.tar.gz
|
||||
sudo systemctl start triangles
|
||||
```
|
||||
|
||||
## Tor Integration
|
||||
|
||||
trianglesd manages Tor automatically:
|
||||
- Starts Tor as a child process on port 19099
|
||||
- Generates a `.onion` hidden service address
|
||||
- Connects to onion seed nodes
|
||||
- No manual Tor configuration needed
|
||||
|
||||
**Do NOT** set `onion=` or `tor=` in the config — the daemon handles everything.
|
||||
|
||||
## Automated Builds
|
||||
|
||||
New releases are built automatically via GitHub Actions when upstream `triangles_v5` publishes a release. The CI uses QEMU ARM64 emulation on GitHub-hosted runners.
|
||||
|
||||
**Manual trigger:** [Actions](https://github.com/SamiAhmed7777/tri-pi/actions) → "Build TRI-PI ARM64" → "Run workflow". You can leave the version blank to build the latest upstream release, or provide a specific upstream tag such as `v5.8.1`.
|
||||
|
||||
## Network
|
||||
|
||||
| Port | Protocol | Purpose |
|
||||
|------|----------|---------|
|
||||
| 24112 | TCP | P2P network |
|
||||
| 19199 | TCP | RPC (localhost only) |
|
||||
| 19099 | TCP | Tor SOCKS (internal) |
|
||||
|
||||
## Building from Source
|
||||
|
||||
See [docs/BUILD.md](docs/BUILD.md) for complete build instructions.
|
||||
|
||||
## License
|
||||
|
||||
See LICENSE file in the [source repository](https://github.com/SamiAhmed7777/triangles_v5).
|
||||
|
||||
---
|
||||
|
||||
Built with ❤️ for the Raspberry Pi community
|
||||
|
||||
Binary file not shown.
-133
@@ -1,133 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from http import HTTPStatus
|
||||
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
FRONTEND_DIR = Path(os.environ.get("TRI_PI_FRONTEND_DIR", ROOT / "frontend"))
|
||||
RPC_URL = os.environ.get("TRIANGLES_RPC_URL", "http://127.0.0.1:19112/")
|
||||
RPC_USER = os.environ.get("TRIANGLES_RPC_USER", "tripi")
|
||||
RPC_PASSWORD = os.environ.get("TRIANGLES_RPC_PASSWORD", "")
|
||||
DATA_DIR = Path(os.environ.get("TRIANGLES_DATA_DIR", Path.home() / ".triangles"))
|
||||
TOR_HOSTNAME_FILE = Path(
|
||||
os.environ.get(
|
||||
"TRI_PI_TOR_HOSTNAME_FILE",
|
||||
DATA_DIR / "tor_data" / "hostname",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def rpc_call(method):
|
||||
payload = json.dumps(
|
||||
{
|
||||
"jsonrpc": "1.0",
|
||||
"id": "tri-pi",
|
||||
"method": method,
|
||||
"params": [],
|
||||
}
|
||||
).encode("utf-8")
|
||||
|
||||
request = urllib.request.Request(
|
||||
RPC_URL,
|
||||
data=payload,
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
if RPC_USER or RPC_PASSWORD:
|
||||
token = base64.b64encode(f"{RPC_USER}:{RPC_PASSWORD}".encode("utf-8")).decode("ascii")
|
||||
request.add_header("Authorization", f"Basic {token}")
|
||||
|
||||
with urllib.request.urlopen(request, timeout=3) as response:
|
||||
data = json.loads(response.read().decode("utf-8"))
|
||||
return data["result"]
|
||||
|
||||
|
||||
def read_onion_address():
|
||||
try:
|
||||
return TOR_HOSTNAME_FILE.read_text(encoding="utf-8").strip()
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
|
||||
def systemctl_is_active(name):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["systemctl", "is-active", name],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=2,
|
||||
)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
return "unknown"
|
||||
return result.stdout.strip() or "unknown"
|
||||
|
||||
|
||||
def collect_status():
|
||||
status = {
|
||||
"node": {
|
||||
"reachable": False,
|
||||
"block_height": None,
|
||||
"connections": None,
|
||||
"staking": None,
|
||||
"errors": [],
|
||||
},
|
||||
"services": {
|
||||
"trianglesd": systemctl_is_active("trianglesd"),
|
||||
"tor": systemctl_is_active("tor"),
|
||||
},
|
||||
"tor": {
|
||||
"onion_address": read_onion_address(),
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
info = rpc_call("getinfo")
|
||||
staking = rpc_call("getstakinginfo")
|
||||
status["node"].update(
|
||||
{
|
||||
"reachable": True,
|
||||
"block_height": info.get("blocks"),
|
||||
"connections": info.get("connections"),
|
||||
"staking": staking.get("staking"),
|
||||
}
|
||||
)
|
||||
except (KeyError, TimeoutError, urllib.error.URLError, urllib.error.HTTPError, json.JSONDecodeError) as exc:
|
||||
status["node"]["errors"].append(str(exc))
|
||||
|
||||
return status
|
||||
|
||||
|
||||
class Handler(SimpleHTTPRequestHandler):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, directory=str(FRONTEND_DIR), **kwargs)
|
||||
|
||||
def do_GET(self):
|
||||
if self.path == "/api/status":
|
||||
body = json.dumps(collect_status(), indent=2).encode("utf-8")
|
||||
self.send_response(HTTPStatus.OK)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Cache-Control", "no-store")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
return
|
||||
return super().do_GET()
|
||||
|
||||
|
||||
def main():
|
||||
bind = os.environ.get("TRI_PI_BIND", "127.0.0.1")
|
||||
port = int(os.environ.get("TRI_PI_PORT", "8080"))
|
||||
server = ThreadingHTTPServer((bind, port), Handler)
|
||||
print(f"tri-pi backend serving {FRONTEND_DIR} at http://{bind}:{port}")
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
BIN
Binary file not shown.
Executable
+187
@@ -0,0 +1,187 @@
|
||||
#!/bin/bash
|
||||
# TRI-PI Bootstrap Installer
|
||||
# One-liner: curl -sSL https://raw.githubusercontent.com/SamiAhmed7777/tri-pi/main/bootstrap.sh | sudo bash
|
||||
#
|
||||
# Downloads the latest release, installs dependencies, creates systemd service,
|
||||
# and optionally bootstraps the blockchain. ~60 second setup.
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔═══════════════════════════════════════╗"
|
||||
echo "║ TRI-PI Bootstrap Installer ║"
|
||||
echo "╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Must be root
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "❌ Error: Run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check ARM64
|
||||
ARCH=$(uname -m)
|
||||
if [[ "$ARCH" != "aarch64" ]]; then
|
||||
echo "❌ Error: This installer requires ARM64 architecture (aarch64)"
|
||||
echo " Detected: $ARCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ ARM64 detected"
|
||||
|
||||
# Get latest release version from GitHub
|
||||
echo "🔍 Checking latest release..."
|
||||
VERSION=$(curl -sL https://api.github.com/repos/SamiAhmed7777/tri-pi/releases/latest | grep '"tag_name"' | cut -d'"' -f4)
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "⚠️ Could not detect latest version"
|
||||
exit 1
|
||||
fi
|
||||
echo " Latest: $VERSION"
|
||||
|
||||
# Install dependencies
|
||||
echo ""
|
||||
echo "📦 Installing dependencies..."
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq tor curl > /dev/null 2>&1
|
||||
echo "✓ Dependencies installed"
|
||||
|
||||
# Download release package
|
||||
echo ""
|
||||
PACKAGE_URL="https://github.com/SamiAhmed7777/tri-pi/releases/download/$VERSION/tri-pi-${VERSION}-arm64.tar.gz"
|
||||
echo "⬇️ Downloading TRI-PI $VERSION..."
|
||||
|
||||
TMP_DIR=$(mktemp -d)
|
||||
cd "$TMP_DIR"
|
||||
|
||||
if ! curl -sL -o tri-pi.tar.gz "$PACKAGE_URL"; then
|
||||
echo "❌ Download failed: $PACKAGE_URL"
|
||||
rm -rf "$TMP_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DL_SIZE=$(du -sh tri-pi.tar.gz | cut -f1)
|
||||
echo "✓ Downloaded ($DL_SIZE)"
|
||||
|
||||
# Extract and install
|
||||
echo ""
|
||||
echo "📦 Installing..."
|
||||
tar xzf tri-pi.tar.gz
|
||||
|
||||
cp bin/trianglesd /usr/local/bin/
|
||||
chmod +x /usr/local/bin/trianglesd
|
||||
echo "✓ Binary installed"
|
||||
|
||||
# Data directory
|
||||
DATA_DIR="/root/.triangles"
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# Generate config
|
||||
if [ ! -f "$DATA_DIR/triangles.conf" ]; then
|
||||
RPC_PASS=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)
|
||||
|
||||
cat > "$DATA_DIR/triangles.conf" << CONFIG
|
||||
# TRI-PI $VERSION Configuration
|
||||
server=1
|
||||
listen=1
|
||||
|
||||
# RPC
|
||||
rpcuser=tripi
|
||||
rpcpassword=$RPC_PASS
|
||||
rpcallowip=127.0.0.1
|
||||
rpcport=19199
|
||||
|
||||
# Network
|
||||
port=24112
|
||||
maxconnections=50
|
||||
|
||||
# Performance tuning for ARM/Pi
|
||||
dbcache=100
|
||||
maxmempool=50
|
||||
|
||||
# Seed nodes
|
||||
addnode=194.233.88.206
|
||||
addnode=74.208.167.19
|
||||
addnode=179.189.35.51
|
||||
CONFIG
|
||||
chmod 600 "$DATA_DIR/triangles.conf"
|
||||
echo "✓ Configuration created"
|
||||
else
|
||||
echo "✓ Existing config preserved"
|
||||
fi
|
||||
|
||||
# Systemd service
|
||||
cat > /etc/systemd/system/triangles.service << SERVICE
|
||||
[Unit]
|
||||
Description=Triangles Cryptocurrency Node
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/trianglesd -daemon=0 -datadir=$DATA_DIR
|
||||
ExecStop=/usr/local/bin/trianglesd -datadir=$DATA_DIR stop
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
TimeoutStopSec=120
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SERVICE
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable triangles > /dev/null 2>&1
|
||||
echo "✓ Systemd service installed"
|
||||
|
||||
# Blockchain bootstrap
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " Blockchain Sync"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo " [1] Download bootstrap (~1.3GB) — FAST, recommended"
|
||||
echo " [2] Sync from peers — slow (days/weeks on Pi)"
|
||||
echo ""
|
||||
read -p " Choice [1/2]: " SYNC_CHOICE
|
||||
|
||||
if [[ "$SYNC_CHOICE" == "1" ]]; then
|
||||
BOOTSTRAP_URL="http://74.208.167.19/triangles-bootstrap.tar.gz"
|
||||
echo ""
|
||||
echo "⬇️ Downloading blockchain bootstrap..."
|
||||
|
||||
if curl -L --connect-timeout 30 --max-time 600 -o /tmp/triangles-bootstrap.tar.gz "$BOOTSTRAP_URL" 2>/dev/null; then
|
||||
echo "✓ Downloaded"
|
||||
echo "📦 Extracting..."
|
||||
tar xzf /tmp/triangles-bootstrap.tar.gz -C "$DATA_DIR/"
|
||||
rm -f /tmp/triangles-bootstrap.tar.gz
|
||||
echo "✓ Blockchain deployed"
|
||||
else
|
||||
echo "⚠️ Bootstrap unreachable — will sync from peers"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
cd /
|
||||
rm -rf "$TMP_DIR"
|
||||
|
||||
# Start the node
|
||||
echo ""
|
||||
echo "🚀 Starting Triangles node..."
|
||||
systemctl start triangles
|
||||
sleep 3
|
||||
|
||||
if systemctl is-active --quiet triangles; then
|
||||
echo "✓ Node is running!"
|
||||
else
|
||||
echo "⚠️ Node may still be starting — check: journalctl -u triangles -f"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════╗"
|
||||
echo "║ ✅ Installation Complete! ║"
|
||||
echo "╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "📊 Check status: trianglesd -datadir=$DATA_DIR getinfo"
|
||||
echo "📋 View logs: journalctl -u triangles -f"
|
||||
echo "🧅 Onion address: cat $DATA_DIR/onion/hostname"
|
||||
echo "🔄 Auto-starts on boot"
|
||||
echo ""
|
||||
@@ -1,11 +0,0 @@
|
||||
# Build
|
||||
|
||||
This directory owns the appliance-side build and validation flow.
|
||||
|
||||
Planned responsibilities:
|
||||
|
||||
- build or collect ARM `trianglesd` artifacts
|
||||
- run QEMU-based smoke tests
|
||||
- package release assets for Raspberry Pi deployment
|
||||
|
||||
The actual daemon source is expected in `../triangles`.
|
||||
@@ -1,186 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Cross-compile trianglesd for aarch64 (Raspberry Pi 3/4/5).
|
||||
#
|
||||
# Supports two build systems found in typical altcoin forks:
|
||||
# 1. autotools (autogen.sh + configure) — with optional depends/ tree
|
||||
# 2. standalone makefile (makefile.unix / src/makefile.unix)
|
||||
#
|
||||
# Prerequisites (installed automatically on Debian/Ubuntu):
|
||||
# - aarch64-linux-gnu cross-toolchain
|
||||
# - build-essential, autoconf, automake, libtool, pkg-config
|
||||
#
|
||||
# Usage:
|
||||
# ./build/build-arm.sh
|
||||
# TRIANGLES_DIR=../triangles JOBS=8 ./build/build-arm.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
TRIANGLES_DIR="${TRIANGLES_DIR:-$REPO_DIR/../triangles}"
|
||||
JOBS="${JOBS:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)}"
|
||||
HOST="${HOST:-aarch64-linux-gnu}"
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$SCRIPT_DIR/output}"
|
||||
|
||||
log() { echo "[tri-pi:build] $*"; }
|
||||
die() { echo "[tri-pi:build] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# ── validate source tree ─────────────────────────────────────────────────────
|
||||
|
||||
if [[ ! -d "$TRIANGLES_DIR/src" ]]; then
|
||||
die "Triangles source not found at $TRIANGLES_DIR/src."
|
||||
echo " Set TRIANGLES_DIR to the sibling checkout, e.g.:" >&2
|
||||
echo " TRIANGLES_DIR=../triangles $0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TRIANGLES_DIR="$(cd "$TRIANGLES_DIR" && pwd)"
|
||||
|
||||
log "Triangles source: $TRIANGLES_DIR"
|
||||
log "Target host: $HOST"
|
||||
log "Parallel jobs: $JOBS"
|
||||
log "Output dir: $OUTPUT_DIR"
|
||||
|
||||
# ── cross-toolchain ──────────────────────────────────────────────────────────
|
||||
|
||||
log "Checking cross-compilation toolchain..."
|
||||
|
||||
if ! command -v "${HOST}-gcc" &>/dev/null; then
|
||||
log "Cross-compiler ${HOST}-gcc not found. Installing..."
|
||||
if command -v apt-get &>/dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq \
|
||||
gcc-aarch64-linux-gnu \
|
||||
g++-aarch64-linux-gnu \
|
||||
binutils-aarch64-linux-gnu
|
||||
else
|
||||
die "Cannot install cross-compiler: apt-get not found. Install ${HOST}-gcc manually."
|
||||
fi
|
||||
fi
|
||||
|
||||
log "Cross-compiler: $(${HOST}-gcc --version | head -1)"
|
||||
|
||||
# ── host build tools ─────────────────────────────────────────────────────────
|
||||
|
||||
log "Checking host build dependencies..."
|
||||
NEEDED=(build-essential autoconf automake libtool pkg-config)
|
||||
MISSING=()
|
||||
for pkg in "${NEEDED[@]}"; do
|
||||
dpkg -s "$pkg" &>/dev/null 2>&1 || MISSING+=("$pkg")
|
||||
done
|
||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
||||
log "Installing: ${MISSING[*]}"
|
||||
sudo apt-get install -y -qq "${MISSING[@]}"
|
||||
fi
|
||||
|
||||
# ── prepare output dir ───────────────────────────────────────────────────────
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# ── build ────────────────────────────────────────────────────────────────────
|
||||
|
||||
# Strategy 1: autotools with depends/ (Bitcoin-derived build system)
|
||||
if [[ -d "$TRIANGLES_DIR/depends" && -f "$TRIANGLES_DIR/autogen.sh" ]]; then
|
||||
log "Build strategy: autotools + depends/"
|
||||
|
||||
log "Building cross-compiled dependencies (this may take a while)..."
|
||||
make -C "$TRIANGLES_DIR/depends" HOST="$HOST" -j"$JOBS"
|
||||
|
||||
cd "$TRIANGLES_DIR"
|
||||
./autogen.sh
|
||||
|
||||
DEPENDS_PREFIX="$TRIANGLES_DIR/depends/$HOST"
|
||||
CONFIG_SITE="$DEPENDS_PREFIX/share/config.site" \
|
||||
./configure --prefix=/ \
|
||||
--disable-tests \
|
||||
--disable-bench \
|
||||
--disable-gui-tests \
|
||||
--with-gui=no
|
||||
|
||||
make -j"$JOBS"
|
||||
|
||||
log "Copying binary..."
|
||||
cp src/trianglesd "$OUTPUT_DIR/trianglesd"
|
||||
${HOST}-strip "$OUTPUT_DIR/trianglesd"
|
||||
|
||||
# Strategy 2: autotools without depends/
|
||||
elif [[ -f "$TRIANGLES_DIR/autogen.sh" || -f "$TRIANGLES_DIR/configure.ac" ]]; then
|
||||
log "Build strategy: autotools (no depends/)"
|
||||
|
||||
cd "$TRIANGLES_DIR"
|
||||
[[ -f autogen.sh ]] && ./autogen.sh
|
||||
|
||||
./configure --host="$HOST" \
|
||||
--prefix=/ \
|
||||
--disable-tests \
|
||||
--disable-bench \
|
||||
--with-gui=no \
|
||||
CC="${HOST}-gcc" \
|
||||
CXX="${HOST}-g++" \
|
||||
AR="${HOST}-ar" \
|
||||
RANLIB="${HOST}-ranlib" \
|
||||
STRIP="${HOST}-strip"
|
||||
|
||||
make -j"$JOBS"
|
||||
|
||||
log "Copying binary..."
|
||||
cp src/trianglesd "$OUTPUT_DIR/trianglesd"
|
||||
${HOST}-strip "$OUTPUT_DIR/trianglesd"
|
||||
|
||||
# Strategy 3: standalone makefile (older altcoin forks)
|
||||
elif [[ -f "$TRIANGLES_DIR/src/makefile.unix" || -f "$TRIANGLES_DIR/makefile.unix" ]]; then
|
||||
log "Build strategy: standalone makefile"
|
||||
|
||||
if [[ -f "$TRIANGLES_DIR/src/makefile.unix" ]]; then
|
||||
MAKE_DIR="$TRIANGLES_DIR/src"
|
||||
MAKEFILE="makefile.unix"
|
||||
else
|
||||
MAKE_DIR="$TRIANGLES_DIR"
|
||||
MAKEFILE="makefile.unix"
|
||||
fi
|
||||
|
||||
cd "$MAKE_DIR"
|
||||
make -f "$MAKEFILE" \
|
||||
CC="${HOST}-gcc" \
|
||||
CXX="${HOST}-g++" \
|
||||
AR="${HOST}-ar" \
|
||||
RANLIB="${HOST}-ranlib" \
|
||||
STRIP="${HOST}-strip" \
|
||||
-j"$JOBS"
|
||||
|
||||
BUILT="$(find "$MAKE_DIR" -maxdepth 1 -name 'trianglesd' -type f | head -1)"
|
||||
[[ -n "$BUILT" ]] || die "Build completed but trianglesd binary not found in $MAKE_DIR"
|
||||
|
||||
log "Copying binary..."
|
||||
cp "$BUILT" "$OUTPUT_DIR/trianglesd"
|
||||
${HOST}-strip "$OUTPUT_DIR/trianglesd"
|
||||
|
||||
else
|
||||
die "Cannot determine build system. Expected one of:
|
||||
- depends/ + autogen.sh (Bitcoin-style)
|
||||
- autogen.sh / configure.ac (autotools)
|
||||
- src/makefile.unix (standalone makefile)
|
||||
in: $TRIANGLES_DIR"
|
||||
fi
|
||||
|
||||
# ── verify ───────────────────────────────────────────────────────────────────
|
||||
|
||||
log "Verifying binary..."
|
||||
FILE_INFO="$(file "$OUTPUT_DIR/trianglesd")"
|
||||
log " $FILE_INFO"
|
||||
|
||||
if echo "$FILE_INFO" | grep -qi 'aarch64\|ARM aarch64'; then
|
||||
log "Architecture: aarch64 confirmed."
|
||||
else
|
||||
log "WARNING: Binary may not be aarch64. Check the output above."
|
||||
fi
|
||||
|
||||
SIZE="$(du -h "$OUTPUT_DIR/trianglesd" | cut -f1)"
|
||||
|
||||
log ""
|
||||
log "Build complete!"
|
||||
log " Binary: $OUTPUT_DIR/trianglesd"
|
||||
log " Size: $SIZE"
|
||||
log ""
|
||||
log "Next step — install on Pi:"
|
||||
log " TRIANGLESD_BIN=$OUTPUT_DIR/trianglesd sudo ./setup/install.sh"
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Extended smoke test - validate services actually work
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORK_DIR="$SCRIPT_DIR/.qemu-work"
|
||||
ROOTFS="$WORK_DIR/rootfs"
|
||||
|
||||
log() { echo "[tri-pi:extended] $*"; }
|
||||
die() { echo "[tri-pi:extended] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# Mount the test image rootfs
|
||||
export PATH="/sbin:/usr/sbin:$PATH"
|
||||
LOOP_DEV=$(losetup -fP --show "$WORK_DIR/test-image.img")
|
||||
mkdir -p "$ROOTFS"
|
||||
mount "${LOOP_DEV}p2" "$ROOTFS"
|
||||
|
||||
cleanup() {
|
||||
umount "$ROOTFS" 2>/dev/null || true
|
||||
rmdir "$ROOTFS" 2>/dev/null || true
|
||||
losetup -d "$LOOP_DEV" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
log "Testing backend Python syntax..."
|
||||
systemd-nspawn -D "$ROOTFS" --pipe python3 -m py_compile /opt/tri-pi/backend/app.py || die "Backend Python syntax error"
|
||||
|
||||
log "Testing backend imports..."
|
||||
systemd-nspawn -D "$ROOTFS" --pipe python3 -c "
|
||||
import sys
|
||||
sys.path.insert(0, '/opt/tri-pi/backend')
|
||||
try:
|
||||
from flask import Flask
|
||||
print('Flask OK')
|
||||
except ImportError as e:
|
||||
print(f'Missing dependency: {e}')
|
||||
sys.exit(1)
|
||||
" || die "Backend missing dependencies"
|
||||
|
||||
log "Validating triangles.conf syntax..."
|
||||
systemd-nspawn -D "$ROOTFS" --pipe bash -c "
|
||||
grep -q '^rpcuser=' /etc/triangles/triangles.conf || exit 1
|
||||
grep -q '^rpcpassword=' /etc/triangles/triangles.conf || exit 1
|
||||
grep -q '^rpcport=' /etc/triangles/triangles.conf || exit 1
|
||||
echo 'Config syntax OK'
|
||||
" || die "triangles.conf validation failed"
|
||||
|
||||
log "Validating Tor config..."
|
||||
systemd-nspawn -D "$ROOTFS" --pipe bash -c "
|
||||
grep -q 'HiddenServiceDir /var/lib/tor/tri-pi' /etc/tor/torrc.d/tri-pi.conf || exit 1
|
||||
grep -q 'HiddenServicePort 24112' /etc/tor/torrc.d/tri-pi.conf || exit 1
|
||||
echo 'Tor config OK'
|
||||
" || die "Tor config validation failed"
|
||||
|
||||
log "Checking frontend files..."
|
||||
for file in index.html app.js styles.css; do
|
||||
[[ -f "$ROOTFS/opt/tri-pi/frontend/$file" ]] || die "Missing frontend file: $file"
|
||||
done
|
||||
|
||||
log "Validating systemd units..."
|
||||
for unit in trianglesd.service tripi-backend.service; do
|
||||
systemd-nspawn -D "$ROOTFS" --pipe systemd-analyze verify "/etc/systemd/system/$unit" 2>&1 || log "Warning: $unit has issues"
|
||||
done
|
||||
|
||||
log "Testing backend environment file..."
|
||||
systemd-nspawn -D "$ROOTFS" --pipe bash -c "
|
||||
source /etc/default/tripi-backend
|
||||
[[ -n \$TRIANGLES_RPC_PASSWORD ]] || exit 1
|
||||
[[ \$TRIANGLES_RPC_URL == 'http://127.0.0.1:19112/' ]] || exit 1
|
||||
echo 'Environment file OK'
|
||||
" || die "Environment file validation failed"
|
||||
|
||||
log ""
|
||||
log "============================================"
|
||||
log " EXTENDED SMOKE TEST PASSED"
|
||||
log "============================================"
|
||||
Executable
+143
@@ -0,0 +1,143 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:-}"
|
||||
|
||||
if [ -z "$VERSION" ] && [ -f .github/docker/Dockerfile.arm64 ]; then
|
||||
VERSION=$(sed -n 's/^ARG TRIANGLES_VERSION=v//p' .github/docker/Dockerfile.arm64 | head -n1)
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "ERROR: Could not determine TRI-PI package version." >&2
|
||||
echo "Pass a version explicitly, e.g. ./build/package-release.sh 5.7.6" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RELEASE_NAME="tri-pi-${VERSION}-arm64"
|
||||
RELEASE_DIR="build/release/${RELEASE_NAME}"
|
||||
|
||||
echo "=== Packaging TRI-PI Release v${VERSION} ==="
|
||||
|
||||
# Clean and create release directory
|
||||
rm -rf build/release
|
||||
mkdir -p "$RELEASE_DIR"/{bin,backend,frontend,services,systemd,docs}
|
||||
|
||||
# Copy binaries
|
||||
echo "📦 Copying binaries..."
|
||||
cp build/output/trianglesd-arm64 "$RELEASE_DIR/bin/trianglesd"
|
||||
cp build/output/trianglesd-arm64-static "$RELEASE_DIR/bin/trianglesd-static"
|
||||
chmod +x "$RELEASE_DIR/bin/"*
|
||||
|
||||
# Copy installer
|
||||
echo "📦 Copying installer..."
|
||||
cp setup/install.sh "$RELEASE_DIR/"
|
||||
chmod +x "$RELEASE_DIR/install.sh"
|
||||
|
||||
# Copy application files
|
||||
echo "📦 Copying application files..."
|
||||
cp -r backend/* "$RELEASE_DIR/backend/" 2>/dev/null || true
|
||||
cp -r frontend/* "$RELEASE_DIR/frontend/" 2>/dev/null || true
|
||||
cp -r services/* "$RELEASE_DIR/services/" 2>/dev/null || true
|
||||
cp -r config "$RELEASE_DIR/" 2>/dev/null || true
|
||||
cp -r tor "$RELEASE_DIR/" 2>/dev/null || true
|
||||
|
||||
# Copy documentation
|
||||
echo "📦 Copying documentation..."
|
||||
cp README.md "$RELEASE_DIR/docs/"
|
||||
cp CLAUDE.md "$RELEASE_DIR/docs/" 2>/dev/null || true
|
||||
[ -f LICENSE ] && cp LICENSE "$RELEASE_DIR/docs/" || echo "MIT License" > "$RELEASE_DIR/docs/LICENSE"
|
||||
|
||||
# Create installation instructions
|
||||
cat > "$RELEASE_DIR/INSTALL.md" << 'INSTALL'
|
||||
# TRI-PI Installation Guide
|
||||
|
||||
## Quick Install (Recommended)
|
||||
|
||||
```bash
|
||||
sudo ./install.sh
|
||||
```
|
||||
|
||||
This will:
|
||||
- Install trianglesd to /opt/tri-pi/
|
||||
- Install Flask backend API
|
||||
- Install web dashboard frontend
|
||||
- Create systemd services
|
||||
- Set up Tor integration
|
||||
- Configure everything automatically
|
||||
- Start all services
|
||||
|
||||
## What Gets Installed
|
||||
|
||||
- **Triangles Daemon**: /opt/tri-pi/bin/trianglesd
|
||||
- **Backend API**: /opt/tri-pi/backend/ (Flask on port 8081)
|
||||
- **Web Dashboard**: /opt/tri-pi/frontend/ (served via Caddy)
|
||||
- **Config**: ~/.triangles/triangles.conf
|
||||
- **Services**:
|
||||
- triangles.service (daemon)
|
||||
- tri-pi-backend.service (API)
|
||||
- tri-pi-frontend.service (dashboard)
|
||||
|
||||
## System Requirements
|
||||
|
||||
- Raspberry Pi 4 or 5 (4GB+ RAM recommended)
|
||||
- Raspberry Pi OS (64-bit) Bookworm or later
|
||||
- 32GB+ storage (SD card or SSD)
|
||||
- Internet connection
|
||||
|
||||
## Post-Installation
|
||||
|
||||
Access the web dashboard:
|
||||
```bash
|
||||
http://<your-pi-ip>:8080
|
||||
```
|
||||
|
||||
Check daemon status:
|
||||
```bash
|
||||
systemctl status triangles
|
||||
```
|
||||
|
||||
View wallet info:
|
||||
```bash
|
||||
/opt/tri-pi/bin/trianglesd getinfo
|
||||
```
|
||||
|
||||
## Manual Installation (Advanced)
|
||||
|
||||
If you prefer manual setup, see docs/MANUAL_INSTALL.md
|
||||
|
||||
## Support
|
||||
|
||||
- GitHub: https://github.com/SamiAhmed7777/triangles_v5
|
||||
- Git: https://git.sami/sami7777/tri-pi
|
||||
- Explorer: https://blocks.cryptographic-triangles.org
|
||||
INSTALL
|
||||
|
||||
# Create checksums
|
||||
echo "🔐 Generating checksums..."
|
||||
cd "$RELEASE_DIR/bin"
|
||||
sha256sum trianglesd > trianglesd.sha256
|
||||
sha256sum trianglesd-static > trianglesd-static.sha256
|
||||
cd - > /dev/null
|
||||
|
||||
# Create release tarball
|
||||
echo "📦 Creating release tarball..."
|
||||
cd build/release
|
||||
tar czf "${RELEASE_NAME}.tar.gz" "$RELEASE_NAME"
|
||||
cd - > /dev/null
|
||||
|
||||
# Generate final checksums
|
||||
cd build/release
|
||||
sha256sum "${RELEASE_NAME}.tar.gz" > "${RELEASE_NAME}.tar.gz.sha256"
|
||||
cd - > /dev/null
|
||||
|
||||
echo ""
|
||||
echo "✅ Release package created!"
|
||||
echo ""
|
||||
echo "📦 Package: build/release/${RELEASE_NAME}.tar.gz"
|
||||
echo "🔐 SHA256: $(cat build/release/${RELEASE_NAME}.tar.gz.sha256 | cut -d' ' -f1)"
|
||||
echo "📊 Size: $(du -h build/release/${RELEASE_NAME}.tar.gz | cut -f1)"
|
||||
echo ""
|
||||
echo "Package contents:"
|
||||
du -sh "build/release/${RELEASE_NAME}"/*
|
||||
echo ""
|
||||
echo "Ready for GitHub release upload!"
|
||||
@@ -1,263 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# QEMU smoke test for the tri-pi appliance.
|
||||
#
|
||||
# Mounts a Raspberry Pi OS arm64 image, injects the tri-pi assets,
|
||||
# runs the installer inside a systemd-nspawn container, and validates
|
||||
# that Tor, the backend, and config generation all work.
|
||||
#
|
||||
# Requires (on the host):
|
||||
# - qemu-user-static (ARM user-mode emulation, registered via binfmt_misc)
|
||||
# - systemd-container (provides systemd-nspawn)
|
||||
# - losetup, mount, rsync
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./build/qemu-smoke.sh
|
||||
# IMAGE_FILE=/path/to/raspios.img sudo ./build/qemu-smoke.sh
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
WORK_DIR="${WORK_DIR:-$SCRIPT_DIR/.qemu-work}"
|
||||
IMAGE_FILE="${IMAGE_FILE:-}"
|
||||
TIMEOUT="${TIMEOUT:-180}"
|
||||
|
||||
# Raspberry Pi OS Bookworm Lite arm64
|
||||
PI_OS_URL="${PI_OS_URL:-https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2024-03-15/2024-03-15-raspios-bookworm-arm64-lite.img.xz}"
|
||||
|
||||
log() { echo "[tri-pi:qemu] $*"; }
|
||||
die() { echo "[tri-pi:qemu] ERROR: $*" >&2; exit 1; }
|
||||
pass() { echo "[tri-pi:qemu] PASS: $*"; ((PASSES++)); }
|
||||
fail() { echo "[tri-pi:qemu] FAIL: $*"; FAILURES+=("$*"); }
|
||||
|
||||
PASSES=0
|
||||
FAILURES=()
|
||||
LOOP_DEV=""
|
||||
MNT_DIR=""
|
||||
|
||||
cleanup() {
|
||||
log "Cleaning up..."
|
||||
[[ -n "$MNT_DIR" && -d "$MNT_DIR" ]] && umount "$MNT_DIR" 2>/dev/null || true
|
||||
[[ -n "$MNT_DIR" && -d "$MNT_DIR" ]] && rmdir "$MNT_DIR" 2>/dev/null || true
|
||||
[[ -n "$LOOP_DEV" ]] && losetup -d "$LOOP_DEV" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# ── pre-flight ────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
die "This script must be run as root (try: sudo $0)"
|
||||
fi
|
||||
|
||||
log "Checking host dependencies..."
|
||||
# Ensure /sbin is in PATH (losetup lives there on Ubuntu)
|
||||
export PATH="/sbin:/usr/sbin:$PATH"
|
||||
for cmd in losetup mount rsync; do
|
||||
command -v "$cmd" &>/dev/null || die "Required command not found: $cmd"
|
||||
done
|
||||
|
||||
# We need either systemd-nspawn or chroot+qemu-user-static
|
||||
USE_NSPAWN=false
|
||||
if command -v systemd-nspawn &>/dev/null; then
|
||||
USE_NSPAWN=true
|
||||
log "Will use systemd-nspawn for container execution."
|
||||
elif [[ -f /usr/bin/qemu-aarch64-static ]]; then
|
||||
log "Will use chroot + qemu-user-static for execution."
|
||||
else
|
||||
die "Need either systemd-nspawn (systemd-container pkg) or /usr/bin/qemu-aarch64-static (qemu-user-static pkg)."
|
||||
fi
|
||||
|
||||
mkdir -p "$WORK_DIR"
|
||||
|
||||
# ── obtain image ─────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ -n "$IMAGE_FILE" ]]; then
|
||||
[[ -f "$IMAGE_FILE" ]] || die "IMAGE_FILE not found: $IMAGE_FILE"
|
||||
log "Using provided image: $IMAGE_FILE"
|
||||
WORK_IMAGE="$WORK_DIR/test-image.img"
|
||||
cp "$IMAGE_FILE" "$WORK_IMAGE"
|
||||
elif [[ -f "$WORK_DIR/raspios-base.img" ]]; then
|
||||
log "Using cached Pi OS image."
|
||||
WORK_IMAGE="$WORK_DIR/test-image.img"
|
||||
cp "$WORK_DIR/raspios-base.img" "$WORK_IMAGE"
|
||||
else
|
||||
log "Downloading Raspberry Pi OS Bookworm Lite (arm64)..."
|
||||
DOWNLOAD="$WORK_DIR/raspios.img.xz"
|
||||
curl -fSL -o "$DOWNLOAD" "$PI_OS_URL"
|
||||
log "Extracting..."
|
||||
xz -d "$DOWNLOAD"
|
||||
mv "${DOWNLOAD%.xz}" "$WORK_DIR/raspios-base.img"
|
||||
WORK_IMAGE="$WORK_DIR/test-image.img"
|
||||
cp "$WORK_DIR/raspios-base.img" "$WORK_IMAGE"
|
||||
fi
|
||||
|
||||
# ── resize for headroom ──────────────────────────────────────────────────────
|
||||
|
||||
log "Adding 512M headroom to image..."
|
||||
truncate -s +512M "$WORK_IMAGE"
|
||||
|
||||
# ── mount rootfs ─────────────────────────────────────────────────────────────
|
||||
|
||||
log "Mounting image rootfs..."
|
||||
LOOP_DEV="$(losetup --find --show --partscan "$WORK_IMAGE")"
|
||||
sleep 1 # wait for partition devices
|
||||
|
||||
# Pi OS images: p1 = boot (FAT32), p2 = rootfs (ext4)
|
||||
ROOTFS_PART="${LOOP_DEV}p2"
|
||||
[[ -b "$ROOTFS_PART" ]] || die "Rootfs partition not found at $ROOTFS_PART"
|
||||
|
||||
# Grow the filesystem to fill the extra space
|
||||
e2fsck -fy "$ROOTFS_PART" &>/dev/null || true
|
||||
resize2fs "$ROOTFS_PART" &>/dev/null || true
|
||||
|
||||
MNT_DIR="$(mktemp -d)"
|
||||
mount "$ROOTFS_PART" "$MNT_DIR"
|
||||
|
||||
# ── inject tri-pi assets ─────────────────────────────────────────────────────
|
||||
|
||||
log "Copying tri-pi into image..."
|
||||
DEST="$MNT_DIR/opt/tri-pi-src"
|
||||
mkdir -p "$DEST"
|
||||
rsync -a --exclude='.git' --exclude='build/.qemu-work' \
|
||||
"$REPO_DIR/" "$DEST/"
|
||||
|
||||
# Copy qemu-user-static into the image so chroot works
|
||||
if [[ -f /usr/bin/qemu-aarch64-static ]]; then
|
||||
cp /usr/bin/qemu-aarch64-static "$MNT_DIR/usr/bin/"
|
||||
fi
|
||||
|
||||
# ── write the in-image test runner ────────────────────────────────────────────
|
||||
|
||||
cat > "$MNT_DIR/opt/run-smoke.sh" <<'SMOKE_EOF'
|
||||
#!/usr/bin/env bash
|
||||
set -uo pipefail
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
RESULTS=()
|
||||
|
||||
check() {
|
||||
local name="$1"; shift
|
||||
if "$@" >/dev/null 2>&1; then
|
||||
RESULTS+=("PASS: $name")
|
||||
PASS=$((PASS+1))
|
||||
else
|
||||
RESULTS+=("FAIL: $name")
|
||||
FAIL=$((FAIL+1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " tri-pi smoke test (inside QEMU rootfs)"
|
||||
echo "============================================"
|
||||
echo ""
|
||||
|
||||
# --- fix APT cache before installer ---
|
||||
echo ">>> Cleaning APT cache..."
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
mkdir -p /var/lib/apt/lists/partial
|
||||
|
||||
# --- run the installer (no trianglesd binary, just infra) ---
|
||||
echo ">>> Running installer..."
|
||||
cd /opt/tri-pi-src
|
||||
bash setup/install.sh 2>&1 || true
|
||||
|
||||
echo ""
|
||||
echo ">>> Running checks..."
|
||||
|
||||
# Config generation
|
||||
check "triangles.conf exists" test -f /etc/triangles/triangles.conf
|
||||
check "rpcpassword is randomized" bash -c 'grep -q "^rpcpassword=.\{16,\}" /etc/triangles/triangles.conf'
|
||||
check "conf is not world-readable" bash -c '! stat -c %a /etc/triangles/triangles.conf | grep -q ".[^0][^0]4"'
|
||||
|
||||
# Installed assets
|
||||
check "backend installed" test -f /opt/tri-pi/backend/app.py
|
||||
check "frontend index installed" test -f /opt/tri-pi/frontend/index.html
|
||||
check "frontend js installed" test -f /opt/tri-pi/frontend/app.js
|
||||
check "frontend css installed" test -f /opt/tri-pi/frontend/styles.css
|
||||
|
||||
# Backend environment file
|
||||
check "env file exists" test -f /etc/default/tripi-backend
|
||||
check "env has RPC password" grep -q '^TRIANGLES_RPC_PASSWORD=.\{16,\}' /etc/default/tripi-backend
|
||||
|
||||
# Tor config
|
||||
check "torrc drop-in exists" test -f /etc/tor/torrc.d/tri-pi.conf
|
||||
check "torrc has HiddenServiceDir" grep -q 'HiddenServiceDir' /etc/tor/torrc.d/tri-pi.conf
|
||||
|
||||
# systemd units
|
||||
check "trianglesd unit installed" test -f /etc/systemd/system/trianglesd.service
|
||||
check "backend unit installed" test -f /etc/systemd/system/tripi-backend.service
|
||||
|
||||
# System user
|
||||
check "triangles user exists" id triangles
|
||||
|
||||
# Directories
|
||||
check "data dir exists" test -d /var/lib/triangles
|
||||
check "tor hs dir exists" test -d /var/lib/tor/tri-pi
|
||||
|
||||
# --- report ---
|
||||
echo ""
|
||||
echo "============================================"
|
||||
echo " Results"
|
||||
echo "============================================"
|
||||
for r in "${RESULTS[@]}"; do echo " $r"; done
|
||||
echo ""
|
||||
echo " Passed: $PASS Failed: $FAIL"
|
||||
echo ""
|
||||
|
||||
if [[ $FAIL -gt 0 ]]; then
|
||||
echo "SMOKE_RESULT=FAILED"
|
||||
exit 1
|
||||
else
|
||||
echo "SMOKE_RESULT=PASSED"
|
||||
exit 0
|
||||
fi
|
||||
SMOKE_EOF
|
||||
chmod +x "$MNT_DIR/opt/run-smoke.sh"
|
||||
|
||||
# ── run tests inside the image ────────────────────────────────────────────────
|
||||
|
||||
log "Executing smoke test inside ARM rootfs..."
|
||||
SMOKE_RC=0
|
||||
|
||||
if $USE_NSPAWN; then
|
||||
# systemd-nspawn handles binfmt_misc automatically, gives us PID 1
|
||||
# --private-users=no allows network access without user namespacing
|
||||
timeout "$TIMEOUT" systemd-nspawn \
|
||||
--quiet \
|
||||
--directory="$MNT_DIR" \
|
||||
--bind-ro=/etc/resolv.conf:/etc/resolv.conf \
|
||||
--private-users=no \
|
||||
/opt/run-smoke.sh || SMOKE_RC=$?
|
||||
else
|
||||
# Manual chroot with qemu-user-static
|
||||
mount --bind /proc "$MNT_DIR/proc" 2>/dev/null || true
|
||||
mount --bind /sys "$MNT_DIR/sys" 2>/dev/null || true
|
||||
mount --bind /dev "$MNT_DIR/dev" 2>/dev/null || true
|
||||
cp /etc/resolv.conf "$MNT_DIR/etc/resolv.conf" 2>/dev/null || true
|
||||
|
||||
timeout "$TIMEOUT" chroot "$MNT_DIR" /opt/run-smoke.sh || SMOKE_RC=$?
|
||||
|
||||
umount "$MNT_DIR/proc" 2>/dev/null || true
|
||||
umount "$MNT_DIR/sys" 2>/dev/null || true
|
||||
umount "$MNT_DIR/dev" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ── report ────────────────────────────────────────────────────────────────────
|
||||
|
||||
echo ""
|
||||
log "============================================"
|
||||
if [[ $SMOKE_RC -eq 0 ]]; then
|
||||
log " SMOKE TEST PASSED"
|
||||
log "============================================"
|
||||
exit 0
|
||||
elif [[ $SMOKE_RC -eq 124 ]]; then
|
||||
log " SMOKE TEST TIMED OUT (${TIMEOUT}s)"
|
||||
log "============================================"
|
||||
exit 2
|
||||
else
|
||||
log " SMOKE TEST FAILED (exit code $SMOKE_RC)"
|
||||
log "============================================"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Test bootstrap functionality in QEMU
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORK_DIR="$SCRIPT_DIR/.qemu-work"
|
||||
|
||||
log() { echo "[tri-pi:test-bootstrap] $*"; }
|
||||
|
||||
export PATH="/sbin:/usr/sbin:$PATH"
|
||||
|
||||
if [[ ! -f "$WORK_DIR/test-image.img" ]]; then
|
||||
log "ERROR: Test image not found. Run qemu-smoke.sh first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROOTFS="$WORK_DIR/test-rootfs"
|
||||
mkdir -p "$ROOTFS"
|
||||
|
||||
LOOP_DEV=$(losetup -fP --show "$WORK_DIR/test-image.img")
|
||||
mount "${LOOP_DEV}p2" "$ROOTFS"
|
||||
|
||||
cleanup() {
|
||||
log "Cleaning up..."
|
||||
umount "$ROOTFS" 2>/dev/null || true
|
||||
rmdir "$ROOTFS" 2>/dev/null || true
|
||||
losetup -d "$LOOP_DEV" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
log "Testing bootstrap script..."
|
||||
log ""
|
||||
|
||||
# Test download (dry run - don't actually download 1.3GB)
|
||||
log ">>> Checking bootstrap URL accessibility..."
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe curl -I http://194.233.88.206/triangles-bootstrap.tar.gz 2>&1 | grep -q "HTTP.*200"; then
|
||||
log " ✓ Bootstrap server reachable"
|
||||
else
|
||||
log " ✗ Bootstrap server not reachable (expected in some environments)"
|
||||
fi
|
||||
|
||||
log ""
|
||||
log ">>> Checking bootstrap script syntax..."
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe bash -n /root/tri-pi/setup/bootstrap.sh 2>&1; then
|
||||
log " ✓ Bootstrap script syntax OK"
|
||||
else
|
||||
log " ✗ Bootstrap script has syntax errors"
|
||||
fi
|
||||
|
||||
log ""
|
||||
log ">>> Testing bootstrap marker logic..."
|
||||
systemd-nspawn -q -D "$ROOTFS" --pipe bash -c '
|
||||
touch /var/lib/triangles/.bootstrapped
|
||||
if bash /root/tri-pi/setup/bootstrap.sh 2>&1 | grep -q "already applied"; then
|
||||
echo " ✓ Skip logic works (marker file detected)"
|
||||
else
|
||||
echo " ✗ Skip logic failed"
|
||||
fi
|
||||
'
|
||||
|
||||
log ""
|
||||
log "Bootstrap testing complete."
|
||||
@@ -1,131 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Test tri-pi services functionality in QEMU
|
||||
# This script mounts the test image, boots services, and validates they work
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
WORK_DIR="${WORK_DIR:-$SCRIPT_DIR/.qemu-work}"
|
||||
|
||||
log() { echo "[tri-pi:test] $*"; }
|
||||
die() { echo "[tri-pi:test] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
export PATH="/sbin:/usr/sbin:$PATH"
|
||||
|
||||
# First run basic smoke test to set up image
|
||||
if [[ ! -f "$WORK_DIR/test-image.img" ]]; then
|
||||
log "Running basic smoke test first to create test image..."
|
||||
bash "$SCRIPT_DIR/qemu-smoke.sh" || die "Basic smoke test failed"
|
||||
fi
|
||||
|
||||
# Now run extended tests
|
||||
log "============================================"
|
||||
log " Extended Service Testing"
|
||||
log "============================================"
|
||||
|
||||
ROOTFS="$WORK_DIR/test-rootfs"
|
||||
mkdir -p "$ROOTFS"
|
||||
|
||||
LOOP_DEV=$(losetup -fP --show "$WORK_DIR/test-image.img")
|
||||
mount "${LOOP_DEV}p2" "$ROOTFS"
|
||||
|
||||
cleanup() {
|
||||
log "Cleaning up..."
|
||||
umount "$ROOTFS" 2>/dev/null || true
|
||||
rmdir "$ROOTFS" 2>/dev/null || true
|
||||
losetup -d "$LOOP_DEV" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
log ""
|
||||
log ">>> Testing Python backend syntax..."
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe python3 -m py_compile /opt/tri-pi/backend/app.py 2>&1; then
|
||||
log " ✓ Backend Python syntax OK"
|
||||
else
|
||||
log " ✗ Backend Python syntax errors"
|
||||
fi
|
||||
|
||||
log ""
|
||||
log ">>> Testing Python stdlib dependencies..."
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe python3 -c "from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer; import json, urllib.request; print('OK')" 2>&1 | grep -q OK; then
|
||||
log " ✓ Python stdlib modules available (http.server, json, urllib)"
|
||||
else
|
||||
log " ✗ Python stdlib imports failed"
|
||||
fi
|
||||
|
||||
log ""
|
||||
log ">>> Validating triangles.conf..."
|
||||
RPC_USER=$(systemd-nspawn -q -D "$ROOTFS" --pipe grep '^rpcuser=' /etc/triangles/triangles.conf | cut -d= -f2)
|
||||
RPC_PASS=$(systemd-nspawn -q -D "$ROOTFS" --pipe grep '^rpcpassword=' /etc/triangles/triangles.conf | cut -d= -f2)
|
||||
RPC_PORT=$(systemd-nspawn -q -D "$ROOTFS" --pipe grep '^rpcport=' /etc/triangles/triangles.conf | cut -d= -f2)
|
||||
|
||||
log " RPC User: $RPC_USER"
|
||||
log " RPC Password: ${RPC_PASS:0:8}... (${#RPC_PASS} chars)"
|
||||
log " RPC Port: $RPC_PORT"
|
||||
|
||||
[[ -n "$RPC_USER" && -n "$RPC_PASS" && -n "$RPC_PORT" ]] && log " ✓ Config values present" || log " ✗ Config missing values"
|
||||
|
||||
log ""
|
||||
log ">>> Validating Tor configuration..."
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe grep -q 'HiddenServiceDir /var/lib/tor/tri-pi' /etc/tor/torrc.d/tri-pi.conf; then
|
||||
log " ✓ Tor hidden service directory configured"
|
||||
else
|
||||
log " ✗ Tor hidden service not configured"
|
||||
fi
|
||||
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe grep -q 'HiddenServicePort 24112 127.0.0.1:24112' /etc/tor/torrc.d/tri-pi.conf; then
|
||||
log " ✓ Tor hidden service port configured"
|
||||
else
|
||||
log " ✗ Tor hidden service port not configured"
|
||||
fi
|
||||
|
||||
log ""
|
||||
log ">>> Checking frontend assets..."
|
||||
for file in index.html app.js styles.css; do
|
||||
if [[ -f "$ROOTFS/opt/tri-pi/frontend/$file" ]]; then
|
||||
SIZE=$(stat -c%s "$ROOTFS/opt/tri-pi/frontend/$file")
|
||||
log " ✓ $file ($SIZE bytes)"
|
||||
else
|
||||
log " ✗ $file missing"
|
||||
fi
|
||||
done
|
||||
|
||||
log ""
|
||||
log ">>> Validating backend environment..."
|
||||
ENV_PASS=$(systemd-nspawn -q -D "$ROOTFS" --pipe bash -c 'source /etc/default/tripi-backend && echo $TRIANGLES_RPC_PASSWORD')
|
||||
ENV_URL=$(systemd-nspawn -q -D "$ROOTFS" --pipe bash -c 'source /etc/default/tripi-backend && echo $TRIANGLES_RPC_URL')
|
||||
|
||||
log " Environment RPC Password: ${ENV_PASS:0:8}... (${#ENV_PASS} chars)"
|
||||
log " Environment RPC URL: $ENV_URL"
|
||||
|
||||
[[ "$ENV_PASS" == "$RPC_PASS" ]] && log " ✓ Password matches config" || log " ✗ Password mismatch!"
|
||||
|
||||
log ""
|
||||
log ">>> Testing systemd unit files..."
|
||||
for unit in trianglesd.service tripi-backend.service; do
|
||||
if systemd-nspawn -q -D "$ROOTFS" --pipe systemd-analyze verify "/etc/systemd/system/$unit" 2>&1 | grep -qiE 'error|fail'; then
|
||||
log " ✗ $unit has issues"
|
||||
else
|
||||
log " ✓ $unit syntax OK"
|
||||
fi
|
||||
done
|
||||
|
||||
log ""
|
||||
log ">>> Checking file permissions..."
|
||||
CONF_PERM=$(stat -c%a "$ROOTFS/etc/triangles/triangles.conf")
|
||||
ENV_PERM=$(stat -c%a "$ROOTFS/etc/default/tripi-backend")
|
||||
DATA_PERM=$(stat -c%a "$ROOTFS/var/lib/triangles")
|
||||
|
||||
log " triangles.conf: $CONF_PERM (should be 640)"
|
||||
log " tripi-backend env: $ENV_PERM (should be 640)"
|
||||
log " data directory: $DATA_PERM (should be 750)"
|
||||
|
||||
[[ "$CONF_PERM" == "640" ]] && log " ✓ Config permissions correct" || log " ⚠ Config permissions: $CONF_PERM (expected 640)"
|
||||
[[ "$ENV_PERM" == "640" ]] && log " ✓ Environment permissions correct" || log " ⚠ Environment permissions: $ENV_PERM (expected 640)"
|
||||
[[ "$DATA_PERM" == "750" ]] && log " ✓ Data dir permissions correct" || log " ⚠ Data dir permissions: $DATA_PERM (expected 750)"
|
||||
|
||||
log ""
|
||||
log "============================================"
|
||||
log " Extended Testing Complete"
|
||||
log "============================================"
|
||||
@@ -1,36 +0,0 @@
|
||||
# Network - Relay Node Configuration
|
||||
# This Pi acts as a full Tor onion node, strengthening the Triangles network
|
||||
port=24112
|
||||
rpcport=19112
|
||||
rpcuser=tripi
|
||||
rpcpassword=changeme
|
||||
rpcallowip=127.0.0.1
|
||||
listen=1 # Accept incoming connections (relay mode)
|
||||
server=1
|
||||
daemon=1
|
||||
maxconnections=32 # Allow 32 peers (tuned for Pi resources)
|
||||
|
||||
# Tor Hidden Service
|
||||
# Your Pi will be reachable as a .onion address on the Triangles network
|
||||
proxy=127.0.0.1:9050
|
||||
tor=127.0.0.1:9050
|
||||
torhiddenservice=1 # Create Tor hidden service
|
||||
torhsport=24112 # Advertise port 24112 to network
|
||||
onlynet=tor # Only connect via Tor (privacy + compatibility)
|
||||
|
||||
# Staking (optional, earns rewards)
|
||||
staking=1
|
||||
|
||||
# Resource Limits (optimized for Raspberry Pi)
|
||||
dbcache=100 # 100MB cache (balance between speed and memory)
|
||||
maxmempool=100 # 100MB mempool
|
||||
checkblocks=100 # Quick validation on startup (not full chain)
|
||||
par=1 # Single-threaded script verification (lower CPU)
|
||||
|
||||
# Network Seeds
|
||||
addnode=seed1.cryptographic-triangles.org
|
||||
addnode=seed2.cryptographic-triangles.org
|
||||
|
||||
# Relay Configuration
|
||||
# Your Pi will relay transactions and blocks for the network
|
||||
# This strengthens decentralization and helps other nodes sync
|
||||
@@ -1,8 +0,0 @@
|
||||
TRI_PI_BIND=127.0.0.1
|
||||
TRI_PI_PORT=8080
|
||||
TRI_PI_FRONTEND_DIR=/opt/tri-pi/frontend
|
||||
TRIANGLES_RPC_URL=http://127.0.0.1:19112/
|
||||
TRIANGLES_RPC_USER=tripi
|
||||
TRIANGLES_RPC_PASSWORD=replace-me
|
||||
TRIANGLES_DATA_DIR=/var/lib/triangles
|
||||
TRI_PI_TOR_HOSTNAME_FILE=/var/lib/tor/tri-pi/hostname
|
||||
@@ -1,87 +0,0 @@
|
||||
# tri-pi Architecture
|
||||
|
||||
## Goal
|
||||
|
||||
Turn a Raspberry Pi into a headless Triangles node appliance with:
|
||||
|
||||
- Tor hidden service exposure
|
||||
- safe defaults for staking
|
||||
- a local status surface
|
||||
- reproducible build and test workflows
|
||||
|
||||
## High-Level Layout
|
||||
|
||||
```text
|
||||
+---------------- Raspberry Pi ----------------+
|
||||
| |
|
||||
| trianglesd <--> Tor <--> onion service |
|
||||
| ^ |
|
||||
| | RPC |
|
||||
| v |
|
||||
| tri-pi backend <--> tri-pi frontend |
|
||||
| ^ |
|
||||
| | systemd / file state |
|
||||
| v |
|
||||
| config, logs, backups, health checks |
|
||||
+----------------------------------------------+
|
||||
```
|
||||
|
||||
## Why This Mirrors velxio
|
||||
|
||||
The inspiration from `velxio` is structural rather than product-level:
|
||||
|
||||
- keep runtime orchestration separate from the core executable
|
||||
- use a dedicated backend/frontend split
|
||||
- make local development reproducible
|
||||
- reserve QEMU for realistic target validation
|
||||
|
||||
`tri-pi` does not need a browser IDE, device simulator, or circuit canvas.
|
||||
|
||||
## Main Components
|
||||
|
||||
### Backend
|
||||
|
||||
The backend is the control plane for the appliance. It should:
|
||||
|
||||
- read node state from `trianglesd` RPC
|
||||
- read service state from `systemctl`
|
||||
- surface onion address and health checks
|
||||
- serve a small local HTTP API
|
||||
|
||||
### Frontend
|
||||
|
||||
The frontend is a local dashboard, not a wallet. It should show:
|
||||
|
||||
- sync height
|
||||
- peer count
|
||||
- staking state
|
||||
- Tor hidden service address
|
||||
- service health and recent errors
|
||||
|
||||
### Setup
|
||||
|
||||
Provisioning scripts install:
|
||||
|
||||
- OS dependencies
|
||||
- Tor
|
||||
- config templates
|
||||
- systemd units
|
||||
- backend/frontend runtime files
|
||||
|
||||
### Build
|
||||
|
||||
Build scripts support:
|
||||
|
||||
- local development against `../triangles`
|
||||
- ARM cross-builds
|
||||
- QEMU smoke tests against Raspberry Pi OS images
|
||||
|
||||
## Boundaries
|
||||
|
||||
`tri-pi` should not:
|
||||
|
||||
- carry protocol logic
|
||||
- fork `trianglesd` behavior
|
||||
- become a general wallet UI
|
||||
|
||||
It should remain an appliance layer around a separate daemon.
|
||||
@@ -1,148 +0,0 @@
|
||||
# Blockchain Bootstrap
|
||||
|
||||
TRI-PI supports **automatic blockchain bootstrapping** to dramatically speed up initial sync.
|
||||
|
||||
## What Is Bootstrap?
|
||||
|
||||
Instead of syncing from block 0 (genesis), your Pi downloads a recent snapshot of the blockchain (~1.3GB) and only syncs the remaining blocks. This reduces initial sync time from **days to hours**.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **During installation**, the installer automatically downloads the bootstrap
|
||||
2. **Extracts blockchain data** to `/var/lib/triangles/`
|
||||
3. **trianglesd starts** and syncs only recent blocks
|
||||
|
||||
## Bootstrap Source
|
||||
|
||||
Bootstrap files are hosted at:
|
||||
```
|
||||
http://194.233.88.206/triangles-bootstrap.tar.gz
|
||||
```
|
||||
|
||||
This server is maintained by the Triangles network and updated weekly with fresh blockchain snapshots.
|
||||
|
||||
## Automatic Bootstrap (Default)
|
||||
|
||||
The installer automatically bootstraps unless you disable it:
|
||||
|
||||
```bash
|
||||
# Install with bootstrap (default)
|
||||
sudo ./setup/install.sh
|
||||
|
||||
# Install WITHOUT bootstrap (sync from genesis)
|
||||
sudo BOOTSTRAP=no ./setup/install.sh
|
||||
```
|
||||
|
||||
## Manual Bootstrap
|
||||
|
||||
If you skipped bootstrap during installation or want to re-bootstrap:
|
||||
|
||||
```bash
|
||||
# Stop the daemon
|
||||
sudo systemctl stop trianglesd
|
||||
|
||||
# Run bootstrap script
|
||||
sudo bash ./setup/bootstrap.sh
|
||||
|
||||
# Start the daemon
|
||||
sudo systemctl start trianglesd
|
||||
```
|
||||
|
||||
## Bootstrap Status
|
||||
|
||||
Check if your node was bootstrapped:
|
||||
|
||||
```bash
|
||||
# Check bootstrap marker file
|
||||
ls -la /var/lib/triangles/.bootstrapped
|
||||
|
||||
# View bootstrap date
|
||||
cat /var/lib/triangles/.bootstrap-date
|
||||
```
|
||||
|
||||
## Time Savings
|
||||
|
||||
**Without bootstrap:**
|
||||
- Full sync from genesis: **2-5 days** (depending on Pi model)
|
||||
- Network bandwidth: ~3GB download over days
|
||||
- CPU intensive (validation from block 0)
|
||||
|
||||
**With bootstrap:**
|
||||
- Download: **1.3GB** (~10-30 minutes on decent connection)
|
||||
- Remaining sync: **1-6 hours** (only recent blocks)
|
||||
- Less CPU usage (fewer blocks to validate)
|
||||
|
||||
## Security
|
||||
|
||||
**Is bootstrap safe?**
|
||||
|
||||
Yes! Even with bootstrap, your node:
|
||||
- ✅ Validates all blocks after the bootstrap point
|
||||
- ✅ Verifies proof-of-work for new blocks
|
||||
- ✅ Rejects invalid transactions
|
||||
- ✅ Maintains full consensus rules
|
||||
|
||||
The bootstrap only **speeds up** getting to the current chain tip. Your node still validates everything from the bootstrap point forward.
|
||||
|
||||
## Bootstrap Updates
|
||||
|
||||
The bootstrap server automatically updates weekly:
|
||||
|
||||
- **Every Sunday at 4 AM PDT**
|
||||
- Captures latest blockchain state
|
||||
- Old bootstrap files are replaced
|
||||
- Always <1 week behind current tip
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Bootstrap download fails:**
|
||||
```bash
|
||||
# Check connectivity to bootstrap server
|
||||
curl -I http://194.233.88.206/triangles-bootstrap.tar.gz
|
||||
|
||||
# Try manual download
|
||||
wget http://194.233.88.206/triangles-bootstrap.tar.gz
|
||||
|
||||
# Skip bootstrap and sync from genesis
|
||||
BOOTSTRAP=no sudo ./setup/install.sh
|
||||
```
|
||||
|
||||
**Bootstrap extraction fails:**
|
||||
```bash
|
||||
# Check disk space
|
||||
df -h /var/lib/triangles
|
||||
|
||||
# Verify download integrity
|
||||
ls -lh /tmp/triangles-bootstrap-*/bootstrap.tar.gz
|
||||
|
||||
# Manually extract
|
||||
sudo tar -xzf bootstrap.tar.gz -C /var/lib/triangles --strip-components=1
|
||||
```
|
||||
|
||||
**Node still syncing slowly after bootstrap:**
|
||||
|
||||
This is normal! After bootstrap, your node still needs to:
|
||||
- Download remaining blocks (could be thousands)
|
||||
- Validate each block
|
||||
- Build indexes
|
||||
|
||||
Check sync progress:
|
||||
```bash
|
||||
# Via RPC
|
||||
curl --user tripi:$(grep rpcpassword /etc/triangles/triangles.conf | cut -d= -f2) \
|
||||
--data-binary '{"jsonrpc":"1.0","id":"1","method":"getblockcount","params":[]}' \
|
||||
-H 'content-type: text/plain;' http://127.0.0.1:19112/
|
||||
|
||||
# Via dashboard
|
||||
curl -s http://127.0.0.1:8080/api/status | jq
|
||||
```
|
||||
|
||||
## Custom Bootstrap Server
|
||||
|
||||
To use a different bootstrap source:
|
||||
|
||||
```bash
|
||||
BOOTSTRAP_URL=http://your-server.com/bootstrap.tar.gz sudo ./setup/install.sh
|
||||
```
|
||||
|
||||
Or edit `/setup/bootstrap.sh` and change `BOOTSTRAP_URL`.
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
# Building trianglesd v5.4.4 from Source (ARM64)
|
||||
|
||||
Complete step-by-step build instructions for ARM64 platforms (Raspberry Pi 4/5, ARM servers).
|
||||
|
||||
## Build Environment
|
||||
|
||||
**Tested on:**
|
||||
- Ubuntu 24.04 ARM64
|
||||
- Hetzner Cloud CAX11 (ARM64)
|
||||
- Raspberry Pi OS 64-bit
|
||||
|
||||
**Build time:** ~15-20 minutes (varies by CPU)
|
||||
|
||||
## Step 1: Install Dependencies
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
git build-essential libtool autotools-dev automake pkg-config \
|
||||
libssl-dev libevent-dev bsdmainutils libboost-all-dev \
|
||||
libminiupnpc-dev libzmq3-dev tor curl wget
|
||||
```
|
||||
|
||||
**Packages explained:**
|
||||
- `build-essential` - GCC, G++, make
|
||||
- `libboost-all-dev` - Boost libraries (1.83.0 on Ubuntu 24.04)
|
||||
- `libssl-dev` - OpenSSL (for crypto functions)
|
||||
- `libevent-dev` - Event notification library
|
||||
- `libminiupnpc-dev` - UPnP support
|
||||
- `libzmq3-dev` - ZeroMQ (messaging)
|
||||
- `tor` - Tor network connectivity
|
||||
|
||||
## Step 2: Build BerkeleyDB 4.8
|
||||
|
||||
trianglesd requires BerkeleyDB 4.8 for wallet compatibility.
|
||||
|
||||
```bash
|
||||
# Create build directory
|
||||
mkdir -p ~/build-deps && cd ~/build-deps
|
||||
|
||||
# Download BerkeleyDB 4.8
|
||||
wget http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz
|
||||
tar xzf db-4.8.30.NC.tar.gz
|
||||
cd db-4.8.30.NC/build_unix
|
||||
```
|
||||
|
||||
### Update config scripts for ARM64
|
||||
|
||||
The original BerkeleyDB 4.8 config scripts are too old for ARM64. Update them:
|
||||
|
||||
```bash
|
||||
wget -O ../dist/config.guess \
|
||||
'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
|
||||
|
||||
wget -O ../dist/config.sub \
|
||||
'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'
|
||||
|
||||
chmod +x ../dist/config.guess ../dist/config.sub
|
||||
```
|
||||
|
||||
### Configure and build
|
||||
|
||||
```bash
|
||||
# Configure for ARM64
|
||||
../dist/configure \
|
||||
--enable-cxx \
|
||||
--disable-shared \
|
||||
--with-pic \
|
||||
--prefix=/usr/local
|
||||
|
||||
# Build (use all CPU cores)
|
||||
make -j$(nproc)
|
||||
|
||||
# Install
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
```
|
||||
|
||||
**Verify installation:**
|
||||
```bash
|
||||
ls -l /usr/local/lib/libdb*
|
||||
# Should show libdb-4.8.a, libdb.a, libdb_cxx-4.8.a, libdb_cxx.a
|
||||
```
|
||||
|
||||
## Step 3: Clone triangles_v5
|
||||
|
||||
```bash
|
||||
cd ~
|
||||
git clone https://github.com/SamiAhmed7777/triangles_v5.git
|
||||
cd triangles_v5
|
||||
git checkout v5.4.4
|
||||
```
|
||||
|
||||
## Step 4: Build trianglesd
|
||||
|
||||
```bash
|
||||
cd src
|
||||
make -f makefile.unix -j$(nproc) USE_UPNP=1
|
||||
```
|
||||
|
||||
**Build flags:**
|
||||
- `-j$(nproc)` - Parallel compilation (uses all CPU cores)
|
||||
- `USE_UPNP=1` - Enable UPnP port mapping
|
||||
|
||||
**Build output:** `trianglesd` binary (~85MB before stripping)
|
||||
|
||||
### Common build warnings (safe to ignore)
|
||||
|
||||
- OpenSSL deprecation warnings (SHA256, RIPEMD160)
|
||||
- Boost deprecated copy warnings
|
||||
- Format string warnings
|
||||
|
||||
These are cosmetic and don't affect functionality.
|
||||
|
||||
## Step 5: Install the Binary
|
||||
|
||||
```bash
|
||||
# Strip debug symbols (reduces size to ~40MB)
|
||||
strip trianglesd
|
||||
|
||||
# Install to system
|
||||
sudo cp trianglesd /usr/local/bin/
|
||||
sudo chmod +x /usr/local/bin/trianglesd
|
||||
```
|
||||
|
||||
**Verify installation:**
|
||||
```bash
|
||||
trianglesd --version
|
||||
# Output: Triangles version v5.4.4
|
||||
|
||||
trianglesd --help | head -20
|
||||
# Should show usage information
|
||||
```
|
||||
|
||||
## Step 6: Create Configuration
|
||||
|
||||
```bash
|
||||
# Create data directory
|
||||
mkdir -p ~/.triangles
|
||||
|
||||
# Generate random RPC password
|
||||
RPC_PASS=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)
|
||||
|
||||
# Create config file
|
||||
cat > ~/.triangles/triangles.conf << CONFIG
|
||||
server=1
|
||||
daemon=1
|
||||
listen=1
|
||||
txindex=1
|
||||
|
||||
# Tor integration (managed automatically by trianglesd)
|
||||
# Seed nodes (v5.4.4)
|
||||
addnode=gxvrhv3qitnc6kobrhsrse46bmcfitnybapor3or3oczzuxn6hfzxyid.onion
|
||||
addnode=i6tk7soznftvoibtskwlezviskiererhjndpsmrff4kaxw7jnd5izfqd.onion
|
||||
addnode=uddaxjbo3lh2zskg7w6gwln4ty5cel7q4c5jbx7fdtv6zf2j47gdlyad.onion
|
||||
addnode=el5sirhhleecuctpeeprelzubpqmoqivvra3rzlwbjttinxa4fq3wnid.onion
|
||||
addnode=sj5dhybnlp3v4y5niyc5unrnd6s43lyx5ibup7rolyosjbi2u2hsbvyd.onion
|
||||
addnode=i3kr5meha7se4ns3wss3h7v46m6uksfzv4wrohdqxpj6n35wyo2bvlid.onion
|
||||
|
||||
# RPC
|
||||
rpcuser=tripi
|
||||
rpcpassword=$RPC_PASS
|
||||
rpcallowip=127.0.0.1
|
||||
rpcport=24113
|
||||
|
||||
maxconnections=125
|
||||
CONFIG
|
||||
|
||||
chmod 600 ~/.triangles/triangles.conf
|
||||
```
|
||||
|
||||
## Step 7: Enable Tor
|
||||
|
||||
```bash
|
||||
sudo systemctl enable tor
|
||||
sudo systemctl start tor
|
||||
sudo systemctl status tor
|
||||
```
|
||||
|
||||
## Step 8: Start trianglesd
|
||||
|
||||
```bash
|
||||
# Start daemon
|
||||
trianglesd
|
||||
|
||||
# Check status (wait 10-15 seconds for startup)
|
||||
trianglesd getinfo
|
||||
|
||||
# Monitor connections
|
||||
trianglesd getpeerinfo
|
||||
```
|
||||
|
||||
## Build Notes
|
||||
|
||||
### Tor Integration
|
||||
|
||||
trianglesd v5.4.4 includes:
|
||||
- **Embedded Tor management** - Starts Tor automatically
|
||||
- **SOCKS proxy** on port 19099 (internal)
|
||||
- **Onion service generation** - Creates .onion address
|
||||
- **Onion seed nodes** - Connects via Tor by default
|
||||
|
||||
**Important:** Do NOT set `onion=` or `tor=` in config. Let trianglesd manage it.
|
||||
|
||||
### Makefile Options
|
||||
|
||||
The `makefile.unix` supports several options:
|
||||
|
||||
```bash
|
||||
# Basic build
|
||||
make -f makefile.unix
|
||||
|
||||
# With UPnP
|
||||
make -f makefile.unix USE_UPNP=1
|
||||
|
||||
# With IPv6
|
||||
make -f makefile.unix USE_IPV6=1
|
||||
|
||||
# Debug build
|
||||
make -f makefile.unix DEBUG=1
|
||||
|
||||
# Clean
|
||||
make -f makefile.unix clean
|
||||
```
|
||||
|
||||
### Cross-Compilation
|
||||
|
||||
To cross-compile for ARM64 from x86_64:
|
||||
|
||||
```bash
|
||||
# Install cross-compiler
|
||||
sudo apt-get install g++-aarch64-linux-gnu
|
||||
|
||||
# Modify makefile or use environment variables
|
||||
CXX=aarch64-linux-gnu-g++ \
|
||||
make -f makefile.unix \
|
||||
USE_UPNP=1
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### BerkeleyDB not found
|
||||
|
||||
If compilation fails with "db_cxx.h not found":
|
||||
|
||||
```bash
|
||||
# Check installation
|
||||
ls -l /usr/local/include/db_cxx.h
|
||||
ls -l /usr/local/lib/libdb_cxx.a
|
||||
|
||||
# Ensure ldconfig was run
|
||||
sudo ldconfig
|
||||
```
|
||||
|
||||
### Boost version mismatch
|
||||
|
||||
Ubuntu 24.04 uses Boost 1.83. Older systems may have issues. Ensure `libboost-all-dev` is installed.
|
||||
|
||||
### OpenSSL warnings
|
||||
|
||||
Deprecated API warnings are safe to ignore. trianglesd uses older OpenSSL APIs but they still work.
|
||||
|
||||
## Build Artifacts
|
||||
|
||||
After successful build:
|
||||
|
||||
- **Binary:** `src/trianglesd` (~85MB, ~40MB stripped)
|
||||
- **LevelDB:** `src/leveldb/libleveldb.a`, `src/leveldb/libmemenv.a`
|
||||
- **Object files:** `src/obj/*.o`
|
||||
|
||||
Clean with:
|
||||
```bash
|
||||
make -f makefile.unix clean
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Build time breakdown** (4-core ARM server):
|
||||
- BerkeleyDB: ~5 minutes
|
||||
- trianglesd: ~10 minutes
|
||||
- **Total: ~15 minutes**
|
||||
@@ -1,46 +0,0 @@
|
||||
# Development
|
||||
|
||||
## Workspace Layout
|
||||
|
||||
Recommended local layout:
|
||||
|
||||
```text
|
||||
e:/repos/
|
||||
├── triangles/
|
||||
└── tri-pi/
|
||||
```
|
||||
|
||||
## Local Backend Run
|
||||
|
||||
```bash
|
||||
python backend/app.py
|
||||
```
|
||||
|
||||
Open `http://127.0.0.1:8080/`.
|
||||
|
||||
## Expected Environment Variables
|
||||
|
||||
The backend reads these variables:
|
||||
|
||||
- `TRI_PI_FRONTEND_DIR`
|
||||
- `TRIANGLES_RPC_URL`
|
||||
- `TRIANGLES_RPC_USER`
|
||||
- `TRIANGLES_RPC_PASSWORD`
|
||||
- `TRIANGLES_DATA_DIR`
|
||||
- `TRI_PI_TOR_HOSTNAME_FILE`
|
||||
|
||||
Defaults are provided in `config/tripi-backend.env.example`.
|
||||
|
||||
## Build Direction
|
||||
|
||||
Short term:
|
||||
|
||||
- use `../triangles` as the source tree
|
||||
- build `trianglesd` separately
|
||||
- let `tri-pi` manage deployment around the binary
|
||||
|
||||
Long term:
|
||||
|
||||
- add containerized cross-build support
|
||||
- add QEMU-based integration tests
|
||||
- publish appliance releases with pinned daemon artifacts
|
||||
@@ -1,108 +0,0 @@
|
||||
# TRI-PI as a Relay Node
|
||||
|
||||
Your Raspberry Pi runs a **full Triangles node** that strengthens the network by:
|
||||
- Accepting incoming connections from other nodes
|
||||
- Relaying transactions across the network
|
||||
- Relaying blocks to help nodes sync
|
||||
- Operating entirely over Tor for privacy and accessibility
|
||||
|
||||
## What It Does
|
||||
|
||||
Each TRI-PI device:
|
||||
1. **Syncs the blockchain** (~3GB, one-time)
|
||||
2. **Creates a Tor hidden service** (.onion address)
|
||||
3. **Accepts connections** from other Triangles nodes
|
||||
4. **Relays transactions** submitted by other wallets
|
||||
5. **Relays blocks** to help new nodes sync
|
||||
6. **Stakes coins** (optional, earns rewards)
|
||||
|
||||
## Network Architecture
|
||||
|
||||
```
|
||||
[Tor Network]
|
||||
|
|
||||
┌────────┼────────┐
|
||||
| | |
|
||||
Pi Node 1 Pi Node 2 Pi Node 3
|
||||
abc.onion def.onion ghi.onion
|
||||
| | |
|
||||
[Relay] [Relay] [Relay]
|
||||
└────────┴────────┘
|
||||
Strengthen Network
|
||||
```
|
||||
|
||||
## Resource Usage
|
||||
|
||||
**Disk:** ~3-4GB (blockchain + indexes)
|
||||
**RAM:** ~200-400MB (with 100MB cache)
|
||||
**CPU:** Low (single-threaded validation)
|
||||
**Network:** Variable (depends on peer count)
|
||||
|
||||
## Benefits of Running Multiple Pis
|
||||
|
||||
1. **Network decentralization** - More nodes = stronger network
|
||||
2. **Geographic distribution** - Deploy worldwide via Tor
|
||||
3. **Redundancy** - Network survives if some nodes go offline
|
||||
4. **Privacy** - All connections over Tor onion routing
|
||||
5. **Censorship resistance** - No public IP, no ISP blocking
|
||||
|
||||
## How Other Nodes Connect
|
||||
|
||||
Your Pi's `.onion` address is automatically:
|
||||
- Generated by Tor when first started
|
||||
- Advertised to the Triangles network
|
||||
- Added to peer discovery mechanisms
|
||||
|
||||
Other nodes can:
|
||||
- Discover your Pi through peer exchange
|
||||
- Connect directly via your .onion address
|
||||
- Request blocks and transactions from you
|
||||
- Relay their own transactions through you
|
||||
|
||||
## Configuration
|
||||
|
||||
See `/etc/triangles/triangles.conf`:
|
||||
|
||||
```conf
|
||||
listen=1 # Accept incoming connections
|
||||
maxconnections=32 # Support up to 32 peers
|
||||
torhiddenservice=1 # Create .onion address
|
||||
onlynet=tor # Only Tor connections
|
||||
```
|
||||
|
||||
## Finding Your Onion Address
|
||||
|
||||
After Tor starts, your `.onion` address is available at:
|
||||
|
||||
```bash
|
||||
# Via the web dashboard
|
||||
curl -s http://127.0.0.1:8080/api/status | jq -r '.onion'
|
||||
|
||||
# Or directly from the file
|
||||
cat /var/lib/tor/tri-pi/hostname
|
||||
```
|
||||
|
||||
## Adding Your Pi to Other Nodes
|
||||
|
||||
To manually connect other Triangles nodes to your Pi:
|
||||
|
||||
```conf
|
||||
# In their triangles.conf
|
||||
addnode=your-pi-address.onion
|
||||
```
|
||||
|
||||
## Performance Expectations
|
||||
|
||||
**Initial sync:** 2-5 days (depending on Pi model and network)
|
||||
**Daily sync:** ~1-10 minutes (catching up new blocks)
|
||||
**Relay load:** Minimal (a few KB/s typically)
|
||||
|
||||
## Scaling the Network
|
||||
|
||||
Deploy multiple Pis to create a **resilient relay infrastructure**:
|
||||
- Each Pi syncs independently
|
||||
- Each Pi has its own wallet
|
||||
- Each Pi strengthens the network
|
||||
- Total decentralization over Tor
|
||||
|
||||
No central point of failure!
|
||||
@@ -1,29 +0,0 @@
|
||||
async function refreshStatus() {
|
||||
const errors = document.getElementById("errors");
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/status", { cache: "no-store" });
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
document.getElementById("reachable").textContent = String(data.node.reachable);
|
||||
document.getElementById("block-height").textContent = valueOrDash(data.node.block_height);
|
||||
document.getElementById("connections").textContent = valueOrDash(data.node.connections);
|
||||
document.getElementById("staking").textContent = valueOrDash(data.node.staking);
|
||||
document.getElementById("trianglesd-status").textContent = data.services.trianglesd;
|
||||
document.getElementById("tor-status").textContent = data.services.tor;
|
||||
document.getElementById("onion-address").textContent = data.tor.onion_address || "Not available";
|
||||
errors.textContent = data.node.errors.length ? data.node.errors.join("\n") : "No errors reported.";
|
||||
} catch (error) {
|
||||
errors.textContent = `Failed to load status: ${error.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
function valueOrDash(value) {
|
||||
return value === null || value === undefined ? "-" : String(value);
|
||||
}
|
||||
|
||||
refreshStatus();
|
||||
setInterval(refreshStatus, 5000);
|
||||
@@ -1,73 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>tri-pi status</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="page">
|
||||
<section class="hero">
|
||||
<p class="eyebrow">Triangles appliance</p>
|
||||
<h1>tri-pi</h1>
|
||||
<p class="lede">Local status for a Raspberry Pi staking node running over Tor.</p>
|
||||
</section>
|
||||
|
||||
<section class="grid">
|
||||
<article class="card">
|
||||
<h2>Node</h2>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>Reachable</dt>
|
||||
<dd id="reachable">Unknown</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Block height</dt>
|
||||
<dd id="block-height">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Peers</dt>
|
||||
<dd id="connections">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Staking</dt>
|
||||
<dd id="staking">-</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article class="card">
|
||||
<h2>Services</h2>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>trianglesd</dt>
|
||||
<dd id="trianglesd-status">-</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>tor</dt>
|
||||
<dd id="tor-status">-</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
|
||||
<article class="card card-wide">
|
||||
<h2>Tor</h2>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>Onion address</dt>
|
||||
<dd id="onion-address">Not available</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<p class="muted">The onion hostname appears after Tor has created the hidden service.</p>
|
||||
</article>
|
||||
|
||||
<article class="card card-wide">
|
||||
<h2>Errors</h2>
|
||||
<pre id="errors">No errors reported.</pre>
|
||||
</article>
|
||||
</section>
|
||||
</main>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,121 +0,0 @@
|
||||
:root {
|
||||
--bg: #f4f0e8;
|
||||
--panel: rgba(255, 252, 247, 0.9);
|
||||
--ink: #1f2a1f;
|
||||
--muted: #54624f;
|
||||
--line: #c8baa4;
|
||||
--accent: #2f6b4f;
|
||||
--accent-soft: #d8e6dc;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
color: var(--ink);
|
||||
background:
|
||||
radial-gradient(circle at top left, #fff8ef 0, transparent 35%),
|
||||
linear-gradient(160deg, #e9e0d0 0%, #f5f2ea 48%, #e6ece8 100%);
|
||||
}
|
||||
|
||||
.page {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 20px 64px;
|
||||
}
|
||||
|
||||
.hero {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
margin: 0 0 8px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.18em;
|
||||
color: var(--accent);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: clamp(44px, 10vw, 82px);
|
||||
line-height: 0.95;
|
||||
}
|
||||
|
||||
.lede {
|
||||
max-width: 700px;
|
||||
margin-top: 12px;
|
||||
color: var(--muted);
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 20px;
|
||||
padding: 20px;
|
||||
backdrop-filter: blur(10px);
|
||||
box-shadow: 0 16px 40px rgba(51, 45, 33, 0.08);
|
||||
}
|
||||
|
||||
.card-wide {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
dl div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
dl div:first-child {
|
||||
border-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
dd {
|
||||
margin: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.muted,
|
||||
pre {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.card-wide {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
# Image
|
||||
|
||||
This directory is reserved for Raspberry Pi OS image customization.
|
||||
|
||||
Planned contents:
|
||||
|
||||
- first-boot provisioning hooks
|
||||
- preseeded service enablement
|
||||
- image build helpers
|
||||
|
||||
For now, `tri-pi` assumes manual installation on an existing Pi OS system.
|
||||
Executable
+181
@@ -0,0 +1,181 @@
|
||||
#!/bin/bash
|
||||
# TRI-PI ARM64 Installation Script
|
||||
# For Raspberry Pi 4/5 (64-bit) and ARM64 servers
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
VERSION=$(cat "$SCRIPT_DIR/VERSION" 2>/dev/null || echo "unknown")
|
||||
|
||||
echo "====================================="
|
||||
echo " TRI-PI $VERSION ARM64 Installer"
|
||||
echo "====================================="
|
||||
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
echo "Error: This script must be run as root (use sudo)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect architecture
|
||||
ARCH=$(uname -m)
|
||||
if [ "$ARCH" != "aarch64" ]; then
|
||||
echo "Error: This package is for ARM64 (aarch64) only."
|
||||
echo "Detected architecture: $ARCH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ ARM64 architecture detected"
|
||||
|
||||
# Install dependencies
|
||||
echo "Installing dependencies..."
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq tor curl > /dev/null 2>&1
|
||||
|
||||
echo "✓ Dependencies installed"
|
||||
|
||||
# Install binary
|
||||
echo "Installing trianglesd..."
|
||||
cp "$SCRIPT_DIR/bin/trianglesd" /usr/local/bin/
|
||||
chmod +x /usr/local/bin/trianglesd
|
||||
|
||||
echo "✓ Binary installed to /usr/local/bin/trianglesd"
|
||||
|
||||
# Determine data directory
|
||||
DATA_DIR="/root/.triangles"
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# Create config
|
||||
if [ ! -f "$DATA_DIR/triangles.conf" ]; then
|
||||
echo "Creating default configuration..."
|
||||
|
||||
RPC_PASS=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)
|
||||
|
||||
cat > "$DATA_DIR/triangles.conf" << CONFIG
|
||||
# TRI-PI $VERSION Configuration
|
||||
server=1
|
||||
listen=1
|
||||
|
||||
# RPC settings
|
||||
rpcuser=tripi
|
||||
rpcpassword=$RPC_PASS
|
||||
rpcallowip=127.0.0.1
|
||||
rpcport=19199
|
||||
|
||||
# Network
|
||||
port=24112
|
||||
maxconnections=50
|
||||
|
||||
# Performance tuning for ARM/Pi
|
||||
dbcache=100
|
||||
maxmempool=50
|
||||
|
||||
# Seed nodes
|
||||
addnode=194.233.88.206
|
||||
addnode=74.208.167.19
|
||||
addnode=179.189.35.51
|
||||
CONFIG
|
||||
|
||||
chmod 600 "$DATA_DIR/triangles.conf"
|
||||
echo "✓ Configuration created at $DATA_DIR/triangles.conf"
|
||||
else
|
||||
echo "✓ Existing configuration found"
|
||||
fi
|
||||
|
||||
# Install systemd service
|
||||
echo "Installing systemd service..."
|
||||
cat > /etc/systemd/system/triangles.service << SERVICE
|
||||
[Unit]
|
||||
Description=Triangles Cryptocurrency Node
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/trianglesd -daemon=0 -datadir=$DATA_DIR
|
||||
ExecStop=/usr/local/bin/trianglesd -datadir=$DATA_DIR stop
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
TimeoutStopSec=120
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SERVICE
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable triangles > /dev/null 2>&1
|
||||
echo "✓ Systemd service installed and enabled"
|
||||
|
||||
# Offer blockchain bootstrap
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " Blockchain Sync Options"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
if [ -f "$DATA_DIR/blk0001.dat" ]; then
|
||||
EXISTING_SIZE=$(du -sh "$DATA_DIR/blk0001.dat" | cut -f1)
|
||||
echo " Existing blockchain data found ($EXISTING_SIZE)"
|
||||
echo ""
|
||||
read -p " Skip bootstrap and keep existing data? [Y/n]: " KEEP_EXISTING
|
||||
if [[ "$KEEP_EXISTING" =~ ^[Nn] ]]; then
|
||||
DO_BOOTSTRAP=1
|
||||
else
|
||||
DO_BOOTSTRAP=0
|
||||
fi
|
||||
else
|
||||
echo " [1] Download bootstrap blockchain (FAST — recommended)"
|
||||
echo " ~1.3GB download, starts near current block height"
|
||||
echo ""
|
||||
echo " [2] Sync from scratch (SLOW — days/weeks on Pi)"
|
||||
echo " Start from genesis block"
|
||||
echo ""
|
||||
read -p " Your choice [1/2]: " SYNC_CHOICE
|
||||
[[ "$SYNC_CHOICE" == "1" ]] && DO_BOOTSTRAP=1 || DO_BOOTSTRAP=0
|
||||
fi
|
||||
|
||||
if [ "$DO_BOOTSTRAP" -eq 1 ]; then
|
||||
BOOTSTRAP_URL="http://74.208.167.19/triangles-bootstrap.tar.gz"
|
||||
echo ""
|
||||
echo "⬇️ Downloading blockchain bootstrap (~1.3GB)..."
|
||||
echo " This will save days of initial sync time."
|
||||
echo ""
|
||||
|
||||
if curl -L --connect-timeout 30 --max-time 600 -o /tmp/triangles-bootstrap.tar.gz "$BOOTSTRAP_URL" 2>/dev/null; then
|
||||
DL_SIZE=$(du -sh /tmp/triangles-bootstrap.tar.gz | cut -f1)
|
||||
echo "✓ Downloaded ($DL_SIZE)"
|
||||
echo "📦 Extracting to $DATA_DIR/ ..."
|
||||
|
||||
# Remove old blockchain data before extracting
|
||||
rm -rf "$DATA_DIR/blk0001.dat" "$DATA_DIR/txleveldb" "$DATA_DIR/database"
|
||||
tar xzf /tmp/triangles-bootstrap.tar.gz -C "$DATA_DIR/"
|
||||
rm -f /tmp/triangles-bootstrap.tar.gz
|
||||
|
||||
echo "✓ Blockchain bootstrap deployed"
|
||||
else
|
||||
echo "⚠️ Bootstrap download failed (server unreachable)"
|
||||
echo " No worries — your node will sync from peers instead."
|
||||
echo " Tip: you can manually scp a bootstrap later."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "====================================="
|
||||
echo " ✅ TRI-PI $VERSION Installed!"
|
||||
echo "====================================="
|
||||
echo ""
|
||||
echo "🚀 Start your node:"
|
||||
echo " sudo systemctl start triangles"
|
||||
echo ""
|
||||
echo "📊 Check status:"
|
||||
echo " trianglesd -datadir=$DATA_DIR getinfo"
|
||||
echo ""
|
||||
echo "📋 View logs:"
|
||||
echo " journalctl -u triangles -f"
|
||||
echo " tail -f $DATA_DIR/debug.log"
|
||||
echo ""
|
||||
echo "🧅 Tor onion address (generated on first run):"
|
||||
echo " cat $DATA_DIR/onion/hostname"
|
||||
echo ""
|
||||
echo "🔄 The node will auto-start on boot."
|
||||
echo ""
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
07d64b0bcb534e3486a2d4aead8883a28aab5e16b4e24e75ef65df86f9e136b2 tri-pi-v5.5.0-arm64.tar.gz
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
d8575816928c67d65929944b2c0bda394ee058b1acfb7cdd3fd3fe40bbb63ddf tri-pi-v5.5.1-arm64.tar.gz
|
||||
Executable
BIN
Binary file not shown.
@@ -1,16 +0,0 @@
|
||||
[Unit]
|
||||
Description=Triangles daemon
|
||||
After=network-online.target tor.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=triangles
|
||||
Group=triangles
|
||||
ExecStart=/usr/local/bin/trianglesd -conf=/etc/triangles/triangles.conf -datadir=/var/lib/triangles
|
||||
ExecStop=/usr/local/bin/trianglesd stop
|
||||
Restart=on-failure
|
||||
TimeoutStopSec=60
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,16 +0,0 @@
|
||||
[Unit]
|
||||
Description=tri-pi local status backend
|
||||
After=network-online.target trianglesd.service tor.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=triangles
|
||||
Group=triangles
|
||||
WorkingDirectory=/opt/tri-pi
|
||||
EnvironmentFile=/etc/default/tripi-backend
|
||||
ExecStart=/usr/bin/python3 /opt/tri-pi/backend/app.py
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Download and extract Triangles blockchain bootstrap
|
||||
# This dramatically speeds up initial sync (hours instead of days)
|
||||
|
||||
BOOTSTRAP_URL="${BOOTSTRAP_URL:-http://194.233.88.206/triangles-bootstrap.tar.gz}"
|
||||
DATA_DIR="${DATA_DIR:-/var/lib/triangles}"
|
||||
TEMP_DIR="/tmp/triangles-bootstrap-$$"
|
||||
|
||||
log() { echo "[tri-pi:bootstrap] $*"; }
|
||||
die() { echo "[tri-pi:bootstrap] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# Check if already bootstrapped
|
||||
if [[ -f "$DATA_DIR/.bootstrapped" ]]; then
|
||||
log "Bootstrap already applied. Skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check if daemon is running
|
||||
if pgrep -x trianglesd >/dev/null; then
|
||||
die "trianglesd is running. Stop it first: systemctl stop trianglesd"
|
||||
fi
|
||||
|
||||
log "Downloading bootstrap from $BOOTSTRAP_URL..."
|
||||
log "This may take several minutes (~1.3GB download)..."
|
||||
|
||||
mkdir -p "$TEMP_DIR"
|
||||
trap "rm -rf '$TEMP_DIR'" EXIT
|
||||
|
||||
# Download with progress
|
||||
if ! curl -#fSL "$BOOTSTRAP_URL" -o "$TEMP_DIR/bootstrap.tar.gz"; then
|
||||
die "Failed to download bootstrap file"
|
||||
fi
|
||||
|
||||
log "Verifying download..."
|
||||
DOWNLOAD_SIZE=$(stat -c%s "$TEMP_DIR/bootstrap.tar.gz")
|
||||
if [[ $DOWNLOAD_SIZE -lt 100000000 ]]; then
|
||||
die "Download too small ($DOWNLOAD_SIZE bytes), possibly corrupted"
|
||||
fi
|
||||
|
||||
log "Extracting bootstrap to $DATA_DIR..."
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
if ! tar -xzf "$TEMP_DIR/bootstrap.tar.gz" -C "$DATA_DIR" --strip-components=1; then
|
||||
die "Failed to extract bootstrap"
|
||||
fi
|
||||
|
||||
# Fix ownership (in case we're running as root)
|
||||
if [[ -n "${SUDO_USER:-}" ]]; then
|
||||
chown -R triangles:triangles "$DATA_DIR"
|
||||
fi
|
||||
|
||||
# Mark as bootstrapped
|
||||
touch "$DATA_DIR/.bootstrapped"
|
||||
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$DATA_DIR/.bootstrap-date"
|
||||
|
||||
log ""
|
||||
log "Bootstrap complete!"
|
||||
log " Data directory: $DATA_DIR"
|
||||
log " Bootstrap date: $(cat "$DATA_DIR/.bootstrap-date")"
|
||||
log ""
|
||||
log "You can now start trianglesd. It will sync only blocks since the bootstrap."
|
||||
log " systemctl start trianglesd"
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Configure a Tor v3 hidden service for the Triangles node.
|
||||
# Called by install.sh, but can also be run standalone.
|
||||
|
||||
TORRC_DIR="${TORRC_DIR:-/etc/tor/torrc.d}"
|
||||
TORRC_PATH="${TORRC_PATH:-$TORRC_DIR/tri-pi.conf}"
|
||||
HIDDEN_SERVICE_DIR="${HIDDEN_SERVICE_DIR:-/var/lib/tor/tri-pi}"
|
||||
NODE_PORT="${NODE_PORT:-24112}"
|
||||
TOR_USER="${TOR_USER:-debian-tor}"
|
||||
|
||||
log() { echo "[tri-pi:tor] $*"; }
|
||||
|
||||
# ── torrc drop-in directory ──────────────────────────────────────────────────
|
||||
|
||||
if [[ ! -d "$TORRC_DIR" ]]; then
|
||||
log "Creating $TORRC_DIR..."
|
||||
install -d -o root -g root -m 0755 "$TORRC_DIR"
|
||||
fi
|
||||
|
||||
# Ensure the main torrc includes our drop-in directory
|
||||
MAIN_TORRC="/etc/tor/torrc"
|
||||
if [[ -f "$MAIN_TORRC" ]] && ! grep -q "%include $TORRC_DIR" "$MAIN_TORRC" 2>/dev/null; then
|
||||
log "Adding include directive to $MAIN_TORRC..."
|
||||
printf '\n# tri-pi: include drop-in config directory\n%%include %s\n' "$TORRC_DIR" >> "$MAIN_TORRC"
|
||||
fi
|
||||
|
||||
# ── hidden service config ────────────────────────────────────────────────────
|
||||
|
||||
log "Writing Tor hidden service config to $TORRC_PATH..."
|
||||
cat > "$TORRC_PATH" <<EOF
|
||||
# tri-pi Triangles hidden service
|
||||
HiddenServiceDir $HIDDEN_SERVICE_DIR
|
||||
HiddenServiceVersion 3
|
||||
HiddenServicePort $NODE_PORT 127.0.0.1:$NODE_PORT
|
||||
EOF
|
||||
chmod 0644 "$TORRC_PATH"
|
||||
|
||||
# ── hidden service directory ─────────────────────────────────────────────────
|
||||
|
||||
if [[ ! -d "$HIDDEN_SERVICE_DIR" ]]; then
|
||||
log "Creating hidden service directory..."
|
||||
install -d -o "$TOR_USER" -g "$TOR_USER" -m 0700 "$HIDDEN_SERVICE_DIR"
|
||||
fi
|
||||
|
||||
log "Tor hidden service configured."
|
||||
log " Config: $TORRC_PATH"
|
||||
log " HS dir: $HIDDEN_SERVICE_DIR"
|
||||
log " Port: $NODE_PORT -> 127.0.0.1:$NODE_PORT"
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEMPLATE="${TEMPLATE:-$SCRIPT_DIR/../config/triangles.conf.template}"
|
||||
OUTPUT="${OUTPUT:-./triangles.conf}"
|
||||
|
||||
PASSWORD="$(tr -dc 'A-Za-z0-9' </dev/urandom | head -c 32)"
|
||||
sed "s/^rpcpassword=.*/rpcpassword=$PASSWORD/" "$TEMPLATE" >"$OUTPUT"
|
||||
|
||||
echo "Generated $OUTPUT"
|
||||
@@ -1,174 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# tri-pi installer for Raspberry Pi OS Bookworm
|
||||
# Installs Tor, the status backend/frontend, and wires up systemd services.
|
||||
#
|
||||
# Usage:
|
||||
# sudo ./setup/install.sh
|
||||
# TRIANGLESD_BIN=/path/to/trianglesd sudo ./setup/install.sh
|
||||
|
||||
PREFIX="${PREFIX:-/opt/tri-pi}"
|
||||
CONF_DIR="${CONF_DIR:-/etc/triangles}"
|
||||
DATA_DIR="${DATA_DIR:-/var/lib/triangles}"
|
||||
TOR_HS_DIR="${TOR_HS_DIR:-/var/lib/tor/tri-pi}"
|
||||
TRIANGLESD_BIN="${TRIANGLESD_BIN:-}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
log() { echo "[tri-pi] $*"; }
|
||||
die() { echo "[tri-pi] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# ── pre-flight ────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
die "This script must be run as root (try: sudo $0)"
|
||||
fi
|
||||
|
||||
if ! grep -qiE 'bookworm|debian|raspbian' /etc/os-release 2>/dev/null; then
|
||||
log "WARNING: This installer targets Raspberry Pi OS Bookworm."
|
||||
log "Detected OS may not be compatible. Continuing anyway..."
|
||||
fi
|
||||
|
||||
# ── system dependencies ──────────────────────────────────────────────────────
|
||||
|
||||
log "Installing system dependencies..."
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq tor python3
|
||||
|
||||
# ── system user ──────────────────────────────────────────────────────────────
|
||||
|
||||
if ! id -u triangles &>/dev/null; then
|
||||
log "Creating system user 'triangles'..."
|
||||
useradd --system --home-dir "$DATA_DIR" --shell /usr/sbin/nologin triangles
|
||||
else
|
||||
log "User 'triangles' already exists."
|
||||
fi
|
||||
|
||||
# ── directories ──────────────────────────────────────────────────────────────
|
||||
|
||||
log "Creating directories..."
|
||||
install -d -o triangles -g triangles -m 0750 "$DATA_DIR"
|
||||
install -d -o root -g root -m 0755 "$CONF_DIR"
|
||||
install -d -o root -g root -m 0755 "$PREFIX"
|
||||
install -d -o root -g root -m 0755 "$PREFIX/backend"
|
||||
install -d -o root -g root -m 0755 "$PREFIX/frontend"
|
||||
|
||||
# ── trianglesd binary ────────────────────────────────────────────────────────
|
||||
|
||||
if [[ -n "$TRIANGLESD_BIN" ]]; then
|
||||
[[ -f "$TRIANGLESD_BIN" ]] || die "TRIANGLESD_BIN set but file not found: $TRIANGLESD_BIN"
|
||||
log "Installing trianglesd from $TRIANGLESD_BIN..."
|
||||
install -o root -g root -m 0755 "$TRIANGLESD_BIN" /usr/local/bin/trianglesd
|
||||
elif [[ -f /usr/local/bin/trianglesd ]]; then
|
||||
log "trianglesd already present at /usr/local/bin/trianglesd."
|
||||
else
|
||||
log "WARNING: No trianglesd binary found."
|
||||
log " Set TRIANGLESD_BIN=/path/to/trianglesd or install it manually."
|
||||
log " Continuing without the daemon binary..."
|
||||
fi
|
||||
|
||||
# ── triangles.conf ───────────────────────────────────────────────────────────
|
||||
|
||||
if [[ -f "$CONF_DIR/triangles.conf" ]]; then
|
||||
log "triangles.conf already exists, preserving it."
|
||||
RPC_PASSWORD="$(grep '^rpcpassword=' "$CONF_DIR/triangles.conf" | cut -d= -f2)"
|
||||
else
|
||||
log "Generating triangles.conf with random RPC password..."
|
||||
# Disable pipefail temporarily to avoid SIGPIPE from head closing the pipe
|
||||
set +o pipefail
|
||||
RPC_PASSWORD="$(head -c 256 /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 32)"
|
||||
set -o pipefail
|
||||
sed "s/^rpcpassword=.*/rpcpassword=$RPC_PASSWORD/" \
|
||||
"$REPO_DIR/config/triangles.conf.template" > "$CONF_DIR/triangles.conf"
|
||||
chmod 0640 "$CONF_DIR/triangles.conf"
|
||||
chown root:triangles "$CONF_DIR/triangles.conf"
|
||||
fi
|
||||
|
||||
# ── backend & frontend assets ────────────────────────────────────────────────
|
||||
|
||||
log "Installing backend and frontend to $PREFIX..."
|
||||
install -m 0644 "$REPO_DIR/backend/app.py" "$PREFIX/backend/app.py"
|
||||
install -m 0644 "$REPO_DIR/frontend/index.html" "$PREFIX/frontend/index.html"
|
||||
install -m 0644 "$REPO_DIR/frontend/app.js" "$PREFIX/frontend/app.js"
|
||||
install -m 0644 "$REPO_DIR/frontend/styles.css" "$PREFIX/frontend/styles.css"
|
||||
|
||||
# ── Tor hidden service ───────────────────────────────────────────────────────
|
||||
|
||||
log "Configuring Tor hidden service..."
|
||||
bash "$SCRIPT_DIR/configure-tor.sh"
|
||||
|
||||
# ── backend environment file ─────────────────────────────────────────────────
|
||||
|
||||
log "Writing /etc/default/tripi-backend..."
|
||||
cat > /etc/default/tripi-backend <<ENVEOF
|
||||
TRI_PI_BIND=127.0.0.1
|
||||
TRI_PI_PORT=8080
|
||||
TRI_PI_FRONTEND_DIR=$PREFIX/frontend
|
||||
TRIANGLES_RPC_URL=http://127.0.0.1:19112/
|
||||
TRIANGLES_RPC_USER=tripi
|
||||
TRIANGLES_RPC_PASSWORD=${RPC_PASSWORD:-replace-me}
|
||||
TRIANGLES_DATA_DIR=$DATA_DIR
|
||||
TRI_PI_TOR_HOSTNAME_FILE=$TOR_HS_DIR/hostname
|
||||
ENVEOF
|
||||
chmod 0640 /etc/default/tripi-backend
|
||||
chown root:triangles /etc/default/tripi-backend
|
||||
|
||||
# ── systemd units ────────────────────────────────────────────────────────────
|
||||
|
||||
log "Installing systemd service files..."
|
||||
install -m 0644 "$REPO_DIR/services/trianglesd.service" /etc/systemd/system/trianglesd.service
|
||||
install -m 0644 "$REPO_DIR/services/tripi-backend.service" /etc/systemd/system/tripi-backend.service
|
||||
systemctl daemon-reload || log "Warning: systemctl daemon-reload failed (non-systemd environment)"
|
||||
|
||||
# ── enable & start ───────────────────────────────────────────────────────────
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1 && systemctl is-system-running >/dev/null 2>&1; then
|
||||
log "Enabling services..."
|
||||
systemctl enable tor.service
|
||||
systemctl enable tripi-backend.service
|
||||
[[ -f /usr/local/bin/trianglesd ]] && systemctl enable trianglesd.service
|
||||
|
||||
log "Starting services..."
|
||||
systemctl restart tor.service
|
||||
systemctl start tripi-backend.service
|
||||
if [[ -f /usr/local/bin/trianglesd ]]; then
|
||||
# Optionally bootstrap the blockchain before starting
|
||||
if [[ "${BOOTSTRAP:-yes}" == "yes" ]] && [[ ! -f "$DATA_DIR/.bootstrapped" ]]; then
|
||||
log "Downloading blockchain bootstrap (speeds up initial sync)..."
|
||||
log "Set BOOTSTRAP=no to skip this step."
|
||||
bash "$SCRIPT_DIR/bootstrap.sh" || log "Warning: Bootstrap failed, will sync from genesis"
|
||||
fi
|
||||
systemctl start trianglesd.service
|
||||
else
|
||||
log "Skipping trianglesd start (binary not installed)."
|
||||
fi
|
||||
else
|
||||
log "Skipping service enable/start (systemd not available or not running as init)."
|
||||
fi
|
||||
|
||||
# ── summary ──────────────────────────────────────────────────────────────────
|
||||
|
||||
log ""
|
||||
log "Installation complete!"
|
||||
log ""
|
||||
log " Dashboard: http://127.0.0.1:8080"
|
||||
log " Config: $CONF_DIR/triangles.conf"
|
||||
log " Data: $DATA_DIR"
|
||||
log " Onion: $TOR_HS_DIR/hostname (available after Tor starts)"
|
||||
log ""
|
||||
if [[ -f "$DATA_DIR/.bootstrapped" ]]; then
|
||||
BOOTSTRAP_DATE=$(cat "$DATA_DIR/.bootstrap-date" 2>/dev/null || echo "unknown")
|
||||
log " Bootstrap: Applied ($BOOTSTRAP_DATE)"
|
||||
log " Node will sync only recent blocks (faster!)"
|
||||
log ""
|
||||
fi
|
||||
log "Useful commands:"
|
||||
log " systemctl status trianglesd tripi-backend tor"
|
||||
log " journalctl -u trianglesd -f"
|
||||
log " curl -s http://127.0.0.1:8080/api/status | python3 -m json.tool"
|
||||
log ""
|
||||
log "To manually bootstrap (if skipped):"
|
||||
log " systemctl stop trianglesd"
|
||||
log " sudo bash $SCRIPT_DIR/bootstrap.sh"
|
||||
log " systemctl start trianglesd"
|
||||
@@ -1,4 +0,0 @@
|
||||
# tri-pi hidden service
|
||||
HiddenServiceDir /var/lib/tor/tri-pi
|
||||
HiddenServiceVersion 3
|
||||
HiddenServicePort 24112 127.0.0.1:24112
|
||||
@@ -1,123 +0,0 @@
|
||||
# TRI-PI Velxio Test
|
||||
|
||||
This directory contains a Velxio project for testing the TRI-PI installer on a Raspberry Pi 3 emulator.
|
||||
|
||||
## What is Velxio?
|
||||
|
||||
Velxio is a web-based multi-board emulator that can run Raspberry Pi 3 with full Raspberry Pi OS in QEMU. It's perfect for testing TRI-PI without physical hardware.
|
||||
|
||||
**Live at:** [velxio.dev](https://velxio.dev)
|
||||
|
||||
## Quick Start (Web UI)
|
||||
|
||||
### Option 1: Use Velxio Cloud
|
||||
|
||||
1. Go to [velxio.dev](https://velxio.dev)
|
||||
2. Click "New Project"
|
||||
3. Select "Raspberry Pi 3B" board
|
||||
4. In the file editor, create `/home/pi/test_installer.py` with the test script (see below)
|
||||
5. Copy the TRI-PI source files to `/opt/tri-pi-src` (via VFS or by modifying the kernel image)
|
||||
6. Click "Run" and watch the Serial Monitor for test results
|
||||
|
||||
### Option 2: Self-Host Velxio
|
||||
|
||||
```bash
|
||||
# Start Velxio locally with Docker
|
||||
cd ../velxio-qemu
|
||||
docker run -d -p 3080:80 ghcr.io/davidmonterocrespo24/velxio:master
|
||||
|
||||
# Open browser to http://localhost:3080
|
||||
```
|
||||
|
||||
### Option 3: Import Project File
|
||||
|
||||
1. Start Velxio (cloud or self-hosted)
|
||||
2. Click "Import Project"
|
||||
3. Upload `tri-pi-test.velxio.json` from this directory
|
||||
4. The project will load with:
|
||||
- Raspberry Pi 3B board
|
||||
- Red LED connected to GPIO17 (physical pin 11)
|
||||
- Test script that runs the installer and validates all components
|
||||
|
||||
## What the Test Does
|
||||
|
||||
The test script (`test_installer.py`) performs these steps:
|
||||
|
||||
1. **Runs the TRI-PI installer** (`/opt/tri-pi-src/setup/install.sh`)
|
||||
2. **Validates installation** by checking:
|
||||
- Config file exists (`/etc/triangles/triangles.conf`)
|
||||
- RPC password is properly randomized (16+ chars)
|
||||
- Backend installed (`/opt/tri-pi/backend/app.py`)
|
||||
- Frontend installed (`/opt/tri-pi/frontend/index.html`)
|
||||
- Tor configured (`/etc/tor/torrc.d/tri-pi.conf`)
|
||||
- systemd units installed
|
||||
- `triangles` user exists
|
||||
- Data directories created
|
||||
3. **Signals success** by blinking the LED 5 times if all checks pass
|
||||
|
||||
## Expected Output
|
||||
|
||||
```
|
||||
[TRI-PI TEST] Starting installer test...
|
||||
[TRI-PI TEST] Current directory: /home/pi
|
||||
[TRI-PI TEST] Python version: 3.11.x
|
||||
|
||||
[tri-pi] Installing system dependencies...
|
||||
[tri-pi] Creating system user 'triangles'...
|
||||
[tri-pi] Generating triangles.conf with random RPC password...
|
||||
[tri-pi] Installing backend and frontend to /opt/tri-pi...
|
||||
[tri-pi] Configuring Tor hidden service...
|
||||
[tri-pi] Installing systemd service files...
|
||||
[tri-pi] Installation complete!
|
||||
|
||||
[TRI-PI TEST] Installer exit code: 0
|
||||
|
||||
[TRI-PI TEST] Running validation checks...
|
||||
✓ PASS: Config file exists
|
||||
✓ PASS: Backend installed
|
||||
✓ PASS: Frontend installed
|
||||
✓ PASS: Tor configured
|
||||
✓ PASS: systemd unit installed
|
||||
✓ PASS: Data directory exists
|
||||
✓ PASS: Tor HS directory exists
|
||||
✓ PASS: RPC password configured
|
||||
✓ PASS: triangles user exists
|
||||
|
||||
[TRI-PI TEST] Results: 9 passed, 0 failed
|
||||
[TRI-PI TEST] ✓ ALL CHECKS PASSED
|
||||
```
|
||||
|
||||
Then the LED will blink 5 times to visually confirm success.
|
||||
|
||||
## Limitations
|
||||
|
||||
- **No trianglesd binary:** The test only validates the installer infrastructure (config, users, directories, services). It does not test the actual trianglesd daemon since we don't have an ARM64 binary yet.
|
||||
- **No network access:** The QEMU instance is isolated, so the installer cannot download packages. It uses the packages already in the Raspberry Pi OS base image.
|
||||
- **No persistent state:** Each run starts with a fresh Pi OS image via qcow2 overlay. Changes don't persist between sessions.
|
||||
|
||||
## Next Steps
|
||||
|
||||
After validating the installer with Velxio:
|
||||
|
||||
1. Build ARM64 trianglesd binary (cross-compile or build in QEMU)
|
||||
2. Add it to the test by uploading to `/usr/local/bin/trianglesd`
|
||||
3. Test that the daemon actually starts and syncs
|
||||
4. Test the web UI at `http://localhost:8080`
|
||||
|
||||
## Files in This Directory
|
||||
|
||||
- `tri-pi-test.velxio.json` — Velxio project file (import into Velxio web UI)
|
||||
- `README.md` — This file
|
||||
- `run-test.sh` — (Not implemented) Would automate running the test via Velxio API
|
||||
- `project.json` — (Old format) Initial attempt at API-based testing
|
||||
|
||||
## Manual Testing (Alternative)
|
||||
|
||||
If you prefer the QEMU smoke test (no Velxio UI), use:
|
||||
|
||||
```bash
|
||||
cd ../build
|
||||
sudo bash qemu-smoke.sh
|
||||
```
|
||||
|
||||
This runs the same validation checks but in a headless systemd-nspawn container instead of the Velxio web UI.
|
||||
@@ -1,115 +0,0 @@
|
||||
# Testing TRI-PI in Velxio
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Open Velxio:**
|
||||
- Go to https://velxio.sami
|
||||
- Or http://194.233.88.206:8099
|
||||
|
||||
2. **Select Raspberry Pi 3:**
|
||||
- Click "Board Picker"
|
||||
- Select "Raspberry Pi 3B"
|
||||
- Board will boot (takes 30-60 seconds)
|
||||
|
||||
3. **Load the test script:**
|
||||
- Click "Files" → "New File"
|
||||
- Name it: `tripi-test.py`
|
||||
- Copy/paste the content from `tripi-test.py`
|
||||
- Click "Run"
|
||||
|
||||
4. **Expected output:**
|
||||
- Environment check (ARM64 Linux)
|
||||
- Dependency detection (Tor, Python3, systemctl)
|
||||
- Directory structure simulation
|
||||
- Config generation preview
|
||||
- Service installation plan
|
||||
- Status API JSON
|
||||
|
||||
## What This Tests
|
||||
|
||||
✅ **Raspberry Pi 3 emulation works:**
|
||||
- Linux kernel boots
|
||||
- Python 3 executes
|
||||
- System commands available
|
||||
|
||||
✅ **Installation prerequisites:**
|
||||
- Checks for Tor
|
||||
- Checks for Python3
|
||||
- Checks for systemd
|
||||
- Validates ARM64 architecture
|
||||
|
||||
✅ **Installation plan:**
|
||||
- User creation (triangles user)
|
||||
- Directory structure (/opt/tri-pi, /etc/triangles, /var/lib/triangles)
|
||||
- Config generation (triangles.conf, tripi-backend env)
|
||||
- Service installation (systemd units)
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Phase 1: Basic validation ✅
|
||||
- [x] Boot Raspberry Pi 3 in Velxio
|
||||
- [x] Run Python test script
|
||||
- [x] Verify environment
|
||||
|
||||
### Phase 2: Full installer test
|
||||
- [ ] Copy full `install.sh` to Velxio
|
||||
- [ ] Run with simulated trianglesd binary
|
||||
- [ ] Verify all files created
|
||||
- [ ] Check service startup
|
||||
|
||||
### Phase 3: Backend API test
|
||||
- [ ] Deploy tripi-backend.py
|
||||
- [ ] Start API server
|
||||
- [ ] Test HTTP endpoints
|
||||
- [ ] Verify JSON responses
|
||||
|
||||
### Phase 4: Tor test
|
||||
- [ ] Configure Tor hidden service
|
||||
- [ ] Generate onion address
|
||||
- [ ] Test connectivity
|
||||
|
||||
### Phase 5: ARM64 binary test
|
||||
- [ ] Build ARM64 trianglesd (we tried earlier)
|
||||
- [ ] Deploy to emulated Pi
|
||||
- [ ] Start daemon
|
||||
- [ ] Verify blockchain sync starts
|
||||
|
||||
## Benefits of Velxio Testing
|
||||
|
||||
🚀 **Fast iteration:**
|
||||
- No SD card writes
|
||||
- No physical Pi needed
|
||||
- Instant restarts
|
||||
|
||||
🔒 **Safe testing:**
|
||||
- No risk to real hardware
|
||||
- Easy rollback
|
||||
- Isolated environment
|
||||
|
||||
📊 **Real Linux:**
|
||||
- Actual Raspberry Pi OS
|
||||
- Real systemd
|
||||
- Real package manager
|
||||
- Real Tor daemon
|
||||
|
||||
## Current Status
|
||||
|
||||
**Environment:** ✅ Validated
|
||||
- Raspberry Pi 3 boots in Velxio
|
||||
- Python 3 available
|
||||
- ARM64 architecture confirmed
|
||||
|
||||
**Installer:** 🔄 Ready for testing
|
||||
- install.sh needs ARM64 trianglesd binary
|
||||
- Can simulate without binary first
|
||||
- All dependencies present
|
||||
|
||||
**Backend:** 📝 Ready to deploy
|
||||
- Python FastAPI backend
|
||||
- Status API endpoints
|
||||
- Tor integration hooks
|
||||
|
||||
**Binary:** ⏳ In progress
|
||||
- ARM64 cross-compile attempted
|
||||
- Need to fix build script
|
||||
- Can test installer without it first
|
||||
@@ -1,68 +0,0 @@
|
||||
{
|
||||
"name": "tri-pi-installer-test",
|
||||
"description": "TRI-PI appliance installer validation on Raspberry Pi 3",
|
||||
"boardType": "pi3",
|
||||
"runtime": "linux",
|
||||
"filesystem": {
|
||||
"rootfs": "raspios-bookworm-arm64-lite",
|
||||
"overlay": [
|
||||
{
|
||||
"src": "../",
|
||||
"dest": "/opt/tri-pi-src",
|
||||
"exclude": ["velxio-*", "build", ".git"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"network": {
|
||||
"internet": true,
|
||||
"ports": [
|
||||
{"host": 8080, "guest": 8080, "protocol": "tcp"},
|
||||
{"host": 24112, "guest": 24112, "protocol": "tcp"}
|
||||
]
|
||||
},
|
||||
"entrypoint": "/opt/tri-pi-src/setup/install.sh",
|
||||
"env": {
|
||||
"DEBIAN_FRONTEND": "noninteractive",
|
||||
"TRI_PI_TEST": "1"
|
||||
},
|
||||
"timeout": 300,
|
||||
"validation": {
|
||||
"checks": [
|
||||
{
|
||||
"name": "triangles.conf exists",
|
||||
"type": "file",
|
||||
"path": "/etc/triangles/triangles.conf"
|
||||
},
|
||||
{
|
||||
"name": "backend installed",
|
||||
"type": "file",
|
||||
"path": "/opt/tri-pi/backend/app.py"
|
||||
},
|
||||
{
|
||||
"name": "frontend installed",
|
||||
"type": "file",
|
||||
"path": "/opt/tri-pi/frontend/index.html"
|
||||
},
|
||||
{
|
||||
"name": "tor configured",
|
||||
"type": "file",
|
||||
"path": "/etc/tor/torrc.d/tri-pi.conf"
|
||||
},
|
||||
{
|
||||
"name": "systemd units installed",
|
||||
"type": "file",
|
||||
"path": "/etc/systemd/system/trianglesd.service"
|
||||
},
|
||||
{
|
||||
"name": "triangles user exists",
|
||||
"type": "command",
|
||||
"command": "id triangles"
|
||||
},
|
||||
{
|
||||
"name": "rpc password configured",
|
||||
"type": "command",
|
||||
"command": "grep -q '^rpcpassword=.\\{16,\\}' /etc/triangles/triangles.conf"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VELXIO_DIR="$SCRIPT_DIR/../velxio-qemu"
|
||||
PROJECT_FILE="$SCRIPT_DIR/project.json"
|
||||
|
||||
log() { echo "[tri-pi:velxio] $*"; }
|
||||
die() { echo "[tri-pi:velxio] ERROR: $*" >&2; exit 1; }
|
||||
|
||||
# Check if Velxio backend is available
|
||||
if [[ ! -d "$VELXIO_DIR" ]]; then
|
||||
die "Velxio not found at $VELXIO_DIR. Clone it first."
|
||||
fi
|
||||
|
||||
# Start Velxio if not running
|
||||
log "Starting Velxio backend..."
|
||||
cd "$VELXIO_DIR"
|
||||
|
||||
# Check if docker-compose is available
|
||||
if ! command -v docker-compose &>/dev/null && ! command -v docker &>/dev/null; then
|
||||
die "Docker or docker-compose not found. Install Docker first."
|
||||
fi
|
||||
|
||||
# Start Velxio services
|
||||
if command -v docker-compose &>/dev/null; then
|
||||
docker-compose up -d
|
||||
else
|
||||
docker compose up -d
|
||||
fi
|
||||
|
||||
log "Waiting for Velxio backend to be ready..."
|
||||
sleep 5
|
||||
|
||||
# Submit the project for execution
|
||||
log "Submitting TRI-PI test project to Velxio..."
|
||||
|
||||
VELXIO_API="${VELXIO_API:-http://localhost:3001}"
|
||||
|
||||
# Create project via API
|
||||
PROJECT_ID=$(curl -s -X POST "$VELXIO_API/api/projects" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @"$PROJECT_FILE" | jq -r '.id')
|
||||
|
||||
if [[ -z "$PROJECT_ID" || "$PROJECT_ID" == "null" ]]; then
|
||||
die "Failed to create Velxio project"
|
||||
fi
|
||||
|
||||
log "Project created: $PROJECT_ID"
|
||||
|
||||
# Start execution
|
||||
log "Starting execution..."
|
||||
curl -s -X POST "$VELXIO_API/api/projects/$PROJECT_ID/start" || die "Failed to start execution"
|
||||
|
||||
# Poll for completion
|
||||
log "Waiting for execution to complete..."
|
||||
TIMEOUT=300
|
||||
ELAPSED=0
|
||||
while [[ $ELAPSED -lt $TIMEOUT ]]; do
|
||||
STATUS=$(curl -s "$VELXIO_API/api/projects/$PROJECT_ID/status" | jq -r '.status')
|
||||
|
||||
case "$STATUS" in
|
||||
completed)
|
||||
log "Execution completed!"
|
||||
break
|
||||
;;
|
||||
failed)
|
||||
log "Execution failed!"
|
||||
curl -s "$VELXIO_API/api/projects/$PROJECT_ID/logs"
|
||||
exit 1
|
||||
;;
|
||||
running)
|
||||
log "Still running... ($ELAPSED/${TIMEOUT}s)"
|
||||
;;
|
||||
*)
|
||||
log "Unknown status: $STATUS"
|
||||
;;
|
||||
esac
|
||||
|
||||
sleep 10
|
||||
ELAPSED=$((ELAPSED + 10))
|
||||
done
|
||||
|
||||
if [[ $ELAPSED -ge $TIMEOUT ]]; then
|
||||
die "Execution timed out after ${TIMEOUT}s"
|
||||
fi
|
||||
|
||||
# Fetch results
|
||||
log "Fetching validation results..."
|
||||
curl -s "$VELXIO_API/api/projects/$PROJECT_ID/results" | jq '.'
|
||||
|
||||
# Fetch logs
|
||||
log "Execution logs:"
|
||||
curl -s "$VELXIO_API/api/projects/$PROJECT_ID/logs"
|
||||
|
||||
log "Test complete!"
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"name": "TRI-PI Installer Test",
|
||||
"description": "Test TRI-PI appliance installation on Raspberry Pi 3",
|
||||
"boards": [
|
||||
{
|
||||
"id": "pi1",
|
||||
"type": "raspberry-pi-3",
|
||||
"position": { "x": 100, "y": 100 }
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"id": "led1",
|
||||
"type": "led",
|
||||
"position": { "x": 400, "y": 150 },
|
||||
"color": "red"
|
||||
}
|
||||
],
|
||||
"wires": [
|
||||
{
|
||||
"from": "pi1:11",
|
||||
"to": "led1:anode",
|
||||
"color": "red"
|
||||
},
|
||||
{
|
||||
"from": "led1:cathode",
|
||||
"to": "pi1:GND",
|
||||
"color": "black"
|
||||
}
|
||||
],
|
||||
"vfs": {
|
||||
"pi1": {
|
||||
"/home/pi/test_installer.py": {
|
||||
"content": "#!/usr/bin/env python3\nimport subprocess\nimport os\nimport sys\n\nprint('[TRI-PI TEST] Starting installer test...')\nprint('[TRI-PI TEST] Current directory:', os.getcwd())\nprint('[TRI-PI TEST] Python version:', sys.version)\n\n# Run the installer\nresult = subprocess.run(\n ['bash', '/opt/tri-pi-src/setup/install.sh'],\n capture_output=True,\n text=True\n)\n\nprint('[TRI-PI TEST] Installer exit code:', result.returncode)\nprint('[TRI-PI TEST] Installer stdout:')\nprint(result.stdout)\nif result.stderr:\n print('[TRI-PI TEST] Installer stderr:')\n print(result.stderr)\n\n# Validation checks\nprint('\\n[TRI-PI TEST] Running validation checks...')\n\nchecks = [\n ('/etc/triangles/triangles.conf', 'Config file exists'),\n ('/opt/tri-pi/backend/app.py', 'Backend installed'),\n ('/opt/tri-pi/frontend/index.html', 'Frontend installed'),\n ('/etc/tor/torrc.d/tri-pi.conf', 'Tor configured'),\n ('/etc/systemd/system/trianglesd.service', 'systemd unit installed'),\n ('/var/lib/triangles', 'Data directory exists'),\n ('/var/lib/tor/tri-pi', 'Tor HS directory exists'),\n]\n\npassed = 0\nfailed = 0\n\nfor path, desc in checks:\n if os.path.exists(path):\n print(f' ✓ PASS: {desc}')\n passed += 1\n else:\n print(f' ✗ FAIL: {desc} (not found: {path})')\n failed += 1\n\n# Check RPC password\ntry:\n with open('/etc/triangles/triangles.conf', 'r') as f:\n conf = f.read()\n if 'rpcpassword=' in conf and len(conf.split('rpcpassword=')[1].split()[0]) >= 16:\n print(f' ✓ PASS: RPC password configured')\n passed += 1\n else:\n print(f' ✗ FAIL: RPC password not properly configured')\n failed += 1\nexcept Exception as e:\n print(f' ✗ FAIL: Could not check RPC password: {e}')\n failed += 1\n\n# Check user exists\ntry:\n result = subprocess.run(['id', 'triangles'], capture_output=True)\n if result.returncode == 0:\n print(f' ✓ PASS: triangles user exists')\n passed += 1\n else:\n print(f' ✗ FAIL: triangles user does not exist')\n failed += 1\nexcept Exception as e:\n print(f' ✗ FAIL: Could not check user: {e}')\n failed += 1\n\nprint(f'\\n[TRI-PI TEST] Results: {passed} passed, {failed} failed')\n\nif failed == 0:\n print('[TRI-PI TEST] ✓ ALL CHECKS PASSED')\n # Blink LED to signal success\n import RPi.GPIO as GPIO\n import time\n GPIO.setmode(GPIO.BCM)\n GPIO.setup(17, GPIO.OUT)\n for _ in range(5):\n GPIO.output(17, GPIO.HIGH)\n time.sleep(0.2)\n GPIO.output(17, GPIO.LOW)\n time.sleep(0.2)\n GPIO.cleanup()\nelse:\n print(f'[TRI-PI TEST] ✗ {failed} CHECK(S) FAILED')\n sys.exit(1)\n"
|
||||
},
|
||||
"/home/pi/script.py": {
|
||||
"content": "#!/usr/bin/env python3\n# This is the default entry point when the Pi boots\nimport subprocess\n\nprint('Running TRI-PI installer test...')\nsubprocess.run(['python3', '/home/pi/test_installer.py'])\n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"author": "Krystie",
|
||||
"created": "2026-03-28",
|
||||
"purpose": "Validate TRI-PI installation process on Raspberry Pi 3 emulator"
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TRI-PI Velxio Test Script
|
||||
Tests the tri-pi installer in Velxio's Raspberry Pi 3 emulator
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
print("=" * 60)
|
||||
print("TRI-PI Installation Test (Velxio Raspberry Pi 3)")
|
||||
print("=" * 60)
|
||||
print()
|
||||
|
||||
# Step 1: Check environment
|
||||
print("[1/7] Checking environment...")
|
||||
print(f" Python version: {sys.version}")
|
||||
print(f" OS: {os.uname().sysname} {os.uname().release}")
|
||||
print(f" Architecture: {os.uname().machine}")
|
||||
print()
|
||||
|
||||
# Step 2: Check dependencies
|
||||
print("[2/7] Checking dependencies...")
|
||||
deps = {
|
||||
"tor": ["tor", "--version"],
|
||||
"python3": ["python3", "--version"],
|
||||
"systemctl": ["systemctl", "--version"]
|
||||
}
|
||||
|
||||
for name, cmd in deps.items():
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
print(f" ✓ {name}: installed")
|
||||
else:
|
||||
print(f" ✗ {name}: not working")
|
||||
except FileNotFoundError:
|
||||
print(f" ✗ {name}: not found")
|
||||
except Exception as e:
|
||||
print(f" ✗ {name}: error ({e})")
|
||||
|
||||
print()
|
||||
|
||||
# Step 3: Simulate user creation
|
||||
print("[3/7] Simulating user creation...")
|
||||
print(" Would create: user 'triangles' with home /var/lib/triangles")
|
||||
print()
|
||||
|
||||
# Step 4: Simulate directory creation
|
||||
print("[4/7] Simulating directory structure...")
|
||||
dirs = [
|
||||
"/opt/tri-pi",
|
||||
"/opt/tri-pi/backend",
|
||||
"/opt/tri-pi/frontend",
|
||||
"/etc/triangles",
|
||||
"/var/lib/triangles"
|
||||
]
|
||||
for d in dirs:
|
||||
print(f" Would create: {d}")
|
||||
print()
|
||||
|
||||
# Step 5: Simulate config generation
|
||||
print("[5/7] Simulating config generation...")
|
||||
print(" Would generate: /etc/triangles/triangles.conf")
|
||||
print(" Would generate: /etc/default/tripi-backend")
|
||||
print(" RPC password: <random-32-chars>")
|
||||
print()
|
||||
|
||||
# Step 6: Simulate service installation
|
||||
print("[6/7] Simulating systemd service installation...")
|
||||
services = [
|
||||
"trianglesd.service",
|
||||
"tripi-backend.service",
|
||||
"tor.service"
|
||||
]
|
||||
for svc in services:
|
||||
print(f" Would install: /etc/systemd/system/{svc}")
|
||||
print()
|
||||
|
||||
# Step 7: Summary
|
||||
print("[7/7] Installation test summary:")
|
||||
print("=" * 60)
|
||||
print("✓ Environment: Compatible (ARM64 Linux)")
|
||||
print("✓ Dependencies: Available")
|
||||
print("✓ User creation: Ready")
|
||||
print("✓ Directory structure: Defined")
|
||||
print("✓ Config generation: Ready")
|
||||
print("✓ Service installation: Ready")
|
||||
print()
|
||||
print("Next steps:")
|
||||
print(" 1. Deploy actual trianglesd binary")
|
||||
print(" 2. Run full installer")
|
||||
print(" 3. Test API endpoints")
|
||||
print(" 4. Verify Tor hidden service")
|
||||
print("=" * 60)
|
||||
|
||||
# Create a status JSON for API testing
|
||||
status = {
|
||||
"status": "test_mode",
|
||||
"node": {
|
||||
"version": "v5.4.2 (simulated)",
|
||||
"connections": 0,
|
||||
"blocks": 0
|
||||
},
|
||||
"tor": {
|
||||
"enabled": True,
|
||||
"status": "not_started"
|
||||
},
|
||||
"install": {
|
||||
"environment_check": "pass",
|
||||
"dependencies": "available",
|
||||
"ready": True
|
||||
}
|
||||
}
|
||||
|
||||
print()
|
||||
print("Status API response (simulated):")
|
||||
print(json.dumps(status, indent=2))
|
||||
Reference in New Issue
Block a user