如何基于星火链网BCDNS实现跨链(v0) - AntChainOpenLabs/AntChainBridge GitHub Wiki

🔎介绍

这个文档将介绍如何启动一个本地环境,包括Relayer、BCDNS、PluginServer、以太坊私有链,BCDNS将使用中国信通院开发的星火链BCDNS,基于这套环境,我们将介绍如何注册跨链身份证书、域名证书,如何使用简单的Demo合约来实现区块链之间的跨链通信。

上图大体介绍了整体操作的脉络。

  • 操作者需要部署中继(Relayer)、BCDNS(颁证服务+智能合约)服务、插件服务以及一条私有链(Ganache)。
  • 在操作过程中需要生成星火链账户,用于在星火链部署合约,该账户会配置颁证服务,注意该账户与星火链测试网交互需要星火令,这目前需要操作者自行申请,后续会优化相关流程。
  • 使用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 --default-authentication-plugin=mysql_native_password --default_time_zone +08:00

然后下载Redis镜像并启动容器:

docker run -itd --name redis-test -p 6379:6379 redis --requirepass 'YOUR_PWD' --maxmemory 500MB

星火链BCDNS

首先运行颁证服务,这里可以按照颁证服务的README的快速开始完成操作,下载代码并编译,完成包括星火链测试网账户创建、合约部署、启动颁证服务、颁证服务初始化等操作,链下的颁证服务加上链上的智能合约共同组成星火链BCDNS。

这里你需要了解如何通过颁证服务申请跨链身份证书,下面一节需要为Relayer签发跨链证书。

