1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
| pragma solidity ^0.5.0; pragma experimental ABIEncoderV2;
import "./../../../libs/math/SafeMath.sol"; import "./../../../libs/common/ZeroCopySource.sol"; import "./../../../libs/common/ZeroCopySink.sol"; import "./../../../libs/utils/Utils.sol"; import "./../upgrade/UpgradableECCM.sol"; import "./../libs/EthCrossChainUtils.sol"; import "./../interface/IEthCrossChainManager.sol"; import "./../interface/IEthCrossChainData.sol"; contract EthCrossChainManager is IEthCrossChainManager, UpgradableECCM { using SafeMath for uint256; address public whiteLister; mapping(address => bool) public whiteListFromContract; //建立地址的白名单,以对应的地址为键,bool值代表该地址是否在白名单中 mapping(address => mapping(bytes => bool)) public whiteListContractMethodMap; //建立可调用函数的白名单。以调用的合约,调用的函数为键,bool值代表能否调用该函数
event InitGenesisBlockEvent(uint256 height, bytes rawHeader); event ChangeBookKeeperEvent(uint256 height, bytes rawHeader); event CrossChainEvent(address indexed sender, bytes txId, address proxyOrAssetContract, uint64 toChainId, bytes toContract, bytes rawdata); event VerifyHeaderAndExecuteTxEvent(uint64 fromChainID, bytes toContract, bytes crossChainTxHash, bytes fromChainTxHash); constructor( address _eccd, uint64 _chainId, address[] memory fromContractWhiteList, bytes[] memory contractMethodWhiteList ) UpgradableECCM(_eccd,_chainId) public { whiteLister = msg.sender; //将初始合约部署者设置为whiteLister for (uint i=0;i<fromContractWhiteList.length;i++) { whiteListFromContract[fromContractWhiteList[i]] = true; }// 初始部署的时候,建立对应的地址白名单。 for (uint i=0;i<contractMethodWhiteList.length;i++) { (address toContract,bytes[] memory methods) = abi.decode(contractMethodWhiteList[i],(address,bytes[])); //将对应的字节解码成对应合约中的函数,建立对应的可调用函数的白名单 for (uint j=0;j<methods.length;j++) { whiteListContractMethodMap[toContract][methods[j]] = true; } } } modifier onlyWhiteLister() { require(msg.sender == whiteLister, "Not whiteLister"); _; } //modifier onlyWhiteLister用来限制一些函数只有whiteLister能够调用
//只有whiteLister能够调用该函数 function setWhiteLister(address newWL) public onlyWhiteLister { require(newWL!=address(0), "Can not transfer to address(0)"); //判断对应的地址不为空 whiteLister = newWL; //将whiteLister设置为newWL。 } //只有whiteLister能够调用该函数 function setFromContractWhiteList(address[] memory fromContractWhiteList) public onlyWhiteLister { for (uint i=0;i<fromContractWhiteList.length;i++) { whiteListFromContract[fromContractWhiteList[i]] = true; } //将一些地址加入地址白名单中 } //只有whiteLister能够调用该函数 function removeFromContractWhiteList(address[] memory fromContractWhiteList) public onlyWhiteLister { for (uint i=0;i<fromContractWhiteList.length;i++) { whiteListFromContract[fromContractWhiteList[i]] = false; } //将一些地址从地址白名单之中移除 } //只有whiteLister能够调用该函数 function setContractMethodWhiteList(bytes[] memory contractMethodWhiteList) public onlyWhiteLister { for (uint i=0;i<contractMethodWhiteList.length;i++) { (address toContract,bytes[] memory methods) = abi.decode(contractMethodWhiteList[i],(address,bytes[])); //将对应的数据进行解码 for (uint j=0;j<methods.length;j++) { whiteListContractMethodMap[toContract][methods[j]] = true; } //将一些合约中的可调用函数加入白名单之中 } } //只有whiteLister能够调用该函数 function removeContractMethodWhiteList(bytes[] memory contractMethodWhiteList) public onlyWhiteLister { for (uint i=0;i<contractMethodWhiteList.length;i++) { (address toContract,bytes[] memory methods) = abi.decode(contractMethodWhiteList[i],(address,bytes[])); //将对应的数据进行解码 for (uint j=0;j<methods.length;j++) { whiteListContractMethodMap[toContract][methods[j]] = false; } //将一些合约中的可调用函数从白名单中移除 } }
/* @notice sync Poly chain genesis block header to smart contrat * @dev this function can only be called once, nextbookkeeper of rawHeader can't be empty * @param rawHeader Poly chain genesis block raw header or raw Header including switching consensus peers info */ //同步Poly Chain的原始区块头到CCD智能合约,该函数只能初始被调用一次,保存共识验证者公钥 function initGenesisBlock(bytes memory rawHeader, bytes memory pubKeyList) whenNotPaused public returns(bool) { IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress); //实例化CCD合约
require(eccd.getCurEpochConPubKeyBytes().length == 0, "EthCrossChainData contract has already been initialized!"); //判断CCD合约之前有无被初始化过,获取存储的共识验证者的公钥所对应的字节,若其长度为0,则说明CCD合约未被初始化 ECCUtils.Header memory header = ECCUtils.deserializeHeader(rawHeader); //将字节形式的rawHeader区块头,去序列化为header结构体 (bytes20 nextBookKeeper, address[] memory keepers) = ECCUtils.verifyPubkey(pubKeyList); require(header.nextBookkeeper == nextBookKeeper, "NextBookers illegal"); //从共识验证者的公钥,得到nextBookKeeper,与区块头中保存的nextBookKeeper进行对比,验证对应的公钥是否合法 //并计算出对应的共识验证者的地址keepers require(eccd.putCurEpochStartHeight(header.height), "Save Poly chain current epoch start height to Data contract failed!"); //记录当前epoch区块的起始高度,并要将其保存到CCD合约之中 require(eccd.putCurEpochConPubKeyBytes(ECCUtils.serializeKeepers(keepers)), "Save Poly chain current epoch book keepers to Data contract failed!"); //将共识验证者的公钥序列化为bytes形式,并将其保存到CCD合约之中
emit InitGenesisBlockEvent(header.height, rawHeader); //emit对应的事件,包含原始区块头的高度,和原始区块头的信息 return true; }
//改变CCD合约中保存的区块高度,共识验证者公钥对应的字节,并存储入CCD合约 function changeBookKeeper(bytes memory rawHeader, bytes memory pubKeyList, bytes memory sigList) whenNotPaused public returns(bool) { ECCUtils.Header memory header = ECCUtils.deserializeHeader(rawHeader); //将对应的区块头,解码为结构体形式的区块头Header IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress); //实例化对应的CCD合约
uint64 curEpochStartHeight = eccd.getCurEpochStartHeight(); require(header.height > curEpochStartHeight, "The height of header is lower than current epoch start height!"); //调用CCD合约getCurEpochStartHeight()函数,获取之前保存的区块高度 //require()用来确保传入的区块头对应的高度要高于对应CCD合约中保存的区块头高度
require(header.nextBookkeeper != bytes20(0), "The nextBookKeeper of header is empty"); //确保rawHeader是关键区块头,包含切换共识验证者的信息
address[] memory polyChainBKs = ECCUtils.deserializeKeepers(eccd.getCurEpochConPubKeyBytes()); //从CCD合约中获取保存的共识验证者公钥的字节,将字节解码为对应的共识验证者的地址 uint n = polyChainBKs.length; //得到对应的共识验证者的数量 require(ECCUtils.verifySig(rawHeader, sigList, polyChainBKs, n - (n - 1) / 3), "Verify signature failed!"); //poly chain上的区块是由共识验证者投票决定。 //调用函数,验证共识验证者的签名,签名者必须大于2/3共识验证者的数目,验证该区块头是否合法 // Convert pubKeyList into ethereum address format and make sure the compound address from the converted ethereum addresses // equals passed in header.nextBooker (bytes20 nextBookKeeper, address[] memory keepers) = ECCUtils.verifyPubkey(pubKeyList); require(header.nextBookkeeper == nextBookKeeper, "NextBookers illegal"); //从共识验证者的公钥,得到nextBookKeeper,与区块头中保存的nextBookKeeper进行对比,验证对应的公钥是否合法
require(eccd.putCurEpochStartHeight(header.height), "Save MC LatestHeight to Data contract failed!"); //将新的当前epoch的区块高度存入CCD合约之中 require(eccd.putCurEpochConPubKeyBytes(ECCUtils.serializeKeepers(keepers)), "Save Poly chain book keepers bytes to Data contract failed!"); //将新的共识验证者地址序列化,成对应的字节,并存入CCD合约之中 emit ChangeBookKeeperEvent(header.height, rawHeader); //emit对应的事件,表示以太坊上更改了Poly chain上的共识验证者地址 return true; }
//源链:ERC20代币跨链到其它链上,该函数将tx对应的event发布到区块链上 //输入的参数:目标链ID,目标链上的智能合约地址,目标链上准备调用的函数方法method,以及交易数据 function crossChain(uint64 toChainId, bytes calldata toContract, bytes calldata method, bytes calldata txData) whenNotPaused external returns (bool) { require(whiteListFromContract[msg.sender],"Invalid from contract"); //进行判断,只允许白名单中的合约地址能够调用该函数 IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress); //实例化对应的CCD合约 uint256 txHashIndex = eccd.getEthTxHashIndex(); //得到对应跨链交易哈希的index,用来区分两个交易 bytes memory paramTxHash = Utils.uint256ToBytes(txHashIndex); //将对应的uint256,转化为bytes形式,用于构造rawParam。
bytes memory rawParam = abi.encodePacked(ZeroCopySink.WriteVarBytes(paramTxHash), ZeroCopySink.WriteVarBytes(abi.encodePacked(sha256(abi.encodePacked(address(this), paramTxHash)))), ZeroCopySink.WriteVarBytes(Utils.addressToBytes(msg.sender)), ZeroCopySink.WriteUint64(toChainId), ZeroCopySink.WriteVarBytes(toContract), ZeroCopySink.WriteVarBytes(method), ZeroCopySink.WriteVarBytes(txData) ); //构造rawParam交易的数据,并将它的哈希保存,作为交易存在的证明 require(eccd.putEthTxHash(keccak256(rawParam)), "Save ethTxHash by index to Data contract failed!"); //将对应的交易信息取哈希,将其存入CCD合约中的映射
emit CrossChainEvent(tx.origin, paramTxHash, msg.sender, toChainId, toContract, rawParam); //emit对应的跨链事件,表示以太坊网络通过Poly Chain向其他公共链发送跨链请求 return true; } /* @notice Verify Poly chain header and proof, execute the cross chain tx from Poly chain to Ethereum * @param proof Poly chain tx merkle proof * @param rawHeader The header containing crossStateRoot to verify the above tx merkle proof * @param headerProof The header merkle proof used to verify rawHeader * @param curRawHeader Any header in current epoch consensus of Poly chain * @param headerSig The coverted signature veriable for solidity derived from Poly chain consensus nodes' signature * used to verify the validity of curRawHeader * @return true or false */ //目标链:验证Poly Chain上的区块头和对应的交易证明,在以太坊上执行来自Poly Chain的跨链交易 //输入:Poly Chain上的交易证明,包含验证poly chain上交易的crossStateRoot的区块头 //,,poly chain上的共识验证者的签名 function verifyHeaderAndExecuteTx(bytes memory proof, bytes memory rawHeader, bytes memory headerProof, bytes memory curRawHeader,bytes memory headerSig) whenNotPaused public returns (bool){ ECCUtils.Header memory header = ECCUtils.deserializeHeader(rawHeader); //将对应的rawHeader解码成对应的Header结构体 IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress); //实例化对应的CCD合约
address[] memory polyChainBKs = ECCUtils.deserializeKeepers(eccd.getCurEpochConPubKeyBytes()); //从CCD合约中获取保存的共识验证者公钥的字节,将字节解码为对应的共识验证者的地址 uint256 curEpochStartHeight = eccd.getCurEpochStartHeight(); //从CCD合约中获取保存的区块高度。
uint n = polyChainBKs.length; //得到共识验证者的数量 if (header.height >= curEpochStartHeight) { //如果跨链交易区块高度大于CCD中保存的区块高度,说明两者是在一个epoch中,直接验证交易区块头的签名 // It's enough to verify rawHeader signature require(ECCUtils.verifySig(rawHeader, headerSig, polyChainBKs, n - ( n - 1) / 3), "Verify poly chain header signature failed!"); //验证包含跨链交易的rawHeader,是否经过了poly chain上的共识验证者签名 } else { // We need to verify the signature of curHeader require(ECCUtils.verifySig(curRawHeader, headerSig, polyChainBKs, n - ( n - 1) / 3), "Verify poly chain current epoch header signature failed!"); //验证poly chain上当前epoch的区块头是否经过了共识验证者的签名
// Then use curHeader.StateRoot and headerProof to verify rawHeader.CrossStateRoot ECCUtils.Header memory curHeader = ECCUtils.deserializeHeader(curRawHeader); //解码出poly chain上当前epoch区块头的结构体信息 bytes memory proveValue = ECCUtils.merkleProve(headerProof, curHeader.blockRoot); //通过headerProof,验证rawHeader区块头是否为合法区块头。 require(ECCUtils.getHeaderHash(rawHeader) == Utils.bytesToBytes32(proveValue), "verify header proof failed!"); } // Through rawHeader.CrossStatesRoot, the toMerkleValue or cross chain msg can be verified and parsed from proof bytes memory toMerkleValueBs = ECCUtils.merkleProve(proof, header.crossStatesRoot); //验证poly chain上包含的跨链交易,根据proof解析出包含的跨链信息toMerkleValueBs ECCUtils.ToMerkleValue memory toMerkleValue = ECCUtils.deserializeMerkleValue(toMerkleValueBs); //解析字节形式的toMerkleValueBs为对应的结构体 require(!eccd.checkIfFromChainTxExist(toMerkleValue.fromChainID, Utils.bytesToBytes32(toMerkleValue.txHash)), "the transaction has been executed!"); //require()调用CCD合约checkIfFromChainTxExist()函数来,根据chainID和交易哈希判断该交易是否已经处理过 require(eccd.markFromChainTxExist(toMerkleValue.fromChainID, Utils.bytesToBytes32(toMerkleValue.txHash)), "Save crosschain tx exist failed!"); //require()调用CCD合约markFromChainTxExist()函数,根据chainID和交易哈希标记该交易已经处理
require(toMerkleValue.makeTxParam.toChainId == chainId, "This Tx is not aiming at this network!"); //检查交易中保存的toChainID是否为以太坊
address toContract = Utils.bytesToAddress(toMerkleValue.makeTxParam.toContract); //获取目标合约,并将其转换为地址,以便CCM合约触发跨链交易tx在以太坊上执行
require(whiteListContractMethodMap[toContract][toMerkleValue.makeTxParam.method],"Invalid to contract or method"); //判断交易调用的合约,和对应的函数是否保存在对应的白名单之中
require(_executeCrossChainTx(toContract, toMerkleValue.makeTxParam.method, toMerkleValue.makeTxParam.args, toMerkleValue.makeTxParam.fromContract, toMerkleValue.fromChainID), "Execute CrossChain Tx failed!"); //执行对应的跨链函数
emit VerifyHeaderAndExecuteTxEvent(toMerkleValue.fromChainID, toMerkleValue.makeTxParam.toContract, toMerkleValue.txHash, toMerkleValue.makeTxParam.txHash); //emit 对应事件,表示从其它公链到以太坊这样的跨链交易成功执行 return true; }
//调用对应的目标合约,触发以太坊上跨链交易的执行 //输入:调用的合约的地址,调用的函数,输入的参数,源链上智能合约的地址,源链的chainID function _executeCrossChainTx(address _toContract, bytes memory _method, bytes memory _args, bytes memory _fromContractAddr, uint64 _fromChainId) internal returns (bool){ require(Utils.isContract(_toContract), "The passed in address is not a contract!"); //确保将要调用的_toContract是一个合约,而不是一个账户地址 bytes memory returnData; bool success;
(success, returnData) = _toContract.call(abi.encodePacked(bytes4(keccak256(abi.encodePacked(_method, "(bytes,bytes,uint64)"))), abi.encode(_args, _fromContractAddr, _fromChainId))); //首先将_method和输入参数的格式“(bytes,bytes,uint64)”进行encodePacked编码 //使用keccak256计算编码字符的哈希,并取前四个字节。 //将哈希的前四个字节,和encode编码的三个参数,一起进行encodePacked编码,作为一个函数调用 require(success == true, "EthCrossChain call business contract failed"); //确保对应函数的调用成功执行
require(returnData.length != 0, "No return value from business contract!"); (bool res,) = ZeroCopySource.NextBool(returnData, 31); require(res == true, "EthCrossChain call business contract return is not true"); //调用方法后,检查对应的返回值,调用成功,returnData将是bytes32类型,并且最后一个字节为01. //只有返回值为真,整个跨链交易才会执行成功 return true; } }
|