File size: 6,316 Bytes
a6a5d8e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""Reciprocity ledger — the AYNI-OS reciprocity organism.

A real, append-only, double-entry give/take ledger over the empire's 14 organs.
Every resource movement is recorded as a KIPU receipt. Reciprocity (Ayni) is the
*pairing* of a take with one or more future gives that return >= the consumed amount
to the drained organ within a finite time-lag tau_max (Trivers 1971 "time-lagged
symbiosis"; Axelrod & Hamilton 1981 direct reciprocity).

This module is pure stdlib (open-source: Python only). No mysticism.
"""
from __future__ import annotations

import hashlib
import json
import time as _time
from dataclasses import asdict, dataclass
from typing import Optional

# Doctrine v11: 14 organs.
ORGANS = (
    "amaru", "sentra", "rosie", "vessels", "killinchu", "kanchay", "wayra",
    "puriq", "yuyay", "hukla", "khipu", "lambda_spine", "yawar", "tinkuy",
)
assert len(ORGANS) == 14, "Doctrine v11 LOCKED: 14 organs"


def _now() -> float:
    return _time.time()


@dataclass(frozen=True)
class Receipt:
    """A single KIPU ledger entry (event). Append-only, content-addressed.

    side:   'take' (organ consumes resource r, amount>0 leaving the organ)
            'give' (organ receives resource r, amount>0 entering the organ)
    pair_id: links a take to the give(s) that reciprocate it (Ayni pairing).
    prev_hash / entry_hash: KIPU receipt chain (tamper-evident).
    """
    seq: int
    ts: float
    organ: str
    side: str            # 'take' | 'give'
    resource: str
    amount: float
    pair_id: str
    prev_hash: str
    entry_hash: str = ""

    def signed_payload(self) -> str:
        d = {k: v for k, v in asdict(self).items() if k != "entry_hash"}
        return json.dumps(d, sort_keys=True, separators=(",", ":"))

    def compute_hash(self) -> str:
        return hashlib.sha256(self.signed_payload().encode()).hexdigest()


class ReciprocityLedger:
    """Append-only double-entry reciprocity ledger (event store)."""

    def __init__(self) -> None:
        self._entries: list[Receipt] = []

    # ---- write path -----------------------------------------------------
    def _append(self, organ: str, side: str, resource: str, amount: float,
                pair_id: str, ts: Optional[float] = None) -> Receipt:
        if organ not in ORGANS:
            raise ValueError(f"unknown organ {organ!r}")
        if side not in ("take", "give"):
            raise ValueError("side must be 'take' or 'give'")
        if amount < 0:
            raise ValueError("amount must be >= 0")
        seq = len(self._entries)
        prev_hash = self._entries[-1].entry_hash if self._entries else "GENESIS"
        r = Receipt(
            seq=seq, ts=ts if ts is not None else _now(), organ=organ,
            side=side, resource=resource, amount=amount, pair_id=pair_id,
            prev_hash=prev_hash,
        )
        r = Receipt(**{**asdict(r), "entry_hash": r.compute_hash()})
        self._entries.append(r)
        return r

    def record_exchange(self, *, taker: str, giver: str, resource: str,
                        amount: float, pair_id: str,
                        ts: Optional[float] = None) -> tuple[Receipt, Receipt]:
        """Record a balanced internal exchange as a DOUBLE-ENTRY pair:
        `giver` gives `amount` of `resource` to `taker` (taker's take is the
        consumption; giver's give is the reciprocation source). Internal exchanges
        net to zero across the empire (Ayni conservation, double-entry).
        Returns (take_receipt, give_receipt).
        """
        base = ts if ts is not None else _now()
        take = self._append(taker, "take", resource, amount, pair_id, ts=base)
        give = self._append(giver, "give", resource, amount, pair_id, ts=base)
        return take, give

    def reciprocate(self, *, organ: str, resource: str, amount: float,
                    pair_id: str, ts: Optional[float] = None) -> Receipt:
        """Record a future give that returns resource to a previously-drained organ.
        This is the Ayni 'pay-back' leg closing a pair_id (>= original take)."""
        return self._append(organ, "give", resource, amount, pair_id, ts=ts)

    def drain(self, *, organ: str, resource: str, amount: float,
              pair_id: str, ts: Optional[float] = None) -> Receipt:
        """Record an unmatched take (a net drain on `organ`)."""
        return self._append(organ, "take", resource, amount, pair_id, ts=ts)

    # ---- read path ------------------------------------------------------
    def entries(self, until_ts: Optional[float] = None) -> list[Receipt]:
        if until_ts is None:
            return list(self._entries)
        return [e for e in self._entries if e.ts <= until_ts]

    def verify_chain(self, until_ts: Optional[float] = None) -> bool:
        """Verify KIPU receipt chain integrity up to until_ts."""
        prev = "GENESIS"
        for e in self.entries(until_ts):
            if e.prev_hash != prev:
                return False
            if e.compute_hash() != e.entry_hash:
                return False
            prev = e.entry_hash
        return True

    def flows(self, organ: str, lo: float = float("-inf"),
              hi: float = float("inf")) -> tuple[float, float]:
        """Return (In_o, Out_o) over [lo, hi] for `organ`."""
        gin = sum(e.amount for e in self._entries
                  if e.organ == organ and e.side == "give" and lo <= e.ts <= hi)
        out = sum(e.amount for e in self._entries
                  if e.organ == organ and e.side == "take" and lo <= e.ts <= hi)
        return gin, out

    def ayni_coefficient(self, organ: str, lo: float = float("-inf"),
                         hi: float = float("inf")) -> float:
        """alpha_o = In / (In + Out); 0.5 when idle (both zero)."""
        gin, out = self.flows(organ, lo, hi)
        if gin + out == 0:
            return 0.5
        return gin / (gin + out)

    def to_jsonl(self) -> str:
        return "\n".join(json.dumps(asdict(e), sort_keys=True) for e in self._entries)

    @classmethod
    def from_jsonl(cls, text: str) -> "ReciprocityLedger":
        led = cls()
        for line in text.splitlines():
            if not line.strip():
                continue
            d = json.loads(line)
            led._entries.append(Receipt(**d))
        return led