A Go-based simulation framework implementing Hyperedge-based Multi-Party Payment Channels (H-MPCs) — a generalization of payment channels that removes routing, removes HTLCs, and replaces pairwise channel updates with a multi-party DAG of signed payment nodes.
This project demonstrates how off-chain payments can be performed inside a hyperedge with:
- No HTLC timeouts
- No multi-hop routing
- No directional liquidity bottlenecks
- A shared, append-only DAG of updates
- Canonical root agreement using aggregated signatures
- Consistent balances across all participants
Traditional PCNs (e.g., Lightning) operate on bilateral channels and rely on routing to deliver payments. These systems suffer from:
- Route failures
- HTLC expiry failures
- Liquidity imbalance
- Low success rate under random traffic
H-MPC eliminates these constraints by letting all participants operate inside a single hyperedge. Every payment is a DAG leaf, signed by receiver and broadcast to all peers. Canonical roots are periodically finalized via aggregated consent.
The system models:
- A P2P transport layer
- A DAG representing all off-chain updates
- Root consensus (all peers agreeing on the canonical DAG root)
- Local balance recomputation
- Simulation mode generating random transactions
Each peer is assigned:
- A key pair
- A local DAG
- An initial balance vector
- Maps for proposal tracking and last committed nodes
All peers start a TCP listener with retry logic to avoid port-binding collisions.
-
Sender proposes a DAG node
- Encodes
(from, to, amount, parentSeq) - Sends to receiver
- Encodes
-
Receiver validates balance
- Computes:
current = initial + incoming - outgoing - Rejects immediately if insufficient
- Computes:
-
Receiver signs consent
- Signature over
from:to:amount - Sends
TRANSACTION_CONSENT
- Signature over
-
Proposer verifies
- If valid → broadcasts
FINALIZED_TRANSACTION
- If valid → broadcasts
-
All peers insert node into local DAG
- Correct parent selection
- Balance recomputation on every finalized update
When the DAG grows, peers:
- Compute canonical root hash
- Propose new root
- Collect signatures from all other peers
- Broadcast a single
FINALIZED_ROOT_AGG
Every peer appends the new root as RootNodes[1] and resets per-user sequences.
Each transaction is a DagNode containing:
From / To
Amount
ParentSeq
Proposer
ID (SHA-256)
IsRoot
Children[]Every peer maintains:
Nodes: hash → node
RootNodes: [initialRoot, canonicalRoot]
LastNodeForUser: user → latest leafThe DAG is printed after each run for debugging.
=== ROOT 0 ===
RootID: 0000000000000000000000000000000000000000000000000000000000000000 | Proposer: root
Node: root -> root | Proposer: root | Amount: 0.00 | ParentSeq: 0 | ID: 0000000000000000000000000000000000000000000000000000000000000000 [ROOT]
Node: peer8 -> peer3 | Proposer: peer8 | Amount: 6.92 | ParentSeq: 1 | ID: 205aceac1871a147754f6667d435e10aee929e53bbc6320bdf69aefc97dd7f47
Node: peer8 -> peer3 | Proposer: peer8 | Amount: 1.29 | ParentSeq: 2 | ID: 2f449affaa39bd3e75c60d72057ece022d72bb9a3f082f5b06acb6257479d4dc
Node: peer10 -> peer2 | Proposer: peer10 | Amount: 4.37 | ParentSeq: 1 | ID: 9f90980c4990d0b81933aae76ba9b1a440d068add5d9bd7bd2a3bef5d41bd0ec
Node: peer10 -> peer4 | Proposer: peer10 | Amount: 7.71 | ParentSeq: 2 | ID: 2b78f4bc3c0bc10d47cbf4286ba53db54ef89955bdd293efd5d0467b6f9ec873
Node: peer4 -> peer9 | Proposer: peer4 | Amount: 2.74 | ParentSeq: 1 | ID: c45701486ad3ac5aec20b10d9f48195ad20c3a7e80734719563abb3485d63706
Node: peer4 -> peer3 | Proposer: peer4 | Amount: 5.86 | ParentSeq: 2 | ID: 6e2666e6446c1285d3135fb47f307c7ca9de402ca08f214c5fd5b4e35f81935f
Node: peer1 -> peer2 | Proposer: peer1 | Amount: 1.06 | ParentSeq: 1 | ID: 84b7d5d11961353ce585e54172e5426a4b89f062987b24aef2e6eaa677397797
Node: peer1 -> peer5 | Proposer: peer1 | Amount: 2.86 | ParentSeq: 2 | ID: 79d3a08b09782f8874c6de1786bff9355a2d1a1f0968f6089543e2bba7d57d17
Node: peer7 -> peer3 | Proposer: peer7 | Amount: 6.41 | ParentSeq: 1 | ID: b8b075a445646e64af40a0d2d5a89562741740bb5e430bc16089b3d92b7d33ae
Node: peer7 -> peer4 | Proposer: peer7 | Amount: 1.89 | ParentSeq: 2 | ID: 97af024031fa62287800bcb341cd0abd10a50d3e0d4cb2e2e0241241b9252442
Node: peer5 -> peer2 | Proposer: peer5 | Amount: 4.92 | ParentSeq: 1 | ID: 50762672e2db4128b19af866df572ca4b595ff27f163992a33eca15a9440f0fa
Node: peer5 -> peer7 | Proposer: peer5 | Amount: 3.53 | ParentSeq: 2 | ID: 863100f8fd8607bd692a38c518c67cd9d1dd316cc535afe5b7f1076d7ce8140a
Node: peer6 -> peer8 | Proposer: peer6 | Amount: 4.30 | ParentSeq: 1 | ID: 650faf311036857e4268c4a286ef20fa8d90567048686eef4645a2db507fb36b
Node: peer3 -> peer10 | Proposer: peer3 | Amount: 7.90 | ParentSeq: 1 | ID: fa0856c749c7da287cc662a65e55758b0e672253bf5b85fb5bb22b2fe93ccd83
Node: peer3 -> peer2 | Proposer: peer3 | Amount: 5.80 | ParentSeq: 2 | ID: 7e775071945ea8a6810e0694272a814fdb78a0eb4615f15c2a3b105132979890
Node: peer3 -> peer7 | Proposer: peer3 | Amount: 1.18 | ParentSeq: 3 | ID: 16d2cfea8308efe8587418ea9bb19c8dc21d7ce92015c1feb0b524cd68b4f089
Node: peer9 -> peer5 | Proposer: peer9 | Amount: 9.30 | ParentSeq: 1 | ID: b1e1ccdaf3c8dbef9098f6ed8991932e69f086add3c3599339c842d9d55250e0
All peers begin with:
BalanceVector = [10, 10, …, 10]Balances are recomputed as:
new[i] = 10 + incoming[i] - outgoing[i]Recomputation occurs:
- After each finalized transaction
- After each finalized canonical root
The simulation:
- Creates N peers
- Runs M random transactions
- Tracks:
- total successes
- total failures
- maximum successes in a run
- maximum failures in a run
At the end, the system prints:
DAG of each peer
Final balances
Success/failure statisticsThe simulation reports:
- Success per run
- Failure per run
- Max success in any run
- Max failure in any run
- Global totals
Failures only occur due to insufficient balance, since routing/HTLC failures do not exist in hyperedges.
- Multi-party payment channel
- No routing across peers
- No HTLCs
- DAG-based update tracking
- Fully signed state transitions
- Canonical root consensus
- Deterministic parent selection
- Local balance recomputation
- Complete TCP-based P2P layer
- Fraud proofs
- Watchtower integration
- Threshold signatures (MuSig2/FROST)
- Inter-hyperedge payment support
- Persistent storage of DAGs
- Benchmarking across larger networks
make runAdjust constants at the top of main.go:
const NumPeers = 10
const TxPerRun = 200
const NumRuns = 30
Below is a conceptual diagram of how a hypergraph-based payment network operates.
Each hyperedge represents a shared multi-party payment channel, not a pairwise link.
┌───────────┐
│ Peer 1 │
└─────┬─────┘
│
▼
┌─────────────────┐
│ Hyperedge H1 │
│ {1, 2, 3, 4} │
└─────────────────┘
│ │ │
▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐
│ P2 │ │ P3 │ │ P4 │
└──┬──┘ └──┬──┘ └──┬──┘
│ │ │
│ ▼ │
│ ┌──────────┐│
│ │ Hyperedge││
│ │ H2 ││
│ │ {3,5,6} ││
│ └──────────┘│
│ │ │
│ ▼ │
│ ┌─────┐ │
│ │ P5 │◄───┘
│ └─────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Hyperedge│ │ Hyperedge│
│ H3 │ │ H4 │
│ {4,7} │ │ {5,8,9} │
└──────────┘ └──────────┘
│ │
▼ ▼
┌─────┐ ┌─────┐
│ P7 │ │ P9 │
└─────┘ └─────┘