Poly Network以太坊智能合约介绍 (eth-contracts)

跨链智能合约介绍

跨链合约主要分为分为逻辑(logic)合约,数据(data)合约和代理(proxy)合约

  • Cross Chain Manager Contract:CCM合约对应的是EthCrossChainManager.sol

  • Cross Chain Data Contract:CCD合约对应的是EthCrossChainData.sol

  • Cross Chain Manager Proxy Contract:CCMP合约对应的是EthCrossChainManagerProxy.sol

  • Business Logic Contract:主要对应的是LoxyProxy.sol
    在这里插入图片描述

跨链智能合约代码具体分析

Business Logic Contract:

LockProxy.sol

代码github位置:polynetwork/eth-contracts/contracts/core/lock_proxy/LockProxy.sol

https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/lock_proxy/LockProxy.sol

pragma solidity ^0.5.0;
import "./../../libs/ownership/Ownable.sol";
import "./../../libs/common/ZeroCopySource.sol";
import "./../../libs/common/ZeroCopySink.sol";
import "./../../libs/utils/Utils.sol";
import "./../../libs/token/ERC20/SafeERC20.sol";
import "./../cross_chain_manager/interface/IEthCrossChainManager.sol";
import "./../cross_chain_manager/interface/IEthCrossChainManagerProxy.sol";
contract LockProxy is Ownable {
    using SafeMath for uint;         //包含基本算数运算,以防止溢出
    using SafeERC20 for IERC20;

    struct TxArgs {
        bytes toAssetHash;        //目标链上的资产哈希
        bytes toAddress;         //目标链上接收对应代币的字节格式的地址
        uint256 amount;          //交易数额
    }
    address public managerProxyContract;    //CCMP合约的地址
    mapping(uint64 => bytes) public proxyHashMap;
    //存储链与链之间的CCM合约之间的映射关系
    //以目标链ID(ChainID)为键,CCM合约地址哈希为值。
    mapping(address => mapping(uint64 => bytes)) public assetHashMap;
    //存储链与链之间的资产映射关系.
    //以源链资产哈希(fromAssetHash)和目标链ID(toChainID)为键,目标资产哈希(toAssetHash)为值。
    mapping(address => bool) safeTransfer;

    event SetManagerProxyEvent(address manager);
    event BindProxyEvent(uint64 toChainId, bytes targetProxyHash);
    event BindAssetEvent(address fromAssetHash, uint64 toChainId, bytes targetProxyHash, uint initialAmount);
    event UnlockEvent(address toAssetHash, address toAddress, uint256 amount);
    event LockEvent(address fromAssetHash, address fromAddress, uint64 toChainId, bytes toAssetHash, bytes toAddress, uint256 amount);
    
    modifier onlyManagerContract() {
        IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract);
        require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract");
        _;
    }
    //getEthCrossChainManager()函数得到跨链管理合约的地址,require()的判断用来确保函数的调用者,是跨链管理合约。
	//Unlock()函数中用到,用来确实对应的资产解锁操作,只能由跨链管理合约发出
    
    function setManagerProxy(address ethCCMProxyAddr) onlyOwner public {
        managerProxyContract = ethCCMProxyAddr;
        emit SetManagerProxyEvent(managerProxyContract);
    }
    //CCMP合约部署之后,将CCMP合约的地址赋值给managerProxyContract
    
    function bindProxyHash(uint64 toChainId, bytes memory targetProxyHash) onlyOwner public returns (bool) {
        proxyHashMap[toChainId] = targetProxyHash;
        emit BindProxyEvent(toChainId, targetProxyHash);
        return true;
    }
    //存储链与链之间的CCM合约之间的映射关系
    //以目标链ID(ChainID)为键,CCM合约哈希为值。
    
    function bindAssetHash(address fromAssetHash, uint64 toChainId, bytes memory toAssetHash) onlyOwner public returns (bool) {
        assetHashMap[fromAssetHash][toChainId] = toAssetHash;
        emit BindAssetEvent(fromAssetHash, toChainId, toAssetHash, getBalanceFor(fromAssetHash));
        return true;
    }
    //存储链与链之间的资产映射关系.
    //以源链资产哈希(fromAssetHash)和目标链ID(toChainID)为键,目标资产哈希(toAssetHash)为值。
    
	//lock()函数应该由用户调用,特定数量的代币将会被锁定,ChainID目标链上的代理合约将会解锁对应的代币
    function lock(address fromAssetHash, uint64 toChainId, bytes memory toAddress, uint256 amount) public payable returns (bool) {
        require(amount != 0, "amount cannot be zero!");     //判断锁定金额不能为0
        
        require(_transferToContract(fromAssetHash, amount), "transfer asset from fromAddress to lock_proxy contract  failed!");
        //将对应amount数量的资产转移到LockProxy合约中
        bytes memory toAssetHash = assetHashMap[fromAssetHash][toChainId]; //得到目标链上的资产哈希
        require(toAssetHash.length != 0, "empty illegal toAssetHash");  
        //require()验证确保目标链上的资产哈希不为0
        TxArgs memory txArgs = TxArgs({
            toAssetHash: toAssetHash,
            toAddress: toAddress,
            amount: amount
        });    //构造对应的交易参数
        bytes memory txData = _serializeTxArgs(txArgs);
        //将对应的交易参数结构体,序列化成为对应的字节数据
        IEthCrossChainManagerProxy eccmp = IEthCrossChainManagerProxy(managerProxyContract);
        address eccmAddr = eccmp.getEthCrossChainManager();
        //得到对应的CCM合约的地址
        IEthCrossChainManager eccm = IEthCrossChainManager(eccmAddr);  //实例化CCM合约
        
        bytes memory toProxyHash = proxyHashMap[toChainId];     //得到bytes形式的CCM合约的地址
        require(toProxyHash.length != 0, "empty illegal toProxyHash");   //确保对应bytes形式的CCM合约地址是合法的
        require(eccm.crossChain(toChainId, toProxyHash, "unlock", txData), "EthCrossChainManager crossChain executed error!");
		//调用对应的CCM跨链管理合约的跨链函数
        emit LockEvent(fromAssetHash, _msgSender(), toChainId, toAssetHash, toAddress, amount);
        //锁定完成,emit对应的LockEvent。
        return true;

    }

    //modifier onlyManagerContract限制该函数只能CCM合约来调用,铸造一定数量的代币到指定的地址
    //输入序列化的对应交易数据,源链合约地址,源链ID
    function unlock(bytes memory argsBs, bytes memory fromContractAddr, uint64 fromChainId) onlyManagerContract public returns (bool) {
        TxArgs memory args = _deserializeTxArgs(argsBs);    //反序列化对应的字节数据为交易参数结构体
        require(fromContractAddr.length != 0, "from proxy contract address cannot be empty");  
        //检查确保源链的CCM合约地址不为空
        require(Utils.equalStorage(proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error!");
   //Utils.equalStorage()函数用来比较两个字节是否相等,验证传入源链的CCM合约地址哈希是否和proxyHashMap mapping存储的一样
        require(args.toAssetHash.length != 0, "toAssetHash cannot be empty");
        //确保目标链资产地址哈希不为空
        address toAssetHash = Utils.bytesToAddress(args.toAssetHash);
		//将目标链资产地址哈希,取十六进制字符串的后40位,得到目标链资产地址
		
        require(args.toAddress.length != 0, "toAddress cannot be empty");  //确保目标链上的收款地址不为空
        address toAddress = Utils.bytesToAddress(args.toAddress);
        //与上面类似,将对应的字节哈希,转换为目标链上的收款地址
        
        require(_transferFromContract(toAssetHash, toAddress, args.amount), "transfer asset from lock_proxy contract to toAddress failed!");
        //将对应amount数量的资产从LockProxy合约转移到用户的toAddress中
        emit UnlockEvent(toAssetHash, toAddress, args.amount);
        //完成对应的铸造,emit对应的UnlockEvent事件
        return true;
    }
    
    //得到对应的账户余额
    function getBalanceFor(address fromAssetHash) public view returns (uint256) {
        if (fromAssetHash == address(0)) {
            // return address(this).balance; // this expression would result in error: Failed to decode output: Error: insufficient data for uint256 type
            //如果对应的源链资产哈希为空的话,证明不存在对应的代币代理合约,直接返回当前LockProxy合约的余额。
            address selfAddr = address(this);
            return selfAddr.balance;
        } else {
        	//否则的话,根据资产地址哈希实例化对应的ERC20代币,
            IERC20 erc20Token = IERC20(fromAssetHash);
            return erc20Token.balanceOf(address(this));  //返回当前合约账户的对应的ERC20代币余额
        }
    }
    
    //将用户对应的资产地址哈希,从fromAddress转移到对应的LockProxy合约中
    function _transferToContract(address fromAssetHash, uint256 amount) internal returns (bool) {
        if (fromAssetHash == address(0)) {
            // fromAssetHash === address(0) denotes user choose to lock ether
            // passively check if the received msg.value equals amount
            //当对应的资产地址哈希为空的时候,代表用户转移的不是ERC20代币,是ether。
            require(msg.value != 0, "transferred ether cannot be zero!");
            require(msg.value == amount, "transferred ether is not equal to amount!");
            //检查对应的转移的以太数量是否为对应的amount
        } else {
            // make sure lockproxy contract will decline any received ether
            require(msg.value == 0, "there should be no ether transfer!");  //这里不应该有以太的转移
            // actively transfer amount of asset from msg.sender to lock_proxy contract
            require(_transferERC20ToContract(fromAssetHash, _msgSender(), address(this), amount), "transfer erc20 asset to lock_proxy contract failed!");
            //调用函数_transferERC20ToContract()
        }
        return true;
    }
    function _transferFromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
        if (toAssetHash == address(0x0000000000000000000000000000000000000000)) {
            // toAssetHash === address(0) denotes contract needs to unlock ether to toAddress
            // convert toAddress from 'address' type to 'address payable' type, then actively transfer ether
            address(uint160(toAddress)).transfer(amount);
            //toAssetHash资产地址为空,表明合约需要解锁以太到toAddress中
        } else {
            // actively transfer amount of asset from lock_proxy contract to toAddress
            require(_transferERC20FromContract(toAssetHash, toAddress, amount), "transfer erc20 asset from lock_proxy contract to toAddress failed!");
            //调用函数_transferERC20FromContract()
        }
        return true;
    }
    
    //将对应数量的ERC20代币转移到LockProxy合约中
    function _transferERC20ToContract(address fromAssetHash, address fromAddress, address toAddress, uint256 amount) internal returns (bool) {
         IERC20 erc20Token = IERC20(fromAssetHash);
         //根据源链的资产地址哈希,实例化对应的ERC20代币,从用户的账户地址转移amount数量代币到LockProxy合约中
        //  require(erc20Token.transferFrom(fromAddress, toAddress, amount), "trasnfer ERC20 Token failed!");
         erc20Token.safeTransferFrom(fromAddress, toAddress, amount);
         return true;
    }
    
    //转移对应的amount数量的ERC20代币到对应的toAddress账户中
    function _transferERC20FromContract(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
         IERC20 erc20Token = IERC20(toAssetHash);
         //根据目标链的资产地址哈希,实例化对应的ERC20代币,从LockProxy合约中转移amount数量代币到toAddress中
        //  require(erc20Token.transfer(toAddress, amount), "trasnfer ERC20 Token failed!");
         erc20Token.safeTransfer(toAddress, amount);
         return true;
    }
    
    function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) {
        bytes memory buff;
        buff = abi.encodePacked(
            ZeroCopySink.WriteVarBytes(args.toAssetHash),
            ZeroCopySink.WriteVarBytes(args.toAddress),
            ZeroCopySink.WriteUint255(args.amount)
            );
        return buff;
    }
	//将对应的交易参数结构体,编码序列化成对应的字节数据
    function _deserializeTxArgs(bytes memory valueBs) internal pure returns (TxArgs memory) {
        TxArgs memory args;
        uint256 off = 0;
        (args.toAssetHash, off) = ZeroCopySource.NextVarBytes(valueBs, off);
        (args.toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off);
        (args.amount, off) = ZeroCopySource.NextUint255(valueBs, off);
        return args;
    }
    //将对应的字节数据,解码反序列化成对应的交易参数结构体
}

