Publié le

Comment créer une adresse Bitcoin ?

Créer une adresse Bitcoin est une façon simple de découvrir la cryptographie. Cet article explique, étape par étape, les mécanismes derrière ce système fascinant et robuste, en rendant la complexité accessible.

Comment créer une adresse Bitcoin ?

Photo de Art Rachen sur Unsplash

Les sources du projet sont disponibles sur GitHub : adrien-chinour/discover-blockchain.

Découverte des adresses Bitcoin

Imagine que tu veux transférer de l’argent à un ami via ton application bancaire. Pour cela, tu as besoin de connaître son numéro de compte, n’est-ce pas ? Eh bien, une adresse Bitcoin fonctionne un peu de la même manière, sauf qu’au lieu de devoir te rendre à une banque, tu utilises la blockchain pour envoyer des fonds. Chaque adresse Bitcoin est comme un numéro de compte unique, un identifiant permettant de savoir où envoyer tes bitcoins. Tout comme dans le système bancaire où ton IBAN permet de recevoir des virements, l'adresse Bitcoin garantit que tes fonds arrivent à la bonne destination.

Les différents formats d’adresses Bitcoin

Les adresses Bitcoin sont au cœur des transactions, mais contrairement aux simples numéros de compte dans le monde bancaire, elles sont générées par des clés cryptographiques et existent dans plusieurs formats.

Au fil des années, le format des adresses Bitcoin a évolué pour répondre à de nouveaux besoins en termes de sécurité, de confidentialité et d’efficacité des transactions. Tout comme les banques proposent différents types de comptes (compte courant, compte épargne…), Bitcoin a également ses propres "types d'adresses", et chaque format a sa propre fonction.

Le nombre d’adresses Bitcoin possibles

L'un des aspects les plus fascinants du système Bitcoin est l'énorme quantité d'adresses qu'il peut générer. Théoriquement, le nombre d'adresses possibles est astronomique, bien plus élevé que le nombre d’étoiles dans l'univers.

Pourquoi ? Parce qu'une adresse Bitcoin est dérivée d'une clé publique générée à partir d'une clé privée (qui est simplement un nombre aléatoire de 256 bits). En gros, tu as un nombre quasi infini de combinaisons possibles pour générer des adresses Bitcoin. Cela signifie qu'il y a suffisamment d'adresses pour que chaque personne sur Terre puisse en avoir des milliers sans jamais risquer de tomber sur une autre adresse déjà utilisée.

Pour être précis, il y a environ 2^160 adresses possibles, ce qui représente un nombre faramineux de possibilités. Pour ce rendre compte de la taille de ce nombre : 1 461 501 637 330 902 918 203 684 832 716 283 019 655 932 542 976.

Et si mon portefeuille générait une adresse Bitcoin existante ?

Le nombre d'adresse actuellement utilisées

Il est difficile de connaître le nombre d'adresse réellement active. Je ne trouve pas de chiffre clair a ce sujet mais il semblerait qu'il y est plus de 70 millions d'adresses détenant des bitcoins. Autre statistique intéressante : on compte environ 1 million d'adresse avec un solde supérieur à 1 BTC.

Création d'une adresse Bitcoin

Comme nous l'avons vu dans la première partie, il existe plusieurs formats d'adresses Bitcoin. Dans cette partie, nous allons nous concentrer sur le format le plus répandu : le format Pay-To-Public Key Hash, ou P2PKH.

1. Clé privée et clé publique avec ECDSA

ECDSA pour Elliptic Curve Digital Signature Algorithm est un algorithme cryptographique. Basé sur les mathématiques des courbes elliptiques, il permet de générer une paire de clés (privée et publique) utilisées pour signer et vérifier des messages ou des transactions. La clé publique est partagée et sert à valider une transaction, tandis que la clé privée est utilisée pour signer les transactions.

Il est très utilisé dans le monde de crypto-monnaie mais pas seulement, il est aussi présent dans le protocole SSL/TLS pour établir des connexions HTTPS.

ECDSA peut être utilisé avec différentes courbes. Bitcoin utilise la courbe secp256k1 pour générer le couple de clés.