Relayer

  • 启动Relayer

    首先需要将Relayer运行起来,并生成中继用于向星火链BCDNS的颁证服务申请证书的公钥和BID Document,这里可以按照README快速开始完成操作。

    Relayer启动时,需要提前使用下一节颁证服务签发的Relayer跨链证书,在证书制作完成之后重启Relayer服务加载该证书即可。

  • BCDNS签发Relayer证书

    这一部分将使用Relayer的身份(公钥)向BCDNS颁证服务申请跨链证书,可以参考颁证服务的README,给出了申请PTC等跨链身份证书的操作步骤。

    • 准备申请内容

      在Relayer的启动过程中,会生成Relayer公钥对应的BID Document,通常是bid_document.json文件中获取到publicKeyHex参数对应的值,比如下面一个例子,公钥的Hex内容:

      {
          "publicKey":[
              {
                  "type":"ED25519",
                  "publicKeyHex":"b06566af665ee55...a24a0f25a74a44cce"
              }
          ]
      }

      下面使用颁证服务的单测代码生成数据,后续会开发专用的工具。使用test/java/org/bcdns/credential/ApplyTesttestRelayApply01辅助生成Relayer证书申请参数,将其中两处的公钥信息替换掉,比如下面的b06566d3ba095bb576e8d75b9e39a3d06cdf6deb25f93d526f13040c5df9cd6c27be8e,替换为上面publicKeyHex的值:

      ...
      biDpublicKeyOperation[0].setPublicKeyHex("b06566d3ba095bb576e8d75b9e39a3d06cdf6deb25f93d526f13040c5df9cd6c27be8e");
      ...
      PublicKeyManager publicKeyManager = new PublicKeyManager("b06566d3ba095bb576e8d75b9e39a3d06cdf6deb25f93d526f13040c5df9cd6c27be8e");
      

      直接Run或者Debug该函数即可得到contentcredentialTypepublicKeysign参数,比如下面结果。

      content:[0, 0, 122, 1, ..., 34, 125, 93, 125]
      credentialType:3
      publicKey:b06566af665ee55...a24a0f25a74a44cce
      sign:[5, 9, 70, 89, ..., -64, -10, -84, 14]
      
    • 发起申请

      然后,和星火链BCDNS颁证服务的README的申请流程的“第三步:申请PTC证书”类似。

      调用/external/vc/apply接口发起证书的申请。content使用上述返回content的byte数组即可,credentialType使用上述放回的credentialType即可,publicKey使用上面的Hex字符串,sign填入上面的byte数组。

      curl -H "Content-Type: application/json" -X POST -d '{"content":your_content,"credentialType":3,"publicKey":"you_publicKey","sign":you_sign}' http://localhost:8114/external/vc/apply

      返回类似下面结果,message显示成功,applyNo则会在第四步使用到。

      {
        "errorCode": 0,
        "message": "success",
        "data": {
          "applyNo": "853a8bcaa14e86898a08be8d2f027586"
        }
      }
    • 申请审核

      下面和星火链BCDNS颁证服务的README的申请流程的“第四步:审核发证”相同,会用到初始化颁证服务的数据。 然后调用/internal/vc/audit接口审核。

      • HTTP头中的accessToken,请使用星火链BCDNS颁证服务的README的申请流程的“第二步:生成access token”中生成的token。
      • applyNo使用上一步的返回值。
      curl -H "Content-Type:application/json" -H "accessToken:you_accessToken" -X POST -d '{"applyNo":"your_applyNo","status":2,"reason":"your_reason"}' http://localhost:8114/internal/vc/audit
      

      返回类似下面的结果,message显示成功,txHash为上传证书的交易hash。

      {
          "errorCode": 0,
          "message": "success",
          "data": {
              "txHash": "f1416e7d625d0d88ea65d00e334b8849eea30993418bfaf9d6035a685fdca40e"
          }
      }
    • 获取证书

      通过调用vc/apply/status接口获得返回值,applyNo填入“发起申请”返回得到的applyNo即可。

      curl -H "Content-Type: application/json" -X POST -d '{"applyNo":"853a8bcaa14e86898a08be8d2f027586"}' http://localhost:8114/external/vc/apply/status
      

      返回结果如下,返回结果中credentialId即为证书id,可以用于下载证书。

      {
          "errorCode": 0,
          "message": "success",
          "data": {
              "status": 2,
              "credentialId": "did:bid:ef27stZJAese6vWp4rbgh8wQvAQA8wvrK",
              "userId": {
                  "type": "BID",
                  "rawId": "ZGlkOmJpZDplZjI3Z21FRENRV2V2MjNyWUpENHdyUFBiRFllQjhqTEs="
              }
          }
      }

      最后调用/external/vc/download接口可以下载证书。credentialId填入上一步的证书ID。

      curl -H "Content-Type: application/json" -X POST -d '{"credentialId":"you_credentialId"}' http://localhost:8114/external/vc/download

      返回类似下面的结果,message显示成功,credential为证书的Base64格式。

      {
          "errorCode": 0,
          "message": "success",
          "data": {
              "credential": "AAAPAgAAAAABAAAAMQEAKQAA...c2FUCijm+sd4m1Q4="
          }
      }

      获取到一个Base64格式的跨链证书之后,可以使用中继的CLI工具,将其转换为PEM格式。

      relayer:> convert-cross-chain-cert-to-pem --base64Input AAAPAgAAAAABAAAAMQEAKQAA...c2FUCijm+sd4m1Q4=
      -----BEGIN RELAYER CERTIFICATE-----
      AAAIAgAAAAABAAAAMQEAKAAAAGRpZDpiaWQ6ZWY5OVJ6OFRpN3g0aTZ6eUNyUHlG
      aXk5dXRzV0JKVVcCAAEAAAADAwA7AAAAAAA1AAAAAAABAAAAAQEAKAAAAGRpZDpi
      ...
      4QlxLUp70uRK43ECAAcAAABFZDI1NTE5AwBAAAAAbA8zkKXCI4Iwp6KBERXOqKln
      JT/qn36in7+iU6SsNEz0rsJpmEvVRT6adNVY7zS/ni35JwWf/zi60DKnQ7xaCA==
      -----END RELAYER CERTIFICATE-----
      

      获得PEM证书之后,放到Relayer安装目录的cc_certs/relayer.crt,然后重启Relayer即可。

      ps -ewf | grep r-bootstrap | grep -v grep | awk '{print $2}' | xargs kill 
      bin/start.sh
  • 生成BCDNS配置文件

    然后,需要基于Relayer提供的CLI工具生成星火链BCDNS的配置文件,对于命令和参数的说明,请参考文档,根据你本地的环境来设置对应的参数值。

    这里要用到星火链BCDNS颁证服务的README的部署合约小节的智能合约,确保你已经完成了部署。

    relayer:> generate-bif-bcdns-conf --relayerPrivateKeyFile /path/to/private_key.pem --relayerCrossChainCertFile /path/to/relayer.crt --certServerUrl http://localhost:8114 --bifChainRpcUrl http://test.bifcore.bitfactory.cn --bifDomainGovernContract did:bid:efjQKy4HEshTueHGKzrJPATKoFeNgHKo --bifRelayerGovernContract did:bid:efSnvCFJSnpWiQiVhnh8Eimgyi4RoNpA --bifPtcGovernContract did:bid:efgTq9DtP2zHAhmKp7M4BhN6AVYMVWV2
    file is : /path/to/bif_bcdns_conf.json

    生成的配置文件位于/path/to/bif_bcdns_conf.json,大概类似下面的Json:

    {
    	"certificationServiceConfig":{
    		"authorizedKeyPem":"-----BEGIN PRIVATE KEY-----\nMFECAQEwB...WnSkTM4=\n-----END PRIVATE KEY-----\n",
    		"authorizedPublicKeyPem":"-----BEGIN PUBLIC KEY-----\nMCowBQYDK2Vw...KDyWnSkTM4=\n-----END PUBLIC KEY-----\n",
    		"authorizedSigAlgo":"Ed25519",
    		"clientCrossChainCertPem":"-----BEGIN RELAYER CERTIFICATE-----\nAAAIA...DyLBh2ITiTQ4IVYlXkYjSBw==\n-----END RELAYER CERTIFICATE-----\n",
    		"clientPrivateKeyPem":"-----BEGIN PRIVATE KEY-----\nMFECAQE...V+RqJKDyWnSkTM4=\n-----END PRIVATE KEY-----\n",
    		"sigAlgo":"Ed25519",
    		"url":"http://localhost:8114"
    	},
    	"chainConfig":{
    		"bifAddress":"did:bid:efbThy5sb...5oQGX6LUGwg",
    		"bifChainRpcUrl":"http://test.bifcore.bitfactory.cn",
    		"bifPrivateKey":"priSPKgnr1a...JNaackZJUo",
    		"certificatesGovernContract":"",
    		"domainGovernContract":"did:bid:efjQKy4HEshTueHGKzrJPATKoFeNgHKo",
    		"ptcGovernContract":"did:bid:efgTq9DtP2zHAhmKp7M4BhN6AVYMVWV2",
    		"relayerGovernContract":"did:bid:efSnvCFJSnpWiQiVhnh8Eimgyi4RoNpA"
    	}
    }
  • 注册BCDNS

    使用CLI将BCDNS注册到Relayer,文件bcdns_bif.json是上一小节你的BCDNS配置文件。

    relayer:> register-bcdnsservice --bcdnsType bif --propFile /path/to/bif_bcdns_conf.json
    