Cross Chain Manager Contract:

EthCrossChainManager.sol

代码github位置:polynetwork/eth-contracts/contracts/core/cross_chain_manager/logic/EthCrossChainManager.sol

https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/logic/EthCrossChainManager.sol

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

Cross Chain Manager Proxy Contract:

EthCrossChainManagerProxy.sol

代码github位置:polynetwork/eth-contracts/contracts/core/cross_chain_manager/upgrade/EthCrossChainManagerProxy.sol

https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/upgrade/EthCrossChainManagerProxy.sol

pragma solidity ^0.5.0;
import "./../../../libs/ownership/Ownable.sol";
import "./../../../libs/lifecycle/Pausable.sol";
import "./../interface/IUpgradableECCM.sol";
import "./../interface/IEthCrossChainManagerProxy.sol";

contract EthCrossChainManagerProxy is IEthCrossChainManagerProxy, Ownable, Pausable {
    address private EthCrossChainManagerAddr_;   //对应的跨链管理合约的地址 
    
    constructor(address _ethCrossChainManagerAddr) public {
        EthCrossChainManagerAddr_ = _ethCrossChainManagerAddr;
    }  //constructor()在合约部署的时候,设置跨链管理合约的地址
    
    //调用者账户pause()函数,触发合约到对应的暂停状态
    function pause() onlyOwner public returns (bool) {
        if (paused()) {
            return true;
        }
        _pause();
        return true;
    }
    //调用者账户unpause()函数,恢复合约到对应的正常状态
    function unpause() onlyOwner public returns (bool) {
        if (!paused()) {
            return true;
        }
        _unpause();
        return true;
    }
    //触发对应的跨链管理合约为暂停状态
    function pauseEthCrossChainManager() onlyOwner whenNotPaused public returns (bool) {
        IUpgradableECCM eccm = IUpgradableECCM(EthCrossChainManagerAddr_);
        require(pause(), "pause EthCrossChainManagerProxy contract failed!");
        //将CCMP合约设置为暂停状态
        require(eccm.pause(), "pause EthCrossChainManager contract failed!");
        //将CCM合约设置为暂停状态
    }
    
    //更新跨链管理合约CCM合约的地址
    function upgradeEthCrossChainManager(address _newEthCrossChainManagerAddr) onlyOwner whenPaused public returns (bool) {
        IUpgradableECCM eccm = IUpgradableECCM(EthCrossChainManagerAddr_);
        if (!eccm.paused()) {
            require(eccm.pause(), "Pause old EthCrossChainManager contract failed!");
        }
        //更新CCM合约之前,先将CCM合约的状态设置为暂停状态。
        require(eccm.upgradeToNew(_newEthCrossChainManagerAddr), "EthCrossChainManager upgradeToNew failed!");
        //调用该函数,将对应的modifier中的owner设置为新的跨链管理合约的地址
        IUpgradableECCM neweccm = IUpgradableECCM(_newEthCrossChainManagerAddr);
        require(neweccm.isOwner(), "EthCrossChainManagerProxy is not owner of new EthCrossChainManager contract");
        EthCrossChainManagerAddr_ = _newEthCrossChainManagerAddr;
    }
    
    //将对应的跨链管理合约恢复为正常状态
    function unpauseEthCrossChainManager() onlyOwner whenPaused public returns (bool) {
        IUpgradableECCM eccm = IUpgradableECCM(EthCrossChainManagerAddr_);
        require(eccm.unpause(), "unpause EthCrossChainManager contract failed!");
        //将CCM合约恢复为正常状态
        require(unpause(), "unpause EthCrossChainManagerProxy contract failed!");
        //将CCMP合约恢复为正常状态
    }
    
    //调用该函数获得CCM合约的地址
    function getEthCrossChainManager() whenNotPaused public view returns (address) {
        return EthCrossChainManagerAddr_;
    }
    
    //改变区块链的链ID
    function changeManagerChainID(uint64 _newChainId) onlyOwner whenPaused public {
        IUpgradableECCM eccm = IUpgradableECCM(EthCrossChainManagerAddr_);
        if (!eccm.paused()) {
            require(eccm.pause(), "Pause old EthCrossChainManager contract failed!");
        }
        //在修改链ID之前,将对应的CCM跨链管理合约触发为暂停状态
        require(eccm.setChainId(_newChainId), "set chain ID failed. ");
    }
}

