// Hiercoin — Fractal Social Hierarchy UTXO with UBI by demurrage.
//
// A complete node in one file: a civil-registry Merkle tree, a UTXO
// set as a binary Merkle sum trie where all value decays 20 %/year
// (the decay IS the basic income), blocks in dictator mode (the
// validator signs everything; the majority-rule header fields are
// zeroed but structurally in place — see the "future" section), a
// wire format, an append-only block log fully re-verified on startup,
// and a JSON API with mempool and block production.
// Standard library only, Go >= 1.22.
//
//	go run hiercoin.go keygen
//	go run hiercoin.go init -dir data
//	go run hiercoin.go run  -dir data -listen 127.0.0.1:8080 -interval 5
//	go run hiercoin.go sign -seed @data/validator.seed -msg <hex>
//
// API: GET /status /node/{key} /balance/{key} /block/{seq} /mempool
//      POST /tx/prepare (unsigned tx -> bytes to sign)
//      POST /tx         (signed tx -> mempool)

package main

import (
	"crypto/ed25519"
	"crypto/rand"
	"crypto/sha256"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"sync"
	"time"
)

// =============================================================== enc

// buf is a tiny canonical encoder: fixed-width big-endian fields,
// u32 length prefixes for lists. All hashes and signatures in the
// system are computed over encodings produced by this type, so the
// byte layout here IS the wire/consensus format.
type buf struct{ b []byte }

func (w *buf) u8(x byte) { w.b = append(w.b, x) }

func (w *buf) u32(x uint32) {
	var t [4]byte
	binary.BigEndian.PutUint32(t[:], x)
	w.b = append(w.b, t[:]...)
}

func (w *buf) u64(x uint64) {
	var t [8]byte
	binary.BigEndian.PutUint64(t[:], x)
	w.b = append(w.b, t[:]...)
}

// u128 writes a non-negative big.Int as 16 bytes big-endian.
// Panics on overflow: amounts are defined as unsigned 128-bit.
func (w *buf) u128(x *big.Int) { w.b = append(w.b, U128Bytes(x)...) }

func (w *buf) bytes(p []byte) { w.b = append(w.b, p...) }

func (w *buf) boolb(v bool) {
	if v {
		w.u8(1)
	} else {
		w.u8(0)
	}
}

// U128Bytes encodes x as exactly 16 big-endian bytes.
func U128Bytes(x *big.Int) []byte {
	if x.Sign() < 0 || x.BitLen() > 128 {
		panic("amount out of u128 range")
	}
	var out [16]byte
	x.FillBytes(out[:])
	return out[:]
}

// H is SHA-256 over the concatenation of parts.
func H(parts ...[]byte) [32]byte {
	h := sha256.New()
	for _, p := range parts {
		h.Write(p)
	}
	var o [32]byte
	copy(o[:], h.Sum(nil))
	return o
}

var zero32 [32]byte

// ============================================================ amount

// Fixed-point scale: 1 TOKEN = 10^16 base units.
var (
	Scale = big.NewInt(10_000_000_000_000_000)
	TOKEN = big.NewInt(10_000_000_000_000_000)

	// Per-second decay factor at scale 10^16: ~0.8^(1/31557600).
	// 20% per year. Value fixed by the spec.
	DecayPerSecond = big.NewInt(9_999_999_929_290_076)
)

const SecondsPerYear = uint64(31_557_600)

// The normalization basis norm_time is FIXED at genesis t0 and never
// moves. Stored normalized values therefore grow by ×1.25 per year,
// which bounds the system's lifetime by construction:
//   · decay ticks coarser than per-second after ~76 years,
//   · u128 root sums overflow around year ~125 at 10^10 population
//     (year ≈ (128 − log2(pop) − 53.2) / 0.322),
//   · DecayPow(t−t0) underflows to 0 at year 166 — hard stop.
// A planned migration (re-basing all stored values, one hard fork)
// resets the clock.

// mulScale computes floor(a*b / Scale) for non-negative a, b.
// This is THE rounding rule of the system: floor at every step.
func mulScale(a, b *big.Int) *big.Int {
	r := new(big.Int).Mul(a, b)
	return r.Quo(r, Scale)
}

var (
	powMu    sync.Mutex
	powCache = map[uint64]*big.Int{}
)

// DecayPow returns decay^dt at scale 10^16, computed by binary
// exponentiation with floor rounding at every step. Deterministic
// and bit-reproducible: same dt always yields the same value.
func DecayPow(dt uint64) *big.Int {
	powMu.Lock()
	if c, ok := powCache[dt]; ok {
		r := new(big.Int).Set(c)
		powMu.Unlock()
		return r
	}
	powMu.Unlock()

	res := new(big.Int).Set(Scale)
	base := new(big.Int).Set(DecayPerSecond)
	for e := dt; e > 0; e >>= 1 {
		if e&1 == 1 {
			res = mulScale(res, base)
		}
		if e > 1 {
			base = mulScale(base, base)
		}
	}

	powMu.Lock()
	powCache[dt] = new(big.Int).Set(res)
	powMu.Unlock()
	return res
}

// Normalize converts a real amount at time t to its normalized value
// at reference time norm: normalized = floor(amount * Scale / decay^(t-norm)).
// Since decay^x <= Scale, normalized >= amount.
func Normalize(amount *big.Int, t, norm uint64) *big.Int {
	if t < norm {
		panic("Normalize: time before norm_time")
	}
	p := DecayPow(t - norm)
	r := new(big.Int).Mul(amount, Scale)
	return r.Quo(r, p)
}

// ValueAt converts a normalized value back to its real value at time T:
// real = floor(norm * decay^(T-normTime) / Scale).
func ValueAt(normVal *big.Int, T, normTime uint64) *big.Int {
	if T < normTime {
		panic("ValueAt: time before norm_time")
	}
	return mulScale(normVal, DecayPow(T-normTime))
}

// tokens returns n * TOKEN as a fresh big.Int.
func tokens(n uint64) *big.Int {
	return new(big.Int).Mul(new(big.Int).SetUint64(n), TOKEN)
}

// ClaimableAt implements the spec formula:
// claimable(T) = contribution × TOKEN × (1 − decay^(T − last_ubi)).
func ClaimableAt(own uint64, lastUBI, T uint64) *big.Int {
	if T <= lastUBI || own == 0 {
		return new(big.Int)
	}
	ct := tokens(own)
	return new(big.Int).Sub(ct, mulScale(ct, DecayPow(T-lastUBI)))
}

// ============================================================== keys

// PubKey is a raw Ed25519 public key.
type PubKey [32]byte

// Sig is a raw Ed25519 signature.
type Sig [64]byte

var (
	ZeroKey PubKey
	ZeroSig Sig
)

func GenKey() (ed25519.PrivateKey, PubKey) {
	_, priv, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		panic(err)
	}
	return priv, Pub(priv)
}

func Pub(priv ed25519.PrivateKey) PubKey {
	var k PubKey
	copy(k[:], priv.Public().(ed25519.PublicKey))
	return k
}

func Sign(priv ed25519.PrivateKey, msg []byte) Sig {
	var s Sig
	copy(s[:], ed25519.Sign(priv, msg))
	return s
}

func VerifySig(pub PubKey, msg []byte, sig Sig) bool {
	return ed25519.Verify(pub[:], msg, sig[:])
}

func (k PubKey) Short() string { return hex.EncodeToString(k[:4]) }

func shortHash(h [32]byte) string { return hex.EncodeToString(h[:8]) }

// ================================================================ tx

// Opcodes. Every signature in the system covers an encoding that
// begins with an opcode ("all signatures include an opcode").
const (
	OpClaim        byte = 0x01
	OpTransfer     byte = 0x02
	OpAdd          byte = 0x03
	OpRemove       byte = 0x04
	OpRekey        byte = 0x05
	OpSetValidator byte = 0x06

	// Reserved for the majority-rule upgrade (not active in dictator mode).
	OpProposer   byte = 0x07
	OpVoteClaim  byte = 0x08
	OpVoteMix    byte = 0x09
	OpVoteCommit byte = 0x0A

	OpConsent  byte = 0x33 // child consent inside Add
	OpTemplate byte = 0x54 // subtree template hashing
	OpHeader   byte = 0xF0
)

// Output as it appears inside a transaction. The spec's Output has a
// `time` field; consensus sets it to the block time at processing, so
// transactions only carry (amount, owner) and the chain stamps time.
type Output struct {
	Amount *big.Int // real value at creation (block) time
	Owner  PubKey
}

func encodeOutputs(w *buf, outs []Output) {
	w.u32(uint32(len(outs)))
	for i := range outs {
		w.u128(outs[i].Amount)
		w.bytes(outs[i].Owner[:])
	}
}

// Tx is anything that can be included in a block.
type Tx interface {
	// ID is the hash identifying the transaction. Signatures are
	// excluded from the ID (anti-malleability); for single-signer
	// transactions ID doubles as the signing hash.
	ID() [32]byte
}

// ---------------------------------------------------------------- Claim

// Claim mints accrued UBI for a tree node with contribution > 0.
type Claim struct {
	Key     PubKey
	Outputs []Output
	Nonce   uint64
	Sig     Sig
}

func (c *Claim) body() []byte {
	var w buf
	w.u8(OpClaim)
	w.bytes(c.Key[:])
	encodeOutputs(&w, c.Outputs)
	w.u64(c.Nonce)
	return w.b
}

func (c *Claim) ID() [32]byte      { return H(c.body()) }
func (c *Claim) SigHash() [32]byte { return c.ID() }

// -------------------------------------------------------------- Transfer

type Outpoint struct {
	Tx    [32]byte
	Index uint32
}

// Transfer spends UTXOs and creates new ones. One signature per input;
// inputs may have different owners. sum(outputs) <= sum(input values
// at block time); the difference is the fee, minted to the proposer
// (== validator in dictator mode).
type Transfer struct {
	Inputs  []Outpoint
	Outputs []Output
	Sigs    []Sig // one per input, over SigHash
}

func (t *Transfer) body() []byte {
	var w buf
	w.u8(OpTransfer)
	w.u32(uint32(len(t.Inputs)))
	for i := range t.Inputs {
		w.bytes(t.Inputs[i].Tx[:])
		w.u32(t.Inputs[i].Index)
	}
	encodeOutputs(&w, t.Outputs)
	return w.b
}

func (t *Transfer) ID() [32]byte      { return H(t.body()) }
func (t *Transfer) SigHash() [32]byte { return t.ID() }

// ------------------------------------------------------------------- Add

// NodeTemplate describes a subtree being added. Own is the node's own
// person contribution (person=1, group=N, org=0); tree_count is derived.
// Leaf is set by the parent at creation, per spec.
type NodeTemplate struct {
	Key      PubKey
	Leaf     bool
	Own      uint64
	Children []NodeTemplate
}