PluginServer

  • 启动插件服务

    请按照插件服务的README配置和启动插件服务(v0.2.1),这中间需要用到中继的TLS证书,这往往在中继安装目录的tls_certs/relayer.crt

  • 安装以太坊插件

    请参考文档编译并安装v0.1.0的以太坊插件,或者从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.jsonprivateKeyurlgasLimitgasPrice可以在ganache页面获取,后面该配置文件,将会用在注册区块链操作中。

    {
      "gasLimit": 3000000,
      "gasPrice":4100000000,
      "privateKey":"0x6322353...39de23b637",
      "url":"http://localhost:7545"
    }

🔎注册区块链

域名申请

区块链想要加入AntChainBridge跨链网络,需要向BCDNS申请域名和域名证书,目前这由中继作为申请者发出请求。

在CLI中执行下面命令,发送申请请求给BCDNS。

relayer:> apply-domain-name-cert --domain eth01.local.bif --oidFilePath /path/to/bid_document.json
your receipt is eb24d486317f1e4ecaec5d215176cca1

上述命令中,--domain指定了要申请的域名,--oidFilePath指定申请者的主体信息,这里使用启动Relayer时生成的BID documnet文件,默认会向本地BCDNS发送请求,要求注册域名,发送之后,星火链BCDNS会返回一个Hex的收据,通过该收据可以按照星火链BCDNS的教程,审批该申请,这里就不展开了。

