# How Amadeus Protocol's UPoW Consensus Mechanism Works: A Deep Dive into Epoch-Based Proof-of-Work

> Explore Amadeus Protocol's UPoW consensus mechanism. Learn how this epoch-adjustable, memory-hard process uses UPOW0, UPOW1, and UPOW2 to make proofs expensive to generate but cheap to verify.

- Repository: [Amadeus Protocol/node](https://github.com/amadeusprotocol/node)
- Tags: deep-dive
- Published: 2026-08-20

---

**Amadeus Protocol's UPoW (Universal Proof-of-Work) is a memory-hard, epoch-adjustable consensus mechanism that increases computational difficulty through three algorithmic stages—UPOW0, UPOW1, and UPOW2—making proofs expensive to generate but cheap to verify.**

The Amadeus Protocol node implements a custom proof-of-work scheme designed to balance security with verification efficiency. Unlike traditional PoW systems that rely solely on hashing speed, UPoW introduces **memory-bandwidth constraints** that evolve over time through epoch-based algorithm upgrades. This article examines the complete implementation in [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex) and explains how each component contributes to the protocol's consensus requirements.

## Core Architecture: Three Stages of UPoW

The UPoW mechanism operates through a clearly defined pipeline with distinct entry points, algorithm selection, and heavy computation delegation.

### Stage 1: Entry Point with Epoch Detection

All UPoW computation begins in `compute_for/6`, located at lines 2–8 of [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex). This function serves as the primary interface, accepting six parameters: epoch, trainer public key, trainer population, computor key, segment VR, and an iteration limit.

```elixir

# Example: Run a single UPoW proof for the current epoch

{:ok, trainer_pk} = Application.fetch_env(:ama, :trainer_pk)
{:ok, trainer_pop} = Application.fetch_env(:ama, :trainer_pop)
epoch = DB.Chain.epoch()

# Generate a random segment VR (96 bytes) and ask for a solution

segment_vr = :crypto.strong_rand_bytes(96)

case UPOW.compute_for(epoch, trainer_pk, trainer_pop, trainer_pk, segment_vr, 10_000) do
  nil -> IO.puts("No solution found within the budget")
  sol -> IO.puts("Found solution: #{Base.encode16(sol)}")
end

```

The function implements **epoch-gated algorithm dispatch**: epochs ≥ 156 delegate to UPOW2, while earlier epochs use UPOW1 (epochs 1–155) or UPOW0 (pre-epoch 1). This versioning allows the protocol to increase difficulty without breaking historical compatibility.

### Stage 2: Algorithm Dispatch via branch_sol/5

The `branch_sol/5` function (lines 24–30) selects the concrete implementation based on epoch:

- **UPOW0** — Simple tensor-based walk for pre-epoch 1
- **UPOW1** — Tensor walk with segment VR for epochs 1–155
- **UPOW2** — Matrix-multiplication-heavy walk for epochs ≥ 156

This structure enables **progressive hardness escalation** without requiring consensus-breaking changes to the protocol.

### Stage 3: Heavy Computation Delegation

The `compute/8` function (lines 17–22) calls `RDB.compute_upow/8`, which performs the actual heavyweight operations including hashing, tensor generation, and matrix multiplication. The result returns as either `{:ok, sol}` for a valid solution or `nil` when no solution satisfies the difficulty target.

## Epoch-Dependent Hardness: The Three Algorithms

### UPOW0 and UPOW1: Tensor-Based Random Walks

Both early algorithms construct **solution seeds** by mixing epoch, trainer public key, population, computor key, and nonce. This seed feeds into **Blake3** to produce deterministic randomness.

**UPOW0** (pre-epoch 1) generates a 1,024-element tensor where each row derives from Blake3's XOF (extendable output function). A random-walk binary consumes 16-bit chunks to select rows; each selected row has every byte squared and replaced, creating a data-dependent transformation with high entropy output.

**UPOW1** (epochs 1–155) reduces the tensor to 256 entries but adds the **segment VR parameter**—a 96-byte random value that increases input entropy. The core walk mechanism remains similar: Blake3-derived randomness drives row selection and byte-squaring transformations.

### UPOW2: Memory-Bandwidth Constrained Matrices

Epoch ≥ 156 activates **UPOW2**, which fundamentally changes the computational profile. Instead of tensor walks, this algorithm:

1. Extracts three large binary blocks (`matrix_a`, `matrix_b`, `matrix_b2`) from Blake3's XOF
2. Performs 16 × 16 × 50,240 matrix multiplication via `MatrixMul.multiply/2`
3. Converts the product back to binary with `MatrixMul.map_to_binary/1`

This design creates **memory-bandwidth bottlenecks** that resist ASIC optimization while remaining feasible on general-purpose hardware with sufficient RAM.

```elixir

# Example: Benchmark the UPOW2 path (epoch ≥ 156)

def benchmark_upow2 do
  pk = Application.fetch_env!(:ama, :trainer_pk)
  pop = Application.fetch_env!(:ama, :trainer_pop)

  {:ok, duration} =
    :timer.tc(fn ->
      UPOW.test_one(156)
    end)

  IO.puts("UPOW2 benchmark took #{duration / 1_000_000} seconds")
end

```

## Verification: Asymmetric Cost Structure

The final proof verification uses `Blake3.hash(sol)` on the concatenated seed and tensor/matrix output. The `BIC.Sol.verify_hash/2` function checks this hash against the current difficulty target.

This creates the desired **asymmetric cost property**: proof generation requires expensive memory operations (especially under UPOW2), while verification needs only a single Blake3 hash and comparison—operations measurable in microseconds rather than seconds or minutes.

## Iterative Search and Developer Tools

The `compute_for/6` function implements iterative nonce search, calling `branch_sol/5` repeatedly until finding a valid hash or exhausting the iteration budget. The module also exposes developer utilities:

- `test/0` — General functionality test
- `test_one/1` — Single proof generation for a specified epoch
- `test_until_find/0` — Continuous search until solution discovery

These functions enable local benchmarking without requiring full node synchronization.

## Key Source Files in the Amadeus Protocol Node

| File | Role |
|------|------|
| [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex) | Main UPoW implementation with entry points, epoch branching, and core compute flow |
| [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex) (Modules `UPOW0`, `UPOW1`, `UPOW2`) | Algorithm variations for different epochs |
| [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex) (Module `MatrixMul`) | High-performance matrix multiplication for UPOW2 path |
| [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex) (Module `MatMulTest`) | Reference test harness for matrix-multiplication correctness |
| `ex/lib/node/RDB` | Persistent interface triggering heavy work; external to upow.ex but central to PoW service |

## Summary

- **UPoW implements epoch-gated algorithm evolution** through three stages (UPOW0/1/2) defined in [`ex/lib/node/upow.ex`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/node/upow.ex)
- **Memory hardness increases progressively**: tensor walks evolve into large matrix multiplications at epoch 156
- **Blake3 provides cryptographic foundation** for seed generation, random walks, and final proof hashing
- **Asymmetric cost structure** makes proofs expensive to generate (especially under UPOW2) but cheap to verify
- **Iterative search with budget limits** prevents infinite computation attempts through the `compute_for/6` iteration parameter

## Frequently Asked Questions

### What makes UPoW different from Bitcoin's proof-of-work?

UPoW introduces **memory-bandwidth constraints** through epoch-dependent algorithms, whereas Bitcoin relies purely on SHA-256 hashing speed. The UPOW2 path specifically uses large matrix multiplications (16 × 16 × 50,240) that create hardware bottlenecks distinct from raw computational throughput, making the proof more resistant to specialized mining hardware over time.

### How does the epoch system affect mining difficulty?

Each epoch threshold triggers an **algorithmic upgrade** rather than just a numeric difficulty adjustment. Epochs 0, 1, and 156 activate progressively more complex computation paths (UPOW0 → UPOW1 → UPOW2) that increase both memory requirements and computational complexity. This provides stronger security guarantees than traditional difficulty retargeting alone.

### Why does UPoW use Blake3 instead of SHA-256?

Blake3 offers **faster performance with equivalent security** and native support for extendable output functions (XOF). The XOF capability enables efficient generation of large tensor and matrix data from a fixed seed without repeated hash calls, optimizing the random walk and matrix construction phases of the algorithm.

### Can miners choose which UPoW algorithm to use?

No. The `compute_for/6` function **automatically selects the algorithm** based on the current chain epoch. Attempting to use an older algorithm on a newer epoch would produce proofs that fail verification against the current difficulty requirements, as encoded in `BIC.Sol.verify_hash/2`.