21026 large

120 孤荷凌寒自学第 0206 天_区块链第 120 天 NFT017

941xue · 于 发布 · 163 次阅读

【主要内容】
今天继续学习了解ntf的相关知识,今天继续批注分析从github上获取的erc721合约的代码,添加自己的理解并批注,共耗时35分钟。
(此外整理作笔记花费了约38分钟)
详细学习过程见文末学习过程屏幕录像。
【今天根据各SOL文件及其合约之间的继承关系,绘制了一张关系图谱】

纯正绿色的连接线表示了合约的继承关系,如果连接线不是纯正绿色的,则表示虽然文件中有对此文件的引用,但却不是合约的继承关系。

【今天批注的ERC721的智能合约】
文件:
ERC721Base.sol
已批注的部分内容如下:

pragma solidity ^0.4.18;

import './SafeMath.sol';  //这是一个library

import './AssetRegistryStorage.sol';  //资产登记所需要的基类全约,其中声明了各种需要登记到区块中的(变量类型为storage)mapping表变量

import './IERC721Base.sol';  //这儿声明了erc721的interface

import './IERC721Receiver.sol';  //在这个文件中声明了一个Interface,声明了让合约可以接收nft的函数onERC721Received

import './ERC165.sol';  //erc165的interface


//--这个合约也不是最子级的合约,虽然这个合约基本完善地完成了NFT资产所需要的各种方法
//FullAssetRegistry.SOL文件中的FullAssetRegistry合约还会继承这个合约生成新的子合约
contract ERC721Base is AssetRegistryStorage, IERC721Base, ERC165 {
  //作为基类合约的IERC721Base和ERC165中声明的只是一个interface,但仍然可以被当作基类合约使用。
  using SafeMath for uint256;  //这儿使用了库 SafeMath.sol中的内容

  // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
  bytes4 private constant ERC721_RECEIVED = 0x150b7a02; //ERC165接口需要的常量,表示 合约是否可以接收 NFT资产的标识

  bytes4 private constant InterfaceId_ERC165 = 0x01ffc9a7; //ERC165需要的常量,表示合约是否支持erc165接口
  /*
   * 0x01ffc9a7 ===
   *   bytes4(keccak256('supportsInterface(bytes4)'))
   */

  bytes4 private constant Old_InterfaceId_ERC721 = 0x7c0633c6;
  bytes4 private constant InterfaceId_ERC721 = 0x80ac58cd;  //ERC721需要的常量,表示合约是否支持erc721接口
   /*
   * 0x80ac58cd ===
   *   bytes4(keccak256('balanceOf(address)')) ^
   *   bytes4(keccak256('ownerOf(uint256)')) ^
   *   bytes4(keccak256('approve(address,uint256)')) ^
   *   bytes4(keccak256('getApproved(uint256)')) ^
   *   bytes4(keccak256('setApprovalForAll(address,bool)')) ^
   *   bytes4(keccak256('isApprovedForAll(address,address)')) ^
   *   bytes4(keccak256('transferFrom(address,address,uint256)')) ^
   *   bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
   *   bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
   */

  //
  // Global Getters
  //

  /**
   * @dev Gets the total amount of assets stored by the contract 获取当前合约管理着多少个NFT的总数量 
   * @return uint256 representing the total amount of assets
   */
  function totalSupply() external view returns (uint256) {
    return _totalSupply(); //通过调用下一个函数到获取
  }
  function _totalSupply() internal view returns (uint256) {
    return _count;  //状态变量(全局变量)_count是在sol文件AssetRegistryStorage.sol中声明的
  }

  //注意到,这儿的每一个方法,都进行了安全隔离,对外,采用external关键字修饰,对内(包括对下级合约——也就是把当前合约当作父级合约的子合约)使用internal关键字。
  /*

    public与private
    对于public和private,相信学过其他主流语言的人都能明白:

    public修饰的变量和函数,任何用户或者合约都能调用和访问。
    private修饰的变量和函数,只能在其所在的合约中调用和访问,即使是其子合约也没有权限访问。
    external和internal
    除 public 和 private 属性之外,Solidity 还使用了另外两个描述函数可见性的修饰词:internal(内部) 和 external(外部)。

    internal 和 private 类似,不过, 如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。
    external 与public 类似,只不过这些函数只能在合约之外调用 - 它们不能被合约内的其他函数调用。

    internal、private、external、public这4种关键字都是可见性修饰符,互不共存(也就是说,同一时间只能使用这四个修饰字中的一个)
    ————————————————
    版权声明:本文为CSDN博主「黄嘉成」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_33829547/java/article/details/80460013

  */
  //
  // Asset-centric getter functions 查询有关NFT的所有者等相关信息的操作
  //

  /**
   * @dev Queries what address owns an asset. This method does not throw. 传入NFT的ID值:assetId,以查询得到此NFT资产的拥有人是谁。
   * In order to check if the asset exists, use the `exists` function or check if the
   * return value of this call is `0`.
   * @return uint256 the assetId
   */
  function ownerOf(uint256 assetId) external view returns (address) {
    return _ownerOf(assetId);
  }
  function _ownerOf(uint256 assetId) internal view returns (address) {
    return _holderOf[assetId];  //状态变量(全局变量)_holderOf是在sol文件AssetRegistryStorage.sol中声明的
  }

  //
  // Holder-centric getter functions
  //
  /**
   * @dev Gets the balance of the specified address 查看一个节点(形参 owner指定)拥有总共多少个NFT
   * @param owner address to query the balance of
   * @return uint256 representing the amount owned by the passed address
   */
  function balanceOf(address owner) external view returns (uint256) {
    return _balanceOf(owner);
  }
  function _balanceOf(address owner) internal view returns (uint256) {
    return _assetsOf[owner].length; //状态变量(全局变量)_assetsOf是在sol文件AssetRegistryStorage.sol中声明的
  }

  //
  // Authorization getters  获取当前资产授权状态的函数方法
  //

  /**
   * @dev Query whether an address has been authorized to move any assets on behalf of someone else  查询 形参 assetHolder 节点 是否已经把 它的所有NFT资产都授权给了 形参 operator 节点,允许 形参 operator 节点 全权处理(包括移动)这些NFT资产。
   * @param operator the address that might be authorized
   * @param assetHolder the address that provided the authorization
   * @return bool true if the operator has been authorized to move any assets
   */
  function isApprovedForAll(address assetHolder, address operator)
    external view returns (bool)
  {
    return _isApprovedForAll(assetHolder, operator);
  }
  function _isApprovedForAll(address assetHolder, address operator)
    internal view returns (bool)
  {
    return _operators[assetHolder][operator]; //状态变量(全局变量)_operators是在sol文件AssetRegistryStorage.sol中声明的
  }

  /**
   * @dev Query what address has been particularly authorized to move an asset 查询指定ID的NFT资产(形参 assetId 指定) 当前 已经被 授权给哪个 节点 处理。
   * @param assetId the asset to be queried for
   * @return bool true if the asset has been approved by the holder
   */
  function getApproved(uint256 assetId) external view returns (address) {
    return _getApprovedAddress(assetId);
  }
  function getApprovedAddress(uint256 assetId) external view returns (address) {
    return _getApprovedAddress(assetId);
  }
  function _getApprovedAddress(uint256 assetId) internal view returns (address) {
    return _approval[assetId];
  }

  /**
   * @dev Query if an operator can move an asset. 查询 一个 节点(形参 operator 指定)是不是已经 被 授权处理 指定ID的NFT(形参 assetId)
   * @param operator the address that might be authorized
   * @param assetId the asset that has been `approved` for transfer
   * @return bool true if the asset has been approved by the holder
   */
  function isAuthorized(address operator, uint256 assetId) external view returns (bool) {
    return _isAuthorized(operator, assetId);
  }
  function _isAuthorized(address operator, uint256 assetId) internal view returns (bool)
  {
    require(operator != 0); //检查 节点地址是否为空
    address owner = _ownerOf(assetId);  //得到指定ID的NFT本身是属于哪个节点地址的
    if (operator == owner) {   //如果指定ID的NFT的归属节点就是当前 要查询 的operator 那么,就直接返回TRUE
      return true;
    }
    return _isApprovedForAll(owner, operator) || _getApprovedAddress(assetId) == operator; 
    //双竖线左边是检查节点owner是否已经授权节点operator处理它的全部NFT资产,双竖线右边是获取指定id的assetId现在的授权处理节点地址是否等于Operator
    //如果以上两个条件二者之一符合,则会返回TRUE,否则 返回FALSE
  }

  //
  // Authorization  执行授权等操作的函数方法区域
  //

  /**
   * @dev Authorize a third party operator to manage (send) msg.sender's asset 当前调用合约的节点(msg.sender)调用此方法来向指定的节点(形参 operator 指定)授权允许其 操作自己的所有NFT资产,或撤消这个授权——是发起授权还是撤消授权由形参 authorized 的BOOL值决定
   * @param operator address to be approved
   * @param authorized bool set to true to authorize, false to withdraw authorization
   */
  function setApprovalForAll(address operator, bool authorized) external {
    return _setApprovalForAll(operator, authorized);
  }
  function _setApprovalForAll(address operator, bool authorized) internal {
    if (authorized) { //如果是发起授权
      require(!_isApprovedForAll(msg.sender, operator));
      _addAuthorization(operator, msg.sender);  //这个方法在本合约稍后定义的,直接操纵 映射表  _operators 来完成
    } else { //如果是撤消授权
      require(_isApprovedForAll(msg.sender, operator));
      _clearAuthorization(operator, msg.sender); //这个方法在本合约稍后定义的,直接操纵 映射表  _operators 来完成
    }
    emit ApprovalForAll(msg.sender, operator, authorized); //广播这个事件,事件的定义在sol文件IERC721Base.sol中声明的
  }

  /**
   * @dev Authorize a third party operator to manage one particular asset //这个函数是授权节点Operator可以操作调用合约节点(msg.sender)的指定ID的一个nft资产。
   * @param operator address to be approved
   * @param assetId asset to approve
   */
  function approve(address operator, uint256 assetId) external {
    address holder = _ownerOf(assetId);  //获取要操作的指定ID的NFT(这儿就是形参assetId)当前的所有者节点的地址
    require(msg.sender == holder || _isApprovedForAll(msg.sender, holder));
    //上一个语句,左边是检查当前合约调用节点是不是就是此ID的NFT资产的所有人,右边检查,当前调用合约的节点是不是已经把自己的所有NFT资产的操作权授权给了当前操作的指定ID的NFT资产的所有人(现在是变量holder表示)。
    //只要两者有其一是true ,语句将会继续 执行,如果两者都是false,则语句就不再继续往下执行。
    require(operator != holder); //如果要被授权的节点就是holder节点,那么就没有意义了,所以这儿要检测一下。

    //于是下一句话进行了进一步的检查,如果要被授权的节点operator已经是指定id的Nft资产可操作者,那就不必要进行重复授权操作了。
    if (_getApprovedAddress(assetId) != operator) {
      _approval[assetId] = operator; //直接将映射表_approval中指定ID的NFT资产的被授权操作节点修改为operator就可以了。
      emit Approval(holder, operator, assetId); //广播这个更改映射表的事件。
    }
  }

  //--下面这个函数 是为函数 _setApprovalForAll 准备的子函数
  function _addAuthorization(address operator, address holder) private {
    _operators[holder][operator] = true;
  }

  //--下面这个函数 是为函数 _setApprovalForAll 准备的子函数
  function _clearAuthorization(address operator, address holder) private {
    _operators[holder][operator] = false;
  }

  //
  // Internal Operations
  //

  //下面这个函数把指定ID的NFT资产(形参assetId指定)的 所有人 指定为 to这个节点 (变更资产所有人)
  function _addAssetTo(address to, uint256 assetId) internal {
    _holderOf[assetId] = to; //在资产所有人的映射表中,变更 此资产的所有人为to节点

    uint256 length = _balanceOf(to); //记录下to这个节点现在有多少个NFT资产

    _assetsOf[to].push(assetId); //向映射表_assetsOf中的to节点下添加新的NFT资产的ID

    _indexOfAsset[assetId] = length; //更改映射表 _indexOfAsset中指定的当前 ID的 NFT资产在to节点自己的资产登记表_assetOf中的Index值,现在发现,这个Index值 是从1开始的,而不是从零开始的。

    _count = _count.add(1); //这儿没有理解为什么_count需要增加1——现在知道 了,因为 把一个指定 ID的nft资产从某个 节点中移除时,这个变量是减少了1的,所以重新 登记 时,这个变量就要加上1
  }

  //下面这个函数把指定 ID的NFT资产(形参assetId指定)从原 拥有者(形参from指定 的节点)中删除掉,操作之后,这个NFT资产的归属节点地址 为0
  function _removeAssetFrom(address from, uint256 assetId) internal {
    uint256 assetIndex = _indexOfAsset[assetId]; //获取指定ID的NFT资产的在其拥有者的所有资产中的index值
    uint256 lastAssetIndex = _balanceOf(from).sub(1); //查询from节点名下有多少个nft资产,这儿减掉1的意思是使Index是从0开始计数的,这和上一个函数的理解是否有冲突
    uint256 lastAssetId = _assetsOf[from][lastAssetIndex]; //获取到指定节点from名下的最后一个nft资产的id值,index的问题,现在我的理解 是,从映射表 _indexOfAsset获取 到的Index值都要减去1才能用于映射表_assetsOf

    _holderOf[assetId] = 0; //将指定ID的NFT的归属节点地址设置为空,意味着,这个NFT资产从原拥有者的名下被删除掉了(它现在不再属于任何一个节点)

    // Insert the last asset into the position previously occupied by the asset to be removed
    //将最后一个资产插入要移除的资产先前占用的位置__把from节点所有的资产列表中_assetsOf[from]原本在最后一项的资产的ID值lastAssetId记录到被删除的那个 资产_assetsOf[from][assetIndex] 的位置那儿——也就是原Index为assetIndex值的那个资产位置现在记录的是原来 最后一个资产的ID
    _assetsOf[from][assetIndex] = lastAssetId;

    // Resize the array
    _assetsOf[from][lastAssetIndex] = 0; //现在from节点的所有资产清单中的index为最后一项资产记录的资产ID值被设置 为 0
    _assetsOf[from].length--; //这儿居然直接将映射表_assetOf[from]的元素长度减少了一

    // Remove the array if no more assets are owned to prevent pollution
    if (_assetsOf[from].length == 0) { //如果from节点名下现在已经没有任何NFT资产了,
      delete _assetsOf[from];  //可以直接删除映射表中指定的一项
    }

    // Update the index of positions for the asset
    _indexOfAsset[assetId] = 0; //现在被从原拥有者名下 删除掉的指定ID的NFT资产的index记录被设置成了0
    _indexOfAsset[lastAssetId] = assetIndex; //因为from节点所拥有的全部NFT资产记录清单中的最后一项资产的index值已经改变,所以要在映射表_indexOfAsset中也作修改。

    _count = _count.sub(1); //这儿记录下整个合约记录的所有NFT资产的总数也减少了1(在重新把这个指定 ID的NFT资产登记 给另一个节点 的时候 ,COUNT值又会加上1)
  }

  //撤消对指定ID的NFT资产(形参assetId指定)可代为操作权的授权声明,是函数approve的逆过程,这个函数仅供本合约内部调用
  function _clearApproval(address holder, uint256 assetId) internal {
    if (_ownerOf(assetId) == holder && _approval[assetId] != 0) { //检查Holder节点是不是此NFT资产的所有人,同时检查 当前NFT资产量不是有必要更改授权
      //不过我发现 ,这儿是直接 传入 一个参数Holder作为地址,意味着并没有 检查 当前调用 合约的节点msg.sender是不是holder这个节点 ,当然这个函数呢使用的关键字是:internal
      //当前函数只能供本合约内部调用 ,那我的理解 是,这个函数是供别的公开函数调用 的子函数
      _approval[assetId] = 0; //现在这个指定 ID的NFT资产被授权给节点地址 0 可以代为操作,意味着之前的授权被撤消了
      emit Approval(holder, 0, assetId); //广播 授权的变更,只是这一次变更为授权给地址为0的节点
    }
  }

  //
  // Supply-altering functions
  //

  //实现让 节点beneficiary 获取指定ID的NFT资产(assetId这个形参指明了是哪个NFT资产),本函数仅供本合约内部调用
  function _generate(uint256 assetId, address beneficiary) internal {
    require(_holderOf[assetId] == 0); //首先检查一下,指定的NFT是否是 没有拥有者 的状态,如果是属于没有归属者的NFT才可以执行后续操作。

    _addAssetTo(beneficiary, assetId); //然后用上面定义过的本合约专用的内部函数 把这个NFT转移给节点:beneficiary

    emit Transfer(0, beneficiary, assetId); //广播这一资产转移 的事件,从节点地址为0的节点转移给beneficiary代表的节点
  }

  //让指定ID的NFT资产(由形参assetId指定)从现在的拥有在手中 脱离(转移开),就是使其现拥有者不再拥有这个NFT资产,本函数仅供本合约内部调用
  function _destroy(uint256 assetId) internal {
    address holder = _holderOf[assetId]; //首先获取指定NFT资产当前 的实际拥有人
    require(holder != 0); //检查此NFT资产当前 的实际拥有地址是否为0

    _removeAssetFrom(holder, assetId); //调用上面定义好的函数,从原拥有人者的资产列表中删除掉这个NFT资产,这一步执行完成后,这个NFT资产的归属节点地址就是0

    emit Transfer(holder, 0, assetId); //广播这一资产转移 的事件,从原节点地址转移给节点地址为0的节点
  }

  //
  // Transaction related operations
  //下面定义了五个函数修改器
  //

  //这个函数修改器用于限制当前调用合约的节点,要执行指定函数操作的节点必须是要操作的NFT资产的拥有节点
  modifier onlyHolder(uint256 assetId) {
    require(_ownerOf(assetId) == msg.sender);
    _;
  }

  //这个函数修改器用于限制针对当前要操作的NFT资产,当前调用合约的节点是不是已经取得了对此NFT资产的操作权(这个节点被授权操作这个NFT了吗?)
  modifier onlyAuthorized(uint256 assetId) {
    require(_isAuthorized(msg.sender, assetId)); //使用了前面定义的函数来检查指定节点这儿是msg.sender是不是已经取得了这个NFT的操作权。
    _;
  }

  //这个函数修改器的作用时,限制from节点是不是assetId对应的NFT资产的实际拥有者
  modifier isCurrentOwner(address from, uint256 assetId) {
    require(_ownerOf(assetId) == from);
    _;
  }

  //这个函数修改器用于限制指定的节点destinatary是不是一个空地址(地址为0)
  modifier isDestinataryDefined(address destinatary) {
    require(destinatary != 0); //只有在其地址不为0的情况下,才会继续执行后面的语句
    _;
  }

  //这个函数修改器的作用时,防止to节点就是指定ID的NFT资产(这儿由assetId指定)的拥有者
  modifier destinataryIsNotHolder(uint256 assetId, address to) {
    require(_ownerOf(assetId) != to); //只有这个Nft资产的拥有者节点不是to节点时,后面的代码才会执行
    _;
  }

  /**
   * @dev Alias of `safeTransferFrom(from, to, assetId, '')`
   * 这是安全进行资产转移 的函数
   * @param from address that currently owns an asset
   * @param to address to receive the ownership of the asset
   * @param assetId uint256 ID of the asset to be transferred
   */
   //这个函数进行资产转移 ,把assetId代表的NFT资产从节点from转移给节点to,这个方法是只能由外部调用合约者使用的方法
  function safeTransferFrom(address from, address to, uint256 assetId) external {
    return _doTransferFrom(from, to, assetId, '', true); //具体过程调用了下面紧接着定义的一个仅供本合约内部调用的函数
  }

  /**
   * @dev Securely transfers the ownership of a given asset from one address to
   * another address, calling the method `onNFTReceived` on the target address if
   * there's code associated with it
   * 这是上一个函数的另一个版本,支持添加额外交易数据
   * @param from address that currently owns an asset
   * @param to address to receive the ownership of the asset
   * @param assetId uint256 ID of the asset to be transferred
   * @param userData bytes arbitrary user information to attach to this transfer
   */
  function safeTransferFrom(address from, address to, uint256 assetId, bytes userData) external {
    return _doTransferFrom(from, to, assetId, userData, true);
  }

  /**
   * @dev Transfers the ownership of a given asset from one address to another address
   * Warning! This function does not attempt to verify that the target address can send
   * tokens.
   * 这是个常规的进行资产转移 的函数(不过调用的是和上面两个函数调用的相同的内部函数)
   * @param from address sending the asset
   * @param to address to receive the ownership of the asset
   * @param assetId uint256 ID of the asset to be transferred
   */
  function transferFrom(address from, address to, uint256 assetId) external {
    return _doTransferFrom(from, to, assetId, '', false);
  }

  //这个仅供合约内部使用的函数才是真正的实现NFT资产转移 的函数过程
  function _doTransferFrom(
    address from,
    address to,
    uint256 assetId,
    bytes userData,
    bool doCheck
  )
    onlyAuthorized(assetId)  //添加了函数修改器,必须保证调用合约者拥有对当下操作的NFT资产的操作权(被授权过)
    internal
  {
    _moveToken(from, to, assetId, userData, doCheck); //进一步调用下级子函数来实现。
  }

  function _moveToken(
    address from,
    address to,
    uint256 assetId,
    bytes userData,
    bool doCheck
  )
    isDestinataryDefined(to) //这儿添加了三个函数修改器,加强检查,首先确保to节点的地址不为0
    destinataryIsNotHolder(assetId, to) //这儿确保to节点不是assetId代表的NFT资产的所有人
    isCurrentOwner(from, assetId) //这儿确保from节点是assetId代表的NFT资产的实际拥有人(这儿有疑惑了,上一个函数只检查确保有操作权,这儿却直接检查 是拥有者)
    private      //这个函数是私有的,只能本合约调用,连子合约都不能调用
  {
    address holder = _holderOf[assetId];  //取得NFT资产原有的所有者节点
    _clearApproval(holder, assetId);  //清除这个nft资产相关的全部授权信息
    _removeAssetFrom(holder, assetId); //从原所有者节点的资产表中删除这个NFT资产
    _addAssetTo(to, assetId); //将这个NFT资产登记到to这个节点名下
    emit Transfer(holder, to, assetId); //广播这次完整的资产转移过程的事件

    if (doCheck && _isContract(to)) { //如果需要执行检查,检查什么?第二个条件是在检查 to 这个节点是一个智能合约地址吗?
      // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))
      //如果to这个节点是一个智能合约所在的节点地址,那么执行下面的代码
      require(
        IERC721Receiver(to).onERC721Received(
          msg.sender, holder, assetId, userData
        ) == ERC721_RECEIVED
      );
      //初步理解 上一句话就是 检查 to这个节点上的智能合约是否支持 接收nft资产,就是看其中有没有定义过一个方法onERC721Received()
    }
  }

  /**
   * Internal function that moves an asset from one holder to another
   */

  /**
   * @dev Returns `true` if the contract implements `interfaceID` and `interfaceID` is not 0xffffffff, `false` otherwise
   * @param  _interfaceID The interface identifier, as specified in ERC-165
   */
   //这个只供外界调用的函数方法,用于检测本合约是否实现了erc165及erc721合约接口,初步理解
  function supportsInterface(bytes4 _interfaceID) external view returns (bool) {

    if (_interfaceID == 0xffffffff) {  //就是要求合约一定不能支持0xffffffff接口,这到底是什么接口规范?
      return false;
    }
    return _interfaceID == InterfaceId_ERC165 || _interfaceID == Old_InterfaceId_ERC721 || _interfaceID == InterfaceId_ERC721;
  }

  //
  // Utilities 下面这个函数用于合约内部检查一个节点地址是否是一个智能合约所在的节点地址。
  //

  function _isContract(address addr) internal view returns (bool) {
    uint size;
    assembly { size := extcodesize(addr) }  //在大括号中的汇编代码,extcodesize指令是求出节点地址addr所在的智能合约的代码的size
    return size > 0; //如果size大于0,就说明这个节点地址是一个智能合约所在的节点,普通的以太坊节点上没有存放智能合约 代码,因此 size=0,而有智能合约驻存的节点地址,上面的智能合约代码,因此 这个size就会>0
  }
}