En termes simples, un entier aléatoire k est choisi dans l'intervalle [1,n−1], où n est l'ordre du point générateur G sur la courbe elliptique secp256k1. Cet ordre n n'est pas exactement 2^256, mais il est légèrement plus petit (c'est une constante spécifique définie pour secp256k1). Cet algorithme produit des coordonnées (x,y) représentant le point k×G sur la courbe elliptique, où G est le point initial ou générateur. La clé publique correspond à ce point, et sa sécurité repose sur la difficulté de retrouver k à partir des coordonnées (x,y), un problème connu sous le nom de problème du logarithme discret sur des courbes elliptiques.

En terme de code on peut traduire cette première étape de la manière suivante.

// The private key in ECDSA is a randomly generated 256-bit integer used as the secret in cryptographic operations.
// It must be kept secure, as it is the foundation for deriving the public key and signing transactions.
const privateKey = crypto.randomBytes(32).toString('hex');

// The public key is derived from the private key using elliptic curve multiplication (privateKey * G).
// It is a pair of coordinates (x, y) on the secp256k1 and is safe to share publicly.
const publicKey = secp256k1.getPublicKey(privateKey);

console.log('privateKey', privateKey); // 18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725
console.log('publicKey', publicKey.toString('hex')); // 0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352 

J'ai utilisé la librairie @noble/curves/secp256k1 pour calculer la clé publique, l'algorithme est trop bas niveau et a moins d'être vraiment a l'aise avec le sujet il est rarement conseillé de coder des fonctions cryptographiques. C'est aussi vrai pour la génération d'adresse Bitcoin, il y a déjà beaucoup de librairie pour faire ça plutôt que de le faire soi-même.

2. Transformer la clé publique en empreinte : SHA-256 et RIPEMD-160

Maintenant que nous avons une clé publique, il est temps de la transformer en une empreinte compacte et sécurisée. Cette transformation repose sur deux fonctions de hachage cryptographique : SHA-256 et RIPEMD-160. Ces outils agissent comme un broyeur mathématique : ils prennent une donnée (la clé publique, dans notre cas) et la compressent en une chaîne unique et fixe, impossible à inverser. Cette étape n’est pas là pour faire joli : elle renforce la sécurité en rendant la clé publique plus difficile à manipuler ou à deviner, tout en la rendant plus pratique à utiliser dans le réseau Bitcoin.

Pour cela, nous allons utiliser les fonctions cryptographiques de Node.js sur notre clé publique.

import * as crypto from "node:crypto";

function ripemd160(buffer) {
    return crypto.createHash('ripemd160').update(buffer).digest();
}

function sha256(buffer) {
    return crypto.createHash('sha256').update(buffer).digest();
}

const fingerprint = ripemd160(sha256(Buffer.from(publicKey)));
console.log(fingerprint.toString('hex')); // f54a5851e9372b87810a8e60cdd2e7cfd80b6e31

On a désormais une clé plus courte et une sécurité optimale. Mais il reste encore 2 étapes avant d'obtenir notre adresse Bitcoin.

3. Ajout de la version et du contrôle : le rôle du checksum

À ce stade, notre empreinte de clé publique est presque prête à devenir une adresse Bitcoin, mais il reste quelques étapes cruciales pour s'assurer qu'elle fonctionne correctement dans le réseau. D'abord, on ajoute un byte de version au début, qui indique au réseau Bitcoin de quel type d'adresse il s'agit (par exemple, 0x00 pour les adresses classiques P2PKH). Ensuite, on calcule un checksum, une sorte de signature mathématique à la fin de l'adresse, qui sert à détecter d'éventuelles erreurs de saisie ou de transmission. Ces deux éléments garantissent que l'adresse est non seulement valide, mais aussi adaptée au bon contexte du réseau Bitcoin. Une étape simple, mais essentielle pour éviter les malentendus numériques !

// For a P2PKH address, the version byte is 0x00
// This byte is prepended to the fingerprint to create an "extended address"
const networkVersion = Buffer.from([0x00]);
const extendedFingerprint = Buffer.concat([networkVersion, fingerprint]);

// The checksum is the first 4 bytes of the double SHA-256 hash of the extended address.
// It helps detect errors in the address when it's used or transmitted
const checksum = sha256(sha256(extendedFingerprint)).subarray(0, 4);

const address = Buffer.concat([extendedFingerprint, checksum]);
console.log('Extended public key', address.toString('hex')); // 00f54a5851e9372b87810a8e60cdd2e7cfd80b6e31

