Problèmes potentiels avec les contrats

Le langage Marlowe est conçu pour comporter le moins possible de pièges et d'écueils, afin que les contrats puissent être rédigés de manière intuitive, en évitant toute surprise. Néanmoins, il est impossible, par conception, d'exclure tous les contrats qui ne devraient pas être écrits, sans rendre Marlowe beaucoup plus difficile à utiliser. De plus, même lorsqu'un contrat est bien écrit, il est toujours possible pour ses utilisateurs d'interagir avec lui de manière non valide, en émettant des transactions non valides.

Dans tous les cas, lorsque ces effets involontaires se produisent, Marlowe est conçu pour se comporter de la manière la plus intuitive et conservatrice possible. Cependant, il est utile d'être conscient de ces problèmes potentiels, et de revoir comment Marlowe se comporte dans ces situations. C'est le sujet de ce tutoriel.

 

Avertissements

Les avertissements de Marlowe sont des indications qu'un contrat est mal écrit. Un contrat bien écrit ne devrait jamais émettre d'avertissement, quelle que soit la façon dont les utilisateurs interagissent avec lui. Idéalement, nous aimerions interdire l'écriture de contrats susceptibles d'émettre des avertissements, mais cela nécessiterait que les contrats Marlowe soient typés de manière dépendante, et l'écriture d'expressions typées de manière dépendante est beaucoup plus compliquée.

Au lieu de cela, Marlowe permet d'écrire des contrats qui émettent des avertissements, et nous fournissons des outils d'analyse statique qui permettent aux développeurs de contrats de vérifier si un contrat particulier peut éventuellement émettre des avertissements. De plus, nous fournissons des comportements de repli pour les cas où un contrat produit un avertissement, malgré nos conseils. Nous fournissons des comportements de repli parce que nous reconnaissons que l'analyse de gros contrats peut être très coûteuse en termes de calcul, et parce que des erreurs peuvent être commises. Nous voulons que les contrats mal écrits échouent de la manière la plus inoffensive possible, c'est-à-dire de manière conservatrice.

 

Paiements non-positifs

Lorsqu'un contrat est supposé payer une somme d'argent inférieure à une unité d'une devise ou d'un token, il émettra un avertissement NonPositivePay , et il ne transférera pas d'argent.