文件:
AssetRegistryStorage.sol

pragma solidity ^0.4.18;

//资产登记所需要的基类全约,其中声明了各种需要登记到区块中的(变量类型为storage)mapping表变量
contract AssetRegistryStorage {

  string internal _name;
  string internal _symbol;
  string internal _description;

  /**
   * Stores the total count of assets managed by this registry
   * 存储此注册表管理的资产总数(就是记录下合约本身总共管理着多少个nft资产)
   */
  uint256 internal _count;

  /**
   * Stores an array of assets owned by a given account
   *存储给定帐户拥有的NFT资产s的映射表,uint256[]是一个数组,其中存储的是这个节点所拥有的全部Nft资产的  ID
   */
  mapping(address => uint256[]) internal _assetsOf;

  /**
   * Stores the current holder of an asset
   * 存储指定ID的NFT资产的当前 所有人 的地址的映射表,也就是说当前ID的NFT 是属于哪个节点的。
   */
  mapping(uint256 => address) internal _holderOf;

  /**
   * Stores the index of an asset in the `_assetsOf` array of its holder
   * 将资产的索引存储在其持有者的“_assetsOf”数组中,记录下指定ID的NFT在映射表_assectsOf中的INDEX值。(根据主合约文件中的使用来看,这个Index值是从1开始的,而不是从零开始的)
   */
  mapping(uint256 => uint256) internal _indexOfAsset;

  /**
   * Stores the data associated with an asset
   * 存储指定ID的NFT资产的真实URL地址(可以传统的HTTP的地址,也可以是区块链的IPFS这样的存储地址
   */
  mapping(uint256 => string) internal _assetData;

  /**
   * For a given account, for a given operator, store whether that operator is
   * allowed to transfer and modify assets on behalf of them.
   * 对于指定的节点(第一个address),是否这个节点已经授权指定的节点(第二个address)来处理自己(第一个address)的nft资产,记录的值是bool类型的(最后一个bool)
   */
  mapping(address => mapping(address => bool)) internal _operators;

  /**
   * Approval array
   * 记录下指定ID的NFT已经授权给哪个ADDRESS去处理。即是说,根据NFT的ID是可以从映射表中查找出当前这个ID的NFT已经被授权给了哪个ADDRESS的节点。
   */
  mapping(uint256 => address) internal _approval;
}