// Hash commits to the subtree shape and contributions. last_ubi /
// last_vote / tree_ubi are consensus-assigned at inclusion (block
// time) and therefore not part of the commitment.
func (t *NodeTemplate) Hash() [32]byte {
	var w buf
	w.u8(OpTemplate)
	w.bytes(t.Key[:])
	w.boolb(t.Leaf)
	w.u64(t.Own)
	w.u32(uint32(len(t.Children)))
	for i := range t.Children {
		ch := t.Children[i].Hash()
		w.bytes(ch[:])
	}
	return H(w.b)
}

func (t *NodeTemplate) countNodes() int {
	n := 1
	for i := range t.Children {
		n += t.Children[i].countNodes()
	}
	return n
}

// Add attaches a child subtree under a parent. The parent signs the
// operation; the subtree root key signs consent over hash+nonce+parent.
type Add struct {
	Parent   PubKey
	ChildKey PubKey
	Hash     [32]byte
	Nonce    uint64 // parent's nonce
	Consent  Sig    // by ChildKey over ConsentMsg
	Sig      Sig    // by Parent over SigHash

	// Transport of the subtree data; bound to the tx via Hash.
	Template NodeTemplate
}

func (a *Add) body() []byte {
	var w buf
	w.u8(OpAdd)
	w.bytes(a.Parent[:])
	w.bytes(a.ChildKey[:])
	w.bytes(a.Hash[:])
	w.u64(a.Nonce)
	return w.b
}

func (a *Add) ID() [32]byte      { return H(a.body()) }
func (a *Add) SigHash() [32]byte { return a.ID() }

// ConsentMsg: child signs hash + nonce + parent pubkey.
func ConsentMsg(hash [32]byte, nonce uint64, parent PubKey) []byte {
	var w buf
	w.u8(OpConsent)
	w.bytes(hash[:])
	w.u64(nonce)
	w.bytes(parent[:])
	return w.b
}

// ---------------------------------------------------------------- Remove

// Remove deletes a child and its entire subtree. Accrued UBI for every
// person in the subtree is automatically minted to their keys, as
// outputs of this transaction in pre-order.
type Remove struct {
	Parent PubKey
	Child  PubKey
	Nonce  uint64 // parent's nonce
	Sig    Sig
}

func (r *Remove) body() []byte {
	var w buf
	w.u8(OpRemove)
	w.bytes(r.Parent[:])
	w.bytes(r.Child[:])
	w.u64(r.Nonce)
	return w.b
}

func (r *Remove) ID() [32]byte      { return H(r.body()) }
func (r *Remove) SigHash() [32]byte { return r.ID() }

// ----------------------------------------------------------------- Rekey

type Rekey struct {
	Old   PubKey
	New   PubKey
	Nonce uint64
	Sig   Sig // by Old
}

func (r *Rekey) body() []byte {
	var w buf
	w.u8(OpRekey)
	w.bytes(r.Old[:])
	w.bytes(r.New[:])
	w.u64(r.Nonce)
	return w.b
}

func (r *Rekey) ID() [32]byte      { return H(r.body()) }
func (r *Rekey) SigHash() [32]byte { return r.ID() }

// ---------------------------------------------------------- SetValidator

type SetValidator struct {
	Validator PubKey
	Nonce     uint64 // root's nonce
	Sig       Sig    // by root key
}

func (s *SetValidator) body() []byte {
	var w buf
	w.u8(OpSetValidator)
	w.bytes(s.Validator[:])
	w.u64(s.Nonce)
	return w.b
}

func (s *SetValidator) ID() [32]byte      { return H(s.body()) }
func (s *SetValidator) SigHash() [32]byte { return s.ID() }

// ============================================================== tree

// PNode is a node in the people tree: a person, a group, or an org.
// Own is the node's own person contribution (tree_count minus the sum
// of the children's tree_counts, stored explicitly for convenience).
// OwnUBI is the node's own normalized UBI base:
//
//	OwnUBI = Normalize(Own × TOKEN, LastUBI, norm_time)
//
// so that Own×TOKEN×decay^(T−LastUBI) == ValueAt(OwnUBI, T) and the
// aggregated TreeUBI matches the spec's tree_ubi.
type PNode struct {
	Key      PubKey
	Leaf     bool
	Nonce    uint64
	LastUBI  uint64
	LastVote uint64 // unused in dictator mode, kept for the upgrade
	Own      uint64
	OwnUBI   *big.Int

	Children []*PNode
	Parent   *PNode

	// Aggregates (cached, recomputed bottom-up).
	TreeCount uint64
	TreeUBI   *big.Int

	hash [32]byte
}

// recompute refreshes aggregates and this node's hash from its
// children (which must already be up to date). Hash layout per spec:
// SHA-256(key || children || leaf || nonce || last_ubi || last_vote ||
// tree_count || tree_ubi).
func (n *PNode) recompute() {
	tc := n.Own
	tu := new(big.Int).Set(n.OwnUBI)
	var w buf
	w.bytes(n.Key[:])
	for _, c := range n.Children {
		tc += c.TreeCount
		tu.Add(tu, c.TreeUBI)
		w.bytes(c.hash[:])
	}
	n.TreeCount = tc
	n.TreeUBI = tu
	w.boolb(n.Leaf)
	w.u64(n.Nonce)
	w.u64(n.LastUBI)
	w.u64(n.LastVote)
	w.u64(n.TreeCount)
	w.u128(n.TreeUBI)
	n.hash = H(w.b)
}

func (n *PNode) Hash() [32]byte { return n.hash }

// PeopleTree is the population register.
type PeopleTree struct {
	Root  *PNode
	byKey map[PubKey]*PNode
}

// NewPeopleTree creates a tree with a single root node (typically a
// person with Own=1) whose UBI accrual starts at t0.
func NewPeopleTree(rootKey PubKey, own uint64, t0, norm uint64) *PeopleTree {
	r := &PNode{
		Key:     rootKey,
		Own:     own,
		LastUBI: t0,
		OwnUBI:  Normalize(tokens(own), t0, norm),
	}
	r.recompute()
	return &PeopleTree{Root: r, byKey: map[PubKey]*PNode{rootKey: r}}
}

func (t *PeopleTree) Get(k PubKey) *PNode { return t.byKey[k] }
func (t *PeopleTree) RootHash() [32]byte  { return t.Root.hash }
func (t *PeopleTree) Population() uint64  { return t.Root.TreeCount }

// Bubble recomputes aggregates and hashes from n up to the root.
func (t *PeopleTree) Bubble(n *PNode) {
	for ; n != nil; n = n.Parent {
		n.recompute()
	}
}

// UnclaimedAt: tree_count × TOKEN − tree_ubi × decay^(T − norm_time).
func (t *PeopleTree) UnclaimedAt(T, norm uint64) *big.Int {
	total := tokens(t.Root.TreeCount)
	u := new(big.Int).Sub(total, ValueAt(t.Root.TreeUBI, T, norm))
	if u.Sign() < 0 {
		u.SetInt64(0)
	}
	return u
}

const maxTemplateNodes = 4096

// checkTemplate validates a subtree template against the current tree:
// key uniqueness (globally and within the template), leaf consistency,
// and a sanity cap on size.
func (t *PeopleTree) checkTemplate(tpl *NodeTemplate) error {
	if tpl.countNodes() > maxTemplateNodes {
		return errors.New("template too large")
	}
	seen := map[PubKey]bool{}
	var walk func(n *NodeTemplate) error
	walk = func(n *NodeTemplate) error {
		if seen[n.Key] {
			return fmt.Errorf("duplicate key in template: %s", n.Key.Short())
		}
		if _, ok := t.byKey[n.Key]; ok {
			return fmt.Errorf("key already in tree: %s", n.Key.Short())
		}
		seen[n.Key] = true
		if n.Leaf && len(n.Children) > 0 {
			return errors.New("leaf node with children")
		}
		for i := range n.Children {
			if err := walk(&n.Children[i]); err != nil {
				return err
			}
		}
		return nil
	}
	return walk(tpl)
}

// DoAdd validates and attaches a subtree under parentKey. Every node in
// the subtree gets LastUBI = blockTime (per spec). The caller is
// responsible for signature/nonce checks and for incrementing the
// parent's nonce BEFORE calling (a single bubble covers everything).
func (t *PeopleTree) DoAdd(parentKey PubKey, tpl *NodeTemplate, blockTime, norm uint64) error {
	p := t.byKey[parentKey]
	if p == nil {
		return errors.New("parent not in tree")
	}
	if p.Leaf {
		return errors.New("parent is a leaf")
	}
	if err := t.checkTemplate(tpl); err != nil {
		return err
	}

	var build func(tp *NodeTemplate, parent *PNode) *PNode
	build = func(tp *NodeTemplate, parent *PNode) *PNode {
		n := &PNode{
			Key:     tp.Key,
			Leaf:    tp.Leaf,
			Own:     tp.Own,
			LastUBI: blockTime,
			OwnUBI:  Normalize(tokens(tp.Own), blockTime, norm),
			Parent:  parent,
		}
		for i := range tp.Children {
			n.Children = append(n.Children, build(&tp.Children[i], n))
		}
		n.recompute()
		t.byKey[n.Key] = n
		return n
	}
	child := build(tpl, p)
	p.Children = append(p.Children, child)
	t.Bubble(p)
	return nil
}

// RemovedPerson is a person entry collected while removing a subtree,
// used to auto-mint accrued UBI.
type RemovedPerson struct {
	Key     PubKey
	Own     uint64
	LastUBI uint64
}

// DoRemove detaches childKey's subtree from parentKey and returns all
// persons (Own > 0) in pre-order. The caller increments the parent's
// nonce before calling.
func (t *PeopleTree) DoRemove(parentKey, childKey PubKey) ([]RemovedPerson, error) {
	p := t.byKey[parentKey]
	if p == nil {
		return nil, errors.New("parent not in tree")
	}
	c := t.byKey[childKey]
	if c == nil {
		return nil, errors.New("child not in tree")
	}
	if c.Parent != p {
		return nil, errors.New("not a child of parent")
	}

	var persons []RemovedPerson
	var walk func(n *PNode)
	walk = func(n *PNode) {
		if n.Own > 0 {
			persons = append(persons, RemovedPerson{Key: n.Key, Own: n.Own, LastUBI: n.LastUBI})
		}
		delete(t.byKey, n.Key)
		for _, ch := range n.Children {
			walk(ch)
		}
	}
	walk(c)

	for i, ch := range p.Children {
		if ch == c {
			p.Children = append(p.Children[:i], p.Children[i+1:]...)
			break
		}
	}
	c.Parent = nil
	t.Bubble(p)
	return persons, nil
}

