Architektur
Der technische Begleitband zum Whitepaper — wie der Referenz-Node den Entwurf umsetzt und welche Entscheidungen dahinterstehen. Erläuternd, nicht normativ.
Das Protokoll sind die Parameterdateien und die Golden Vectors, dazu die benannten Regeln, wo immer sie definiert sind; die Wire-Spezifikation trägt die Anforderungen der Peer-Ebene. Diese Seite erläutert diese Oberfläche und hält die Entscheidungen dahinter fest. Wo sie der normativen Oberfläche widerspricht, gewinnt die normative Oberfläche, und dieser Text wird korrigiert. Der vollständige technische Begleitband ist docs/ARCHITECTURE.md.
Zwei Prädikate, zwei Engines#
Das Leben eines Zertifikats, von Anfang bis Ende:
wallet network every full node
+------------+ gossip +--------------+ +---------------------+
| build cert | ---------> | cert topic | ------------> | STATELESS PIPELINE |
| (reads, | | (TLS gossip) | | V1..V8, batch sigs, |
| writes, | +--------------+ | native re-exec |
| sigs, seq, | | -> mark VALID |
| deposit) | +----------+----------+
+------------+ | mempool
v
miner (any node) +---------------------+
+-------------------+ block | FOLD (sequential) |
| assemble ordered | --------> | F-rules per cert: |
| hash list + bodies| gossip | APPLY / SKIP / DROP |
| + RandomX solve | | -> new state |
+-------------------+ +---------------------+
Gültigkeit ist zustandslos und parallel: sie läuft einmal je Zertifikat je Node, vor und unabhängig von Blöcken. Anwendbarkeit ist zustandsbehaftet und sequenziell: sie läuft im Fold an der festgeschriebenen Position des Zertifikats. Der Miner führt nichts aus; er ordnet Hashes, deren Validierung er bereits gesehen hat, und löst den Proof of Work. Der Fold ist eine enge Schleife über einen Arbeitssatz im Speicher — vergleichen, addieren, schreiben.
Technische Grundsätze#
| Grundsatz | |
|---|---|
| P1 | Der Fold ist unantastbar. Die Zustandsübergangsfunktion lebt in einem reinen Paket ohne I/O, ohne Uhren, ohne Goroutinen, ohne Map-Iteration und ohne Gleitkomma. Sie ist der einzige Code, dessen Fehler sich nachträglich nicht beheben lassen. Alles andere im Node ist austauschbare Verrohrung. |
| P2 | Determinismus schlägt Performance. Jede Optimierung, die Nichtdeterminismus riskiert, wird in Konsenscode abgelehnt. Performance gehört in die zustandslose Pipeline, wo sie gefahrlos ist. |
| P3 | Keine Admin-Schlüssel, keine privilegierte RPC. Es gibt keinen Codepfad, über den irgendein Schlüssel pausieren, aktualisieren, prägen oder reorganisieren könnte. Was nicht in den Fold-Regeln steht, existiert nicht. |
| P4 | Kleine Konsensoberfläche. core/ importiert nichts außerhalb der Standardbibliothek. Der Rest des Nodes darf Ökosystembibliotheken verwenden; der Kern nicht. |
| P5 | Spezifikation zuerst. Die Golden Vectors sind das Protokoll. Der Go-Code ist eine Referenzimplementierung; eine unabhängige Implementierung, die die Vektoren besteht, ist ein Peer, kein Fork. Das ist es, was es dem Betreuer erlaubt, irgendwann niemand Bestimmtes zu sein. |
| P6 | Reproduzierbar ab v0.1. Fixierte Go-Toolchain, -trimpath, fixierte Abhängigkeiten. Vertrauen wandert von der Binärdatei zum Code, und das ist das einzige Vertrauen, das ein anonymer Autor anbieten kann. |
| P7 | Eine Binärdatei, per Höhe freigeschaltet. Die Maschine, die Bond-Operationen und die Sequencer-Registrierung sind in jedes Release einkompiliert und werden unterhalb ihrer Aktivierungshöhe abgelehnt — Validate verlangt h1_vm ≥ h1_bond und verlangt, dass h1_vm auf eine Epochengrenze fällt. Eine Ära kommt, weil die Chain eine Zahl erreicht hat, nie weil Betreiber zum Upgrade aufgefordert wurden. |
P3 ist der Punkt, auf den ein skeptischer Leser am stärksten drücken sollte, und die Treasury ist die Stelle dafür: die Genesis enthält überhaupt keinen Schlüssel und keinen Ausgabepfad, es gibt also kein Privileg zu halten, zu delegieren oder zu stehlen. Das 3-aus-5-Quorum von Era 2 wird durch einen künftigen Hard Fork festgelegt — derselbe soziale Mechanismus wie bei jeder anderen Konsensänderung, derselben Ablehnung unterworfen und selbst dann nur in der Lage, genau eine Zelle zu bewegen. Ein Quorum, das nur erscheint, wenn das Netzwerk zustimmt, es einzuschreiben, und das dann eine Zelle hält, ist eine Ausgaberegel. Ein Admin-Schlüssel ist einer, der existiert, bevor irgendwer zugestimmt hat, und der an alles heranreicht.
Kryptografische Primitive#
| Rolle | Wahl | Warum |
|---|---|---|
| Signaturen | Ed25519 | Batch-Verifikation (die Auffahrt zur GPU), keine Malleability, winzige Schlüssel. Strenge, bei Genesis festgelegte Regeln: kanonische Kodierungen für den öffentlichen Schlüssel und R sind Pflicht, der öffentliche Schlüssel muss torsionsfrei und nicht von kleiner Ordnung sein, und die Verifikation erfolgt kofaktorlos. |
| Hashing | BLAKE3 | Zertifikats- und Block-IDs, Adressen, State Root. Schnell genug, um mit der Leitungsgeschwindigkeit der Weiterleitung zu hashen; parallelfreundlich für den State Root der Epoche. |
| Proof of Work | RandomX | CPU-optimiert. Das einzige cgo im Baum, hinter einem Build-Tag, und in einem Build ohne dieses Tag nicht vorhanden. pow_engine steht in der Konsenswurzel, eine Binärdatei mit der falschen Engine verweigert also den Start, statt den falschen Beweis anzunehmen. |
| Domänentrennung | verpflichtend | Jeder Hash ist blake3(tag ‖ payload). Signaturen signieren über die Chain-ID und die Konsenswurzel, was Replay sowohl zwischen Netzwerken als auch über zwei Inkarnationen eines Netzwerks hinweg ausschließt. |
Die Torsionsablehnung ist es, was den Batch-Pfad sicher macht. Ein Schlüssel gemischter Ordnung ist nicht von kleiner Ordnung, keine Sperrliste erreicht ihn also, und er ist genau die Stelle, an der ein kofaktorbehafteter Batch-Verifizierer und ein kofaktorloser Einzelverifizierer uneins sind. Liegen der Schlüssel und R in der Untergruppe primer Ordnung, sind die beiden beweisbar äquivalent — ein Batch-Verifizierer darf also kofaktorbehaftet sein, sofern er dieselben Kodierungs- und Torsionsregeln vor dem Batching anwendet. Diese Pflicht ist der Preis der Wahl, und den Batch-Verifizierer gibt es noch nicht.
Kanonische Kodierung und Bezeichner#
Alle Konsensobjekte sind SSZ-Container: eine einzige kanonische Byte-Kodierung, keine Map-Reihenfolge, keine Mehrdeutigkeit optionaler Felder, feste Offsets für billiges teilweises Parsen und native Merkleisierung.
cert_id = blake3("zcd/certid/v1" || ssz(certificate with an empty signature list))
cert_exemplar = blake3("zcd/cert/v1" || ssz(certificate))
block_id = blake3("zcd/block/v1" || ssz(header))
Die ersten beiden sind verschiedene Digests, die verschiedene Fragen beantworten, und eine Implementierung, die den einen dort verwendet, wo der andere hingehört, ist monetär kaputt. Die ID beantwortet ist diese Autorisierung abgerechnet worden; der Exemplar-Hash beantwortet belegen diese Bytes das. Die beiden teilen sich nie einen Schlüssel.
Signaturen liegen außerhalb des Urbildes der ID, weil eine Signatur ein randomisierter Nachweis ist: der Unterzeichner wählt die Nonce, jede Nonce ergibt eine weitere gültige und vollkommen kanonische Signatur über denselben Body, und kein Verifizierer kann prüfen, welche verwendet wurde. Lägen sie innerhalb, hätte eine Autorisierung unbegrenzt viele IDs, jede abrechenbar, jede von irgendeinem einzelnen ihrer erforderlichen Unterzeichner aus der Befugnis der anderen heraus erzeugbar.
Ein Gebot außerhalb der ID wäre ein Gebot, das jeder unterwegs umschreiben könnte — aufgebläht, um das Guthaben des Unterzeichners über die Grundgebühr zu verbrennen, oder auf null gesetzt, um das Zertifikat aus jedem Block herauszuhalten. Was ein Zertifikat zahlt, ist Teil dessen, was sein Unterzeichner autorisiert hat, also wird es gehasht und signiert. Eine spätere Kodierungsänderung, die “
Die Regel, die daraus folgt: parsen, nicht zweimal validieren. Das Dekodieren erzwingt die kanonische Form, ein dekodiertes Objekt ist also konstruktionsbedingt strukturell gültig, und die Regel-Engines prüfen die Form nie erneut.
Das Zellenmodell#
Eine Zelle ist der Wert an einem Slot. Zellwerte sind vorzeichenlose 256-Bit-Ganzzahlen, in 32 Bytes big-endian gespeichert. Fehlende Zellen lesen sich als null — null ist Abwesenheit, was eine Konsensanforderung ist und keine Bequemlichkeit der Implementierung: es hält den State Root zu einer Funktion des Zustands statt der Historie, die ihn hervorgebracht hat.
Getrennt davon führt das Protokoll eine Spent-Address-Registry: eine dauerhafte Konsensmenge von One-Shot-Adressen, deren Signaturbefugnis verbrannt wurde.
Addr = version || blake3("zcd/addr/v1" || version || payload)[:31]
| Version | Art | Belastungsautorisierung |
|---|---|---|
0x01 | One-Shot, Nutzer | Signatur des Eigentümers; jedes belastende Zertifikat muss zusätzlich ein ausdrückliches MARK_SPENT tragen. Nachdem es angewendet wurde, schlagen alle Lese- und Schreibvorgänge unter der Adresse für immer fehl. |
0x02 | persistent, Nutzer | Signatur des Eigentümers; für immer wiederverwendbar. Kann nie in die Spent-Registry gelangen. |
0x03 | Asset | Geregelt durch die unveränderlichen Befugniszellen des Assets. |
0x00 | Protokoll | Nur im Fold: Epoch Beacon, Grundgebührzellen, Coinbase-Maturity-Ring, Treasury-Zelle. |
0x04 | reserviert — Zelle mit verborgenem Wert (Era S) | In Era 0 unerreichbar. |
0x04 wird jetzt reserviert statt später vergeben, denn ein verborgener
Wert muss an seiner Adresse von einem gewöhnlichen unterscheidbar sein: ein Pedersen-Commitment
und ein 256-Bit-Guthaben sind beide 32 Bytes, ein Guarded Delta, das aus Versehen oder böswillig auf einen
Commitment-Slot zielt, ließe den Fold also eine Ganzzahl zu der Kodierung eines Kurvenpunkts addieren — eine Rechnung,
die jede Prüfung besteht und eine Zelle hinterlässt, die niemand je ausgeben kann. Diese Tabelle ist bei Genesis eingefroren,
also wird das Byte hier beansprucht und unerreichbar gelassen.
Der Registry-Eintrag wird nie verdichtet. Zellwerte unter einer ausgegebenen Adresse dürfen nach dem Undo Horizon beschnitten werden, aber der Eintrag, der die Adresse als ausgegeben festhält, ist das, was ihre Wiederauferstehung verhindert. Er ist reiner Anhänge-Konsenszustand, etwa 33 Bytes pro Adresse, für immer — das ehrliche offene Problem des Protokolls, strukturell geteilt mit jedem Nullifier-Set-Entwurf.
Zustandslose Gültigkeit und das Abrechnungsgesetz#
Die V-Regeln laufen auf jedem Zertifikat, parallel, vor der Aufnahme in den Mempool und während der Blockverifikation, und brauchen keinerlei Zustand: kanonische Form und Chain-ID; jede Signatur, die über die Signaturwurzel aufgeht; eine allein aus dem Zertifikat ableitbare Autorisierung; deklarierte Reads, die dem entsprechen, was das Programm herleitet; und ein Erstattungsziel, geprüft gegen das, was das Zertifikat selbst verbrennt.
Das Abrechnungsgesetz des Systems ist ein Satz, und es ist das, woran man sich halten sollte:
Eine Signatur, höchstens eine Abrechnung, nie an einer Position, der ihr Unterzeichner nicht ausweichen konnte.
Diese Spezifikation fügt dem Vokabular des Whitepapers einen Begriff hinzu: Drop, ein nicht abgerechnetes Nicht-Ereignis. Ein Zertifikat, das die Anwendung mit bereits verbrauchter Einlage erreicht, wird gedroppt — nicht abgerechnet, nicht als gesehen markiert, frei zur erneuten Einreichung gegen eine frische Einlage —, ehrliche Nutzer verlieren also nichts durch Wettläufe auf ihrer eigenen Einlagezelle.
Aufbau des Repositorys#
zycord/
spec/ parameter sets, golden vectors, library images <- THE PROTOCOL
core/ consensus-critical; standard library only (P4)
types/ crypto/ ssz/ u256/ state/ validity/ fold/ params/ genesis/
cevm/ the certificate-adapted EVM; vendored interpreter, pure Go
stdlib/ the pre-deployed library, its addresses and code hashes
pow/randomx/ the mainnet engine: vendored C++, cgo, behind a build tag
node/ storage/ chain/ verify/ mempool/ miner/ p2p/ sync/ rpc/ stratum/
wallet/ key management, certificate builders (reference; not consensus)
contracts/ the reference contracts, in Solidity
sim/ simulator, fuzz harnesses, differential refold, chaos soak
cmd/ zycordd, zcd
desktop/ the wallet in a native window — a separate Go module
docs/ architecture, protocol, operating guide, whitepaper
Abhängigkeitspfeile zeigen ausschließlich nach innen — node → core,
wallet → core, nie umgekehrt —, und nichts innerhalb von core/
greift über core/ und die Standardbibliothek hinaus. Eine Ausnahme, benannt und durchgesetzt:
core/pow/randomx ist das einzige cgo im Baum und übersetzt nur unter dem Build-Tag,
jeder Build ohne das Tag kommt also weiterhin mit der Standardbibliothek allein aus und braucht keine C-Toolchain. Die CI
führt die Prüfung aus, die das durchsetzt, denn die Drittanbieterprüfung greppt Modulpfade, und cgo hat
keine — wodurch die Regel von niemandem durchgesetzt wurde, bis sie hinzugefügt wurde.
Wie es getestet wird#
- Golden Vectors. Jede Fold-, Block- und Gültigkeitsregel hat positive und negative Fälle als
(pre-state, block) → (post-state | invalid, outcomes, fees). Die Suite ist der Kompatibilitätsvertrag für unabhängige Implementierungen. - Die Griefing-Suite. Erneute Aufnahme angewendeter und übersprungener Zertifikate, abgelaufene Aufnahme, unterbotene Aufnahme, abhängige Ketten unter Proposer-Umsortierung, Burn-and-Refund-Zyklen, Kreditstürme Dritter, Wettläufe an der Mint-Cap-Grenze.
- Eigenschaftsbasiert. Fold-Determinismus unter Permutation der Proposer-Reihenfolge, Delta-Kommutativität, ABA-Toleranz und Erhaltung — einschließlich Einlagen im Umlauf, dem Maturity-Ring und der Treasury-Zelle, denn eine Erhaltungsprüfung, die die Treasury auslässt, meldet jeden Block als wertschöpfend.
- Differenziell. Eine absichtlich naive zweite Fold-Implementierung, auf Offensichtlichkeit statt Geschwindigkeit geschrieben, wird gegen die echte gefuzzt. Divergenz blockiert ein Release.
- Adversarielle Simulation. Skip-Stürme, Wettläufe zur Einlagenleerung, Drop-stopfende Miner, Reorg-Folter, Zeitstempelmanipulation, Eclipse-Lite-Weiterleitungsszenarien. Szenariokonfigurationen sind eingecheckt; Läufe sind über den Seed reproduzierbar.
- Nebenläufigkeit, mit Absicht. Jede Komponente, die mehr als eine Goroutine berührt, hat einen Test, der nebenläufig ist, in der Form, die der Prozess tatsächlich verwendet.
-raceauf einer Suite mit einer einzigen Goroutine misst nichts und meldet Erfolg. - Der Chaos-Dauertest. Echte Node-Prozesse über echte Sockets hinter einem Proxy, der Latenz, Jitter, Verluste und Partitionen einspeist, wobei Nodes zufällig per
SIGKILLabgeschossen werden. Das ist die Oberfläche, die das Data Race fand, das die gesamte-race-Suite übersah.
Genesis ist ein Artefakt, keine Zeremonie#
zcd genesis gibt den Genesis-Block aus — leerer Zustand, initialisierte Beacon-Zellen,
leere Spent-Registry, keinerlei Zuteilungen — und seine ID. Der angekündigte
Launch legt sich Wochen im Voraus auf das Code-Tag, den Parameter-Hash, die Genesis-ID und den
Startzeitpunkt fest. Jeder kann alle vier in Millisekunden aus dem Quellcode auf jedem Rechner nachbauen.
Es gibt sonst nichts, dem zu vertrauen wäre.