文件:
IERC721Base.sol

pragma solidity ^0.4.18;

//这儿声明了erc721的interface
interface IERC721Base {
  function totalSupply() external view returns (uint256);

  // function exists(uint256 assetId) external view returns (bool);
  function ownerOf(uint256 assetId) external view returns (address);

  function balanceOf(address holder) external view returns (uint256);

  function safeTransferFrom(address from, address to, uint256 assetId) external;
  function safeTransferFrom(address from, address to, uint256 assetId, bytes userData) external;

  function transferFrom(address from, address to, uint256 assetId) external;

  function approve(address operator, uint256 assetId) external;
  function setApprovalForAll(address operator, bool authorized) external;

  function getApprovedAddress(uint256 assetId) external view returns (address);
  function isApprovedForAll(address assetHolder, address operator) external view returns (bool);

  function isAuthorized(address operator, uint256 assetId) external view returns (bool);

  /**
  * @dev Deprecated transfer event. Now we use the standard with three parameters
  * It is only used in the ABI to get old transfer events. Do not remove
  */
  event Transfer(
    address indexed from,
    address indexed to,
    uint256 indexed assetId,
    address operator,
    bytes userData,
    bytes operatorData
  );
  /**
   * @dev Deprecated transfer event. Now we use the standard with three parameters
   * It is only used in the ABI to get old transfer events. Do not remove
   */
  event Transfer(
    address indexed from,
    address indexed to,
    uint256 indexed assetId,
    address operator,
    bytes userData
  );
  event Transfer(
    address indexed from,
    address indexed to,
    uint256 indexed assetId
  );
  event ApprovalForAll(
    address indexed holder,
    address indexed operator,
    bool authorized  //这个广播参数表示 ,当前事件是生成授权还是撤消授权
  );
  event Approval(
    address indexed owner,
    address indexed operator,
    uint256 indexed assetId
  );
}

