One person, one unit of stake

Peoplecoin

UBI, elections and a global 1-on-1 video event

Abstract

A very simple coin with UBI by demurrage and consensus by "one person, one unit of stake", on an anonymous population registry kept by a monthly global 1-on-1 video event. Everyone registered is paired at random with one other account for a fifteen minute call, at the same time everywhere, and a pair passes or fails as a whole. A pass is one credential for one person for one month: a registration for the next event, a vote for a block producer, a UBI payment, and an invite token, three of which register a newcomer. The registry never establishes who anyone is. What it proves is that a person was there and was seen by another, at a time when everyone was being seen.

01

The period

Everything runs on a four-week period of 40 320 one-minute slots, counted from genesis and split into four weekly phases: period = slot / SlotsPerPeriod, phase = (slot % SlotsPerPeriod) / SlotsPerPhase, integer division throughout.

Phase 1      The event: 15 minutes on video, at 07:00 UTC Saturday plus SHA256(uint32(period)) mod 24 hours. The handshake is done before it.
             Verify is signed here, so that voting has at least the whole second phase.
Phases 1–2   Verified, Register, Mix, Vote, Commit.
Phase 3      Reveal: the random number, and the UBI.
Phase 4      Pause, so that the seed, and with it the next pairs, are known a week ahead.

The hour comes from the period number rather than the seed, so it is known from genesis and cannot be moved by anyone; the seed decides the pairs, the slots and the votes.

At the end of the period the reveals are tallied and the winning number becomes the seed, 0 if there were none; election_trie[1], rng_trie[1] and reg_trie[1] rotate into [0]; [1] and token_trie are cleared. The three rotating tries hold the current period in [0] and the next in [1]: the registry being paired now and the one being registered for next month, the votes producing blocks now and the votes for next month, the numbers being revealed now and the commits for next month.

02

State

Header {
    number            uint64      — block count
    slot              uint64      — slot number
    time              uint64
    tx_trie           [32]byte    — root hash
    coin_trie         [32]byte
    token_trie        [32]byte
    election_trie     [2][32]byte
    rng_trie          [2][32]byte
    reg_trie          [2][32]byte
    prev              [32]byte    — previous block hash
    validator         [32]byte    — public key
    nonce             [32]byte    — election commit preimage
    sig               [64]byte    — Ed25519 signature
}

Tries

All tries are binary Merkle tries. Path navigation follows the key bit by bit, MSB first (bit 0 = most significant bit of byte 0); left child 0, right child 1.

Leaf hash:    SHA256(key ‖ value)
Branch hash:  SHA256(bit_position ‖ aggregate ‖ left_hash ‖ right_hash)
Empty:        [32]byte{0}

bit_position (uint8) is the key bit where the branch diverges, committing the branch to its position in the trie. Insert to a key that diverges from an existing leaf at bit position p splices in a new branch at that position; delete removes the leaf and collapses the parent if only one child remains.

The address-keyed tries (reg, rng, election) are storage tries: ordered, each branch counting the entries below it as its aggregate, so that every entry has an index — its rank in key order, leftmost leaf 0 — for Feistel pairing, slot assignment and RNG voting. The other tries have no aggregate, and omit it from the hash.

StorageBranch {
    count       uint64
    left_hash   [32]byte
    right_hash  [32]byte
}

Replay protection

Address-keyed tries (reg, rng, election) use public keys as keys. Signatures include the period number, so a signature from period 7 cannot be replayed in period 8. The tries rotate each period, discarding old entries, and inserts to an occupied address are rejected. Within a period, operations are not replayable: each either consumes its input or makes an irreversible change (verify false → true, commit empty → filled).

UTXO-keyed tries (coin, token) use unique UTXO IDs as keys. Each output is identified by SHA256(tx_hash ‖ output_index), where tx_hash is the operation's hash in the tx trie; fee outputs by SHA256(number ‖ output_index), output 0 the validator and output 1 the voter. UTXO IDs are unique by construction.

03

Population registration

Registering