Cross Chain Data Contract:

EthCorssChainData.sol

代码github位置:polynetwork/eth-contracts/contracts/core/cross_chain_manager/data/EthCrossChainData.sol

https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/data/EthCrossChainData.sol

pragma solidity ^0.5.0;
import "./../../../libs/ownership/Ownable.sol";
import "./../../../libs/lifecycle/Pausable.sol";
import "./../interface/IEthCrossChainData.sol";
contract EthCrossChainData is IEthCrossChainData, Ownable, Pausable{
    //该映射存储以太坊发起的跨链交易的哈希值,以自增的index为键,对应的跨链交易哈希为值
    //该映射是为了Poly Chain可以验证来自以太坊的跨链交易请求tx的存在
    mapping(uint256 => bytes32) public EthToPolyTxHashMap;
    
    // uint256 index记录着当前映射mapping的长度
    uint256 public EthToPolyTxHashIndex;
    
    //当Poly Chain更改共识验证者的时候,poly chain的共识验证者的公钥需要转换成bytes形式,
    //以便智能合约将其转换为地址类型,并验证Poly chain账户签名衍生的签名
    bytes public ConKeepersPkBytes;

    //记录着poly chain区块上当前epoch的起始高度
    uint32 public CurEpochStartHeight;
    
    //以链的chainID,对应的跨链交易bytes为键,映射的值--表示该交易是否已经被处理
    mapping(uint64 => mapping(bytes32 => bool)) FromChainTxExist;
    
    // 未用到,未来的潜在使用
    mapping(bytes32 => mapping(bytes32 => bytes)) public ExtraData;
    
    // 存储poly chain区块上当前epoch的起始高度
    function putCurEpochStartHeight(uint32 curEpochStartHeight) public whenNotPaused onlyOwner returns (bool) {
        CurEpochStartHeight = curEpochStartHeight;
        return true;
    }

    // 获得之前存储的poly chain区块上的epoch的起始高度
    function getCurEpochStartHeight() public view returns (uint32) {
        return CurEpochStartHeight;
    }

    //存储共识验证者的公钥对应的字节
    function putCurEpochConPubKeyBytes(bytes memory curEpochPkBytes) public whenNotPaused onlyOwner returns (bool) {
        ConKeepersPkBytes = curEpochPkBytes;
        return true;
    }

    // 获得之前存储的共识验证者的公钥所对应的字节
    function getCurEpochConPubKeyBytes() public view returns (bytes memory) {
        return ConKeepersPkBytes;
    }

    // 标记来自源链chainID,对应的bytes32 tx已经被处理,modifier onlyOwner显示只有对应的owner才能调用该函数
    function markFromChainTxExist(uint64 fromChainId, bytes32 fromChainTx) public whenNotPaused onlyOwner returns (bool) {
        FromChainTxExist[fromChainId][fromChainTx] = true;
        return true;
    }

    //判断来自fromchainID的交易fromchaintx是否已经被处理
    function checkIfFromChainTxExist(uint64 fromChainId, bytes32 fromChainTx) public view returns (bool) {
        return FromChainTxExist[fromChainId][fromChainTx];
    }

    //获取当前记录的跨链请求tx的索引,该txs是记录从以太坊到其它公共链的跨链请求
    //以帮助CCM合约来区分两个跨链tx的请求
    function getEthTxHashIndex() public view returns (uint256) {
        return EthToPolyTxHashIndex;
    }

    //保存以太坊上的跨链交易tx的哈希,将对应记录的index自增1
    function putEthTxHash(bytes32 ethTxHash) public whenNotPaused onlyOwner returns (bool) {
        EthToPolyTxHashMap[EthToPolyTxHashIndex] = ethTxHash;
        EthToPolyTxHashIndex = EthToPolyTxHashIndex + 1;
        return true;
    }

    //根据对应的ethTxHashIndex索引来获得对应的以太坊上的跨链交易的哈希
    function getEthTxHash(uint256 ethTxHashIndex) public view returns (bytes32) {
        return EthToPolyTxHashMap[ethTxHashIndex];
    }

    // extra data的存储函数, 可能未来使用
    function putExtraData(bytes32 key1, bytes32 key2, bytes memory value) public whenNotPaused onlyOwner returns (bool) {
        ExtraData[key1][key2] = value;
        return true;
    }
    // extra data的读取函数,可能未来使用
    function getExtraData(bytes32 key1, bytes32 key2) public view returns (bytes memory) {
        return ExtraData[key1][key2];
    }
    
    //调用pause()函数,触发将对应的合约设置为停止状态
    function pause() onlyOwner whenNotPaused public returns (bool) {
        _pause();
        return true;
    }
    
    //调用unpause()函数,将对应的合约设置为恢复
    function unpause() onlyOwner whenPaused public returns (bool) {
        _unpause();
        return true;
    }
}

