01 · IntroductionWhat SELEN is

SELEN is a proof-of-work Layer 1 where privacy is not a setting. Every transfer hides three things: which output was spent, who received the new outputs, and how much moved. The chain still lets anyone check that no coins were created from nothing and that nothing was spent twice.

What sets it apart is the receiver. Most private chains find a payment's receiver with elliptic-curve Diffie–Hellman. That works today, but a large quantum computer running Shor's algorithm would let anyone who knows an address go back through an archived chain and pick out every payment to it. SELEN quantum addresses use ML-KEM-768, the lattice key-encapsulation scheme standardised by NIST as FIPS 203, so recorded payments stay closed after that day.

The node, block explorer, browser wallet, faucet and miner are one open-source Node.js program. The public testnet runs on exactly the code described here.

02 · Quick startFrom nothing to a private payment

  1. Open the wallet and create a wallet. Write down the 24 words and pick a password for this browser.
  2. Open the Faucet tab and request tSELEN. The coins arrive after the next block.
  3. Wait about five minutes. New outputs can sit in a ring once they have 10 confirmations.
  4. Send some to a friend's selenq1… address. Open the transfer in the explorer and press Verify in your browser.
  5. Want more? Point the miner at your address.
tSELEN is a test coin. It has no value and will not carry over to mainnet.

03 · AddressesTwo kinds of address

A wallet is a 32-byte seed. Everything else is derived from it with domain-separated hashes:

a   = H("selen/spend", seed)        spend key,  A = a·G
b   = H("selen/view",  seed)        view key,   B = b·G
kem = ML-KEM-768.KeyGen(SHA-512("selen/kem" ‖ seed))
PrefixContainsFinds payments with
selenq1…A, B and the 1,184-byte ML-KEM public keyML-KEM-768 decapsulation. Sealed against Q-day.
selen1…A and BElliptic-curve Diffie–Hellman (r·B). Private today, not sealed against Q-day.

Addresses use bech32m encoding. A quantum address is long, around 2,000 characters, because it carries a full lattice public key. Wallets copy it for you. Addresses never appear on chain.

04 · OutputsHow a payment is addressed

For every output the sender derives a fresh shared secret with the receiver:

quantum:  (capsule, ss) = ML-KEM-768.Encaps(kem_pk)     capsule = 1,088 bytes
classic:  r random, R = r·G, ss = r·B

s   = H("selen/key", ss)
P   = s·G + A                      one-time key, unlinkable to the address
y   = H("selen/mask", ss)          commitment blinding
C   = y·G + v·H                    Pedersen commitment to the amount v
ek  = v ⊕ SHA-256("selen/amount" ‖ ss)[0..8]
tag = SHA-256("selen/tag" ‖ ss)[0]  one-byte view tag for fast scanning

The receiver's wallet tries every new output. For quantum outputs it decapsulates the capsule with its ML-KEM secret key; for classic ones it computes b·R. If the view tag matches and s·G + A equals P, the output is theirs, and the amount and blinding factor open the commitment. The one-time secret key is x = s + a.

The group is ristretto255, a prime-order group, so there are no cofactor edge cases in ring signatures or key images. H is a second generator obtained by hashing to the curve, so nobody knows its discrete log with respect to G.

05 · TransfersRings, key images, balance

Rings of 16

Each input names 16 outputs from the chain by global index: the real one plus 15 decoys picked by the wallet, sorted ascending so the position says nothing. The wallet signs with MLSAG over two rows: the one-time keys Pj, and the commitment differences Cj − C′, where C′ is the input's pseudo-output commitment.

Key images

Signing reveals I = x·Hp(P). The same output always gives the same I, so nodes refuse any transfer whose key image is already on chain or already waiting in the mempool.

Balance

Pseudo-output masks are chosen so that ΣC′ = ΣCout + fee·H. Nodes check that equation without learning any amount.

Range proofs

Each output commitment is split into 48 bit commitments Ci = ri·G + bi·2i·H, with Σri = y. A two-member ring signature over {Ci, Ci − 2iH} proves each bi is 0 or 1. The amount is therefore between 0 and 248 − 1 and cannot wrap around the group order to mint coins.

Fees

0.0001 tSELEN per started 1,000 bytes. A typical two-output transfer to quantum addresses is about 17 kB.

06 · ConsensusArgon2id proof of work

A block header holds version, height, previous block id, time, Merkle root of the transfers, difficulty and nonce. Its proof of work is Argon2id over the header bytes (2 MiB of memory, one pass, salt SELEN-POW-v1-tst). A header is valid when the 256-bit hash, read big-endian, is at most (2256 − 1) / difficulty.