A registration is an entry in reg_trie[1], keyed by a public key, made during phases 1–2 for the coming period. It costs one registration token, earned by passing this month's event, or InviteCost = 3 invite tokens, earned by three people who passed and gave theirs away. Tokens are mixed before use, so that the key registered for next month cannot be tied to the key that passed this month, nor a newcomer's key to the friends who invited them.

RegEntry {
    verify      bool
    verified    bool
}

Pairing

At the period boundary the registry rotates into reg_trie[0], and its entries are paired by index under a Feistel shuffle keyed by the seed. The shuffle is computed in memory and never written to the tries; it is deterministic, so everyone can find their partner from the trie state and the seed, a week ahead.

feistel(index, N, seed):
    k = ceil(log2(N))
    left_bits = floor(k / 2)
    right_bits = k - left_bits
    loop:
        L = index >> right_bits
        R = index & (2^right_bits - 1)
        for round = 0, 1, 2, 3, 4, 5:
            mask = SHA256(seed ‖ uint8(round) ‖ uint64(R))[0..ceil(left_bits/8)]
            mask = mask >> (8*ceil(left_bits/8) - left_bits)
            R, L = L ⊕ mask, R
        result = (L << right_bits) | R
        if result < N: return result
        index = result

feistel_inverse(index, N, seed):
    same loop, rounds 5, 4, 3, 2, 1, 0, with the mirrored step:
        mask = SHA256(seed ‖ uint8(round) ‖ uint64(L))[0..ceil(left_bits/8)]
        mask = mask >> (8*ceil(left_bits/8) - left_bits)
        L, R = R ⊕ mask, L

Seed is 32 bytes. Truncation takes the first bytes of the SHA256 output and right-shifts to left_bits width.

Pairs are formed from the shuffled order, 0↔1, 2↔3, and so on: a citizen's partner is feistel_inverse(feistel(index) ⊕ 1). If N is odd, the last in the shuffled order has no partner and cannot be verified that period.

The event

The pair meets on video for fifteen minutes, at the same time as every other pair. Afterwards, and only if the partner was there for the whole of it, each signs Verify on their own entry: a person was there, for the whole event. Nothing is decided on the spot: Verify can be signed any time in the rest of the first phase, and Verified any time in the first two, so there is a week to attest and two to collect. Once both flags are set, each submits Verified on their own entry and is paid an election token, a registration token, an invite token, and an RNG entry. Verified requires both flags, so the pair passes or fails as a whole.

Whoever fails — as the odd one out, because the partner did not come, or by refusing an account with nobody behind it — has no entry for next month, and is back when three people who passed give them their invite tokens. A newcomer enters the same way, on three invite tokens, and is checked the same way: by whoever they are paired with.

The rule is to attest only a person one has met, for the whole event. Paired with an account with nobody behind it, there is one thing to do: refuse, and lose the round. The account will attest; the loss is one's own choice.

Lock & key

Two honest citizens, each paired with one of an attacker's accounts, could be connected to each other through them: each sees a person, each verifies, and both accounts pass with nobody behind them. The trivial defence is to ask the partner which pair they are in; a relayed partner names the wrong one. But that puts the whole defence in one spoken sentence, which a real-time deepfake might be able to alter, so a stronger one may be needed. The strongest is to exchange which pair each is in bound to the person, in a way that cannot be altered live.

That is the video lock & key. In a handshake before the event, each side seals a recording of itself, with its public key as data inside the sealed payload, and sends it to the partner, before either has seen the other. In the call they exchange the keys, open the recordings, and check that the key inside is the partner's registration key and that the person recorded is, in every way, the person on the screen. A relay would have to decide what each side will see before it has seen either of them, could not change it afterwards, and would then have to be that person, live, for the whole call. It cannot.

04

Tokens

Election, registration and invite tokens are UTXOs in the token trie, minted at Verified with ID SHA256(tx_hash ‖ output_index) — election 0, registration 1, invite 2 — valid for the period and cleared at the boundary. Registration and election tokens are mixed to fresh keys of one's own. An invite token is given, by a mix with the recipient's key as output; whether it is anonymized by the giver before or by the recipient after is up to them. Mixing is 1-to-1, as many outputs as inputs, and each token carries a mix_count, capped at MaxMixes = 32 against spam: the outputs' counts must sum to the inputs' plus one per input.