文件IERC721Enumerable.sol

pragma solidity ^0.4.18;

//这个基类合约声明了为获取指定节点NFT资产的声明式 函数两个(其实有其它函数被注释掉了)
contract IERC721Enumerable {

  /**
   * @notice Enumerate active tokens
   * @dev Throws if `index` >= `totalSupply()`, otherwise SHALL NOT throw.
   * @param index A counter less than `totalSupply()`
   * @return The identifier for the `index`th asset, (sort order not
   *  specified)
   */
  // TODO (eordano): Not implemented
  // function tokenByIndex(uint256 index) public view returns (uint256 _assetId);

  /**
   * @notice Count of owners which own at least one asset
   *  Must not throw.
   * @return A count of the number of owners which own asset
   */
  // TODO (eordano): Not implemented
  // function countOfOwners() public view returns (uint256 _count);

  /**
   * @notice Enumerate owners
   * @dev Throws if `index` >= `countOfOwners()`, otherwise must not throw.
   * @param index A counter less than `countOfOwners()`
   * @return The address of the `index`th owner (sort order not specified)
   */
  // TODO (eordano): Not implemented
  // function ownerByIndex(uint256 index) public view returns (address owner);

  /**
   * @notice Get all tokens of a given address
   * @dev This is not intended to be used on-chain
   * @param owner address of the owner to query
   * @return a list of all assetIds of a user
   */
  function tokensOf(address owner) external view returns (uint256[]);

  /**
   * @notice Enumerate tokens assigned to an owner
   * @dev Throws if `index` >= `balanceOf(owner)` or if
   *  `owner` is the zero address, representing invalid assets.
   *  Otherwise this must not throw.
   * @param owner An address where we are interested in assets owned by them
   * @param index A counter less than `balanceOf(owner)`
   * @return The identifier for the `index`th asset assigned to `owner`,
   *   (sort order not specified)
   */
  function tokenOfOwnerByIndex(
    address owner, uint256 index
  ) external view returns (uint256 tokenId);
}

文件:
ERC721Enumerable.sol

pragma solidity ^0.4.18;

import './IERC721Enumerable.sol'; //这个基类合约声明了为获取指定节点NFT资产的声明式函数两个(其实有其它函数被注释掉了)
import './AssetRegistryStorage.sol'; 

//这个合约用于枚举出或获取出指定节点的NFT资产
contract ERC721Enumerable is AssetRegistryStorage, IERC721Enumerable {

  /**
   * @notice Get all tokens of a given address 把节点地址owner所拥有的全部nFT资产的ID列表返回
   * @dev This is not intended to be used on-chain
   * @param owner address of the owner to query
   * @return a list of all assetIds of a user
   */
  function tokensOf(address owner) external view returns (uint256[]) {
    return _assetsOf[owner]; //状态变量(全局变量)_assetsOf是在sol文件AssetRegistryStorage.sol中声明的
    //这个映射表中存放着节点owner的全部资产的ID列表
  }

  /**
   * @notice Enumerate tokens assigned to an owner
   * @dev Throws if `index` >= `balanceOf(owner)` or if
   *  `owner` is the zero address, representing invalid assets.
   *  Otherwise this must not throw.
   * @param owner An address where we are interested in assets owned by them
   * @param index A counter less than `balanceOf(owner)`
   * @return The identifier for the `index`th asset assigned to `owner`,
   *   (sort order not specified)
   */
   //这个函数只返回指定index的属于节点地址owner的那个NFT资产的ID值,index值是owner节点只是自己的全部资产的排序值
  function tokenOfOwnerByIndex(
    address owner, uint256 index
  )
    external
    view
    returns (uint256 assetId)
  {
    require(index < _assetsOf[owner].length); //限制其最大值,防止下标溢出,这儿可见,为这个函数传入第二个实参时,它的计数是从0开始的
    require(index < (1<<127)); //位运算1<<127表示二进制表示的1向左移动127位 这儿也是限制Index允许 的最大值
    return _assetsOf[owner][index]; //返回 指定 Index的那个 NFT资产的ID值
  }

}

