====== std.blockchain.p2p — Blockchain P2P-Netzwerk ======
''std.blockchain.p2p'' implementiert eine TCP-basierte P2P-Kommunikationsschicht für Blockchain-Knoten. Jeder Knoten kann bis zu 32 Peers verbinden, Transaktionen und Blöcke broadcasten und eingehende Verbindungen akzeptieren.
→ [[lyx_-_programmiersprache:units:blockchain|std.blockchain (Kern)]] · [[lyx_-_programmiersprache:units|Standard Library]]
----
===== Protokoll =====
Binäres Nachrichtenformat: ''[1 Byte Typ][4 Bytes Länge BE][N Bytes Payload]''
^ Konstante ^ Wert ^ Richtung ^ Bedeutung ^
| ''BLP2P_MSG_QUERY_LATEST'' | 1 | → Peer | Letzten Block anfordern |
| ''BLP2P_MSG_RESP_LATEST'' | 2 | ← Peer | Antwort mit letztem Block |
| ''BLP2P_MSG_QUERY_CHAIN'' | 3 | → Peer | Vollständige Kette anfordern (ab Index) |
| ''BLP2P_MSG_RESP_CHAIN'' | 4 | ← Peer | Ketten-Antwort (definiert, **nicht implementiert**) |
| ''BLP2P_MSG_BROADCAST_TX'' | 5 | ↔ beide | Neue Transaktion verteilen |
| ''BLP2P_MSG_BROADCAST_BLOCK'' | 6 | ↔ beide | Neuen Block verteilen |
**Wichtig:** QUERY_CHAIN (3) wird gesendet wenn die eigene Kette kürzer ist, aber ''BLP2PHandleIncoming'' behandelt weder QUERY_CHAIN noch RESP_CHAIN. Chain-Synchronisation ist daher noch nicht vollständig implementiert.
Weitere Konstanten:
^ Konstante ^ Wert ^ Bedeutung ^
| ''BLP2P_MAX_PEERS'' | 32 | Maximale gleichzeitige Peer-Verbindungen |
| ''BLP2P_MSG_HDR_LEN'' | 5 | Header-Größe: 1 Typ + 4 Länge |
----
===== Node-Struktur (40 Bytes) =====
^ Offset ^ Feld ^ Beschreibung ^
| 0 | ''bc'' | Zeiger auf die zugehörige Blockchain (aus ''std.blockchain'') |
| 8 | ''listenPort'' | TCP-Port zum Lauschen |
| 16 | ''serverFd'' | Server-Socket-Deskriptor; -1 wenn nicht gestartet |
| 24 | ''peerCount'' | Anzahl aktiver Peer-Verbindungen |
| 32 | ''peerFds'' | Zeiger auf Array mit 32 × int64 Socket-FDs (-1 = freier Slot) |
**Offset-Konstanten:** ''BLP2P_NODE_BC'' (0), ''BLP2P_NODE_PORT'' (8), ''BLP2P_NODE_SERVER_FD'' (16), ''BLP2P_NODE_PEER_COUNT'' (24), ''BLP2P_NODE_PEER_FDS'' (32), ''BLP2P_NODE_SIZE'' (40)
----
===== Öffentliche API =====
==== Node-Lebenszyklus ====
^ Funktion ^ Rückgabe ^ Beschreibung ^
| ''BLP2PNodeNew(bc, port)'' | ''int64'' Node-Ptr | Erstellt P2P-Knoten; alle Peer-Slots auf -1 initialisiert |
| ''BLP2PNodeFree(node)'' | ''void'' | Schließt alle Verbindungen und gibt Node frei |
| ''BLP2PNodeStart(node)'' | ''int64'' 1/0 | Bindet Socket, startet Lauschen; setzt SO_REUSEADDR; 1 = OK |
| ''BLP2PNodeStop(node)'' | ''void'' | Schließt Server-Socket (Peer-Verbindungen bleiben offen) |
==== Verbindungen ====
^ Funktion ^ Rückgabe ^ Beschreibung ^
| ''BLP2PConnectPeer(node, ip, port)'' | ''int64'' 1/0 | Verbindet zu Peer; ''ip'' als int64 (4 Bytes big-endian); sendet sofort QUERY_LATEST; 0 wenn kein freier Slot |
| ''BLP2PPoll(node)'' | ''int64'' 1/0 | Akzeptiert eine neue eingehende Verbindung; sendet RESP_LATEST an neuen Peer |
**Achtung:** ''BLP2PPoll'' verwendet blockierendes ''accept'' trotz des Namens. Es wartet auf eine neue Verbindung und kehrt nicht sofort zurück wenn keine anliegt. Für nicht-blockierende Nutzung muss der Server-FD vorab auf non-blocking gesetzt werden.
==== Nachrichtenverarbeitung ====
^ Funktion ^ Rückgabe ^ Beschreibung ^
| ''BLP2PHandleIncoming(node, fd)'' | ''int64'' 1/0 | Liest und verarbeitet eine Nachricht vom angegebenen FD; 0 = Verbindung getrennt |
| ''BLP2PBroadcastTx(node, tx)'' | ''void'' | Serialisiert TX und sendet ihn an alle Peers |
| ''BLP2PBroadcastBlock(node, block)'' | ''void'' | Serialisiert Block und sendet ihn an alle Peers |
**BLP2PHandleIncoming** behandelt:
* **QUERY_LATEST** → antwortet mit aktuellem letzten Block
* **BROADCAST_TX** → deserialisiert TX und versucht ''BLAddTransaction''; bei Erfolg wird TX vom bc übernommen; bei Ablehnung wird TX freigegeben
* **BROADCAST_BLOCK** → siehe Block-Empfang unten
==== Block-Empfang (BROADCAST_BLOCK) ====
^ Situation ^ Aktion ^
| ''index == lastIdx + 1'' und Hash + PoW gültig | Block an Kette anhängen, Ledger aktualisieren, Block rebroadcasten |
| ''index > lastIdx + 1'' (eigene Kette kürzer) | QUERY_CHAIN senden (Antwort wird nicht verarbeitet — bekannte Einschränkung) |
| ''index ≤ lastIdx'' | Block verwerfen |
----
===== Deserializer =====
Die Deserializer-Funktionen werden intern von ''BLP2PHandleIncoming'' genutzt, sind aber auch öffentlich für eigene Protokoll-Erweiterungen.
^ Funktion ^ Rückgabe ^ Beschreibung ^
| ''BLDeserializeInt32(buf, off)'' | ''int64'' | Liest 4-Byte big-endian ab Offset ''off'' |
| ''BLDeserializeInt64(buf, off)'' | ''int64'' | Liest 8-Byte big-endian ab Offset ''off'' |
| ''BLDeserializeTx(buf, off, outPtr)'' | ''int64'' neuer Offset | Deserialisiert TX aus ''buf'' ab ''off''; schreibt allokierten TX-Ptr nach ''outPtr[0]''; gibt neuen Offset zurück |
''BLDeserializeTx'' allokiert intern einen neuen TX (224 Bytes). Der Aufrufer muss den TX freigeben wenn er nicht via ''BLAddTransaction'' an den Mempool übergeben wird.
----
===== IP-Adresse übergeben =====
''BLP2PConnectPeer'' erwartet die IPv4-Adresse als ''int64'' (4 Bytes, big-endian):
// 192.168.1.100 → (192 << 24) | (168 << 16) | (1 << 8) | 100
var ip: int64 := (192 << 24) | (168 << 16) | (1 << 8) | 100;
BLP2PConnectPeer(node, ip, 9000);
----
===== Vollständiges Beispiel =====
import std.blockchain;
import std.blockchain.p2p;
import std.alloc;
import std.io;
fn main(): int64 {
// Blockchain erstellen
var bc: int64 := BLBlockchainNew(1, 50000000, 10);
// P2P-Knoten auf Port 9000 starten
var node: int64 := BLP2PNodeNew(bc, 9000);
if BLP2PNodeStart(node) == 0 then {
PrintLn("Fehler: Port 9000 belegt"c);
BLP2PNodeFree(node);
BLBlockchainFree(bc);
return 1;
}
PrintLn("P2P-Knoten lauscht auf Port 9000"c);
// Zu einem bekannten Peer verbinden: 192.168.1.50:9000
var peerIp: int64 := (192 << 24) | (168 << 16) | (1 << 8) | 50;
if BLP2PConnectPeer(node, peerIp, 9000) == 1 then {
PrintLn("Verbunden mit 192.168.1.50"c);
}
// Hauptschleife (vereinfacht — in echtem Code: epoll/select verwenden)
var fds: int64 := peek64(node + BLP2P_NODE_PEER_FDS);
var i: int64 := 0;
while i < BLP2P_MAX_PEERS do {
var fd: int64 := peek64(fds + i * 8);
if fd != (0 - 1) then {
if BLP2PHandleIncoming(node, fd) == 0 then {
// Verbindung getrennt — Slot freigeben
poke64(fds + i * 8, 0 - 1);
}
}
i := i + 1;
}
BLP2PNodeStop(node);
BLP2PNodeFree(node);
BLBlockchainFree(bc);
return 0;
}
----
===== Einschränkungen =====
^ Einschränkung ^ Details ^
| Blockierendes ''accept'' | ''BLP2PPoll'' wartet auf neue Verbindungen — nicht für Event-Loop ohne epoll geeignet |
| Chain-Sync unvollständig | QUERY_CHAIN wird gesendet, RESP_CHAIN nicht verarbeitet — kein vollständiger Catch-up möglich |
| Max. 32 Peers | Feste Obergrenze — kein dynamisches Wachstum |
| Kein Peer-Discovery | Peer-Adressen müssen manuell via ''BLP2PConnectPeer'' eingetragen werden |
| Kein Timeout | Hängende Verbindungen blockieren ''BLP2PHandleIncoming'' |
| Nur IPv4 | Kein IPv6-Support |
Letzte Aktualisierung: 2026-06-13