// DoRekey changes a node's key. Caller verifies the signature; the
// node's nonce is incremented here.
func (t *PeopleTree) DoRekey(old, new PubKey) error {
	n := t.byKey[old]
	if n == nil {
		return errors.New("node not in tree")
	}
	if _, ok := t.byKey[new]; ok {
		return errors.New("new key already in tree")
	}
	delete(t.byKey, old)
	n.Key = new
	n.Nonce++
	t.byKey[new] = n
	t.Bubble(n)
	return nil
}

// Clone deep-copies the tree.
func (t *PeopleTree) Clone() *PeopleTree {
	m := make(map[PubKey]*PNode, len(t.byKey))
	var cp func(n *PNode, parent *PNode) *PNode
	cp = func(n *PNode, parent *PNode) *PNode {
		c := &PNode{
			Key:       n.Key,
			Leaf:      n.Leaf,
			Nonce:     n.Nonce,
			LastUBI:   n.LastUBI,
			LastVote:  n.LastVote,
			Own:       n.Own,
			OwnUBI:    new(big.Int).Set(n.OwnUBI),
			Parent:    parent,
			TreeCount: n.TreeCount,
			TreeUBI:   new(big.Int).Set(n.TreeUBI),
			hash:      n.hash,
		}
		for _, ch := range n.Children {
			c.Children = append(c.Children, cp(ch, c))
		}
		m[c.Key] = c
		return c
	}
	root := cp(t.Root, nil)
	return &PeopleTree{Root: root, byKey: m}
}

// ============================================================== utxo

// Entry is an unspent output as stored in the set. Norm is the amount
// normalized to the current norm_time; real value at T is
// ValueAt(Norm, T, normTime).
type Entry struct {
	Op    Outpoint
	Norm  *big.Int
	Time  uint64 // creation (block) time
	Owner PubKey
}

// The UTXO commitment is a binary Merkle sum trie keyed by
// H(tx_hash || index). Internal nodes sum their children, so the root
// carries the total normalized supply. The structure is canonical for
// a given key set (independent of insertion order): internal nodes
// exist exactly along diverging key prefixes.
type tnode struct {
	leaf bool
	key  [32]byte // leaf only: full key
	e    *Entry   // leaf only
	l, r *tnode   // internal only
	sum  *big.Int
	h    [32]byte
}

func bitAt(k [32]byte, d int) int { return int(k[d>>3]>>(7-uint(d&7))) & 1 }

func (n *tnode) fix() {
	if n.leaf {
		n.sum = new(big.Int).Set(n.e.Norm)
		var w buf
		w.u8(0x00)
		w.bytes(n.key[:])
		w.u128(n.e.Norm)
		w.u64(n.e.Time)
		w.bytes(n.e.Owner[:])
		n.h = H(w.b)
		return
	}
	n.sum = new(big.Int)
	lh, rh := zero32, zero32
	if n.l != nil {
		n.sum.Add(n.sum, n.l.sum)
		lh = n.l.h
	}
	if n.r != nil {
		n.sum.Add(n.sum, n.r.sum)
		rh = n.r.h
	}
	var w buf
	w.u8(0x01)
	w.bytes(lh[:])
	w.bytes(rh[:])
	w.u128(n.sum)
	n.h = H(w.b)
}

func leafNode(key [32]byte, e *Entry) *tnode {
	n := &tnode{leaf: true, key: key, e: e}
	n.fix()
	return n
}

// splitLeaves builds the internal chain from depth d down to the first
// bit where the two keys diverge.
func splitLeaves(a, b *tnode, d int) *tnode {
	in := &tnode{}
	ba, bb := bitAt(a.key, d), bitAt(b.key, d)
	if ba == bb {
		c := splitLeaves(a, b, d+1)
		if ba == 0 {
			in.l = c
		} else {
			in.r = c
		}
	} else {
		if ba == 0 {
			in.l, in.r = a, b
		} else {
			in.l, in.r = b, a
		}
	}
	in.fix()
	return in
}

func insertRec(n *tnode, d int, lf *tnode) (*tnode, error) {
	if n == nil {
		return lf, nil
	}
	if n.leaf {
		if n.key == lf.key {
			return n, errors.New("duplicate utxo key")
		}
		return splitLeaves(n, lf, d), nil
	}
	var err error
	if bitAt(lf.key, d) == 0 {
		n.l, err = insertRec(n.l, d+1, lf)
	} else {
		n.r, err = insertRec(n.r, d+1, lf)
	}
	if err != nil {
		return n, err
	}
	n.fix()
	return n, nil
}

// deleteRec removes key and collapses now-redundant internals: when an
// internal is left with a single leaf child, the leaf is pulled up.
func deleteRec(n *tnode, d int, key [32]byte) (*tnode, *tnode) {
	if n == nil {
		return nil, nil
	}
	if n.leaf {
		if n.key == key {
			return nil, n
		}
		return n, nil
	}
	var rem *tnode
	if bitAt(key, d) == 0 {
		n.l, rem = deleteRec(n.l, d+1, key)
	} else {
		n.r, rem = deleteRec(n.r, d+1, key)
	}
	if rem == nil {
		return n, nil
	}
	if n.l == nil && n.r == nil {
		return nil, rem
	}
	if n.l == nil && n.r.leaf {
		return n.r, rem
	}
	if n.r == nil && n.l.leaf {
		return n.l, rem
	}
	n.fix()
	return n, rem
}

// UTXOSet combines the Merkle sum trie (commitment) with a direct map
// (O(1) validation lookups). Both are kept in sync.
type UTXOSet struct {
	root    *tnode
	entries map[Outpoint]*Entry
}

func NewUTXOSet() *UTXOSet { return &UTXOSet{entries: map[Outpoint]*Entry{}} }

func opKey(o Outpoint) [32]byte {
	var w buf
	w.bytes(o.Tx[:])
	w.u32(o.Index)
	return H(w.b)
}

func (u *UTXOSet) Insert(o Outpoint, norm *big.Int, time uint64, owner PubKey) error {
	if _, ok := u.entries[o]; ok {
		return errors.New("outpoint already exists")
	}
	if norm.Sign() < 0 || norm.BitLen() > 128 {
		return errors.New("normalized amount out of range")
	}
	e := &Entry{Op: o, Norm: new(big.Int).Set(norm), Time: time, Owner: owner}
	nr, err := insertRec(u.root, 0, leafNode(opKey(o), e))
	if err != nil {
		return err
	}
	u.root = nr
	u.entries[o] = e
	return nil
}

func (u *UTXOSet) Get(o Outpoint) *Entry { return u.entries[o] }

func (u *UTXOSet) Spend(o Outpoint) (*Entry, error) {
	e, ok := u.entries[o]
	if !ok {
		return nil, errors.New("output missing or already spent")
	}
	nr, rem := deleteRec(u.root, 0, opKey(o))
	if rem == nil {
		return nil, errors.New("trie desync")
	}
	u.root = nr
	delete(u.entries, o)
	return e, nil
}

// Sum is the total normalized supply. Real supply at T is
// ValueAt(Sum(), T, normTime).
func (u *UTXOSet) Sum() *big.Int {
	if u.root == nil {
		return new(big.Int)
	}
	return new(big.Int).Set(u.root.sum)
}

func (u *UTXOSet) Root() [32]byte {
	if u.root == nil {
		return zero32
	}
	return u.root.h
}

func (u *UTXOSet) Len() int { return len(u.entries) }

func (u *UTXOSet) ForEach(f func(*Entry)) {
	for _, e := range u.entries {
		f(e)
	}
}

// Clone deep-copies the set. Trie structure is canonical for a key
// set, so rebuilding by re-insertion yields an identical root.
func (u *UTXOSet) Clone() *UTXOSet {
	c := NewUTXOSet()
	for _, e := range u.entries {
		if err := c.Insert(e.Op, e.Norm, e.Time, e.Owner); err != nil {
			panic(err) // impossible: keys unique
		}
	}
	return c
}

// ============================================================= state

const (
	maxOutputs = 1024
	maxInputs  = 1024
)

// State is the full chain state between blocks.
type State struct {
	NormTime  uint64 // fixed reference time for normalized values (= genesis t0)
	Time      uint64 // time of the last applied block
	Seq       uint64 // seq of the last applied block
	LastHash  [32]byte
	Validator PubKey

	Tree *PeopleTree
	UTXO *UTXOSet
}

func (s *State) Clone() *State {
	c := *s
	c.Tree = s.Tree.Clone()
	c.UTXO = s.UTXO.Clone()
	return &c
}

// checkOutputs validates transaction outputs: positive amounts within
// u128, sane count. Returns the sum of amounts.
func checkOutputs(outs []Output, allowEmpty bool) (*big.Int, error) {
	if len(outs) > maxOutputs {
		return nil, errors.New("too many outputs")
	}
	if len(outs) == 0 && !allowEmpty {
		return nil, errors.New("no outputs")
	}
	sum := new(big.Int)
	for i := range outs {
		a := outs[i].Amount
		if a == nil || a.Sign() <= 0 || a.BitLen() > 128 {
			return nil, errors.New("invalid output amount")
		}
		sum.Add(sum, a)
	}
	if sum.BitLen() > 128 {
		return nil, errors.New("output sum overflow")
	}
	return sum, nil
}

// mintOutputs inserts outs as (txid, i) at block time T.
func (s *State) mintOutputs(txid [32]byte, outs []Output, T uint64) error {
	for i := range outs {
		norm := Normalize(outs[i].Amount, T, s.NormTime)
		if err := s.UTXO.Insert(Outpoint{Tx: txid, Index: uint32(i)}, norm, T, outs[i].Owner); err != nil {
			return err
		}
	}
	return nil
}

// ----------------------------------------------------------------- Claim

// applyClaim returns a fee: claimable(T) minus what the outputs mint.
// Outputs may sum to LESS than claimable — required for live drift,
// since claimable grows every second and a claim is signed before its
// block time is known. last_ubi advances to T regardless, so the
// remainder is not claimable again; it goes to the block's fee output.
func (s *State) applyClaim(c *Claim, T uint64) (*big.Int, error) {
	n := s.Tree.Get(c.Key)
	if n == nil {
		return nil, errors.New("claim: key not in tree")
	}
	if n.Own == 0 {
		return nil, errors.New("claim: node has no person contribution")
	}
	if c.Nonce != n.Nonce {
		return nil, fmt.Errorf("claim: bad nonce (have %d want %d)", c.Nonce, n.Nonce)
	}
	sh := c.SigHash()
	if !VerifySig(c.Key, sh[:], c.Sig) {
		return nil, errors.New("claim: bad signature")
	}
	want := ClaimableAt(n.Own, n.LastUBI, T)
	if want.Sign() == 0 {
		return nil, errors.New("claim: nothing claimable")
	}
	sum, err := checkOutputs(c.Outputs, false)
	if err != nil {
		return nil, fmt.Errorf("claim: %w", err)
	}
	if sum.Cmp(want) > 0 {
		return nil, fmt.Errorf("claim: outputs %s exceed claimable %s", sum, want)
	}
	if err := s.mintOutputs(c.ID(), c.Outputs, T); err != nil {
		return nil, fmt.Errorf("claim: %w", err)
	}
	n.LastUBI = T
	n.OwnUBI = Normalize(tokens(n.Own), T, s.NormTime)
	n.Nonce++
	s.Tree.Bubble(n)
	return new(big.Int).Sub(want, sum), nil
}