文件
IERC721Metadata.sol

pragma solidity ^0.4.18;

//这个基类合约是对Nfts资产的描述信息,包括这类资产的全称,代号,描述及资产地址(URL)
contract IERC721Metadata {

  /**
   * @notice A descriptive name for a collection of NFTs in this contract 本智能合约中NFT集合的描述性名称
   */
  function name() external view returns (string);

  /**
   * @notice An abbreviated name for NFTs in this contract
   */
  function symbol() external view returns (string);

  /**
   * @notice A description of what this DAR is used for
   */
  function description() external view returns (string);

  /**
   * Stores arbitrary info about a token
   */
  function tokenMetadata(uint256 assetId) external view returns (string);
}

文件:
ERC721Metadata.sol

pragma solidity ^0.4.18;

import './IERC721Metadata.sol';  //这个基类合约是对Nfts资产的描述信息,包括这类资产的全称,代号,描述及资产地址(URL)
import './AssetRegistryStorage.sol'; //资产登记所需要的基类全约,其中声明了各种需要登记到区块中的(变量类型为storage)mapping表变量

//这个合约用于获取NFT资产的一般性信息,包括 名称,描述,标识缩写等 
contract ERC721Metadata is AssetRegistryStorage, IERC721Metadata {
  function name() external view returns (string) {
    return _name; //这儿的所有状态变量(会被写入到区块中的全局变量,storage类型)都是在文件AssetRegistryStorage.sol中声明的
  }
  function symbol() external view returns (string) {
    return _symbol;
  }
  function description() external view returns (string) {
    return _description;
  }
  function tokenMetadata(uint256 assetId) external view returns (string) {
    return _assetData[assetId];
  }
  function _update(uint256 assetId, string data) internal {
    _assetData[assetId] = data;
  }
}

