Solidity est le langage de programmation de la plateforme d’applications décentralisées la plus connue (et la plus utilisée) : Ethereum. Il a été créé par Gavin Wood et rendu public en août 2014, puis développé notamment par Christian Reitwiessner et Alex Beregszaszi. Tout comme le langage de script de Bitcoin, qui permet de programmer des transactions financières conditionnelles avec son jeu d’opcodes, Solidity permet de coder des smart contracts – un ensemble d’instructions qui permettent au réseau de mettre à jour l’état de la blockchain en fonction des conditions et des variables définies par leur créateur.
Cependant, Solidity va plus loin que le langage utilisé sur Bitcoin et permet de réaliser des transactions plus complexes, car il présente une différence majeure.
Solidity : un langage Turing-complet
Par essence, le langage de script de Bitcoin n’est pas “Turing-complet”, et c’est précisément ce qui a incité Vitalik Buterin à créer une blockchain reposant sur un système différent.
Que signifie l’expression “Turing-complet”, au juste ? Elle fait référence au mathématicien et cryptologue Alan Turing[1], un des précurseurs de l’informatique, qui décrivit un modèle pour définir les concepts d’algorithme, de programmation et de décidabilité[2] : la fameuse machine de Turing[3]. Il s’agit avant tout d’un concept et non d’un objet physique – même si Alan Turing donna vie à ce concept de calculateur idéal, en créant “la bombe”, une sorte d’ordinateur avant l’heure, pouvant déchiffrer les messages des Allemands durant la seconde guerre mondiale.
Un langage est dit “Turing-complet” s’il peut permettre de simuler une machine de Turing, avec le même pouvoir expressif et la même calculabilité. En particulier, un langage Turing-complet doit permettre d’exécuter des fonctions récursives ou “calculables”, nécessaires pour mettre en place des boucles conditionnelles (if, then, else) et plus généralement pour que le langage soit déterministe (les mêmes fonctions appliquées aux mêmes arguments donneront toujours le même résultat).
Cette machine de Turing a plusieurs points communs avec Ethereum :
- Le ruban (infini dans le modèle abstrait) a été remplacé par des lignes de codes, les instructions des smart-contracts ;
- La tête de lecture/écriture par les nœuds validateurs et le matériel des mineurs, qui mettent à jour l’état des contrats sur la blockchain ;
- Le registre d’état par la blockchain d’Ethereum ;
- Enfin, la table d’actions par le compilateur qui traduit les instructions des contrats en code binaire afin qu’elles soit exécutées.
Fonctionnement de Solidity
Solidity est un langage de programmation orienté objet qui présente des similitudes avec JavaScript ou C++. C’est un langage de haut niveau, qui sera compilé en langage de bas niveau (bytecode) pour être interprété par l’environnement d’exécution d’Ethereum.
Il s’agit de la Machine Virtuelle d’Ethereum ou EVM : cet environnement d’exécution est isolé du réseau et permet d’interpréter le code Solidity une fois compilé, afin de garantir la mise à jour de l’état des contrats (et donc de la blockchain), via tous les nœuds validateurs du réseau. Ce sont les mineurs qui exécutent ces instructions et mettent à jour la blockchain d’Ethereum : ils sont récompensés financièrement pour cela. Chaque instruction d’un contrat Solidity aura donc un coût d’exécution (en gas). Afin d’éviter les boucles infinies au sein d’un programme, il y a toujours une limite au gas qui pourra être consommé.
Tout comme les classes dans les langages de programmation orientés objet, les contrats Solidity sont définis par des variables d’état, des fonctions, des modificateurs, des événements et des types. Il est possible d’appeler d’autres contrats à l’intérieur d’un contrat (message calls), d’injecter des données externes au sein d’un contrat, et il existe aussi des contrats spéciaux, les librairies et les interfaces.
L’EVM dispose de trois zones distinctes pour traiter les données :
- La zone de stockage : associée à un compte Ethereum, elle est persistante et intervient entre l’appel d’une fonction et une transaction. Elle est donc la plus coûteuse en gas.
- La zone de mémoire : associée à chaque contrat et mise à jour lors de chaque appel (message call), sa taille varie proportionnellement à la quantité de données gérées par le smart contract. Elle a aussi un coût en gas.
- La pile de la machine virtuelle : elle comporte les instructions du contrat, qui seront exécutées de façon descendante – les opérations se font en prenant l’élément situé en haut de la pile, en envoyant le résultat dans la pile et en passant à l’élément suivant.
Le panel d’instructions de l’EVM est minimal pour des raisons de compatibilité et de sécurité, mais il est évidemment possible de faire toutes les opérations arithmétiques et logiques classiques, des sauts conditionnels, etc… Il est également possible d’accéder à des données au niveau d’un bloc, comme son numéro de version ou son horodatage.
Les composants d’un smart contract
Concrètement, on peut visualiser un contrat comme un ensemble de lignes de code (ses fonctions), et un ensemble de données (qui définissent l’état du contrat sur la blockchain Ethereum) stockés sur un compte Ethereum (généralement celui de son propriétaire ou créateur). Sur Ethereum, on parle plus précisément de compte et non d’adresse, car il existe deux types de comptes : les comptes externes, contrôlés par des paires de clefs publiques/privées (les adresses typiques contrôlées par des humains), et les comptes dédiés aux contrats (contrôlés par le code associé).
Plus précisément, un contrat est défini par différents éléments :
- Les variables d’état : elles peuvent être de différent types et leur valeur est stockée en permanence dans le contrat.
- Les fonctions : au cœur du code du contrat, elles seront exécutées selon les arguments et les paramètres injectés, et renverront les variables souhaitées. Elles peuvent être appelées de façon interne ou externe et on peut définir leur degré de visibilité.
- Les modificateurs : ces outils permettent d’amender les fonctions d’un contrat de façon déclarative, ce qui est pratique pour éviter les répétitions dans le code et gagner en lisibilité.
- Les événements : ils permettent d’interagir avec le système de journalisation des actions réalisées par le contrat (les “logs” générés par l’EVM).
- Les types permettent de définir ce que représente une variable. Avec Solidity, on peut créer des structs (des types “personnalisés” pouvant regrouper plusieurs variables) et faire du mapping (indexer des variables selon un système clé -> valeur). Il y a des différences notables avec d’autres langages orientés objets, notamment dans le cadre du mapping.
La notion d’héritage est également importante pour comprendre Solidity : un contrat peut hériter des propriétés d’un ou plusieurs autres contrats. Avec l’héritage, si on fait appel à des smart contracts externes, le code de ces derniers sera compilé au sein du contrat créé, puis déployé sur la blockchain.
Les interfaces sont semblables à des notices spécifiant la structure des fonctions d’un contrat (nom, entrées/sorties) sans en décrire le code. Elles sont utiles pour interagir avec plusieurs contrats externes et créer des contrats complexes, comme en faisant appel à des librairies.
Les librairies sont semblables à des contrats déployés indéfiniment sur la blockchain, dont le code est réutilisé par de multiples contrats y faisant appel. Les librairies sont donc du code isolé, qui sera exécuté spécifiquement pour le smart contract les intégrant.
Il existe de nombreuses intégrations de Solidity et interfaces de développement. Remix est l’IDE la plus utilisée, Atom est un éditeur de texte bien apprécié de la communauté, Metamask le client léger/wallet le populaire. De nombreux outils et plugins gratuits sont disponibles pour développer sur Ethereum. Une bonne manière d’appréhender les smart contracts d’Ethereum et d’avoir une vue d’ensemble de Solidity, c’est de lire sa documentation et d’aller jeter un coup d’œil sur Etherscan à quelques contrats simples.
Il existe plusieurs réseaux de test sur Ethereum : les erreurs y sont permises et le gas ne coûte rien. En revanche, une fois déployé sur la blockchain d’Ethereum, un contrat fonctionnera tant qu’il aura assez de gas pour l’alimenter ! Les erreurs peuvent donc s’avérer très coûteuses, ce qui fut le cas dans la fameuse affaire TheDAO ou avec le smart contract de Parity.
Développer avec Solidity – ressources
Solidity est désormais le langage phare pour coder et déployer des smart contracts. De nombreuses plateformes blockchain l’utilisent, comme Qtum, Ubiq, Rootstock ou encore Tendermint. Si vous vous intéressez à l’aspect technique des cryptomonnaies et des applications décentralisées, comprendre et utiliser Solidity est une base solide ! Il faut le dire, Solidity n’est pas un langage complexe.
Il existe des ressources anglophones gratuites :
- La documentation officielle de Solidity
- Les tutoriels de Block Geeks
- Les tutoriels de Coinmonks
Pour l’instant, il existe peu de ressources sur le sujet en français :
- Les nombreux articles et tutoriels d’Ethereum France