// -------------------------------------------------------------- Transfer

// applyTransfer returns the fee (inputs − outputs, real value at T).
func (s *State) applyTransfer(t *Transfer, T uint64) (*big.Int, error) {
	if len(t.Inputs) == 0 || len(t.Inputs) > maxInputs {
		return nil, errors.New("transfer: bad input count")
	}
	if len(t.Sigs) != len(t.Inputs) {
		return nil, errors.New("transfer: need one signature per input")
	}
	seen := map[Outpoint]bool{}
	entries := make([]*Entry, len(t.Inputs))
	for i, op := range t.Inputs {
		if seen[op] {
			return nil, errors.New("transfer: duplicate input")
		}
		seen[op] = true
		e := s.UTXO.Get(op)
		if e == nil {
			return nil, errors.New("transfer: input missing or spent")
		}
		entries[i] = e
	}
	sh := t.SigHash()
	for i, e := range entries {
		if !VerifySig(e.Owner, sh[:], t.Sigs[i]) {
			return nil, fmt.Errorf("transfer: bad signature for input %d", i)
		}
	}
	outSum, err := checkOutputs(t.Outputs, true)
	if err != nil {
		return nil, fmt.Errorf("transfer: %w", err)
	}
	inSum := new(big.Int)
	for _, e := range entries {
		inSum.Add(inSum, ValueAt(e.Norm, T, s.NormTime))
	}
	if outSum.Cmp(inSum) > 0 {
		return nil, fmt.Errorf("transfer: outputs %s exceed inputs %s", outSum, inSum)
	}
	for _, op := range t.Inputs {
		if _, err := s.UTXO.Spend(op); err != nil {
			return nil, err
		}
	}
	if err := s.mintOutputs(t.ID(), t.Outputs, T); err != nil {
		return nil, err
	}
	return new(big.Int).Sub(inSum, outSum), nil
}

// ------------------------------------------------------------------- Add

func (s *State) applyAdd(a *Add, T uint64) error {
	p := s.Tree.Get(a.Parent)
	if p == nil {
		return errors.New("add: parent not in tree")
	}
	if a.Nonce != p.Nonce {
		return fmt.Errorf("add: bad nonce (have %d want %d)", a.Nonce, p.Nonce)
	}
	sh := a.SigHash()
	if !VerifySig(a.Parent, sh[:], a.Sig) {
		return errors.New("add: bad parent signature")
	}
	if a.Template.Key != a.ChildKey {
		return errors.New("add: template root key mismatch")
	}
	if a.Template.Hash() != a.Hash {
		return errors.New("add: template hash mismatch")
	}
	if !VerifySig(a.ChildKey, ConsentMsg(a.Hash, a.Nonce, a.Parent), a.Consent) {
		return errors.New("add: bad child consent")
	}
	p.Nonce++
	if err := s.Tree.DoAdd(a.Parent, &a.Template, T, s.NormTime); err != nil {
		return fmt.Errorf("add: %w", err)
	}
	return nil
}

// ---------------------------------------------------------------- Remove

func (s *State) applyRemove(r *Remove, T uint64) error {
	p := s.Tree.Get(r.Parent)
	if p == nil {
		return errors.New("remove: parent not in tree")
	}
	if r.Nonce != p.Nonce {
		return fmt.Errorf("remove: bad nonce (have %d want %d)", r.Nonce, p.Nonce)
	}
	sh := r.SigHash()
	if !VerifySig(r.Parent, sh[:], r.Sig) {
		return errors.New("remove: bad signature")
	}
	p.Nonce++
	persons, err := s.Tree.DoRemove(r.Parent, r.Child)
	if err != nil {
		return fmt.Errorf("remove: %w", err)
	}
	// Auto-mint accrued UBI to each removed person's key, as outputs
	// of this transaction, in pre-order. Zero-claimable persons
	// (added at this very block time) are skipped.
	txid := r.ID()
	idx := uint32(0)
	for _, pr := range persons {
		amt := ClaimableAt(pr.Own, pr.LastUBI, T)
		if amt.Sign() == 0 {
			continue
		}
		norm := Normalize(amt, T, s.NormTime)
		if err := s.UTXO.Insert(Outpoint{Tx: txid, Index: idx}, norm, T, pr.Key); err != nil {
			return fmt.Errorf("remove mint: %w", err)
		}
		idx++
	}
	return nil
}

// ----------------------------------------------------------------- Rekey

func (s *State) applyRekey(r *Rekey, T uint64) error {
	n := s.Tree.Get(r.Old)
	if n == nil {
		return errors.New("rekey: node not in tree")
	}
	if r.Nonce != n.Nonce {
		return fmt.Errorf("rekey: bad nonce (have %d want %d)", r.Nonce, n.Nonce)
	}
	sh := r.SigHash()
	if !VerifySig(r.Old, sh[:], r.Sig) {
		return errors.New("rekey: bad signature")
	}
	return s.Tree.DoRekey(r.Old, r.New)
}

// ---------------------------------------------------------- SetValidator

func (s *State) applySetValidator(sv *SetValidator, T uint64) error {
	root := s.Tree.Root
	if sv.Nonce != root.Nonce {
		return fmt.Errorf("setvalidator: bad nonce (have %d want %d)", sv.Nonce, root.Nonce)
	}
	sh := sv.SigHash()
	if !VerifySig(root.Key, sh[:], sv.Sig) {
		return errors.New("setvalidator: bad root signature")
	}
	s.Validator = sv.Validator
	root.Nonce++
	s.Tree.Bubble(root)
	return nil
}

// ApplyTxs applies transactions in order and returns total fees.
func (s *State) ApplyTxs(txs []Tx, T uint64) (*big.Int, error) {
	fees := new(big.Int)
	for i, tx := range txs {
		var err error
		switch v := tx.(type) {
		case *Claim:
			var fee *big.Int
			fee, err = s.applyClaim(v, T)
			if err == nil {
				fees.Add(fees, fee)
			}
		case *Transfer:
			var fee *big.Int
			fee, err = s.applyTransfer(v, T)
			if err == nil {
				fees.Add(fees, fee)
			}
		case *Add:
			err = s.applyAdd(v, T)
		case *Remove:
			err = s.applyRemove(v, T)
		case *Rekey:
			err = s.applyRekey(v, T)
		case *SetValidator:
			err = s.applySetValidator(v, T)
		default:
			err = errors.New("unknown tx type")
		}
		if err != nil {
			return nil, fmt.Errorf("tx %d: %w", i, err)
		}
	}
	return fees, nil
}

// FeeOutpoint identifies the synthetic per-block fee output. The spec
// says fees are collected by the proposer but leaves the mechanism
// open; we mint a single output per block with a deterministic
// pseudo-txid derived from the block seq.
func FeeOutpoint(seq uint64) Outpoint {
	var w buf
	w.bytes([]byte("fee"))
	w.u64(seq)
	return Outpoint{Tx: H(w.b), Index: 0}
}

// SupplyAt is the real UTXO supply at time T from the trie root.
func (s *State) SupplyAt(T uint64) *big.Int {
	return ValueAt(s.UTXO.Sum(), T, s.NormTime)
}

// UnclaimedAt is the unclaimed UBI at time T from the tree root.
func (s *State) UnclaimedAt(T uint64) *big.Int {
	return s.Tree.UnclaimedAt(T, s.NormTime)
}

// ============================================================= block

// Header per spec. In dictator mode the proposer/vote machinery is
// dormant: Proposer, Rand, VoteTrie, NextVotes, PropTrie and PropSig
// are all zero, and the validator both decides contents and signs.
// The fields stay in the header so the majority-rule upgrade only
// starts filling them in — the wire format does not change.
type Header struct {
	Seq        uint64
	Time       uint64
	PeopleTree [32]byte
	UTXOTrie   [32]byte
	VoteTrie   [32]byte // zero (reserved)
	NextVotes  [32]byte // zero (reserved)
	PropTrie   [32]byte // zero (reserved)
	Prev       [32]byte
	Proposer   PubKey // zero (reserved)
	Validator  PubKey
	Rand       [32]byte // zero (reserved)
	PropSig    Sig      // zero (reserved)
	ValSig     Sig
}

func (h *Header) encode(withSigs bool) []byte {
	var w buf
	w.u8(OpHeader)
	w.u64(h.Seq)
	w.u64(h.Time)
	w.bytes(h.PeopleTree[:])
	w.bytes(h.UTXOTrie[:])
	w.bytes(h.VoteTrie[:])
	w.bytes(h.NextVotes[:])
	w.bytes(h.PropTrie[:])
	w.bytes(h.Prev[:])
	w.bytes(h.Proposer[:])
	w.bytes(h.Validator[:])
	w.bytes(h.Rand[:])
	if withSigs {
		w.bytes(h.PropSig[:])
		w.bytes(h.ValSig[:])
	}
	return w.b
}

// SigHash is what proposer and validator sign (opcode 0xF0 included,
// signatures excluded).
func (h *Header) SigHash() [32]byte { return H(h.encode(false)) }

// Hash identifies the block (signatures included).
func (h *Header) Hash() [32]byte { return H(h.encode(true)) }

// Block is a header plus the transactions that produce its state.
// There is no tx root in the spec header: the block commits to the
// *resulting* state, and verification re-executes the transactions.
type Block struct {
	Header Header
	Txs    []Tx
}

// execute runs the deterministic state transition shared by builder
// and verifier: clone, apply txs, mint the fee output.
// Returns the new state. The fee output (if any) is owned by the
// signing validator of THIS block, i.e. prev.Validator — a
// SetValidator inside the block takes effect from the next block.
func execute(prev *State, txs []Tx, T uint64) (*State, error) {
	if T < prev.Time {
		return nil, errors.New("block time before previous block")
	}
	ns := prev.Clone()
	fees, err := ns.ApplyTxs(txs, T)
	if err != nil {
		return nil, err
	}
	if fees.Sign() > 0 {
		norm := Normalize(fees, T, ns.NormTime)
		if err := ns.UTXO.Insert(FeeOutpoint(prev.Seq+1), norm, T, prev.Validator); err != nil {
			return nil, fmt.Errorf("fee mint: %w", err)
		}
	}
	return ns, nil
}

