智能合约学习笔记——僵尸工厂(一)

根据CryptoZombies学solidity

Lesson 1 搭建僵尸工厂

生成僵尸的工厂,首先建立一个批量生成僵尸的合约

跳转应用市场

pragma solidity ^0.4.19;

contract ZombieFactory { //建立合约僵尸工厂  
    event NewZombie(uint zombieId, string name, uint dna); // 这里建立事件

    uint dnaDigits = 16; // 僵尸的DNA包括头部基因;眼部基因;上衣基因;皮肤基因;眼色基因;衣服颜色基因
    uint dnaModulus = 10 ** dnaDigits;//用于后续取余运算

    struct Zombie {
        string name;
        uint dna;
    }//建立一个僵尸的结构体,包括僵尸的名字和dna

    Zombie[] public zombies;//僵尸的数组,将生成的僵尸数据存储到这个数组中

    function _createZombie(string _name, uint _dna) private {//这里用的是私有函数,只能在合约中被调用,注意私有函数的名字要用_开头
        uint id = zombies.push(Zombie(_name, _dna)) -1 ;//将生成的新僵尸数据加入数组,push返回加入后数组长度,减一后是为这个新僵尸在数组中的索引
        NewZombie(id, _name, _dna); // 当将新生成的僵尸加入数组时就会触发事件
    }

    function _generateRandomDna(string _str) private view returns (uint) {//这里用到了view,只能调用合约中的变量不能改动,另一个修饰符pure甚至不能调用函数外的数据
        uint rand = uint(keccak256(_str)); //根据输入的字符生成随机数
        return rand % dnaModulus; //我们需要的随机数是长度为16的,截取我们需要的返回
    }

    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);//根据僵尸的名字生成随机数作为僵尸的dna
        _createZombie(_name, randDna);//根据僵尸的名字和dna调用另一个函数生成僵尸
    }

}

javascript实现

当合约完成之后,就需要写一段 JavaScript 前端代码来调用这个合约。

tomcat

以太坊有一个 JavaScript 库,名为Web3.js。
在后面的课程里,我们会进一步地教你如何安装一个合约,如何设置Web3.js。 但是现在我们通过一段代码来了解 Web3.js 是如何和我们发布的合约交互的吧。

数据卷

这里JavaScript 所做的就是获取由zombieDetails 产生的数据, 并且利用浏览器里的 JavaScript 神奇功能 (我们用 Vue.js),置换出图像以及使用CSS过滤器。在后面的课程中,就可以看到全部的代码。

时序数据库

// 下面是调用合约的方式:
var abi = /* abi是由编译器生成的 */
var ZombieFactoryContract = web3.eth.contract(abi)
var contractAddress = /* 发布之后在以太坊上生成的合约地址 */
var ZombieFactory = ZombieFactoryContract.at(contractAddress)
// `ZombieFactory` 能访问公共的函数以及事件

// 某个监听文本输入的监听器:
$("#ourButton").click(function(e) {
  var name = $("#nameInput").val()
  //调用合约的 `createRandomZombie` 函数:
  ZombieFactory.createRandomZombie(name)
})

// 监听 `NewZombie` 事件, 并且更新UI
var event = ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  generateZombie(result.zombieId, result.name, result.dna)
})

// 获取 Zombie 的 dna, 更新图像
function generateZombie(id, name, dna) {
  let dnaStr = String(dna)
  // 如果dna少于16位,在它前面用0补上
  while (dnaStr.length < 16)
    dnaStr = "0" + dnaStr

  let zombieDetails = {
    // 前两位数构成头部.我们可能有7种头部, 所以 % 7
    // 得到的数在0-6,再加上1,数的范围变成1-7
    // 通过这样计算:
    headChoice: dnaStr.substring(0, 2) % 7 + 1// 我们得到的图片名称从head1.png 到 head7.png

    // 接下来的两位数构成眼睛, 眼睛变化就对11取模:
    eyeChoice: dnaStr.substring(2, 4) % 11 + 1,
    // 再接下来的两位数构成衣服,衣服变化就对6取模:
    shirtChoice: dnaStr.substring(4, 6) % 6 + 1,
    //最后6位控制颜色. 用css选择器: hue-rotate来更新
    // 360度:
    skinColorChoice: parseInt(dnaStr.substring(6, 8) / 100 * 360),
    eyeColorChoice: parseInt(dnaStr.substring(8, 10) / 100 * 360),
    clothesColorChoice: parseInt(dnaStr.substring(10, 12) / 100 * 360),
    zombieName: name,
    zombieDescription: "A Level 1 CryptoZombie",
  }
  return zombieDetails
}