文件:
FullAssetRegistry.sol

pragma solidity ^0.4.18;

import './ERC721Base.sol';
import './ERC721Enumerable.sol';
import './ERC721Metadata.sol';

//目前的理解 是,这个合约额外增加了一个检查指定ID的NFT资产是否存在的方法,并返回资产的小数位数(代币的最小单位)
contract FullAssetRegistry is ERC721Base, ERC721Enumerable, ERC721Metadata {
  constructor() public {
  }

  /**
   * @dev Method to check if an asset identified by the given id exists under this DAR.
   * @return uint256 the assetId
   */
   //指定ID的NFT资产(由assetId形参指定)是否存在,此方法公开给外部调用
  function exists(uint256 assetId) external view returns (bool) {
    return _exists(assetId);
  }
  //上一个方法所需要的内部调用的方法
  function _exists(uint256 assetId) internal view returns (bool) {
    return _holderOf[assetId] != 0;  //这个状态变量在文件AssetRegistryStorage.sol中声明的
  }

  //返回代币的小数位数(对于NFT资产来说,就是没有小数,都是整数)
  function decimals() external pure returns (uint256) {
    return 0;
  }
}

整个ERC721资产的合约代码已经完整批注理解完成,不过现在思考 到的问题是:
我究竟应当怎样部署这个合约体系呢?
是部署处于继承关系最后面的那个合约:FullAssetRegistry.sol 吗?
发现对于合约部署部分的理解原来一直都并不深入,况且从来没有完整地在本地配置完整以太坊节点进行了相关的测试和部署,这样看来,这方面的知识是比较欠缺的。
恳请有知道的大佬给予指导,定将万分感激。

