9. 合约和用户地址介绍

在区块链上,通常会有一个地址,作为用户或者合约在链上的唯一标识符。为了保证地址的唯一性,地址一般由哈希计算得到。一般来说,用户的地址根据公钥计算得到,而智能合约的地址由区块链自动生成。

9.1. 计算方式

在ChainMaker上,用户地址也是根据用户的公钥计算得到的,但合约地址,可以由链自动生成,也可以根据用户的合约名计算得到。且对于合约来说,ChainMaker支持合约名与合约地址两种方式调用。对于任意类型的合约,包括evm、wasmer、wxvm等等,ChainMaker部署合约时,都会将用户提供的合约名计算为合约地址,并在合约部署成功后将合地址放入contract对象内返回,用户反序列化contract对象就得到该合约的地址。

对于ChainMaker来说,当前支持以下三种地址类型:

  • CHAINMAKER

    此地址格式为20字节数组,但一般会将其转换为可展示的16进制字符串,转换后长度为40字节,例如:ce244336a16f64c5b6b27feae28a5ebd270be8ee。CHAINMAKER对于公钥模式的用户来说,由于计算SKI稍显复杂,现已不推荐使用。

    • 用户地址——根据用户的SKI(SubjectKeyId)计算keccak256哈希,然后截取哈希值的后20字节,对外展示时,会再转换为十六进制字符串形式;

    • 合约地址——根据合约名计算keccak256哈希,然后截取哈希值的后20字节,对外展示时,会再转换为十六进制字符串形式。

  • ZXL

    过期,不建议使用

  • ETHEREUM

    此地址格式也是20字节数组,转换为可展示的16进制字符串后,一般会加一个”0x“前缀,长度也变成了42字节,例如:0x7cf146966856b4899b4f25f169d81176d0942050,但”0x“前缀非必须,可加可不加。

    • 用户地址——对用户公钥序列化后,计算keccak256哈希,然后截取哈希值的后20字节,对外展示时,会再转换为十六进制字符串形式;

    • 合约地址——根据合约名计算keccak256哈希,然后截取哈希值的后20字节,对外展示时,会再转换为十六进制字符串形式。

    ETHEREUM与CHAINMAKER非常相似,尤其对于合约地址,两者的参数和哈希算法都是相同的。但对于用户地址稍有不同,主要是地址的计算参数,ETHEREUM是序列化后的公钥,而CHAINMAKER为SKI,哈希算法仍然是相同的。

    ETHEREUM地址类型的优点是兼容以太坊地址类型,而且,也是ChainMaker的默认地址类型。

9.2. 版本历史

在长安链2.3.0版本之前,除了solidity合约外,其他类型的合约不支持地址,只有合约名,且solidity的合约名本身就是地址,非地址类型的字符串无法作为合约名使用。此外,合约地址只支持CHAINMAKERZXL两种类型。

自2.3.0版本开始,长安链内所有类型的合约都已支持地址功能,且地址和合约名分开,成为合约的不同属性,用户可以通过合约名调用合约,也可以通过地址调用。此外,还添加了新的地址类型ETHEREUM,且长安链所有类型合约,都支持以上三种地址类型。

9.3. 配置

在ChainMaker内,地址类型可以通过配置文件chainconfig/bc.yml内的addr_type字段指定,配置为0,是CHAINMAKER类型,1为ZXL类型,2为EHEREUM类型,默认配置类型为2,如下所示。

chainconfig/bc.yml配置地址类型的片段

......
# 虚拟机配置
vm:
  addr_type: 2 #0:chainmaker, 1:zxl, 2:ethereum
  # 虚拟机支持列表
  support_list:
......

9.4. 计算接口

对于地址的计算,ChainMaker为开发者提供了统一的计算接口,这些接口统一放在了utils模块下的address.go原文件内(gitlab链接:https://git.chainmaker.org.cn/chainmaker/utils/-/blob/master/address.go),开发者可通过导入该模块,并调用对应的接口计算地址,以保证接口计算的一致性。以下表格为地址计算接口展示。

utils/address.go下提供的地址计算接口

接口 功能
func GenerateAddrStr(data []byte, addrType config.AddrType) (string, err) 根据数据计算十六进制字符串类型的地址
func GenerateAddrInt(data []byte, addrType config.AddrType) (big.Int, err) 根据数据计算big.Int类型的地址
func PkToAddrStr(pk crypto.PublicKey, addrType config.AddrType, hashType crypto.HashType)(string, err) 根据公钥计算16进制字符串类型的地址
func PkToAddrInt(pk crypto.PublicKey, addrType config.AddrType, hashType crypto.HashType)(big.Int, err) 根据公钥计算big.Int类型的地址
func CertToAddrStr(cert *x509.Certificate, addrType config.AddrType) (string, err) 根据证书计算16进制字符串类型的地址
func CertToAddrInt(cert *x509.Certificate, addrType config.AddrType)(big.Int, err) 根据证书计算big.Int类型的地址
func GetStrAddrFromMember(member protocol.Member, addrType config.AddrType) (string, err) 根据member类型计算16进制字符串类型的地址
func GetIntAddrFromMember(member protocol.Member, addrType config.AddrType) (big.Int, err) 根据member类型计算big.Int类型的地址