token_id:
    0x00  election
    0x01  registration
    0x02  invite
TokenEntry {
    token_id    uint8
    owner       [32]byte
    mix_count   uint32
}

TokenBranch {
    left_hash   [32]byte
    right_hash  [32]byte
}

Mix {
    token_id    uint8
    inputs      [][32]byte          — token UTXO IDs
    outputs     []{mix_count uint32, owner [32]byte}
    sigs        []sig
}
05

Random number

Every Verified creates an RNG entry, which commits to a number in phases 1–2 and reveals it in phase 3 of the following period, when the reveal also pays the UBI. The security of the ledger rests on the randomization; the UBI is the incentive, or reminder, to contribute to it.

A reveal casts a vote, SHA256(address ‖ preimage ‖ seed) mod rngEntryCount, with the seed of the period the reveal takes place in — produced by the previous period's reveals, after the commit window had closed, so unknown when the preimage was chosen. The number with the most votes wins, the lowest on a tie, and becomes the seed as a big-endian uint64 zero-padded to 32 bytes; votes over N numbers spread as Poisson, e^-1/k!. The commit is zeroed after the reveal, against replay. If nobody reveals, the seed is 0.

RngEntry {
    commit      [32]byte
}

The seed mutates the next period's votes and sets its pairing and its slot assignment.

06

Elections and blocks

An election token is a vote for a validator: Vote inserts a commit, SHA256(validator_pubkey ‖ nonce), into election_trie[1], keyed by the voter's public key. The commit hides the validator until block production. The voter shares the nonce with the validator off-chain; a zero nonce is possible, a random one recommended against DoS.

ElectionEntry {
    commit      [32]byte    — SHA256(validator_pubkey ‖ nonce)
}

Blocks are produced in one-minute slots. The slot's validator is the election entry at index SHA256(seed ‖ uint64(slot)) mod electionEntryCount, known in advance from the seed but hidden by the commit: the validator reveals the preimage in the header, nodes check SHA256(header.validator ‖ header.nonce) == commit, and the voter — the entry's key — receives half the block's fees. Since nobody can tell which validator owns an upcoming slot, targeted DoS on block producers is not possible. The fork choice is the chain with the highest number, the fewest skipped slots, and forks cannot reach back past two period boundaries, against long range attacks. A period without votes leaves the next without validators, and the chain ends: at least one vote per period is required for the chain to operate.

Header hash (signing message):
SHA256(number ‖ slot ‖ time ‖ tx_trie ‖ coin_trie ‖
token_trie ‖ election_trie[0] ‖ election_trie[1] ‖ rng_trie[0] ‖ rng_trie[1] ‖
reg_trie[0] ‖ reg_trie[1] ‖
prev ‖ validator ‖ nonce)

Block hash: SHA256(header_hash ‖ sig)

Transactions

Transactions are recorded in each block in a transaction trie, keyed by tx_hash = SHA256(message ‖ sig(s)), or SHA256(message) for the unsigned Prune. The trie commits to the set of transactions; ordering is irrelevant. There is no transaction chaining: state changes made by one operation are not visible to another in the same block, so all operations validate against pre-block state and can be validated in any order. A block is invalid if two of its operations write the same key in the same trie, whether by consuming the same input or by inserting or updating the same entry.

Since the tx trie is unordered and there is no chaining, block validation and construction are trivially parallelizable. A validator can internally partition work across sub-nodes by trie key prefix — each handles its range, and the roots are merged. The protocol does not need to know about this: it sees one block, one signature, one root.

History

State is bounded by population and UTXO count, but transaction history grows with every block and is what dominates storage over time. Old block history is not technically necessary for validation, since the state trie roots are committed in each header, so old blocks can be pruned. A consensus can emerge on how long nodes are expected to store them, for example 10 years, or for very high transactions per second, 1 year; archival beyond that is best effort. Users who need proof of a past payment keep their own receipts and the header chain. The chain itself begins the same way: genesis is a state, not a block.

07

Coin

