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

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 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. This function serves as the primary interface, accepting six parameters: epoch, trainer public key, trainer population, computor key, segment VR, and an iteration limit.


# 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.


# 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 Main UPoW implementation with entry points, epoch branching, and core compute flow
ex/lib/node/upow.ex (Modules UPOW0, UPOW1, UPOW2) Algorithm variations for different epochs
ex/lib/node/upow.ex (Module MatrixMul) High-performance matrix multiplication for UPOW2 path
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
  • 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →