区块头和区块体数据也会被当作输入数据做一次Hash运算,其运算结果会被存储在下一个区块的区块头中,这样任何区块内容的修改都会反映到区块的Hash值上,而区块的Hash值又是下一个区块的输入数据,它又会被当作新区块的数据参与一次新区块的Hash运算,随着时间的推移和交易量的增加,所有的区块会通过保存前一个区块的Hash运算结果的方式组成一条链。
智能合约在区块链上的可执行代码是一种类似汇编语言的指令集,这些指令集通过EVM的解释和执行,对区块链的状态进行读写,实现合约规定的业务逻辑。因此通过Solidity这种高级编程语言,加上Solidity编译器,可以将高级语言编译成汇编指令集码,再将其部署到区块链上执行。
当函数调用者满足条件后,我们进入真正的 NFT 转移程序。首先清除待转移 NFT 的原有授权,代码如下:
assembly {
if approvedAddress {
sstore(approvedAddressSlot, 0)
}
}
直接将 _tokenApprovals 中 NFT 对应的值清空。
接下来,我们进入了最复杂的 NFT 转移阶段,该阶段的逻辑大致如下:
修正转移双方的 balance 参数
--_packedAddressData[from];
++_packedAddressData[to];
1
2
更新 tokenId 对应的 _packedOwnerships 数据:
_packedOwnerships[tokenId] = _packOwnershipData(
to,
_BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
);
由于转移过程必须进行初始化,所以此处将转移的 NFT 的 nextInitialized 设置为 True
考虑下一个 NFT 是否被初始化,
如转移下图中 tokenId = 3 的 NFT:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wKsxnwNP-1675684640935)(https://files.catbox.moe/20sjdu.svg)]
该 NFT 转移后,由于破坏了拥有者 0x2 的连续性,所以我们需要重写 tokenId = 4 的对应数据,代码如下:
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
if (_packedOwnerships[nextTokenId] == 0) {
if (nextTokenId != _currentIndex) {
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
此处使用了 _packedOwnerships[nextTokenId] == 0 排除了 tokenId = 4 转移的特殊情况。该 NFT 位于连续 NFT 的末尾,转移此 NFT 不会破环连续性
至此,我们完成了 NFT 的转移的核心流程。接下来就是已经介绍过的 Transfer 释放流程:
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
assembly {
// Emit the Transfer
event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
from, // from
.
toMasked, // to
.
tokenId // tokenId
.
)
}
if (toMasked == 0) _revert(TransferToZeroAddress.selector);