Il y a un peu plus d’un an à présent, nous avons développé une solution d’horodatage basée sur une blockchain. Elle permet de prouver l’antériorité d’un contenu (une idée ou une création par exemple) via l’utilisation d’une blockchain publique Ethereum directement depuis son navigateur. Donc sans organe central de contrôle. Il s’agit d’un cas d’usage simple mais parfaitement applicable aux besoins métiers des applications de nos clients : sceller un contenu et ses métadonnées de manière sûre et pérenne. Le code source est publié sous la licence Apache 2.

Pour faciliter la lecture, nous vous proposons une série de cinq articles qui présentent trois points de vue complémentaires de l’outil :

  1. Point de vue conceptuel : 🎨 Comprendre les notions de base d’une blockchain publique
  2. Point de vue utilisateur : 🕺 Interagir avec l’application décentralisée
  3. Point de vue développeur : 🏭 Configurer son environnement de développement
  4. Point de vue développeur : 🤝 Comprendre le développement du smart contract
  5. Point de vue développeur : 💻 Comprendre le développement du client

Ce quatrième article fournit des explications sur l’écriture des smart contracts et sur la manière de les déployer dans une blockchain de développements ou une blockchain publique.

Le code source des smarts contracts est dans le sous projet smart-contracts. Les smart contracts sont écrits dans le langage dédié solidity et compilés pour être exécutés dans une machine virtuelle Ethereum (EVM). Nous vous invitons à lire la documentation de référence du langage : https://solidity.readthedocs.io.

Commençons par expliciter le code source du smart contract mĂ©tier. 

# Smart contract métier AntProver

Il s’agit du smart contract principal qui réalise la preuve d’antériorité. Il permet de stocker les informations de manière structurée et de les requêter via une sorte d’API de services. Pour faciliter la lecture de cet article, nous explicitons le coeur de ce smart contract : une fonction d’écriture des informations et une fonction de consultation.

Le smart contract est défini dans le fichier smart-contracts/contracts/AntProver.sol.

# Squelette du contrat

Afin d’éviter un changement bloquant potentiel d’une version future (relativement au moment du développement), nous bornons les versions acceptées du compilateur. La directive suivante indique que le compilateur doit être en version 0.5.X :

Nous commençons par définir le contrat AntProver. Ici, nous n’avons pas de comportement particulier à la création :

# Structure et variables

Afin de porter les données d’un enregistrement, nous définissons la structure de données Record. Les types que nous utilisons ici sont tous simples. Nous avons des entiers non signés pour l’horodatage et le numéro de bloc, une chaîne de caractères pour le commentaire et une adresse pour l’émetteur :

Nous créons la variable hashesMetadata dans laquelle nous faisons correspondre un Record à chaque empreinte de fichier enregistrée. Dans la mesure où nous souhaitons que cette variable ne soit visible que par le contrat, nous utilisons l’accesseur private :

# Fonction d’écriture

Pour référencer un contenu à partir de son empreinte hash et du commentaire associé comment, nous définissons la fonction d’écriture addDocHash, accessible depuis l’extérieur, grâce à l’accesseur public :

Nous aurions pu omettre le mot clé memory pour le paramètre comment, qui est le mode de passage d’arguments activé par défaut. Avec ce mode, les arguments sont passés par valeur, contrairement au passage par référence activé avec le mot clé storage.

Notons que pour créer l’instance de Record, nous utilisons des informations récupérées à partir des paramètres et de propriétés :

  • Les informations du bloc sont disponibles dans la propriĂ©tĂ© block.
    • now est un alias de block.timestamp. Il s’agit de l’horodatage du bloc courant.
    • bloc.number permet de connaĂ®tre le numĂ©ro de bloc.
  • Les informations du message sont disponibles dans la propriĂ©tĂ© msg
    • msg.sender permet de rĂ©cupĂ©rer l’adresse de l’émetteur

D’autres propriétés sont disponibles.

L’enregistrement fraîchement créé newRecord est stocké en face de son hash dans dans variable d’association hashesMetadata.

# Fonction de lecture

La recherche d’un contenu est rĂ©alisĂ©e avec la fonction de lecture findDocHash. Elle permet de retrouver les informations Ă©ventuellement associĂ©es Ă  une empreinte. On la dĂ©clare comme Ă©tant une view function de manière Ă  s’assurer qu’elle ne modifiera pas l’état : variable, production d’Ă©vĂ©nement, crĂ©ation d’un contrat, etc. De ce fait, un appel Ă  cette fonction ne gĂ©nĂ©rera aucun frais.

La fonction findDocHash prend une empreinte en paramètre et retourne un quadruplet correspondant aux valeurs de l’instance de Record associée. Grâce à la variable d’association, l’accès est direct :

