====== 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