// BuildBlock executes txs on top of prev at time T and produces a
// signed block plus the resulting state. valPriv must be the key of
// prev.Validator (dictator mode: the validator builds and signs).
func BuildBlock(prev *State, txs []Tx, T uint64, valPriv ed25519.PrivateKey) (*Block, *State, error) {
	if Pub(valPriv) != prev.Validator {
		return nil, nil, errors.New("build: key is not the active validator")
	}
	ns, err := execute(prev, txs, T)
	if err != nil {
		return nil, nil, err
	}
	h := Header{
		Seq:        prev.Seq + 1,
		Time:       T,
		PeopleTree: ns.Tree.RootHash(),
		UTXOTrie:   ns.UTXO.Root(),
		Prev:       prev.LastHash,
		Validator:  prev.Validator,
		// VoteTrie/NextVotes/PropTrie/Proposer/Rand/PropSig: zero.
	}
	sh := h.SigHash()
	h.ValSig = Sign(valPriv, sh[:])
	ns.Seq = h.Seq
	ns.Time = T
	ns.LastHash = h.Hash()
	return &Block{Header: h, Txs: txs}, ns, nil
}

// VerifyBlock checks b against prev and, on success, returns the new
// state. Verification re-executes the transactions and requires the
// header roots to match exactly.
func VerifyBlock(prev *State, b *Block) (*State, error) {
	h := &b.Header
	if h.Seq != prev.Seq+1 {
		return nil, fmt.Errorf("verify: bad seq %d (want %d)", h.Seq, prev.Seq+1)
	}
	if h.Time < prev.Time {
		return nil, errors.New("verify: time before previous block")
	}
	if h.Prev != prev.LastHash {
		return nil, errors.New("verify: prev hash mismatch")
	}
	if h.Validator != prev.Validator {
		return nil, errors.New("verify: wrong validator")
	}
	// Dictator mode: the dormant fields must be zero.
	if h.Proposer != ZeroKey || h.Rand != zero32 ||
		h.VoteTrie != zero32 || h.NextVotes != zero32 || h.PropTrie != zero32 ||
		h.PropSig != ZeroSig {
		return nil, errors.New("verify: proposer/vote fields must be zero in dictator mode")
	}
	sh := h.SigHash()
	if !VerifySig(h.Validator, sh[:], h.ValSig) {
		return nil, errors.New("verify: bad validator signature")
	}
	ns, err := execute(prev, b.Txs, h.Time)
	if err != nil {
		return nil, fmt.Errorf("verify: %w", err)
	}
	if ns.Tree.RootHash() != h.PeopleTree {
		return nil, errors.New("verify: people_tree root mismatch")
	}
	if ns.UTXO.Root() != h.UTXOTrie {
		return nil, errors.New("verify: utxo_trie root mismatch")
	}
	ns.Seq = h.Seq
	ns.Time = h.Time
	ns.LastHash = h.Hash()
	return ns, nil
}

// Chain is the simplest possible container: current state plus the
// block history. Forkless by construction (single validator signer).
type Chain struct {
	State  *State
	Blocks []*Block
}

// NewChain creates genesis at time t0: a people tree with a single
// root person (Own=1) and an empty UTXO set. The genesis header is
// seq 0 with prev = zero, signed by the initial validator.
func NewChain(rootKey PubKey, validator PubKey, t0 uint64, valPriv ed25519.PrivateKey) (*Chain, error) {
	if Pub(valPriv) != validator {
		return nil, errors.New("genesis: private key does not match validator")
	}
	st := &State{
		NormTime:  t0,
		Time:      t0,
		Seq:       0,
		Validator: validator,
		Tree:      NewPeopleTree(rootKey, 1, t0, t0),
		UTXO:      NewUTXOSet(),
	}
	h := Header{
		Seq:        0,
		Time:       t0,
		PeopleTree: st.Tree.RootHash(),
		UTXOTrie:   st.UTXO.Root(),
		Validator:  validator,
	}
	sh := h.SigHash()
	h.ValSig = Sign(valPriv, sh[:])
	st.LastHash = h.Hash()
	return &Chain{State: st, Blocks: []*Block{{Header: h}}}, nil
}

// Produce builds the next block, self-verifies it against the current
// state (builder and verifier must agree bit for bit), and advances
// the chain.
func (c *Chain) Produce(txs []Tx, T uint64, valPriv ed25519.PrivateKey) (*Block, error) {
	b, ns, err := BuildBlock(c.State, txs, T, valPriv)
	if err != nil {
		return nil, err
	}
	vs, err := VerifyBlock(c.State, b)
	if err != nil {
		return nil, fmt.Errorf("self-verify failed: %w", err)
	}
	if vs.LastHash != ns.LastHash {
		return nil, errors.New("self-verify: state divergence")
	}
	c.State = ns
	c.Blocks = append(c.Blocks, b)
	return b, nil
}

// ============================================================ future

// This file is the dormant half of the spec: proposer rotation and
// voting ("folkstyret"). Nothing here is wired into consensus yet —
// dictator mode keeps the corresponding header fields zeroed. The
// types, opcodes and signing formats are defined now so that the
// upgrade is purely additive:
//
//   1. ProposerReg txs populate a prop_trie (same Merkle style as the
//      UTXO trie, keyed by H(key)); its root goes into Header.PropTrie.
//   2. VoteClaim/VoteMix/VoteCommit maintain next_votes; at each
//      period boundary next_votes becomes vote_trie (Header.VoteTrie /
//      Header.NextVotes).
//   3. Each block the active proposer reveals the next onion layer:
//      H(revealed) == previous layer; Header.Rand = prevRand XOR
//      revealed selects a committed vote, whose candidate proposes.
//      Header.Proposer + Header.PropSig become non-zero.
//   4. The validator keeps signing every header exactly as today, so
//      VerifyBlock only *adds* checks — existing ones stay unchanged.

// ProposerReg registers a proposer candidate with a hash-onion
// commitment H^n(seed). Inactive for N blocks after inclusion.
type ProposerReg struct {
	Key   PubKey
	Onion [32]byte // H^n(seed)
	Sig   Sig
}

func (p *ProposerReg) body() []byte {
	var w buf
	w.u8(OpProposer)
	w.bytes(p.Key[:])
	w.bytes(p.Onion[:])
	return w.b
}

func (p *ProposerReg) ID() [32]byte      { return H(p.body()) }
func (p *ProposerReg) SigHash() [32]byte { return p.ID() }

// VoteClaim mints one vote token per person and period into
// next_votes at the next free index. Updates last_vote (the PNode
// field already exists and is hashed — no tree format change needed).
type VoteClaim struct {
	Key   PubKey
	Nonce uint64
	Sig   Sig
}

func (v *VoteClaim) body() []byte {
	var w buf
	w.u8(OpVoteClaim)
	w.bytes(v.Key[:])
	w.u64(v.Nonce)
	return w.b
}

func (v *VoteClaim) ID() [32]byte      { return H(v.body()) }
func (v *VoteClaim) SigHash() [32]byte { return v.ID() }

// VoteToken is an entry in next_votes/vote_trie: fixed position,
// swappable owner, optional commitment.
type VoteToken struct {
	Idx    uint64
	Owner  PubKey
	Commit [32]byte // H(candidate || nonce); zero until committed
}

// VoteMix swaps the owners of two uncommitted tokens (same indices in,
// same indices out). Reuses the multi-owner signature pattern from
// Transfer: one signature per input token, over the shared body.
type VoteMix struct {
	IdxA, IdxB uint64
	SigA, SigB Sig
}

func (v *VoteMix) body() []byte {
	var w buf
	w.u8(OpVoteMix)
	w.u64(v.IdxA)
	w.u64(v.IdxB)
	return w.b
}

func (v *VoteMix) ID() [32]byte      { return H(v.body()) }
func (v *VoteMix) SigHash() [32]byte { return v.ID() }

// VoteCommit locks a token to H(candidate || nonce). A committed token
// can no longer be mixed.
type VoteCommit struct {
	Idx    uint64
	Commit [32]byte
	Sig    Sig // by the token's current owner
}

func (v *VoteCommit) body() []byte {
	var w buf
	w.u8(OpVoteCommit)
	w.u64(v.Idx)
	w.bytes(v.Commit[:])
	return w.b
}

func (v *VoteCommit) ID() [32]byte      { return H(v.body()) }
func (v *VoteCommit) SigHash() [32]byte { return v.ID() }

// ============================================================== wire

// Wire format: the full transport encoding of transactions and blocks,
// including signatures and (for Add) the subtree template. The hashed
// tx bodies (tx.go) are reused verbatim as prefixes, so wire bytes and
// consensus hashes can never drift apart. This is the format for the
// on-disk block log, and later for gossip / external auditors /
// proposer→validator handoff.

// rdr is the decoding counterpart of buf: it never panics, it
// accumulates the first error and returns zero values after it.
type rdr struct {
	b   []byte
	err error
}

func (r *rdr) need(n int) []byte {
	if r.err != nil {
		return make([]byte, n)
	}
	if len(r.b) < n {
		r.err = errors.New("wire: truncated")
		return make([]byte, n)
	}
	p := r.b[:n]
	r.b = r.b[n:]
	return p
}

func (r *rdr) u8() byte       { return r.need(1)[0] }
func (r *rdr) u32() uint32    { return binary.BigEndian.Uint32(r.need(4)) }
func (r *rdr) u64() uint64    { return binary.BigEndian.Uint64(r.need(8)) }
func (r *rdr) u128() *big.Int { return new(big.Int).SetBytes(r.need(16)) }
func (r *rdr) boolb() bool    { return r.u8() != 0 }

func (r *rdr) h32() (h [32]byte) { copy(h[:], r.need(32)); return }
func (r *rdr) key() (k PubKey)   { copy(k[:], r.need(32)); return }
func (r *rdr) sig() (s Sig)      { copy(s[:], r.need(64)); return }

func (r *rdr) done() error {
	if r.err == nil && len(r.b) > 0 {
		return errors.New("wire: trailing bytes")
	}
	return r.err
}

// ------------------------------------------------------------- outputs

func decodeOutputs(r *rdr) []Output {
	n := r.u32()
	if n > maxOutputs {
		r.err = errors.New("wire: too many outputs")
		return nil
	}
	outs := make([]Output, 0, n)
	for i := uint32(0); i < n && r.err == nil; i++ {
		outs = append(outs, Output{Amount: r.u128(), Owner: r.key()})
	}
	return outs
}

// ------------------------------------------------------------ template

func encodeTemplate(w *buf, t *NodeTemplate) {
	w.bytes(t.Key[:])
	w.boolb(t.Leaf)
	w.u64(t.Own)
	w.u32(uint32(len(t.Children)))
	for i := range t.Children {
		encodeTemplate(w, &t.Children[i])
	}
}

func decodeTemplate(r *rdr, budget *int) NodeTemplate {
	*budget--
	if *budget < 0 {
		r.err = errors.New("wire: template too large")
		return NodeTemplate{}
	}
	t := NodeTemplate{Key: r.key(), Leaf: r.boolb(), Own: r.u64()}
	n := r.u32()
	if n > uint32(maxTemplateNodes) {
		r.err = errors.New("wire: template too large")
		return t
	}
	for i := uint32(0); i < n && r.err == nil; i++ {
		t.Children = append(t.Children, decodeTemplate(r, budget))
	}
	return t
}