4. Encodage Base58 : rendre l’adresse human-friendly

Maintenant que nous avons généré une adresse Bitcoin sous forme binaire, il est temps de la rendre plus lisible et pratique à utiliser. C'est ici qu'intervient l'encodage Base58. Ce système permet de convertir notre adresse en une chaîne de caractères qui est plus facile à manipuler pour les utilisateurs tout en restant compacte et résistante aux erreurs. Contrairement au format standard hexadécimal, le Base58 élimine les caractères ambigus (comme le "0", "O", "I", et "l"), ce qui réduit les risques de confusion lors de la saisie ou de la lecture de l'adresse. Grâce à cet encodage, une adresse Bitcoin devient non seulement plus lisible, mais également plus sûre pour les utilisateurs dans leurs transactions quotidiennes.

import { binary_to_base58 } from "base58-js";

const btcAddress = binary_to_base58(address);
console.log('bitcoin address', btcAddress); // 1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs

J'utilise la librairie base58-js pour faire l'encode binaire vers base58. Aucun interêt de coder la fonction binary_to_base58 il suffit d'en comprendre l'utilité.

Et voilà, nous avons enfin notre adresse bitcoin : 1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs

Pour vérifier une adresse vous pouvez aller sur l'explorateur de blockchain.com et saisir l'adresse pour vérifier l'activité de celle-ci : 1PMycacnJaSqwwJqjawXBErnLsZ7RkXUAs.

Voici les documentations sur lesquelles je me suis basé pour la génération d'une adresse bitcoin :

Amélioration SegWit pour Bitcoin

SegWit (Segregated Witness) est une amélioration du protocole Bitcoin introduite pour augmenter le débit des transactions en réduisant leur taille. Cela est rendu possible en déplaçant les signatures dans une base de données séparée. En plus de cette optimisation, SegWit représente une avancée technique majeure du protocole Bitcoin. Il corrige le problème de malléabilité des transactions et ouvre la voie à des solutions innovantes telles que le Lightning Network.

Le problème de malléabilité des transactions permettait de modifier les signatures d'une transaction, changeant son identifiant (TxID) sans altérer son contenu économique.

Par ailleurs, SegWit permet une meilleure utilisation de l'espace des blocs, augmentant efficacement leur capacité sans compromettre la décentralisation.

SegWit (abréviation de Segregated Witness) est une mise à niveau rétrocompatible du protocole Bitcoin (à savoir un soft fork) qui modifie en profondeur la structure des transactions en déplaçant les données de signature (le témoin ou witness) dans une base de données séparée (segregated). cryptoast.fr.

SegWit se traduit par l’introduction de nouveaux formats d'adresse sur le réseau Bitcoin. Par exemple, l'adresse P2PKH, qui représente près de 40 % des adresses Bitcoin, a évolué en P2WPKH, qui représente désormais 20 % des adresses. Ce dernier est devenu le nouveau standard d'adresse et connaît une adoption massive.

Bitcoin address types compared: P2PKH, P2SH, P2WPKH, and more

Pour générer ce type d'adresse, on applique également un double hachage (SHA-256 puis RIPEMD-160) sur la clé publique, mais au lieu de simplement ajouter un byte de version classique, l'adresse est encodée dans un format différent (bech32) et commence par "bc1". Ainsi il n'y a plus de Base58 qui est remplacé par l'utilisation de Bech32.

// Hash the public key with SHA-256, then RIPEMD-160 to create the public key hash.
const publicKeyHash = ripemd160(sha256(Buffer.from(publicKey)));
console.log('Public Key Hash', publicKeyHash.toString('hex'));

// Convert to 5-bit words (Bech32 requires base32 encoding)
const words = bech32.toWords(publicKeyHash); // Convert public key hash to 5-bit words
words.unshift(0x00); // Prepend the witness version byte (P2WPKH is version 0)

// Encode with Bech32
const humanReadablePart = 'bc'; // HRP (bc for MainNet and tb for TestNet)
const btcAddress = bech32.encode(humanReadablePart, words);
console.log('Bitcoin Address (P2WPKH)', btcAddress);

Bech32 est une amélioration par rapport au format Base58. Il offre un mécanisme de vérification (checksum) plus fiable, est plus lisible pour les humains grâce à son utilisation de caractères minuscules et évite les ambiguïtés. De plus, il est mieux adapté aux QR codes, devenus un moyen courant pour partager et échanger des adresses Bitcoin de manière numérique.