github: https://github.com/lhghroom/Self-learning-blockchain-from-scratch

【欢迎大家加入[就是要学]社群】
如今,这个世界的变化与科技的发展就像一个机器猛兽,它跑得越来越快,跑得越来越快,在我们身后追赶着我们。
很多人很早就放弃了成长,也就放弃了继续奔跑,多数人保持终身不变的样子,原地不动,成为那猛兽的肚中餐——当然那也不错,在猛兽的逼迫下,机械的重复着自我感觉还良好地稳定工作与生活——而且多半感觉不到这有什么不正常的地方,因为在猛兽肚子里的是大多数人,就好像大多数人都在一个大坑里,也就感觉不出来这是一个大坑了,反而坑外的世界显得有些不大正常。
为什么我们不要做坑里的大多数人?
因为真正的人生,应当有百万种可能 ;因为真正的一生可以有好多辈子组成,每一辈子都可以做自己喜欢的事情;因为真正的人生,应当有无数种可以选择的权利,而不是总觉得自己别无选择。因为我们要成为一九法则中为数不多的那个一;因为我们要成为自己人生的导演而不是被迫成为别人安排的戏目中的演员。
【请注意】
就是要学社群并不会告诉你怎样一夜暴富!也不会告诉你怎样不经努力就实现梦想!
【请注意】
就是要学社群并没有任何可以应付未来一切变化的独门绝技,也没有值得吹嘘的所谓价值连城的成功学方法论!
【请注意】
社群只会互相帮助,让每个人都看清自己在哪儿,自己是怎样的,重新看见心中的梦想,唤醒各自内心中的那个英雄,然后勇往直前,成为自己想要成为的样子!
期待与你并肩奔赴未来!

QQ群:646854445 (【就是要学】终身成长)

【原文地址】
https://www.941xue.com/content.aspx?id=1736
【同步语音笔记】
https://www.ximalaya.com/keji/19103006/358342795

【学习过程屏幕录屏】
https://www.bilibili.com/video/BV1Wa4y1j7s4/

  • 暂无回复。