Ist es möglich, innerhalb eines in Solidity geschriebenen Vertrags zu überprüfen, ob ein Vertrag an eine bestimmte Adresse vergeben wurde oder ob diese Adresse keinen Code enthält?
Das funktioniert:
function isContract(address _addr) private returns (bool isContract){
uint32 size;
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
Die Assemblersprache, auf die alle Ethereum-Verträge herunterkompiliert werden, enthält einen Opcode für genau diese Operation: EXTCODESIZE
. Dieser Opcode gibt die Größe des Codes einer Adresse zurück. Wenn die Größe größer als Null ist, handelt es sich bei der Adresse um einen Vertrag. Sie müssen jedoch innerhalb des Vertrags Assemblercode schreiben, um auf diesen Opcode zugreifen zu können, da der Solidity-Compiler ihn derzeit nicht direkt unterstützt. Der obige Code erstellt eine private Methode, die Sie innerhalb Ihres Vertrags aufrufen können, um zu prüfen, ob eine andere Adresse Code enthält. Wenn Sie keine private Methode wünschen, entfernen Sie das Schlüsselwort private aus dem Funktionsheader.
Bearbeiten: EXTCODESIZE
gibt 0 zurück, wenn es vom Konstruktor eines Vertrags aufgerufen wird. Wenn Sie dies also in einer sicherheitssensiblen Umgebung verwenden, müssen Sie überlegen, ob dies ein Problem darstellt.
Volle Anerkennung an @AnAllergyToAnalogy für den Warnhinweis.
Ich habe ein Beispiel gemacht, um zu demonstrieren, dass a constructor
diese Methode austrickst. Posting für andere, die auf diesen Thread stoßen könnten.
In der Praxis isContract
kann ein Angreifer, der von einem Konstruktor aus aufruft, nicht zuverlässig erkannt werden.
pragma solidity 0.4.25;
contract Victim {
function isContract() public view returns(bool){
uint32 size;
address a = msg.sender;
assembly {
size := extcodesize(a)
}
return (size > 0);
}
}
contract Attacker {
bool public iTrickedIt;
Victim v;
constructor(address _v) public {
v = Victim(_v);
// addrss(this) doesn't have code, yet
iTrickedIt = !v.isContract();
}
}
iTrickedIt
Angreifer eincheckenIch hoffe es hilft.
AKTUALISIEREN
Address.sol
in OpenZeppelin/contracts/utility hat eine Funktion isContract
, die nach dem beschriebenen Prinzip funktioniert. Dieselbe Warnung gilt. Bewusst verwenden.
Da dies sicherheitsrelevant ist, ist es hilfreich, https://stackoverflow.com/questions/37644395/how-to-find-out-if-an-ethereum-address-is-a-contract zu betonen :
Die am besten bewertete Antwort mit der isContract
Funktion, die EXTCODESIZE verwendet, wurde als hackbar entdeckt.
Die Funktion gibt false zurück, wenn sie vom Konstruktor eines Vertrags aufgerufen wird (weil der Vertrag noch nicht bereitgestellt wurde).
Der Code sollte, wenn überhaupt, sehr vorsichtig verwendet werden, um Sicherheitshacks zu vermeiden, wie zum Beispiel:
https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide ( Archiv )
Zur Wiederholung :
Verwenden Sie die EXTCODESIZE-Prüfung nicht, um zu verhindern, dass Smart Contracts eine Funktion aufrufen. Dies ist nicht narrensicher, es kann durch einen Konstruktoraufruf untergraben werden, da EXTCODESIZE für diese Adresse 0 zurückgibt, während der Konstruktor ausgeführt wird.
Siehe Beispielcode für einen Vertrag, der EXTCODESIZE dazu bringt, 0 zurückzugeben.
Wenn Sie sicherstellen möchten, dass ein EOA Ihren Vertrag anruft, ist ein einfacher Weg require(msg.sender == tx.origin)
. Das Verhindern eines Vertrags ist jedoch ein Anti-Pattern mit Sicherheits- und Interoperabilitätsüberlegungen .
Dies muss erneut überprüft werden, wenn die Kontoabstraktion implementiert wird.
Daran arbeite ich auch. Es gibt einen Opcode namens extcodehash
. Es sagt
Der EXTCODEHASH des Kontos ohne Code ist c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470, was der keccack256-Hash der leeren Daten ist
Ich denke also, dass es eine Möglichkeit gibt, dies zu überprüfen isContract
, indem Sie dies extcodehash
in Kombination mit verwendenc5d2460186f72...
function isContract(address addr) internal view returns (bool) {
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
bytes32 codehash;
assembly {
codehash := extcodehash(addr)
}
return (codehash != 0x0 && codehash != accountHash);
}
Das heißt, wenn der Code-Hash weder 0 noch c5d2460186f72...
ist, können wir daraus schließen, dass dies die Vertragsadresse ist?
Ein offensichtlicher Punkt, der in den zuvor geposteten Antworten nicht hervorgehoben wurde, ist das JA, das Erfordernis
assembly {
size := extcodesize(_addr)
}
garantiert, dass nur ein Vertrag die Prüfung bestehen kann, wenn size > 0
.
Die umgekehrte Überprüfung, um festzustellen, ob es sich bei einem Absender NICHT um einen Vertrag (sondern um einen EOA) handelt, ist jedoch viel komplizierter und erfordert zum Nachweis ein Signaturverifizierungsschema. Es gibt 2 Ansätze, über die Sie hier und hier mehr nachlesen können .
pragma solidity >=0.8.0;
function isContract(address _addr) private returns (bool isContract) {
isContract = _addr.code.length > 0;
}
Beachten Sie, dass alle in den anderen Kommentaren erwähnten Sicherheitsvorkehrungen weiterhin gelten.
Der beste Weg, um zu überprüfen und sicherzustellen, dass eine Adresse kein Vertrag ist, ist der Vergleich von tx.origin mit msg.sender. Dafür könntest du einen Modifikator machen.
modifier onlyEoa() {
require(tx.origin == msg.sender, "Not EOA");
_;
}
address.code.length > 0
ist nicht immer eine gute Lösung, denn wenn ein Vertrag Ihren Vertrag von seinem Konstruktor aufruft, dann address.code.length
wird 0 sein, weil der angreifende Vertrag noch nicht konstruiert wurde, was Sie zu der Annahme verleitet, dass dies kein Vertrag ist.
Weitere Informationen in dieser Antwort: Gibt es in Solidity eine einfache Funktion, um zu überprüfen, ob eine Adresse eine Vertragsadresse oder eine Brieftaschenadresse ist?
AnAllergyToAnalogy
EXTCODESIZE
wenn der Funktionsaufruf innerhalb dieses Vertragskonstruktors erfolgt ist. Dies bedeutet, dass Sie nicht blind verwenden könnenEXCODESIZE
, um zu verhindern, dass andere Verträge mit Ihren interagieren, es muss mit Vorsicht verwendet werden.Thorkil Værge
selfdestruct
. Als Sicherheitsmaßnahme wollte ich nicht, dass der ERC20 Gelder auf gelöschte Finanzkontrakte überweist, also habe ich diese Prüfung eingeführt. Für diesen Anwendungsfall funktioniert diese Prüfung, aber für andere Anwendungsfälle sollte die B9lab-Warnung von Rob Hitchens beachtet werden.schämen
function isContract(address addr) private returns (bool isContract) { return addr.code.length > 0; }
haxpor