
Examen final du cours CS251 (Cryptomonnaies et technologie blockchain) de l'Université Stanford, 2021
TechFlow SélectionTechFlow Sélection

Examen final du cours CS251 (Cryptomonnaies et technologie blockchain) de l'Université Stanford, 2021
Examen final du cours CS251 (Cryptomonnaies et technologie blockchain) de l'université Stanford, 2021
Traduit par : TechFlow intern
Conformément aux dispositions explicites et à l'esprit du Code d'honneur de Stanford, je n'ai reçu aucune aide lors de cet examen, ni en ai fourni à quiconque.
Signature : _________________________
• Cet examen comprend 6 questions, totalisant 100 points.
• Vous devez terminer l'examen dans le temps imparti.
• Veuillez répondre sur Gradescope (D5GKRX).
• Soyez concis dans vos réponses.
Question 1. (18 points) Questions générales.
A) → Expliquez brièvement pourquoi les systèmes Rollup stockent toutes les transactions sur la chaîne ? Que se passerait-il si les données de transaction étaient perdues, sans sauvegarde ailleurs ?
B) → Considérez le code Solidity suivant :
pragma solidity ^0.8.0;
contract ERC20 is IERC20 {
mapping(address => uint256) private _balances;
event Transfer(address indexed from, address indexed to, uint256 value); function _transfer(address sender, address recipient, uint256 amount) { emit Transfer(sender, recipient, amount);
}}
Supposons que ce code soit déployé dans deux contrats : un contrat à l'adresse X et un autre à l'adresse Y. Laquelle des options suivantes peut lire l'état _balances dans le contrat à l'adresse X ? Entourez la ou les bonnes réponses.
A Le code dans la fonction _transfer() du contrat ERC20 à l'adresse X
B Le code dans la fonction _transfer() du contrat ERC20 à l'adresse Y
C Un utilisateur final utilisant etherscan.io
C) → En reprenant la question précédente, laquelle des options suivantes peut lire l'événement émis Transfer lorsque la fonction _transfer() est appelée ? Entourez la bonne réponse.
A Le code dans une fonction getBalance() définie dans le contrat ERC20 à l'adresse X
B Le code dans une fonction getBalance() définie dans le contrat ERC20 à l'adresse Y
C Un utilisateur final utilisant etherscan.io
D) → Deux transactions Ethereum, tx1 et tx2, sont soumises simultanément. La transaction tx1 a un maxPriorityFee égal à y, et tx2 a un maxPriorityFee égal à 2y. Est-ce que tx2 sera forcément exécutée avant tx1 sur la chaîne ? Justifiez votre réponse. Vous pouvez supposer que le maxFee de tx1 et tx2 est supérieur au baseFee + maxPriorityFee.
E) → Alice veut acheter une voiture auprès du vendeur Bob. Elle envoie 1 bitcoin à l'adresse Bitcoin de Bob. Bob attend une transaction où : 1) l'entrée provient de l'adresse d'Alice, et 2) l'une des sorties est un UTXO lié à son adresse, d'une valeur de 1 BTC. Dès que Bob voit cette transaction sur la blockchain Bitcoin, il remet les clés à Alice, qui peut alors partir avec la voiture. Cette méthode est-elle sécurisée ? Alice pourrait-elle obtenir la voiture gratuitement ? Si oui, expliquez pourquoi. Sinon, expliquez comment Bob devrait s'y prendre pour garantir qu'il soit payé.
F) → Alice possède une Tesla neuve modèle Y. Peut-elle l'utiliser comme garantie pour emprunter sur le système Compound, sans la vendre ? Si oui, expliquez comment. Sinon, expliquez pourquoi.
Question 2. (20 points) Diffusion byzantine.
Supposons qu'il y ait n parties, avec n > 3, dont l'une est désignée comme expéditeur. L'expéditeur possède un bit b ∈ {0,1}. Un protocole de diffusion consiste en des messages échangés entre les parties, et chaque partie produit en sortie un bit bi, où i = 1, ..., n ou 0.
• Nous disons que le protocole assure la cohérence si, pour deux parties honnêtes quelconques, si l'une produit b et l'autre b', alors b = b'.
• Nous disons que le protocole est valide si, lorsque l'expéditeur est honnête, tous les participants honnêtes produisent en sortie le bit b de l'expéditeur.
• Nous disons que le protocole assure l'universalité : si une partie honnête produit un bit en sortie, alors toutes les parties honnêtes finissent par produire un bit.
Un protocole de diffusion fiable (RBC) est un protocole de diffusion satisfaisant ces trois propriétés. Nous supposons l'existence d'une infrastructure à clés publiques (PKI), c'est-à-dire que chaque partie possède une clé privée de signature, et que chaque partie connaît correctement la clé publique de vérification de signature des autres.
Dans un réseau synchrone, considérons le protocole de diffusion suivant :
• Étape 0 : L'expéditeur envoie son bit d'entrée b (accompagné de sa signature) à toutes les autres parties. Il produit ensuite b en sortie et termine.
• Étape 1 : Chaque destinataire non-expéditeur i renvoie aux autres non-expéditeurs l'information reçue de l'expéditeur, signée par i. S'il n'a reçu aucun message de l'expéditeur, il ne fait rien. De même, s'il reçoit un message mal formé (signature invalide ou message non constitué d'un seul bit), il ne fait rien à cette étape.
• Étape 2 : Chaque non-expéditeur rassemble tous les messages reçus (au plus n−1 messages : un provenant de l'expéditeur à l'étape 0, et un par non-expéditeur à l'étape 1). S'il reçoit deux messages signés de l'expéditeur avec des bits opposés (par exemple, un message signé contenant 0, un autre contenant 1), alors l'expéditeur est malhonnête ; le non-expéditeur produit 0 en sortie et termine. Sinon, si tous les messages correctement signés de l'expéditeur contiennent le même bit, le non-expéditeur produit ce bit. S'il n'a reçu aucun message, il ne produit rien.
Pour les questions suivantes, décrivez une attaque ou expliquez pourquoi aucune attaque n'est possible.
A) Supposons qu'il y ait au plus une seule partie malhonnête. Le protocole préserve-t-il la cohérence ?
B) Supposons qu'il y ait au plus une seule partie malhonnête. Le protocole préserve-t-il la validité ?
C) Supposons qu'il y ait au plus deux parties malhonnêtes. Montrez que le protocole ne garantit pas la cohérence.
D) Supposons qu'il y ait au plus deux parties malhonnêtes. Le protocole préserve-t-il la validité ?
E) Pour tout nombre de parties malhonnêtes, le protocole assure-t-il l'universalité ?
Question 3 (20 points) : Market maker automatisé (AMM).
Vous êtes fournisseur de liquidités sur Uniswap V2, ayant apporté 5 ETH et 5000 DAI au pool DAI/ETH. En supposant qu'1 DAI vaut 1 dollar, votre mise totale est de 10 000 dollars.
A) Quelques mois plus tard, le prix de 1 ETH monte à 2000 DAI. Une fois que le pool DAI/ETH s'est ajusté à ce nouveau taux de change, vous décidez de retirer toute votre part de liquidités. En supposant que le système ne prélève pas de frais (φ = 1), combien d'ETH et de DAI allez-vous recevoir ?
B) Si vous aviez simplement conservé vos 5 ETH et 5000 DAI, votre patrimoine vaudrait maintenant 15 000 DAI, soit un gain de 5000 DAI. Pendant cette période, comparé à la stratégie de « simple détention », quelle perte avez-vous subie en tant que fournisseur de liquidités sur Uniswap V2 ? Exprimez cette perte en valeur absolue en dollars, en supposant que 1 DAI = 1 USD. On appelle cela une perte temporaire, bien qu'ici elle soit en réalité quasi permanente.
C) Si vous avez perdu x dollars en tant que fournisseur de liquidités sur Uniswap V2, où sont allés ces fonds ? Plus précisément, qui a gagné ces x dollars durant ce processus ?
D) Passons maintenant à l'utilisation d'Uniswap V2 pour échanger. Supposons que Bob utilise le pool DAI/ETH pour échanger une grande quantité de DAI contre de l'ETH. Après la transaction, le pool contient légèrement plus de DAI et moins d'ETH. Ainsi, le ratio des actifs dans le pool est légèrement déséquilibré.
L'arbitragiste Alice repère cette opportunité et souhaite effectuer une transaction inverse afin de rééquilibrer le pool. Elle vise à tirer profit de cette transaction, et souhaite donc que sa transaction soit exécutée immédiatement après celle de Bob. Cette stratégie est appelée « suivi » (taille).
Comment Alice peut-elle mettre en œuvre ce plan de suivi ? Proposez une méthode permettant à la transaction d'Alice d'avoir une chance raisonnable d'être exécutée juste après celle de Bob.
E) Supposons que 10 arbitragistes différents, cherchant à exploiter l'opportunité créée par la transaction de Bob, exécutent exactement la même stratégie de suivi en même temps, en utilisant tous le mécanisme décrit dans la partie (D). Lequel de ces 10 aura le plus de chances de réussir ?
Question 4. [16 points] : Vulnérabilité de réentrance dans Hashmasks
Dans le cours 8 section 3, nous avons étudié la vulnérabilité de réentrance dans Solidity. Dans cette question, nous allons examiner un exemple réel intéressant. Considérons l'extrait de code Solidity utilisé par l'un des 16384 NFT. Via la fonction mintNFT() de ce contrat NFT, un utilisateur peut frapper jusqu'à 20 NFT en une seule fois. Vous pouvez supposer que toutes les variables internes ont été correctement initialisées par le constructeur (non affiché).
function mintNFT(uint256 numberOfNfts) public payable {
require(totalSupply() < 16384, "Sale has already ended");
require(numberOfNfts > 0, "numberOfNfts cannot be 0");
require(numberOfNfts <= 20, "You may not buy more than 20 NFTs at once"); require(totalSupply().add(numberOfNfts) <= 16384, "Exceeds NFT supply"); require(getNFTPrice().mul(numberOfNfts) == msg.value, "Value sent is not correct");
for (uint i = 0; i < numberOfNfts; i++) {
uint mintIndex = totalSupply(); // get number of NFTs issued so far
_safeMint(msg.sender, mintIndex); // mint the next one
} }
function _safeMint(address to, uint256 tokenId) internal virtual {
// Mint one NFT and assign it to address(to).
require(!_exists(tokenId), "ERC721: token already minted");
_data = _mint(to, tokenId); // mint NFT and assign it to address to
_totalSupply ++; // increment totalSupply() by one
if (to.isContract()) {
// Confirm that NFT was recorded properly by calling
// the function onERC721Received() at address(to).
// The arguments to the function are not important here.
// If onERC721Received is implemented correctly at address(to) then
// the function returns _ERC721_RECEIVED if all is well.
bytes4 memory retval=
IERC721Receiver(to).onERC721Received(to, address(0), tokenId, _data);
require(retval == _ERC721_RECEIVED, "NFT Rejected by receiver");
} }
Montrons que _safeMint n'est en réalité pas sûr (malgré son nom).
A) Supposons que 16370 NFT aient déjà été frappés, donc totalSupply() = 16370. Expliquez comment un contrat malveillant pourrait provoquer la création de plus de 16384 NFT. Quel est le nombre maximal de NFT qu'un attaquant peut frapper ?
Indice : Que se passe-t-il si le contrat appelé via onERC721Received est malveillant ? Examinez attentivement la boucle de frappe et pensez à la vulnérabilité de réentrance.
B) En supposant que totalSupply() vaut actuellement 16370, écrivez le code Solidity d'un contrat malveillant permettant d'exploiter la faille décrite en (A).
C) Quelle ligne de code Solidity ajouteriez-vous ou modifieriez-vous dans le code précédent pour empêcher cette attaque ? Notez qu'une seule transaction ne doit pas pouvoir frapper plus de 20 NFT.
Question 5. (15 points) Problèmes Bitcoin.
A) L'avantage du protocole Lightning Network est de permettre des paiements sans avoir à publier de transaction sur le réseau Bitcoin. Les paiements via Lightning Network remplaceront-ils un jour entièrement toutes les transactions Bitcoin, rendant la blockchain inutile ?
B) Rappelons qu'une transaction Bitcoin comporte un ensemble d'adresses d'entrée et un ensemble d'adresses de sortie. Généralement, chaque adresse d'entrée autorise (signature comprise) le paiement complet de la transaction. Ce type de signature s'appelle SIGHASH_ALL.
Supposons à la place qu'on signe uniquement la partie entrée Txin (sans inclure la signature), en utilisant la clé de chaque adresse d'entrée, sans signer quoi que ce soit d'autre — en particulier, la partie sortie Txout n'est pas signée. (Ce type de signature s'appelle SIGHASH_NONE.)
Une fois la transaction soumise au réseau Bitcoin, les mineurs peuvent-ils voler des fonds depuis les adresses d'entrée dans une transaction utilisant SIGHASH_NONE ? Si oui, expliquez comment. Sinon, expliquez pourquoi.
C) Si quelqu'un découvrait une méthode permettant de falsifier une signature ECDSA pour un message arbitraire à partir de la seule connaissance de la clé publique, quel impact cela aurait-il sur Bitcoin ? Supposez qu'une falsification prenne 30 minutes et ne puisse pas être accélérée.
Question 6. (11 points) : Tornado Cash
Dans le cours 14, nous avons étudié le mixeur Tornado Cash. Rappelons que le contrat Tornado Cash doit stocker une grande liste de nullifiers, et ajoute un nouvel élément à chaque retrait. Lors d'un retrait, le contrat doit vérifier que le nullifier du jeton retiré n'est pas déjà dans la liste des nullifiers dépensés. S'il n'y est pas, le contrat ajoute ce nullifier à l'ensemble. Tornado Cash implémente cela via une mapping :
mapping(bytes32 => bool) public nullifierHashes;
Durant le retrait, le contrat doit valider la preuve zk-SNARK fournie. Si elle est valide, il doit faire :
bytes32 _nullifierHash; // nullifier of note being withdrawn require(!nullifierHashes[_nullifierHash], "The note has been spent"); nullifierHashes[_nullifierHash] = true;
A) Supposons que k retraits aient eu lieu avec succès. Considérons un mineur validant une transaction Ethereum. En fonction de k, combien d'espace de stockage ce mineur doit-il allouer pour la mapping nullifierHashes ? Vous pouvez supposer que, hormis cette mapping, le contrat Tornado n'a besoin d'aucun autre stockage à long terme.
B) Il serait préférable de stocker la liste des nullifiers dépensés Sk hors chaîne, par exemple dans le cloud. Le contrat Tornado stockerait alors seulement un court engagement (commitment) sur l'ensemble courant Sk. Lors d'un appel à withdraw(), l'utilisateur fournirait tous les paramètres habituels, ainsi que :
• Une preuve π que le nullifier nf du jeton retiré n'est pas dans l'ensemble engagé Sk, i.e., nf ∉ Sk,
• Des informations suffisantes pour que le contrat calcule l'engagement mis à jour de Sk+1 := Sk ∪ {nf}
Le contrat vérifierait la preuve π que nf ∉ Sk, calculerait l'engagement de Sk+1, puis remplacerait l'engagement courant de Sk par celui de Sk+1.
Plusieurs structures de données offrent ces fonctionnalités, par exemple un engagement de Sk sous forme d'un hachage de 32 octets, et une preuve π composée de 2⌈log₂k⌉ hachages de 32 octets. Cette preuve courte permet au contrat Tornado de calculer l'engagement court de Sk+1. Un exemple peut être obtenu en adaptant l'arbre Merkle Patricia vu en cours 7, mais nous laissons cela pour une autre fois.
Bien que cette méthode réduise fortement la taille du stockage contractuel, elle ne vaut la peine d'être implémentée que si elle diminue le coût en gaz de la fonction de retrait. Considérons les coûts en gaz suivants :
• Écriture dans une entrée de stockage valant zéro : 20 000 gaz
• Écriture dans une entrée de stockage non nulle : 5 000 gaz
• calldata (tableau d'octets des paramètres de fonction) : 16 gaz par octet
Supposons que nous ne comptabilisions que les coûts en gaz listés ci-dessus. À partir de quelle valeur de k ce changement devient-il rentable en termes de gaz économisé ? Rappelons que la preuve π fait 32 × 2⌈log₂k⌉ octets, et doit être incluse dans le calldata de l'appel à withdraw().
C) Rappelons que Tornado Cash fournit un outil de conformité permettant à un utilisateur de dé-anonymiser son jeton : cet outil génère un fichier liant le dépôt d'un utilisateur à un retrait spécifique. Ce document pourrait devoir être soumis à une bourse centralisée (comme Coinbase) avant qu'elle n'accepte ces fonds.
Supposons que n personnes déposent chacune un jeton dans un pool Tornado, donnant un niveau d'anonymat de n (par exemple n = 1000). Ensuite, ces n personnes retirent leurs jetons vers n nouvelles adresses Ethereum (une par adresse). Un observateur ne peut pas savoir quelle nouvelle adresse correspond à quelle personne parmi les n, donc la taille de l'ensemble d'anonymat est n.
Mais supposons que n−1 personnes utilisent l'outil de conformité et envoient le document résultant à Coinbase. Que signifie cela pour la vie privée de la dernière personne qui souhaite garder son adresse privée ?
Lien du cours : https://cs251.stanford.edu/
Bienvenue dans la communauté officielle TechFlow
Groupe Telegram :https://t.me/TechFlowDaily
Compte Twitter officiel :https://x.com/TechFlowPost
Compte Twitter anglais :https://x.com/BlockFlow_News