Une adresse à partir d'un script

Jusqu'à présent, nous avons exploré des formats d'adresse basés sur les clés publiques et privées, mais ce n'est pas le seul modèle. Il existe également des formats comme le P2SH (Pay-To-Script Hash) et son évolution SegWit, le P2WSH.

Ces types d'adresses permettent de valider les transactions à l'aide de scripts, offrant ainsi une flexibilité bien plus grande. Elles sont particulièrement utiles dans plusieurs cas d'utilisation avancés, tels que :

  • Transactions multi-signature : plusieurs clés doivent approuver une transaction.
  • Dépôt fiduciaire (escrow) : un tiers contrôle temporairement les fonds.
  • Atomic Swaps : échanges de cryptomonnaies sans intermédiaire.

Les smart contracts avec Bitcoin (3/3) : Les cas d'utilisation

Cela illustre la puissance et la flexibilité du protocole Bitcoin, qui va bien au-delà de la simple association des adresses Bitcoin à des clés publiques. Grâce à des formats comme P2SH et P2WSH, les adresses peuvent contenir beaucoup plus d'informations et permettre des logiques complexes, telles que des scripts de validation qui intègrent des conditions multiples pour autoriser une transaction. Cela ouvre la porte à des fonctionnalités avancées, comme les contrats intelligents ou les transactions multi-signatures, qui renforcent la sécurité et la variété des échanges. Ces possibilités ajoutent une dimension supplémentaire au protocole, prouvant que derrière chaque adresse se cache bien plus que ce que l'on imagine à première vue.

Bitcoin dispose de son propre language de script, il s'agit d'un langage intentionnellement conçu pour être simple et sécurisé. Ce langage fonctionne de manière stack-based, c'est-à-dire qu’il utilise une pile pour empiler et dépiler des données ou des instructions. Il n'est pas possible de faire de boucle ni d'instruction complexe, le langage dispose d'opérations que l'on appelle opcodes.

Prenons un exemple très simple et assez commun : une adresse qui nécessite la validation de 2 personnes pour effectuer une transaction.

Notre code Bitcoin aurait alors la forme suivante :

<sig1> <pubkey1> CHECKSIG <sig2> <pubkey2> CHECKSIG AND

On utilise l’opération CHECKSIG pour vérifier que chaque signature correspond bien à la clé publique attendue, et on combine ces vérifications à l’aide de l’opération AND entre les deux instructions CHECKSIG.

Le AND est placé à la fin car, comme mentionné précédemment, l'exécution du script s'effectue à l'aide d'une pile. Il est donc nécessaire que toutes les opérations précédentes soient empilées et leurs résultats disponibles dans la pile au moment de l'exécution du AND.

// Generate two random key pairs
const privateKey1 = crypto.randomBytes(32).toString('hex');
const privateKey2 = crypto.randomBytes(32).toString('hex');
const pubkey1 = secp256k1.getPublicKey(privateKey1);
const pubkey2 = secp256k1.getPublicKey(privateKey2);

// Create the redeem script
const redeemScript = bitcoin.script.compile([
  pubkey1,                          // First public key
  bitcoin.opcodes.OP_CHECKSIG,      // CHECKSIG for sig1 and pubkey1
  pubkey2,                          // Second public key
  bitcoin.opcodes.OP_CHECKSIG,      // CHECKSIG for sig2 and pubkey2
  bitcoin.opcodes.OP_BOOLAND        // Combine both results with AND
]);

// Create a P2WSH payment object
const p2wsh = bitcoin.payments.p2wsh({
  redeem: {output: redeemScript},
});

console.log('P2WSH', p2wsh.address);

Le script qui est utilisé pour générer l'adresse est appelé Redeem Script, il définit les conditions nécessaire pour dépenser des bitcoins.

Aller plus loin

Voici, selon moi, l'essentiel pour comprendre le fonctionnement des adresses Bitcoin. J'ai hâte de me replonger dans le sujet avec un second article sur la blockchain Bitcoin. Ce sera l'occasion d'explorer des éléments fascinants comme le système de blocs, les transactions, les nœuds du réseau, et bien plus encore, afin de mieux comprendre les rouages de cette technologie unique.

Voici quelques sites qui sont très instructifs, en français, sur le sujet :