【CryptoZombies - 2 Solidity 进阶】001 智能合约的不可篡改性与Ownable

目录

一、前言

二、不可篡改性与外部依赖

1、讲解

1.不可篡改

2.外部依赖关系

2、实战

1.要求

2.代码

三、Ownable 

1、讲解

1.构造函数

2.函数修饰符

2、实战

1.要求

2.代码


一、前言

看了一些区块链的教程,论文,在网上刚刚找到了一个项目实战,CryptoZombies。

终于要更换新标题了,这次命名为进阶,也就是说之前的是基础教程,因为从这一次的内容开始,我们将通过这个项目实战,更加深入了解solidity。也更能区分solidity和其他的一些编程语言的区别。当然,我们会在后面详细说明。

如果你想了解更多有关于机器学习、深度学习、区块链、计算机视觉等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!

二、不可篡改性与外部依赖

1、讲解

1.不可篡改

我们学完了solidity的基础,但是我们发现,我们讲的 Solidity 和其他语言没有质的区别,它长得也很像 JavaScript。但是以太坊solidity开发的是去中心化的应用DAPP,这个和普通的应用是有很大的差别的。

我们都知道,以太坊是区块链2.0,我们在以太坊上部署合约,就满足区块链中的一个非常重要的特性,不可篡改

所以如果我们将我们写好的合约上传到以太坊,就不能再修改了。合约就会永远保存在以太坊上,这个特性保证了合约的安全性。但是我们的合约编写的一旦有漏洞,我们就只能放弃这个合约,然后转移到新的修复后的合约上。

总结一下,这个特性的优点如下:

因为其不可篡改,一个合约定义并部署好后,所有的程序会按照既定的代码一丝不苟的执行,其结果不会被中途恶意用户篡改。从而极大的保证了合约的安全性。也更具公平性。

 也不是说这个特性是没有缺点的:

因为其不可篡改,一旦发现合约存在问题,那这个合约就作废了,并且一直留存在以太坊中,久而久之,无用的协议增多,白白浪费很多内存。就像你电脑中一旦有了垃圾文件,一直删不掉,着实挺难受,强迫症更难受。

2.外部依赖关系

我们之前调用加密小猫,在地址中使用的地址是固定的,也就是硬编码到DAPP中的,一旦加密小猫出现问题,那我们的DAPP也就会出现问题了,所以我们在真实的使用中是不能使用硬编码的,我们要通过一个函数来获取其地址,这样我们就可以便于在DAPP的关键部分中使用参数进行数据的修改了。

在这个过程中我们对外部的合约有依赖,涉及到了外部依赖关系,这种依赖,我们应该是灵活可动态修正的依赖。

2、实战

1.要求

我们来定义一个函数,从 kitty 合约中获取它的基因:

1.删除采用硬编码 方式的 ckAddress 代码行。

2.之前创建 kittyContract 变量的那行代码,修改为对 kittyContract 变量的声明 -- 暂时不给它指定具体的实例。

3.创建名为 setKittyContractAddress 的函数, 它带一个参数 _addressaddress类型), 可见性设为external。

4.在函数内部,添加一行代码,将 kittyContract 变量设置为返回值:KittyInterface(_address)

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  // 1. Remove this:
  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  // 2. Change this to just a declaration:
  KittyInterface kittyContract = KittyInterface(ckAddress);

  // 3. Add setKittyContractAddress method here

  function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

2.代码

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  // 1. Remove this:
  
  // 2. Change this to just a declaration:
  KittyInterface kittyContract;

  // 3. Add setKittyContractAddress method here
  function setKittyContractAddress(address _address) external {
    kittyContract = KittyInterface(_address);
  }

  function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

三、Ownable 

1、讲解

在上面的例题中,我们将函数声明为外部。我想大家应该还记得外部的特点:

定义为外部的函数只能在合约之外调用 - 它们不能被合约内的其他函数调用。

如果我们这样定义函数,函数之外的合约都可以调用。也就是说任何可以调用该函数的人都可以修改加密猫的地址。我们确实希望这个地址能够在合约中修改,但是我们并不希望所有人都可以修改,所以我们还要继续完善权限。

在这里我们使用Ownable合约。首先我们先举一个示例,然后再逐步讲解。

contract Ownable {
  address public owner;
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }
}

在这里,我们涉及到如下知识点:

1.构造函数

这个名词,想必大家都很熟悉了,在这里的构造函数是:

  function Ownable() public {
    owner = msg.sender;
  }

在以太坊中,构造函数不是必须的,但是一旦我们使用,就要遵守相关规则:

1.与合约同名。

2.构造函数只在合约最初被创建的时候执行一次。

 

2.函数修饰符

函数修饰符跟函数很类似,不过函数修饰符是用来修饰其他已有函数的,在这里的函数修饰符是:

  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

函数修饰符在这里的作用是:

在其他语句执行前,为它检查下先验条件。

在我们的这个样例中,我们使用函数修饰符检查调用者,确保只有合约的主人才能运行本函数。

函数修饰符看起来跟函数没什么不同,不过关键字modifier 告诉编译器,这是个modifier(修饰符),而不是个function(函数)。它不能像函数那样被直接调用,只能被添加到函数定义的末尾,用以改变函数的行为

接下来我们来看一下修饰符如何使用。

contract MyContract is Ownable {
  event LaughManiacally(string laughter);

  //注意! `onlyOwner`上场 :
  function likeABoss() external onlyOwner {
    LaughManiacally("Muahahahaha");
  }
}

对于上述代码执行过程如下:

1.调用likeABoss 函数时,首先执行 onlyOwner 中的代码。

2.执行到 onlyOwner 中的 _; 语句时,程序再返回并执行 likeABoss 中的代码。

注:一般修饰符常用于添加require检查

除了上面这两个还有很多,我们以后再慢慢讲到。

所以一个Ownable合约基本流程如下:

1.合约创建,构造函数先行,将其 owner 设置为msg.sender(其部署者)。

2.为它加上一个修饰符 onlyOwner,它会限制陌生人的访问,将访问某些函数的权限锁定在 owner 上。

3.允许将合约所有权转让给他人

 

2、实战

1.要求

在僵尸代码中实现小猫的基因。:

1.在程序中导入 ownable.sol 的内容。 。

2.修改 ZombieFactory 合约, 让它继承自 Ownable。

3.将 onlyOwner 函数修饰符添加到 setKittyContractAddress 中

2.代码

pragma solidity >=0.5.0 <0.6.0;

// 1. Import here
import "./ownable.sol";

// 2. Inherit here:
contract ZombieFactory is Ownable {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string memory _name, uint _dna) internal {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        emit NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;
    }

    function createRandomZombie(string memory _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  KittyInterface kittyContract;

  // Modify this function:
  function setKittyContractAddress(address _address) external onlyOwner {
    kittyContract = KittyInterface(_address);
  }

  function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

你可能感兴趣的:(Blockchain)