Fractal Social Hierarchy UTXO System with Universal Basic Income by Demurrage and One Person One Unit of Stake
A population register and a UTXO payment layer. The register is a social hierarchy: your superior attested your identity. The hierarchy maps onto existing civil registration — countries, municipalities, individuals — but supports any trust model, from tribal to national. Being registered gives you universal basic income, claimable as UTXO. Coins lose value continuously at ~1.7% per period (~20% per year). A person holding exactly 1 token neither gains nor loses — the UBI exactly compensates the demurrage. Each UTXO pays a small rent per slot for occupying state. The people vote for block validators. Each vote is a hash commitment. One vote is drawn randomly each block — the selected validator reveals the preimage, which seeds the random selection of the next. The secret votes prevent DoS attacks on block producers and provide the random number generator, all in one.
People Tree
Node
Node {
node_id hash — permanent, SHA256(creator_id || creator_nonce); creator is parent (Add) or old root (Crown)
owners []pubkey — max 8, who can sign for this node
threshold uint8 — number of signatures required
leaf bool
child_trie hash — trie of children, keyed by node_id, value is H(Node)
nonce uint
last_claim uint — last claimed period number
count uint — person count (0 = org, 1 = person, N = group)
tree_count uint — total persons in subtree
}A node is a person, a group of persons, or an organization.
A person contributes 1 to count. A group represents multiple persons as a single node (contributes N, max 8). An org is a non-person entity (contributes 0 — no UBI). For persons and groups: count == len(owners) — one key per person. For persons: threshold = 1. For groups: threshold in 1..count. Orgs may have up to max_owners owners independently of count.
node_id = SHA256(creator_id || creator_nonce) is permanent and never changes. The creator is the parent (Add) or the old root (Crown). Operations reference nodes by node_id. Validators maintain an index from node_id to position in the tree. The genesis root's node_id is set at genesis.
tree_count is the total number of persons in the subtree, aggregated upward. Population = root.tree_count. A node's own person contribution is count.
leaf — if true, the node cannot have children. Set by the parent at creation.
H(Node) = SHA-256(node_id || owners || threshold || leaf || child_trie || nonce || last_claim || count || tree_count).
UBI
Each period, each person can claim a fixed UBI payment equal to the demurrage on one token:
UBIPerPeriod = TOKEN × DemurragePerPeriod = 1 700 000
A person holding exactly one token receives as much in UBI as they lose to demurrage — the system is neutral at one token per person.
A person claims UBIPerPeriod × contribution per period. A group of 5 claims 5×. An org: nothing.
Add
Add {
parent_id hash — node_id of the parent
nonce uint — parent's nonce
owners []pubkey — new child's owners
threshold uint8
leaf bool
count uint
sigs []sig — parent's owners sign (threshold)
}Parent adds child. node_id = SHA256(parent_id || nonce). last_claim = current period. Increment parent's nonce. Recompute tree_count upward. The child can Leave if it does not accept its position.
Remove
Remove {
parent_id hash — node_id of the parent
child_id hash — node_id of the child to remove
nonce uint — parent's nonce
sigs []sig — parent's owners sign (threshold)
}Child and its entire subtree are deleted from the tree. Increment parent's nonce. Recompute tree_count upward.
Move
Move {
child_id hash — node_id of the child
new_parent_id hash — node_id of the new parent
nonce uint — new parent's nonce
deadline uint
consent []sig — child's owners sign (threshold): deadline + new_parent_id
sigs []sig — new parent's owners sign (threshold)
}Moves a child (and its entire subtree) from its current parent to a new parent. The child must not be the root. The new parent must not be the child itself or in the child's subtree — this prevents cycles. The old parent is looked up from the tree. State is preserved: last_claim, nonce, subtree — nothing is reset. Increment new parent's nonce. Recompute tree_count upward for both old and new parent.
Leave
Leave {
child_id hash — node_id of the child
nonce uint — child's nonce
sigs []sig — child's owners sign (threshold)
}Same as Remove but initiated by the child. The child must not be the root. The parent is looked up from the tree. Recompute tree_count upward.
Crown
Crown {
old_root_id hash
old_root_nonce uint
new_owners []pubkey — new root's owners
new_thresh uint8
new_count uint
sigs []sig — current root signs (threshold)
}Creates a new root above the current root. Only the current root can perform this. The new node's node_id = SHA256(old_root_id || old_root_nonce). The old root becomes a child of the new root. leaf = false. last_claim = current period. Increment old root's nonce. Recompute tree_count.
Rekey
Rekey {
node_id hash
nonce uint
new_owners []pubkey — new owner set
new_thresh uint8 — new threshold
sigs []sig — current owners sign (threshold)
}Changes a node's owners and threshold. Constraints: len(new_owners) ≤ max_owners, 1 ≤ new_thresh ≤ len(new_owners). For persons and groups: len(new_owners) == count. For persons: new_thresh = 1. Increment nonce.
UTXO Layer
Output
Output {
owners []pubkey — max 8, public keys (for sig verification)
threshold uint8 — number of signatures required
amount uint64
slot uint32 — slot at creation
}Identified by (tx_hash, index) where tx_hash = SHA256(signing_message || sigs). Trie key: SHA256(tx_hash || index). This applies to all outputs: Transfer (index per output), Claim UBI and vote tokens (index per owner). Fee outputs use tx_hash = SHA256(number). Spent outputs are removed from the set.
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 xDecay[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.
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 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.
Pruning
An output 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 is added to the block fee. 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).
Transfer
Transfer {
inputs []Outpoint
outputs []Output
sigs []sig — one per input (threshold signatures for multisig)
}
Outpoint {
tx hash
index uint
}Spends UTXOs and creates new ones. Each input is signed by its owners — different owners can participate in the same transaction.
Validation: each input exists and is unspent. Each output amount > 0. Sum of output amounts ≤ sum of input spendable values. Difference is the transaction fee.
Processing: remove spent inputs from the coin trie. Create new outputs with slot = current slot.
Fees
Transaction fees are accumulated during the block and split 50-50 between the validator and the voter (the selected election entry's trie key). Odd unit to validator. Fee UTXOs use tx_hash = SHA256(number), index 0 for validator, 1 for voter. Each fee UTXO is {owners: [address], threshold: 1, amount: share, slot: current_slot}. A fee UTXO is only created when its share is > 0.
Claim
Claim {
node_id hash
nonce uint
sigs []sig — node's owners sign (threshold)
}Claims UBI and vote tokens. Node must exist in the tree with count > 0. last_claim must be < current period. Mints one UBI UTXO per owner in coin_trie: {owners: [owners[i]], threshold: 1, amount: UBIPerPeriod, slot: current_slot}. Creates one election token per owner in token_trie, each {owner: owners[i], mixed: 0}. Owner *i* receives output index *i* in both tries: SHA256(tx_hash ‖ i). The tries are separate key spaces, so identical IDs do not collide. Sets last_claim to current period. Increments nonce. Allowed in both phases — tokens claimed after the lock are not usable for voting but UBI is unconditional.
Trie Construction
All tries (coin, election, token) are binary Merkle tries. Path navigation follows the key bit-by-bit, MSB first. Left child = 0, right child = 1.
Leaf hash: SHA256(key || value), or SHA256(key) for entries without value
Branch hash: SHA256(bit_position || left_hash || right_hash)
Empty trie root: [32]byte{0}bit_position (uint8) is the key bit where the branch diverges. This commits the branch to its position in the trie, preventing proof providers from misrepresenting the trie structure.
The election trie branches include a count aggregate: SHA256(bit_position || count || left_hash || right_hash). All entries are committed, so count = total entries. Insert to a key that diverges from an existing leaf splices in a new branch at that bit position. Delete removes the leaf and collapses the parent if only one child remains.
Consensus
Block Production
Blocks are produced at fixed time intervals — one slot per interval. slot increments by 1 per slot interval; skipped slots leave gaps. time = genesis_time + slot × SlotSeconds. number increments by 1 per block. Each slot has one validator selected via the election trie.
If the selected candidate is offline, the slot is skipped and rand = H(prev_rand || 0) — same formula with a zero nonce. The same removal rule as selection applies: the entry is removed only if count > remaining_slots_in_period. Fork choice: the chain with the most blocks wins. Blocks before the previous period boundary are final — no reorg is accepted past that point.
The genesis header includes an election_trie[0] with at least one committed vote. This is sufficient for the initial validator to produce blocks until the population votes for the next.
Election
Validator selection operates in fixed-length periods of 4 weeks. Each period, vote tokens are claimed into token_trie, mixed for anonymity, and committed into election_trie[1]. At the end of the period, election_trie[1] rotates to election_trie[0] and election_trie[1] and token_trie are cleared. One entry is selected each block — its candidate validates that block.
Token Trie
Stores vote tokens, keyed by SHA256(tx_hash || index). Entry: {owner, mixed}. All tokens have amount=1. mixed starts at 0 from a Claim and increments with each mix. Claim creates entries here. Mix shuffles them. Vote consumes them into election_trie[1].
Election Trie
Each internal node carries the count of entries in its subtree. The root gives the total — the pool size for RNG selection.
Entries are keyed by the voter's address (the token owner after mixing). The value is a commit: H(validator || nonce). The commit hides which validator the voter chose until block production.
Mix
Shuffles vote tokens for anonymity. 1-to-1: number of inputs = number of outputs.
Inputs: existing entries in token_trie. One signature per input, by the entry's owner.
Outputs: {owner, mixed} back into token_trie. Constraint: each output's mixed is between 1 and max_mix. Constraint: sum(output mixed) ≥ sum(input mixed) + count(outputs).
Only allowed in the open phase.
Vote
Converts one token into a committed vote entry.
Vote {
token outpoint — token in token_trie
commit hash — H(validator || nonce)
sig sig — signed by token's owner
}Consumes the token from token_trie. Creates one entry in election_trie[1] keyed by token.owner with value commit.
Only allowed in the open phase.
The voter shares the nonce with the validator off-chain — the validator needs it to produce a block when the entry is selected. A zero nonce is technically possible but a random nonce is recommended for DoS protection. If the nonce is not shared, the slot is skipped.
Activation
Each period has two phases. In the first half, token_trie and election_trie[1] are open — tokens can be mixed and committed. At the midpoint, both lock. Claims (UBI + vote tokens) are allowed in both phases — late tokens simply cannot be used for voting. At the end of the period, election_trie[1] becomes election_trie[0], and election_trie[1] and token_trie are cleared.
The lock-to-activation gap ensures no one can predict which position the RNG will select.
Selection
rand mod count selects a position. The trie's cumulative sums enable traversal to the selected entry.
The selected entry's key is the voter (who receives half the fees). The validator reveals the preimage in the header: H(header.validator || header.nonce) == entry.commit. Since the commit hides which validator owns each slot, targeted DoS attacks on upcoming block producers are not possible. rand = H(prev_rand || header.nonce).
If count > remaining_slots_in_period, the entry is removed from the trie after selection. Otherwise, the entry stays — allowing it to be selected again. This ensures the pool never empties: at small populations, each person is selected multiple times; at large populations, entries are consumed and each person is selected at most once.
Header
Header {
number uint — block count since genesis
slot uint — slot number since genesis
time uint
tx_trie hash
coin_trie hash
token_trie hash
election_trie [2]hash
people_tree hash
rand hash — H(prev_rand ‖ nonce)
prev hash
validator pubkey
nonce hash — election commit preimage
sig sig — signed by validator
}people_tree is the Merkle root of the node tree. coin_trie is the root of the coin trie. token_trie stores vote tokens. election_trie[0] is the active election trie. election_trie[1] is the next period's committed entries. tx_trie commits to the set of operations in the block — keyed by tx_hash = SHA256(serialized_operation). There is no transaction chaining: state changes made by one operation are not visible to another in the same block. All operations validate against pre-block state. At most one people tree operation per node per block. number starts at 0. time is deterministic from slot: each slot is SlotSeconds apart.
Civil Registration Compatibility
The tree structure maps onto existing population registration infrastructure. A country, region, municipality, or village can be an org node with its citizens as children. The hierarchy does not prescribe a specific administrative structure — any nesting of individuals, groups, and organizations works.
Governance
The root node controls the top of the people tree. Adding a new level above the root is done via Crown — the current root signs the operation and becomes a child of the new root. Replacing the root against its will is a social hard fork: participants agree off-chain, the people_tree root in the header is updated at a coordinated block height, and the chain continues. The majority chain retains the network's value. No on-chain governance mechanism is required.
Signing Messages
The opcode prefix prevents cross-operation replay. All fields are big-endian.
Transfer: SHA256(0x00 || SHA256(inputs) || SHA256(outputs)) Mix: SHA256(0x01 || SHA256(inputs) || SHA256(outputs)) Vote: SHA256(0x02 || token_outpoint || commit) Add: SHA256(0x03 || parent_id || nonce || SHA256(owners) || threshold || leaf || count) Remove: SHA256(0x04 || parent_id || nonce || child_id) Move (parent): SHA256(0x05 || new_parent_id || nonce || child_id) Move (consent): SHA256(0x05 || new_parent_id || deadline || child_id) Leave: SHA256(0x06 || child_id || nonce) Crown: SHA256(0x07 || old_root_id || old_root_nonce || SHA256(new_owners) || new_thresh || new_count) Rekey: SHA256(0x08 || node_id || nonce || SHA256(new_owners) || new_thresh) Claim: SHA256(0x09 || node_id || nonce)
Unsigned operations:
Prune: SHA256(0xFF || utxo_id)
Primitives
SHA-256, Ed25519. Amounts are unsigned 64-bit integers. All fields are big-endian. Variable-length fields are serialized with a uint8 length prefix. All signatures include an opcode.
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.
| Constant | Value | Description |
|---|---|---|
| TOKEN | 10^8 | Base units per token (8 decimals) |
| SlotsPerPeriod | 40320 | 4 weeks in minutes |
| SlotSeconds | 60 | Block interval in seconds |
| 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 |
| max_mix | 10 | Maximum mixing depth for vote tokens |
| max_owners | 8 | Maximum owners per Node or Output |
| MaxBlockSize | consensus | Maximum block size in bytes |
| RentShift | 10 | Rent = 1 base unit per 1 024 slots |
period = slot / SlotsPerPeriod (integer division).