区块链通过时间戳保证每个区块依次顺序相连,而这个时间戳就像数据的生产日期,证明这个数据在什么时间点就已经存在,时间戳是区块元数据的一部分,这使得区块具有天然的时间属性。
随着区块链架构体系的不断发展,越来越多的研究对区块进行改造从而实现了对空间属性的支持。因此,区块链技术可以为数据打上时空标签,通过在区块中加入数据的时间和空间信息,将同样内容的数据集标识为不同的数据集个体,从而解决了数据要素流通中数据集可以被无限复制而无法辨识的难题,实现数据来源可确认。
区块链的可追溯性来源于区块链数据结构的特殊性。在区块链系统中,它的链式结构是从创世区块开始的,其后系统产生的所有区块都通过父区块的哈希值前后相连,并最终能追溯到创世区块。
由于每个区块都包含一段时间内系统进行的所有交易数据,因此完整的区块链数据包含了自创世区块以来,系统所有进行的交易及交易前后的关联信息。同时,得益于区块链信息的不可篡改特性,使得这种可追溯性是可靠可信的。
function _checkContractOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
)private returns(bool){
try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(),from,tokenId,_data)returns(
bytes4 retval
){
return retval==ERC721A__IERC721Receiver(to).onERC721Received.selector;
}catch(bytes memory reason){
if(reason.length==0){
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
assembly{
revert(add(32,reason),mload(reason))
}
}
}
我们在深入解析Safe多签钱包智能合约:Fallback合约内已经对onERC721Received的相关内容进行了分析,读者可自行阅读理解。此处,我们主要对try/catch这一少见的solidity关键词进行分析。
try关键词后必须为一个外部函数调用,在此处为
ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(),from,tokenId,_data),即调用了外部ERC721A__IERC721Receiver的onERC721Received函数。return会将外部调用的返回值封装为特定的函数名,此处为retval。
如果外部调用和返回值封装没有出现错误,就会运行第一个语句块的语句,此处为
return retval==ERC721A__IERC721Receiver(to).onERC721Received.selector;
该语句块较为简单,不再具体分析。
catch用来捕获错误,solidity提供了以下catch语句:
catch Error(string memory reason){...}用于捕获revert("reasonString")或require(false,"reasonString")等语句造成的错误
catch Panic(uint errorCode){...}用于捕获panic类型错误,如assert、除以0等错误
catch(bytes memory lowLevelData){...}用于直接捕获底层错误信息,涵盖所有类型错误
在真实场景下,显然我们无法保证调用的合约使用solidity编写,所以使用最后一张catch方法是有必要的。
显然,此处使用的是最后一种catch语句。在捕获到底层错误后,我们首先使用if语句判断此错误信息是否长度为0,如果长度为0,则意味着我们没有具体的错误信息,采取直接抛出TransferToNonERC721ReceiverImplementer.selector的策略。
此处使用了_revert函数,此函数是对revert包装,定义如下:
function _revert(bytes4 errorSelector)internal pure{
assembly{
mstore(0x00,errorSelector)
revert(0x00,0x04)
}
}