ECDSA Blockchain Blockchain ECDSA
ECDSA Blockchain, also called Minichain, is an educational blockchain developed with Francesco Marrocco and Leo Vincenzo Petrarca as the final project for the Foundations of Algebra and Geometry course, taught by Prof. Domenico Fiorenza, in the Master’s degree in Applied Mathematics at Sapienza University of Rome.
The project builds a small, inspectable ledger instead of treating a blockchain as a black box. It follows an Ethereum-inspired account model, signs transactions on the secp256k1 elliptic curve, collects pending transactions in a mempool, mines blocks with a simplified proof-of-work rule, and synchronizes several local nodes over HTTP. A deliberately vulnerable signing mode then demonstrates how reusing one ECDSA nonce is enough to expose a private key.
This is a teaching system, not a production cryptocurrency. Its deliberately compact protocol makes every algebraic and state transition visible in the code.
The algebra behind a wallet
The cryptographic layer works in the finite field on the secp256k1 curve
The points on this curve, together with the point at infinity, form an abelian group. A standard generator has prime order . A private key is a non-zero scalar
and its public key is the curve point
Computing from is efficient through repeated point doubling and addition. Reversing the operation - recovering from and - is the elliptic-curve discrete logarithm problem on which the security of the key pair relies.
minichain/crypto.py delegates secp256k1 point operations to coincurve. Public keys are stored in compressed form. To derive an address, the code expands the public key, removes its 0x04 prefix, computes Keccak-256, and retains the last 20 bytes. The result is an Ethereum-style identifier, represented here as 40 lowercase hexadecimal characters without a 0x prefix.
Signing a transaction with ECDSA
Before signing, a transaction payload is serialized as canonical JSON: keys are sorted, separators are fixed, and the result is encoded as UTF-8. SHA-256 maps those bytes to a 32-byte digest. If the digest is interpreted as , ECDSA chooses a fresh secret scalar and computes
The signature is the pair . A verifier uses the public key and computes
then checks whether the -coordinate of agrees with modulo . This proves that the signer knew without transmitting the private key.
The normal path uses coincurve to produce a DER-encoded signature, while helper functions convert between DER and the fixed-width hexadecimal values stored in a transaction. The sender’s compressed public key travels with the transaction; the chain verifies the signature and derives the sender address from that key instead of performing public-key recovery.
Three nonces, three different jobs
The implementation makes a useful distinction between quantities that are all called a nonce but solve different problems:
- The cryptographic nonce is the ephemeral ECDSA secret. It must never be reused across different messages.
- The account nonce records how many accepted transactions an account has sent. A transaction is valid only when its nonce exactly matches the current state, preventing the same signed transfer from being applied repeatedly.
- The mining nonce is a field in a candidate block. A miner changes it until the block hash satisfies the proof-of-work target.
Confusing these concepts is dangerous: the account nonce protects ledger semantics, whereas secrecy and uniqueness of protect the signing key itself.
Transactions and global state
The ledger uses a state map
A transaction contains a recipient, positive integer value, account nonce, timestamp, sender public key, optional data, and an ECDSA signature. Transaction.payload_dict() excludes the signature itself so that all nodes hash the same signed message.
Before a transaction enters the mempool, Blockchain.check_tx_rules() verifies that:
- the amount is positive;
- the destination is a 20-byte hexadecimal address;
- the public key and ECDSA signature are valid;
- the transaction nonce equals the sender’s current account nonce;
- the sender has sufficient funds.
The mempool also rejects another pending transaction with the same sender and account nonce. Applying a valid transaction debits the sender, credits the recipient, and increments only the sender’s account nonce. Genesis allocations initialize every node from the same deterministic balances, and each node persists its chain and state in a port-specific JSON file.
Blocks and proof of work
A block commits to its index, timestamp, transactions, previous block hash, difficulty, mining nonce, and proposer. These fields are canonically serialized and hashed with SHA-256. A block is mined when its hexadecimal hash begins with difficulty zeroes:
where is the number of required leading hexadecimal zeroes. A uniformly distributed hash succeeds with probability on each attempt, so the expected number of trials is .
make_candidate_block() takes up to 50 transactions from the mempool. mine_block() tries successive integer nonces, with a default cap of five million attempts. Before appending a mined or received block, each node checks the index, previous hash, recomputed hash, proof of work, and every transaction. State updates are applied to a snapshot so that one invalid transaction cannot leave a partially modified ledger. A configurable toy block reward credits the proposer after successful validation.
A small peer-to-peer laboratory
Each node is a Flask service with endpoints for its identity and chain, account balances and nonces, transaction submission, mining, block reception, and synchronization. Transactions and blocks are relayed to configured peers on a best-effort basis. The /sync endpoint downloads peer chains and adopts a longer one only after replaying and validating it from the shared genesis state.
The repository supports a complete local experiment:
wallets -> shared genesis allocation -> three Flask nodes
-> signed transaction -> peer mempools
-> proof-of-work block -> validation and propagation
-> synchronized balances and account nonces
CLI utilities create wallets and genesis allocations, start nodes, submit transactions, and run an automatic transfer-mining-synchronization scenario. A blue per-node web console exposes the same actions through a browser.
The design intentionally omits production features such as fees, gas, Merkle trees, authenticated peer transport, cumulative-work fork choice, and adversarial network handling. Its longest-chain rule compares block counts, not total work. These simplifications keep the experiment legible, but they are also why it must not be used to secure real assets.
The weak-nonce attack
The security failure demonstrated by the project is concrete and entirely algebraic. Suppose the same signs two different digests and . Because both signatures use the same point , they have the same value :
Subtracting the equations eliminates the private key term. Anyone observing the signatures can recover
and then
attacks/weak_nonce/make_weak_txs.py implements ECDSA explicitly with a caller-selected and creates two different signed payloads with that same scalar. recover_privkey.py verifies that the signatures share , performs the two modular inversions above, and derives an address from the recovered key as a check.
The browser demonstration makes the attack observable across the ledger. In vulnerable mode, the user console signs one transaction, mines it so the account nonce advances, and signs a second transaction with the same cryptographic nonce. The red attacker console scans both blocks and the mempool, groups signatures by public key and , and recovers the private key when it finds a repeated pair.
The lesson is broader than this toy chain: correct block validation cannot compensate for broken signature generation. A single reused ephemeral secret collapses the one-way protection of the elliptic-curve public key.
Implementation map
minichain/crypto.pyhandles secp256k1 keys, hashes, addresses, DER conversion, signing, verification, and optional public-key recovery.minichain/models.pydefines deterministic transaction and block representations.minichain/chain.pyimplements account state, validation, the mempool, proof of work, rewards, and chain replacement.minichain/node.pyprovides persistence, peer relay, and the Flask REST API.scripts/contains wallet, genesis, node, transaction, and scenario tools.attacks/weak_nonce/contains the vulnerable signer and algebraic key recovery.webapp/contains the normal user console and the attacker visualization.report/contains the complete English and Italian reports and presentation slides.
References
- Magrini, M., Marrocco, F., and Petrarca, L. V. ECDSA Blockchain: source code, reports, and demonstrations. GitHub, 2026.
- Standards for Efficient Cryptography Group. SEC 2: Recommended Elliptic Curve Domain Parameters, Version 2.0, 2010.
- National Institute of Standards and Technology. FIPS 186-5: Digital Signature Standard, 2023.
- Pornin, T. RFC 6979: Deterministic Usage of the Digital Signature Algorithm and ECDSA, 2013.
- Ethereum Foundation. Ethereum accounts: account state, transaction nonces, keys, and address derivation.
- Nakamoto, S. Bitcoin: A Peer-to-Peer Electronic Cash System, 2008.
ECDSA Blockchain, chiamata anche Minichain, è una blockchain didattica sviluppata con Francesco Marrocco e Leo Vincenzo Petrarca come progetto finale del corso di Foundations of Algebra and Geometry, tenuto dal Prof. Domenico Fiorenza, nella laurea magistrale in Matematica Applicata presso Sapienza Università di Roma.
Il progetto costruisce un registro piccolo e ispezionabile, invece di trattare la blockchain come una scatola nera. Segue un modello ad account ispirato a Ethereum, firma le transazioni sulla curva ellittica secp256k1, raccoglie le transazioni in attesa in una mempool, esegue il mining dei blocchi con una regola proof of work semplificata e sincronizza più nodi locali tramite HTTP. Una modalità di firma volutamente vulnerabile dimostra poi che riutilizzare una sola volta un nonce ECDSA è sufficiente a esporre una chiave privata.
Si tratta di un sistema didattico, non di una criptovaluta destinata alla produzione. Il protocollo volutamente compatto rende visibili nel codice tutti i passaggi algebrici e le transizioni di stato.
L’algebra alla base di un wallet
Il livello crittografico opera nel campo finito sulla curva secp256k1
I punti della curva, insieme al punto all’infinito, formano un gruppo abeliano. Un generatore standard ha ordine primo . Una chiave privata è uno scalare diverso da zero
e la relativa chiave pubblica è il punto della curva
Calcolare a partire da è efficiente mediante raddoppi e somme ripetute di punti. Invertire l’operazione, cioè ricavare da e , è il problema del logaritmo discreto su curva ellittica su cui si basa la sicurezza della coppia di chiavi.
minichain/crypto.py delega a coincurve le operazioni sui punti di secp256k1. Le chiavi pubbliche sono memorizzate in forma compressa. Per ricavare un indirizzo, il codice espande la chiave pubblica, rimuove il prefisso 0x04, calcola Keccak-256 e conserva gli ultimi 20 byte. Il risultato è un identificatore in stile Ethereum, qui rappresentato da 40 caratteri esadecimali minuscoli senza prefisso 0x.
Firma di una transazione con ECDSA
Prima della firma, il payload di una transazione viene serializzato come JSON canonico: le chiavi sono ordinate, i separatori sono fissi e il risultato è codificato in UTF-8. SHA-256 trasforma questi byte in un digest di 32 byte. Interpretando il digest come , ECDSA sceglie un nuovo scalare segreto e calcola
La firma è la coppia . Un verificatore usa la chiave pubblica e calcola
quindi controlla che la coordinata di coincida con modulo . In questo modo dimostra che il firmatario conosceva senza trasmettere la chiave privata.
Il percorso normale usa coincurve per produrre una firma codificata in DER, mentre funzioni di supporto convertono tra DER e i valori esadecimali a larghezza fissa memorizzati nella transazione. La chiave pubblica compressa del mittente viaggia insieme alla transazione; la catena verifica la firma e ricava l’indirizzo del mittente dalla chiave, senza eseguire il recupero della chiave pubblica.
Tre nonce per tre compiti diversi
L’implementazione distingue chiaramente tre quantità chiamate tutte nonce, ma destinate a problemi differenti:
- Il nonce crittografico è il segreto effimero di ECDSA. Non deve mai essere riutilizzato per messaggi diversi.
- Il nonce dell’account registra quante transazioni accettate ha inviato un account. Una transazione è valida soltanto quando il suo nonce coincide esattamente con lo stato corrente, impedendo che lo stesso trasferimento firmato venga applicato più volte.
- Il nonce di mining è un campo del blocco candidato. Un miner lo modifica finché l’hash del blocco soddisfa l’obiettivo proof of work.
Confondere questi concetti è pericoloso: il nonce dell’account protegge la semantica del registro, mentre la segretezza e l’unicità di proteggono la chiave di firma.
Transazioni e stato globale
Il registro usa una mappa di stato
Una transazione contiene destinatario, valore intero positivo, nonce dell’account, timestamp, chiave pubblica del mittente, dati opzionali e firma ECDSA. Transaction.payload_dict() esclude la firma, così tutti i nodi calcolano l’hash dello stesso messaggio firmato.
Prima di accettare una transazione nella mempool, Blockchain.check_tx_rules() verifica che:
- l’importo sia positivo;
- la destinazione sia un indirizzo esadecimale di 20 byte;
- la chiave pubblica e la firma ECDSA siano valide;
- il nonce della transazione coincida con quello corrente dell’account del mittente;
- il mittente disponga di fondi sufficienti.
La mempool rifiuta anche una seconda transazione in attesa con lo stesso mittente e nonce dell’account. L’applicazione di una transazione valida addebita il mittente, accredita il destinatario e incrementa soltanto il nonce del mittente. Le allocazioni di genesi inizializzano ogni nodo con gli stessi saldi deterministici e ciascun nodo salva catena e stato in un file JSON specifico per la propria porta.
Blocchi e proof of work
Un blocco include nel proprio impegno indice, timestamp, transazioni, hash del blocco precedente, difficoltà , nonce di mining e proponente. Questi campi vengono serializzati in modo canonico e sottoposti a SHA-256. Il mining ha successo quando l’hash esadecimale inizia con difficulty zeri:
dove è il numero richiesto di zeri esadecimali iniziali. Per un hash uniforme, ogni tentativo ha probabilità di successo e il numero atteso di tentativi è .
make_candidate_block() preleva fino a 50 transazioni dalla mempool. mine_block() prova nonce interi successivi, con un limite predefinito di cinque milioni di tentativi. Prima di aggiungere un blocco estratto o ricevuto, ogni nodo controlla indice, hash precedente, hash ricalcolato, proof of work e tutte le transazioni. Gli aggiornamenti di stato vengono applicati a una copia temporanea, così una transazione non valida non può lasciare il registro parzialmente modificato. Una ricompensa di blocco didattica e configurabile accredita il proponente dopo la validazione.
Un piccolo laboratorio peer-to-peer
Ogni nodo è un servizio Flask con endpoint per identità e catena, saldi e nonce degli account, invio delle transazioni, mining, ricezione dei blocchi e sincronizzazione. Transazioni e blocchi vengono inoltrati ai peer configurati quando possibile. L’endpoint /sync scarica le catene dei peer e ne adotta una più lunga soltanto dopo averla riprodotta e validata a partire dallo stato di genesi condiviso.
Il repository supporta un esperimento locale completo:
wallet -> allocazione di genesi condivisa -> tre nodi Flask
-> transazione firmata -> mempool dei peer
-> blocco proof of work -> validazione e propagazione
-> saldi e nonce degli account sincronizzati
Le utilità a riga di comando creano wallet e allocazioni di genesi, avviano i nodi, inviano transazioni ed eseguono uno scenario automatico di trasferimento, mining e sincronizzazione. Una console web blu per ciascun nodo espone le stesse operazioni nel browser.
Il progetto omette volutamente funzionalità da produzione come commissioni, gas, alberi di Merkle, trasporto autenticato tra peer, scelta dei fork per lavoro cumulativo e gestione di reti avversarie. La regola della catena più lunga confronta il numero di blocchi, non il lavoro totale. Queste semplificazioni rendono l’esperimento leggibile, ma spiegano anche perché non debba essere usato per proteggere beni reali.
L’attacco con nonce debole
Il problema di sicurezza dimostrato dal progetto è concreto e interamente algebrico. Supponiamo che lo stesso firmi due digest differenti e . Poiché entrambe le firme usano lo stesso punto , condividono lo stesso valore :
Sottraendo le equazioni si elimina il termine della chiave privata. Chiunque osservi le firme può ricavare
e quindi
attacks/weak_nonce/make_weak_txs.py implementa ECDSA esplicitamente con un valore scelto dal chiamante e crea due payload differenti firmati con lo stesso scalare. recover_privkey.py verifica che le firme condividano , esegue le due inversioni modulari e ricava un indirizzo dalla chiave recuperata come controllo.
La dimostrazione nel browser rende osservabile l’attacco nell’intero registro. In modalità vulnerabile, la console utente firma una transazione, ne esegue il mining affinché il nonce dell’account avanzi e firma una seconda transazione con lo stesso nonce crittografico. La console rossa dell’attaccante analizza blocchi e mempool, raggruppa le firme per chiave pubblica e e recupera la chiave privata quando trova una coppia ripetuta.
La lezione va oltre questa catena didattica: una validazione corretta dei blocchi non può compensare una generazione difettosa delle firme. Basta riutilizzare una volta un segreto effimero per annullare la protezione unidirezionale della chiave pubblica su curva ellittica.
Mappa dell’implementazione
minichain/crypto.pygestisce chiavi secp256k1, hash, indirizzi, conversione DER, firma, verifica e recupero opzionale della chiave pubblica.minichain/models.pydefinisce rappresentazioni deterministiche di transazioni e blocchi.minichain/chain.pyimplementa stato degli account, validazione, mempool, proof of work, ricompense e sostituzione della catena.minichain/node.pyfornisce persistenza, inoltro ai peer e API REST Flask.scripts/contiene strumenti per wallet, genesi, nodi, transazioni e scenari.attacks/weak_nonce/contiene il firmatario vulnerabile e il recupero algebrico della chiave.webapp/contiene la normale console utente e la visualizzazione per l’attaccante.report/contiene le relazioni complete in inglese e italiano e le slide della presentazione.
Riferimenti
- Magrini, M., Marrocco, F., and Petrarca, L. V. ECDSA Blockchain: source code, reports, and demonstrations. GitHub, 2026.
- Standards for Efficient Cryptography Group. SEC 2: Recommended Elliptic Curve Domain Parameters, Version 2.0, 2010.
- National Institute of Standards and Technology. FIPS 186-5: Digital Signature Standard, 2023.
- Pornin, T. RFC 6979: Deterministic Usage of the Digital Signature Algorithm and ECDSA, 2013.
- Ethereum Foundation. Ethereum accounts: account state, transaction nonces, keys, and address derivation.
- Nakamoto, S. Bitcoin: A Peer-to-Peer Electronic Cash System, 2008.