以太坊中如何判断一个地址为合约账户地址

对以太坊稍微了解一些朋友,应该知道,以太坊中有两类账户,一类是普通的由私钥控制的外部账户,一类是关联有合约代码的合约账户。 那么如何区分呢?

## 0x01 以太坊中的两类账户 对以太坊稍微了解一些朋友,应该知道,以太坊中有两类账户,一类是普通的由私钥控制的外部账户,一类是关联有合约代码的合约账户。 ![以太坊地址](https://img.learnblockchain.cn/2020/04/28_/25586563.png) 但是如果我给你下面两个地址,你知道哪个是外部账户地址,哪个是合约账户地址么? 0x8415A51d68e80aebb916A6f5Dafb8af18bFE2F9d 0x11b314EF962425231150739EF2627FB13214e5Bf ## 0x02 常用方法 区分的关键是看这个地址有没有与之相关联的代码。 EVM 提供了一个操作码 EXTCODESIZE,用来获取地址相关联的代码大小(长度),如果是外部账号地址,则没有代码返回。因此我们可以使用以下方法判断合约地址及外部账号地址: ``` function isContract(address addr) internal view returns (bool) { uint256 size; assembly { size := extcodesize(addr) } return size > 0; } ``` 如果是在合约外部判断,则可以使用 web3.eth.getCode(),或者是对应的 JSON-RPC 方法 eth_getcode。 getCode() 用来获取参数地址所对应合约的代码,如果参数是一个外部账号地址,则返回 "0x";如果参数是合约,则返回对应的字节码,如下所示: ``` web3.eth.getCode("0x8415A51d68e80aebb916A6f5Dafb8af18bFE2F9d") "0x" ``` ``` web3.eth.getCode("0x11b314EF962425231150739EF2627FB13214e5Bf") "0x" ``` 什么?两个地址竟然得到同样的结果!!!如果其中一个地址是合约地址的话,不是应该得到合约关联的字节码么?难道两个地址都是外部账户地址? ## 0x03 未上链的合约地址 哈哈,告诉你吧,上面两个地址中的其中一个地址,是我在链下生成的,代码还没部署到区块链网络中呢,你当然得不到关联代码啦。 合约账户地址是怎么生成的呢?其实有两种生成方式,我是用的比较简单的方式生成的,生成的公式如下: ``` 合约地址 = keccak256(rlp([sender, nonce]))
 ``` 对应的 java 代码为: ``` public static String generateAddress(String address, long nonce) { byte[] addressAsBytes = Numeric.hexStringToByteArray(address); byte[] calculatedAddressAsBytes = Hash.sha3(RlpEncoder.encode( new RlpList( RlpString.create(addressAsBytes), RlpString.create((nonce))))); calculatedAddressAsBytes = Arrays.copyOfRange(calculatedAddressAsBytes, 12, calculatedAddressAsBytes.length); String calculatedAddressAsHex = Numeric.toHexString(calculatedAddressAsBytes); return calculatedAddressAsHex; } ``` 调用代码为: ``` AddressUtils.generateAddress("0x8415A51d68e80aebb916A6f5Dafb8af18bFE2F9d", 18)) ``` 这说明什么? 当你通过是否关联有代码来判断一个地址是否是合约账户地址时,心里要明镜似的,一个地址关联有代码,那它肯定是合约地址,如果一个地址没有代码关联,并不能肯定这个地址是外部账户地址还是合约地址。 ## 0x04 怎么办? 需要判断地址类型的一个常见需求就是只允许外部账户调用我们的合约,不允许合约账户调用我们的合约,满足这个需求,在合约里加上这个判断就可以了: ``` require(tx.origin == msg.sender) ```

0x01 以太坊中的两类账户

对以太坊稍微了解一些朋友,应该知道,以太坊中有两类账户,一类是普通的由私钥控制的外部账户,一类是关联有合约代码的合约账户。 以太坊中如何判断一个地址为合约账户地址插图

但是如果我给你下面两个地址,你知道哪个是外部账户地址,哪个是合约账户地址么? 0x8415A51d68e80aebb916A6f5Dafb8af18bFE2F9d 0x11b314EF962425231150739EF2627FB13214e5Bf

0x02 常用方法

区分的关键是看这个地址有没有与之相关联的代码。 EVM 提供了一个操作码 EXTCODESIZE,用来获取地址相关联的代码大小(长度),如果是外部账号地址,则没有代码返回。因此我们可以使用以下方法判断合约地址及外部账号地址:

function isContract(address addr) internal view returns (bool) {
    uint256 size;
    assembly { size := extcodesize(addr) }
    return size > 0;
  }

如果是在合约外部判断,则可以使用 web3.eth.getCode(),或者是对应的 JSON-RPC 方法 eth_getcode。 getCode() 用来获取参数地址所对应合约的代码,如果参数是一个外部账号地址,则返回 "0x";如果参数是合约,则返回对应的字节码,如下所示:

web3.eth.getCode("0x8415A51d68e80aebb916A6f5Dafb8af18bFE2F9d")
"0x"
web3.eth.getCode("0x11b314EF962425231150739EF2627FB13214e5Bf")
"0x"

什么?两个地址竟然得到同样的结果!!!如果其中一个地址是合约地址的话,不是应该得到合约关联的字节码么?难道两个地址都是外部账户地址?

0x03 未上链的合约地址

哈哈,告诉你吧,上面两个地址中的其中一个地址,是我在链下生成的,代码还没部署到区块链网络中呢,你当然得不到关联代码啦。 合约账户地址是怎么生成的呢?其实有两种生成方式,我是用的比较简单的方式生成的,生成的公式如下:

合约地址 = keccak256(rlp([sender, nonce]))


对应的 java 代码为:

 public static String generateAddress(String address, long nonce) {
    byte[] addressAsBytes = Numeric.hexStringToByteArray(address);

    byte[] calculatedAddressAsBytes =
        Hash.sha3(RlpEncoder.encode(
            new RlpList(
                RlpString.create(addressAsBytes),
                RlpString.create((nonce)))));

    calculatedAddressAsBytes = Arrays.copyOfRange(calculatedAddressAsBytes,
        12, calculatedAddressAsBytes.length);
    String calculatedAddressAsHex = Numeric.toHexString(calculatedAddressAsBytes);
    return calculatedAddressAsHex;
  }

调用代码为:

AddressUtils.generateAddress("0x8415A51d68e80aebb916A6f5Dafb8af18bFE2F9d", 18))

这说明什么? 当你通过是否关联有代码来判断一个地址是否是合约账户地址时,心里要明镜似的,一个地址关联有代码,那它肯定是合约地址,如果一个地址没有代码关联,并不能肯定这个地址是外部账户地址还是合约地址。

0x04 怎么办?

需要判断地址类型的一个常见需求就是只允许外部账户调用我们的合约,不允许合约账户调用我们的合约,满足这个需求,在合约里加上这个判断就可以了:

require(tx.origin == msg.sender)

区块链技术网。

  • 发表于 2020-04-28 21:59
  • 阅读 ( 5216 )
  • 学分 ( 43 )
  • 分类:以太坊

评论