web3.py est le client Python des nœuds Ethereum ; la signature des messages et des transactions est déléguée au paquet eth-account (0.14.0 avec web3 8.0.0), qui s’appuie à son tour sur eth-keys pour l’arithmétique de courbe. La plupart du code de signature importe directement eth_account, comme le fait l’exemple.
Account.sign_message(encode_defunct(text=...)) implémente personal_sign : le préfixe EIP-191 avec la longueur en octets écrite en décimal, keccak-256, un nonce RFC 6979 (comparé à un signataire de référence indépendant) et s dans la moitié inférieure, le tout renvoyé sous forme de 65 octets terminés par v = 27 ou 28. encode_defunct est l’élément que l’on oublie : signer des octets bruts avec unsafe_sign_hash omet le préfixe, et c’est précisément ce qui rend une signature inutilisable ailleurs comme preuve de connexion.
Account.recover_message() a récupéré le signataire à partir de la signature miroir high-S comme à partir de v = 0/1 : cette méthode accepte donc plus de formes qu’ethers. Normalisez s vous-même si les signatures sont stockées ou dédoublonnées. La forme compacte EIP-2098 de 64 octets a été rejetée avec Unexpected recoverable signature length: Expected 65, but got 64 bytes ; étendez-la à 65 octets avant la récupération.
Pour les données typées, utilisez encode_typed_data (EIP-712) plutôt que de hacher le JSON vous-même, et vérifiez ce qu’un portefeuille est invité à signer avec l’inspecteur EIP-712. Les portefeuilles de contrat intelligent exigent un appel ERC-1271 au contrat au lieu de recover_message ; les messages de connexion se déboguent le plus facilement dans le vérificateur SIWE.
L’exemple signe un message de connexion et compare l’adresse récupérée : c’est cette comparaison qui devrait remplacer toute vérification octet par octet des signatures.