The Coin trie stores unspent outputs, keyed by UTXO ID. Each entry stores the owners (up to MaxOwners), signature threshold, the amount, and the slot at creation. In transfers any remainder from inputs to outputs is paid as transaction fee, split 50-50 between validator and voter. Output amounts must be greater than zero; fee outputs are only created if their amount is.

CoinEntry {
    owners      [][32]byte  — public keys (for sig verification)
    threshold   uint8       — number of signatures required
    amount      uint64
    slot        uint32      — slot at creation
}

CoinBranch {
    left_hash   [32]byte
    right_hash  [32]byte
}

Transfer {
    inputs      [][32]byte                              — UTXO IDs
    outputs     []{amount uint64, owners [][32]byte, threshold uint8}
    sigs        []sig
}

Demurrage

Coins lose value continuously at 1.7% per period (~20% per year, half-life ~40 periods). The nominal amount stored in a coin entry never changes; its effective value is computed when the entry is used, from the number of slots elapsed since it was created:

t = current_slot − entry.slot
effective(entry) = decay(entry.amount, t)

decay is exponentiation by squaring. A single constant defines the curve — the per-slot factor in 63-bit fixed point, so that 40 320 slots retain 0.983 — and the powers 2^i are derived from it by repeated squaring at startup:

Decay[0]   = 9223368114598617273          — floor(2^63 × 0.983^(1 / SlotsPerPeriod))
Decay[i+1] = (Decay[i] × Decay[i]) >> 63     i = 0 .. 25

decay(amount, t):
    if t ≥ 2^27: return 0
    x = amount                        — 128-bit intermediate
    for i in 0 .. 26:
        if bit i of t is set:
            x = (x × Decay[i]) >> 63
    return x

Decay[i] is what a coin retains after 2^i slots; t written in binary selects which factors to multiply. All products fit in 128 bits. At most 27 multiplications per input, all integer, so every node computes the same value; the squaring chain, not the real-valued curve, is the definition. Rounding is at most a few dozen base units per spend regardless of amount. There is no per-slot accumulation — the value is always computed from the total t. Beyond 2^27 slots (~3 300 periods) the value is zero: the largest amount a uint64 can hold has decayed below one base unit by ~2 600 periods, so the table needs no more entries.

Rent

Each UTXO pays rent for occupying state. Rent is a fixed number of base units per slot since creation, using the same t as demurrage:

rent(entry) = t >> RentShift

A single shift; like decay, it is computed from the total t, so nothing accumulates.

A UTXO's spendable value is effective value minus accumulated rent (saturating at zero):

spendable(entry) = max(effective(entry) − rent(entry), 0)

The rate is one base unit per 1 024 slots (~17 hours), ~39 base units per period. Small UTXOs expire faster; large UTXOs are negligibly affected.

Transfer validation:

requires: all output amounts > 0
requires: Σ output amounts ≤ Σ spendable(input)

The remainder (effective input value − output sum) is the transaction fee, so the rent consumed by an input is paid to the block as well; the outputs are limited by the spendable value. Inputs lose ~4 × 10^−7 of their value per slot, so a transfer that lands a few slots later than the wallet assumed has a marginally smaller fee; wallets should leave a margin so that a late inclusion does not push the outputs above the spendable value and invalidate the transfer.

UBI

Each period, verified citizens receive a UBI payment of a fixed amount, equal to the demurrage on one token over one period:

UBIPerPeriod = TOKEN × DemurragePerPeriod = 1 700 000

A person holding exactly one token therefore receives as much in UBI as they lose to demurrage (decay(TOKEN, SlotsPerPeriod) = 98 299 996, within rounding) — the system is neutral at one token per person, and the effective money supply converges to one token per person. The payment is made by the Reveal operation, to the key of the RNG entry.

Pruning

A coin entry is prunable when rent meets or exceeds its effective value:

prunable(entry) = rent(entry) >= effective(entry)

Pruning deletes the entry from the coin trie. Any remaining effective value (the portion consumed by rent) is added to the block fee accumulator. Only the block's validator may include Prune operations.

A 1 UBI entry retains 80% after one year and 11% after ten; rent overtakes the remaining value after ~292 periods (~22 years). Demurrage destroys most of the value; rent finishes the cleanup.

08

