Depuis la popularisation d’internet, les utilisateurs comme les entreprises, abusent de la portée et de la facilité d’accès d’Internet en l’inondant d’emails et de contenus « non sollicités » : du « spam », comme l’appellent les jeunes.
En 1997, les boîtes de réception sont partout et malheureusement les spammeurs aussi ! Ce qui a amené la création du Proof of Work ! (Attention, ces explications contiennent du code)
Sommaire
Pourquoi les gens envoient-ils du spam ?
La plupart des spams essaient de vendre quelque chose (publicité) ou essaient d’inciter la cible à partager ses informations sensibles (hameçonnage/Phishing) que l’expéditeur pourra utiliser de manière malveillante.
Pourquoi est-il si facile d’envoyer des spams ?
Un email est une série de caractères qui peuvent être reproduis et distribuées quasi-instantanément à des millions de personnes pratiquement sans frais (par rapport aux meilleures options suivantes).
L’envoi d’e-mails est bon marché principalement parce que :
- Le canal de distribution (Internet) est essentiellement gratuit et ouvert à tous
- Il nécessite des ressources de calcul négligeables pour envoyer un e-mail
C’est ainsi que fut crée le SPAM !
Et si l’envoi d’un email ou tout autres opérations, nécessitaient un ordinateur pour effectuer un effort de calcul modeste afin d’envoyer un message ?
L’envoi d’un seul email ne serait pas trop exigeant (peut-être ne prendrait-il qu’une seconde environ), mais envoyer des millions et des millions deviendrait rapidement un effort herculéen nécessitant des ressources de calcul massives, et donc coûteuses.
Cela a donc donné naissance à des systèmes de preuve de travail. (PoW)
Dans un système de preuve de travail, un ordinateur doit résoudre un problème de calcul d’un niveau de difficulté prédéterminé avant d’être autorisé à effectuer une action souhaitée.
Une fois que l’ordinateur a résolu le problème, il partage sa réponse comme « preuve » de ses efforts.
Un autre ordinateur ou le réseau sera ainsi capable de vérifier rapidement la preuve d’effort fournie par l’ordinateur et permettra donc l’action souhaitée.
Dans le cas présenté, qui est la limitation du spam, le résultat sera donc que notre boite mail n’acceptera que les e-mail provenant de quelqu’un capable de fournir la preuve qu’il a fait un effort pour nous le faire parvenir.
Alors, comment peut-on vérifier qu’un ordinateur a fait un effort particulier ?
Imaginez que nous ayons une fonction qui, pour une entrée donnée (dans notre cas l’email), produit une sortie de taille fixe.
Par exemple, lorsque nous envoyons une simple chaîne via notre fonction :
our_function(“Qui est l'enfoiré qui a mangé mes frites?”)
Il renvoie :
d5b1f2b59a73a93fb62ec75ee5df121f7716a8a757cd15c35bbc20ae8e814379
Il produit cette sortie chaque fois que nous envoyons cette chaîne exacte à travers lui.
Maintenant, regardez ce qui se passe lorsque nous modifions un seul caractère dans notre entrée :
our_function(“Qui est l'enfoiré qui a mangé mes frites!”)
Retour :
81c4f3e63c45f8cdf7fcd46d32eddbf8bf51bd656719b68ec50d226ca36a941b
Le rendu est radicalement différent !
Cela rend donc impossible de déchiffrer la manière dont l’entrée est transformée en sortie. Car comme nous l’avons vu, avec la moindre modification de notre entrée, le résultat change totalement.
N’oubliez pas non plus que notre résultat est toujours une chaîne de taille fixe. Dans cet exemple, notre sortie comportera toujours 64 caractères.
Par souci de simplicité, imaginons que chaque caractère de la chaîne de sortie ne peut être que l’une des 16 valeurs hexadécimales (0-9, af). Même avec ces contraintes, notre fonction donne un nombre énorme de sorties possibles.
Un rappel mathématique : une chaîne de 64 caractères où chaque caractère peut être l'un des 16 éléments produit 16^64 sorties différentes possibles. 16^64 est un nombre ridiculement grand. Comme, les atomes totaux dans l'univers observable sont énormes.
Compte tenu de l’énorme gamme de valeurs de sortie possibles, nous serons en mesure de générer une chaîne de sortie unique pour à peu près n’importe quelle entrée.
Parfois, deux entrées différentes correspondent à la même valeur (une « collision »), mais dans notre fonction, cela est extrêmement rare.
En termes simples : notre fonction crée un identifiant unique pour chaque entrée.
Ce type de fonction que nous venons de créer est appelée : une « fonction de hachage cryptographique ».
Les fonctions de hachage ont d’autres particularités toutes aussi passionnantes, mais nous couvrirons la principale pour le moment !
Comment utiliser cette fonction pour « prouver » qu’un ordinateur a effectué un effort ?
Rappelez-vous que notre fonction s’exécute rapidement. elle est facile à exécuter produit un résultat unique.
Mais, imaginez si nous voulions :
Résoudre pour une entrée, n, où la fonction nommée our_function(n) renvoie la sortie suivante : '0c836ce63af37a723cf4f8012f6827a58ba55196bf0f9dc9f962098d24f6ab7f'.
En supposant que l’ordinateur n’a jamais vu la chaîne de sortie (et ait connu l’entrée qui l’a produite), il devra commencer à faire des suppositions aléatoires et à vérifier la sortie.
Mais, rappelez-vous, cela peut prendre jusqu’à 16^64 occurrences pour obtenir la bonne réponse.
Cela pourrait littéralement prendre des milliards d’années à résoudre, alors à la place, nous devrions poser une question beaucoup moins énergivore comme :
Dans une chaîne aléatoire, résolvez une entrée, n, où our_function(n + random_string) renvoie une sortie où les trois premiers caractères sont des 0.
random_string= 'a638e5L'
Même s’ils découvraient une entrée, n, qui avait précédemment produit une sortie commençant par trois 0, cette même entrée n combinée à une chaîne aléatoire produirait une valeur très différente, les obligeant à repartir de zéro et à résoudre un n différent.
Bien évidemment, notre fonction de génération de chaînes aléatoires devrait être vraiment aléatoire. Un générateur aléatoire à moitié calculé qui produirait les mêmes défis plus fréquemment que la probabilité pure augmenterait la probabilité qu’un challenger reçoive le même défi deux fois.
Une fonction simple pour deviner la valeur de n pourrait ressembler à ce qui suit :
def mint(challenge, work_factor):
guess = 0
while True:
n = str(guess)
if verify(challenge, work_factor, n):
print("Found the correct guess!")
return n
guess += 1
Dans le code ci-dessus, « challenge » est la chaîne aléatoire que nous enverrons à un challenger.
Et nous pourrions valider des suppositions aléatoires pour n avec quelque chose comme ça. Nous demandons simplement si Hash(n + random_string) renvoie une valeur commençant par un nombre suffisant de 0.
def verify(challenge, work_factor, n):
full_header = n + str(challenge)
h = hashlib.sha256()
h.update(full_header.encode('utf-8'))
hash_output = h.hexdigest()
for i in range(0, work_factor):
if not hash_output[i] == str(0):
return False
return True
Assez simple, non ? (J’imagine que non :p )
Nous devrons développer quelques éléments supplémentaires pour rendre cela plus « parfait », comme les concepts inventés pour lutter contre le spam (Hashcash, en particulier), mais j’espère que cette démonstration vous aura appris les base de la preuve de travail.
Le proof of work qui est utilisé par un grand nombre de cryptomonnaies comme Ethereum utilise le même système pour valider chaque opérations sur la blockchain !