// ------------------------------------------------------------------ tx

// EncodeTx serializes a transaction: hashed body first, then the
// signatures, then (Add) the template.
func EncodeTx(t Tx) []byte {
	var w buf
	switch v := t.(type) {
	case *Claim:
		w.bytes(v.body())
		w.bytes(v.Sig[:])
	case *Transfer:
		w.bytes(v.body())
		for i := range v.Sigs { // count == len(Inputs), implied by body
			w.bytes(v.Sigs[i][:])
		}
	case *Add:
		w.bytes(v.body())
		w.bytes(v.Consent[:])
		w.bytes(v.Sig[:])
		encodeTemplate(&w, &v.Template)
	case *Remove:
		w.bytes(v.body())
		w.bytes(v.Sig[:])
	case *Rekey:
		w.bytes(v.body())
		w.bytes(v.Sig[:])
	case *SetValidator:
		w.bytes(v.body())
		w.bytes(v.Sig[:])
	default:
		panic("EncodeTx: unknown tx type")
	}
	return w.b
}

func decodeTx(r *rdr) Tx {
	switch op := r.u8(); op {
	case OpClaim:
		return &Claim{Key: r.key(), Outputs: decodeOutputs(r), Nonce: r.u64(), Sig: r.sig()}
	case OpTransfer:
		n := r.u32()
		if n > maxInputs {
			r.err = errors.New("wire: too many inputs")
			return nil
		}
		t := &Transfer{}
		for i := uint32(0); i < n && r.err == nil; i++ {
			t.Inputs = append(t.Inputs, Outpoint{Tx: r.h32(), Index: r.u32()})
		}
		t.Outputs = decodeOutputs(r)
		for i := uint32(0); i < n && r.err == nil; i++ {
			t.Sigs = append(t.Sigs, r.sig())
		}
		return t
	case OpAdd:
		a := &Add{Parent: r.key(), ChildKey: r.key(), Hash: r.h32(), Nonce: r.u64(),
			Consent: r.sig(), Sig: r.sig()}
		budget := maxTemplateNodes
		a.Template = decodeTemplate(r, &budget)
		return a
	case OpRemove:
		return &Remove{Parent: r.key(), Child: r.key(), Nonce: r.u64(), Sig: r.sig()}
	case OpRekey:
		return &Rekey{Old: r.key(), New: r.key(), Nonce: r.u64(), Sig: r.sig()}
	case OpSetValidator:
		return &SetValidator{Validator: r.key(), Nonce: r.u64(), Sig: r.sig()}
	default:
		r.err = fmt.Errorf("wire: unknown opcode 0x%02x", op)
		return nil
	}
}

// DecodeTx parses exactly one transaction.
func DecodeTx(b []byte) (Tx, error) {
	r := &rdr{b: b}
	t := decodeTx(r)
	if err := r.done(); err != nil {
		return nil, err
	}
	return t, nil
}

// --------------------------------------------------------------- block

// EncodeBlock: header (with sigs) + u32 tx count + per tx u32 len + bytes.
func EncodeBlock(b *Block) []byte {
	var w buf
	w.bytes(b.Header.encode(true))
	w.u32(uint32(len(b.Txs)))
	for _, t := range b.Txs {
		tb := EncodeTx(t)
		w.u32(uint32(len(tb)))
		w.bytes(tb)
	}
	return w.b
}

func decodeHeader(r *rdr) Header {
	if op := r.u8(); op != OpHeader && r.err == nil {
		r.err = fmt.Errorf("wire: bad header opcode 0x%02x", op)
	}
	return Header{
		Seq: r.u64(), Time: r.u64(),
		PeopleTree: r.h32(), UTXOTrie: r.h32(),
		VoteTrie: r.h32(), NextVotes: r.h32(), PropTrie: r.h32(),
		Prev: r.h32(), Proposer: r.key(), Validator: r.key(), Rand: r.h32(),
		PropSig: r.sig(), ValSig: r.sig(),
	}
}

const maxTxBytes = 1 << 22 // 4 MiB per tx, sanity cap

func DecodeBlock(b []byte) (*Block, error) {
	r := &rdr{b: b}
	blk := &Block{Header: decodeHeader(r)}
	n := r.u32()
	for i := uint32(0); i < n && r.err == nil; i++ {
		l := r.u32()
		if l > maxTxBytes {
			return nil, errors.New("wire: tx too large")
		}
		tb := r.need(int(l))
		if r.err != nil {
			break
		}
		t, err := DecodeTx(tb)
		if err != nil {
			return nil, fmt.Errorf("wire: tx %d: %w", i, err)
		}
		blk.Txs = append(blk.Txs, t)
	}
	if err := r.done(); err != nil {
		return nil, err
	}
	return blk, nil
}

// ============================================================= store

// The chain persists as an append-only block log. State is never
// written to disk — it is fully derived: on startup every block is
// re-verified with VerifyBlock (full re-execution + root comparison),
// so a node can only ever come up on a valid chain. The same record
// bytes are what external auditors would consume.
//
// Layout:
//   record 0: u32 len | rootKey(32) | t0(8) | genesis header(433)
//   record N: u32 len | EncodeBlock(block N)

type Store struct {
	f *os.File
}

func writeRec(w io.Writer, rec []byte) error {
	var l [4]byte
	binary.BigEndian.PutUint32(l[:], uint32(len(rec)))
	if _, err := w.Write(l[:]); err != nil {
		return err
	}
	_, err := w.Write(rec)
	return err
}

// CreateStore writes a fresh log for the given genesis.
func CreateStore(path string, rootKey PubKey, t0 uint64, gen *Header) (*Store, error) {
	f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o644)
	if err != nil {
		return nil, err
	}
	var w buf
	w.bytes(rootKey[:])
	w.u64(t0)
	w.bytes(gen.encode(true))
	if err := writeRec(f, w.b); err != nil {
		return nil, err
	}
	if err := f.Sync(); err != nil {
		return nil, err
	}
	return &Store{f: f}, nil
}

// genesisState rebuilds and validates the genesis state from the
// stored parameters + header (the header is checked, not trusted).
func genesisState(rootKey PubKey, t0 uint64, h *Header) (*State, error) {
	st := &State{
		NormTime:  t0,
		Time:      t0,
		Validator: h.Validator,
		Tree:      NewPeopleTree(rootKey, 1, t0, t0),
		UTXO:      NewUTXOSet(),
	}
	if h.Seq != 0 || h.Time != t0 || h.Prev != zero32 {
		return nil, errors.New("genesis: bad seq/time/prev")
	}
	if h.PeopleTree != st.Tree.RootHash() || h.UTXOTrie != st.UTXO.Root() {
		return nil, errors.New("genesis: root mismatch")
	}
	if h.Proposer != ZeroKey || h.Rand != zero32 || h.PropSig != ZeroSig ||
		h.VoteTrie != zero32 || h.NextVotes != zero32 || h.PropTrie != zero32 {
		return nil, errors.New("genesis: proposer/vote fields must be zero")
	}
	sh := h.SigHash()
	if !VerifySig(h.Validator, sh[:], h.ValSig) {
		return nil, errors.New("genesis: bad validator signature")
	}
	st.LastHash = h.Hash()
	return st, nil
}

// OpenStore reads the log, replays and verifies every block, and
// returns the store (positioned for appends) plus the resulting chain.
func OpenStore(path string) (*Store, *Chain, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, nil, err
	}
	rest := data
	next := func() ([]byte, error) {
		if len(rest) == 0 {
			return nil, io.EOF
		}
		if len(rest) < 4 {
			return nil, errors.New("store: truncated length")
		}
		l := binary.BigEndian.Uint32(rest[:4])
		rest = rest[4:]
		if uint32(len(rest)) < l {
			return nil, errors.New("store: truncated record")
		}
		rec := rest[:l]
		rest = rest[l:]
		return rec, nil
	}

	rec, err := next()
	if err != nil {
		return nil, nil, fmt.Errorf("store: genesis record: %w", err)
	}
	r := &rdr{b: rec}
	rootKey := r.key()
	t0 := r.u64()
	gh := decodeHeader(r)
	if err := r.done(); err != nil {
		return nil, nil, fmt.Errorf("store: genesis record: %w", err)
	}
	st, err := genesisState(rootKey, t0, &gh)
	if err != nil {
		return nil, nil, err
	}
	ch := &Chain{State: st, Blocks: []*Block{{Header: gh}}}

	for {
		rec, err := next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, nil, err
		}
		blk, err := DecodeBlock(rec)
		if err != nil {
			return nil, nil, fmt.Errorf("store: block %d: %w", ch.State.Seq+1, err)
		}
		ns, err := VerifyBlock(ch.State, blk)
		if err != nil {
			return nil, nil, fmt.Errorf("store: block %d: %w", blk.Header.Seq, err)
		}
		ch.State = ns
		ch.Blocks = append(ch.Blocks, blk)
	}

	f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0)
	if err != nil {
		return nil, nil, err
	}
	return &Store{f: f}, ch, nil
}

// Append durably writes one block.
func (s *Store) Append(b *Block) error {
	if err := writeRec(s.f, EncodeBlock(b)); err != nil {
		return err
	}
	return s.f.Sync()
}

// ============================================================ server

// Server: JSON API + mempool + block production loop. One mutex guards
// chain, store and mempool; every mutation goes through Chain.Produce
// (which self-verifies) followed by a durable Store.Append.

type Server struct {
	mu       sync.Mutex
	chain    *Chain
	store    *Store
	mempool  []Tx
	valPriv  ed25519.PrivateKey
	interval time.Duration
}

// nowT is the block clock: wall time, never before the chain tip
// (protects against clock steps backwards).
func (s *Server) nowT() uint64 {
	t := uint64(time.Now().Unix())
	if t < s.chain.State.Time {
		t = s.chain.State.Time
	}
	return t
}

// ------------------------------------------------------ production loop

func (s *Server) produceLoop() {
	tick := time.NewTicker(s.interval)
	for range tick.C {
		s.mu.Lock()
		s.produceLocked()
		s.mu.Unlock()
	}
}

func (s *Server) produceLocked() {
	if len(s.mempool) == 0 {
		return
	}
	T := s.nowT()
	// Select the valid subset in submission order: each candidate is
	// tried on a scratch clone so a failing tx cannot poison state.
	scratch := s.chain.State.Clone()
	var keep []Tx
	for _, tx := range s.mempool {
		trial := scratch.Clone()
		if _, err := trial.ApplyTxs([]Tx{tx}, T); err != nil {
			log.Printf("drop tx %x: %v", tx.ID(), err)
			continue
		}
		scratch = trial
		keep = append(keep, tx)
	}
	s.mempool = nil
	if len(keep) == 0 {
		return
	}
	b, err := s.chain.Produce(keep, T, s.valPriv)
	if err != nil {
		log.Printf("produce failed: %v", err)
		return
	}
	if err := s.store.Append(b); err != nil {
		log.Fatalf("store append failed: %v", err) // cannot continue safely
	}
	log.Printf("block %d @ %d: %d tx, people %x utxo %x",
		b.Header.Seq, b.Header.Time, len(b.Txs),
		b.Header.PeopleTree[:6], b.Header.UTXOTrie[:6])
}

