====== Blockchain mit Lyx ====== Dieser Guide zeigt den praktischen Einsatz von ''std.blockchain'' und ''std.blockchain.p2p'': Schlüsselpaar-Generierung, Transaktionen erstellen und signieren, Mining, Kettenvalidierung und ein P2P-Netzwerk aufbauen. → [[lyx_-_programmiersprache:units:blockchain|std.blockchain (Unit-Referenz)]] · [[lyx_-_programmiersprache:units:blockchain:p2p|std.blockchain.p2p]] · [[lyx_-_programmiersprache:guides:welche-unit|Welche Unit?]] ---- ===== Wann Blockchain einsetzen? ===== ^ Szenario ^ Blockchain? ^ Alternative ^ | Dezentrales, manipulationssicheres Transaktionslog | **ja** | — | | Proof-of-Work als Mechanismus gegen Spam / Flood | **ja** | — | | Lernprojekt: Krypto + Konsens verstehen | **ja** | — | | Einfaches Audit-Log (kein Konsens nötig) | nein | ''std.log'' + signierte Einträge (''std.crypto.ecc'') | | Datenbank mit versionierter Geschichte | nein | PostgreSQL + Trigger / Eventstore | | Verteilte Datenhaltung mit SQL | nein | ''std.db.postgres'' + Replikation | | Mehr als 1 024 Adressen gleichzeitig | nein¹ | Ledger-Kapazität anpassen (''BL_LEDGER_CAP'') | ¹ ''BL_LEDGER_CAP = 1024'' ist in der Quelle fest. Bei größeren Netzwerken muss die Konstante vor dem Kompilieren erhöht werden. ---- ===== Konzeptüberblick ===== std.crypto.ecc std.crypto.sha256 │ │ ▼ ▼ Schlüsselpaar ──────▶ Adresse (32 Bytes) (priv 32 B / pub 64 B) SHA-256(pubKey) │ ▼ BLNewTransaction ──▶ BLSignTransaction ──▶ BLAddTransaction (Mempool) │ ▼ BLMinePendingTransactions (PoW-Schleife, blockiert) │ Block + Ledger │ BLIsValidChain (optional) Der **Ledger** ist eine immutable Hash-Tabelle. Jede ''BLApplyTransaction''- und ''BLApplyBlock''-Operation gibt einen **neuen** Ledger-Pointer zurück — der alte muss danach freigegeben werden. ---- ===== Schlüssel und Adressen ===== Lyx-Blockchain verwendet secp256k1 (gleicher Algorithmus wie Bitcoin). Die Adresse ist der SHA-256 des unkomprimierten Public Keys — kein Base58, keine Checksums. import std.crypto.ecc; import std.crypto.sha256; import std.blockchain; import std.alloc; import std.io; fn SchluesselUndAdresse(): void { // Schlüsselpaar generieren var priv: int64 := alloc(BL_PRIVKEY_LEN); // 32 Bytes var pub: int64 := alloc(BL_PUBKEY_LEN); // 64 Bytes X||Y ECCGenerateKeyPair(priv, pub); // Adresse = SHA-256(pubKey), 32 Bytes var addr: int64 := alloc(BL_ADDR_LEN); SHA256(pub, BL_PUBKEY_LEN, addr); // Adresse als Hex ausgeben var i: int64 := 0; while i < BL_ADDR_LEN do { Print(IntToStr(peek8(addr + i) & 0xFF)c); i := i + 1; } PrintLn(""c); free(priv, BL_PRIVKEY_LEN); free(pub, BL_PUBKEY_LEN); free(addr, BL_ADDR_LEN); } **Wichtig:** Private Keys sicher verwalten. Es gibt keine Wallet-Funktion in ''std.blockchain'' — Persistenz und Key-Management liegen beim Aufrufer. ---- ===== Blockchain erstellen ===== ''BLBlockchainNew'' erstellt eine neue Kette mit einem Genesis-Block. Der Genesis-Block hat Timestamp 0 und einen berechneten Hash. import std.blockchain; fn BlockchainErstellen(): int64 { // difficulty=1: Hash braucht 1 führendes Null-Byte // reward=50_000_000: 50 Coins Mining-Belohnung (1 Coin = 1_000_000 Basiseinheiten) // maxTxPerBlock=100: max 100 TXs pro Block var bc: int64 := BLBlockchainNew(1, 50 * BL_BASE_UNIT, 100); Print("Kette erstellt. Länge: "c); PrintLn(IntToStr(BLChainLength(bc))c); // → 1 (Genesis) return bc; // Aufrufer muss BLBlockchainFree(bc) aufrufen } ---- ===== Transaktionen erstellen und signieren ===== Eine Transaktion besteht aus drei Schritten: erstellen, signieren, dem Mempool hinzufügen. import std.blockchain; import std.crypto.ecc; import std.crypto.sha256; import std.alloc; import std.io; fn TransaktionSenden(bc: int64, senderPriv: int64, senderPub: int64, empfaengerAddr: int64, betragCoins: int64, gebuehrCoins: int64): void { // Aktuelle Nonce des Senders aus dem Ledger lesen var senderAddr: int64 := alloc(BL_ADDR_LEN); SHA256(senderPub, BL_PUBKEY_LEN, senderAddr); var nonce: int64 := BLBlockchainGetNonce(bc, senderAddr); // Transaktion erstellen (Signature-Feld noch Null) var tx: int64 := BLNewTransaction(senderPub, empfaengerAddr, betragCoins * BL_BASE_UNIT, gebuehrCoins * BL_BASE_UNIT, nonce); // Signieren mit Private Key if BLSignTransaction(tx, senderPriv) == 0 then { PrintLn("Fehler: Signierung fehlgeschlagen"c); BLTxFree(tx); free(senderAddr, BL_ADDR_LEN); return; } // Dem Mempool hinzufügen — BLAddTransaction übernimmt bei Erfolg die Ownership var ok: int64 := BLAddTransaction(bc, tx); if ok == 0 then { PrintLn("TX abgelehnt (Nonce, Guthaben oder Signatur ungültig)"c); BLTxFree(tx); // Aufrufer gibt frei wenn abgelehnt } else { PrintLn("TX akzeptiert"c); // tx NICHT mehr free'n — gehört jetzt dem Mempool } free(senderAddr, BL_ADDR_LEN); } **BLAddTransaction prüft automatisch:** * SHA-256(pubKey) == senderAddr (kein Key-Spoofing) * ECDSA-Signatur gültig * Nonce stimmt mit Ledger überein (kein Replay) * Guthaben ≥ Betrag + Gebühr * Kein Duplikat im Mempool ---- ===== Mining ===== ''BLMinePendingTransactions'' nimmt bis zu ''maxTxPerBlock'' Transaktionen aus dem Mempool (FIFO), fügt eine Coinbase-TX hinzu und löst den PoW. Die Funktion **blockiert** bis ein gültiger Hash gefunden wird. import std.blockchain; import std.io; fn NaechstenBlockMinen(bc: int64, minerAddr: int64): void { Print("Mining Block "c); Print(IntToStr(BLChainLength(bc))c); PrintLn("..."c); var block: int64 := BLMinePendingTransactions(bc, minerAddr); if block == 0 then { PrintLn("Mining fehlgeschlagen (Ledger-Fehler)"c); return; } // Block gehört jetzt der Kette — NICHT freigeben Print("Block gemined. Nonce: "c); PrintLn(IntToStr(peek64(block + BL_BLK_NONCE))c); Print("Kettenlaenge: "c); PrintLn(IntToStr(BLChainLength(bc))c); } **Coinbase-TX:** ''BLMinePendingTransactions'' erstellt automatisch eine Coinbase-Transaktion mit ''reward + Summe aller Gebühren''. Die Coinbase landet immer als erste TX im Block. ==== Schwierigkeitsanpassung ==== Alle 10 Blöcke wird die Schwierigkeit angepasst, sodass die durchschnittliche Blockzeit bei 10 Sekunden liegt. Der Änderungsfaktor ist auf ±4 begrenzt. ^ Schwierigkeit ^ Bedeutung ^ Typische Dauer (CPU-abhängig) ^ | 1 | 1 führendes Null-Byte | Millisekunden | | 2 | 2 führende Null-Bytes | Sekunden | | 3 | 3 führende Null-Bytes | Minuten | | 4+ | ... | exponentiell steigend | Für Tests und Entwicklung empfiehlt sich ''difficulty=1''. ---- ===== Guthaben und Nonce abfragen ===== import std.blockchain; import std.io; fn KontostandAusgeben(bc: int64, addr: int64): void { var balance: int64 := BLBlockchainGetBalance(bc, addr); var nonce: int64 := BLBlockchainGetNonce(bc, addr); Print("Guthaben: "c); Print(IntToStr(balance / BL_BASE_UNIT)c); PrintLn(" Coins"c); Print("Nonce: "c); PrintLn(IntToStr(nonce)c); } Die Nonce beginnt bei 0 und wird bei jeder akzeptierten TX um 1 erhöht. Die nächste TX muss genau die aktuelle Nonce aus dem Ledger verwenden — sonst wird sie von ''BLAddTransaction'' abgelehnt. ---- ===== Kette validieren ===== ''BLIsValidChain'' prüft die gesamte Kette von Block 1 bis zum aktuellen Kopf: Hash-Integrität, Verkettung, Merkle-Root, PoW und vollständiger Ledger-Replay. import std.blockchain; import std.io; fn KetteValidieren(bc: int64): void { if BLIsValidChain(bc) == 1 then { PrintLn("Kette gueltig"c); } else { PrintLn("Kette ungueltig — Manipulation erkannt!"c); } } **Achtung:** Bei langen Ketten ist ''BLIsValidChain'' aufwendig — es werden alle Blöcke und alle Transaktionen erneut verarbeitet. Im Produktionseinsatz nur gezielt aufrufen (z.B. beim Sync mit einem neuen Peer). ---- ===== P2P-Netzwerk ===== ==== Knoten starten ==== import std.blockchain; import std.blockchain.p2p; import std.io; fn P2PKnotenStarten(bc: int64, port: int64): int64 { var node: int64 := BLP2PNodeNew(bc, port); if BLP2PNodeStart(node) == 0 then { PrintLn("Fehler: Port bereits belegt oder Bind-Fehler"c); BLP2PNodeFree(node); return 0; } Print("P2P-Knoten lauscht auf Port "c); PrintLn(IntToStr(port)c); return node; // Aufrufer ruft BLP2PNodeFree(node) } ==== Zu einem Peer verbinden ==== ''BLP2PConnectPeer'' erwartet die IPv4-Adresse als Integer (4 Bytes, big-endian) und sendet sofort eine QUERY_LATEST-Nachricht, um den aktuellen Stand vom Peer abzuholen. import std.blockchain.p2p; import std.io; fn PeerVerbinden(node: int64): void { // 192.168.1.50 → (192 << 24) | (168 << 16) | (1 << 8) | 50 var ip: int64 := (192 << 24) | (168 << 16) | (1 << 8) | 50; var port: int64 := 9000; if BLP2PConnectPeer(node, ip, port) == 1 then { PrintLn("Verbunden"c); } else { PrintLn("Verbindung fehlgeschlagen (kein Slot oder TCP-Fehler)"c); } } Max. 32 Peers gleichzeitig. Wenn alle Slots belegt sind, gibt ''BLP2PConnectPeer'' 0 zurück ohne zu verbinden. ==== Nachrichten empfangen ==== ''BLP2PHandleIncoming'' liest **eine** Nachricht von einem bestimmten FD und verarbeitet sie. In einer echten Anwendung gehört das in eine Event-Schleife (epoll oder select). import std.blockchain.p2p; import std.io; fn EinfacheEventSchleife(node: int64): void { var fds: int64 := peek64(node + BLP2P_NODE_PEER_FDS); while 1 == 1 do { // Neue eingehende Verbindungen akzeptieren // Achtung: BLP2PPoll blockiert bis eine Verbindung anliegt BLP2PPoll(node); // Bestehende Verbindungen abfragen 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 { // Peer hat Verbindung getrennt — Slot freigeben poke64(fds + i * 8, 0 - 1); poke64(node + BLP2P_NODE_PEER_COUNT, peek64(node + BLP2P_NODE_PEER_COUNT) - 1); Print("Peer getrennt (Slot "c); Print(IntToStr(i)c); PrintLn(")"c); } } i := i + 1; } } } **Hinweis:** Die obige Schleife ist vereinfacht. In einer echten Anwendung sollte ''std.net.epoll'' verwendet werden, damit ''BLP2PPoll'' und ''BLP2PHandleIncoming'' nicht blockieren. ==== Transaktionen und Blöcke broadcasten ==== Nach dem Mining einen Block an alle verbundenen Peers verteilen: import std.blockchain; import std.blockchain.p2p; fn MineUndBroadcast(bc: int64, node: int64, minerAddr: int64): void { var block: int64 := BLMinePendingTransactions(bc, minerAddr); if block == 0 then { return; } // Block an alle Peers senden BLP2PBroadcastBlock(node, block); } Eine neue TX vor dem Mining an alle Peers verteilen: import std.blockchain; import std.blockchain.p2p; import std.io; fn TxEinreichenUndBroadcasten(bc: int64, node: int64, tx: int64): void { var ok: int64 := BLAddTransaction(bc, tx); if ok == 1 then { BLP2PBroadcastTx(node, tx); // tx gehört jetzt dem Mempool } else { PrintLn("TX abgelehnt"c); BLTxFree(tx); } } ---- ===== Vollständiges Beispiel: Zwei-Knoten-Netzwerk ===== import std.blockchain; import std.blockchain.p2p; import std.crypto.ecc; import std.crypto.sha256; import std.alloc; import std.io; fn main(): int64 { // Knoten A: Blockchain + P2P var bcA: int64 := BLBlockchainNew(1, 50 * BL_BASE_UNIT, 100); var nodeA: int64 := BLP2PNodeNew(bcA, 9001); BLP2PNodeStart(nodeA); // Schlüssel für Miner A var privA: int64 := alloc(BL_PRIVKEY_LEN); var pubA: int64 := alloc(BL_PUBKEY_LEN); ECCGenerateKeyPair(privA, pubA); var addrA: int64 := alloc(BL_ADDR_LEN); SHA256(pubA, BL_PUBKEY_LEN, addrA); // Schlüssel für Teilnehmer B var privB: int64 := alloc(BL_PRIVKEY_LEN); var pubB: int64 := alloc(BL_PUBKEY_LEN); ECCGenerateKeyPair(privB, pubB); var addrB: int64 := alloc(BL_ADDR_LEN); SHA256(pubB, BL_PUBKEY_LEN, addrB); // Block 1 minen: A erhält 50 Coins Mining-Belohnung BLMinePendingTransactions(bcA, addrA); Print("A nach Block 1: "c); PrintLn(IntToStr(BLBlockchainGetBalance(bcA, addrA) / BL_BASE_UNIT)c); // TX: A schickt 10 Coins an B (1 Coin Gebühr) var nonce: int64 := BLBlockchainGetNonce(bcA, addrA); var tx: int64 := BLNewTransaction(pubA, addrB, 10 * BL_BASE_UNIT, 1 * BL_BASE_UNIT, nonce); BLSignTransaction(tx, privA); var ok: int64 := BLAddTransaction(bcA, tx); if ok == 0 then { PrintLn("TX abgelehnt"c); BLTxFree(tx); } // Block 2 minen: enthält TX + Coinbase (50 + 1 = 51 Coins für A) var block2: int64 := BLMinePendingTransactions(bcA, addrA); if block2 != 0 then { BLP2PBroadcastBlock(nodeA, block2); } Print("A nach Block 2: "c); PrintLn(IntToStr(BLBlockchainGetBalance(bcA, addrA) / BL_BASE_UNIT)c); Print("B nach Block 2: "c); PrintLn(IntToStr(BLBlockchainGetBalance(bcA, addrB) / BL_BASE_UNIT)c); // Kettenvalidierung if BLIsValidChain(bcA) == 1 then { PrintLn("Kette A gueltig"c); } // Aufräumen BLP2PNodeStop(nodeA); BLP2PNodeFree(nodeA); BLBlockchainFree(bcA); // gibt Ledger, alle Blöcke, Mempool frei free(privA, BL_PRIVKEY_LEN); free(pubA, BL_PUBKEY_LEN); free(addrA, BL_ADDR_LEN); free(privB, BL_PRIVKEY_LEN); free(pubB, BL_PUBKEY_LEN); free(addrB, BL_ADDR_LEN); return 0; } ---- ===== Speicherverwaltung ===== Die Ownership-Regeln sind nicht intuitiv — dieser Überblick hilft Fehler zu vermeiden: ^ Objekt ^ Erstellt von ^ Freigabe ^ Besonderheit ^ | TX | ''BLNewTransaction'' / ''BLNewCoinbaseTx'' | ''BLTxFree(tx)'' | Nicht freigeben wenn ''BLAddTransaction'' = 1 | | Block | ''BLNewBlock'' | ''BLBlockFree(block)'' | Befreit txPtrs-Array, **nicht** die TX-Pointer darin | | Ledger | ''BLLedgerNew'' | ''BLLedgerFree(l)'' | ''BLApplyTransaction/Block'' gibt **neuen** Ptr zurück — alten danach freigeben | | Blockchain | ''BLBlockchainNew'' | ''BLBlockchainFree(bc)'' | Gibt Ledger + alle Blöcke + Mempool-TXs frei | | P2P-Node | ''BLP2PNodeNew'' | ''BLP2PNodeFree(node)'' | Schließt alle Peer-Verbindungen; bc wird **nicht** freigegeben | | Deserialisierter TX | ''BLDeserializeTx'' | ''BLTxFree(tx)'' | Wenn nicht via ''BLAddTransaction'' übergeben | **Immutable Ledger — typisches Muster:** // RICHTIG: alten Ledger nach Apply freigeben var oldLedger: int64 := peek64(bc + BL_BC_LEDGER); var newLedger: int64 := BLApplyTransaction(oldLedger, tx); if newLedger != 0 then { BLLedgerFree(oldLedger); poke64(bc + BL_BC_LEDGER, newLedger); } // FALSCH: alten Ledger nicht freigeben → Memory Leak // var newLedger: int64 := BLApplyTransaction(peek64(bc + BL_BC_LEDGER), tx); // poke64(bc + BL_BC_LEDGER, newLedger); Im normalen Anwendungsfall verwenden Senior-Entwickler ''BLBlockchainNew'' und ''BLAddTransaction''/''BLMinePendingTransactions'' — dann verwaltet ''bc'' den Ledger intern und das Muster ist nicht nötig. ---- ===== Fehlerbehandlung ===== ^ Funktion ^ Rückgabe 0 bedeutet … ^ Ursache (typisch) ^ | ''BLAddTransaction'' | TX abgelehnt | Nonce falsch, Guthaben zu gering, Signatur ungültig, Duplikat | | ''BLMinePendingTransactions'' | Mining-Fehler | Ledger-Anwendung fehlgeschlagen (interner Fehler) | | ''BLSignTransaction'' | Signierung fehlgeschlagen | Ungültiger Private Key | | ''BLVerifyTransaction'' | Signatur ungültig | Key/TX-Manipulation | | ''BLIsValidChain'' | Kette ungültig | Hash-Abweichung, falscher Ledger-Zustand, PoW-Lücke | | ''BLP2PNodeStart'' | Port-Fehler | Port belegt, fehlende Rechte | | ''BLP2PConnectPeer'' | Verbindung fehlgeschlagen | Host nicht erreichbar, kein freier Peer-Slot | | ''BLP2PHandleIncoming'' | Verbindung getrennt | Peer hat Socket geschlossen oder Lesefehler | ---- ===== Bekannte Einschränkungen ===== ^ Einschränkung ^ Details ^ | Ledger-Kapazität | Max. 1 024 Adressen (''BL_LEDGER_CAP''). Beim Überschreiten gibt ''BLApplyTransaction'' 0 zurück | | PoW blockiert | ''BLMinePendingTransactions'' kehrt erst zurück wenn ein Hash gefunden wurde | | Chain-Sync unvollständig | ''BLP2PHandleIncoming'' sendet QUERY_CHAIN wenn die eigene Kette kürzer ist, verarbeitet RESP_CHAIN aber nicht — kein vollständiger Catch-up möglich | | BLP2PPoll blockiert | Trotz des Namens verwendet ''BLP2PPoll'' blockierendes ''accept'' | | Keine Persistenz | Blockchain liegt vollständig im RAM — kein eingebautes Speichern | | Kein Fee-Markt | FIFO-Auswahl aus dem Mempool, keine Gebühren-Priorisierung | | Kein UTXO | Kontobasiertes Modell (wie Ethereum), kein UTXO (wie Bitcoin) | | Max. 32 Peers | Feste Obergrenze in ''BLP2P_MAX_PEERS'' | ---- ===== Weiterführend ===== * [[lyx_-_programmiersprache:units:blockchain|std.blockchain — Unit-Referenz]] — vollständige API mit Struct-Layouts und Offset-Konstanten * [[lyx_-_programmiersprache:units:blockchain:p2p|std.blockchain.p2p — Unit-Referenz]] — Protokoll-Details, Deserializer * [[lyx_-_programmiersprache:units:crypto:ecc|std.crypto.ecc]] — ECDSA secp256k1 (Signierung) * [[lyx_-_programmiersprache:units:crypto:sha256|std.crypto.sha256]] — SHA-256 (Hashing, Adressen) * [[lyx_-_programmiersprache:units:net:epoll|std.net.epoll]] — für nicht-blockierende P2P-Event-Schleifen Letzte Aktualisierung: 2026-06-13