Difficulty retargets every block with a linearly weighted moving average over the last 45 solve times, aiming at 30 seconds. Timestamps must exceed the median of the last 11 blocks and may run at most 120 seconds ahead of a node's clock. Nodes follow the chain with the most cumulative work.

Nobody signs a block. There is no validator set, no stake and no block-signing key to steal.

Emission

The first transaction of each block is the coinbase. It pays 32 tSELEN plus fees to the miner's address as a one-time output. The reward halves every 1,051,200 blocks (about a year) down to a tail of 0.5 tSELEN. Coinbase amounts are public by design so anyone can audit supply; the receiver is still hidden.

07 · ParametersTestnet constants

ConstantValue
networkselen-testnet-1
block time30 s target, LWMA over 45 blocks
ring size16
maturity10 blocks before an output can sit in a ring
range proof48 bits
KEMML-KEM-768 (FIPS 203), 1,184-byte keys, 1,088-byte capsules
groupristretto255
proof of workArgon2id, 2 MiB, t = 1, p = 1
units1 tSELEN = 108 atomic units
limits16 inputs, 16 outputs, 250 kB per transfer, 200 transfers or 4 MB per block
trusted setupnone

08 · Running a nodeHold the chain yourself

npm install
npm start                                   # own chain, built-in miner and faucet
PEER=https://selen.network npm start       # follow the public testnet, verify every block

With PEER set, the node downloads blocks in batches and re-checks proof of work, timestamps, difficulty, every ring signature, every range proof, every balance equation and every key image before it stores anything. If the peer shows a branch with more work, the node rolls back to the fork point and follows it. Transfers and blocks you submit locally are forwarded to the peer.

VariableMeaning
PORTHTTP port, default 9310
DATA_DIRwhere chain.db lives
MINER0 to disable the built-in miner
MINER_THREADS / MINER_DUTYthreads, and the fraction of time they work
MINER_ADDRESSreward address for the built-in miner
FAUCET0 to disable the faucet

09 · MiningBrowser and command line

/mine runs Argon2id (WebAssembly) in one Web Worker per thread. It asks the node for a job tied to your address, searches nonces, and submits a block when the hash meets the target.

node tools/miner.mjs --node https://selen.network --address selenq1… --threads 4

The command-line miner uses Node's native Argon2id and the same job API. Each found block pays its full reward to your address.

10 · Node APIEverything the site reads

All responses are JSON. Amounts are strings in atomic units.

EndpointReturns
GET /api/statusheight, tip, difficulty, hashrate, output and key-image counts, mempool size
GET /api/eventsserver-sent events: block, mempool, rollback
GET /api/blocks?before=&limit=block summaries, newest first
GET /api/block/:height|:idheader, Argon2id hash, transfers
GET /api/tx/:idfull transfer plus resolved ring members, for in-browser verification
GET /api/outputs?from=&limit=outputs by global index, for wallet scanning
GET /api/ring?ids=keys and commitments of the given outputs
GET /api/keyimages?from=spent key images since a height, plus pending ones
POST /api/txsubmit a signed transfer
GET /api/mining/job?address=header template, target, Argon2id parameters
POST /api/mining/submit{ job, nonce } → accepted block
POST /api/faucet{ address } → testnet coins (one per address per 12 h)
GET /api/sync/blocks?from=raw blocks for peers

11 · Security modelWhat holds, and against whom

PropertyTodayAgainst a quantum adversary
receiver, quantum addresshiddenhidden: ML-KEM-768
receiver, classic addresshiddenexposed to anyone who knows the address
amounthiddenhidden: Pedersen commitments hide perfectly, and the encrypted amount is keyed by the KEM secret
which ring member was spenthidden among 16can be revealed: key images rest on discrete log
no inflationsoundneeds Perigee: binding of commitments rests on discrete log

Put plainly: a future quantum computer could tell which of the sixteen ring members a recorded input really spent, and in principle forge new spends. It could not tell who received a payment sent to a selenq1 address, or how much. Perigee moves spend proofs to hash-based constructions to close the first two gaps.

The code is new and has not been audited. Treat the testnet as an experiment.

12 · $SELENThe token

$SELEN is a token on Robinhood Chain that funds development and carries the project to mainnet. It is separate from tSELEN, the testnet coin.

Contract address: published at launch.

13 · Current statusWhere things stand

  • Live: private testnet, quantum addresses, explorer with in-browser verification, browser wallet, faucet, browser and CLI mining, peer sync.
  • Now: $SELEN on Robinhood Chain.
  • Planned: Perigee: hash-based spend proofs and full-chain anonymity, specified and reviewed in public.
  • After review: mainnet from a fresh genesis with Perigee active at block one.