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?
Kryptographische Grundlagen:
Beträge:
BL_BASE_UNIT = 1.000.000int64Immutable Ledger-Pattern:
BLApplyTransaction und BLApplyBlock geben neue Ledger-Pointer zurück| 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 | ||
| 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 |
| 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)
| 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)
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)
| 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:
| 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 |
| 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);
| 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
| 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)
| 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 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)
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'')
Alle 10 Blöcke: newDiff = (difficulty × expected_time) / actual_time
BL_MIN_DIFFICULTY = 1| 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 |
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;
}
BLMinePendingTransactions kehrt erst zurück wenn ein gültiger Hash gefunden wurde. Bei hoher Schwierigkeit kann das Sekunden bis Minuten dauern.BL_LEDGER_CAP angepasst werden.BLSerializeInt64/Int32 + Dateisystem.BLMinePendingTransactions wählt TXs per FIFO, nicht nach Gebühren.Letzte Aktualisierung: 2026-06-13