Security

The system rests on an honest majority and nothing else.

Collusion

A collusion attack is a group that shares its people and its accounts and behaves as one. Two of its accounts paired together attest each other and pass with nobody on camera, so it puts its people only in mixed pairs, where one is needed: the honest partner sees a person and verifies. Since attesting costs the pool nothing and can only gain, its strategy is always to attest, and every honest citizen who gives in to an empty account can be counted as having joined the colluders. The population is then exactly two sides.

Let the pool hold a share X of all accounts with K people, and let c = InviteCost. Each round a share X of its accounts meet each other and pass free; the rest, 1 − X of them, face an honest citizen and pass only with a person in the call. Without invites it could sustain K/(1 − X) accounts. With invites it holds more. Every pass earns a token, c tokens make an account, and the pool spends every one, so it always carries accounts beyond what its free pairs and its people can cover: empty, paired with honest citizens, failing, and re-created next round from the tokens. In steady state the accounts lost equal the accounts made, and since every pass earns one token, that is one account in c + 1 failing each round; the other c pass and earn exactly enough to replace it. The pool's people cover the remaining mixed pairs, (1 − X) − 1/(c + 1) of its accounts, so it holds A = K(c + 1) / (c − (c + 1)X) accounts. At half of all accounts the honest side holds as many, A = 2K(c + 1) / (c − 1), and the pool needs a share (c − 1) / (3c + 1) of all people: the honest majority the system rests on is (2c + 2) / (3c + 1).

With c = 3: four accounts per person — two passing free, one with the person in it, one empty and failing — and a fifth of everyone. That is the 80% honest majority: eight in ten who, paired with nothing, forgo that month's verification and UBI. It would be 86% at c = 2, 77% at c = 4, 75% at c = 5, and 67% with no invites at all.

Losing a round

An honest citizen who refuses loses the round and is back next month on their friends' invites. The chance of losing one is the chance of meeting one of the pool's empty accounts — of the pool's accounts that face an honest citizen, 1/((c + 1)(1 − X)) are empty — plus an honest partner who did not come, at no-show rate m: P = X / ((c + 1)(1 − X)) + (1 − X)m. With c = 3 and 5% no-shows that is 7% when the pool is 7% of the population (a tenth of the accounts), 16% at 17% (a third), and 27% at the 20% that gives it half. Re-entry costs c tokens against the one a pass earns, so the honest side can bring back all of its own as long as it loses fewer than one account in c + 1 per round; at X = 1/2 the empties alone take exactly that. The pool reaches half the accounts at the point where the honest side runs out of invites for its own, no-shows aside, and not before.

No-shows hurt the honest side more, since the pool's own pairs are immune to them. Someone who registers and then does not appear fails the pair, so the partner falls too; someone who does not register harms nobody. Most absence is foreseeable, and someone who cannot come can hand their key to someone they trust who was stood up; the model assumes no one does.

Attendance is therefore a security parameter, and the UBI is a strong incentive: missing the event costs the month's UBI, and re-entry takes three invites. The random hour lands in a citizen's night about a third of the time, three to four times a year: a few 25-minute sessions a year, handshake included.

The invite cost

Every pass earns one invite token, and InviteCost of them register a newcomer; the cost sets the majority. The higher it is, the larger the share of the population that has to collude to reach half the accounts — an honest majority of 80% at three, 77% at four, 67% with no invites at all — and the harder it is for the honest side to bring back its own, the stood-up, the refused and the absent, and to grow, at most one in c per month. Three is the balance: an honest majority of four in five, re-entry on three friends, and a registry that can grow by a third a month.

The random number

The RNG is not an attack vector. Colluders cannot choose their votes — the seed is unknown at commit time — only drop them by withholding a reveal, forfeiting UBI. Votes spread as Poisson over N numbers with a maximum of ~log N, so withholding only lets them pick among near-tied winners: rarely any choice below 15% colluders, a few hundred at the tipping point. The best of a few hundred seeds is worth about three standard deviations of pairing luck, √(N·X) accounts against a pair term of N·X, shrinking as 1/√N with scale: about a point of the twenty at ten thousand citizens, a tenth of a point at a million. The hour of the event is drawn from the period number, not the seed, so withholding cannot move it into anyone's night.