// -------------------------------------------------------- JSON <-> tx

type jOutput struct {
	Amount string `json:"amount"` // decimal, base units (10^16 = 1 TOKEN)
	Owner  string `json:"owner"`  // hex 32B
}

type jOutpoint struct {
	Tx    string `json:"tx"` // hex 32B
	Index uint32 `json:"index"`
}

type jTemplate struct {
	Key      string      `json:"key"`
	Leaf     bool        `json:"leaf"`
	Own      uint64      `json:"own"`
	Children []jTemplate `json:"children,omitempty"`
}

type jTx struct {
	Type string `json:"type"` // claim|transfer|add|remove|rekey|setvalidator

	Key       string      `json:"key,omitempty"`
	Parent    string      `json:"parent,omitempty"`
	ChildKey  string      `json:"child_key,omitempty"`
	Child     string      `json:"child,omitempty"`
	Old       string      `json:"old,omitempty"`
	New       string      `json:"new,omitempty"`
	Validator string      `json:"validator,omitempty"`
	Nonce     uint64      `json:"nonce"`
	Outputs   []jOutput   `json:"outputs,omitempty"`
	Inputs    []jOutpoint `json:"inputs,omitempty"`
	Template  *jTemplate  `json:"template,omitempty"`

	Sig     string   `json:"sig,omitempty"`
	Sigs    []string `json:"sigs,omitempty"`
	Consent string   `json:"consent,omitempty"`
}

func hexN(s string, n int) ([]byte, error) {
	b, err := hex.DecodeString(s)
	if err != nil {
		return nil, err
	}
	if len(b) != n {
		return nil, fmt.Errorf("want %d bytes, got %d", n, len(b))
	}
	return b, nil
}

func pKey(s string) (k PubKey, err error) {
	b, err := hexN(s, 32)
	if err == nil {
		copy(k[:], b)
	}
	return
}

func pSig(s string) (g Sig, err error) {
	if s == "" { // allowed unsigned (for /tx/prepare)
		return
	}
	b, err := hexN(s, 64)
	if err == nil {
		copy(g[:], b)
	}
	return
}

func p32(s string) (h [32]byte, err error) {
	b, err := hexN(s, 32)
	if err == nil {
		copy(h[:], b)
	}
	return
}

func pAmount(s string) (*big.Int, error) {
	a, ok := new(big.Int).SetString(s, 10)
	if !ok || a.Sign() < 0 || a.BitLen() > 128 {
		return nil, errors.New("bad amount")
	}
	return a, nil
}

func pOutputs(js []jOutput) ([]Output, error) {
	var outs []Output
	for _, o := range js {
		a, err := pAmount(o.Amount)
		if err != nil {
			return nil, err
		}
		k, err := pKey(o.Owner)
		if err != nil {
			return nil, err
		}
		outs = append(outs, Output{Amount: a, Owner: k})
	}
	return outs, nil
}

func pTemplate(j *jTemplate) (NodeTemplate, error) {
	k, err := pKey(j.Key)
	if err != nil {
		return NodeTemplate{}, err
	}
	t := NodeTemplate{Key: k, Leaf: j.Leaf, Own: j.Own}
	for i := range j.Children {
		c, err := pTemplate(&j.Children[i])
		if err != nil {
			return t, err
		}
		t.Children = append(t.Children, c)
	}
	return t, nil
}

// toTx builds a transaction from JSON. For Add, the committed hash is
// derived from the template — the parent's signature covers it, so a
// tampered template simply fails signature verification.
func toTx(j *jTx) (Tx, error) {
	switch j.Type {
	case "claim":
		k, err := pKey(j.Key)
		if err != nil {
			return nil, err
		}
		outs, err := pOutputs(j.Outputs)
		if err != nil {
			return nil, err
		}
		sig, err := pSig(j.Sig)
		if err != nil {
			return nil, err
		}
		return &Claim{Key: k, Outputs: outs, Nonce: j.Nonce, Sig: sig}, nil
	case "transfer":
		t := &Transfer{}
		for _, in := range j.Inputs {
			h, err := p32(in.Tx)
			if err != nil {
				return nil, err
			}
			t.Inputs = append(t.Inputs, Outpoint{Tx: h, Index: in.Index})
		}
		outs, err := pOutputs(j.Outputs)
		if err != nil {
			return nil, err
		}
		t.Outputs = outs
		for _, s := range j.Sigs {
			g, err := pSig(s)
			if err != nil {
				return nil, err
			}
			t.Sigs = append(t.Sigs, g)
		}
		return t, nil
	case "add":
		if j.Template == nil {
			return nil, errors.New("add: template required")
		}
		p, err := pKey(j.Parent)
		if err != nil {
			return nil, err
		}
		ck, err := pKey(j.ChildKey)
		if err != nil {
			return nil, err
		}
		tpl, err := pTemplate(j.Template)
		if err != nil {
			return nil, err
		}
		consent, err := pSig(j.Consent)
		if err != nil {
			return nil, err
		}
		sig, err := pSig(j.Sig)
		if err != nil {
			return nil, err
		}
		return &Add{Parent: p, ChildKey: ck, Hash: tpl.Hash(), Nonce: j.Nonce,
			Consent: consent, Sig: sig, Template: tpl}, nil
	case "remove":
		p, err := pKey(j.Parent)
		if err != nil {
			return nil, err
		}
		c, err := pKey(j.Child)
		if err != nil {
			return nil, err
		}
		sig, err := pSig(j.Sig)
		if err != nil {
			return nil, err
		}
		return &Remove{Parent: p, Child: c, Nonce: j.Nonce, Sig: sig}, nil
	case "rekey":
		o, err := pKey(j.Old)
		if err != nil {
			return nil, err
		}
		n, err := pKey(j.New)
		if err != nil {
			return nil, err
		}
		sig, err := pSig(j.Sig)
		if err != nil {
			return nil, err
		}
		return &Rekey{Old: o, New: n, Nonce: j.Nonce, Sig: sig}, nil
	case "setvalidator":
		v, err := pKey(j.Validator)
		if err != nil {
			return nil, err
		}
		sig, err := pSig(j.Sig)
		if err != nil {
			return nil, err
		}
		return &SetValidator{Validator: v, Nonce: j.Nonce, Sig: sig}, nil
	}
	return nil, fmt.Errorf("unknown tx type %q", j.Type)
}

func fromTx(t Tx) *jTx {
	hx := func(b []byte) string { return hex.EncodeToString(b) }
	outs := func(os []Output) []jOutput {
		var r []jOutput
		for _, o := range os {
			r = append(r, jOutput{Amount: o.Amount.String(), Owner: hx(o.Owner[:])})
		}
		return r
	}
	switch v := t.(type) {
	case *Claim:
		return &jTx{Type: "claim", Key: hx(v.Key[:]), Nonce: v.Nonce,
			Outputs: outs(v.Outputs), Sig: hx(v.Sig[:])}
	case *Transfer:
		j := &jTx{Type: "transfer", Outputs: outs(v.Outputs)}
		for _, in := range v.Inputs {
			j.Inputs = append(j.Inputs, jOutpoint{Tx: hx(in.Tx[:]), Index: in.Index})
		}
		for _, s := range v.Sigs {
			j.Sigs = append(j.Sigs, hx(s[:]))
		}
		return j
	case *Add:
		var tpl func(t *NodeTemplate) *jTemplate
		tpl = func(t *NodeTemplate) *jTemplate {
			j := &jTemplate{Key: hx(t.Key[:]), Leaf: t.Leaf, Own: t.Own}
			for i := range t.Children {
				j.Children = append(j.Children, *tpl(&t.Children[i]))
			}
			return j
		}
		return &jTx{Type: "add", Parent: hx(v.Parent[:]), ChildKey: hx(v.ChildKey[:]),
			Nonce: v.Nonce, Template: tpl(&v.Template),
			Consent: hx(v.Consent[:]), Sig: hx(v.Sig[:])}
	case *Remove:
		return &jTx{Type: "remove", Parent: hx(v.Parent[:]), Child: hx(v.Child[:]),
			Nonce: v.Nonce, Sig: hx(v.Sig[:])}
	case *Rekey:
		return &jTx{Type: "rekey", Old: hx(v.Old[:]), New: hx(v.New[:]),
			Nonce: v.Nonce, Sig: hx(v.Sig[:])}
	case *SetValidator:
		return &jTx{Type: "setvalidator", Validator: hx(v.Validator[:]),
			Nonce: v.Nonce, Sig: hx(v.Sig[:])}
	}
	return nil
}

// ------------------------------------------------------------ handlers

func writeJSON(w http.ResponseWriter, code int, v any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(code)
	enc := json.NewEncoder(w)
	enc.SetIndent("", "  ")
	enc.Encode(v)
}

func jerr(w http.ResponseWriter, code int, err error) {
	writeJSON(w, code, map[string]string{"error": err.Error()})
}

func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
	s.mu.Lock()
	defer s.mu.Unlock()
	st := s.chain.State
	T := s.nowT()
	supply := st.SupplyAt(T)
	uncl := st.UnclaimedAt(T)
	sum := new(big.Int).Add(supply, uncl)
	target := new(big.Int).Mul(new(big.Int).SetUint64(st.Tree.Population()), TOKEN)
	writeJSON(w, 200, map[string]any{
		"seq":         st.Seq,
		"time":        st.Time,
		"now":         T,
		"norm_time":   st.NormTime,
		"validator":   hex.EncodeToString(st.Validator[:]),
		"people_root": hex.EncodeToString(func() []byte { h := st.Tree.RootHash(); return h[:] }()),
		"utxo_root":   hex.EncodeToString(func() []byte { h := st.UTXO.Root(); return h[:] }()),
		"population":  st.Tree.Population(),
		"utxo_count":  st.UTXO.Len(),
		"supply":      supply.String(),
		"unclaimed":   uncl.String(),
		"sum":         sum.String(),
		"target":      target.String(),
		"mempool":     len(s.mempool),
		"token":       TOKEN.String(),
	})
}

