本系统通过搭建骨干节点网关服务,为跨链交易提供转发和验证服务,以实现星火主链与以太坊测试网进行跨链互操作。骨干节点网关服务主要包括三大模块:跨链事件监听模块、跨链事件验证模块和跨链事件转发模块。
**跨链事件监听模块:**对于以太坊测试网,需要按要求开发出跨链智能合约,用户通过调用合约触发跨链事件,骨干节点网关使用以太坊的SDK来时刻监听到这些跨链事件。对于星火主链上发起的跨链交易,骨干节点则通过星火·链网超级节点开放平台API提供的跨链服务,来轮询获取主链上的跨链交易,完成监听的功能。
**跨链事件验证模块:**骨干节点网关需要对以太坊子链发起的跨链交易进行验证,并将验证结果和跨链交易一起提交到主链上,主链对验证证明进行备份存储,可以采用抽验的方式进行二次验证,也能够为后续的问责机制提供数字证明。
**跨链事件转发模块:**以太坊测试网和星火主链之间的跨链交易,需要骨干节点网关进行转发。骨干节点网关通过以太坊测试网的跨链智能合约来获取跨链交易详情,针对不同类型的跨链交易分别进行处理,进而有选择的调用星火·链网超级节点开放平台API提供的接口服务,将跨链交易转发到星火主链上。反之亦然,骨干节点网关通过星火·链网超级节点开放平台API提供的接口服务,来获取主链上的跨链交易详情,然后对交易进行处理组装,最后转发到以太坊测试网上。
跨链交易从以太坊测试网到星火主链的流程:
跨链交易从星火主链到以太坊测试网的流程:
环境准备是部署和使用以太坊跨链demo的第一步,主要是说明demo及相关组件运行的硬件配置和软件依赖,您需要在部署跨链demo之前确认机器满足下述的要求。
配置 | 推荐配置 | 最低配置 |
---|---|---|
CPU | 2.4GHz | 1.5GHz |
内存 | 16GB | 8GB |
核心 | 8核 | 4核 |
带宽 | 10Mb | 2Mb |
- Windows 7 或者 Windows 10
- Ubuntu 16.04 及以上
- CentOS 7.2 及以上
- 星火·链网超级节点开放平台API
- Nodejs
- Postman
- MetaMask
├─build //truffle合约编译
│ └─contracts
├─contracts //solidity跨链合约,主要为CrossChainContract.sol和CrossChainContractAdditional.sol
├─demo //骨干网关节点跨链服务,lib为主链签名SDK,node_modules包括web3js、request、ethereumjs-tx等
│ ├─lib //backbone_bif.js和backbone_eth.js为与以太坊私有链交互的骨干网关服务;
│ │ └─bifchain-js-signer //backbone_server_bif.js和backbone_server_eth.js为与以太坊测试网交互的骨干网关服务;
│ └─ node_modules
├─images
├─lib //库合约
├─migrations //truffle部署跨链合约
└─test
Ropsten是以太坊官方提供的测试网络,是为了解决Morden难度炸弹问题而重新启动的一条区块链,目前仍在运行,共识机制为PoW。星火·链网跨链服务既可以在本地以太坊私有链环境下使用,也可以在以太坊测试网环境中运行,本文主要讲述星火主链与以太坊Ropsten测试网进行跨链交互。
以太坊的跨链智能合约主要为CrossChainContract.sol和CrossChainContractAdditional.sol(补充合约),可以使用remix或者truffle进行编译,编译器版本建议选择0.7.0,同时由于跨链合约过大需要使用优化编译Enable optimization,得到合约的ABI。
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"AssetBurnEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"AssetMintEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"approveEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"string","name":"cross_TxNo","type":"string"},{"indexed":false,"internalType":"uint8","name":"txType","type":"uint8"}],"name":"sendAckedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"string","name":"cross_TxNo","type":"string"},{"indexed":false,"internalType":"uint8","name":"txType","type":"uint8"}],"name":"sendTxEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"string","name":"chain_Code","type":"string"}],"name":"setChainCodeEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"address","name":"gateway_Address","type":"address"}],"name":"setGatewayEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"string","name":"cross_TxNo","type":"string"},{"indexed":false,"internalType":"uint8","name":"txType","type":"uint8"}],"name":"startTxEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"address","name":"to","type":"address"}],"name":"takeOutEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"operation","type":"string"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferEvent","type":"event"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"getAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"crossTxNo","type":"string"}],"name":"getCrossTx","outputs":[{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"enum Storage.TxResultEnum","name":"","type":"uint8"},{"internalType":"enum Storage.TxRefundedEnum","name":"","type":"uint8"},{"internalType":"enum Storage.TxOriginEnum","name":"","type":"uint8"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"crossChainAdd","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"crossTxNo","type":"string"},{"internalType":"enum Storage.TxResultEnum","name":"result","type":"uint8"},{"internalType":"string","name":"version","type":"string"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"sendAcked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"srcBid","type":"string"},{"internalType":"address payable","name":"destAddress","type":"address"},{"internalType":"string","name":"srcChainCode","type":"string"},{"internalType":"string","name":"destChainCode","type":"string"},{"internalType":"uint8","name":"txType","type":"uint8"},{"internalType":"string","name":"crossTxNo","type":"string"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"string","name":"extension","type":"string"},{"internalType":"string","name":"remark","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"bytes","name":"proof","type":"bytes"}],"name":"sendTx","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"chainCode","type":"string"}],"name":"setChainCode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gatewayAddress","type":"address"}],"name":"setGateway","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"srcAddress","type":"address"},{"internalType":"string","name":"destBid","type":"string"},{"internalType":"string","name":"srcChainCode","type":"string"},{"internalType":"string","name":"destChainCode","type":"string"},{"internalType":"uint8","name":"txType","type":"uint8"},{"internalType":"bytes","name":"payload","type":"bytes"},{"internalType":"string","name":"extension","type":"string"},{"internalType":"string","name":"remark","type":"string"},{"internalType":"string","name":"version","type":"string"}],"name":"startTx","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"crossTxNo","type":"string"},{"internalType":"address payable","name":"to","type":"address"}],"name":"takeOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"}]
跨链合约的部署可以采用remix或者web3js,以太坊跨链智能合约已部署到Ropsten测试网,合约地址:0x717649243802CB60c347Eb76622B000F1Dd74F30。
//合约初始化只能进行一次
const crossAdditional = "0x970994c0C74870b60feB9A93F862EeF1DC8DB8a2"
const contractInit = myContract.methods.initialize(crossAdditional)
const contractInitAbi = contractInit.encodeABI();
let estimatedGas;
let nonce;
contractInit.estimateGas({from: deployerAccount}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(deployerAccount).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: 10000000000,
gasLimit: 3000000,
to: crossChainContractAddress,
data: contractInitAbi,
from: deployerAccount,
nonce: '0x' + nonce
};
const tx = new Tx(txParams, {chain:'ropsten', hardfork: 'petersburg'});
tx.sign(deployerPrivateKey);
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
})
});
});
//设置的chainCode必须是向主链申请注册通过的,具体方法请参考星火·链网生态接入开发规范
const _ChainCode = "a202"
const contractSetChainCode = myContract.methods.setChainCode(_ChainCode)
const contractSetChainCodeAbi = contractSetChainCode.encodeABI();
let estimatedGas;
let nonce;
contractSetChainCode.estimateGas({from: deployerAccount}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(deployerAccount).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: 10000000000,
gasLimit: 3000000,
to: crossChainContractAddress,
data: contractSetChainCodeAbi,
from: deployerAccount,
nonce: '0x' + nonce
};
const tx = new Tx(txParams, {chain:'ropsten', hardfork: 'petersburg'});
tx.sign(deployerPrivateKey);
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
})
});
});
//负责监听以太坊的跨链合约事件,以及转发和反馈跨链事件
const gatewayAddress = "0xE1fAdFBba60313C932Fe538a400aDf37d6308f0B"
//const gatewayPrivatekey = "9e5e9bc3c721539c949b10cec8ce2d6079cf181d5ccfbd78c81e052ee6f62394"
const contractSetGateway = myContract.methods.setGateway(gatewayAddress)
const contractSetGatewayAbi = contractSetGateway.encodeABI();
let estimatedGas;
let nonce;
console.log("Getting gas estimate");
contractSetGateway.estimateGas({from: deployerAccount}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(deployerAccount).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: 10000000000,
gasLimit: 3000000,
to: crossChainContractAddress,
data: contractSetGatewayAbi,
from: deployerAccount,
nonce: '0x' + nonce
};
const tx = new Tx(txParams, {chain:'ropsten', hardfork: 'petersburg'});
tx.sign(deployerPrivateKey);
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
})
});
});
参考星火·链网超级节点开放平台API和星火·链网生态接入开发规范文档。
骨干节点网关主要负责跨链交易的监听,转发和验证。网关服务由backbone_server_bif.js和backbone_server_eth.js两部分组成,分别负责监听和转发星火主链和以太坊测试网上的跨链交易。网关与以太坊的交互使用web3js,与星火主链交互使用http。
监听并转发以太坊跨链合约事件,每5秒一次,可以自行调整。
var rule = new schedule.RecurrenceRule();
var times = [];
for(var i=1; i<60; ){
//每5秒查询一次跨链事件
i += 5;
times.push(i);
}
rule.second = times;
schedule.scheduleJob(rule,async() => {
//获取当前区块号
var blockHeight = await web3.eth.getBlockNumber();
console.log("schedule", "blockHeight:", blockHeight);
if (blockNum < blockHeight) {
blockNum = blockHeight
await getEvents(blockHeight, blockHeight);
}
})
let getEvents = async (fromBlock, toBlock) =>{
console.log('fromBlock:', fromBlock,'toBlock:', toBlock);
//监听3种跨链事件
myContract.getPastEvents('allEvents',{
filter:{operation:['startTxEvent', 'sendTxEvent', 'sendAckedEvent']},
fromBlock:fromBlock,
toBlock: toBlock,
},
async (error, events) => {
for (const event of events){
//获取跨链事件的接口类型、跨链编号、跨链类型
var _carossTxState = event.returnValues[0]
var _crossTxNo = event.returnValues[1]
var _crossTxType = event.returnValues[2]
//获取区块号、交易的哈希
var _ledgerSeq = event.blockNumber
var _txHash = event.transactionHash
if (_carossTxState == 'startTx')
{
//用户发送跨链交易事件,网关节点转发跨链交易
_startTx(_crossTxNo, _crossTxType, _ledgerSeq, _txHash)
}
else if (_carossTxState == 'sendTx')
{
//网关节点转发的跨链交易成功上链,需要回Ack给源链
_sendTx(_crossTxNo, _ledgerSeq, _txHash)
}
else if (_carossTxState == 'sendAcked')
{
//网关节点回的Ack上链成功,需要给目标连回复Ack
_sendAcked(_crossTxNo, _ledgerSeq, _txHash)
}
}
})
}
监听并处理转发星火主链跨链合约事件,每5秒一次,可以自行调整。
var rule = new schedule.RecurrenceRule();
var times = [];
for(var i=1; i<60; ){
//每5秒查询一次跨链事件
i += 5;
times.push(i);
}
rule.second = times;
schedule.scheduleJob(rule,async() => {
console.log("schedule", "pageStart:", pageStart);
await getEvents(listURL, queryListParams, pageStart);
})
//获取主链跨链合约事件列表
let getEvents = async (_listURL, _queryListParams, _pageStart) =>{
_queryListParams.params.pageStart = _pageStart
request({
url: _listURL,
method: "POST",
json: true,
headers: "",
body: _queryListParams
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
//记录总的跨链交易数量
txTotal = body.data.page.pageTotal
if (_pageStart <= txTotal) {
queryTxDetailParams.params.crossTxNo = body.data.list[0].crossTxNo
//根据跨链编号获取跨链具体详情
gateWayDeal(detailURL, queryTxDetailParams);
pageStart += 1;
}
}
})
}
function gateWayDeal(_detailURL, _queryTxDetailParams) {
request({
url: _detailURL,
method: "POST",
json: true,
headers: "",
body: _queryTxDetailParams
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var _txState = body.data.result
var _txType = body.data.flowList[0].type
//根据跨链交易详情,处理跨链交易
if (_txState == 0 && _txType == 0) {
//该交易为主链用户发起的跨链交易startTx,子链网关节点需要发送sendTx
var params = {"SrcBid":body.data.srcAddress, "DestAddress":body.data.destAddress, "SrcChainCode":body.data.srcChainCode, "DestChainCode":body.data.destChainCode, "TxType":body.data.payloadType, "CrossTxNo":body.data.crossTxNo, "Payload":"", "Extension":body.data.flowList[0].extension, "Remark":body.data.remark, "Version": "V1", "Proof":""}
if (body.data.payloadType == 0) {
//主链积分转移
var amount = body.data.payloadGas.amount
var payload0 = web3.eth.abi.encodeParameter('uint256', amount)
params.Payload = payload0
console.log("user startTx maingas")
}
else if (body.data.payloadType == 1)
{
var masterAmount = body.data.payloadSgas.masterAmount
var srcAmount = body.data.payloadSgas.srcAmount
var srcRate = body.data.payloadSgas.srcTokenRate
var destAmount = body.data.payloadSgas.destAmount
var destRate = body.data.payloadSgas.destTokenRate
var payload1 = web3.eth.abi.encodeParameters(['uint256', 'uint256', 'uint256', 'uint256', 'uint256'], [masterAmount, srcAmount, srcRate, destAmount, destRate])
params.Payload = payload1
console.log("user startTx subSgas")
}
else if (body.data.payloadType == 2)
{
//var method = body.data.payloadContractCall.contractMethod
//var para = body.data.payloadContractCall.contractInput
//var payload2 = web3.eth.abi.encodeParameters(['string', 'bytes', 'bytes'], [method, inputParams, tokenParams])
}
//获取区块号和交易hash
var ledgerSeq = body.data.flowList[0].lastUpdateSeqNum
var txHash = body.data.flowList[0].txHash
var proof = web3.eth.abi.encodeParameters(['uint256', 'string'], [ledgerSeq, txHash])
params.Proof = proof
sendTx(params);
}
else if (_txState == 0 && _txType == 1)
{
//子链网关节点发送sendAcked
var params = {"CrossTxNo":body.data.crossTxNo, "Result": 1, "Version": "V1", "Proof":""}
var ledgerSeq = body.data.flowList[0].lastUpdateSeqNum
var txHash = body.data.flowList[0].txHash
var proof = web3.eth.abi.encodeParameters(['uint256', 'string'], [ledgerSeq, txHash])
params.Proof = proof
console.log("Gateway sendAcked to source chain")
sendAcked(params)
}
else if (_txState != 0 && body.data.srcChainCode == 0)
{
//子链网关节点发送sendAcked
var params = {"CrossTxNo":body.data.crossTxNo, "Result": 1, "Version": "V1", "Proof":""}
var ledgerSeq = body.data.flowList[1].lastUpdateSeqNum
var txHash = body.data.flowList[1].txHash
var proof = web3.eth.abi.encodeParameters(['uint256', 'string'], [ledgerSeq, txHash])
params.Proof = proof
console.log("Gateway sendAcked to dest chain")
sendAcked(params)
}
else
{
console.log("Tx has been solved")
}
}
})
}
本节主要演示星火主链到以太坊测试网的主链积分转移流程。
2.4.1 启动骨干网关节点服务
cd ~/demo
>node backbone_server_bif.js
//另起终端
cd ~/demo
>node backbone_server_eth.js
2.4.2 查询以太坊用户主链积分余额
var addr = "0xB2F001cB0a3561C497Acf9aD8879a3A9188a3c4a"
myContract.methods.balanceOf(addr).call({from:addr}, function(error, result){
console.log(result)
})
~100000
2.4.3 发送主链积分转移请求
主链用户发送跨链交易,其账户余额必须够支付要转移的主链积分以及手续费。
获取交易blob:
http请求方式:POST
http://{url}/crosschain/user/start/tx/maingas/blob
{
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlLZXkiOiJkaWQ6YmlkOmVmUW9DeEVlcWNObkVTa05uMlZKcENqWWZwWDE5cWVjIiwiaXNzIjoiQklGLUNIQUlOIiwiZXhwIjoxNjMxNzAwNDkxLCJiaWQiOiJkaWQ6YmlkOmVmajdqcjU1VDRSd2ZwRUo5TDFaUXh2d0w0TmdZaFV5In0.uGlgSLKIZygXWCdCaM8q9PbhbetF-2Z9OxSVD6X0ruI",
"params": {
"userBid": "did:bid:efRyzZabnVCEbCm4ABNuYwyfSp1bEjwy",
"srcAddress": "did:bid:efRyzZabnVCEbCm4ABNuYwyfSp1bEjwy",
"destAddress": "0xB2F001cB0a3561C497Acf9aD8879a3A9188a3c4a",
"destChainCode": "a202",
"payload": {
"amount": "100000000"
},
"remark": "I am from a202 for start main gas",
"extension": "extension",
"version": "1000"
}
}
提交交易上链:
http请求方式:POST
http://{url}/crosschain/user/start/tx/maingas/submit
{
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcGlLZXkiOiJkaWQ6YmlkOmVmUW9DeEVlcWNObkVTa05uMlZKcENqWWZwWDE5cWVjIiwiaXNzIjoiQklGLUNIQUlOIiwiZXhwIjoxNjMxNzAwNDkxLCJiaWQiOiJkaWQ6YmlkOmVmajdqcjU1VDRSd2ZwRUo5TDFaUXh2d0w0TmdZaFV5In0.uGlgSLKIZygXWCdCaM8q9PbhbetF-2Z9OxSVD6X0ruI",
"params": {
"blobId": "60",
"signerList": [
{
"signBlob": "bb93db1c223a8d6782120e054e240ace5b3d1521432a9555184af0faa973eca329738a27ea77fc36b2ec88b13eb2bce0248e2807e2727b2505869585e8b35504",
"publicKey": "b06566541eec6e186bca85610308baeff71b7c0372f47a365055da8b585b4b5b4124d1"
}
]
}
}
2.4.4 再次查询以太坊用户主链积分余额
var addr = "0xB2F001cB0a3561C497Acf9aD8879a3A9188a3c4a"
myContract.methods.balanceOf(addr).call({from:addr}, function(error, result){
console.log(result)
})
~100100000
2.4.5 查询主链跨链交易详情
{
"errorCode": 0,
"message": "操作成功",
"data": {
"chainCode": "0",
"crossTxNo": "0:a202:9742a46b697b062ff01f358aac7ff741",
"srcChainCode": "0",
"destChainCode": "a202",
"srcAddress": "did:bid:efRyzZabnVCEbCm4ABNuYwyfSp1bEjwy",
"destAddress": "0xB2F001cB0a3561C497Acf9aD8879a3A9188a3c4a",
"remark": "I am from a202 for start main gas",
"result": "1",
"refunded": "0",
"lastUpdateSeqNum": "72680",
"payloadType": "0",
"payloadGas": {
"amount": "100000000"
},
"payloadContractCall": null,
"payloadSgas": null,
"payloadData": null,
"flowList": [
{
"chainCode": "0",
"extension": "extension",
"version": "1000",
"type": "0",
"txSender": "did:bid:efRyzZabnVCEbCm4ABNuYwyfSp1bEjwy",
"txHash": "abf729cc67bf3e5bc340729122215761827f136294ddc2183d92067b14814932",
"lastUpdateSeqNum": "72678",
"txErrorCode": "0",
"txTime": "1632297210000",
"ack": null
},
{
"chainCode": "0",
"extension": "",
"version": "1000",
"type": "2",
"txSender": "did:bid:ef3edP9uBW7TKR86T1ib2hMke7UPucqh",
"txHash": "c6fd27ffdc65512d2949fd8b8a7cdc6e51d4f254e88a5674da9ac25bf3babd1a",
"lastUpdateSeqNum": "72680",
"txErrorCode": "0",
"txTime": "1632297273000",
"ack": {
"ackResult": "1"
}
}
]
}
}
2.4.5 查询以太坊跨链交易详情
{
address: '0x717649243802CB60c347Eb76622B000F1Dd74F30',
blockHash: '0x399ab3442ae42b607b4f6c10a4ac332685864b03918760013a6492a22a395640',
blockNumber: 11083899,
logIndex: 1,
removed: false,
transactionHash: '0xf1523d044c042aaba3bf2001c99f054a65504a963c29d4310dd1c658a3680959',
transactionIndex: 2,
id: 'log_5a56c410',
returnValues: Result {
'0': 'sendTx',
'1': '0:a202:9742a46b697b062ff01f358aac7ff741',
'2': '0',
operation: 'sendTx',
cross_TxNo: '0:a202:9742a46b697b062ff01f358aac7ff741',
txType: '0'
},
event: 'sendTxEvent',
signature: '0xb03cbff5de7f38c07d0c4e60ffb200c548db20948f548306ffdc15e77f9f889e',
raw: {
data: '0x000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000673656e64547800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027303a613230323a393734326134366236393762303632666630316633353861616337666637343100000000000000000000000000000000000000000000000000',
topics: [
'0xb03cbff5de7f38c07d0c4e60ffb200c548db20948f548306ffdc15e77f9f889e'
]
}
}
1.星火主链的user转100000000主链积分给主链的跨链智能合约,并锁定资产在合约中;
2.网关节点通过星火·链网超级节点开放平台API轮询监听到主链上的跨链交易,并验证跨链交易的可靠性;
3.网关节点转发跨链交易到以太坊测试网,同时增发主链积分给以太坊测试网的目标user;
4.网关节点通过以太坊的sdk,监听到测试网上跨链交易,并验证跨链交易的可靠性;
5.网关节点给主链发送跨链交易反馈信息,同时主链的跨链合约将锁定得100000000主链积分转给网关节点;
6.网关节点监听到此跨链交易,并验证跨链交易的可靠性;
7.最终网关节点给以太坊测试网也发送跨链交易反馈信息。
- 接口:
function setChainCode(string memory chainCode) public onlyOwner {}
- 权限:合约的owner
- 功能:存储子链注册的AC码,该操作只有部署合约的owner能够调用。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
chainCode | string | 子链的AC码 | Y |
- 结果:若成功,则合约触发event,setChainCodeEvent("setChainCode",chainCode)。
- 接口:
function setGateway(address gatewayAddress) public onlyOwner {}
- 权限:合约的owner
- 功能:注册子链的网关节点地址,该操作只有部署合约的owner能够调用。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
gatewayAddress | address | 网关节点的地址 | Y |
- 结果:若成功,则合约触发event,setGatewayEvent("setGateway", gatewayAddress)。
- 接口:
function startTx(address srcAddress, string memory destBid, string memory srcChainCode, string memory destChainCode, uint8 txType, bytes memory payload, string memory extension,
string memory remark, string memory version) public payable {}
- 权限:子链内任意需要进行跨链操作的用户
- 功能:用户发起从子链到主链(或者其他子链)的交易,可以是主链积分转移,数据传递,合约互操作,子链积分兑换。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
srcAddress | address | 源地址 | Y |
destBid | string | 目标地址 | Y |
srcChainCode | string | 源链的链码 | Y |
destChainCode | string | 目标链的链码 | Y |
txType | uint | 交易类型,0为主链积分转移,1为子链积分兑换,2为数据传递,3为合约互操作; | Y |
payload | bytes | json字符串,根据交易类型定义对应的扩展信息,参照跨链类型定义; | Y |
extension | string | json字符串,用户扩展信息 | N |
remark | string | 备注信息 | N |
version | string | 版本信息,由原始用户发起交易携带,填写源链跨链合约的版本信息 | Y |
- 结果:若成功,则合约触发event,startTxEvent("startTx", crossTxNo)。
- 接口:
function sendTx(string memory srcBid, address payable destAddress, string memory srcChainCode, string memory destChainCode, uint8 txType, string memory crossTxNo,
bytes memory payload, string memory extension, string memory remark, string memory version, bytes memory proof) public payable {}
- 权限:只能由跨链网关进行调用
- 功能:跨链网关提交从主链或者其他子链到当前链的跨链交易。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
srcBid | string | 源地址 | Y |
destAddress | address | 目标地址 | Y |
srcChainCode | string | 源链的链码 | Y |
destChainCode | string | 目标链的链码 | Y |
txType | uint | 交易类型,0为主链积分转移,1为子链积分兑换,2为数据传递,3为合约互操作; | Y |
crossTxNo | string | 跨链交易编号 | Y |
payload | bytes | json字符串,根据交易类型定义对应的扩展信息,参照跨链类型定义; | Y |
extension | string | json字符串,用户扩展信息 | N |
remark | string | 备注信息 | N |
version | string | 版本信息,由原始用户发起交易携带,填写源链跨链合约的版本信息 | Y |
- 结果:若成功,则合约触发event,sendTxEvent("sendTx", crossTxNo)。
- 接口:
function sendAcked(string memory crossTxNo, TxResultEnum result, string memory version,
bytes memory proof) public {}
- 权限:只能由跨链网关进行调用
- 功能:跨链网关提交从主链或者其他子链到当前链的跨链交易确认,合约在该状态,实现解锁、转移、增发、销毁资产等。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
crossTxNo | string | 跨链交易编号 | Y |
result | uint | 跨链交易结果,0为成功,1为失败,2为超时 | Y |
extension | string | json字符串,用户扩展信息 | N |
version | string | 版本信息,由原始用户发起交易携带,填写源链跨链合约的版本信息 | Y |
- 结果:若成功,则合约触发event,sendAckedEvent("sendAckedTx", crossTxNo)。
- 接口:
function takeOut(string memory crossTxNo, address payable to) public {}
- 权限:发起跨链交易的用户
- 功能:用户在发起跨链交易异常(失败或者超时)后,可以主动取出资产。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
crossTxNo | string | 跨链交易编号 | Y |
toAddress | uint8 | 跨链交易结果,0为成功,1为失败,2为超时 | Y |
- 结果:若成功,则合约触发event,takeOutEvent("takeOut", crossTxNo)。
- 接口:
function approve(address from, address to, uint256 value) public {}
- 权限:子链用户
- 功能:授权账户 spender 可以从交易发送者账户转出数量为 value 的主链积分。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
from | address | 授权账户 | Y |
to | address | 被授权账户 | Y |
value | uint256 | 授权能够转移的主链积分数量 | Y |
- 结果:若成功,则合约触发event,approveEvent("approve", value)。
- 接口:
function transfer(address to, uint256 value) public {}
- 权限:子链用户
- 功能:转移 value 数量的主链积分到的地址 to。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
to | address | 接收地址 | Y |
value | uint256 | 转移的主链积分数量 | Y |
- 结果:若成功,则合约触发event,transferEvent("transfer", value)。
- 接口:
function getCrossTx(string memory crossTxNo) public view returns (bytes memory,bytes memory, TxResultEnum, TxRefundedEnum, TxOriginEnum, bytes memory, bytes memory,bytes memory) {}
- 权限:子链用户
- 功能:根据跨链交易的编号,查询跨链交易详情。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
crossTxNo | string | 跨链交易编号 | Y |
- 结果:若成功,返回跨链交易详情。
参数 | 参数类型 | 描述 |
---|---|---|
basicInfo | bytes | 跨链交易的SrcChainCode、DestChainCode、SrcBid、DestBid、TxType等信息 |
Payload | bytes | 跨链交易的扩展信息 |
Result | uint256 | 跨链交易结果,0为成功,1为失败,2为超时 |
Refunded | uint256 | 跨链交易的资产撤回信息 |
Origin | uint256 | 跨链交易的所处位置状态信息 |
sendProof | bytes | 用户发起跨链交易的区块号、交易哈希、验证人信息 |
ackProof | bytes | 网关节点转发跨链交易的区块号、交易哈希、验证人信息 |
extensionInfo | bytes | remark、extension、version信息 |
- 接口:
function balanceOf(address addr) public view returns (uint256) {}
- 权限:子链用户
- 功能:查询账户的主链积分数量。
- 参数:
参数 | 参数类型 | 描述 | 是否必须(Y/N) |
---|---|---|---|
addr | address | 带查询账户 | Y |
- 结果:若成功,返回账户主链积分数量。
- 接口:
function getVersion() public view returns (string memory) {}
- 权限:子链用户
- 功能:查询跨链合约的版本信息。
- 参数:无
- 结果:若成功,返回合约版本信息。