Une adresse dans Solidity peut être un compte ou un contrat (ou d'autres éléments, tels qu'une transaction). Quand j'ai une variable x contenant une adresse, comment puis-je vérifier s'il s'agit d'un contrat ou non?
(Oui, j'ai lu le chapitre sur les types dans le doc)
Edit: La solidité a changé depuis l'écriture de cette réponse, @ manuel-aráoz a la bonne réponse.
Il n’existe aucun moyen de vérifier si une adresse est un contrat. L'un des objectifs de Ethereum est que les contrats humains et intelligents soient traités de manière égale. Cela conduit à un avenir où les contrats intelligents interagissent de manière transparente avec les contrats humains et autres. Cela pourrait changer à l'avenir, mais pour le moment, une adresse arbitraire est ambiguë.
Oui, vous pouvez utiliser un code d'assemblage EVM pour obtenir la taille du code de l'adresse:
function isContract(address addr) returns (bool) {
uint size;
Assembly { size := extcodesize(addr) }
return size > 0;
}
Ce n'est pas quelque chose que vous pouvez interroger depuis un contrat en utilisant Solidity, mais si vous voulez simplement savoir si une adresse contient le code du contrat, vous pouvez vérifier à l'aide de votre console geth ou similaire avec par exemple:
> eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f")
avec la chaîne hexadécimale (ici 0xbfb2e296d9cf3e593e79981235aed29ab9984c0f
) comme adresse à interroger. Cela retournera le bytecode stocké à cette adresse.
Vous pouvez également utiliser un scanner blockchain pour trouver le code source du contrat à cette adresse, par exemple la bibliothèque ecsol comme indiqué sur etherscan.io .
La réponse la plus votée avec la fonction isContract
qui utilise EXTCODESIZE a été découverte comme pouvant être piratée.
La fonction retournera false si elle est appelée depuis le constructeur du contrat (car le contrat n'a pas encore été déployé).
Le code doit être utilisé très soigneusement, le cas échéant, pour éviter des attaques de sécurité telles que:
https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide ( archive )
Pour répéter :
N'utilisez pas le contrôle EXTCODESIZE pour empêcher les contrats intelligents d'appeler une fonction. Ce n'est pas infaillible, il peut être renversé par un appel de constructeur, car pendant l'exécution du constructeur, EXTCODESIZE pour cette adresse renvoie 0.
Voir exemple de code pour un contrat qui astuce EXTCODESIZE pour renvoyer 0.
Si vous voulez vous assurer qu'un EOA appelle votre contrat, un moyen simple est require(msg.sender == tx.Origin)
. Cependant, la prévention d'un contrat est un anti-pattern avec des considérations security et interopérabilité .
Cela nécessitera une nouvelle visite lorsque l'abstraction du compte est mise en œuvre.
Ce que vous pouvez faire, si vous avez les informations sous la main. Si l’adresse de l’émetteur des transactions était nulle ou non occupée, vous pouvez indiquer si l’adresse est un compte contractuel ou un compte EOA (compte appartenant à l’extérieur). lors de l'envoi d'une transaction de création de contrat sur le réseau, l'adresse de réception dans la transaction est null/non utilisée.
Référence de github: https://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions
J'espère que cela t'aides.
Réponse courte:
require(tx.Origin == msg.sender);
tx.Origin est une référence de l'adresse d'origine qui lance cet appel de fonction en série, tandis que msg.sender est l'adresse qui appelle directement la fonction cible. Ce qui signifie que tx.Origin doit être un humain, msg.sender peut être un contrat ou un humain. Ainsi, si quelqu'un vous appelle depuis un contrat, msg.sender est une adresse de contrat différente de tx.Origin.
Je sais que la plupart des contrats peuvent utiliser le code de @Manuel Aráoz, qui fonctionne dans la plupart des cas. Mais si vous appelez une fonction dans le constructeur d'un contrat, extcodesize renverra 0, ce qui échouera à la vérification isContract.
REMARQUE: N'UTILISEZ PAS tx.Origin dans d'autres circonstances si vous n'êtes pas certain de ce que cela représente car.