func (s *Server) handleNode(w http.ResponseWriter, r *http.Request) {
	k, err := pKey(r.PathValue("key"))
	if err != nil {
		jerr(w, 400, err)
		return
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	n := s.chain.State.Tree.Get(k)
	if n == nil {
		jerr(w, 404, errors.New("key not in tree"))
		return
	}
	T := s.nowT()
	var children []string
	for _, c := range n.Children {
		children = append(children, hex.EncodeToString(c.Key[:]))
	}
	parent := ""
	if n.Parent != nil {
		parent = hex.EncodeToString(n.Parent.Key[:])
	}
	writeJSON(w, 200, map[string]any{
		"key":           r.PathValue("key"),
		"leaf":          n.Leaf,
		"own":           n.Own,
		"nonce":         n.Nonce,
		"last_ubi":      n.LastUBI,
		"tree_count":    n.TreeCount,
		"claimable_now": ClaimableAt(n.Own, n.LastUBI, T).String(),
		"now":           T,
		"parent":        parent,
		"children":      children,
	})
}

func (s *Server) handleBalance(w http.ResponseWriter, r *http.Request) {
	k, err := pKey(r.PathValue("key"))
	if err != nil {
		jerr(w, 400, err)
		return
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	T := s.nowT()
	type ju struct {
		Tx      string `json:"tx"`
		Index   uint32 `json:"index"`
		Value   string `json:"value_now"`
		Created uint64 `json:"created"`
	}
	total := new(big.Int)
	var us []ju
	s.chain.State.UTXO.ForEach(func(e *Entry) {
		if e.Owner != k {
			return
		}
		v := ValueAt(e.Norm, T, s.chain.State.NormTime)
		total.Add(total, v)
		us = append(us, ju{Tx: hex.EncodeToString(e.Op.Tx[:]), Index: e.Op.Index,
			Value: v.String(), Created: e.Time})
	})
	sort.Slice(us, func(i, j int) bool {
		if us[i].Tx != us[j].Tx {
			return us[i].Tx < us[j].Tx
		}
		return us[i].Index < us[j].Index
	})
	writeJSON(w, 200, map[string]any{"now": T, "total": total.String(), "utxos": us})
}

func (s *Server) handleBlock(w http.ResponseWriter, r *http.Request) {
	var seq uint64
	if _, err := fmt.Sscan(r.PathValue("seq"), &seq); err != nil {
		jerr(w, 400, err)
		return
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if seq >= uint64(len(s.chain.Blocks)) {
		jerr(w, 404, errors.New("no such block"))
		return
	}
	b := s.chain.Blocks[seq]
	h := &b.Header
	hx := func(b []byte) string { return hex.EncodeToString(b) }
	var txs []*jTx
	for _, t := range b.Txs {
		txs = append(txs, fromTx(t))
	}
	writeJSON(w, 200, map[string]any{
		"seq": h.Seq, "time": h.Time,
		"people_tree": hx(h.PeopleTree[:]), "utxo_trie": hx(h.UTXOTrie[:]),
		"prev": hx(h.Prev[:]), "validator": hx(h.Validator[:]),
		"val_sig": hx(h.ValSig[:]),
		"hash":    hx(func() []byte { x := h.Hash(); return x[:] }()),
		"txs":     txs,
	})
}

func (s *Server) handleMempool(w http.ResponseWriter, r *http.Request) {
	s.mu.Lock()
	defer s.mu.Unlock()
	var out []map[string]string
	for _, t := range s.mempool {
		id := t.ID()
		out = append(out, map[string]string{
			"id": hex.EncodeToString(id[:]), "type": fromTx(t).Type})
	}
	writeJSON(w, 200, out)
}

// handleTxPrepare: submit an UNSIGNED tx, get back the exact bytes to
// sign (hex). For Add, both the parent's message and the child's
// consent message are returned. Nothing is queued.
func (s *Server) handleTxPrepare(w http.ResponseWriter, r *http.Request) {
	var j jTx
	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
		jerr(w, 400, err)
		return
	}
	t, err := toTx(&j)
	if err != nil {
		jerr(w, 400, err)
		return
	}
	id := t.ID()
	resp := map[string]any{
		"id":           hex.EncodeToString(id[:]),
		"sign_message": hex.EncodeToString(id[:]), // SigHash == ID
		"note":         "sign with ed25519 over sign_message bytes; put hex signature in 'sig' (or 'sigs', one per input) and POST /tx",
	}
	if a, ok := t.(*Add); ok {
		resp["consent_message"] = hex.EncodeToString(ConsentMsg(a.Hash, a.Nonce, a.Parent))
		resp["template_hash"] = hex.EncodeToString(a.Hash[:])
	}
	writeJSON(w, 200, resp)
}

func (s *Server) handleTxSubmit(w http.ResponseWriter, r *http.Request) {
	var j jTx
	if err := json.NewDecoder(r.Body).Decode(&j); err != nil {
		jerr(w, 400, err)
		return
	}
	t, err := toTx(&j)
	if err != nil {
		jerr(w, 400, err)
		return
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	// Dry-run against confirmed state + current mempool, at the
	// earliest possible inclusion time, for immediate feedback.
	T := s.nowT()
	trial := s.chain.State.Clone()
	for _, p := range s.mempool {
		trial.ApplyTxs([]Tx{p}, T) // best effort; conflicts re-checked at production
	}
	if _, err := trial.ApplyTxs([]Tx{t}, T); err != nil {
		jerr(w, 422, err)
		return
	}
	s.mempool = append(s.mempool, t)
	id := t.ID()
	writeJSON(w, 200, map[string]string{"id": hex.EncodeToString(id[:]), "status": "queued"})
}

func (s *Server) routes() *http.ServeMux {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /status", s.handleStatus)
	mux.HandleFunc("GET /node/{key}", s.handleNode)
	mux.HandleFunc("GET /balance/{key}", s.handleBalance)
	mux.HandleFunc("GET /block/{seq}", s.handleBlock)
	mux.HandleFunc("GET /mempool", s.handleMempool)
	mux.HandleFunc("POST /tx/prepare", s.handleTxPrepare)
	mux.HandleFunc("POST /tx", s.handleTxSubmit)
	mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "hiercoin — endpoints: GET /status /node/{key} /balance/{key} /block/{seq} /mempool; POST /tx/prepare /tx")
	})
	return mux
}

// =============================================================== cmd

// hiercoin — dictator-mode node for the Hiercoin chain.
//
//	hiercoin keygen                          generate a keypair (seed + pub)
//	hiercoin init -dir D [-t0 N] [-root-pub HEX]
//	                                      create validator key + genesis log
//	hiercoin run  -dir D [-listen A] [-interval SEC]
//	                                      replay log, serve API, produce blocks
//	hiercoin sign -seed HEX|@file -msg HEX   ed25519-sign a message (client helper)

func usage() {
	fmt.Fprintln(os.Stderr, "usage: hiercoin keygen | init | run | sign  (use -h per command)")
	os.Exit(2)
}

func main() {
	log.SetFlags(log.Ltime)
	if len(os.Args) < 2 {
		usage()
	}
	switch os.Args[1] {
	case "keygen":
		cmdKeygen()
	case "init":
		cmdInit(os.Args[2:])
	case "run":
		cmdRun(os.Args[2:])
	case "sign":
		cmdSign(os.Args[2:])
	default:
		usage()
	}
}

func cmdKeygen() {
	priv, pub := GenKey()
	fmt.Printf("seed %x\npub  %x\n", priv.Seed(), pub[:])
}

func loadSeed(spec string) (ed25519.PrivateKey, error) {
	s := spec
	if strings.HasPrefix(spec, "@") {
		b, err := os.ReadFile(spec[1:])
		if err != nil {
			return nil, err
		}
		s = strings.TrimSpace(string(b))
	}
	b, err := hex.DecodeString(s)
	if err != nil || len(b) != ed25519.SeedSize {
		return nil, fmt.Errorf("seed must be %d hex bytes", ed25519.SeedSize)
	}
	return ed25519.NewKeyFromSeed(b), nil
}

func cmdSign(args []string) {
	fs := flag.NewFlagSet("sign", flag.ExitOnError)
	seed := fs.String("seed", "", "hex seed or @file")
	msg := fs.String("msg", "", "hex message to sign")
	fs.Parse(args)
	priv, err := loadSeed(*seed)
	if err != nil {
		log.Fatal(err)
	}
	m, err := hex.DecodeString(*msg)
	if err != nil {
		log.Fatal(err)
	}
	sig := Sign(priv, m)
	fmt.Printf("%x\n", sig[:])
}

func seedPath(dir string) string { return filepath.Join(dir, "validator.seed") }
func logPath(dir string) string  { return filepath.Join(dir, "chain.log") }

func cmdInit(args []string) {
	fs := flag.NewFlagSet("init", flag.ExitOnError)
	dir := fs.String("dir", "hiercoin-data", "data directory")
	t0f := fs.Uint64("t0", 0, "genesis unix time (default: now)")
	rootPub := fs.String("root-pub", "", "root person pubkey hex (default: validator key)")
	fs.Parse(args)

	if err := os.MkdirAll(*dir, 0o755); err != nil {
		log.Fatal(err)
	}
	priv, pub := GenKey()
	if err := os.WriteFile(seedPath(*dir), []byte(hex.EncodeToString(priv.Seed())+"\n"), 0o600); err != nil {
		log.Fatal(err)
	}
	root := pub
	if *rootPub != "" {
		k, err := pKey(*rootPub)
		if err != nil {
			log.Fatalf("root-pub: %v", err)
		}
		root = k
	}
	t0 := *t0f
	if t0 == 0 {
		t0 = uint64(time.Now().Unix())
	}
	ch, err := NewChain(root, pub, t0, priv)
	if err != nil {
		log.Fatal(err)
	}
	if _, err := CreateStore(logPath(*dir), root, t0, &ch.Blocks[0].Header); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("initialized %s\n  t0        %d\n  root      %x\n  validator %x\n  seed      %s\n",
		*dir, t0, root[:], pub[:], seedPath(*dir))
}

func cmdRun(args []string) {
	fs := flag.NewFlagSet("run", flag.ExitOnError)
	dir := fs.String("dir", "hiercoin-data", "data directory")
	listen := fs.String("listen", "127.0.0.1:8080", "listen address")
	interval := fs.Uint("interval", 5, "block interval, seconds")
	fs.Parse(args)

	priv, err := loadSeed("@" + seedPath(*dir))
	if err != nil {
		log.Fatal(err)
	}
	store, chain, err := OpenStore(logPath(*dir))
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("replayed %d block(s), seq %d, population %d",
		len(chain.Blocks)-1, chain.State.Seq, chain.State.Tree.Population())
	if me := Pub(priv); me != chain.State.Validator {
		log.Fatalf("our key %x is not the current validator %x (SetValidator rotated?)",
			me[:6], chain.State.Validator[:6])
	}
	s := &Server{chain: chain, store: store, valPriv: priv,
		interval: time.Duration(*interval) * time.Second}
	go s.produceLoop()
	log.Printf("listening on http://%s (block interval %ds)", *listen, *interval)
	log.Fatal(http.ListenAndServe(*listen, s.routes()))
}