其它关键合约

EthCrossChainUtils.sol

代码github位置:polynetwork/eth-contracts/contracts/core/cross_chain_manager/libs/EthCrossChainUtils.sol

https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/libs/EthCrossChainUtils.sol

pragma solidity ^0.5.0;
import "./../../../libs/common/ZeroCopySource.sol";
import "./../../../libs/common/ZeroCopySink.sol";
import "./../../../libs/utils/Utils.sol";
import "./../../../libs/math/SafeMath.sol";
library ECCUtils {
    using SafeMath for uint256;       //基本的算术运算,防止溢出
    struct Header {
        uint32 version;
        uint64 chainId;
        uint32 timestamp;
        uint32 height;
        uint64 consensusData;
        bytes32 prevBlockHash;
        bytes32 transactionsRoot;
        bytes32 crossStatesRoot;
        bytes32 blockRoot;
        bytes consensusPayload;
        bytes20 nextBookkeeper;
    }       
    struct ToMerkleValue {
        bytes  txHash;  // cross chain txhash
        uint64 fromChainID;
        TxParam makeTxParam;
    }
    struct TxParam {
        bytes txHash; //  source chain txhash
        bytes crossChainId;
        bytes fromContract;
        uint64 toChainId;
        bytes toContract;
        bytes method;
        bytes args;
    }
    uint constant POLYCHAIN_PUBKEY_LEN = 67;
    uint constant POLYCHAIN_SIGNATURE_LEN = 65;

    /* @notice                  Verify Poly chain transaction whether exist or not
    *  @param _auditPath        Poly chain merkle proof
    *  @param _root             Poly chain root
    *  @return                  The verified value included in _auditPath
    */
    //进行merkle proof的验证,验证对应的交易是否合法,proof中包含对应的跨链消息,解码返回
    function merkleProve(bytes memory _auditPath, bytes32 _root) internal pure returns (bytes memory) {
        uint256 off = 0;
        bytes memory value;
        (value, off)  = ZeroCopySource.NextVarBytes(_auditPath, off);
        bytes32 hash = Utils.hashLeaf(value);
        //从_auditPath中读取得到树的根哈希值
        
        uint size = _auditPath.length.sub(off).div(33);
        //得到对应的节点数量,每个节点33个字节,第一个字节代表左右节点,32字节哈希
        bytes32 nodeHash;
        byte pos;
        for (uint i = 0; i < size; i++) {
            (pos, off) = ZeroCopySource.NextByte(_auditPath, off);
            //读取每个节点的第一个字节进行判断
            (nodeHash, off) = ZeroCopySource.NextHash(_auditPath, off);
            //获取每个节点的节点哈希
            if (pos == 0x00) {
                hash = Utils.hashChildren(nodeHash, hash);
            } else if (pos == 0x01) {
                hash = Utils.hashChildren(hash, nodeHash);
            } else {
                revert("merkleProve, NextByte for position info failed");
                节点第一个字节不符合对应格式,报错
            }
            //判断节点为对应的左节点还是右结点,分别计算对应的哈希
        }
        require(hash == _root, "merkleProve, expect root is not equal actual root");
        //将对应根据路径计算出的哈希值,和保存的哈希值进行比较,判断对应根哈希是否正确
        return value;
    }

    /* @notice              calculate next book keeper according to public key list
    *  @param _keyLen       consensus node number
    *  @param _m            minimum signature number
    *  @param _pubKeyList   consensus node public key list
    *  @return              two element: next book keeper, consensus node signer addresses
    */
    //根据共识验证者的公钥来计算出下一代的共识验证者
    //输入:共识验证者的数量,最少的签名数量,共识验证者的公钥
    //输出:bytes下一代共识验证者,以及共识验证者的地址
    function _getBookKeeper(uint _keyLen, uint _m, bytes memory _pubKeyList) internal pure returns (bytes20, address[] memory){
         bytes memory buff;
         buff = ZeroCopySink.WriteUint16(uint16(_keyLen));
         address[] memory keepers = new address[](_keyLen);
         bytes32 hash;
         bytes memory publicKey;
         for(uint i = 0; i < _keyLen; i++){
             publicKey = Utils.slice(_pubKeyList, i*POLYCHAIN_PUBKEY_LEN, POLYCHAIN_PUBKEY_LEN);
             //得到每个共识验证者对应的公钥
             buff =  abi.encodePacked(buff, ZeroCopySink.WriteVarBytes(Utils.compressMCPubKey(publicKey)));
             hash = keccak256(Utils.slice(publicKey, 3, 64));
             keepers[i] = address(uint160(uint256(hash)));
             //将公钥进行哈希,获得共识验证者的地址
         }

         buff = abi.encodePacked(buff, ZeroCopySink.WriteUint16(uint16(_m)));
         bytes20  nextBookKeeper = ripemd160(abi.encodePacked(sha256(buff)));
         return (nextBookKeeper, keepers);
    }

    /* @notice              Verify public key derived from Poly chain
    *  @param _pubKeyList   serialized consensus node public key list
    *  @param _sigList      consensus node signature list
    *  @return              return two element: next book keeper, consensus node signer addresses
    */
    //验证来自poly chain的公钥   输入是序列化的共识验证者公钥
    //返回的是下一代的共识验证者,以及对应共识验证者的地址。
    function verifyPubkey(bytes memory _pubKeyList) internal pure returns (bytes20, address[] memory) {
        require(_pubKeyList.length % POLYCHAIN_PUBKEY_LEN == 0, "_pubKeyList length illegal!");
        //确保公钥长度能够整除单个polychain上的公钥长度,确保公钥长度合法
        uint n = _pubKeyList.length / POLYCHAIN_PUBKEY_LEN;  //得到对应的公钥数量
        require(n >= 1, "too short _pubKeyList!");     //验证确保公钥的数量大于1
        return _getBookKeeper(n, n - (n - 1) / 3, _pubKeyList);
        //调用_getBookKeeper()获得共识验证者的地址
    }

    /* @notice              Verify Poly chain consensus node signature
    *  @param _rawHeader    Poly chain block header raw bytes
    *  @param _sigList      consensus node signature list
    *  @param _keepers      addresses corresponding with Poly chain book keepers' public keys
    *  @param _m            minimum signature number
    *  @return              true or false
    */
    //验证poly chain上的共识验证者的签名,判断某个交易是否经过poly chain的验证
    //输入:poly chain上的区块头数据,共识验证者的签名,共识验证者地址,最少需要有的签名数量
    function verifySig(bytes memory _rawHeader, bytes memory _sigList, address[] memory _keepers, uint _m) internal pure returns (bool){
        bytes32 hash = getHeaderHash(_rawHeader);
		
        uint sigCount = _sigList.length.div(POLYCHAIN_SIGNATURE_LEN);
        address[] memory signers = new address[](sigCount);
        bytes32 r;
        bytes32 s;
        uint8 v;
        for(uint j = 0; j  < sigCount; j++){
            r = Utils.bytesToBytes32(Utils.slice(_sigList, j*POLYCHAIN_SIGNATURE_LEN, 32));
            s =  Utils.bytesToBytes32(Utils.slice(_sigList, j*POLYCHAIN_SIGNATURE_LEN + 32, 32));
            v =  uint8(_sigList[j*POLYCHAIN_SIGNATURE_LEN + 64]) + 27;
            signers[j] =  ecrecover(sha256(abi.encodePacked(hash)), v, r, s);
            if (signers[j] == address(0)) return false;
        }
        //利用共识验证者的签名,调用ecrecover()函数计算出签名者的地址,进行地址的验证
        return Utils.containMAddresses(_keepers, signers, _m);
		//调用Utils中的containMAddresses函数
		//来判断区块头中计算出的签名者公钥,和CCD保存的签名者公钥,是否有_m个签名,验证是否通过
    }

    //将对应的poly chain上的共识验证者的地址,编码为对应的bytes字节形式
    function serializeKeepers(address[] memory keepers) internal pure returns (bytes memory) {
        uint256 keeperLen = keepers.length;
        bytes memory keepersBytes = ZeroCopySink.WriteUint64(uint64(keeperLen));
        for(uint i = 0; i < keeperLen; i++) {
            keepersBytes = abi.encodePacked(keepersBytes, ZeroCopySink.WriteVarBytes(Utils.addressToBytes(keepers[i])));
        }
        return keepersBytes;
    }

    //将poly chain上的字节bytes形式保存的共识验证者地址,解码为address形式
    function deserializeKeepers(bytes memory keepersBytes) internal pure returns (address[] memory) {
        uint256 off = 0;
        uint64 keeperLen;
        (keeperLen, off) = ZeroCopySource.NextUint64(keepersBytes, off);
        address[] memory keepers = new address[](keeperLen);
        bytes memory keeperBytes;
        for(uint i = 0; i < keeperLen; i++) {
            (keeperBytes, off) = ZeroCopySource.NextVarBytes(keepersBytes, off);
            keepers[i] = Utils.bytesToAddress(keeperBytes);
        }
        return keepers;
    }

    //将Poly Chain传递的交易数据进行解码,得到对应的toMerkleValue结构体数据
    function deserializeMerkleValue(bytes memory _valueBs) internal pure returns (ToMerkleValue memory) {
        ToMerkleValue memory toMerkleValue;
        uint256 off = 0;

        (toMerkleValue.txHash, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        (toMerkleValue.fromChainID, off) = ZeroCopySource.NextUint64(_valueBs, off);

        TxParam memory txParam;

        (txParam.txHash, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        (txParam.crossChainId, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        (txParam.fromContract, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        (txParam.toChainId, off) = ZeroCopySource.NextUint64(_valueBs, off);
        (txParam.toContract, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        (txParam.method, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        (txParam.args, off) = ZeroCopySource.NextVarBytes(_valueBs, off);
        
        toMerkleValue.makeTxParam = txParam;

        return toMerkleValue;
    }

    //将对应的bytes形式的rawHeader,解码成对应的Header结构体
    function deserializeHeader(bytes memory _headerBs) internal pure returns (Header memory) {
        Header memory header;
        uint256 off = 0;
        (header.version, off)  = ZeroCopySource.NextUint32(_headerBs, off);
        (header.chainId, off) = ZeroCopySource.NextUint64(_headerBs, off);
        (header.prevBlockHash, off) = ZeroCopySource.NextHash(_headerBs, off);
        (header.transactionsRoot, off) = ZeroCopySource.NextHash(_headerBs, off);
        (header.crossStatesRoot, off) = ZeroCopySource.NextHash(_headerBs, off);
        (header.blockRoot, off) = ZeroCopySource.NextHash(_headerBs, off);
        (header.timestamp, off) = ZeroCopySource.NextUint32(_headerBs, off);
        (header.height, off) = ZeroCopySource.NextUint32(_headerBs, off);
        (header.consensusData, off) = ZeroCopySource.NextUint64(_headerBs, off);
        (header.consensusPayload, off) = ZeroCopySource.NextVarBytes(_headerBs, off);
        (header.nextBookkeeper, off) = ZeroCopySource.NextBytes20(_headerBs, off);
        return header;
    }

    /* @notice            Deserialize Poly chain block header raw bytes
    *  @param rawHeader   Poly chain block header raw bytes
    *  @return            header hash same as Poly chain
    */
    //得到poly chain上的区块头数据的哈希
    function getHeaderHash(bytes memory rawHeader) internal pure returns (bytes32) {
        return sha256(abi.encodePacked(sha256(rawHeader)));
    }
}
UpgradableECCM.sol

代码github位置:polynetwork/eth-contracts/contracts/core/cross_chain_manager/upgrade/UpgradableECCM.sol

https://github.com/polynetwork/eth-contracts/blob/master/contracts/core/cross_chain_manager/upgrade/UpgradableECCM.sol

pragma solidity ^0.5.0;

import "./../interface/IEthCrossChainData.sol";
import "./../interface/IUpgradableECCM.sol";
import "./../../../libs/lifecycle/Pausable.sol";
import "./../../../libs/ownership/Ownable.sol";

contract UpgradableECCM is IUpgradableECCM, Ownable, Pausable {
    address public EthCrossChainDataAddress;
    uint64 public chainId;  
    
    constructor (address ethCrossChainDataAddr, uint64 _chainId) Pausable() Ownable()  public {
        EthCrossChainDataAddress = ethCrossChainDataAddr;
        chainId = _chainId;
    }
    //初始化部署时,输入对应的CCD合约的地址,和对应的chainID
    
    function pause() onlyOwner public returns (bool) {
        if (!paused()) {
            _pause();
        }
        IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress);
        if (!eccd.paused()) {
            require(eccd.pause(), "pause EthCrossChainData contract failed");
        }
        //将该合约和CCD合约,触发为暂停状态
        return true;
    }
    
    function unpause() onlyOwner public returns (bool) {
        if (paused()) {
            _unpause();
        }
        IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress);
        if (eccd.paused()) {
            require(eccd.unpause(), "unpause EthCrossChainData contract failed");
        }
        //将该合约和CCD合约,恢复为正常状态
        return true;
    }

    // modifier onlyOwner限制只有owner,才能将对应的owner更改为新的CCM合约地址
    function upgradeToNew(address newEthCrossChainManagerAddress) whenPaused onlyOwner public returns (bool) {
        IEthCrossChainData eccd = IEthCrossChainData(EthCrossChainDataAddress);
        eccd.transferOwnership(newEthCrossChainManagerAddress);
        //该函数用于将modifier中的owner更改为新的CCM合约地址
        return true;
    }
    
    //更改chainID,modifier onlyOwner限制只有owner才能更改对应的chainID
    function setChainId(uint64 _newChainId) whenPaused onlyOwner public returns (bool) {
        chainId = _newChainId;
        return true;
    }
}

通用智能合约代码分析

Context.sol

提供有关当前执行上下文的信息,包括交易的发送者及交易的数据。

https://github.com/polynetwork/eth-contracts/blob/master/contracts/libs/GSN/Context.sol

Ownable.sol

提供基本访问控制机制的合约模块,如:授权一个账户访问特定的函数功能。

该模块主要通过继承来使用,提供可用的modifier onlyOwner,应用于对应的函数,限制特定的owner来调用。

https://github.com/polynetwork/eth-contracts/tree/master/contracts/libs/ownership

contract Ownable is Context {
    address private _owner;
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    //代表合约所有权的转移
    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }
    //进行初始化合约,将部署者设置为初始所有者

    function owner() public view returns (address) {
        return _owner;
    }
	//返回当前合约所有者的地址
	
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }
    //该modifier用于限制只有所有者账户,才能调用对应的账户

    function isOwner() public view returns (bool) {
        return _msgSender() == _owner;
    }
	//判断调用者是否为当前的所有者

    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }
	//该函数用于将合约的所有者去除,恢复初始状态
	
    function transferOwnership(address newOwner) public  onlyOwner {
        _transferOwnership(newOwner);
    }
	//该函数由当前所有者调用,将合约的所有权转移到新账户'newOwner'。
 
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
    //将该合约的所有权转移给新账户,emit对应的事件
}
Utils.sol

该函数进行一些bytes ,uint,address之间的转换,以及一些merkle proof和verify sig用到的函数

https://github.com/polynetwork/eth-contracts/blob/master/contracts/libs/utils/Utils.sol

pragma solidity ^0.5.0;


library Utils {

    /* @notice      Convert the bytes array to bytes32 type, the bytes array length must be 32
    *  @param _bs   Source bytes array
    *  @return      bytes32
    */
    //将bytes数组转换为bytes32类型,bytes数组长度必须为32
    function bytesToBytes32(bytes memory _bs) internal pure returns (bytes32 value) {
        require(_bs.length == 32, "bytes length is not 32.");
        assembly {
            // load 32 bytes from memory starting from position _bs + 0x20 since the first 0x20 bytes stores _bs length
            value := mload(add(_bs, 0x20))
        }
    }

    /* @notice      Convert bytes to uint256
    *  @param _b    Source bytes should have length of 32
    *  @return      uint256
    */
    //将对应的bytes转换为uint256格式
    function bytesToUint256(bytes memory _bs) internal pure returns (uint256 value) {
        require(_bs.length == 32, "bytes length is not 32.");
        assembly {
            // load 32 bytes from memory starting from position _bs + 32
            value := mload(add(_bs, 0x20))
        }
        require(value <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range");
    }

    /* @notice      Convert uint256 to bytes
    *  @param _b    uint256 that needs to be converted
    *  @return      bytes
    */
   // 将uint256转化为bytes格式
    function uint256ToBytes(uint256 _value) internal pure returns (bytes memory bs) {
        require(_value <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range");
        assembly {
            // Get a location of some free memory and store it in result as
            // Solidity does for memory variables.
            bs := mload(0x40)
            // Put 0x20 at the first word, the length of bytes for uint256 value
            mstore(bs, 0x20)
            //In the next word, put value in bytes format to the next 32 bytes
            mstore(add(bs, 0x20), _value)
            // Update the free-memory pointer by padding our last write location to 32 bytes
            mstore(0x40, add(bs, 0x40))
        }
    }

    /* @notice      Convert bytes to address
    *  @param _bs   Source bytes: bytes length must be 20
    *  @return      Converted address from source bytes
    */
    //将bytes转化为对应的地址
    function bytesToAddress(bytes memory _bs) internal pure returns (address addr)
    {
        require(_bs.length == 20, "bytes length does not match address");
        assembly {
            // for _bs, first word store _bs.length, second word store _bs.value
            // load 32 bytes from mem[_bs+20], convert it into Uint160, meaning we take last 20 bytes as addr (address).
            addr := mload(add(_bs, 0x14))
        }

    }
    
    /* @notice      Convert address to bytes
    *  @param _addr Address need to be converted
    *  @return      Converted bytes from address
    */
    //将地址转化为对应的bytes
    function addressToBytes(address _addr) internal pure returns (bytes memory bs){
        assembly {
            // Get a location of some free memory and store it in result as
            // Solidity does for memory variables.
            bs := mload(0x40)
            // Put 20 (address byte length) at the first word, the length of bytes for uint256 value
            mstore(bs, 0x14)
            // logical shift left _a by 12 bytes, change _a from right-aligned to left-aligned
            mstore(add(bs, 0x20), shl(96, _addr))
            // Update the free-memory pointer by padding our last write location to 32 bytes
            mstore(0x40, add(bs, 0x40))
       }
    }

    /* @notice          Do hash leaf as the multi-chain does
    *  @param _data     Data in bytes format
    *  @return          Hashed value in bytes32 format
    */
    //计算叶子节点的哈希值
    function hashLeaf(bytes memory _data) internal pure returns (bytes32 result)  {
        result = sha256(abi.encodePacked(byte(0x0), _data));
    }

    /* @notice          Do hash children as the multi-chain does
    *  @param _l        Left node
    *  @param _r        Right node
    *  @return          Hashed value in bytes32 format
    */
    //计算左右节点的哈希
    function hashChildren(bytes32 _l, bytes32  _r) internal pure returns (bytes32 result)  {
        result = sha256(abi.encodePacked(bytes1(0x01), _l, _r));
    }

    /* @notice              Compare if two bytes are equal, which are in storage and memory, seperately
                            Refer from https://github.com/summa-tx/bitcoin-spv/blob/master/solidity/contracts/BytesLib.sol#L368
    *  @param _preBytes     The bytes stored in storage
    *  @param _postBytes    The bytes stored in memory
    *  @return              Bool type indicating if they are equal
    */
    //比较两个bytes类型的数据是否相等
    function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes_slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // fslot can contain both the length and contents of the array
                // if slength < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                // slength != 0
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes_slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    /* @notice              Slice the _bytes from _start index till the result has length of _length
                            Refer from https://github.com/summa-tx/bitcoin-spv/blob/master/solidity/contracts/BytesLib.sol#L246
    *  @param _bytes        The original bytes needs to be sliced
    *  @param _start        The index of _bytes for the start of sliced bytes
    *  @param _length       The index of _bytes for the end of sliced bytes
    *  @return              The sliced bytes
    */
    //从bytes类型的数据,指定的偏移量分片获得指定长度的bytes数据
    function slice(
        bytes memory _bytes,
        uint _start,
        uint _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_bytes.length >= (_start + _length));

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                // lengthmod <= _length % 32
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }
    /* @notice              Check if the elements number of _signers within _keepers array is no less than _m
    *  @param _keepers      The array consists of serveral address
    *  @param _signers      Some specific addresses to be looked into
    *  @param _m            The number requirement paramter
    *  @return              True means containment, false meansdo do not contain.
    */
    //用于验证签名,比较两个签名地址数组之中,是否有m个地址相同
    function containMAddresses(address[] memory _keepers, address[] memory _signers, uint _m) internal pure returns (bool){
        uint m = 0;
        for(uint i = 0; i < _signers.length; i++){
            for (uint j = 0; j < _keepers.length; j++) {
                if (_signers[i] == _keepers[j]) {
                    m++;
                    // delete _keepers[j];
                    _keepers[j] = 0x7777777777777777777777777777777777777777;
                }
            }
        }
        return m >= _m;
    }

    /* @notice              TODO
    *  @param key
    *  @return
    */
    //将对应的公钥进行压缩
    function compressMCPubKey(bytes memory key) internal pure returns (bytes memory newkey) {
         require(key.length >= 67, "key lenggh is too short");
         newkey = slice(key, 0, 35);
         if (uint8(key[66]) % 2 == 0){
             newkey[2] = byte(0x02);
         } else {
             newkey[2] = byte(0x03);
         }
         return newkey;
    }
    
    /**
     * @dev Returns true if `account` is a contract.
     *      Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L18
     *
     * This test is non-exhaustive, and there may be false-negatives: during the
     * execution of a contract's constructor, its address will be reported as
     * not containing a contract.
     *
     * IMPORTANT: It is unsafe to assume that an address for which this
     * function returns false is an externally-owned account (EOA) and not a
     * contract.
     */
     //判断该账户是合约账户还是普通账户
    function isContract(address account) internal view returns (bool) {
        // This method relies in extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
        // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
        // for accounts without code, i.e. `keccak256('')`
        bytes32 codehash;
        bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
        // solhint-disable-next-line no-inline-assembly
        assembly { codehash := extcodehash(account) }
        return (codehash != 0x0 && codehash != accountHash);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值