Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions docs/MINE_YOUR_GRANDMAS_COMPUTER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Mine Your Grandma's Computer: The 15-Minute RustChain Guide

Got an old laptop collecting dust in the closet? Don't throw it away! That vintage hardware is exactly what the **RustChain** network values most. In under 15 minutes, you can turn e-waste into an RTC-earning node.

This guide will take you from "I found an old computer" to "It's earning RTC" using real examples of a **Core 2 Duo Windows Laptop** and a **PowerPC G4 Mac**.

---

## 🛑 Does my computer qualify? (The Quick Check)

RustChain rewards **real silicon**, not raw power.
If your computer meets these two simple criteria, it qualifies:
1. **It boots an operating system** (Windows XP/7/10, Mac OS X, or Linux).
2. **It can connect to the internet** (Wi-Fi or Ethernet).

**What DOESN'T qualify?**
- Virtual Machines (VMware, VirtualBox, Proxmox). *They earn 0.000000001x rewards.*
- Modern Cloud VPS (AWS, DigitalOcean). *They earn standard base rewards, but cost more than they earn.*

---

## 📈 The Antiquity Multiplier (Explained in Plain English)

Why use an old computer instead of a brand new gaming PC?
**The Antiquity Multiplier.**

RustChain is designed to preserve computing history. The older and weirder your computer's processor is, the more RTC you earn per epoch:
- **Modern PC (Core i9):** 0.8x rewards
- **Old Core 2 Duo Laptop (2006):** 1.3x rewards
- **Power Mac G4 (2003):** 2.5x rewards

A 20-year-old PowerBook G4 will earn **more than three times** the RTC of a brand new $3,000 gaming desktop!

---

## 🛠️ Walkthrough 1: The Core 2 Duo Windows Laptop (2006-2009 Era)

*Example Hardware: Dell Inspiron 1520 or ThinkPad T61*

