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: → std.blockchain.p2p

Standard Library · std.crypto · 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 (XY uncompressed)
BL_PRIVKEY_LEN 32 Private Key
BL_ADDR_LEN 32 Adresse = SHA-256(pubKey)
BL_SIG_LEN 64 Signatur (RS 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 XY — 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 RS — 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 → 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