====== std.blockchain — Blockchain-Kern (Serializer, Modelle, Ledger, Konsens) ====== ''std.blockchain'' implementiert eine vollständige Proof-of-Work-Blockchain in Lyx — ohne externe Bibliotheken. Die Unit ist in vier Module aufgeteilt, die aufeinander aufbauen: | **WP-BL-02 Serializer** | Big-Endian-Kodierung, Merkle-Root-Berechnung | | **WP-BL-03 Core Models** | Transaktions- und Block-Strukturen, Hashing, Signierung | | **WP-BL-04 Ledger** | Zustandsverwaltung (Guthaben, Nonce) als immutable Hash-Tabelle | | **WP-BL-05 Consensus** | Blockchain-Struct, Mempool, Mining, Kettenvalidierung | P2P-Netzwerk: → [[lyx_-_programmiersprache:units:blockchain:p2p|std.blockchain.p2p]] → [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:units:crypto|std.crypto]] · [[lyx_-_programmiersprache:guides:welche-unit|Welche Unit?]] ---- ===== Grundkonzepte ===== **Kryptographische Grundlagen:** * Public Key = 64 Bytes (X||Y, unkomprimierter secp256k1-Punkt) * Private Key = 32 Bytes * Adresse = SHA-256(Public Key), 32 Bytes — kein Base58, keine Checksums * Signatur = R||S compact, 64 Bytes (ECDSA secp256k1) * Alle Hashes = SHA-256, 32 Bytes **Beträge:** * Alle Beträge in Basis-Einheiten: 1 Coin = ''BL_BASE_UNIT'' = 1.000.000 * Kein Gleitkomma — alle Rechnungen in ''int64'' **Immutable Ledger-Pattern:** * ''BLApplyTransaction'' und ''BLApplyBlock'' geben **neue** Ledger-Pointer zurück * Der alte Ledger bleibt unverändert und muss vom Aufrufer freigegeben werden * Atomare Block-Anwendung: bei Fehler mitten im Block wird kein Zwischenzustand gespeichert ---- ===== Konstanten ===== ==== Kryptographie ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''BL_BASE_UNIT'' | 1.000.000 | Basis-Einheiten pro Coin | | ''BL_PUBKEY_LEN'' | 64 | Public Key (X||Y uncompressed) | | ''BL_PRIVKEY_LEN'' | 32 | Private Key | | ''BL_ADDR_LEN'' | 32 | Adresse = SHA-256(pubKey) | | ''BL_SIG_LEN'' | 64 | Signatur (R||S compact) | | ''BL_HASH_LEN'' | 32 | SHA-256-Hash | ==== Schwierigkeitsanpassung ==== ^ Konstante ^ Wert ^ Bedeutung ^ | ''BL_DIFFICULTY_INTERVAL'' | 10 | Alle 10 Blöcke Schwierigkeit anpassen | | ''BL_BLOCK_TARGET_NS'' | 10.000.000.000 | Ziel-Blockzeit: 10 Sekunden in Nanosekunden | | ''BL_MIN_DIFFICULTY'' | 1 | Mindest-Schwierigkeit | | ''BL_MAX_DIFFICULTY_FACTOR'' | 4 | Maximaler Änderungsfaktor pro Intervall | ---- ===== BLTransaction — Struktur (224 Bytes) ===== ^ Offset ^ Feld ^ Größe ^ Beschreibung ^ | 0 | ''senderAddr'' | 32 B | SHA-256(senderPubKey) — bei Coinbase: alles Nullen | | 32 | ''senderPubKey'' | 64 B | Uncompressed secp256k1 X||Y — bei Coinbase: alles Nullen | | 96 | ''receiverAddr'' | 32 B | Empfängeradresse | | 128 | ''amount'' | 8 B | Betrag in Basis-Einheiten | | 136 | ''fee'' | 8 B | Transaktionsgebühr | | 144 | ''nonce'' | 8 B | Sequenznummer des Senders (verhindert Replay) | | 152 | ''timestamp'' | 8 B | Erstellungszeit in Nanosekunden | | 160 | ''signature'' | 64 B | ECDSA-Signatur R||S — bei BLNewTransaction: Nullen | **Offset-Konstanten:** ''BL_TX_SENDER_ADDR'' (0), ''BL_TX_SENDER_PUB'' (32), ''BL_TX_RECV_ADDR'' (96), ''BL_TX_AMOUNT'' (128), ''BL_TX_FEE'' (136), ''BL_TX_NONCE'' (144), ''BL_TX_TIMESTAMP'' (152), ''BL_TX_SIG'' (160), ''BL_TX_SIZE'' (224) ---- ===== BLBlock — Struktur (144 Bytes Header) ===== ^ Offset ^ Feld ^ Größe ^ Beschreibung ^ | 0 | ''index'' | 8 B | Block-Höhe (0 = Genesis) | | 8 | ''timestamp'' | 8 B | Erstellungszeit in Nanosekunden | | 16 | ''prevHash'' | 32 B | SHA-256 des Vorgänger-Blocks | | 48 | ''merkleRoot'' | 32 B | Merkle-Wurzel aller Transaktionen | | 80 | ''nonce'' | 8 B | PoW-Nonce | | 88 | ''difficulty'' | 8 B | Anzahl führender Null-Bytes im gültigen Hash | | 96 | ''hash'' | 32 B | SHA-256 des serialisierten Headers | | 128 | ''txCount'' | 8 B | Anzahl der Transaktionen | | 136 | ''txPtrs'' | 8 B | Zeiger auf allokiertes Array von TX-Pointern | **Offset-Konstanten:** ''BL_BLK_INDEX'' (0), ''BL_BLK_TIMESTAMP'' (8), ''BL_BLK_PREV_HASH'' (16), ''BL_BLK_MERKLE'' (48), ''BL_BLK_NONCE'' (80), ''BL_BLK_DIFFICULTY'' (88), ''BL_BLK_HASH'' (96), ''BL_BLK_TX_COUNT'' (128), ''BL_BLK_TX_PTRS'' (136), ''BL_BLK_SIZE'' (144) ---- ===== Ledger — Struktur ===== Der Ledger ist eine **feste Hash-Tabelle** mit 1024 Buckets und linearem Probing. Er speichert Guthaben und Nonce pro Adresse. ^ Konstante ^ Wert ^ Bedeutung ^ | ''BL_LEDGER_CAP'' | 1024 | Anzahl Buckets (fix) | | ''BL_LEDGER_BUCKET'' | 56 | Bucket-Größe: addr(32) + balance(8) + nonce(8) + used(8) | | ''BL_LEDGER_HDR'' | 8 | Header-Größe | | ''BL_LEDGER_SIZE'' | 57.352 | Gesamtgröße: 8 + 1024 × 56 | Bucket-Offsets: ''BL_BUCKET_ADDR'' (0), ''BL_BUCKET_BALANCE'' (32), ''BL_BUCKET_NONCE'' (40), ''BL_BUCKET_USED'' (48) ---- ===== WP-BL-02 Serializer ===== ^ Funktion ^ Signatur ^ Beschreibung ^ | ''BLSerializeInt32'' | ''(v, out): void'' | Schreibt 4-Byte big-endian nach ''out'' | | ''BLSerializeInt64'' | ''(v, out): void'' | Schreibt 8-Byte big-endian nach ''out'' | | ''BLMerkleRoot'' | ''(txPtrs, count, out): void'' | Berechnet Merkle-Wurzel von ''count'' Transaktionen; schreibt 32 Bytes nach ''out'' | ''BLMerkleRoot''-Details: * Leere Transaktionsliste → SHA-256 des leeren Puffers * Ungerade Anzahl → letztes Blatt wird dupliziert (Bitcoin-Konvention) ---- ===== WP-BL-03 Core Models ===== ==== Transaktionen ==== ^ Funktion ^ Rückgabe ^ Beschreibung ^ | ''BLNewTransaction(pubKey, recvAddr, amount, fee, nonce)'' | ''int64'' TX-Ptr | Erstellt neue Transaktion; ''senderAddr = SHA-256(pubKey)''; Signatur-Feld ist Null — muss danach signiert werden | | ''BLNewCoinbaseTx(recvAddr, amount, blockIndex)'' | ''int64'' TX-Ptr | Coinbase: ''senderAddr'' und ''senderPub'' Nullen; ''nonce = blockIndex'' | | ''BLTxFree(tx)'' | ''void'' | Gibt Transaktion frei | | ''BLIsCoinbase(tx)'' | ''int64'' 1/0 | 1 wenn ''senderAddr'' alles Nullen | | ''BLTxHash(tx, out)'' | ''void'' | SHA-256 der vollständigen Serialisierung → 32 Bytes nach ''out'' | | ''BLSignTransaction(tx, privKey)'' | ''int64'' 1/0 | ECDSA-Signatur berechnen und in ''tx.signature'' schreiben; 1 = OK | | ''BLVerifyTransaction(tx)'' | ''int64'' 1/0 | Signatur prüfen; Coinbase immer 1 | ==== Blöcke ==== ^ Funktion ^ Rückgabe ^ Beschreibung ^ | ''BLNewBlock(index, prevHash, txPtrs, txCount, difficulty)'' | ''int64'' Block-Ptr | Erstellt Block; berechnet Merkle-Root; ''hash''-Feld ist Null — PoW noch ausstehend | | ''BLBlockFree(block)'' | ''void'' | Gibt Block und txPtrs-Array frei; **TX-Pointer selbst werden nicht freigegeben** | | ''BLCalculateHash(block, out)'' | ''void'' | SHA-256 des serialisierten Block-Headers → 32 Bytes nach ''out'' | | ''BLMeetsTarget(hash, difficulty)'' | ''int64'' 1/0 | 1 wenn Hash ''difficulty'' führende Null-Bytes hat | **Workflow für manuelles Mining:** // 1. Block erstellen (hash-Feld = Null) var block: int64 := BLNewBlock(index, prevHash, txPtrs, txCount, diff); // 2. PoW-Schleife var hashBuf: int64 := alloc(32); while 1 == 1 do { BLCalculateHash(block, hashBuf); if BLMeetsTarget(hashBuf, diff) == 1 then { break; } poke64(block + BL_BLK_NONCE, peek64(block + BL_BLK_NONCE) + 1); } // 3. Hash in Block schreiben var i: int64 := 0; while i < 32 do { poke8(block + BL_BLK_HASH + i, peek8(hashBuf + i)); i := i + 1; } free(hashBuf, 32); ---- ===== WP-BL-04 Ledger ===== ^ Funktion ^ Rückgabe ^ Beschreibung ^ | ''BLLedgerNew()'' | ''int64'' Ledger-Ptr | Erstellt leeren Ledger (57.352 Bytes) | | ''BLLedgerFree(l)'' | ''void'' | Gibt Ledger frei | | ''BLGetBalance(l, addr)'' | ''int64'' | Guthaben der Adresse; 0 wenn nicht vorhanden | | ''BLGetNonce(l, addr)'' | ''int64'' | Nonce der Adresse; 0 wenn nicht vorhanden | | ''BLApplyTransaction(l, tx)'' | ''int64'' neuer Ledger oder 0 | Wendet TX an; gibt neuen Ledger zurück; prüft Nonce, Guthaben; Aufrufer muss alten Ledger freigeben | | ''BLApplyBlock(l, block)'' | ''int64'' neuer Ledger oder 0 | Wendet alle TXs des Blocks atomar an; bei Fehler wird 0 zurückgegeben und kein Zwischenzustand gespeichert | **BLApplyTransaction prüft:** 1. Nonce = erwartetem Wert (verhindert Replay) 2. Betrag und Gebühr ≥ 0 3. ''balance(sender) ≥ amount + fee'' * Coinbase: überspringt alle Prüfungen, schreibt direkt dem Empfänger gut ---- ===== WP-BL-05 Consensus ===== ==== Blockchain-Struct (80 Bytes) ==== ^ Offset ^ Feld ^ Beschreibung ^ | 0 | ''ledger'' | Aktueller Ledger-Ptr | | 8 | ''difficulty'' | Aktuelle Mining-Schwierigkeit | | 16 | ''miningReward'' | Block-Belohnung in Basis-Einheiten | | 24 | ''maxTxPerBlock'' | Maximale TXs pro Block (FIFO aus Mempool) | | 32 | ''chainCount'' | Anzahl Blöcke in der Kette | | 40 | ''chainCap'' | Kapazität des Block-Arrays (dynamisch wachsend) | | 48 | ''chainBlocks'' | Ptr auf Array von Block-Pointern | | 56 | ''mempoolCount'' | Anzahl TXs im Mempool | | 64 | ''mempoolCap'' | Kapazität des Mempool-Arrays | | 72 | ''mempoolTxs'' | Ptr auf Array von TX-Pointern | **Offset-Konstanten:** ''BL_BC_LEDGER'' (0), ''BL_BC_DIFFICULTY'' (8), ''BL_BC_REWARD'' (16), ''BL_BC_MAX_TX'' (24), ''BL_BC_CHAIN_COUNT'' (32), ''BL_BC_CHAIN_CAP'' (40), ''BL_BC_CHAIN_BLKS'' (48), ''BL_BC_MEMPOOL_CNT'' (56), ''BL_BC_MEMPOOL_CAP'' (64), ''BL_BC_MEMPOOL_TXS'' (72), ''BL_BC_SIZE'' (80) ==== Consensus-API ==== ^ Funktion ^ Rückgabe ^ Beschreibung ^ | ''BLBlockchainNew(difficulty, reward, maxTxPerBlock)'' | ''int64'' BC-Ptr | Erstellt Blockchain mit Genesis-Block (Timestamp = 0, Hash berechnet) | | ''BLBlockchainFree(bc)'' | ''void'' | Gibt alles frei: Ledger, alle Blöcke, alle Mempool-TXs | | ''BLChainLength(bc)'' | ''int64'' | Anzahl der Blöcke | | ''BLGetBlock(bc, index)'' | ''int64'' Block-Ptr oder 0 | Block an Position ''index'' (0 = Genesis); 0 wenn außerhalb | | ''BLAddTransaction(bc, tx)'' | ''int64'' 1/0 | Validiert TX und fügt ihn dem Mempool hinzu; bei 1 übernimmt bc die Ownership; bei 0 gehört TX weiterhin dem Aufrufer | | ''BLMinePendingTransactions(bc, minerAddr)'' | ''int64'' Block-Ptr oder 0 | Mined neuen Block; **blockiert bis PoW gelöst** | | ''BLIsValidChain(bc)'' | ''int64'' 1/0 | Validiert gesamte Kette (vollständiger Replay) | | ''BLBlockchainGetBalance(bc, addr)'' | ''int64'' | Guthaben aus aktuellem Ledger | | ''BLBlockchainGetNonce(bc, addr)'' | ''int64'' | Nonce aus aktuellem Ledger | ==== BLAddTransaction — Validierung ==== ''BLAddTransaction'' prüft in dieser Reihenfolge: 1. Kein Coinbase (wird abgelehnt) 2. SHA-256(senderPubKey) = senderAddr 3. ECDSA-Signatur gültig 4. Nonce = aktuellem Wert im Ledger 5. amount ≥ 0 und fee ≥ 0 6. balance ≥ amount + fee 7. Kein Duplikat im Mempool (via TX-Hash) ==== BLMinePendingTransactions — Ablauf ==== 1. Bis ''maxTxPerBlock'' TXs aus Mempool (FIFO — keine Gebühren-Sortierung) 2. Gebühren summieren 3. Coinbase-TX erstellen: Betrag = ''reward + totalFees'' 4. Block erstellen: [coinbase] + ausgewählte TXs 5. **PoW-Schleife** (blockiert): Nonce erhöhen bis ''BLMeetsTarget == 1'' 6. Block auf Ledger anwenden 7. Block an Kette anhängen, verwendete TXs aus Mempool entfernen 8. Schwierigkeit anpassen (wenn ''chainCount % BL_DIFFICULTY_INTERVAL == 0'') ==== Schwierigkeitsanpassung ==== Alle 10 Blöcke: ''newDiff = (difficulty × expected_time) / actual_time'' * Maximale Änderung pro Intervall: Faktor 4 (hoch oder runter) * Minimum: ''BL_MIN_DIFFICULTY = 1'' * Zielzeit: 10 Sekunden pro Block ---- ===== Speicherverwaltung ===== ^ Handle ^ Freigabe ^ Anmerkung ^ | ''BLNewTransaction'' → TX-Ptr | ''BLTxFree(tx)'' | Signatur erst nach ''BLNewTransaction'' setzen | | ''BLNewBlock'' → Block-Ptr | ''BLBlockFree(block)'' | TX-Pointer im txPtrs-Array werden **nicht** freigegeben | | ''BLLedgerNew'' → Ledger-Ptr | ''BLLedgerFree(l)'' | ''BLApplyTransaction/Block'' gibt neuen Ptr zurück — alten danach freigeben | | ''BLBlockchainNew'' → BC-Ptr | ''BLBlockchainFree(bc)'' | Gibt Ledger, alle Blöcke und alle Mempool-TXs frei | | ''BLAddTransaction'' bei Erfolg | TX gehört dem Mempool | Nicht selbst freigeben wenn Rückgabe = 1 | | ''BLMinePendingTransactions'' | Block gehört der Kette | Nicht selbst freigeben — ''BLBlockchainFree'' übernimmt das | ---- ===== Vollständiges Beispiel ===== import std.blockchain; import std.crypto.ecc; import std.crypto.sha256; import std.alloc; import std.io; fn main(): int64 { // Blockchain erstellen: Schwierigkeit 1, Belohnung 50 Coins, max 10 TXs/Block var bc: int64 := BLBlockchainNew(1, 50 * BL_BASE_UNIT, 10); // Alice: Schlüsselpaar erstellen var alicePriv: int64 := alloc(BL_PRIVKEY_LEN); var alicePub: int64 := alloc(BL_PUBKEY_LEN); ECCGenerateKeyPair(alicePriv, alicePub); // Alice' Adresse = SHA-256(pubKey) var aliceAddr: int64 := alloc(BL_ADDR_LEN); SHA256(alicePub, BL_PUBKEY_LEN, aliceAddr); // Bob: Schlüsselpaar erstellen var bobPriv: int64 := alloc(BL_PRIVKEY_LEN); var bobPub: int64 := alloc(BL_PUBKEY_LEN); ECCGenerateKeyPair(bobPriv, bobPub); var bobAddr: int64 := alloc(BL_ADDR_LEN); SHA256(bobPub, BL_PUBKEY_LEN, bobAddr); // Genesis-Block minen — Alice erhält Mining-Belohnung (50 Coins) BLMinePendingTransactions(bc, aliceAddr); Print("Alice: "c); PrintLn(IntToStr(BLBlockchainGetBalance(bc, aliceAddr) / BL_BASE_UNIT)c); // TX: Alice schickt 10 Coins an Bob (1 Coin Gebühr) var nonce: int64 := BLBlockchainGetNonce(bc, aliceAddr); var tx: int64 := BLNewTransaction(alicePub, bobAddr, 10 * BL_BASE_UNIT, // amount 1 * BL_BASE_UNIT, // fee nonce); BLSignTransaction(tx, alicePriv); var ok: int64 := BLAddTransaction(bc, tx); if ok == 0 then { PrintLn("TX abgelehnt"c); BLTxFree(tx); // bei Ablehnung: Aufrufer gibt frei } // Zweiten Block minen BLMinePendingTransactions(bc, aliceAddr); Print("Alice: "c); PrintLn(IntToStr(BLBlockchainGetBalance(bc, aliceAddr) / BL_BASE_UNIT)c); Print("Bob: "c); PrintLn(IntToStr(BLBlockchainGetBalance(bc, bobAddr) / BL_BASE_UNIT)c); Print("Kette: "c); PrintLn(IntToStr(BLChainLength(bc))c); Print("Gültig: "c); PrintLn(IntToStr(BLIsValidChain(bc))c); free(alicePriv, BL_PRIVKEY_LEN); free(alicePub, BL_PUBKEY_LEN); free(aliceAddr, BL_ADDR_LEN); free(bobPriv, BL_PRIVKEY_LEN); free(bobPub, BL_PUBKEY_LEN); free(bobAddr, BL_ADDR_LEN); BLBlockchainFree(bc); return 0; } ---- ===== Hinweise und Einschränkungen ===== * **PoW blockiert**: ''BLMinePendingTransactions'' kehrt erst zurück wenn ein gültiger Hash gefunden wurde. Bei hoher Schwierigkeit kann das Sekunden bis Minuten dauern. * **Ledger-Kapazität**: Maximal 1024 Adressen (feste Hash-Tabelle). Für größere Netzwerke muss ''BL_LEDGER_CAP'' angepasst werden. * **Keine Netzwerkschicht**: Diese Unit ist lokal. P2P-Kommunikation → [[lyx_-_programmiersprache:units:blockchain:p2p|std.blockchain.p2p]] * **Keine Persistenz**: Blockchain liegt vollständig im RAM. Für Persistenz: eigene Serialisierung via ''BLSerializeInt64/Int32'' + Dateisystem. * **Kein UTXO**: Der Ledger nutzt ein kontobasiertes Modell (wie Ethereum), kein UTXO-Modell (wie Bitcoin). * **Kein Fee-Markt**: ''BLMinePendingTransactions'' wählt TXs per FIFO, nicht nach Gebühren. Letzte Aktualisierung: 2026-06-13