Les paiements négatifs doivent être mis en œuvre sous forme de dépôts positifs (lors du paiement d'un participant) ou de paiements positifs en sens inverse (lors du paiement entre comptes).

 

Dépôts non-positifs

Lorsqu'un contrat est supposé attendre une somme d'argent inférieure à une unité d'une devise ou d'un token, il attendra quand même une transaction IDeposit , mais cette transaction ne doit pas transférer d'argent dans le contrat et aucun argent n'est transféré au participant qui émet la transaction. Une fois que ce "faux" dépôt est réussi, le contrat émettra un avertissement NonPositiveDeposit .

Les dépôts négatifs doivent toujours être effectués sous forme de paiements positifs.

 

Paiement partiel

Lorsqu'un contrat est supposé payer une somme d'argent supérieure à celle qui se trouve sur le compte source, il se contentera de transférer ce qui est disponible sur ce compte, même s'il y a suffisamment d'argent sur tous les comptes du contrat, et émettra un avertissement PartialPay .

Les paiements partiels doivent être évités car un contrat qui ne produit jamais de paiement partiel est un contrat explicite. Les contrats explicites rassurent leurs utilisateurs sur le fait qu'ils seront exécutoires et que, quel que soit l'endroit du contrat où il est indiqué qu'un paiement doit être effectué, il le sera effectivement.

 

Let shadowing (ombrage) (pas couvert par l'analyse statique)

Lorsqu'un contrat atteint une construction Let qui redéfinit une valeur avec un identifiant qui a déjà été défini par un Let externe, le contrat émettra un avertissement Shadowing , et remplacera la définition précédente.

Le shadowing est une mauvaise pratique de programmation, car il est source de confusion. L'utilisation du même identifiant pour plus d'une chose peut induire en erreur les développeurs ou les utilisateurs en leur faisant penser qu'une utilisation de Use va être évalué à un montant alors qu'il va être évalué à un autre montant différent.

 

Mauvaises odeurs

Il existe d'autres "mauvaises odeurs" qui indiquent qu'un contrat a probablement été mal conçu.

Ces contrats sont valides, dans le sens où ils ne causeront pas nécessairement d'avertissements, et ils font ce qu'ils disent faire, mais ils ont des caractéristiques qui suggèrent que soit le développeur du contrat n'était pas pleinement conscient des conséquences du contrat, soit le développeur a délibérément écrit le contrat d'une manière qui était confuse pour le lecteur.

 

Utilisation de Let non définie (devrait être un avertissement)

Lorsqu'une construction Use utilise un identifiant qui n'a pas encore été défini, elle est évaluée à la valeur par défaut de 0. Aucun avertissement ne sera émis mais, une fois encore, il s'agit d'une mauvaise pratique car elle peut être trompeuse. (Constant 0) devrait être utilisé à la place car elle rend explicite le montant en question.

 

Parties inaccessibles d'un contrat

C'est la principale mauvaise odeur des contrats Marlowe. Si une partie du contrat est inaccessible, pourquoi l'avoir incluse en premier lieu ?

Cette mauvaise odeur prend un certain nombre de formes.

 

Sub-Contract est inaccessible

Par exemple:

Le contrat précédent est équivalent à contract2. En général, vous ne devriez jamais utiliser FalseObs, et vous ne devez utiliser que TrueObs comme observation de base d'une construction de Case .

 

Observation est toujours raccourcie

Par exemple:

L'observation précédente est équivalente à observation1. Encore une fois, vous ne devez utiliser que TrueObs comme observation de base d'une construction de Case .

 

Une branche When est inaccessible

Par exemple:

contract2 est inaccessible, l'ensemble Case pourrait être supprimée du contrat et le comportement serait le même.

 

Timeouts imbriqués non incrémentés

Par exemple:

contract1 est inaccessible : après le slot 10, le contrat évoluera directement en contract2. Le When interne ne fait aucune différence pour le contrat.

 

Problèmes d'utilisabilité

Même si un contrat évite les avertissements et ne comporte pas de code inaccessible, il pourrait permettre à des utilisateurs malveillants de placer d'autres utilisateurs dans des situations indésirables qui n'étaient pas prévues à l'origine par le développeur du contrat.

 

Mauvais cadencement des constructions When 

Considérez le contrat suivant:

Il n'y a rien de mal en principe à ce contrat, mais si (Role "alice") fait son choix dans le slot 9, il sera pratiquement impossible pour bob de faire son choix à temps et d'obtenir le remboursement de l'argent sur son compte (Role "bob"). À moins que cela ne fasse partie d'un jeu et que ce soit l'effet recherché, il s'agit probablement d'un contrat injuste pour (Role "bob").

En général, une bonne pratique consiste à s'assurer que les constructions When ont des timeouts croissants, et que l'augmentation entre les timeouts est raisonnable pour que les différentes parties puissent émettre et faire accepter leurs transactions par la blockchain. Il existe de nombreuses raisons pour lesquelles la participation d'une partie peut être retardée : une panne d'approvisionnement en énergie, un pic soudain du nombre de transactions en attente dans la blockchain, des attaques de réseau, etc. Il est donc important de prévoir beaucoup de temps et d'être généreux avec les timeouts et les augmentations de timeouts.

 

Erreurs

Finalement, même si un contrat est parfaitement rédigé, les utilisateurs peuvent l'utiliser de manière incorrecte, et nous appelons ces utilisations incorrectes des erreurs d'usage.

Dans tous les cas, lorsqu'une transaction provoque une erreur, la transaction n'aura aucun effet sur le Contract ou son State. En fait, le wallet d'un utilisateur saura à l'avance si une transaction va produire une erreur, car les transactions sont déterministes, et les utilisateurs ne devraient donc jamais avoir besoin d'envoyer une transaction erronée à la blockchain.

 

Intervalle ambigüe

Lorsqu'une transaction atteint un timeout, son intervalle de slot doit être sans ambiguïté quant à savoir si le timeout est passé ou non. Par exemple, si le plus haut When d’un contrat a un timeout 10 et une transaction avec un intervalle slot [6, 14] est émise, la transaction provoquera une erreur AmbiguousSlotIntervalError , car il est impossible de savoir si le timeout est passé juste en regardant la transaction. Pour éviter cela, la transaction doit être divisée en deux transactions distinctes:

  1. Une avec intervalle de slot [6, 9].
  2. Une autre avec un intervalle de slot [10, 14].

 

Appliquer no-match

Si une transaction ne fournit pas les données d'entrée prévues par Contract, alors le contrat émettra une erreur NoMatchError , et toute la transaction sera supprimée.

 

Transaction inutile

Si une transaction n'a pas d'effet sur le Contract ou State, le résultat sera une erreur UselessTransaction , et la transaction entière sera rejetée. La raison pour laquelle nous rejetons les transactions inutiles est qu'elles ouvrent la porte aux attaques par déni de service (DoS), car un attaquant potentiel pourrait inonder le contrat de transactions inutiles et empêcher les transactions nécessaires d'entrer dans la blockchain.

© Copyright 2020, IOHK Revision bca9eb4b.

 

Vous trouverez une copie officielle de ce document ici :

https://alpha.marlowe.iohkdev.io/doc/marlowe/tutorials/potential-problems-with-contracts.html

https://docs.cardano.org/projects/plutus/en/latest/marlowe/tutorials/potential-problems-with-contracts.html

 

Plus de traductions de Cardano à: Cardano For The World