Nstrike — Blockchain légère (MVP)
# Nstrike — Blockchain légère (MVP)
## 📖 Description
**Nstrike** est une implémentation _from scratch_ d'une **Blockchain** de type Ethereum, écrite entièrement en C++17 moderne sans aucune bibliothèque externe (ni OpenSSL, ni Boost, ni secp256k1). Le projet vise à démontrer le fonctionnement interne d'une **Blockchain** complète : cryptographie, machines virtuelles, consensus Proof-of-Work, comptes, jetons ERC-20, RPC JSON et CLI.
> **Auteur** : Martial Zinsou
>
> **Version** : 0.1.0 (MVP)
>
> **Licence** : MIT
>
> **Langage** : C++17 (sans dépendance externe)
### Fonctionnalités principales
Composant | Description
---|---
**Cryptographie** | SHA-256, Keccak-256 (permutation Keccak-f[1600]), secp256k1 (ECDSA, RFC 6979, ecrecover), adresses 20 octets style Ethereum
**Arithmétique 256 bits** | `u256` avec addition/soustraction modulaire, multiplication de Montgomery, inversion modulaire (Fermat), exponentiation modulaire, division exacte
**Comptes & État** | Nonce, solde, code contrat, storage (map clé→valeur), racine d'état canonique (Keccak cumulatif)
**Transactions** | Sérialisation canonique (magic + big-endian), signature ECDSA + recovery ID, vérification expéditeur (`ecrecover`), gas
**Mini-VM (NVM)** | Interpréteur pile 32 octets, opcodes style EVM (arithmétique, sauts, storage, calldata, Keccak, résultat), assembleur avec labels & `PUSH_SEL`
**Jeton ERC-20** | Template compilé en NVM : `totalSupply`, `balanceOf`, `transfer`, `approve`, `allowance`, `transferFrom`, `name`, `symbol`, `decimals`
**Blockchain & Consensus** | Blocs (en-tête + txs), PoW léger (zéros de tête Keccak double), difficulté ajustable (fenêtre 24 blocs), récompense 50 NST + halving 210 000 blocs, mempool
**RPC JSON 2.0** | `getbalance`, `sendtx`, `getblock`, `getblockcount`, `getgasprice`, `mine`, `chainstatus`, `deploytoken`, `callcontract`
**CLI** | `account new/list`, `address`, `balance`, `send`, `token create/transfer/balance`, `mine`, `chain`, `rpc`
## 🏗️ Architecture
Nstrike/
├── src/
│ ├── config.hpp # Constantes du protocole
│ ├── common.hpp/.cpp # Types de base, hex, u256 (Montgomery), aléa
│ ├── sha256.hpp/.cpp # SHA-256 from scratch
│ ├── keccak.hpp/.cpp # Keccak-256 / SHA3-256 + Keccak-f[1600]
│ ├── crypto.hpp/.cpp # secp256k1, ECDSA, clés, adresses
│ ├── json.hpp/.cpp # Parseur/sérialiseur JSON minimal (RFC 8259)
│ ├── state.hpp/.cpp # Comptes, storage, racine d'état
│ ├── tx.hpp/.cpp # Transaction, signature, hash, sérialisation
│ ├── vm.hpp/.cpp # Mini-VM NVM, assembleur, template ERC-20
│ ├── chain.hpp/.cpp # Blocs, PoW, difficulté, récompense, mempool
│ ├── rpc.hpp/.cpp # Serveur JSON-RPC 2.0 (stdio)
│ └── main.cpp # CLI (wallet, envoi, minage, jetons, RPC)
├── tests/
│ └── test_main.cpp # Tests unitaires (vecteurs + intégration)
├── assets/
│ └── logo.svg # Logo Nstrike (style Microsoft)
├── Makefile # Build (make, make test, make run, make clean)
└── README.md # Ce fichier
## ⚙️ Prérequis
* **macOS / Linux** (testé sur macOS ARM64 & x86_64, Linux x86_64)
* **Clang ≥ 10** ou **GCC ≥ 9** (support C++17 complet)
* `make`
> ⚠️ Aucune dépendance externe (OpenSSL, Boost, etc.) n'est requise.
## 🚀 Installation & Build
# Cloner le dépôt
git clone https://github.com/<votre-utilisateur>/Nstrike.git
cd Nstrike
# Compiler (binaire + tests)
make # → build/nstrike (CLI)
make test # → lance la suite de tests (32 vérifications)
# Nettoyer
make clean
### Résultat attendu des tests
[ OK ] sha256_vectors
[ OK ] keccak256_vectors
[ OK ] u256_basics
[ OK ] u256_modular
[ OK ] ecc_basics
[ OK ] ecdsa_roundtrip
[ OK ] ecdsa_many
[ OK ] hex_roundtrip
8 test(s), 32 vérification(s), 0 échec(s)
## 💻 Utilisation (CLI)
### Portefeuille
# Créer un nouveau compte (clé privée + adresse)
./build/nstrike account new
# → Nouveau compte : 0xabc123...
# Lister les comptes du wallet (~/.nstrike/wallet.json)
./build/nstrike account list
### Minage (Proof-of-Work)
# Miner 1 bloc pour l'adresse donnée (récompense 50 NST + frais)
./build/nstrike mine 0x3ab19ecdaea8a14f4bb2a6f07fa4801e40be7c1f 1
# Miner 5 blocs
./build/nstrike mine 0x3ab19... 5
> ⚠️ **Limitation MVP** : la chaîne n'est pas persistée sur disque. Chaque invocation CLI crée une nouvelle chaîne en mémoire. Pour un minage réel, utilisez le serveur RPC (voir ci-dessous) ou intégrez la persistance (RocksDB, LevelDB, etc.).
### Solde
./build/nstrike balance 0x3ab19ecdaea8a14f4bb2a6f07fa4801e40be7c1f
### Transfert NST
# <privHex> <dest> <montant en nwei> [gasPrice en nwei]
./build/nstrike send 0x2e066... 0xabc123... 0x0de0b6b3a7640000 0x1
### Jeton ERC-20
# Déployer un jeton (créateur doit avoir un compte dans le wallet)
./build/nstrike token create 0x3ab19... "MonToken" "MTK" 18 0x56bc75e2d63100000
# Transférer des jetons
./build/nstrike token transfer 0x3ab19... 0xTokenAddr 0xDestAddr 0x1000
# Consulter le solde d'un jeton
./build/nstrike token balance 0xTokenAddr 0xUserAddr
### État de la Blockchain
./build/nstrike chain
### Serveur JSON-RPC (stdio)
# Lance le serveur RPC sur l'entrée/sortie standard (Ctrl-D pour quitter)
./build/nstrike rpc
Exemple de requête RPC :
{"jsonrpc":"2.0","method":"getbalance","params":{"address":"0x3ab19..."},"id":1}
Réponse :
{"jsonrpc":"2.0","result":"0x2b5e3af16b1880000","id":1}
## 📡 API JSON-RPC 2.0
Méthode | Paramètres | Description
---|---|---
`getbalance` | `{"address": "0x..."}` | Solde NST (nwei)
`sendtx` | `{"tx": "0x..."}` | Envoie une transaction signée (mempool)
`getblock` | `{"height": 0}` | Infos bloc (hash, hauteur, timestamp, miner, nb tx)
`getblockcount` | `{}` | Hauteur actuelle
`getgasprice` | `{}` | Prix de gas suggéré (1 nwei)
`mine` | `{"miner":"0x...","count":1}` | Mine `count` blocs
`chainstatus` | `{}` | Hauteur, difficulté, mempool, stateRoot
`deploytoken` | `{"creator":"0x...","nonce":0,"name":"...","symbol":"...","decimals":18,"supply":"0x..."}` | Déploie un ERC-20
`callcontract` | `{"contract":"0x...","data":"0x...","gas":50000,"from":"0x..."}` | Appel lecture seule (VM)
## 🧪 Tests
make test
La suite vérifie :
* Vecteurs officiels SHA-256 & Keccak-256 (FIPS 202 / NIST)
* Permutation Keccak-f1600
* Arithmétique `u256` : add/sub/mul/div/mod/inv/pow modulaire
* secp256k1 : multiplication scalaire, addition, doublage, ECDSA (signature, vérification, récupération)
* Round-trip hex/JSON
* ERC-20 : déploiement, `transfer`, `balanceOf`, `approve`, `allowance`, `transferFrom`
## 📚 Wiki GitHub (Structure recommandée)
> Après avoir poussé sur GitHub, créez les pages Wiki suivantes via l'onglet **Wiki** du dépôt :
### 1. **Home** — Vue d'ensemble
* Résumé du projet, objectifs, public cible (éducation, recherche, prototypage)
* Diagramme d'architecture (Mermaid)
### 2. **Cryptography** — Détails crypto
* SHA-256 : constantes, compression, padding
* Keccak-256 : taux 136, domaine 0x01, permutation f1600, vecteurs de test
* secp256k1 : paramètres (p, n, G), coordonnées affines & Jacobien, formules d'addition/doublage
* ECDSA : RFC 6979 (k déterministe HMAC-SHA256), récupération `recid` (0..3)
* Adresses : `keccak256(pub[1..64])[12..32]`
### 3. **U256 Arithmetic** — Arithmétique 256 bits
* Représentation little-endian 4×u64
* Addition/soustraction modulaire (sans retenue haute)
* Multiplication de Montgomery : REDC (CIOS), `R = 2^256`, `m' = -p⁻¹ mod 2^64`
* Inversion modulaire : `a^(p-2) mod p` (exponentiation binaire gauche→droite)
* Division exacte (algorithme shift-and-subtract 256 itérations)
### 4. **Virtual Machine (NVM)** — Mini-VM
* Jeu d'opcodes (table complète avec coûts de gas)
* Pile de mots 32 octets (max 1023)
* Calldata : `CALLDATALOAD(offset)`, `CALLDATASIZE`
* Storage : `SLOAD(key)`, `SSTORE(key,value)` — clé = mot 32 octets
* Keccak : `KECCAK` pop a,b → push keccak(a||b) — utilisé pour clés de mapping
* Sauts : `JUMP(dest)`, `JUMPI(dest,cond)` — `dest` doit être `JUMPDEST`
* Résultat : `RESULT(n)` → sortie `n` mots
* Assembler : labels `L_x:`, `PUSH1..PUSH32`, `PUSH_SEL "sig"`, `JUMP L_x` / `JUMPI L_x` (auto PUSH2)
### 5. **ERC-20 Template** — Contrat jeton
* Layout storage :
* slot 0 : `totalSupply`
* slot 1 : `name` (ASCII left-aligned 32 octets)
* slot 2 : `symbol`
* slot 3 : `decimals` (u8 dans LSB)
* slot 4 : `balances[addr]` → clé = `keccak(addr||word(4))`
* slot 5 : `allowance[owner][spender]` → clé = `keccak(spender||keccak(owner||5))`
* Dispatch par selector (premiers 4 octets de `keccak(sig)` alignés à gauche)
* Fonctions : `totalSupply`, `name`, `symbol`, `decimals`, `balanceOf`, `transfer`, `approve`, `transferFrom`, `allowance`
### 6. **Chain & Consensus** — Chaîne & consensus
* En-tête : version, hauteur, timestamp, difficulté (bits), prevHash, stateRoot, txRoot, miner, nonce
* Hachage bloc : Keccak(sérialisation big-endian)
* PoW : `Keccak(Keccak(header))` doit avoir `difficulty` zéros de tête (MSB first)
* Difficulté : fenêtre 24 blocs, cible 15 s/bloc
* Si temps réel > 3× attendu → difficulté –1
* Si temps réel < 1/3 attendu → difficulté +1
* Bornes [16, 32] bits
* Récompense : `BASE_REWARD = 50 NST = 5×10¹⁹ nwei`, halving tous les 210 000 blocs (`shrBits(1)` itératif)
* Mempool : validation complète (`runTx` sur copie d'état) avant insertion
* Application bloc : exécution séquentielle, remboursement gas non consommé, frais au mineur, stateRoot final
### 7. **CLI Reference** — Référence CLI
* Tableau complet commandes/sous-commandes/arguments
* Exemples copiables
* Format wallet JSON (`~/.nstrike/wallet.json`)
### 8. **RPC Specification** — Spécification RPC
* Format JSON-RPC 2.0 (request/response, erreurs standard)
* Liste exhaustive méthodes, paramètres, codes d'erreur personnalisés (-32000 tx invalide)
### 9. **Limitations & Roadmap** — Limitations & feuille de route
* **Pas de persistance** : état perdu à chaque processus (MVP)
* **Pas de P2P** : pas de réseau, pas de synchronisation
* **Gas simplifié** : coûts fixes, pas de schedule EIP-1559
* **Pas de receipts / logs** : pas d'indexation événements
* **Feuille de route** : persistance (RocksDB), P2P (libp2p), sync, receipts, métriques, tests fuzzing
### 10. **Contributing** — Contribuer
* Style : C++17, `-Wall -Wextra`, pas de dépendances
* Tests : ajouter cas dans `tests/test_main.cpp`
* PR : CI (GitHub Actions) `make test` obligatoire
## 🤝 Contribution
Les contributions sont les bienvenues ! Merci de :
1. Forker le dépôt
2. Créer une branche `feature/ma-fonctionnalite`
3. Ajouter des tests dans `tests/test_main.cpp`
4. `make test` doit passer
5. Ouvrir une Pull Request
## 📄 Licence
Ce projet est sous licence **MIT** — voir le fichier LICENSE pour les détails.
## 🙏 Remerciements
* **XKCP** (Keccak Code Package) pour les vecteurs de test officiels
* **NIST FIPS 202** (SHA-3 / Keccak)
* **Standards Efficient Cryptography Group (SECG)** — secp256k1
* **Ethereum Yellow Paper** — modèle de référence comptes/gas/VM
* **RFC 6979** — ECDSA déterministe
> **Note** : Ce MVP est conçu à des fins éducatives et de prototypage. Il n'est **pas** destiné à la production (absence de persistance, P2P, audit de sécurité, etc.).
# Chain & Consensus — Blocs, PoW, Difficulté, Récompense, Mempool
> **Module** : `src/chain.hpp/.cpp`
## 📘 About
**Description** : Structure de bloc avec header (version, height, timestamp, difficultyBits, prevHash, stateRoot, txRoot, miner, nonce) et body (vector). Algorithme PoW : Keccak256(double header serialization), validation par difficultéBits zéros de tête. Ajustement de difficulté tous les 24 blocs (fenêtre 24 * 15s). Récompense de base 50 NST avec halving tous les 210000 blocs. Mempool avec validation nonce, signature, solde, gas intrinsèque. Exécution séquentielle sur copie d'état avant commit bloc.
**Tags** : #blockchain #pow #difficulty #reward #halving #mempool #consensus #block #mine
> **Auteur** : Martial Zinsou
## 🧱 Structure de Bloc
### BlockHeader
Champ | Type | Description
---|---|---
`version` | `uint32_t` | Version du protocole (1)
`height` | `uint32_t` | Hauteur du bloc (0 = genesis)
`timestamp` | `uint64_t` | Unix timestamp (secondes)
`difficultyBits` | `u256` | Bits de zéros de tête requis (MSB first)
`prevHash` | `fix32` | Hash du bloc précédent
`stateRoot` | `fix32` | Racine d'état après exécution des txs
`txRoot` | `fix32` | Racine des transactions (Keccak cumulatif)
`miner` | `fix20` | Adresse du mineur (bénéficiaire récompense)
`nonce` | `u256` | Preuve de travail (cherché par minage)
### Block
struct Block {
BlockHeader header;
std::vector<Transaction> txs;
bytes serialize() const;
static Block deserialize(const bytes& b);
};
### Sérialisation (Big-endian)
version(4) | height(4) | timestamp(8) | difficulty(32)
| prevHash(32) | stateRoot(32) | txRoot(32)
| miner(20) | nonce(32) | ntx(4) | [tx_len(4) + tx_data]*
## ⛏️ Proof-of-Work (PoW Léger)
### Algorithme
1. `h1 = Keccak256(header_serialisé)`
2. `h2 = Keccak256(h1)` (double hachage style Bitcoin)
3. **Valide si** `h2` a au moins `difficultyBits` zéros de tête (MSB first)
### Vérification (`validPoW`)
bool validPoW() const {
fix32 h = Keccak256(Keccak256(header_serialized));
for i in 0..difficultyBits-1:
if bit(h, i) != 0: return false;
return true;
}
### Minage (`mineBlocks`)
* Pré-calcule la partie fixe de l'en-tête (sans nonce)
* Boucle `nonce = 0,1,2...` jusqu'à `validPoW()`
* Taux ~600k H/s sur CPU (Keccak optimisé)
* Difficulté par défaut 16 bits → ~1-10 sec sur CPU moderne
## 📈 Ajustement de Difficulté
### Paramètres
Paramètre | Valeur | Description
---|---|---
`TARGET_BLOCK_SECONDS` | 15 | Temps cible par bloc
`DIFFICULTY_ADJUST_WINDOW` | 24 | Fenêtre d'ajustement (blocs)
`DEFAULT_DIFFICULTY_BITS` | 16 | Difficulté initiale
### Algorithme (`nextDifficulty`)
uint32_t nextDifficulty(blocks, now):
if blocks.size() <= WINDOW: return current
actual = now - timestamp[blocks.size() - W - 1]
expected = W * 15
if actual > expected * 3: return max(16, cur - 1) // trop lent → plus facile
if actual < expected / 3: return min(32, cur + 1) // trop rapide → plus dur
return cur
* **Bornes** : [16, 32] bits
* **Fréquence** : Tous les 24 blocs (~6 min à 15s/bloc)
## 💰 Récompense & Halving
### Récompense de base
BASE_REWARD = "50000000000000000000" // 50 NST = 5×10¹⁹ nwei
HALVING_INTERVAL = 210000 // blocs (~4 ans à 15s/bloc)
### Calcul (`rewardAt`)
u256 rewardAt(height):
r = BASE_REWARD
halvings = height / 210000
repeat halvings times: r >>= 1 // division exacte par 2
return r
* Bloc 0–209999 : 50 NST
* Bloc 210000–419999 : 25 NST
* Bloc 420000–629999 : 12.5 NST
* etc.
### Frais de gas
* `fees = Σ (gasUsed_i * gasPrice_i)` pour toutes txs du bloc
* Total mineur = `reward + fees`
* Crédité via `state.addBalance(miner, total)`
## 📥 Mempool & Exécution Transactions
### Validation (`addTx` / `runTx`)
1. **Chain ID** : `tx.chainId == CHAIN_ID`
2. **Signature** : `tx.verified()` + `sender()` valide
3. **Nonce** : `tx.nonce == state.nonce(sender)`
4. **Solde** : `balance(sender) >= gasLimit*gasPrice + value`
5. **Gas intrinsèque** : `21000 + 100*data.size + (isCreate?32000:0)`
6. **Exécution** sur copie d'état (`State probe = state`)
* Transfert simple : déduction solde + crédit destinataire
* Création contrat : `deriveContractAddress`, `setCode`, `setStorage`
* Appel contrat : `vmExecute` avec gas restant
7. **Remboursement** : `unused = gasLimit - consumed` → `refund = unused * gasPrice`
8. **Échec** → rollback (copie jetée), tx rejetée
### Application Bloc (`mineBlocks`)
1. Prend jusqu'à `MAX_TX_PER_BLOCK` (256) txs valides du mempool
2. Exécute séquentiellement sur `State target = state`
3. Accumule frais, met à jour `target`
4. Calcule `stateRoot = target.stateRoot()`
5. `txRoot = cumulativeRoot(txHashes)`
6. Mine nonce (PoW)
7. Commit : `state = target`, retire txs minées du mempool
## 🔗 Validation Bloc (`validateBlock`)
bool validateBlock(b):
if b.height != blocks.size(): return false
if b.prevHash != (blocks.empty()? 0 : blocks.back().hash()): return false
if !b.validPoW(): return false
return true
## 📂 Fichiers
Fichier | Contenu
---|---
`chain.hpp` | `BlockHeader`, `Block`, `Chain` API
`chain.cpp` | PoW, difficulté, récompense, mempool, exécution txs
## 🔗 Voir aussi
* State-and-Accounts — Racine d'état, comptes
* Transactions — Sérialisation, gas, exécution
* Virtual-Machine-NVM — Appels contrat via VM
# Cryptography — SHA-256, Keccak-256, secp256k1, ECDSA
> **Module** : `src/sha256.hpp/.cpp`, `src/keccak.hpp/.cpp`, `src/crypto.hpp/.cpp`
## 📘 About
**Description** : Implémentation from scratch de primitives cryptographiques essentielles :
* SHA-256 (FIPS 180-4) — hashage de blocs, vecteurs de test XKCP validés
* Keccak-256 — permutation Keccak-f[1600], standard Ethereum
* secp256k1 — courbe elliptique, ECDSA, ecrecover, RFC 6979 nonces déterministes
* HMAC-SHA256 — utilisation RFC 6979 pour génération nonce ECDSA
**Tags** : #cryptography #sha256 #keccak #secp256k1 #ecdsa #rfc6979 #hash #crypto
> **Auteur** : Martial Zinsou
## 🔐 SHA-256 (FIPS 180-4)
### Implémentation
* **Fichiers** : `src/sha256.hpp/.cpp`
* **Classe** : `SHA256` (incrémental : `update` / `digest`) + `sha256(bytes)` stateless
* **Constantes** : 8 mots d'init (racines carrées premiers 8 nombres premiers), 64 constantes de tour K64
* **Padding** : 1 bit '1', zéros, longueur 64 bits big-endian
* **Bloc** : 512 bits (64 octets), 64 tours
* **Sortie** : 256 bits (32 octets, big-endian)
### Vecteurs de test (FIPS 180-4)
SHA256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
SHA256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
SHA256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
= 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1
## 🔑 Keccak-256 / SHA3-256 (FIPS 202)
### Implémentation
* **Fichiers** : `src/keccak.hpp/.cpp`
* **Classes** : `Keccak256` (dom=0x01 défaut, dom=0x06 pour SHA3-256), `keccak_f1600(uint64_t[25])` cœur exposé
* **Architecture éponge** :
* État : 1600 bits = 25×64 bits (5×5 lanes)
* Taux (rate) : 1088 bits = 136 octets (Keccak-256)
* Capacité : 512 bits (sécurité 256 bits)
* **Permutation Keccak-f[1600]** (24 tours) :
1. **θ (theta)** : parité colonnes → XOR lignes
2. **ρ (rho)** : rotation bit à bit par constantes RHO[5][5]
3. **π (pi)** : permutation lanes (x,y) → (y, 2x+3y)
4. **χ (chi)** : non-linéaire par ligne `a = a ⊕ (¬b ∧ c)`
5. **ι (iota)** : XOR constante de tour RC[round] sur lane (0,0)
* **Padding multi-rate** : `dom | 0x00* | 0x80` (dom=0x01 Keccak, 0x06 SHA3)
* **Sortie** : 32 premiers octets de l'état (little-endian lanes → big-endian bytes)
### Vecteur de test XKCP (permutation)
keccak_f1600(0) = f1258f7940e1dde7... (premiers 8 octets)
### Vecteurs Keccak-256 (Ethereum)
Keccak256("") = c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
Keccak256("abc")= 4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45
## 📐 secp256k1 (Elliptic Curve)
### Paramètres (SEC 2 / Standards for Efficient Cryptography)
p = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F
= 2^256 - 2^32 - 977
n = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141
(ordre du sous-groupe, premier)
Gx = 79BE667E F9DCBBAC 55A06295 CE870B07 029BFCDB 2DCE28D9 59F2815B 16F81798
Gy = 483ADA77 26A3C465 5DA4FBFC 0E1108A8 FD17B448 A6855419 9C47D08F FB10D4B8
Équation : `y² = x³ + 7 (mod p)` (courbe Koblitz, a=0, b=7)
### Représentation des points
* **API publique** : Coordonnées affines `(x,y)` (`PublicKey` struct)
* **Interne** : Coordonnées de Jacobien `(X,Y,Z)` pour éviter inversions
* Affine → Jacobien : `(x,y) → (x,y,1)`
* Jacobien → Affine : `x = X/Z², y = Y/Z³` (une seule inversion à la fin)
### Formules (Jacobien)
* **Doublage** (2P) : formule "dbl-2007-bl" (8M + 3S + 7A)
* **Addition** (P+Q, P≠Q) : formule "add-2007-bl" (11M + 5S + 9A)
* **Addition mixte** (Jacobien + Affine) : optimisée pour `ecMulG`
### Multiplication scalaire
* **Algorithme** : Échelle binaire gauche→droite (MSB→LSB)
* **Fenêtre** : Non fenêtrée (simple, constant-time pour bits du scalaire)
* **Précalcul** : Aucun (MVP) — optimisable avec table 2⁴ points
## ✍️ ECDSA (RFC 6979 Déterministe)
### Signature
Données : message hash `z` (32 octets), clé privée `d` (0 < d < n)
1. **Génération k (RFC 6979)** :
* HMAC-SHA256 DRBG avec clé = `d`, message = `z`
* Boucle jusqu'à `0 < k < n`
* `k` déterministe pour même `(d,z)` — pas d'entropie runtime
2. **Calcul signature** :
* `R = k * G` ; `r = R.x mod n` (si r=0 → retry)
* `s = k⁻¹(z + r*d) mod n`
* **Low-s** : si `s > n/2` → `s = n - s` (BIP-62, malléabilité)
* `recid = (R.y & 1) | ((R.x >= n) ? 2 : 0)` (parité y + débordement x)
### Vérification
Données : `z`, `(r,s)`, clé publique `Q`
1. `w = s⁻¹ mod n`
2. `u1 = z*w mod n`, `u2 = r*w mod n`
3. `R = u1*G + u2*Q` (addition Jacobien)
4. Valide si `R.x mod n == r`
### Récupération (ecrecover)
Données : `z`, `(r,s,recid)`
1. `x = r + (recid & 2 ? n : 0)` (gérer dépassement x ≥ n)
2. `y² = x³ + 7 mod p` → `y = sqrt(y²)` via `y = y²^((p+1)/4) mod p`
* Choisir `y` tel que `y & 1 == recid & 1` (parité)
3. `R = (x,y)` ; `e = z` (entier)
4. `Q = r⁻¹(s*R - e*G)` → clé publique récupérée
## 🏠 Adresses Nstrike (Style Ethereum)
pubkey = 0x04 || x(32) || y(32) (65 octets, non-compressed)
address = keccak256(pubkey)[12..31] (20 derniers octets)
## 📂 Fichiers Sources
Fichier | Description
---|---
`sha256.hpp/.cpp` | SHA-256 (classe + stateless)
`keccak.hpp/.cpp` | Keccak-256, SHA3-256, Keccak-f[1600]
`crypto.hpp/.cpp` | secp256k1, ECDSA, RFC6979, ecrecover, clés, adresses, HMAC-SHA256
## 🔗 Références
* FIPS 180-4 — SHA-256
* FIPS 202 — SHA-3/Keccak
* SEC 2 — Paramètres secp256k1
* RFC 6979 — ECDSA déterministe
* XKCP — Vecteurs Keccak officiels
* Ethereum Yellow Paper — Modèle de référence # ERC-20 Template — Contrat Jeton Compilé en NVM
> **Module** : `src/vm.hpp/.cpp` — `erc20Bytecode()`, `erc20Deploy()`
## 📘 About
**Description** : Fournir un jeton ERC-20 complet prêt à déployer, écrit en NVM assembly (compilé par `assembleNvm`), sans Solidity ni compilateur externe. Layout storage avec 6 slots clés (totalSupply, name, symbol, decimals, balances, allowance). Dispatch par selector sur les 4 premiers octets du calldata. 9 fonctions implémentées : totalSupply, name, symbol, decimals, balanceOf, transfer, approve, transferFrom, allowance. Fonction `erc20Deploy` pour déploiement avec init storage et mintage creator.
**Tags** : #erc20 #token #smart-contract #nvm #assembly #bytecode #deploy #mintage #solidity-alternative
> **Auteur** : Martial Zinsou
## 🎯 Objectif
Fournir un **jeton ERC-20 complet** prêt à déployer, écrit en **NVM assembly** (compilé par `assembleNvm`), sans Solidity ni compilateur externe.
## 📦 Layout Storage (Slots)
Slot | Clé | Contenu | Type
---|---|---|---
0 | `word(0)` | `totalSupply` | `u256`
1 | `word(1)` | `name` (ASCII left-aligned, 32 octets) | `fix32`
2 | `word(2)` | `symbol` (ASCII left-aligned) | `fix32`
3 | `word(3)` | `decimals` (u8 dans LSB) | `u256`
4 | `keccak(addr32 \ | word(4))` | `balances[addr]`
5 (inner) | `keccak(owner32 \ | word(5))` | `allowance_inner`
5 (outer) | `keccak(spender32 \ | inner)` | `allowance[owner][spender]`
> **Note** : `word(s) = u256::toBytes(u256(s))` (32 octets big-endian).
>
> `addr32 = u256::toBytes(u256::fromBytes(addr))` (adresse right-aligned dans 32 octets).
## 🔀 Dispatch par Selector
Le premier mot du calldata (offset 0) = selector aligné à gauche (`keccak(sig)[0..3] << 224`).
CALLDATASIZE PUSH1 4 LT JUMPI L_revert
PUSH32 0 CALLDATALOAD
PUSH_SEL "totalSupply()" EQ JUMPI L_total
PUSH_SEL "name()" EQ JUMPI L_name
PUSH_SEL "symbol()" EQ JUMPI L_symbol
PUSH_SEL "decimals()" EQ JUMPI L_decimals
PUSH_SEL "balanceOf(address)" EQ JUMPI L_balance
PUSH_SEL "transfer(address,uint256)" EQ JUMPI L_transfer
PUSH_SEL "approve(address,uint256)" EQ JUMPI L_approve
PUSH_SEL "transferFrom(address,address,uint256)" EQ JUMPI L_from
PUSH_SEL "allowance(address,address)" EQ JUMPI L_allow
REVERT
## 📋 Fonctions Implémentées (9)
Fonction | Selector | Args (offset) | Retour | Description
---|---|---|---|---
`totalSupply()` | 0x18160ddd | — | `supply` | Offre totale
`name()` | 0x06fdde03 | — | `name` (32 octets) | Nom du jeton
`symbol()` | 0x95d89b41 | — | `symbol` | Symbole
`decimals()` | 0x313ce567 | — | `decimals` (u8) | Décimales
`balanceOf(address)` | 0x70a08231 | `addr`@4 | `balance` | Solde d'une adresse
`transfer(addr,uint)` | 0xa9059cbb | `to`@4, `amt`@36 | `1`/`0` | Transfert
`approve(addr,uint)` | 0x095ea7b3 | `spender`@4, `amt`@36 | `1`/`0` | Autorisation
`transferFrom(f,t,amt)` | 0x23b872dd | `from`@4, `to`@36, `amt`@68 | `1`/`0` | Transfert délégué
`allowance(o,s)` | 0xdd62ed3e | `owner`@4, `spender`@36 | `allowance` | Autorisation restante
## 🔧 Implémentation Clé (Extraits)
### balanceOf
L_balance:
PUSH32 4 CALLDATALOAD // addr
PUSH1 4 KECCAK SLOAD // key=keccak(addr||4), load
PUSH1 1 RESULT STOP
### transfer
L_transfer:
PUSH32 4 CALLDATALOAD // to
DUP1 ISZERO JUMPI L_revert // reject to==0
CALLER PUSH1 4 KECCAK DUP1 SLOAD // keyC, balC
PUSH32 36 CALLDATALOAD // amount
DUP2 LT ISZERO JUMPI L_revert // balC < amount → revert
SWAP1 SUB SSTORE // balC -= amount
PUSH32 4 CALLDATALOAD PUSH1 4 KECCAK DUP1 SLOAD
PUSH32 36 CALLDATALOAD ADD SSTORE // bal(to) += amount
PUSH1 1 RESULT STOP
### approve
L_approve:
CALLER PUSH1 5 KECCAK // inner = keccak(caller||5)
PUSH32 4 CALLDATALOAD SWAP1 KECCAK // key = keccak(spender||inner)
PUSH32 36 CALLDATALOAD SSTORE // allowance = amount
PUSH1 1 RESULT STOP
### transferFrom
L_from:
PUSH32 4 CALLDATALOAD PUSH1 5 KECCAK // innerF
CALLER SWAP1 KECCAK DUP1 SLOAD // keyA, allowed
PUSH32 68 CALLDATALOAD // amount (arg2)
DUP2 LT ISZERO JUMPI L_revert // allowed < amount → revert
SWAP1 SUB SSTORE // allowed -= amount
// debit from
PUSH32 4 CALLDATALOAD PUSH1 4 KECCAK DUP1 SLOAD
PUSH32 68 CALLDATALOAD
DUP2 LT ISZERO JUMPI L_revert
SWAP1 SUB SSTORE
// credit to
PUSH32 36 CALLDATALOAD PUSH1 4 KECCAK DUP1 SLOAD
PUSH32 68 CALLDATALOAD ADD SSTORE
PUSH1 1 RESULT STOP
## 🚀 Déploiement (`erc20Deploy`)
fix20 erc20Deploy(State& state, const fix20& creator, const u256& creatorNonce,
const std::string& name, const std::string& symbol,
uint8_t decimals, const u256& initialSupply);
1. `addr = deriveContractAddress(creator, creatorNonce)`
2. `state.setCode(addr, erc20Bytecode())`
3. Storage init :
* `slot 0 = initialSupply`
* `slot 1 = name` (ASCII left-aligned 32 octets)
* `slot 2 = symbol`
* `slot 3 = decimals` (LSB)
* `balances[creator] = initialSupply` via `keccak(creator||word(4))`
4. `creatorNonce++`
## 📂 Fichiers
Fichier | Contenu
---|---
`vm.hpp` | API `erc20Bytecode()`, `erc20Deploy()`
`vm.cpp` | Source assembleur `ERC20_SRC` + implémentation déploiement
## 🔗 Voir aussi
* EIP-20 — Standard ERC-20
* ABI Specification — Selector encoding
* Virtual-Machine-NVM — VM & assembleur
# RPC Specification — JSON-RPC 2.0
> **Module** : `src/rpc.hpp/.cpp`
## 📘 About
**Description** : Serveur JSON-RPC 2.0 en stdio avec 9 endpoints supportés : getbalance, sendtx, getblock, getblockcount, getgasprice, mine, chainstatus, deploytoken, callcontract. Format requête/réponse standard JSON-RPC 2.0. Formats de données : address (20 octets hex), quantity/u256 (64 hex chars), boolean, string. Codes d'erreur : -32700 (parse), -32600 (invalid request), -32601 (method not found), -32602 (invalid params), -32603 (internal). Mode interaction : lecture stdin, écriture stdout.
**Tags** : #json-rpc #rpc #stdio #api #endpoints #json #method #getbalance #sendtx #getblock
> **Auteur** : Martial Zinsou
## 🌐 Endpoints JSON-RPC Supportés
### Requête JSON-RPC 2.0 Standard
{
"jsonrpc": "2.0",
"method": "<method_name>",
"params": [...],
"id": <request_id>
}
### Réponse JSON-RPC 2.0 Succès
{
"jsonrpc": "2.0",
"result": <result>,
"id": <request_id>
}
### Réponse JSON-RPC 2.0 Erreur
{
"jsonrpc": "2.0",
"error": {
"code": <error_code>,
"message": "<error_message>"
},
"id": <request_id>
}
## 📋 Méthodes Supportées
### 1. `getbalance`
**Description** : Récupérer le solde d'une adresse.
**Paramètres**
[{"address": "0xabc123..."}]
**Retour**
"0x0000000000000000000000000000000000000000000000000000000000000000" (hex string, nwei)
**Exemple**
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"getbalance","params":[{"address":"0xabc123"}],"id":1}'
### 2. `sendtx`
**Description** : Envoyer une transaction signée.
**Paramètres**
[{"tx": "0x..."}] // Transaction hexadécimale sérialisée
**Retour**
"0x<hash_32_bytes_hex>" (hash de la transaction)
**Exemple**
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"sendtx","params":[{"tx":"0x0123456789abcdef..."}],"id":1}'
### 3. `getblock`
**Description** : Récupérer les informations d'un bloc par hauteur.
**Paramètres**
[{"height": 5}]
**Retour**
{
"hash": "0x...",
"height": 5,
"timestamp": 1234567890,
"miner": "0x...",
"txCount": 3,
"transactions": ["0x...", "0x...", "0x..."]
}
### 4. `getblockcount`
**Description** : Récupérer la hauteur actuelle du bloc (dernier bloc).
**Paramètres** : `[]` (vide)
**Retour**
"0x5" (hex string, hauteur actuelle)
### 5. `getgasprice`
**Description** : Récupérer le prix de gas suggéré.
**Paramètres** : `[]` (vide)
**Retour**
"0x1" (hex string, 1 nwei = prix minimum)
**Remarque** : Prix fixe de 1 nwei par unité de gas (configuration par défaut).
### 6. `mine`
**Description** : Miner des blocs pour une adresse donnée.
**Paramètres**
[{"miner": "0xabc123...", "count": 1}]
**Retour**
{
"status": "success",
"blocksMined": 1,
"newHeight": 1,
"reward": "0x32000000000000000000" // 50 NST en hex
}
### 7. `chainstatus`
**Description** : Récupérer l'état complet de la chaîne.
**Paramètres** : `[]` (vide)
**Retour**
{
"status": "synced",
"currentHeight": 15,
"difficultyBits": 16,
"blockTime": 15,
"chainId": 1
}
### 8. `deploytoken`
**Description** : Déployer un contrat jeton ERC-20.
**Paramètres**
[{"creator": "0xabc123...", "name": "MyToken", "symbol": "MTK", "decimals": 18, "initialSupply": "0x1000000000000000000"}]
**Retour**
"0xabc123..." (adresse du contrat déployé)
### 9. `callcontract`
**Description** : Appeler une fonction de contrat.
**Paramètres**
[{"contract": "0xabc123...", "data": "0x..." , "gas": "0x5208", "from": "0xdef456..."}]
**Retour**
"0x..." (résultat de l'appel VM, hex string)
## 🔌 Serveur RPC en Ligne de Commande (`rpcServeStdio`)
### Mode d'Interaction
Le serveur RPC lit les requêtes JSON depuis **stdin** et renvoie les réponses sur **stdout**.
# Démarrage
./build/nstrike rpc
# Envoi d'une requête (via un autre terminal ou pipe)
echo '{"jsonrpc":"2.0","method":"getbalance","params":[{"address":"0xabc123"}],"id":1}' | ./build/nstrike rpc
# Sortie attendue
{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}
### Implémentation C++
void rpcServeStdio(Chain& chain) {
std::string line;
while (std::getline(std::cin, line)) {
if (line.empty()) continue;
Json req;
if (!Json::tryParse(line, req)) {
Json resp = errResp(-32700, "parse error", req.value("id", Json::Null));
fputs(resp.dump().c_str(), stdout);
fputc('\n', stdout);
fflush(stdout);
continue;
}
Json resp = rpcHandle(req, chain);
fputs(resp.dump().c_str(), stdout);
fputc('\n', stdout);
fflush(stdout);
}
}
### Gestion des Erreurs
Code | Message | Description
---|---|---
`-32700` | Parse error | Requête JSON invalide
`-32600` | Invalid Request | Méthode inconnue ou version JSON-RPC non supportée
`-32601` | Method not found | La méthode n'existe pas
`-32602` | Invalid params | Paramètres invalides
`-32603` | Internal error | Erreur interne au traitement
## 📦 Formats de Données
### Adresse (`address`)
* Format : 20 octets right-aligned dans 32 octets
* Hexadécimal : `0x` + 40 caractères hexadécimaux
* Exemple : `0xabc123def4567890123456789012345678901234`
### Nonce (`quantity` / `u256`)
* Format : hexadécimal non signé 256 bits
* Hexadécimal : `0x` + 64 caractères hexadécimaux
* Exemple : `0x0000000000000000000000000000000000000000000000000000000000000000`
### Booléen (`boolean`)
* `true` / `false` (JSON standard)
### Chaîne (`string`)
* JSON standard, UTF-8 supporté
* Exemple : `"0xabc123..."`
## 📂 Fichiers
Fichier | Contenu
---|---
`rpc.hpp` | Définitions d'endpoints, structures de requête/réponse
`rpc.cpp` | Implémentation serveur JSON-RPC, gestion des méthodes
## 🔗 Voir aussi
* CLI-Reference — Commandes en ligne de commande
* Virtual-Machine-NVM — Appels de contrat via RPC
* Chain-and-Consensus — État de la chaîne, minage
# State and Accounts — Comptes, Stockage, Racines d'État
> **Module** : `src/state.hpp/.cpp`
## 📘 About
**Description** : Modèle de compte complet avec adresse (fix20), nonce (u256), balance (u256), codeHash (fix32), storageRoot (fix32). Slots storage : slot 0 = totalSupply, slot 1 = name, slot 2 = symbol, slot 3 = decimals, slot 4 = balances[addr] via keccak, slot 5 = allowance via double-keccak. Racines d'état : stateRoot(), storageRoot(), codeHash() implémentées avec keccak accumulative hashing. Validation nonce, protection replay CHAIN_ID.
**Tags** : #state #accounts #storage #root #hash #account-model #u256 #fix32 #keccak
> **Auteur** : Martial Zinsou
## 👤 Modèle de Compte
### Structure Account
Champ | Type | Description
---|---|---
`address` | `fix20` | Adresse (20 octets, right-aligned dans 32 octets)
`nonce` | `u256` | Nonce du compte (incrémente à chaque tx)
`balance` | `u256` | Solde en nwei (1 NST = 10⁹ nwei)
`codeHash` | `fix32` | Hash du code bytecode (keccak256)
`storageRoot` | `fix32` | Racine d'arbre de stockage (patricia)
### Déduction d'Adresse
fix20 deriveAddress(const fix20& privKey) {
// Keccak(privKey || 0x00) → last 20 bytes
bytes k = Keccak256::hash(bytes(privKey.data(), privKey.size()) + bytes("\x00"));
return fix20(k.end() - 20, k.end());
}
### Nonce & Replay Protection
* `state.nonce(addr)` → u256
* `tx.nonce` doit égaler `state.nonce(sender)` ✅
* `++state.nonce(sender)` après exécution réussie
* `CHAIN_ID` inclus dans la signature (protection replay cross-chain)
## 💾 Stockage Contract (Storage Model)
### Slots de Storage (32 octets = word)
Slot | Clé (keccak) | Contenu | Type
---|---|---|---
0 | — | `totalSupply` (template ERC-20) | `u256`
1 | — | `name` (template ERC-20, ASCII left-aligned) | `fix32`
2 | — | `symbol` (template ERC-20) | `fix32`
3 | — | `decimals` (template ERC-20, u8 LSB) | `u256`
4 | `keccak(addr32 \ | word(4))` | `balances[addr]`
5 | `keccak(spender32 \ | keccak(owner32 \ | word(5)))`
### Accès Stockage
u256 State::getBalance(const fix20& addr) const {
fix32 key = Keccak256::hash(
bytes(u256::toBytes(u256(4))).append(
bytes(addr.data(), addr.size())
));
return SLOAD(key);
}
void State::setBalance(const fix20& addr, u256 bal) {
fix32 key = Keccak256::hash(
bytes(u256::toBytes(u256(4))).append(
bytes(addr.data(), addr.size())
));
SSTORE(key, bal);
}
u256 State::getAllowance(const fix20& owner, const fix20& spender) const {
fix32 innerKey = Keccak256::hash(
bytes(spender.data(), spender.size()).append(
Keccak256::hash(bytes(owner.data(), owner.size()))
);
fix32 key = Keccak256::hash(bytes(u256::toBytes(u256(5))).append(bytes(innerKey)));
return SLOAD(key);
}
void State::setAllowance(const fix20& owner, const fix20& spender, u256 amt) {
fix32 innerKey = Keccak256::hash(
bytes(spender.data(), spender.size()).append(
Keccak256::hash(bytes(owner.data(), owner.size()))
);
fix32 key = Keccak256::hash(bytes(u256::toBytes(u256(5))).append(bytes(innerKey)));
SSTORE(key, amt);
}
### Stockage Universel (non-ERC-20)
Pour les comptes de base (code, storage patricia) :
Slot | Clé | Contenu
---|---|---
`keccak(addr32 \ | word(0))` | nonce
`keccak(addr32 \ | word(1))` | balance
`keccak(addr32 \ | word(2))` | codeHash
`keccak(addr32 \ | word(3))` | storageRoot
## 🌳 Racines d'État (State Roots)
### `stateRoot()` Global
fix32 State::stateRoot() const {
fix32 h = fix32(HASH_ZERO);
for (auto& kv : accounts) {
fix32 addrW = u256::toBytes(u256::fromBytes(k.first.data(), k.first.size()));
fix32 nonceW = u256::toBytes(k.second.nonce);
fix32 balW = u256::toBytes(k.second.balance);
fix32 codeW = k.second.code.empty() ? HASH_ZERO : keccak256(k.second.code);
fix32 storW = storageRoot(k.first); // racine de storage du compte
fix32 leaf = keccak(addrW.append(nonceW).append(balW).append(codeW).append(storW));
h = keccak(h.append(leaf));
}
return h;
}
### `storageRoot(const fix20& account)`
fix32 State::storageRoot(const fix20& a) const {
fix32 h = fix32(HASH_ZERO);
for (auto& sv : storage[a]) {
fix32 keyW = u256::toBytes(u256::fromBytes(sv.first.data(), sv.first.size()));
fix32 valW = u256::toBytes(sv.second);
fix32 leaf = keccac(keyW.append(valW));
h = keccak(h.append(leaf));
}
return h;
}
### `codeHash(const fix20& account)`
fix32 State::codeHash(const fix20& a) const {
auto code = getCode(a);
return code.empty() ? HASH_ZERO : keccak256(code);
}
## 📂 Fichiers
Fichier | Contenu
---|---
`state.hpp` | `Account`, `State` API, stockage, racines
`state.cpp` | Implémentation SLOAD/SSTORE, racines, adresses
## 🔗 Voir aussi
* Transactions — Utilisation du storage dans txs
* Virtual-Machine-NVM — SLOAD/SSTORE en VM
* Chain-and-Consensus — Blocs, état global