Deepfakes and autonomous AI

The system never verifies who anyone is, only that a person is present for the whole event. A deepfake modifies a person — the face, the voice, a spoken word — but it is not an artificial person and still requires an underlying human actor, so it does not change how many people there are. The one place vulnerable to deepfakes, the relay attack, is covered by the lock & key; a generated person that holds a conversation is not a deepfake but a different, and hypothetical, attack.

The one attack the system could not defend itself against is the hypothetical future scenario of a machine that passes a 15 minute 1-on-1 video call, the Turing test — not outright, but well enough to pass against the average citizen a share q of the time. An account it runs passes with probability X + (1 − X)q and earns a token each time, so it reproduces itself once that reaches c / (c + 1): from q = 75% at c = 3 a handful of invites is enough, and the accounts grow with nobody behind them from the start. Below that the attack still takes people. Combined with collusion, the share of humans the pool needs falls from 20% with no machine to at most 14% at q = 30%, Turing's own bar, 8% at 50%, 4% at 60% and under 1% at 70%. Whether and when such a machine is possible is not known. The system makes humankind the interrogator: it has to fool not a chosen judge but the average person, every month, with a new stranger each time.

No slashing

There is no slashing: with an honest majority following the fork choice, double-signing is settled by the majority itself, and a minority gains nothing from it. Proof-of-work burns electricity as a side effect of weighted selection; the selection cannot be done without it, so there is no way to tell whether the cost is what makes it secure or merely a side effect. The assumption that block production needs a cost arose in 2013–14, as an objection to proof-of-stake: "there is nothing at stake". Proof-of-stake answered by adding the cost back by hand as "slashing". That objection may have been mistaken: the honest majority may be all there is to it.

09

Operations

0x00  Transfer        { inputs, outputs, sigs }                                        — any phase
0x01  Mix             { token_id, inputs, outputs, sigs }                              — phases 1–2
0x02  Vote            { token_utxo, commit, sig }                                      — phases 1–2
0x03  Register        { token_utxos, sig }                                             — phases 1–2
0x04  Verify          { reg_addr, sig }                                                — phase 1
0x05  Verified        { reg_addr, sig }                                                — phases 1–2
0x06  Commit          { rng_addr, commit, sig }                                        — phases 1–2
0x07  Reveal          { rng_addr, preimage, sig }                                      — phase 3
0xFF  Prune           { utxo_id }                                                      — any phase, validator-only, unsigned

Signatures are Ed25519. For address-keyed tries (reg, rng, election), the public key is the trie key. For UTXO-keyed tries (coin, token), the public key is the entry's owner field (or one of the owners for multisig). Signatures must be canonical: s < L, and R and public keys must be canonically encoded points (RFC 8032). Non-canonical signatures are rejected.

Signing messages (the opcode prefix prevents cross-operation replay):

Transfer:        SHA256(0x00 ‖ SHA256(inputs) ‖ SHA256(outputs))
Mix:             SHA256(0x01 ‖ token_id ‖ SHA256(inputs) ‖ SHA256(outputs))
Vote:            SHA256(0x02 ‖ token_utxo ‖ commit)
Register:        SHA256(0x03 ‖ SHA256(token_utxos))
Verify:          SHA256(0x04 ‖ period ‖ reg_addr)
Verified:        SHA256(0x05 ‖ period ‖ reg_addr)
Commit:          SHA256(0x06 ‖ period ‖ rng_addr ‖ commit)
Reveal:          SHA256(0x07 ‖ period ‖ rng_addr ‖ preimage)

Unsigned messages:

Prune:           SHA256(0xFF ‖ utxo_id)

All fields are big-endian. Variable-length fields are serialized with a uint8 length prefix. Addresses are 32 bytes, period is uint32, coin entry slots are uint32 (~8 000 years), amounts are uint64, output indices are uint8.

Signatures in a Transfer are a flat list consumed per input in input order. Each input requires exactly threshold signatures, matched against that input's owners in ascending owner-index order. Duplicate owner matches or extra signatures are rejected.

State transitions

