chainbridge solidity核心简要分析

https://github.com/ChainSafe/...
背景 跨链桥的模式一般分为资金池,或者影子资产模式。然而,不管是哪一种模式都好,其实无一例外都是lock/release,或者 mint/burn两个方式。我觉得对于合约端来说,实现跨链的合约应该是有很成熟的方案,而且可能会存在一个模版式的解决方案,chainbridge的solidity我觉得是其中一种。下面很简单整理一下最关键的部分:
资金和逻辑分离 核心逻辑在Bridge合约,上面有权限控制,金库暂停的逻辑。这里核心的安全逻辑处理。资金则存储在handler模块,这个是单独部署的,有ERC20Handler,ERC721Hander。handler是通过callback参数传入Bridge合约的,最核心的有2个函数:Deposite和executeProposal。分别用来做资金存入和取出的。
function deposit( bytes32 resourceID, address depositer, bytescalldata data ) external override onlyBridge returns (bytes memory) { uint256amount; (amount) = abi.decode(data, (uint)); address tokenAddress = _resourceIDToTokenContractAddress[resourceID]; require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted"); if (_burnList[tokenAddress]) { burnERC20(tokenAddress, depositer, amount); } else { lockERC20(tokenAddress, depositer, address(this), amount); } }function executeProposal(bytes32 resourceID, bytes calldata data) external override onlyBridge { uint256amount; uint256lenDestinationRecipientAddress; bytesmemory destinationRecipientAddress; (amount, lenDestinationRecipientAddress) = abi.decode(data, (uint, uint)); destinationRecipientAddress = bytes(data[64:64 + lenDestinationRecipientAddress]); bytes20 recipientAddress; address tokenAddress = _resourceIDToTokenContractAddress[resourceID]; assembly { recipientAddress := mload(add(destinationRecipientAddress, 0x20)) }require(_contractWhitelist[tokenAddress], "provided tokenAddress is not whitelisted"); if (_burnList[tokenAddress]) { mintERC20(tokenAddress, address(recipientAddress), amount); } else { releaseERC20(tokenAddress, address(recipientAddress), amount); } }

Proposal 【chainbridge solidity核心简要分析】Proposal字面就是提案的意思。因为资金取出是一个很危险的操作,所以这个操作在chainBridge这里需要一个委员会进行投票,只有在投票通过后,提案才能被确认。资金才可以被取出。如果把Proposal取消的话,可以变成中心化操作,但要有更强的权限控制管理。
function voteProposal(uint8 domainID, uint64 depositNonce, bytes32 resourceID, bytes calldata data) external onlyRelayers whenNotPaused { address handler = _resourceIDToHandlerAddress[resourceID]; uint72 nonceAndID = (uint72(depositNonce) << 8) | uint72(domainID); bytes32 dataHash = keccak256(abi.encodePacked(handler, data)); Proposal memory proposal = _proposals[nonceAndID][dataHash]; require(_resourceIDToHandlerAddress[resourceID] != address(0), "no handler for resourceID"); if (proposal._status == ProposalStatus.Passed) { executeProposal(domainID, depositNonce, data, resourceID, true); return; }address sender = _msgSender(); require(uint(proposal._status) <= 1, "proposal already executed/cancelled"); require(!_hasVoted(proposal, sender), "relayer already voted"); if (proposal._status == ProposalStatus.Inactive) { proposal = Proposal({ _status : ProposalStatus.Active, _yesVotes : 0, _yesVotesTotal : 0, _proposedBlock : uint40(block.number) // Overflow is desired. }); emit ProposalEvent(domainID, depositNonce, ProposalStatus.Active, dataHash); } else if (uint40(sub(block.number, proposal._proposedBlock)) > _expiry) { // if the number of blocks that has passed since this proposal was // submitted exceeds the expiry threshold set, cancel the proposal proposal._status = ProposalStatus.Cancelled; emit ProposalEvent(domainID, depositNonce, ProposalStatus.Cancelled, dataHash); }if (proposal._status != ProposalStatus.Cancelled) { proposal._yesVotes = (proposal._yesVotes | _relayerBit(sender)).toUint200(); proposal._yesVotesTotal++; // TODO: check if bit counting is cheaper.emit ProposalVote(domainID, depositNonce, proposal._status, dataHash); // Finalize if _relayerThreshold has been reached if (proposal._yesVotesTotal >= _relayerThreshold) { proposal._status = ProposalStatus.Passed; emit ProposalEvent(domainID, depositNonce, ProposalStatus.Passed, dataHash); } } _proposals[nonceAndID][dataHash] = proposal; if (proposal._status == ProposalStatus.Passed) { executeProposal(domainID, depositNonce, data, resourceID, false); } }

最后 跨链桥的合约层是比较简单的,复杂的逻辑都在跨链逻辑层,常用的语言是Golang。

    推荐阅读