# Prérequis d’un smart contract

Pour sécuriser le référencement d’un contenu, on souhaite s’assurer que l’empreinte du fichier n’existe pas déjà. Au début de la fonction addDocHash, on ajoute un prérequis de manière à provoquer une erreur en cas de besoin :

Puis on crée la fonction exists qui teste si l’empreinte a déjà été enregistrée :

Lorsqu’une empreinte n’existe pas dans hashesMetadata, la propriété blockNumber a la valeur 0. En effet, il n’existe pas de valeur nulle dans le langage solidity et toute variable a la valeur par défaut.

# Gestion d’événements

Un smart contract peut générer des événements que des applications peuvent écouter via une interface RPC (Remote Procedure Call). Dans le cas présent, nous allons ajouter le type d’événement HashAdded :

Après l’enregistrement d’une empreinte à la fin de addDocHash, on émet une occurrence de l’événement avec l’appel suivant :

Nous verrons dans l’article suivant que nous pouvons écouter ce type d’événement depuis le client. Dans l’exemple présent, le frontal utilisateur affichera l’événement lorsque la transaction aura été validée de manière à ce que l’utilisateur soit en mesure de récupérer le certificat final au format pdf à conserver avec le document initial.

# Gestion des migrations / déploiement

Maintenant que nous avons expliqué le fonctionnement interne du smart contract métier, revenons à l’initialisation du sous projet. Ceci a été fait avec l’outil truffle et sa commande unbox. Le projet a été initialisé avec l’arborescence suivante :

  • contracts : source des smart contracts solidity
  • migrations : scripts de dĂ©ploiement
  • test : fichiers de tests
  • truffle-config.js : configuration du projet

Pour la gestion des migrations, les fichiers suivants sont utilisés :

En complément de ceux-ci et du smart contract AntProver.sol, nous avons écrit le script migrations/1567466298_ant_prover.js qui permet de déployer le smart contract AntProver :

La configuration truffle-config.js suivante permet de déployer dans la blockchain Ganache mise en place telle que décrite dans l’article précédent :

Pour rappel, nous avons déployé dans la blockchain de développement avec la commande :

# DĂ©ploiement dans une blockchain publique

Pour déployer le smart contract dans une blockchain publique, nous allons utiliser le wallet Infura. Dans la suite, nous déploierons le smart contract sur l’instance testnet (réseau de tests) Ropsten.

Infura est une API qui permet d’accĂ©der au rĂ©seau Ethereum. Avant de pouvoir dĂ©ployer, il faut se crĂ©er un compte, un projet puis rĂ©cupĂ©rer le PROJECT ID.

La définition des variables est nécessaire pour le déploiement final (exemple) :

En effet, nous les exploitons dans la configuration truffle-config.js qui est complĂ©tĂ©e avec la configuration ropsten. Pour celle-ci, nous profitons du provider web3 truffle-hdwallet-provider (renommĂ© @truffle/hdwallet-provider depuis) : 

Puis nous précisons le réseau “ropsten” plutôt que “development” lors de la migration :

# Métadonnées du smart contract

Lorsque le smart contract Ă©crit en solidity est compilĂ©, un fichier de  mĂ©tadonnĂ©es est gĂ©nĂ©rĂ©. Par exemple, nous avons versionnĂ© le fichier smart-contracts/build/contracts/AntProver.json produit car il prĂ©sente un intĂ©rĂŞt particulier.

Ce fichier est riche en informations. Par exemple, nous retrouvons la version du compilateur utilisée (ex : 0.5.8+commit.23d335f2), les sources ou encore le contrat ABI (Application Binary Interface). Ce dernier définit l’interface pour une utilisation en dehors de la blockchain ou au sein de la blockchain dans une communication entre smart contracts.

Nous retrouvons également les informations sur le contrat déployé dans les réseaux dans “networks”. Par exemple on retrouve l’adresse de déploiement du smart contract 0xA22fe2812085cf1b755796F5dA60EdFDc6E8dEe6 dans le réseau Ropsten qui porte l’identifiant 3 :

# Pour continuer

Dans cet article, nous avons couvert le développement côté blockchain du projet. Nous avons commencé par expliciter la manière dont nous avons conçu le smart contract en partant du squelette et en l’enrichissant avec la définition des structures de données, des données et des fonctions d’écriture et de lecture. Puis nous avons vu comment l’enrichir en le sécurisant et ajoutant une gestion d’événements.

Enfin, nous avons expliqué comment déployer le smart contract dans une blockchain de développements puis dans une blockchain publique.

Le prochain article et dernier de la série sera dédié au développement du client web qui permet d’interagir avec le smart contract écrit et déployé.