Transfer:
    requires: all output amounts > 0
    requires: Σ output amounts ≤ Σ spendable(input)
    coin_trie: delete inputs, insert outputs (slot = current_slot)
    Σ effective(input) − Σ output amounts added to block fee accumulator

Mix:
    requires: len(outputs) == len(inputs)
    requires: Σ output.mix_count = Σ input.mix_count + len(inputs)
    token_trie: delete inputs, insert outputs

Vote:
    requires: token is election type
    token_trie: delete token → election_trie[1]: insert {commit} at token.owner

Register:
    requires: one reg token, or InviteCost invite tokens, all with the same owner
    token_trie: delete tokens → reg_trie[1]: insert entry at owner

Verify:
    requires: verify=false on entry at reg_addr
    reg_trie[0]: set verify=true on entry at reg_addr

Verified:
    requires: reg_addr has a partner (not the odd one out), both in pair have verify=true (pair checked via Feistel), verified=false on reg_addr
    reg_trie[0]: set verified=true
    token_trie: mint election (output 0), reg (output 1) and invite (output 2) tokens with owner=reg_addr
    rng_trie[1]: insert entry at reg_addr

Commit:
    requires: commit field is empty
    requires: commit ≠ 0
    rng_trie[1]: set commit field on entry at rng_addr

Reveal:
    requires: SHA256(preimage) == commit
    rng_trie[0]: zero commit
    coin_trie: insert UBI payment {owners: [rng_addr], threshold: 1, amount: UBIPerPeriod, slot: current_slot} at SHA256(tx_hash ‖ 0)

Prune:
    requires: rent(entry) >= effective(entry)
    coin_trie: delete entry
    remaining effective value added to block fee accumulator

Fees are accumulated during the block and paid out once at the end:
split 50-50 between validator and voter, odd unit to validator.
UTXO IDs: SHA256(number ‖ 0) for validator, SHA256(number ‖ 1) for voter.
Each fee UTXO is only created if its amount is greater than zero.
10

Constants

TOKEN           = 10^8              — 1 token in base units (8 decimals)
SlotsPerPeriod  = 40320             — 4 weeks in minutes
SlotSeconds     = 60
PeriodSeconds   = 2419200           — SlotsPerPeriod × SlotSeconds
DemurragePerPeriod = 1.7%           — 0.983 per period, ~20% per year
DecayShift      = 63                — fixed-point bits in decay()
Decay0          = 9223368114598617273 — 2^63 × 0.983^(1/SlotsPerPeriod)
UBIPerPeriod    = 1700000           — TOKEN × DemurragePerPeriod
SlotsPerPhase   = 10080             — 1 week
NumPhases       = 4
MaxMixes        = 32
MaxOwners       = 8                                     — max owners per UTXO
InviteCost      = 3                                     — invite tokens per new registration
MinOutput       = > 0                                   — no dust threshold, rent handles cleanup
RentShift       = 10                                    — rent = 1 base unit per 1 024 slots (~39 per period)
MaxBlockSize    = consensus                             — e.g. 1 GB, scales with population
11

Genesis

From zero

Genesis is a state at slot 0, with prev = 0 and seed 0: a registry, at least one vote in the election trie for the first period and one election token for the next. The coin trie is empty; the first UBI is paid by the first reveals.

The registry has to be large enough to grow. Below six entries it cannot — five is a fixed point: four pass, four tokens, one invite, five again — a single no-show locks it at five below eight, and it grows through no-shows only from about a dozen. A few dozen is comfortable, and there is nothing against starting from far more.

As a fork

The chain can also be grown first in a bootstrap mode: this protocol with simpler rules, run until those in it judge it has critical mass. Nobody loses their place — a registration token comes regardless of the event's outcome, an election token and the UBI only when the pair passes — and newcomers are voted in by majority. At a period boundary it is forked: its trie roots there are the genesis state, and its slot count continues; the roots are in every header, so the history behind them can be ignored. The seed is the one tallied at that boundary; it follows from the reveals against the rng trie in the header prev points to, and can be proven from them. Nothing is re-based, and nothing signed before the fork can be replayed after it: the periods it was signed for are over.