使用该收据,向BCDNS发送审核请求,批准域名证书,your_applyNo填入上一步的收据,you_accessToken使用“星火链BCDNS”小节的第一步的返回值。

curl -H "Content-Type:application/json" -H "accessToken:you_accessToken" -X POST -d '{"applyNo":"your_applyNo","status":2,"reason":"your_reason"}' http://localhost:8114/internal/vc/audit

在CLI可以查询该域名证书的申请状态:

relayer:> query-domain-cert-application-state --domain eth01.local.bif
your application not finished: applying

如果申请成功则会显示:

relayer:> query-domain-cert-application-state --domain eth01.local.bif
your application finished: apply_success

同样地,申请域名eth02.local.bif备用。

配置区块链

通过CLI配置区块链到Relayer,这里首先指定域名domain为刚申请的eth01.local.bif,区块链类型product为以太坊插件指定的simple-ethereumblockchainId则随意指定不要重复即可,pluginServerId为上面注册的PluginServer的ID,confFile为以太坊配置文件。

relayer:> add-blockchain-anchor --domain eth01.local.bif --product simple-ethereum --blockchainId eth01.id --pluginServerId myps.id --confFile /path/to/eth.json

启动区块链

通过CLI工具启动Relayer对于该链的监听、转发等服务,统一称为Anchor服务。

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
	}
}

同样地,用域名eth02.local.bif注册eth02.id。在注册时,可以将eth.json中的账户私钥替换成另一个私钥(Ganache上获取),以防链上nonce冲突。虽然注册用了同一条链,但是会部署两套系统合约,在跨链系统中,会被作为两条链处理,即在AntChainBridge跨链中,参与的区块链都是逻辑链。

🔎执行Demo

下面将要尝试从eth01.local.bif的智能合约发送一段消息到eth02.local.bif的智能合约。

准备合约

首先,给出消息发送合约和消息接收合约的代码。

  • 发送合约

    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.solReceiverContract.sol并拷贝进去。使用Remix分别编译两个合约、并选择“Injected Provider - MetaMask”作为ENVIRONMENT,部署合约到Ganache。

    在这里也可以使用“Custom - External HTTP Provider”配置Ganache的RPC地址即可,注意不要使用注册区块链的私钥。

    部署完成后,对于SenderContract.sol,需要完成一次配置操作:

    在Remix调用SenderContract.sol实例的setSdpMSGAddress方法,将eth01.local.bif通过get-blockchain-contracts拿到的sdp_contract地址作为参数即可,比如上面例子的0xc3ca7b2e976427a39854033f687a9c84eaea3f82

配置授权

Relayer的Anchor服务会对发送的跨链消息进行监听,并发送给接收链,这里会校验发送链的权限,如果无权限,则会失败。

通过CLI,完成合约-合约级别授权:

relayer:> add-cross-chain-msg-acl --grantDomain eth01.local.bif --grantIdentity 0x3202821beaC4F58be60bb465d1ae4b1899Cbc79a --ownerDomain eth02.local.bif --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_poolsdp_msg_archive,可以看到字段tx_hashtx_fail_reason

🔎Q&A

收集中👏

⚠️ **GitHub.com Fallback** ⚠️