欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 16:02:27
...

目录

一、前言

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

1、讲解

1.不可篡改

2.外部依赖关系

2、实战

1.要求

2.代码

三、Ownable 

1、讲解

1.构造函数

2.函数修饰符

2、实战

1.要求

2.代码


一、前言

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

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

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

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

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

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