如何确定以太坊地址是不是为合约?
Posted
技术标签:
【中文标题】如何确定以太坊地址是不是为合约?【英文标题】:How to find out if an Ethereum address is a contract?如何确定以太坊地址是否为合约? 【发布时间】:2016-10-05 07:00:22 【问题描述】:Solidity 中的地址可以是账户或合约(或其他事物,例如交易)。当我有一个变量 x ,持有一个地址时,我如何测试它是否是一个合约?
(是的,我已经阅读了文档中的chapter on types)
【问题讨论】:
【参考方案1】:使用 EXTCODESIZE 的 isContract
函数获得最高票数的答案被发现是可破解的。
如果从合约的构造函数调用该函数将返回 false(因为合约尚未部署)。
应该非常小心地使用代码,以避免安全黑客攻击,例如:
https://www.reddit.com/r/ethereum/comments/916xni/how_to_pwn_fomo3d_a_beginners_guide (archive)
致repeat:
不要使用 EXTCODESIZE 检查来阻止智能合约调用函数。这不是万无一失的,它可以被构造函数调用破坏,因为在构造函数运行时,该地址的 EXTCODESIZE 返回 0。
请参阅 sample code 以了解欺骗 EXTCODESIZE 返回 0 的合约。
检查调用者是否为合约
如果您想确保 EOA 正在调用您的合约,一个简单的方法是 require(msg.sender == tx.origin)
。但是,阻止合同是 anti-pattern 与 security 和 interoperability 考虑因素。
require(msg.sender == tx.origin)
在实现帐户抽象时需要重新访问。
检查被调用者是否为合约
正如@Luke 在评论中指出的那样,没有没有通用的链上方法来了解被调用者。如果您想“调用”一个地址,没有通用的方法可以确定该地址是合约、EOA 还是可以部署新合约的地址,或者它是否是 CREATE2 地址。
一种适用于某些被调用者的非通用方式:您可以在链上进行映射,以存储已知 EOA 或合约的地址。 (请记住,对于没有任何链上历史记录的地址,您无法知道它是 EOA 还是可以部署合约的地址。)
【讨论】:
应该指出require(msg.sender == tx.origin)
只检测函数的调用者是否为EOA,不能用于检测任何其他第三方合约是否为EOA(如作为你想从你自己的函数中调用的合约)。
@LukeHutchison 赞成,好点子!增加了调用者和被调用者的情况;如果我遗漏了一些事情或您有其他建议,很高兴收到您的来信。
为了完整起见,您可以添加 extcodesize 现在已抽象为 <address>.code.size
的solidity(无需组装)。人们也需要识别这种形式。 (我可能语法错误,我现在不在电脑附近。)【参考方案2】:
如果您手头有信息,您可以做什么。 如果交易发件人地址为空或未被占用,那么您可以判断该地址是合约账户还是 EOA(外部拥有的账户)。 即在网络上发送创建合约交易时,交易中的接收地址为空/未使用。
来自github的参考: https://github.com/ethereum/go-ethereum/wiki/Contracts-and-Transactions
希望这会有所帮助。
【讨论】:
这个链接显然已经过时了。现在是 2021 年,这个答案来自 2016 年。【参考方案3】:简答:
require(tx.origin == msg.sender);
tx.origin 是对发起这个串行函数调用的原始地址的引用,而 msg.sender 是直接调用目标函数的地址。这意味着,tx.origin 必须是人类,msg.sender 可以是合约或人类。因此,如果有人通过合约给你打电话,那么 msg.sender 就是一个不同于 tx.origin 的合约地址。
我知道大多数合同可能会使用@Manuel Aráoz 的代码,该代码在大多数情况下都有效。但是如果你在合约的构造函数中调用一个函数,extcodesize 将返回 0,这会导致 isContract 检查失败。
注意:如果您不清楚它代表什么,请勿在其他情况下使用 tx.origin,因为 .
【讨论】:
【参考方案4】:编辑:自从首次编写此答案以来,Solidity 发生了变化,@manuel-aráoz 有正确的答案。
没有办法检查地址是否是合约。以太坊的目标之一是让人类和智能合约都得到平等对待。这导致了智能合约与人类和其他合约无缝交互的未来。未来可能会发生变化,但目前任意地址是不明确的。
【讨论】:
发送到合约所消耗的gas与发送到一个地址所消耗的gas完全不同。如果有一个目标以同样的方式处理这两个事情,就不会有气体的区别。【参考方案5】:是的,你可以,通过使用一些 EVM 汇编代码来获取地址的代码大小:
function isContract(address addr) returns (bool)
uint size;
assembly size := extcodesize(addr)
return size > 0;
【讨论】:
这里有一些info关于这个函数是如何工作的 这段代码很危险,不再建议使用,因为它是 hackable,因为 EXTCODESIZE 在合约的构造函数中返回 0。【参考方案6】:这不是您可以使用 Solidity 从合约中查询的内容,但如果您只是想知道某个地址是否包含合约代码,您可以使用您的 geth 控制台或类似的控制台进行检查,例如:
> eth.getCode("0xbfb2e296d9cf3e593e79981235aed29ab9984c0f")
使用十六进制字符串(此处为0xbfb2e296d9cf3e593e79981235aed29ab9984c0f
)作为您要查询的地址。这将返回存储在该地址的字节码。
您还可以使用区块链扫描仪在该地址查找合约的源代码,例如the ecsol library,如etherscan.io 所示。
【讨论】:
以上是关于如何确定以太坊地址是不是为合约?的主要内容,如果未能解决你的问题,请参考以下文章