可以部署这个合约,并输入僵尸的名字,生成一个僵尸
在这里插入图片描述

图像配准

Lesson 2 僵尸攻击人类

这里用到了两个合约,放在两个不同的文件中
首先,ZombieFactory

测试用例

pragma solidity ^0.4.19;

contract ZombieFactory {

    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;//映射,可以通过id访问地址
    mapping (address => uint) ownerZombieCount;//映射,可以通过地址询问Id

    function _createZombie(string _name, uint _dna) internal {//这里用到了internal,指这个函数是合约内部的函数,只能在合约内部被调用,但是如果一个合约继承于这个合约那么也可以调用这个函数
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;//可以通过msg.sender调用得到当前调用者的地址,这里是将地址存入对应id的映射
        ownerZombieCount[msg.sender]++;//这个地址名下的僵尸数目加一
        NewZombie(id, _name, _dna);
    }

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

    function createRandomZombie(string _name) public {
        require(ownerZombieCount[msg.sender] == 0);//这里用到了require来限制()里的语句必须是true,也就是必须没有僵尸才能调用这个函数来创造第一个僵尸
        uint randDna = _generateRandomDna(_name);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}

以及一个ZombieFactory的子合约

arm

pragma solidity ^0.4.19;

import "./zombiefactory.sol";//讲zombiefactory文件引入,方便下面继承ZombieFactory合约

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 {//ZombieFeeding合约继承于ZombieFactory合约

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);//指向接口

  function feedAndMultiply(uint _zombieId, uint _targetDna, string _species ) public {
    require(msg.sender == zombieToOwner[_zombieId]);//限制只有调用者是这个僵尸的主人才行
    Zombie storage myZombie = zombies[_zombieId];//storage类型变量是指永久存储在区块链中的变量
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;//新生成的僵尸的dna应该是两个dna的平均值
    if (keccak256(_species) == keccak256("kitty")) {//判断一下,如果是kitty的话,dna的最后两位变成99
      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");//
  }

}

JavaScript 实现

我们只用编译和部署 ZombieFeeding,就可以将这个合约部署到以太坊了。我们最终完成的这个合约继承自 ZombieFactory,因此它可以访问自己和父辈合约中的所有 public 方法。

安卓音乐播放器

我们来看一个与我们的刚部署的合约进行交互的例子, 这个例子使用了 JavaScript 和 web3.js:

变量的值

var abi = /* abi generated by the compiler */
var ZombieFeedingContract = web3.eth.contract(abi)
var contractAddress = /* our contract address on Ethereum after deploying */
var ZombieFeeding = ZombieFeedingContract.at(contractAddress)

// 假设我们有我们的僵尸ID和要攻击的猫咪ID
let zombieId = 1;
let kittyId = 1;

// 要拿到猫咪的DNA,我们需要调用它的API。这些数据保存在它们的服务器上而不是区块链上。
// 如果一切都在区块链上,我们就不用担心它们的服务器挂了,或者它们修改了API,
// 或者因为不喜欢我们的僵尸游戏而封杀了我们
let apiUrl = "https://api.cryptokitties.co/kitties/" + kittyId
$.get(apiUrl, function(data) {
  let imgUrl = data.image_url
  // 一些显示图片的代码
})

// 当用户点击一只猫咪的时候:
$(".kittyImage").click(function(e) {
  // 调用我们合约的 `feedOnKitty` 函数
  ZombieFeeding.feedOnKitty(zombieId, kittyId)
})

// 侦听来自我们合约的新僵尸事件好来处理
ZombieFactory.NewZombie(function(error, result) {
  if (error) return
  // 这个函数用来显示僵尸:
  generateZombie(result.zombieId, result.name, result.dna)
})

在这里插入图片描述

鍖哄潡閾?

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注