-
Notifications
You must be signed in to change notification settings - Fork 19
6. AntChain Bridge跨链:如何基于Embedded BCDNS实现跨链
本文档旨在介绍如何搭建一个本地环境,其中涵盖了AntChain Bridge Relayer、Embedded BCDNS、PluginServer以及以太坊私有链的启动流程。Embedded BCDNS是AntChain Bridge框架下定义并实现的一种区块链域名服务(BCDNS),它不仅全面实现了BCDNS的核心功能,还为跨链身份认证、凭证签发与查询提供了强大支持。此外,它还支持区块链域名证书的认证,以及跨链路由服务的注册和查询功能。在这个基础上,文档将进一步阐述如何注册跨链身份和域名证书。
上图大体介绍了整体操作的脉络。
- 操作者需要部署中继(Relayer)、Embedded BCDNS服务将会运行在Relayer、插件服务以及一条私有链(Ganache)。
- 使用CLI工具生成BCDNS的密钥、自签根证书。
- 使用CLI工具生成Relayer公私钥,向BCDNS申请中继证书,会配置到Relayer中。
- 操作过程中,涉及很多证书,主要是中继证书,用于验证Relayer本身的权限和身份,然后是域名证书,区块链注册进中继之前,需要向BCDNS申请域名证书,目前可以通过中继申请,其余则是自签名的X.509证书,用于TLS连接。
- 私有链采用Ganache模拟一条以太坊,账户可以在Ganache找到,这里默认操作者熟悉相关操作。
- 整个操作,用到很多工具,比如Ganache、MetaMask、Docker等,这里默认操作者熟悉相关操作。
目前系统使用了MySQL和Redis,这里建议使用docker快速安装依赖。
首先通过脚本安装docker,或者在官网下载。
wget -qO- https://get.docker.com/ | bash
然后下载MySQL镜像并启动容器,注意这里指定了时区为+08:00
,请修改为您的时区。
docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD='YOUR_PWD' mysql:8 --mysql-native-password=ON --default_time_zone +08:00
然后下载Redis镜像并启动容器:
docker run -itd --name redis-test -p 6379:6379 redis --requirepass 'YOUR_PWD' --maxmemory 500MB
- 启动Relayer
首先,可以按照Relayer的README的快速开始完成【编译】、【配置】、【跨链身份】,执行generate-relayer-account
之后先不用申请Relayer跨链证书,先完成下面的配置工作。
在Relayer的安装目录,找到config/application.yml
,将下面配置relayer.network.node.crosschain_cert_path
注释掉,也就是先不配置Relayer跨链证书,不要忘记将generate-relayer-account
的私钥文件路径填入配置relayer.network.node.private_key_path
。
relayer:
network:
node:
sig_algo: Ed25519
# crosschain_cert_path: classpath:cc_certs/relayer.crt
private_key_path: file: cc_certs/private_key.pem
之后按照【启动Embedded BCDNS】小节配置内置BCDNS功能,别忘记为BCDNS功能在relayer数据库中创建表,SQL脚本在此。
随后按照【运行】的步骤启动Relayer。
- 准备Relayer证书CSR
下面从内置的BCDNS完成Relayer跨链证书的申请,首先准备证书的证书签名请求(Certificate Signing Request, CSR)。
用法可以参考Relayer CLI文档,具体使用如下:
使用generate-relayer-csr
生成CSR,指定--pubkeyFile
到relayer account使用的公钥,即跨链身份小节中generate-relayer-account
生成的公钥,拷贝生成的Base64字符串,在下一步中使用。
relayer:> generate-relayer-csr --pubkeyFile /path/to/public_key.pem
your CSR is
AACXAAAAAA...KRMzgQAAAAAAA==
- 签署Relayer证书
在当前版本中,Relayer无法申请自己的证书,需要通过向Embedded BCDNS获取。
这里需要使用grpcurl或者postman来发送grpc请求,这里给出grpcurl
的示例。
将上一步中构建的CSR填入certSigningRequest
请求中:
Note
-import-path 指定了存放embedded-bcdns-service.proto的文件夹路径,这里可以在这里下载proto文件,然后保存到本地。
-d @ 指定了Embedded BCDNS的服务地址,默认是localhost:8090。
grpcurl -plaintext \
-import-path /path/to/acb-sdk/bcdns-services/embedded-bcdns/embedded-bcdns-core/src/main/proto/ \
-proto embedded-bcdns-service.proto \
-d @ localhost:8090 \
com.alipay.antchain.bridge.bcdns.embedded.grpc.EmbeddedBcdnsService/applyRelayerCertificate <<EOM
{
"certSigningRequest": "AACXAAAAAA...KRMzgQAAAAAAA=="
}
EOM
运行之后,将返回类似下面的json数据,applyReceipt
是用来查询证书签署结果的收据ID。
{
"applyRelayerCertificateResp": {
"applyReceipt": "16b363c1b73b0a2e73a394b69cd592542144dff96e16e0b2a9f07c0f6842cb91"
}
}
运行下面命令,使用你的收据值替换applyReceipt
参数,向Embedded BCDNS发送请求,查询证书。
grpcurl -plaintext \
-import-path /Users/zouxyan/IdeaProjects/odats-plugins/bcdns-services/embedded-bcdns/embedded-bcdns-core/src/main/proto/ \
-proto embedded-bcdns-service.proto \
-d @ localhost:8090 \
com.alipay.antchain.bridge.bcdns.embedded.grpc.EmbeddedBcdnsService/queryRelayerCertificateApplicationResult <<EOM
{
"applyReceipt": "16b363c1b73b0a2e73a394b69cd592542144dff96e16e0b2a9f07c0f6842cb91"
}
EOM
成功发送的话,可以拿到类似下面的返回,这里的Base64字段certificate
是指Relayer README中提到的跨链证书。
{
"applicationResult": {
"isFinalResult": true,
"certificate": "AADzAQAAAAABAAAAMQEAQAAA...tAzoHKIeYVth+W6bjXjAQ=="
}
}
可以使用CLI工具将Base64格式的证书转换成PEM格式,以用于Relayer。
relayer:> convert-cross-chain-cert-to-pem --base64Input AADzAQAAAAABAAAAMQEAQAAA...tAzoHKIeYVth+W6bjXjAQ==
-----BEGIN RELAYER CERTIFICATE-----
AAAIAgAAAAABAAAAMQEAKAAAAGRpZDpiaWQ6ZWY5OVJ6OFRpN3g0aTZ6eUNyUHlG
aXk5dXRzV0JKVVcCAAEAAAADAwA7AAAAAAA1AAAAAAABAAAAAQEAKAAAAGRpZDpi
...
4QlxLUp70uRK43ECAAcAAABFZDI1NTE5AwBAAAAAbA8zkKXCI4Iwp6KBERXOqKln
JT/qn36in7+iU6SsNEz0rsJpmEvVRT6adNVY7zS/ni35JwWf/zi60DKnQ7xaCA==
-----END RELAYER CERTIFICATE-----
参考Relayer README,在获得PEM格式的中继证书和密钥之后,将其配置到文件中,这里假设将证书和密钥分别放在cc_certs/relayer.crt
和cc_certs/private_key.pem
:
relayer:
network:
node:
sig_algo: Ed25519
crosschain_cert_path: file:cc_certs/relayer.crt
private_key_path: file:cc_certs/private_key.pem
最后重启Relayer服务即可。
bin/stop.sh && bin/start.sh
-
启动插件服务
请按照插件服务的README配置和启动插件服务(v0.2.3),这中间需要用到中继的TLS证书,这往往在中继安装目录的
tls_certs/relayer.crt
。 -
安装以太坊插件
请参考文档编译并安装v0.1.1的以太坊插件,或者从release下载,重启插件服务可以重新加载插件,或者使用CLI工具。
-
注册PluginServer到Relayer
使用Relayer的CLI工具,注册PluginServer,这里用到插件服务的TLS证书
certs/server.crt
和URL,配置项按照本地环境填入即可,pluginServerId
不要重复即可,后续注册区块链需要用到该ID。relayer:> register-plugin-server --address localhost:9090 --pluginServerId myps.id --pluginServerCAPath /path/to/certs/server.crt
-
Ganache
这里推荐使用Ganache启动一条以太坊环境,从命令行启动Ganache的文档,或者使用客户端。
推荐将Ganache配置为自动出块,可以参考文档,找到
SERVER
配置页,关闭AUTOMINE
,然后可以设置出块时间MINING BLOCK TIME
,设置之后,可以点击右上的SAVE AND RESTRAT
。 -
MetaMask
首先,按照教程安装MetaMask,这里假设开发者已经熟悉MetaMask的使用。
在MetaMask上,添加Ganache区块链网络、添加区块链的账户信息。
-
生成以太坊插件配置
参考文档,填写配置json文件,并保存下来,比如
eth.json
,privateKey
、url
、gasLimit
和gasPrice
可以在ganache页面获取,后面该配置文件,将会用在注册区块链操作中。{ "gasLimit": 3000000, "gasPrice":4100000000, "privateKey":"0x6322353...39de23b637", "url":"http://localhost:7545" }
区块链想要加入AntChainBridge跨链网络,需要向BCDNS申请域名和域名证书
- 注册BCDNS服务
首先要构造Embedded BCDNS客户端的配置文件,这里给出模版,保存到本地,假设是config/embedded_root_bcdns.json
。
{
"server_address":"grpc://0.0.0.0:8090"
}
使用register-bcdnsservice
注册BCDNS服务:
relayer:> register-bcdnsservice --bcdnsType embedded --propFile /path/to/config/embedded_root_bcdns.json
success
- BCDNS签发域名证书
apply-domain-name-cert
用于申请域名证书,oidFilePath
指向generate-relayer-account
生成的public_key.pem
,参数domain
则代表要申请的区块链域名:
relayer:> apply-domain-name-cert --applicantOidType X509_PUBLIC_KEY_INFO --oidFilePath /path/to/public_key.pem --domain testdomain1
your receipt is 6165148aa797eb97416fcdf8fbb2a648704d0343ea2ad0d8826837c3bda3781a
- 查询域名证书申请结果
query-domain-cert-application-state
用于查询指定区块链域名的证书申请状态:
relayer:> query-domain-cert-application-state --domain testdomain1
application finished: apply_success
- 查询域名证书
query-domain-name-cert-from-bcdns
用于从BCDNS查询指定域名的证书信息:
relayer:> query-domain-name-cert-from-bcdns --domain testdomain1
同样地,申请域名testdomain2
备用。
通过CLI配置区块链到Relayer,这里首先指定域名domain
为刚申请的testdomain1
,区块链类型product
为以太坊插件指定的simple-ethereum
,blockchainId
则随意指定不要重复即可,pluginServerId
为上面注册的PluginServer的ID,confFile
为以太坊配置文件。
add-blockchain-anchor
用于向系统添加一个指定的区块链配置:
relayer:> add-blockchain-anchor --domain testdomain1 --product simple-ethereum --blockchainId eth01.id --pluginServerId myps.id --confFile /path/to/eth.json
通过CLI工具启动Relayer对于该链的监听、转发等服务,统一称为Anchor服务。
add-blockchain-anchor
用于在Relayer(中继器)中启动区块链锚定服务:
relayer:> start-blockchain-anchor --product simple-ethereum --blockchainId eth01.id
启动之后,Relayer会调用插件实现系统合约的部署和初始化,可以通过CLI查看合约的状态和地址,这部分信息在后面初始化Demo合约时会用到:
relayer:> get-blockchain-contracts --product simple-ethereum --blockchainId eth01.id
{"am_contract":"empty","state":"INIT","sdp_contract":"empty"}
显示为INIT
则说明没有部署完成。
relayer:> get-blockchain-contracts --product simple-ethereum --blockchainId eth01.id
{"am_contract":"0x6e6fcd3a010dce59e2513de116ce284efde5a21a","state":"DEPLOY_FINISHED","sdp_contract":"0xc3ca7b2e976427a39854033f687a9c84eaea3f82"}
显示为DEPLOY_FINISHED
则说明部署完成。
启动的区块链,Relayer会对其启动Anchor服务,扫描注册之后的每个高度,可以通过CLI工具查看当前扫描的高度。
relayer:> get-blockchain-heights --product simple-ethereum --blockchainId eth01.id
{
"crosschainTaskBlockHeight":{
"gmtModified":"2023-12-27 19:20:23",
"height":2096510
},
"latestBlockHeight":{
"gmtModified":"2023-12-27 19:20:23",
"height":2096511
}
}
同样地,用域名testdomain2
注册eth02.id
。在注册时,可以将eth.json中的账户私钥替换成另一个私钥(Ganache上获取),以防链上nonce冲突。虽然注册用了同一条链,但是会部署两套系统合约,在跨链系统中,会被作为两条链处理,即在AntChainBridge跨链中,参与的区块链都是逻辑链。
下面将要尝试从testdomain1
的智能合约发送一段消息到testdomain2
的智能合约。
首先,给出消息发送合约和消息接收合约的代码。
-
发送合约
pragma solidity ^0.8.0; interface ProtocolInterface { function sendMessage( string calldata _destination_domain, bytes32 _receiver, bytes calldata _message ) external; function sendUnorderedMessage( string calldata _destination_domain, bytes32 _receiver, bytes calldata _message ) external; } contract SenderContract { address sdp_address; function setSdpMSGAddress(address _sdp_address) public { sdp_address = _sdp_address; } function send( bytes32 receiver, string memory domain, bytes memory _msg ) public { ProtocolInterface sdp = ProtocolInterface(sdp_address); sdp.sendMessage(domain, receiver, _msg); } function sendUnordered( bytes32 receiver, string memory domain, bytes memory _msg ) public { ProtocolInterface sdp = ProtocolInterface(sdp_address); sdp.sendUnorderedMessage(domain, receiver, _msg); } }
-
接收合约
pragma solidity ^0.8.0; contract ReceiverContract { bytes last_msg; bytes last_uo_msg; event amNotify(string key, bytes32 value, string enterprise); function recvMessage( string memory domain_name, bytes32 author, bytes memory message ) public { require(message.length != 32, "32B"); last_msg = message; emit amNotify(domain_name, author, string(message)); } function getLastMsg() public view returns (bytes memory) { return last_msg; } function recvUnorderedMessage( string memory domain_name, bytes32 author, bytes memory message ) public { require(message.length != 32, "32B"); last_uo_msg = message; emit amNotify(domain_name, author, string(message)); } function getLastUnorderedMsg() public view returns (bytes memory) { return last_uo_msg; } }
打开Remix,关于Remix的使用,请参考官方教程,这里不对基本操作做过多的介绍。
将上述代码分别创建一个文件比如
SenderContract.sol
和ReceiverContract.sol
并拷贝进去。使用Remix分别编译两个合约、并选择“Injected Provider - MetaMask”作为ENVIRONMENT,部署合约到Ganache。在这里也可以使用“Custom - External HTTP Provider”配置Ganache的RPC地址即可,注意不要使用注册区块链的私钥。
部署完成后,对于
SenderContract.sol
,需要完成一次配置操作:在Remix调用
SenderContract.sol
实例的setSdpMSGAddress
方法,将testdomain1
通过get-blockchain-contracts
拿到的sdp_contract
地址作为参数即可,比如上面例子的0xc3ca7b2e976427a39854033f687a9c84eaea3f82
。
Relayer的Anchor服务会对发送的跨链消息进行监听,并发送给接收链,这里会校验发送链的权限,如果无权限,则会失败。
通过CLI,完成合约-合约级别授权:
relayer:> add-cross-chain-msg-acl --grantDomain testdomain1 --grantIdentity 0x3202821beaC4F58be60bb465d1ae4b1899Cbc79a --ownerDomain testdomain2 --ownerIdentity 0x6f0E88921360dD375CA309d6e96B44C81FB25979
在上面命令中,grantDomain
为发送链域名,grantIdentity
为发送合约地址,这里替换成你部署的合约地址,ownerDomain
为接收链域名,ownerIdentity
为接收合约地址,这里替换成你部署的合约地址。
SenderContract.sol
有接口senderUnordered
,用来发送无序的跨链消息。
发送之前你需要准备三个参数:
-
receiver:接收合约的地址,这里为bytes32,因为在跨链中,所有账户地址都需要表达为32Bytes,像以太坊地址,这里是前缀加12Bytes的0x00,拓展到32Bytes,比如
0x0000000000000000000000006f0E88921360dD375CA309d6e96B44C81FB25979
,这里替换成自己的接收合约地址(后缀20Bytes)。 -
domain:接收域名,这里是
eth02.local.bif
。 -
_msg:发送的消息,这里是bytes类型,需要填入Hex格式,比如下面一个hex,解出来是:
I'm sending a 🦊 to a 🐶 by antchain bridge.
0x49276d2073656e64696e67206120f09fa68a20746f206120f09f90b620627920616e74636861696e206272696467652e
然后,使用Remix调用发送合约的senderUnordered
接口,发送跨链消息。
接收合约的recvUnorderedMessage
方法会被接收链的SDP合约调用,以传递跨链消息。
调用接收合约的getLastUnorderedMsg
,可以看到接收到的信息,依然是hex值,解出来后,可以看到消息字符串。
如果需要交易hash信息,或者没有接收到,可以查询数据库的sdp_msg_pool
和sdp_msg_archive
,可以看到字段tx_hash
和tx_fail_reason
。
收集中👏