Base32 地址
在 Conflux 网络中,每个 账户 都与一对公钥和私钥相关联,并通过一个地址来识别。 本页面介绍地址在core space中的表示和计算方式。
关于地址的基本概念,请参阅 General-address。
Hex地址 和 Base32 地址
在 Conflux-rust v1.1.1
发布之前,Conflux 地址完全以十六进制编码字符串形式呈现,例如 0x1292d4955b47f5153b88c12c7a94048f09839
此格式与Etherum和其他兼容的EVM区块链使用的地址非常相似。 然而,Conflux 采用了独特的方法来计算 EOA 地址,这意味着 **从相同的私钥生成的地址字符串在 Conflux 和 Ethereum 之间通常会有所不同。**这种外观上的相似性,加上计算上的差异,很容易让用户将 Conflux 地址与 Ethereum 地址混淆,可能导致资产的损失。
为了解决这个问题,Conflux在 CIP-37 中引入了一个新的基于 base32 编码地址格式。 新格式 直接从原始的十六进制编码地址派生,包括一个独特的前缀(如"cfx")、一个可选的地址类型和一个校验和。 因此,上文提到的Hex编码地址可以转换成更容易识别的base32地址。例如 cfx:aakkfzezns4h8ymx1cgmcnd4x3aev6e2hexz250ym5
, 可选的,也可以表示为详细格式地址,详细格式包含了非必须的地址类型信息, 例如 CFX:TYPE .USER:AAKKFZEZNS4H8YMX1CGMCN4X3AEV6E2HEXZ250YM5
. 这种新格式最大限度地减少了Conflux 和 Etherum地址之间混淆的风险,提供了更安全和更方便的用户体验。
Base32 地址在 Conflux Core 生态系统中被广泛使用,智能合约 .sol
源代码除外。 在.sol
文件中需要硬编码EIP-55 校验和地址的情况下,开发人员应该选择使用Conflux的十六进制编码地址,而不是Base32格式。
地址计算
本节内容仅供信息参考。 用户或开发者通常不需要自己计算十六进制地址。 建议基于 SDK 或 RPC 的返回值来获取 EOA / 合约地址,使用 SDK 或 在线地址转换器 来转换十六进制和 base32 地址格式。
十六进制地址计算
Base32地址 直接由原始的十六进制编码地址派生而来。 因此,我们需要理解十六进制地址的计算方法。
Conflux 十六进制地址是一个20字节的十六进制值,以“0x”开头的包括42个字符的字符串表示。 十六进制编码地址以一个1(3)字符“类型标识”开头,表示地址类型。 目前有三种类型的标识:
(0x)1
: 代表一个EOA 帐户的地址(0x)8
: 代表一个合约的地址(0x)0
: 表示一个在链上实现硬编码逻辑 内置合约, 或一个空地址 (0x0000000000000000000000000000000000000000000000000000
)。
EOA 十六进制地址计算
EOA 十六进制地址的计算在 Conflux 协议规范 3.1:账户
中有具体说明。 将账户公钥进行Keccak运算得到摘要,账户地址由4位类型标识和该摘要的最右侧156位串联而成。
合约地址计算
参见下面的示例。
- Abi
- Bytecode
[{
"inputs": [
{
"internalType": "uint256",
"name": "inputValue",
"type": "uint256"
}
],
"name": "increment",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "pure",
"type": "function"
}]
0x608060405234801561001057600080fd5b506101a1806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80637cf5dab014610030575b600080fd5b61004a600480360381019061004591906100b1565b610060565b60405161005791906100ed565b60405180910390f35b600060018261006f9190610137565b9050919050565b600080fd5b6000819050919050565b61008e8161007b565b811461009957600080fd5b50565b6000813590506100ab81610085565b92915050565b6000602082840312156100c7576100c6610076565b5b60006100d58482850161009c565b91505092915050565b6100e78161007b565b82525050565b600060208201905061010260008301846100de565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101428261007b565b915061014d8361007b565b925082820190508082111561016557610164610108565b5b9291505056fea26469706673582212208eb410cb79fbf08652e19d496e31d076d04be7ed242c64a44aec4c5af0f2533b64736f6c63430008130033
可选的,合约可以通过 create2
操作码进行部署。
合约地址的计算方式与以太坊有很大不同。
如果使用 create2
那么可以按照下面的代码进行计算合约地址:
Create2Factory
合约已经通过 CIP-31 部署, 您可以使用 0x8a3a92281df6497105513b18543fd3b60c778e40
合同或部署您自己的 Create2Factory 合约。
- Python
- Javascript
# using web3.py is also viable
# from web3 import Web3
from conflux_web3 import Web3
# ensure salt is a bytes32 to avoid unmatched result caused by encoding approach
def compute_address_using_salt(
salt: bytes, bytecode_hash: bytes, create2_factory_address: str
):
core_part = Web3.solidity_keccak(
["bytes1", "address", "bytes32", "bytes32"],
["0xff", create2_factory_address, salt, bytecode_hash],
)
return "0x8" + core_part.hex()[-39:]
if __name__ == "__main__":
salt = (1111).to_bytes(32)
bytecode_hash = Web3.solidity_keccak(["bytes"], [bytecode])
create2_factory_address = "0x8A3A92281Df6497105513B18543fd3B60c778E40"
address = compute_address_using_salt(
salt=salt,
bytecode_hash=bytecode_hash,
create2_factory_address=create2_factory_address,
)
print(address) # 0x80ac53cc16c0b58dc5bde5af47f5ef9e84693fe4
import { solidityPackedKeccak256, toBeHex } from "ethers"; // ethers v6
function computeAddressUsingSalt(
salt,
bytecode,
create2FactoryAddress = "0x8A3A92281Df6497105513B18543fd3B60c778E40"
) {
const hash = solidityPackedKeccak256(
["bytes1", "address", "bytes32", "bytes32"],
[
"0xff",
create2FactoryAddress,
toBeHex(salt, 32),
solidityPackedKeccak256(["bytes"], [bytecode]),
]
);
return `0x8${hash.slice(-39)}`;
}
computeAddressUsingSalt(1111, bytecode) // 0x80ac53cc16c0b58dc5bde5af47f5ef9e84693fe4
如果不使用 create2
部署合约:
- Python
- Javascript
# using web3.py is also viable
# from web3 import Web3
from conflux_web3 import Web3
def compute_address_using_nonce(
nonce: int, bytecode_hash: bytes, hex_deployer_address: str
):
core_part = Web3.solidity_keccak(
["bytes1", "address", "bytes32", "bytes32"],
["0x00", hex_deployer_address, nonce.to_bytes(32, "little"), bytecode_hash],
)
return "0x8" + core_part.hex()[-39:]
if __name__ == "__main__":
nonce = 1
bytecode_hash = Web3.solidity_keccak(["bytes"], [bytecode])
# The address of the transaction sender "cfx:aamz08kfa8wsu69jhhcgrwkjkh69p85wj6222847yp" in hex is 0x155B792507a4E873e839c466C92849f9F67b7247.
hex_deployer_address = "0x155B792507a4E873e839c466C92849f9F67b7247"
address = compute_address_using_nonce(
nonce=nonce,
bytecode_hash=bytecode_hash,
hex_deployer_address=hex_deployer_address,
)
print(address) # 0x837f77a1e8da5b860905a07bc1921e43fbfb04ef
import { hexlify, solidityPackedKeccak256, toBeArray, zeroPadBytes } from "ethers";
function computeAddressUsingNonce(nonce, bytecode, hexDeployerAddress) {
const hash = solidityPackedKeccak256(
["bytes1", "address", "bytes32", "bytes32"],
[
"0x00",
hexDeployerAddress,
hexlify(zeroPadBytes(toBeArray(nonce).reverse(), 32)),
solidityPackedKeccak256(["bytes"], [bytecode]),
]
);
return `0x8${hash.slice(-39)}`;
}
computeAddressUsingNonce(1,bytecode,"0x155B792507a4E873e839c466C92849f9F67b7247") // 0x837f77a1e8da5b860905a07bc1921e43fbfb04ef
Base32地址计算
Conflux的 base32 地址指由 CIP-37 定义的具有网络前缀的Conflux Base32校验和地址。 该地址由表示该地址有效的网络的网络前缀、一个冒号(":"
) 和一个 Base32 编码的载荷组成,并包含一个校验和,例如cfx:aarc9abycue0hzgyr53m6cxedgccrmybjgh4xg
。 可选的,地址可以在网络前缀和载荷之间包含一组键值对,格式为key.value
,以冒号分隔,例如cfx:type.user:aarc9abycue0hhzgyrr53m6cxedgccrmmyybjgh4xg
。
网络前缀(Network Prefix)
网络前缀
是以下值之一: "cfx"
(代表主网, 对应于网络 ID 1029), "cfxtest"
(测试网, 相对应网络ID 1), "net[n]"
n !=1,1029
(代表私有Conflux网络)
有效的网络前缀示例: "cfx"
, "cfxtest"
, "net17"
无效的网络前缀示例: "bch"
, "conflux"
, "net1"
, "net1029"
地址类型(Address Type)
地址类型是一个可选字段,为地址类型提供可读的信息。 对于空地址 (0x000000000000000000000000000000000000000000
), 地址类型必须是 type. null
。 其他为:
0x0
:type.builtin
0x1
:type.user
0x8
:type.contract
载荷 (Payload)
- 拼接
版本字节(version-byte)
:将版本字节
(0x00
) 与十六进制地址拼接起来,得到一个21字节数组。 - Base32 编码:将以上结果从左到右编码,将每5位序列映射到对应的 ASCII 字符(见下文字母表)。 在结尾补零位(应为2个零位),以完成未完成的任何块。
0x00 => a 0x08 => j 0x10 => u 0x18 => 2
0x01 => b 0x09 => k 0x11 => v 0x19 => 3
0x02 => c 0x0a => m 0x12 => w 0x1a => 4
0x03 => d 0x0b => n 0x13 => x 0x1b => 5
0x04 => e 0x0c => p 0x14 => y 0x1c => 6
0x05 => f 0x0d => r 0x15 => z 0x1d => 7
0x06 => g 0x0e => s 0x16 => 0 0x1e => 8
0x07 => h 0x0f => t 0x17 => 1 0x1f => 9
校验和(Checksum)
- 准备输入校验和输入:
data
被用作校验和函数的输入。 它包含:网络前缀
每个字符的低 5 位,例如"cfx..."
变成0x03, 0x06, 0x18, ...