### Step 1: Download the Miner
1. Turn on the laptop and connect to Wi-Fi.
2. Open a web browser and download the latest `win-miner` bundle from the [RustChain Releases page](https://github.com/Scottcjn/Rustchain/releases).
3. Extract the ZIP file to your Desktop.

### Step 2: Create a Wallet (Optional, if you don't have one)
Double-click `rustchain-wallet.exe` and follow the prompts to generate a new wallet address. Save your 12-word recovery phrase safely!

### Step 3: Run the Fingerprint Check
RustChain needs to verify your hardware is real.
1. Open the extracted folder.
2. Hold `Shift` and right-click in the folder background, then select **"Open command window here"** (or PowerShell).
3. Type: `miner.exe --dry-run` and press Enter.

*(Screenshot: A Windows command prompt showing a successful fingerprint check, with CPU identified as Core 2 Duo and "Hardware Check: PASSED")*
> **Look for this line:** `Fingerprint verification: SUCCESS. Architecture: x86_64 (Core 2 Duo)`

### Step 4: Start Mining!
Double click the `start_mining.bat` file.
- It will ask for your Wallet Address. Paste it in.
- It will ask for a miner name (e.g., `grandmas-thinkpad`).
- Type `YES` to agree to the consent screen.

*(Screenshot: The miner showing "Attestation submitted successfully" and waiting for the next epoch)*
**Boom. You're earning RTC.**

---

## 🍎 Walkthrough 2: The PowerPC Mac G3/G4/G5 (1997-2005 Era)

*Example Hardware: PowerBook G4 or iMac G3 (Running Mac OS X Leopard or Tiger)*

### Step 1: Get the Python Miner
PowerPC Macs are legendary on RustChain (earning up to 2.5x rewards!). Because they are so old, we use the lightweight Python miner.
1. Open the Terminal application (in `Applications > Utilities`).
2. Clone the repository (or download the ZIP if git isn't installed):
```bash
curl -LO https://github.com/Scottcjn/Rustchain/archive/refs/heads/main.zip
unzip main.zip
cd Rustchain-main/miners/linux
```

### Step 2: The Fingerprint Check
Run the dry-run to ensure your PowerPC chip is correctly identified by the network.
```bash
python3 miner_threaded.py --dry-run
```
*(Screenshot: A Mac Terminal window showing `sys_vendor: Apple Computer, Inc.`, `Architecture: PowerPC G4`, and `Fingerprint: SUCCESS`)*

### Step 3: Start Attesting
Start the miner in the background!
```bash
python3 miner_threaded.py --wallet YOUR_RTC_WALLET_ADDRESS --name powerbook-g4
```
Type `OUI` (or `YES` depending on your locale) to agree to the consent screen.

*(Screenshot: The terminal displaying "Epoch 1234: Attestation accepted. Multiplier: 2.5x")*

---

## 💡 Pro-Tips for Vintage Mining
- **Keep it cool:** Old laptops get hot. Keep them on a hard surface.
- **Screen timeout:** Set your computer to never go to sleep, but allow the screen to turn off to save power.
- **Check your stats:** Enter your wallet address on the [RustChain Explorer](https://rustchain.org) to watch your vintage hardware rake in the rewards!
106 changes: 87 additions & 19 deletions node/sophia_elya_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,37 @@ def _ensure_balance_micro_schema(conn):
)
conn.execute("DROP TABLE balances_legacy_real")

def _ensure_epoch_state_settlement_schema(conn):
"""Keep Sophia's epoch_state compatible with shared settlement guards."""
conn.execute(
"CREATE TABLE IF NOT EXISTS epoch_state ("
"epoch INTEGER PRIMARY KEY, "
"accepted_blocks INTEGER DEFAULT 0, "
"finalized INTEGER DEFAULT 0, "
"settled INTEGER DEFAULT 0, "
"settled_ts INTEGER)"
)
columns = {row[1] for row in conn.execute("PRAGMA table_info(epoch_state)").fetchall()}
newly_added_settled = False
if "settled" not in columns:
try:
conn.execute("ALTER TABLE epoch_state ADD COLUMN settled INTEGER DEFAULT 0")
newly_added_settled = True
except sqlite3.OperationalError:
pass # a concurrent migrator won the ADD COLUMN race; column now exists
if "settled_ts" not in columns:
try:
conn.execute("ALTER TABLE epoch_state ADD COLUMN settled_ts INTEGER")
except sqlite3.OperationalError:
pass
conn.execute("UPDATE epoch_state SET settled = 0 WHERE settled IS NULL")
# ONE-TIME backfill, only when we just added the column: rows finalized by the
# pre-settlement code path were already paid, so mark them settled exactly
# once during migration. Never re-run on later startups — that could suppress
# a legitimate finalized-but-not-yet-settled row in a two-phase/shared flow.
if newly_added_settled:
conn.execute("UPDATE epoch_state SET settled = 1 WHERE finalized = 1 AND COALESCE(settled, 0) = 0")

def init_db():
"""Initialize database with epoch tables"""
with sqlite3.connect(DB_PATH) as c:
Expand All @@ -92,7 +123,7 @@ def init_db():
c.execute("CREATE TABLE IF NOT EXISTS tickets (ticket_id TEXT PRIMARY KEY, expires_at INTEGER, commitment TEXT)")

# New epoch tables
c.execute("CREATE TABLE IF NOT EXISTS epoch_state (epoch INTEGER PRIMARY KEY, accepted_blocks INTEGER DEFAULT 0, finalized INTEGER DEFAULT 0)")
_ensure_epoch_state_settlement_schema(c)
# `weight` is a non-financial pro-rata multiplier; balances are financial
# and stay in integer micro-RTC units.
c.execute("CREATE TABLE IF NOT EXISTS epoch_enroll (epoch INTEGER, miner_pk TEXT, weight REAL, PRIMARY KEY (epoch, miner_pk))")
Expand Down Expand Up @@ -146,8 +177,11 @@ def _finite_float(value, default=1.0):
def inc_epoch_block(epoch):
"""Increment accepted blocks for epoch"""
with sqlite3.connect(DB_PATH) as c:
c.execute("INSERT OR IGNORE INTO epoch_state(epoch, accepted_blocks, finalized) VALUES (?,0,0)", (epoch,))
c.execute("UPDATE epoch_state SET accepted_blocks = accepted_blocks + 1 WHERE epoch=?", (epoch,))
c.execute("PRAGMA busy_timeout=5000")
c.execute("INSERT OR IGNORE INTO epoch_state(epoch, accepted_blocks, finalized, settled) VALUES (?,0,0,0)", (epoch,))
# Do not inflate the block count once the epoch is finalized/settled —
# a late block must not change the count the reward was computed against.
c.execute("UPDATE epoch_state SET accepted_blocks = accepted_blocks + 1 WHERE epoch=? AND COALESCE(finalized,0)=0 AND COALESCE(settled,0)=0", (epoch,))

def enroll_epoch(epoch, miner_pk, weight):
"""Enroll miner in epoch with weight.
Expand All @@ -164,28 +198,58 @@ def enroll_epoch(epoch, miner_pk, weight):
def finalize_epoch(epoch, per_block_rtc):
"""Finalize epoch and distribute rewards"""
with sqlite3.connect(DB_PATH) as c:
row = c.execute("SELECT finalized, accepted_blocks FROM epoch_state WHERE epoch=?", (epoch,)).fetchone()
c.execute("PRAGMA busy_timeout=5000")
c.execute("BEGIN IMMEDIATE")
# COALESCE settled so a legacy/shared row whose column was added without
# a value cannot crash int() here.
row = c.execute(
"SELECT COALESCE(finalized, 0), COALESCE(accepted_blocks, 0), COALESCE(settled, 0) "
"FROM epoch_state WHERE epoch=?",
(epoch,),
).fetchone()
if not row:
c.rollback()
return {"ok": False, "reason": "no_state"}

finalized, blocks = int(row[0]), int(row[1])
finalized, blocks, settled = int(row[0]), int(row[1]), int(row[2])
if settled:
c.rollback()
return {"ok": False, "reason": "already_settled"}
if finalized:
# Status probe only — do NOT mutate on this read path. Legacy
# finalized-but-unsettled rows are reconciled by the init-time
# backfill in _ensure_epoch_state_settlement_schema().
c.rollback()
return {"ok": False, "reason": "already_finalized"}

total_reward = per_block_rtc * blocks
miners = list(c.execute("SELECT miner_pk, weight FROM epoch_enroll WHERE epoch=?", (epoch,)))
sum_w = sum(w for _, w in miners) or 0.0
payouts = []

if sum_w > 0 and total_reward > 0:
for pk, w in miners:
amt = total_reward * (w / sum_w)
c.execute("INSERT OR IGNORE INTO balances(miner_pk, balance_rtc) VALUES (?,0)", (pk,))
amount_micro = _rtc_to_micro(amt)
c.execute("UPDATE balances SET balance_rtc = balance_rtc + ? WHERE miner_pk=?", (amount_micro, pk))
payouts.append((pk, _micro_to_rtc(amount_micro)))
claim = c.execute(
"UPDATE epoch_state SET settled=1, settled_ts=?, finalized=1 WHERE epoch=? AND COALESCE(settled,0)=0",
(int(time.time()), epoch),
)
if claim.rowcount != 1:
c.rollback()
return {"ok": False, "reason": "already_settled"}

c.execute("UPDATE epoch_state SET finalized=1 WHERE epoch=?", (epoch,))
try:
total_reward = per_block_rtc * blocks
miners = list(c.execute("SELECT miner_pk, weight FROM epoch_enroll WHERE epoch=?", (epoch,)))
sum_w = sum(w for _, w in miners) or 0.0
payouts = []

if sum_w > 0 and total_reward > 0:
for pk, w in miners:
amt = total_reward * (w / sum_w)
c.execute("INSERT OR IGNORE INTO balances(miner_pk, balance_rtc) VALUES (?,0)", (pk,))
amount_micro = _rtc_to_micro(amt)
c.execute("UPDATE balances SET balance_rtc = balance_rtc + ? WHERE miner_pk=?", (amount_micro, pk))
payouts.append((pk, _micro_to_rtc(amount_micro)))

c.commit()
except Exception:
# Roll back the settlement claim + any partial credits together so the
# epoch stays unsettled and can be retried (no half-paid epoch).
c.rollback()
raise
return {"ok": True, "blocks": blocks, "total_reward": total_reward, "sum_w": sum_w, "payouts": payouts}

def get_balance(miner_pk):
Expand Down Expand Up @@ -244,9 +308,11 @@ def get_epoch():

# Get epoch state
with sqlite3.connect(DB_PATH) as c:
row = c.execute("SELECT accepted_blocks, finalized FROM epoch_state WHERE epoch=?", (epoch,)).fetchone()
row = c.execute("SELECT accepted_blocks, finalized, COALESCE(settled,0), settled_ts FROM epoch_state WHERE epoch=?", (epoch,)).fetchone()
blocks = int(row[0]) if row else 0
finalized = bool(row[1]) if row else False
settled = bool(row[2]) if row else False
settled_ts = (row[3] if row else None)

# Count enrolled miners
miners = c.execute("SELECT COUNT(*), SUM(weight) FROM epoch_enroll WHERE epoch=?", (epoch,)).fetchone()
Expand All @@ -263,6 +329,8 @@ def get_epoch():
"enrolled_miners": miner_count,
"total_weight": total_weight,
"finalized": finalized,
"settled": settled,
"settled_ts": settled_ts,
"epoch_pot": PER_BLOCK_RTC * blocks
})

Expand Down
Loading
Loading