如何确定以太坊地址是不是为合约?

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 所示。

【讨论】:

以上是关于如何确定以太坊地址是不是为合约?的主要内容,如果未能解决你的问题,请参考以下文章

以太坊的ABI编码

如何判断一个以太坊地址是合约还是普通账户?

聊聊以太坊智能合约ABI

聊聊以太坊智能合约ABI

以太坊合约地址错误是怎么回事

以太坊